Skip to content

Commit

Permalink
fixup! feat(ivy): enhance [style] and [class] bindings to be animatio…
Browse files Browse the repository at this point in the history
…n aware
  • Loading branch information
matsko committed Sep 27, 2018
1 parent a28a7b2 commit 02a5e45
Show file tree
Hide file tree
Showing 24 changed files with 328 additions and 344 deletions.
4 changes: 2 additions & 2 deletions packages/core/src/core_render3_private_export.ts
Original file line number Diff line number Diff line change
Expand Up @@ -166,12 +166,12 @@ export {
Player as ɵPlayer,
PlayState as ɵPlayState,
PlayerHandler as ɵPlayerHandler,
} from './render3/styling/interfaces';
} from './render3/interfaces/player';

export {
addPlayer as ɵaddPlayer,
getPlayers as ɵgetPlayers,
} from './render3/styling/players';
} from './render3/players';

// we reexport these symbols just so that they are retained during the dead code elimination
// performed by rollup while it's creating fesm files.
Expand Down
4 changes: 1 addition & 3 deletions packages/core/src/render3/component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,7 @@ import {assertComponentType, assertDefined} from './assert';
import {getLElementFromComponent, readPatchedLViewData} from './context_discovery';
import {getComponentDef} from './definition';
import {queueInitHooks, queueLifecycleHooks} from './hooks';
import {PlayerHandler} from './styling/interfaces';

import {PlayerHandler} from './interfaces/player';
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions';
import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition';
import {LElementNode} from './interfaces/node';
Expand All @@ -26,7 +25,6 @@ import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags,
import {getRootView, stringify} from './util';



/** Options that control how the component should be bootstrapped. */
export interface CreateComponentOptions {
/** Which renderer factory to use. */
Expand Down
26 changes: 15 additions & 11 deletions packages/core/src/render3/instructions.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ import {Sanitizer} from '../sanitization/security';
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';

import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert';
import {attachPatchData, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery';
import {getRootView} from './discovery_utils';
import {attachPatchData, getContext, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery';
import {getRootContext, getRootView} from './discovery_utils';
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
Expand All @@ -24,16 +24,17 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
import {LQueries} from './interfaces/query';
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
import {StylingContext} from './interfaces/styling';
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getContainerNode, getHostElementNode, getLViewChild, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation';
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
import {BindingPlayerFactory} from './styling/binding_player_factory';
import {allocStylingContext, createStylingContextTemplate, renderStyleAndClassBindings as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
import {PlayerFactory, StylingContext} from './styling/interfaces';
import {assertDataInRangeInternal, getLNode, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, stringify} from './util';



/**
* A permanent marker promise which signifies that the current CD tree is
* clean.
Expand Down Expand Up @@ -1580,7 +1581,11 @@ function getStylingContext(index: number): StylingContext {
* index.)
*/
export function elementStylingApply<T>(index: number): void {
renderElementStyles(getStylingContext(index), renderer, viewData);
const totalPlayersQueued = renderElementStyles(getStylingContext(index), renderer, viewData);
if (totalPlayersQueued > 0) {
const rootContext = getRootContext(viewData);
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
}
}

/**
Expand Down Expand Up @@ -2363,11 +2368,7 @@ export function markViewDirty(view: LViewData): void {
ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext should be defined');

const rootContext = currentView[CONTEXT] as RootContext;
const nothingScheduled = rootContext.flags === RootContextFlags.Empty;
rootContext.flags |= RootContextFlags.DetectChanges;
if (nothingScheduled) {
scheduleTick(rootContext);
}
scheduleTick(rootContext, RootContextFlags.DetectChanges);
}

/**
Expand All @@ -2381,8 +2382,11 @@ export function markViewDirty(view: LViewData): void {
* `scheduleTick` requests. The scheduling function can be overridden in
* `renderComponent`'s `scheduler` option.
*/
export function scheduleTick<T>(rootContext: RootContext) {
if (rootContext.clean == _CLEAN_PROMISE) {
export function scheduleTick<T>(rootContext: RootContext, flags: RootContextFlags) {
const nothingScheduled = rootContext.flags === RootContextFlags.Empty;
rootContext.flags |= flags;

if (nothingScheduled && rootContext.clean == _CLEAN_PROMISE) {
let res: null|((val: null) => void);
rootContext.clean = new Promise<null>((r) => res = r);
rootContext.scheduler(() => {
Expand Down
3 changes: 1 addition & 2 deletions packages/core/src/render3/interfaces/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,12 +6,11 @@
* found in the LICENSE file at https://angular.io/license
*/

import {StylingContext} from '../styling/interfaces';

import {LContainer} from './container';
import {LInjector} from './injector';
import {LQueries} from './query';
import {RComment, RElement, RText} from './renderer';
import {StylingContext} from './styling';
import {LViewData, TView} from './view';


Expand Down
89 changes: 89 additions & 0 deletions packages/core/src/render3/interfaces/player.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/

/**
* A shared interface which contains an animation player
*/
export interface Player {
parent?: Player|null;
state: PlayState;
play(): void;
pause(): void;
finish(): void;
destroy(): void;
addEventListener(state: PlayState|string, cb: (data?: any) => any): void;
}

export abstract class PlayerFactory {
public dirty !: boolean;
abstract buildPlayer(existingPlayer?: Player|null): Player|null;
}

/**
* The state of a given player
*
* Do not change the increasing nature of the numbers since the player
* code may compare state by checking if a number is higher or lower than
* a certain numeric value.
*/
export const enum PlayState {Pending = 0, Running = 1, Paused = 2, Finished = 100, Destroyed = 200}

/**
* The context that stores all the active players and queued player factories present on an element.
*/
export interface PlayerContext extends Array<null|number|Player|PlayerFactory> {
[PlayerIndex.NonFactoryPlayersStart]: number;
[PlayerIndex.ClassMapFactoryPosition]: PlayerFactory|null;
[PlayerIndex.ClassMapPlayerPosition]: Player|null;
[PlayerIndex.StyleMapFactoryPosition]: PlayerFactory|null;
[PlayerIndex.StyleMapPlayerPosition]: Player|null;
}

/**
* Designed to be used as an injection service to capture all animation players.
*
* When present all animation players will be passed into the flush method below.
* This feature is designed to service application-wide animation testing, live
* debugging as well as custom animation choreographing tools.
*/
export interface PlayerHandler {
/**
* Designed to kick off the player at the end of change detection
*/
flushPlayers(): void;

/**
* @param player The player that has been scheduled to run within the application.
* @param context The context as to where the player was bound to
*/
queuePlayer(player: Player, context: ComponentInstance|DirectiveInstance|HTMLElement): void;
}

export const enum PlayerIndex {
// The position where the index that reveals where players start in the PlayerContext
NonFactoryPlayersStart = 0,
// The position where the factory to handle a {key:value} map expression for classes
ClassMapFactoryPosition = 1,
// The position where the last player assigned to the class factory is stored
ClassMapPlayerPosition = 2,
// The position where the factory to handle a {key:value} map expression for styles
StyleMapFactoryPosition = 3,
// The position where the last player assigned to the style factory is stored
StyleMapPlayerPosition = 4,
// The position where any factories start in the PlayerContext
FactoriesStartPosition = 1,
// The position where non map-based factories start in the PlayerContext
SingleFactoriesStartPosition = 5,
// For each factory there is a player in the player context (therefore size = 2)
PlayerAndFactoryTupleSize = 2,
// The player exists next to the factory in the list
PlayerOffsetPosition = 1,
}

export declare type ComponentInstance = {};
export declare type DirectiveInstance = {};
Original file line number Diff line number Diff line change
Expand Up @@ -6,79 +6,10 @@
* found in the LICENSE file at https://angular.io/license
*/
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
import {LElementNode} from '../interfaces/node';
import {LElementNode} from './node';
import {PlayerContext} from './player';


/**
* A shared interface which contains an animation player
*/
export interface Player {
parent?: Player|null;
state: PlayState;
play(): void;
pause(): void;
finish(): void;
destroy(): void;
addEventListener(state: PlayState|string, cb: (data?: any) => any): void;
}

export abstract class PlayerFactory {
public dirty !: boolean;
abstract buildPlayer(existingPlayer?: Player|null): Player|null;
}

/**
* The state of a given player
*
* Do not change the increasing nature of the numbers since the player
* code may compare state by checking if a number is higher or lower than
* a certain numeric value.
*/
export const enum PlayState {Pending = 0, Running = 1, Paused = 2, Finished = 100, Destroyed = 200}

/**
* The context that stores all the active players and queued player factories present on an element.
*/
export interface PlayerContext extends Array<null|number|Player|PlayerFactory> {
[PlayerIndex.NonFactoryPlayersStart]: number;
[PlayerIndex.ClassMapFactoryPosition]: PlayerFactory|null;
[PlayerIndex.ClassMapPlayerPosition]: Player|null;
[PlayerIndex.StyleMapFactoryPosition]: PlayerFactory|null;
[PlayerIndex.StyleMapPlayerPosition]: Player|null;
}
export declare type ComponentInstance = {};
export declare type DirectiveInstance = {};

/**
* Designed to be used as an injection service to capture all animation players.
*
* When present all animation players will be passed into the flush method below.
* This feature is designed to service application-wide animation testing, live
* debugging as well as custom animation choreographing tools.
*/
export interface PlayerHandler {
/**
* Designed to kick off the player at the end of change detection
*/
flushPlayers(): void;

/**
* @param player The player that has been scheduled to run within the application.
* @param context The context as to where the player was bound to
*/
queuePlayer(player: Player, context: ComponentInstance|DirectiveInstance|HTMLElement): void;
}

export const enum BindingType {
Unset = 0,
Class = 2,
Style = 3,
Attr = 4,
}

export interface BindingStore {
setValue(type: BindingType, element: HTMLElement, prop: string, value: any): void;
}

/**
* The styling context acts as a styling manifest (shaped as an array) for determining which
Expand Down Expand Up @@ -287,23 +218,12 @@ export const enum StylingIndex {
BitMask = 0b11111111111111, // 14 bits
}

export const enum PlayerIndex {
// The position where the index that reveals where players start in the PlayerContext
NonFactoryPlayersStart = 0,
// The position where the factory to handle a {key:value} map expression for classes
ClassMapFactoryPosition = 1,
// The position where the last player assigned to the class factory is stored
ClassMapPlayerPosition = 2,
// The position where the factory to handle a {key:value} map expression for styles
StyleMapFactoryPosition = 3,
// The position where the last player assigned to the style factory is stored
StyleMapPlayerPosition = 4,
// The position where any factories start in the PlayerContext
FactoriesStartPosition = 1,
// The position where non map-based factories start in the PlayerContext
SingleFactoriesStartPosition = 5,
// For each factory there is a player in the player context (therefore size = 2)
PlayerAndFactoryTupleSize = 2,
// The player exists next to the factory in the list
PlayerOffsetPosition = 1,
export const enum BindingType {
Unset = 0,
Class = 2,
Style = 3,
}

export interface BindingStore {
setValue(type: BindingType, element: HTMLElement, prop: string, value: any): void;
}
3 changes: 2 additions & 1 deletion packages/core/src/render3/interfaces/view.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,15 @@
import {Injector} from '../../di/injector';
import {QueryList} from '../../linker';
import {Sanitizer} from '../../sanitization/security';
import {PlayerHandler} from '../styling/interfaces';

import {LContainer} from './container';
import {ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefList, PipeDefInternal, PipeDefList} from './definition';
import {LElementNode, LViewNode, TElementNode, TNode, TViewNode} from './node';
import {PlayerHandler} from './player';
import {LQueries} from './query';
import {Renderer3} from './renderer';


/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
export const HEADER_OFFSET = 17;

Expand Down
43 changes: 43 additions & 0 deletions packages/core/src/render3/players.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* @license
* Copyright Google Inc. All Rights Reserved.
*
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import './ng_dev_mode';

import {getContext} from './context_discovery';
import {getRootContext} from './discovery_utils';
import {scheduleTick} from './instructions';
import {ComponentInstance, DirectiveInstance, Player} from './interfaces/player';
import {RootContextFlags} from './interfaces/view';
import {EMPTY_ARR, addPlayerInternal, getOrCreatePlayerContext, getPlayerContext, getPlayersInternal, getStylingContext, throwInvalidRefError} from './styling/util';

export function addPlayer(
ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void {
const context = getContext(ref);
if (!context) {
ngDevMode && throwInvalidRefError();
return;
}

const element = context.native as HTMLElement;
const lViewData = context.lViewData;
const playerContext = getOrCreatePlayerContext(element, context) !;
const rootContext = getRootContext(lViewData);
addPlayerInternal(playerContext, rootContext, element, player, 0, ref);
scheduleTick(getRootContext(lViewData), RootContextFlags.FlushPlayers);
}

export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] {
const context = getContext(ref);
if (!context) {
ngDevMode && throwInvalidRefError();
return EMPTY_ARR;
}

const stylingContext = getStylingContext(context.lViewData, context.lNodeIndex);
const playerContext = stylingContext ? getPlayerContext(stylingContext) : null;
return playerContext ? getPlayersInternal(playerContext) : EMPTY_ARR;
}
3 changes: 2 additions & 1 deletion packages/core/src/render3/styling/binding_player_factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* Use of this source code is governed by an MIT-style license that can be
* found in the LICENSE file at https://angular.io/license
*/
import {BindingStore, BindingType, Player, PlayerFactory} from './interfaces';
import {Player, PlayerFactory} from '../interfaces/player';
import {BindingStore, BindingType} from '../interfaces/styling';

export class BindingPlayerFactory<T> extends PlayerFactory implements BindingStore {
private _values: {[key: string]: string | null}|null = null;
Expand Down

0 comments on commit 02a5e45

Please sign in to comment.