feat(ivy): enhance [style] and [class] bindings to be animation aware (#26096)
PR Close #26096
This commit is contained in:
parent
be337a2e52
commit
fa8e633be5
|
@ -163,6 +163,7 @@ export {
|
||||||
|
|
||||||
export {
|
export {
|
||||||
Player as ɵPlayer,
|
Player as ɵPlayer,
|
||||||
|
PlayerFactory as ɵPlayerFactory,
|
||||||
PlayState as ɵPlayState,
|
PlayState as ɵPlayState,
|
||||||
PlayerHandler as ɵPlayerHandler,
|
PlayerHandler as ɵPlayerHandler,
|
||||||
} from './render3/interfaces/player';
|
} from './render3/interfaces/player';
|
||||||
|
@ -171,6 +172,10 @@ export {
|
||||||
LContext as ɵLContext,
|
LContext as ɵLContext,
|
||||||
} from './render3/interfaces/context';
|
} from './render3/interfaces/context';
|
||||||
|
|
||||||
|
export {
|
||||||
|
bindPlayerFactory as ɵbindPlayerFactory,
|
||||||
|
} from './render3/styling/player_factory';
|
||||||
|
|
||||||
export {
|
export {
|
||||||
addPlayer as ɵaddPlayer,
|
addPlayer as ɵaddPlayer,
|
||||||
getPlayers as ɵgetPlayers,
|
getPlayers as ɵgetPlayers,
|
||||||
|
|
|
@ -74,9 +74,9 @@ export function getHostComponent<T = {}>(target: {}): T|null {
|
||||||
* Returns the `RootContext` instance that is associated with
|
* Returns the `RootContext` instance that is associated with
|
||||||
* the application where the target is situated.
|
* the application where the target is situated.
|
||||||
*/
|
*/
|
||||||
export function getRootContext(target: {}): RootContext {
|
export function getRootContext(target: LViewData | {}): RootContext {
|
||||||
const context = loadContext(target) !;
|
const lViewData = Array.isArray(target) ? target : loadContext(target) !.lViewData;
|
||||||
const rootLViewData = getRootView(context.lViewData);
|
const rootLViewData = getRootView(lViewData);
|
||||||
return rootLViewData[CONTEXT] as RootContext;
|
return rootLViewData[CONTEXT] as RootContext;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,7 @@ import {ACTIVE_INDEX, LContainer, VIEWS} from './interfaces/container';
|
||||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefListOrFactory, InitialStylingFlags, PipeDefListOrFactory, RenderFlags} from './interfaces/definition';
|
||||||
import {INJECTOR_SIZE} from './interfaces/injector';
|
import {INJECTOR_SIZE} from './interfaces/injector';
|
||||||
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
|
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TProjectionNode, TViewNode} from './interfaces/node';
|
||||||
|
import {PlayerFactory} from './interfaces/player';
|
||||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||||
import {LQueries} from './interfaces/query';
|
import {LQueries} from './interfaces/query';
|
||||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
|
@ -27,9 +28,10 @@ import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, Curre
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
import {createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
import {createStylingContextTemplate, renderStyleAndClassBindings, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings';
|
||||||
|
import {BoundPlayerFactory} from './styling/player_factory';
|
||||||
import {getStylingContext} from './styling/util';
|
import {getStylingContext} from './styling/util';
|
||||||
import {assertDataInRangeInternal, getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootView, getTNode, isComponent, isContentQueryHost, isDifferent, loadInternal, readPatchedLViewData, stringify} from './util';
|
import {assertDataInRangeInternal, getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getRootContext, getRootView, getTNode, isComponent, isContentQueryHost, isDifferent, loadInternal, readPatchedLViewData, stringify} from './util';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1501,9 +1503,11 @@ function generatePropertyAliases(
|
||||||
* renaming as part of minification.
|
* renaming as part of minification.
|
||||||
* @param value A value indicating if a given class should be added or removed.
|
* @param value A value indicating if a given class should be added or removed.
|
||||||
*/
|
*/
|
||||||
export function elementClassProp<T>(
|
export function elementClassProp(
|
||||||
index: number, stylingIndex: number, value: T | NO_CHANGE): void {
|
index: number, stylingIndex: number, value: boolean | PlayerFactory): void {
|
||||||
updateElementClassProp(getStylingContext(index, viewData), stylingIndex, value ? true : false);
|
const val =
|
||||||
|
(value instanceof BoundPlayerFactory) ? (value as BoundPlayerFactory<boolean>) : (!!value);
|
||||||
|
updateElementClassProp(getStylingContext(index, viewData), stylingIndex, val);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1534,7 +1538,7 @@ export function elementClassProp<T>(
|
||||||
* @param styleSanitizer An optional sanitizer function that will be used (if provided)
|
* @param styleSanitizer An optional sanitizer function that will be used (if provided)
|
||||||
* to sanitize the any CSS property values that are applied to the element (during rendering).
|
* to sanitize the any CSS property values that are applied to the element (during rendering).
|
||||||
*/
|
*/
|
||||||
export function elementStyling<T>(
|
export function elementStyling(
|
||||||
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
classDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||||
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
styleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||||
styleSanitizer?: StyleSanitizeFn | null): void {
|
styleSanitizer?: StyleSanitizeFn | null): void {
|
||||||
|
@ -1565,8 +1569,13 @@ export function elementStyling<T>(
|
||||||
* specifically for element styling--the index must be the next index after the element
|
* specifically for element styling--the index must be the next index after the element
|
||||||
* index.)
|
* index.)
|
||||||
*/
|
*/
|
||||||
export function elementStylingApply<T>(index: number): void {
|
export function elementStylingApply(index: number): void {
|
||||||
renderElementStyles(getStylingContext(index, viewData), renderer);
|
const totalPlayersQueued =
|
||||||
|
renderStyleAndClassBindings(getStylingContext(index, viewData), renderer, viewData);
|
||||||
|
if (totalPlayersQueued > 0) {
|
||||||
|
const rootContext = getRootContext(viewData);
|
||||||
|
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1589,8 +1598,9 @@ export function elementStylingApply<T>(index: number): void {
|
||||||
* Note that when a suffix is provided then the underlying sanitizer will
|
* Note that when a suffix is provided then the underlying sanitizer will
|
||||||
* be ignored.
|
* be ignored.
|
||||||
*/
|
*/
|
||||||
export function elementStyleProp<T>(
|
export function elementStyleProp(
|
||||||
index: number, styleIndex: number, value: T | null, suffix?: string): void {
|
index: number, styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||||
|
suffix?: string): void {
|
||||||
let valueToAdd: string|null = null;
|
let valueToAdd: string|null = null;
|
||||||
if (value) {
|
if (value) {
|
||||||
if (suffix) {
|
if (suffix) {
|
||||||
|
@ -2386,11 +2396,7 @@ export function markViewDirty(view: LViewData): void {
|
||||||
ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext should be defined');
|
ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext should be defined');
|
||||||
|
|
||||||
const rootContext = currentView[CONTEXT] as RootContext;
|
const rootContext = currentView[CONTEXT] as RootContext;
|
||||||
const nothingScheduled = rootContext.flags === RootContextFlags.Empty;
|
scheduleTick(rootContext, RootContextFlags.DetectChanges);
|
||||||
rootContext.flags |= RootContextFlags.DetectChanges;
|
|
||||||
if (nothingScheduled) {
|
|
||||||
scheduleTick(rootContext);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -2404,8 +2410,11 @@ export function markViewDirty(view: LViewData): void {
|
||||||
* `scheduleTick` requests. The scheduling function can be overridden in
|
* `scheduleTick` requests. The scheduling function can be overridden in
|
||||||
* `renderComponent`'s `scheduler` option.
|
* `renderComponent`'s `scheduler` option.
|
||||||
*/
|
*/
|
||||||
export function scheduleTick<T>(rootContext: RootContext) {
|
export function scheduleTick<T>(rootContext: RootContext, flags: RootContextFlags) {
|
||||||
if (rootContext.clean == _CLEAN_PROMISE) {
|
const nothingScheduled = rootContext.flags === RootContextFlags.Empty;
|
||||||
|
rootContext.flags |= flags;
|
||||||
|
|
||||||
|
if (nothingScheduled && rootContext.clean == _CLEAN_PROMISE) {
|
||||||
let res: null|((val: null) => void);
|
let res: null|((val: null) => void);
|
||||||
rootContext.clean = new Promise<null>((r) => res = r);
|
rootContext.clean = new Promise<null>((r) => res = r);
|
||||||
rootContext.scheduler(() => {
|
rootContext.scheduler(() => {
|
||||||
|
|
|
@ -19,6 +19,43 @@ export interface Player {
|
||||||
addEventListener(state: PlayState|string, cb: (data?: any) => any): void;
|
addEventListener(state: PlayState|string, cb: (data?: any) => any): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum BindingType {
|
||||||
|
Unset = 0,
|
||||||
|
Class = 2,
|
||||||
|
Style = 3,
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface BindingStore { setValue(prop: string, value: any): void; }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Defines the shape which produces the Player.
|
||||||
|
*
|
||||||
|
* Used to produce a player that will be placed on an element that contains
|
||||||
|
* styling bindings that make use of the player. This function is designed
|
||||||
|
* to be used with `PlayerFactory`.
|
||||||
|
*/
|
||||||
|
export interface PlayerFactoryBuildFn {
|
||||||
|
(element: HTMLElement, type: BindingType, values: {[key: string]: any},
|
||||||
|
currentPlayer: Player|null): Player|null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Used as a reference to build a player from a styling template binding
|
||||||
|
* (`[style]` and `[class]`).
|
||||||
|
*
|
||||||
|
* The `fn` function will be called once any styling-related changes are
|
||||||
|
* evaluated on an element and is expected to return a player that will
|
||||||
|
* be then run on the element.
|
||||||
|
*
|
||||||
|
* `[style]`, `[style.prop]`, `[class]` and `[class.name]` template bindings
|
||||||
|
* all accept a `PlayerFactory` as input and this player factories.
|
||||||
|
*/
|
||||||
|
export interface PlayerFactory { '__brand__': 'Brand for PlayerFactory that nothing will match'; }
|
||||||
|
|
||||||
|
export interface PlayerBuilder extends BindingStore {
|
||||||
|
buildPlayer(currentPlayer: Player|null): Player|undefined|null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The state of a given player
|
* The state of a given player
|
||||||
*
|
*
|
||||||
|
@ -29,11 +66,15 @@ export interface Player {
|
||||||
export const enum PlayState {Pending = 0, Running = 1, Paused = 2, Finished = 100, Destroyed = 200}
|
export const enum PlayState {Pending = 0, Running = 1, Paused = 2, Finished = 100, Destroyed = 200}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The context that stores all active animation players present on an element.
|
* The context that stores all the active players and queued player factories present on an element.
|
||||||
*/
|
*/
|
||||||
export declare type PlayerContext = Player[];
|
export interface PlayerContext extends Array<null|number|Player|PlayerBuilder> {
|
||||||
export declare type ComponentInstance = {};
|
[PlayerIndex.NonBuilderPlayersStart]: number;
|
||||||
export declare type DirectiveInstance = {};
|
[PlayerIndex.ClassMapPlayerBuilderPosition]: PlayerBuilder|null;
|
||||||
|
[PlayerIndex.ClassMapPlayerPosition]: Player|null;
|
||||||
|
[PlayerIndex.StyleMapPlayerBuilderPosition]: PlayerBuilder|null;
|
||||||
|
[PlayerIndex.StyleMapPlayerPosition]: Player|null;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Designed to be used as an injection service to capture all animation players.
|
* Designed to be used as an injection service to capture all animation players.
|
||||||
|
@ -54,3 +95,29 @@ export interface PlayerHandler {
|
||||||
*/
|
*/
|
||||||
queuePlayer(player: Player, context: ComponentInstance|DirectiveInstance|HTMLElement): void;
|
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
|
||||||
|
NonBuilderPlayersStart = 0,
|
||||||
|
// The position where the player builder lives (which handles {key:value} map expression) for
|
||||||
|
// classes
|
||||||
|
ClassMapPlayerBuilderPosition = 1,
|
||||||
|
// The position where the last player assigned to the class player builder is stored
|
||||||
|
ClassMapPlayerPosition = 2,
|
||||||
|
// The position where the player builder lives (which handles {key:value} map expression) for
|
||||||
|
// styles
|
||||||
|
StyleMapPlayerBuilderPosition = 3,
|
||||||
|
// The position where the last player assigned to the style player builder is stored
|
||||||
|
StyleMapPlayerPosition = 4,
|
||||||
|
// The position where any player builders start in the PlayerContext
|
||||||
|
PlayerBuildersStartPosition = 1,
|
||||||
|
// The position where non map-based player builders start in the PlayerContext
|
||||||
|
SinglePlayerBuildersStartPosition = 5,
|
||||||
|
// For each player builder there is a player in the player context (therefore size = 2)
|
||||||
|
PlayerAndPlayerBuildersTupleSize = 2,
|
||||||
|
// The player exists next to the player builder in the list
|
||||||
|
PlayerOffsetPosition = 1,
|
||||||
|
}
|
||||||
|
|
||||||
|
export declare type ComponentInstance = {};
|
||||||
|
export declare type DirectiveInstance = {};
|
||||||
|
|
|
@ -5,14 +5,11 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||||
import {RElement} from '../interfaces/renderer';
|
import {RElement} from '../interfaces/renderer';
|
||||||
|
|
||||||
import {PlayerContext} from './player';
|
import {PlayerContext} from './player';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The styling context acts as a styling manifest (shaped as an array) for determining which
|
* The styling context acts as a styling manifest (shaped as an array) for determining which
|
||||||
* styling properties have been assigned via the provided `updateStylingMap`, `updateStyleProp`
|
* styling properties have been assigned via the provided `updateStylingMap`, `updateStyleProp`
|
||||||
|
@ -184,17 +181,19 @@ export interface InitialStyles extends Array<string|null|boolean> { [0]: null; }
|
||||||
*/
|
*/
|
||||||
export const enum StylingFlags {
|
export const enum StylingFlags {
|
||||||
// Implies no configurations
|
// Implies no configurations
|
||||||
None = 0b000,
|
None = 0b0000,
|
||||||
// Whether or not the entry or context itself is dirty
|
// Whether or not the entry or context itself is dirty
|
||||||
Dirty = 0b001,
|
Dirty = 0b0001,
|
||||||
// Whether or not this is a class-based assignment
|
// Whether or not this is a class-based assignment
|
||||||
Class = 0b010,
|
Class = 0b0010,
|
||||||
// Whether or not a sanitizer was applied to this property
|
// Whether or not a sanitizer was applied to this property
|
||||||
Sanitize = 0b100,
|
Sanitize = 0b0100,
|
||||||
|
// Whether or not any player builders within need to produce new players
|
||||||
|
PlayerBuildersDirty = 0b1000,
|
||||||
// The max amount of bits used to represent these configuration values
|
// The max amount of bits used to represent these configuration values
|
||||||
BitCountSize = 3,
|
BitCountSize = 4,
|
||||||
// There are only three bits here
|
// There are only three bits here
|
||||||
BitMask = 0b111
|
BitMask = 0b1111
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
|
/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */
|
||||||
|
@ -222,10 +221,11 @@ export const enum StylingIndex {
|
||||||
FlagsOffset = 0,
|
FlagsOffset = 0,
|
||||||
PropertyOffset = 1,
|
PropertyOffset = 1,
|
||||||
ValueOffset = 2,
|
ValueOffset = 2,
|
||||||
// Size of each multi or single entry (flag + prop + value)
|
PlayerBuilderIndexOffset = 3,
|
||||||
Size = 3,
|
// Size of each multi or single entry (flag + prop + value + playerBuilderIndex)
|
||||||
|
Size = 4,
|
||||||
// Each flag has a binary digit length of this value
|
// Each flag has a binary digit length of this value
|
||||||
BitCountSize = 14, // (32 - 3) / 2 = ~14
|
BitCountSize = 14, // (32 - 4) / 2 = ~14
|
||||||
// The binary digit value as a mask
|
// The binary digit value as a mask
|
||||||
BitMask = 0b11111111111111 // 14 bits
|
BitMask = 0b11111111111111, // 14 bits
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,15 +9,16 @@
|
||||||
import {Injector} from '../../di/injector';
|
import {Injector} from '../../di/injector';
|
||||||
import {QueryList} from '../../linker';
|
import {QueryList} from '../../linker';
|
||||||
import {Sanitizer} from '../../sanitization/security';
|
import {Sanitizer} from '../../sanitization/security';
|
||||||
import {PlayerHandler} from '../interfaces/player';
|
|
||||||
|
|
||||||
import {LContainer} from './container';
|
import {LContainer} from './container';
|
||||||
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition';
|
import {ComponentDef, ComponentQuery, ComponentTemplate, DirectiveDef, DirectiveDefList, HostBindingsFunction, PipeDef, PipeDefList} from './definition';
|
||||||
import {TElementNode, TNode, TViewNode} from './node';
|
import {TElementNode, TNode, TViewNode} from './node';
|
||||||
|
import {PlayerHandler} from './player';
|
||||||
import {LQueries} from './query';
|
import {LQueries} from './query';
|
||||||
import {RElement, Renderer3} from './renderer';
|
import {RElement, Renderer3} from './renderer';
|
||||||
import {StylingContext} from './styling';
|
import {StylingContext} from './styling';
|
||||||
|
|
||||||
|
|
||||||
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
|
/** Size of LViewData's header. Necessary to adjust for it when setting slots. */
|
||||||
export const HEADER_OFFSET = 17;
|
export const HEADER_OFFSET = 17;
|
||||||
|
|
||||||
|
|
|
@ -5,43 +5,60 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {getContext} from './context_discovery';
|
import './ng_dev_mode';
|
||||||
import {scheduleTick} from './instructions';
|
|
||||||
import {ComponentInstance, DirectiveInstance, PlayState, Player} from './interfaces/player';
|
|
||||||
import {RootContextFlags} from './interfaces/view';
|
|
||||||
import {CorePlayerHandler} from './styling/core_player_handler';
|
|
||||||
import {getOrCreatePlayerContext} from './styling/util';
|
|
||||||
import {getRootContext} from './util';
|
|
||||||
|
|
||||||
|
import {getContext} from './context_discovery';
|
||||||
|
import {getRootContext} from './discovery_utils';
|
||||||
|
import {scheduleTick} from './instructions';
|
||||||
|
import {ComponentInstance, DirectiveInstance, Player} from './interfaces/player';
|
||||||
|
import {HEADER_OFFSET, RootContextFlags} from './interfaces/view';
|
||||||
|
import {addPlayerInternal, getOrCreatePlayerContext, getPlayerContext, getPlayersInternal, getStylingContext, throwInvalidRefError} from './styling/util';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a player to an element, directive or component instance that will later be
|
||||||
|
* animated once change detection has passed.
|
||||||
|
*
|
||||||
|
* When a player is added to a reference it will stay active until `player.destroy()`
|
||||||
|
* is called. Once called then the player will be removed from the active players
|
||||||
|
* present on the associated ref instance.
|
||||||
|
*
|
||||||
|
* To get a list of all the active players on an element see [getPlayers].
|
||||||
|
*
|
||||||
|
* @param ref The element, directive or component that the player will be placed on.
|
||||||
|
* @param player The player that will be triggered to play once change detection has run.
|
||||||
|
*/
|
||||||
export function addPlayer(
|
export function addPlayer(
|
||||||
ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void {
|
ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void {
|
||||||
const elementContext = getContext(ref) !;
|
const context = getContext(ref);
|
||||||
const animationContext = getOrCreatePlayerContext(elementContext.native, elementContext) !;
|
if (!context) {
|
||||||
animationContext.push(player);
|
ngDevMode && throwInvalidRefError();
|
||||||
|
return;
|
||||||
player.addEventListener(PlayState.Destroyed, () => {
|
|
||||||
const index = animationContext.indexOf(player);
|
|
||||||
if (index >= 0) {
|
|
||||||
animationContext.splice(index, 1);
|
|
||||||
}
|
|
||||||
player.destroy();
|
|
||||||
});
|
|
||||||
|
|
||||||
const rootContext = getRootContext(elementContext.lViewData);
|
|
||||||
const playerHandler =
|
|
||||||
rootContext.playerHandler || (rootContext.playerHandler = new CorePlayerHandler());
|
|
||||||
playerHandler.queuePlayer(player, ref);
|
|
||||||
|
|
||||||
const nothingScheduled = rootContext.flags === RootContextFlags.Empty;
|
|
||||||
|
|
||||||
// change detection may or may not happen therefore
|
|
||||||
// the core code needs to be kicked off to flush the animations
|
|
||||||
rootContext.flags |= RootContextFlags.FlushPlayers;
|
|
||||||
if (nothingScheduled) {
|
|
||||||
scheduleTick(rootContext);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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(rootContext, RootContextFlags.FlushPlayers);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns a list of all the active players present on the provided ref instance (which can
|
||||||
|
* be an instance of a directive, component or element).
|
||||||
|
*
|
||||||
|
* This function will only return players that have been added to the ref instance using
|
||||||
|
* `addPlayer` or any players that are active through any template styling bindings
|
||||||
|
* (`[style]`, `[style.prop]`, `[class]` and `[class.name]`).
|
||||||
|
*/
|
||||||
export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] {
|
export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] {
|
||||||
return getOrCreatePlayerContext(ref);
|
const context = getContext(ref);
|
||||||
|
if (!context) {
|
||||||
|
ngDevMode && throwInvalidRefError();
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
|
||||||
|
const stylingContext = getStylingContext(context.nodeIndex - HEADER_OFFSET, context.lViewData);
|
||||||
|
const playerContext = stylingContext ? getPlayerContext(stylingContext) : null;
|
||||||
|
return playerContext ? getPlayersInternal(playerContext) : [];
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,13 +5,20 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||||
import {InitialStylingFlags} from '../interfaces/definition';
|
import {InitialStylingFlags} from '../interfaces/definition';
|
||||||
|
import {BindingStore, BindingType, Player, PlayerBuilder, PlayerFactory, PlayerIndex} from '../interfaces/player';
|
||||||
import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer';
|
import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer';
|
||||||
import {InitialStyles, StylingContext, StylingFlags, StylingIndex} from '../interfaces/styling';
|
import {InitialStyles, StylingContext, StylingFlags, StylingIndex} from '../interfaces/styling';
|
||||||
|
import {LViewData, RootContext} from '../interfaces/view';
|
||||||
|
import {getRootContext} from '../util';
|
||||||
|
|
||||||
|
import {BoundPlayerFactory} from './player_factory';
|
||||||
|
import {addPlayerInternal, allocPlayerContext, createEmptyStylingContext, getPlayerContext} from './util';
|
||||||
|
|
||||||
|
const EMPTY_ARR: any[] = [];
|
||||||
|
const EMPTY_OBJ: {[key: string]: any} = {};
|
||||||
|
|
||||||
import {EMPTY_ARR, EMPTY_OBJ, createEmptyStylingContext} from './util';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a styling context template where styling information is stored.
|
* Creates a styling context template where styling information is stored.
|
||||||
|
@ -124,12 +131,14 @@ export function createStylingContextTemplate(
|
||||||
setFlag(context, indexForSingle, pointers(initialFlag, indexForInitial, indexForMulti));
|
setFlag(context, indexForSingle, pointers(initialFlag, indexForInitial, indexForMulti));
|
||||||
setProp(context, indexForSingle, prop);
|
setProp(context, indexForSingle, prop);
|
||||||
setValue(context, indexForSingle, null);
|
setValue(context, indexForSingle, null);
|
||||||
|
setPlayerBuilderIndex(context, indexForSingle, 0);
|
||||||
|
|
||||||
const flagForMulti =
|
const flagForMulti =
|
||||||
initialFlag | (initialValue !== null ? StylingFlags.Dirty : StylingFlags.None);
|
initialFlag | (initialValue !== null ? StylingFlags.Dirty : StylingFlags.None);
|
||||||
setFlag(context, indexForMulti, pointers(flagForMulti, indexForInitial, indexForSingle));
|
setFlag(context, indexForMulti, pointers(flagForMulti, indexForInitial, indexForSingle));
|
||||||
setProp(context, indexForMulti, prop);
|
setProp(context, indexForMulti, prop);
|
||||||
setValue(context, indexForMulti, null);
|
setValue(context, indexForMulti, null);
|
||||||
|
setPlayerBuilderIndex(context, indexForMulti, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
// there is no initial value flag for the master index since it doesn't
|
// there is no initial value flag for the master index since it doesn't
|
||||||
|
@ -142,7 +151,7 @@ export function createStylingContextTemplate(
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets and resolves all `multi` styling on an `StylingContext` so that they can be
|
* Sets and resolves all `multi` styling on an `StylingContext` so that they can be
|
||||||
* applied to the element once `renderStyling` is called.
|
* applied to the element once `renderStyleAndClassBindings` is called.
|
||||||
*
|
*
|
||||||
* All missing styles/class (any values that are not provided in the new `styles`
|
* All missing styles/class (any values that are not provided in the new `styles`
|
||||||
* or `classes` params) will resolve to `null` within their respective positions
|
* or `classes` params) will resolve to `null` within their respective positions
|
||||||
|
@ -150,43 +159,73 @@ export function createStylingContextTemplate(
|
||||||
*
|
*
|
||||||
* @param context The styling context that will be updated with the
|
* @param context The styling context that will be updated with the
|
||||||
* newly provided style values.
|
* newly provided style values.
|
||||||
* @param classes The key/value map of CSS class names that will be used for the update.
|
* @param classesInput The key/value map of CSS class names that will be used for the update.
|
||||||
* @param styles The key/value map of CSS styles that will be used for the update.
|
* @param stylesInput The key/value map of CSS styles that will be used for the update.
|
||||||
*/
|
*/
|
||||||
export function updateStylingMap(
|
export function updateStylingMap(
|
||||||
context: StylingContext, classes: {[key: string]: any} | string | null,
|
context: StylingContext, classesInput: {[key: string]: any} | string |
|
||||||
styles?: {[key: string]: any} | null): void {
|
BoundPlayerFactory<null|string|{[key: string]: any}>| null,
|
||||||
styles = styles || null;
|
stylesInput?: {[key: string]: any} | BoundPlayerFactory<null|{[key: string]: any}>|
|
||||||
|
null): void {
|
||||||
|
stylesInput = stylesInput || null;
|
||||||
|
|
||||||
|
const element = context[StylingIndex.ElementPosition] !as HTMLElement;
|
||||||
|
const classesPlayerBuilder = classesInput instanceof BoundPlayerFactory ?
|
||||||
|
new ClassAndStylePlayerBuilder(classesInput as any, element, BindingType.Class) :
|
||||||
|
null;
|
||||||
|
const stylesPlayerBuilder = stylesInput instanceof BoundPlayerFactory ?
|
||||||
|
new ClassAndStylePlayerBuilder(stylesInput as any, element, BindingType.Style) :
|
||||||
|
null;
|
||||||
|
|
||||||
|
const classesValue = classesPlayerBuilder ?
|
||||||
|
(classesInput as BoundPlayerFactory<{[key: string]: any}|string>) !.value :
|
||||||
|
classesInput;
|
||||||
|
const stylesValue = stylesPlayerBuilder ? stylesInput !.value : stylesInput;
|
||||||
|
|
||||||
// early exit (this is what's done to avoid using ctx.bind() to cache the value)
|
// early exit (this is what's done to avoid using ctx.bind() to cache the value)
|
||||||
const ignoreAllClassUpdates = classes === context[StylingIndex.PreviousMultiClassValue];
|
const ignoreAllClassUpdates = classesValue === context[StylingIndex.PreviousMultiClassValue];
|
||||||
const ignoreAllStyleUpdates = styles === context[StylingIndex.PreviousMultiStyleValue];
|
const ignoreAllStyleUpdates = stylesValue === context[StylingIndex.PreviousMultiStyleValue];
|
||||||
if (ignoreAllClassUpdates && ignoreAllStyleUpdates) return;
|
if (ignoreAllClassUpdates && ignoreAllStyleUpdates) return;
|
||||||
|
|
||||||
|
context[StylingIndex.PreviousMultiClassValue] = classesValue;
|
||||||
|
context[StylingIndex.PreviousMultiStyleValue] = stylesValue;
|
||||||
|
|
||||||
let classNames: string[] = EMPTY_ARR;
|
let classNames: string[] = EMPTY_ARR;
|
||||||
let applyAllClasses = false;
|
let applyAllClasses = false;
|
||||||
|
let playerBuildersAreDirty = false;
|
||||||
|
|
||||||
|
const classesPlayerBuilderIndex =
|
||||||
|
classesPlayerBuilder ? PlayerIndex.ClassMapPlayerBuilderPosition : 0;
|
||||||
|
if (hasPlayerBuilderChanged(
|
||||||
|
context, classesPlayerBuilder, PlayerIndex.ClassMapPlayerBuilderPosition)) {
|
||||||
|
setPlayerBuilder(context, classesPlayerBuilder, PlayerIndex.ClassMapPlayerBuilderPosition);
|
||||||
|
playerBuildersAreDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const stylesPlayerBuilderIndex =
|
||||||
|
stylesPlayerBuilder ? PlayerIndex.StyleMapPlayerBuilderPosition : 0;
|
||||||
|
if (hasPlayerBuilderChanged(
|
||||||
|
context, stylesPlayerBuilder, PlayerIndex.StyleMapPlayerBuilderPosition)) {
|
||||||
|
setPlayerBuilder(context, stylesPlayerBuilder, PlayerIndex.StyleMapPlayerBuilderPosition);
|
||||||
|
playerBuildersAreDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
// each time a string-based value pops up then it shouldn't require a deep
|
// each time a string-based value pops up then it shouldn't require a deep
|
||||||
// check of what's changed.
|
// check of what's changed.
|
||||||
if (!ignoreAllClassUpdates) {
|
if (!ignoreAllClassUpdates) {
|
||||||
context[StylingIndex.PreviousMultiClassValue] = classes;
|
if (typeof classesValue == 'string') {
|
||||||
if (typeof classes == 'string') {
|
classNames = classesValue.split(/\s+/);
|
||||||
classNames = classes.split(/\s+/);
|
|
||||||
// this boolean is used to avoid having to create a key/value map of `true` values
|
// this boolean is used to avoid having to create a key/value map of `true` values
|
||||||
// since a classname string implies that all those classes are added
|
// since a classname string implies that all those classes are added
|
||||||
applyAllClasses = true;
|
applyAllClasses = true;
|
||||||
} else {
|
} else {
|
||||||
classNames = classes ? Object.keys(classes) : EMPTY_ARR;
|
classNames = classesValue ? Object.keys(classesValue) : EMPTY_ARR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
classes = (classes || EMPTY_OBJ) as{[key: string]: any};
|
const classes = (classesValue || EMPTY_OBJ) as{[key: string]: any};
|
||||||
|
const styleProps = stylesValue ? Object.keys(stylesValue) : EMPTY_ARR;
|
||||||
if (!ignoreAllStyleUpdates) {
|
const styles = stylesValue || EMPTY_OBJ;
|
||||||
context[StylingIndex.PreviousMultiStyleValue] = styles;
|
|
||||||
}
|
|
||||||
|
|
||||||
const styleProps = styles ? Object.keys(styles) : EMPTY_ARR;
|
|
||||||
styles = styles || EMPTY_OBJ;
|
|
||||||
|
|
||||||
const classesStartIndex = styleProps.length;
|
const classesStartIndex = styleProps.length;
|
||||||
const multiStartIndex = getMultiStartIndex(context);
|
const multiStartIndex = getMultiStartIndex(context);
|
||||||
|
@ -213,13 +252,18 @@ export function updateStylingMap(
|
||||||
isClassBased ? classNames[adjustedPropIndex] : styleProps[adjustedPropIndex];
|
isClassBased ? classNames[adjustedPropIndex] : styleProps[adjustedPropIndex];
|
||||||
const newValue: string|boolean =
|
const newValue: string|boolean =
|
||||||
isClassBased ? (applyAllClasses ? true : classes[newProp]) : styles[newProp];
|
isClassBased ? (applyAllClasses ? true : classes[newProp]) : styles[newProp];
|
||||||
|
const playerBuilderIndex =
|
||||||
|
isClassBased ? classesPlayerBuilderIndex : stylesPlayerBuilderIndex;
|
||||||
|
|
||||||
const prop = getProp(context, ctxIndex);
|
const prop = getProp(context, ctxIndex);
|
||||||
if (prop === newProp) {
|
if (prop === newProp) {
|
||||||
const value = getValue(context, ctxIndex);
|
const value = getValue(context, ctxIndex);
|
||||||
const flag = getPointers(context, ctxIndex);
|
const flag = getPointers(context, ctxIndex);
|
||||||
|
setPlayerBuilderIndex(context, ctxIndex, playerBuilderIndex);
|
||||||
|
|
||||||
if (hasValueChanged(flag, value, newValue)) {
|
if (hasValueChanged(flag, value, newValue)) {
|
||||||
setValue(context, ctxIndex, newValue);
|
setValue(context, ctxIndex, newValue);
|
||||||
|
playerBuildersAreDirty = playerBuildersAreDirty || !!playerBuilderIndex;
|
||||||
|
|
||||||
const initialValue = getInitialValue(context, flag);
|
const initialValue = getInitialValue(context, flag);
|
||||||
|
|
||||||
|
@ -242,13 +286,16 @@ export function updateStylingMap(
|
||||||
setValue(context, ctxIndex, newValue);
|
setValue(context, ctxIndex, newValue);
|
||||||
if (hasValueChanged(flagToCompare, initialValue, newValue)) {
|
if (hasValueChanged(flagToCompare, initialValue, newValue)) {
|
||||||
setDirty(context, ctxIndex, true);
|
setDirty(context, ctxIndex, true);
|
||||||
|
playerBuildersAreDirty = playerBuildersAreDirty || !!playerBuilderIndex;
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// we only care to do this if the insertion is in the middle
|
// we only care to do this if the insertion is in the middle
|
||||||
const newFlag = prepareInitialFlag(newProp, isClassBased, getStyleSanitizer(context));
|
const newFlag = prepareInitialFlag(newProp, isClassBased, getStyleSanitizer(context));
|
||||||
insertNewMultiProperty(context, ctxIndex, isClassBased, newProp, newFlag, newValue);
|
playerBuildersAreDirty = playerBuildersAreDirty || !!playerBuilderIndex;
|
||||||
|
insertNewMultiProperty(
|
||||||
|
context, ctxIndex, isClassBased, newProp, newFlag, newValue, playerBuilderIndex);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -272,6 +319,13 @@ export function updateStylingMap(
|
||||||
if (doRemoveValue) {
|
if (doRemoveValue) {
|
||||||
setDirty(context, ctxIndex, true);
|
setDirty(context, ctxIndex, true);
|
||||||
setValue(context, ctxIndex, null);
|
setValue(context, ctxIndex, null);
|
||||||
|
|
||||||
|
// we keep the player factory the same so that the `nulled` value can
|
||||||
|
// be instructed into the player because removing a style and/or a class
|
||||||
|
// is a valid animation player instruction.
|
||||||
|
const playerBuilderIndex =
|
||||||
|
isClassBased ? classesPlayerBuilderIndex : stylesPlayerBuilderIndex;
|
||||||
|
setPlayerBuilderIndex(context, ctxIndex, playerBuilderIndex);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -292,7 +346,9 @@ export function updateStylingMap(
|
||||||
const value: string|boolean =
|
const value: string|boolean =
|
||||||
isClassBased ? (applyAllClasses ? true : classes[prop]) : styles[prop];
|
isClassBased ? (applyAllClasses ? true : classes[prop]) : styles[prop];
|
||||||
const flag = prepareInitialFlag(prop, isClassBased, sanitizer) | StylingFlags.Dirty;
|
const flag = prepareInitialFlag(prop, isClassBased, sanitizer) | StylingFlags.Dirty;
|
||||||
context.push(flag, prop, value);
|
const playerBuilderIndex =
|
||||||
|
isClassBased ? classesPlayerBuilderIndex : stylesPlayerBuilderIndex;
|
||||||
|
context.push(flag, prop, value, playerBuilderIndex);
|
||||||
dirty = true;
|
dirty = true;
|
||||||
}
|
}
|
||||||
propIndex++;
|
propIndex++;
|
||||||
|
@ -301,11 +357,15 @@ export function updateStylingMap(
|
||||||
if (dirty) {
|
if (dirty) {
|
||||||
setContextDirty(context, true);
|
setContextDirty(context, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (playerBuildersAreDirty) {
|
||||||
|
setContextPlayersDirty(context, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Sets and resolves a single styling property/value on the provided `StylingContext` so
|
* Sets and resolves a single styling property/value on the provided `StylingContext` so
|
||||||
* that they can be applied to the element once `renderStyling` is called.
|
* that they can be applied to the element once `renderStyleAndClassBindings` is called.
|
||||||
*
|
*
|
||||||
* Note that prop-level styling values are considered higher priority than any styling that
|
* Note that prop-level styling values are considered higher priority than any styling that
|
||||||
* has been applied using `updateStylingMap`, therefore, when styling values are rendered
|
* has been applied using `updateStylingMap`, therefore, when styling values are rendered
|
||||||
|
@ -318,13 +378,34 @@ export function updateStylingMap(
|
||||||
* @param value The CSS style value that will be assigned
|
* @param value The CSS style value that will be assigned
|
||||||
*/
|
*/
|
||||||
export function updateStyleProp(
|
export function updateStyleProp(
|
||||||
context: StylingContext, index: number, value: string | boolean | null): void {
|
context: StylingContext, index: number,
|
||||||
|
input: string | boolean | null | BoundPlayerFactory<string|boolean|null>): void {
|
||||||
const singleIndex = StylingIndex.SingleStylesStartPosition + index * StylingIndex.Size;
|
const singleIndex = StylingIndex.SingleStylesStartPosition + index * StylingIndex.Size;
|
||||||
const currValue = getValue(context, singleIndex);
|
const currValue = getValue(context, singleIndex);
|
||||||
const currFlag = getPointers(context, singleIndex);
|
const currFlag = getPointers(context, singleIndex);
|
||||||
|
const value: string|boolean|null = (input instanceof BoundPlayerFactory) ? input.value : input;
|
||||||
|
|
||||||
// didn't change ... nothing to make a note of
|
// didn't change ... nothing to make a note of
|
||||||
if (hasValueChanged(currFlag, currValue, value)) {
|
if (hasValueChanged(currFlag, currValue, value)) {
|
||||||
|
const isClassBased = (currFlag & StylingFlags.Class) === StylingFlags.Class;
|
||||||
|
const element = context[StylingIndex.ElementPosition] !as HTMLElement;
|
||||||
|
const playerBuilder = input instanceof BoundPlayerFactory ?
|
||||||
|
new ClassAndStylePlayerBuilder(
|
||||||
|
input as any, element, isClassBased ? BindingType.Class : BindingType.Style) :
|
||||||
|
null;
|
||||||
|
const value = (playerBuilder ? (input as BoundPlayerFactory<any>).value : input) as string |
|
||||||
|
boolean | null;
|
||||||
|
const currPlayerIndex = getPlayerBuilderIndex(context, singleIndex);
|
||||||
|
|
||||||
|
let playerBuildersAreDirty = false;
|
||||||
|
let playerBuilderIndex = playerBuilder ? currPlayerIndex : 0;
|
||||||
|
if (hasPlayerBuilderChanged(context, playerBuilder, currPlayerIndex)) {
|
||||||
|
const newIndex = setPlayerBuilder(context, playerBuilder, currPlayerIndex);
|
||||||
|
playerBuilderIndex = playerBuilder ? newIndex : 0;
|
||||||
|
setPlayerBuilderIndex(context, singleIndex, playerBuilderIndex);
|
||||||
|
playerBuildersAreDirty = true;
|
||||||
|
}
|
||||||
|
|
||||||
// the value will always get updated (even if the dirty flag is skipped)
|
// the value will always get updated (even if the dirty flag is skipped)
|
||||||
setValue(context, singleIndex, value);
|
setValue(context, singleIndex, value);
|
||||||
const indexForMulti = getMultiOrSingleIndex(currFlag);
|
const indexForMulti = getMultiOrSingleIndex(currFlag);
|
||||||
|
@ -335,8 +416,6 @@ export function updateStyleProp(
|
||||||
let multiDirty = false;
|
let multiDirty = false;
|
||||||
let singleDirty = true;
|
let singleDirty = true;
|
||||||
|
|
||||||
const isClassBased = (currFlag & StylingFlags.Class) === StylingFlags.Class;
|
|
||||||
|
|
||||||
// only when the value is set to `null` should the multi-value get flagged
|
// only when the value is set to `null` should the multi-value get flagged
|
||||||
if (!valueExists(value, isClassBased) && valueExists(valueForMulti, isClassBased)) {
|
if (!valueExists(value, isClassBased) && valueExists(valueForMulti, isClassBased)) {
|
||||||
multiDirty = true;
|
multiDirty = true;
|
||||||
|
@ -347,6 +426,10 @@ export function updateStyleProp(
|
||||||
setDirty(context, singleIndex, singleDirty);
|
setDirty(context, singleIndex, singleDirty);
|
||||||
setContextDirty(context, true);
|
setContextDirty(context, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (playerBuildersAreDirty) {
|
||||||
|
setContextPlayersDirty(context, true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -360,7 +443,8 @@ export function updateStyleProp(
|
||||||
* @param addOrRemove Whether or not to add or remove the CSS class
|
* @param addOrRemove Whether or not to add or remove the CSS class
|
||||||
*/
|
*/
|
||||||
export function updateClassProp(
|
export function updateClassProp(
|
||||||
context: StylingContext, index: number, addOrRemove: boolean): void {
|
context: StylingContext, index: number,
|
||||||
|
addOrRemove: boolean | BoundPlayerFactory<boolean>): void {
|
||||||
const adjustedIndex = index + context[StylingIndex.ClassOffsetPosition];
|
const adjustedIndex = index + context[StylingIndex.ClassOffsetPosition];
|
||||||
updateStyleProp(context, adjustedIndex, addOrRemove);
|
updateStyleProp(context, adjustedIndex, addOrRemove);
|
||||||
}
|
}
|
||||||
|
@ -378,15 +462,19 @@ export function updateClassProp(
|
||||||
* @param context The styling context that will be used to determine
|
* @param context The styling context that will be used to determine
|
||||||
* what styles will be rendered
|
* what styles will be rendered
|
||||||
* @param renderer the renderer that will be used to apply the styling
|
* @param renderer the renderer that will be used to apply the styling
|
||||||
* @param styleStore if provided, the updated style values will be applied
|
* @param classesStore if provided, the updated class values will be applied
|
||||||
* to this key/value map instead of being renderered via the renderer.
|
* to this key/value map instead of being renderered via the renderer.
|
||||||
* @param classStore if provided, the updated class values will be applied
|
* @param stylesStore if provided, the updated style values will be applied
|
||||||
* to this key/value map instead of being renderered via the renderer.
|
* to this key/value map instead of being renderered via the renderer.
|
||||||
|
* @returns number the total amount of players that got queued for animation (if any)
|
||||||
*/
|
*/
|
||||||
export function renderStyling(
|
export function renderStyleAndClassBindings(
|
||||||
context: StylingContext, renderer: Renderer3, styleStore?: {[key: string]: any},
|
context: StylingContext, renderer: Renderer3, rootOrView: RootContext | LViewData,
|
||||||
classStore?: {[key: string]: boolean}) {
|
classesStore?: BindingStore | null, stylesStore?: BindingStore | null): number {
|
||||||
|
let totalPlayersQueued = 0;
|
||||||
if (isContextDirty(context)) {
|
if (isContextDirty(context)) {
|
||||||
|
const flushPlayerBuilders: any =
|
||||||
|
context[StylingIndex.MasterFlagPosition] & StylingFlags.PlayerBuildersDirty;
|
||||||
const native = context[StylingIndex.ElementPosition] !;
|
const native = context[StylingIndex.ElementPosition] !;
|
||||||
const multiStartIndex = getMultiStartIndex(context);
|
const multiStartIndex = getMultiStartIndex(context);
|
||||||
const styleSanitizer = getStyleSanitizer(context);
|
const styleSanitizer = getStyleSanitizer(context);
|
||||||
|
@ -397,6 +485,7 @@ export function renderStyling(
|
||||||
const prop = getProp(context, i);
|
const prop = getProp(context, i);
|
||||||
const value = getValue(context, i);
|
const value = getValue(context, i);
|
||||||
const flag = getPointers(context, i);
|
const flag = getPointers(context, i);
|
||||||
|
const playerBuilder = getPlayerBuilder(context, i);
|
||||||
const isClassBased = flag & StylingFlags.Class ? true : false;
|
const isClassBased = flag & StylingFlags.Class ? true : false;
|
||||||
const isInSingleRegion = i < multiStartIndex;
|
const isInSingleRegion = i < multiStartIndex;
|
||||||
|
|
||||||
|
@ -422,17 +511,52 @@ export function renderStyling(
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isClassBased) {
|
if (isClassBased) {
|
||||||
setClass(native, prop, valueToApply ? true : false, renderer, classStore);
|
setClass(
|
||||||
|
native, prop, valueToApply ? true : false, renderer, classesStore, playerBuilder);
|
||||||
} else {
|
} else {
|
||||||
const sanitizer = (flag & StylingFlags.Sanitize) ? styleSanitizer : null;
|
const sanitizer = (flag & StylingFlags.Sanitize) ? styleSanitizer : null;
|
||||||
setStyle(native, prop, valueToApply as string | null, renderer, sanitizer, styleStore);
|
setStyle(
|
||||||
|
native, prop, valueToApply as string | null, renderer, sanitizer, stylesStore,
|
||||||
|
playerBuilder);
|
||||||
}
|
}
|
||||||
setDirty(context, i, false);
|
setDirty(context, i, false);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (flushPlayerBuilders) {
|
||||||
|
const rootContext =
|
||||||
|
Array.isArray(rootOrView) ? getRootContext(rootOrView) : rootOrView as RootContext;
|
||||||
|
const playerContext = getPlayerContext(context) !;
|
||||||
|
const playersStartIndex = playerContext[PlayerIndex.NonBuilderPlayersStart];
|
||||||
|
for (let i = PlayerIndex.PlayerBuildersStartPosition; i < playersStartIndex;
|
||||||
|
i += PlayerIndex.PlayerAndPlayerBuildersTupleSize) {
|
||||||
|
const builder = playerContext[i] as ClassAndStylePlayerBuilder<any>| null;
|
||||||
|
const playerInsertionIndex = i + PlayerIndex.PlayerOffsetPosition;
|
||||||
|
const oldPlayer = playerContext[playerInsertionIndex] as Player | null;
|
||||||
|
if (builder) {
|
||||||
|
const player = builder.buildPlayer(oldPlayer);
|
||||||
|
if (player !== undefined) {
|
||||||
|
if (player != null) {
|
||||||
|
const wasQueued = addPlayerInternal(
|
||||||
|
playerContext, rootContext, native as HTMLElement, player, playerInsertionIndex);
|
||||||
|
wasQueued && totalPlayersQueued++;
|
||||||
|
}
|
||||||
|
if (oldPlayer) {
|
||||||
|
oldPlayer.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (oldPlayer) {
|
||||||
|
// the player builder has been removed ... therefore we should delete the associated
|
||||||
|
// player
|
||||||
|
oldPlayer.destroy();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setContextPlayersDirty(context, false);
|
||||||
|
}
|
||||||
setContextDirty(context, false);
|
setContextDirty(context, false);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return totalPlayersQueued;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -449,10 +573,16 @@ export function renderStyling(
|
||||||
*/
|
*/
|
||||||
function setStyle(
|
function setStyle(
|
||||||
native: any, prop: string, value: string | null, renderer: Renderer3,
|
native: any, prop: string, value: string | null, renderer: Renderer3,
|
||||||
sanitizer: StyleSanitizeFn | null, store?: {[key: string]: any}) {
|
sanitizer: StyleSanitizeFn | null, store?: BindingStore | null,
|
||||||
|
playerBuilder?: ClassAndStylePlayerBuilder<any>| null) {
|
||||||
value = sanitizer && value ? sanitizer(prop, value) : value;
|
value = sanitizer && value ? sanitizer(prop, value) : value;
|
||||||
if (store) {
|
if (store || playerBuilder) {
|
||||||
store[prop] = value;
|
if (store) {
|
||||||
|
store.setValue(prop, value);
|
||||||
|
}
|
||||||
|
if (playerBuilder) {
|
||||||
|
playerBuilder.setValue(prop, value);
|
||||||
|
}
|
||||||
} else if (value) {
|
} else if (value) {
|
||||||
ngDevMode && ngDevMode.rendererSetStyle++;
|
ngDevMode && ngDevMode.rendererSetStyle++;
|
||||||
isProceduralRenderer(renderer) ?
|
isProceduralRenderer(renderer) ?
|
||||||
|
@ -479,10 +609,15 @@ function setStyle(
|
||||||
* @param store an optional key/value map that will be used as a context to render styles on
|
* @param store an optional key/value map that will be used as a context to render styles on
|
||||||
*/
|
*/
|
||||||
function setClass(
|
function setClass(
|
||||||
native: any, className: string, add: boolean, renderer: Renderer3,
|
native: any, className: string, add: boolean, renderer: Renderer3, store?: BindingStore | null,
|
||||||
store?: {[key: string]: boolean}) {
|
playerBuilder?: ClassAndStylePlayerBuilder<any>| null) {
|
||||||
if (store) {
|
if (store || playerBuilder) {
|
||||||
store[className] = add;
|
if (store) {
|
||||||
|
store.setValue(className, add);
|
||||||
|
}
|
||||||
|
if (playerBuilder) {
|
||||||
|
playerBuilder.setValue(className, add);
|
||||||
|
}
|
||||||
} else if (add) {
|
} else if (add) {
|
||||||
ngDevMode && ngDevMode.rendererAddClass++;
|
ngDevMode && ngDevMode.rendererAddClass++;
|
||||||
isProceduralRenderer(renderer) ? renderer.addClass(native, className) :
|
isProceduralRenderer(renderer) ? renderer.addClass(native, className) :
|
||||||
|
@ -558,6 +693,54 @@ function setValue(context: StylingContext, index: number, value: string | null |
|
||||||
context[index + StylingIndex.ValueOffset] = value;
|
context[index + StylingIndex.ValueOffset] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function hasPlayerBuilderChanged(
|
||||||
|
context: StylingContext, builder: ClassAndStylePlayerBuilder<any>| null, index: number) {
|
||||||
|
const playerContext = context[StylingIndex.PlayerContext] !;
|
||||||
|
if (builder) {
|
||||||
|
if (!playerContext || index === 0) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (!playerContext) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return playerContext[index] !== builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPlayerBuilder(
|
||||||
|
context: StylingContext, builder: ClassAndStylePlayerBuilder<any>| null,
|
||||||
|
insertionIndex: number): number {
|
||||||
|
let playerContext = context[StylingIndex.PlayerContext] || allocPlayerContext(context);
|
||||||
|
if (insertionIndex > 0) {
|
||||||
|
playerContext[insertionIndex] = builder;
|
||||||
|
} else {
|
||||||
|
insertionIndex = playerContext[PlayerIndex.NonBuilderPlayersStart];
|
||||||
|
playerContext.splice(insertionIndex, 0, builder, null);
|
||||||
|
playerContext[PlayerIndex.NonBuilderPlayersStart] +=
|
||||||
|
PlayerIndex.PlayerAndPlayerBuildersTupleSize;
|
||||||
|
}
|
||||||
|
return insertionIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setPlayerBuilderIndex(context: StylingContext, index: number, playerBuilderIndex: number) {
|
||||||
|
context[index + StylingIndex.PlayerBuilderIndexOffset] = playerBuilderIndex;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlayerBuilderIndex(context: StylingContext, index: number): number {
|
||||||
|
return (context[index + StylingIndex.PlayerBuilderIndexOffset] as number) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
function getPlayerBuilder(context: StylingContext, index: number): ClassAndStylePlayerBuilder<any>|
|
||||||
|
null {
|
||||||
|
const playerBuilderIndex = getPlayerBuilderIndex(context, index);
|
||||||
|
if (playerBuilderIndex) {
|
||||||
|
const playerContext = context[StylingIndex.PlayerContext];
|
||||||
|
if (playerContext) {
|
||||||
|
return playerContext[playerBuilderIndex] as ClassAndStylePlayerBuilder<any>| null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
function setFlag(context: StylingContext, index: number, flag: number) {
|
function setFlag(context: StylingContext, index: number, flag: number) {
|
||||||
const adjustedIndex =
|
const adjustedIndex =
|
||||||
index === StylingIndex.MasterFlagPosition ? index : (index + StylingIndex.FlagsOffset);
|
index === StylingIndex.MasterFlagPosition ? index : (index + StylingIndex.FlagsOffset);
|
||||||
|
@ -586,6 +769,14 @@ export function setContextDirty(context: StylingContext, isDirtyYes: boolean): v
|
||||||
setDirty(context, StylingIndex.MasterFlagPosition, isDirtyYes);
|
setDirty(context, StylingIndex.MasterFlagPosition, isDirtyYes);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function setContextPlayersDirty(context: StylingContext, isDirtyYes: boolean): void {
|
||||||
|
if (isDirtyYes) {
|
||||||
|
(context[StylingIndex.MasterFlagPosition] as number) |= StylingFlags.PlayerBuildersDirty;
|
||||||
|
} else {
|
||||||
|
(context[StylingIndex.MasterFlagPosition] as number) &= ~StylingFlags.PlayerBuildersDirty;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function findEntryPositionByProp(
|
function findEntryPositionByProp(
|
||||||
context: StylingContext, prop: string, startIndex?: number): number {
|
context: StylingContext, prop: string, startIndex?: number): number {
|
||||||
for (let i = (startIndex || 0) + StylingIndex.PropertyOffset; i < context.length;
|
for (let i = (startIndex || 0) + StylingIndex.PropertyOffset; i < context.length;
|
||||||
|
@ -602,6 +793,7 @@ function swapMultiContextEntries(context: StylingContext, indexA: number, indexB
|
||||||
const tmpValue = getValue(context, indexA);
|
const tmpValue = getValue(context, indexA);
|
||||||
const tmpProp = getProp(context, indexA);
|
const tmpProp = getProp(context, indexA);
|
||||||
const tmpFlag = getPointers(context, indexA);
|
const tmpFlag = getPointers(context, indexA);
|
||||||
|
const tmpPlayerBuilderIndex = getPlayerBuilderIndex(context, indexA);
|
||||||
|
|
||||||
let flagA = tmpFlag;
|
let flagA = tmpFlag;
|
||||||
let flagB = getPointers(context, indexB);
|
let flagB = getPointers(context, indexB);
|
||||||
|
@ -623,10 +815,12 @@ function swapMultiContextEntries(context: StylingContext, indexA: number, indexB
|
||||||
setValue(context, indexA, getValue(context, indexB));
|
setValue(context, indexA, getValue(context, indexB));
|
||||||
setProp(context, indexA, getProp(context, indexB));
|
setProp(context, indexA, getProp(context, indexB));
|
||||||
setFlag(context, indexA, getPointers(context, indexB));
|
setFlag(context, indexA, getPointers(context, indexB));
|
||||||
|
setPlayerBuilderIndex(context, indexA, getPlayerBuilderIndex(context, indexB));
|
||||||
|
|
||||||
setValue(context, indexB, tmpValue);
|
setValue(context, indexB, tmpValue);
|
||||||
setProp(context, indexB, tmpProp);
|
setProp(context, indexB, tmpProp);
|
||||||
setFlag(context, indexB, tmpFlag);
|
setFlag(context, indexB, tmpFlag);
|
||||||
|
setPlayerBuilderIndex(context, indexB, tmpPlayerBuilderIndex);
|
||||||
}
|
}
|
||||||
|
|
||||||
function updateSinglePointerValues(context: StylingContext, indexStartPosition: number) {
|
function updateSinglePointerValues(context: StylingContext, indexStartPosition: number) {
|
||||||
|
@ -647,13 +841,13 @@ function updateSinglePointerValues(context: StylingContext, indexStartPosition:
|
||||||
|
|
||||||
function insertNewMultiProperty(
|
function insertNewMultiProperty(
|
||||||
context: StylingContext, index: number, classBased: boolean, name: string, flag: number,
|
context: StylingContext, index: number, classBased: boolean, name: string, flag: number,
|
||||||
value: string | boolean): void {
|
value: string | boolean, playerIndex: number): void {
|
||||||
const doShift = index < context.length;
|
const doShift = index < context.length;
|
||||||
|
|
||||||
// prop does not exist in the list, add it in
|
// prop does not exist in the list, add it in
|
||||||
context.splice(
|
context.splice(
|
||||||
index, 0, flag | StylingFlags.Dirty | (classBased ? StylingFlags.Class : StylingFlags.None),
|
index, 0, flag | StylingFlags.Dirty | (classBased ? StylingFlags.Class : StylingFlags.None),
|
||||||
name, value);
|
name, value, playerIndex);
|
||||||
|
|
||||||
if (doShift) {
|
if (doShift) {
|
||||||
// because the value was inserted midway into the array then we
|
// because the value was inserted midway into the array then we
|
||||||
|
@ -696,3 +890,35 @@ function hasValueChanged(
|
||||||
// everything else is safe to check with a normal equality check
|
// everything else is safe to check with a normal equality check
|
||||||
return a !== b;
|
return a !== b;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export class ClassAndStylePlayerBuilder<T> implements PlayerBuilder {
|
||||||
|
private _values: {[key: string]: string | null} = {};
|
||||||
|
private _dirty = false;
|
||||||
|
private _factory: BoundPlayerFactory<T>;
|
||||||
|
|
||||||
|
constructor(factory: PlayerFactory, private _element: HTMLElement, private _type: BindingType) {
|
||||||
|
this._factory = factory as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
setValue(prop: string, value: any) {
|
||||||
|
if (this._values[prop] !== value) {
|
||||||
|
this._values[prop] = value;
|
||||||
|
this._dirty = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
buildPlayer(currentPlayer?: Player|null): Player|undefined|null {
|
||||||
|
// if no values have been set here then this means the binding didn't
|
||||||
|
// change and therefore the binding values were not updated through
|
||||||
|
// `setValue` which means no new player will be provided.
|
||||||
|
if (this._dirty) {
|
||||||
|
const player =
|
||||||
|
this._factory.fn(this._element, this._type, this._values !, currentPlayer || null);
|
||||||
|
this._values = {};
|
||||||
|
this._dirty = false;
|
||||||
|
return player;
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
import {Player, PlayerHandler} from '../interfaces/player';
|
import {PlayState, Player, PlayerHandler} from '../interfaces/player';
|
||||||
|
|
||||||
export class CorePlayerHandler implements PlayerHandler {
|
export class CorePlayerHandler implements PlayerHandler {
|
||||||
private _players: Player[] = [];
|
private _players: Player[] = [];
|
||||||
|
@ -13,7 +13,7 @@ export class CorePlayerHandler implements PlayerHandler {
|
||||||
flushPlayers() {
|
flushPlayers() {
|
||||||
for (let i = 0; i < this._players.length; i++) {
|
for (let i = 0; i < this._players.length; i++) {
|
||||||
const player = this._players[i];
|
const player = this._players[i];
|
||||||
if (!player.parent) {
|
if (!player.parent && player.state === PlayState.Pending) {
|
||||||
player.play();
|
player.play();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
/**
|
||||||
|
* @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 {PlayerFactory, PlayerFactoryBuildFn} from '../interfaces/player';
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Combines the binding value and a factory for an animation player.
|
||||||
|
*
|
||||||
|
* Used to bind a player to an element template binding (currently only
|
||||||
|
* `[style]`, `[style.prop]`, `[class]` and `[class.name]` bindings
|
||||||
|
* supported). The provided `factoryFn` function will be run once all
|
||||||
|
* the associated bindings have been evaluated on the element and is
|
||||||
|
* designed to return a player which will then be placed on the element.
|
||||||
|
*
|
||||||
|
* @param factoryFn The function that is used to create a player
|
||||||
|
* once all the rendering-related (styling values) have been
|
||||||
|
* processed for the element binding.
|
||||||
|
* @param value The raw value that will be exposed to the binding
|
||||||
|
* so that the binding can update its internal values when
|
||||||
|
* any changes are evaluated.
|
||||||
|
*/
|
||||||
|
export function bindPlayerFactory<T>(factoryFn: PlayerFactoryBuildFn, value: T): PlayerFactory {
|
||||||
|
return new BoundPlayerFactory(factoryFn, value) as any;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class BoundPlayerFactory<T> {
|
||||||
|
'__brand__': 'Brand for PlayerFactory that nothing will match';
|
||||||
|
constructor(public fn: PlayerFactoryBuildFn, public value: T) {}
|
||||||
|
}
|
|
@ -5,19 +5,19 @@
|
||||||
* Use of this source code is governed by an MIT-style license that can be
|
* 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
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
import '../ng_dev_mode';
|
||||||
|
|
||||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||||
import {getContext} from '../context_discovery';
|
import {getContext} from '../context_discovery';
|
||||||
import {ACTIVE_INDEX, LContainer} from '../interfaces/container';
|
import {ACTIVE_INDEX, LContainer} from '../interfaces/container';
|
||||||
import {LContext} from '../interfaces/context';
|
import {LContext} from '../interfaces/context';
|
||||||
import {PlayerContext} from '../interfaces/player';
|
import {PlayState, Player, PlayerContext, PlayerIndex} from '../interfaces/player';
|
||||||
import {RElement} from '../interfaces/renderer';
|
import {RElement} from '../interfaces/renderer';
|
||||||
import {InitialStyles, StylingContext, StylingIndex} from '../interfaces/styling';
|
import {InitialStyles, StylingContext, StylingIndex} from '../interfaces/styling';
|
||||||
import {FLAGS, HEADER_OFFSET, HOST, LViewData} from '../interfaces/view';
|
import {FLAGS, HEADER_OFFSET, HOST, LViewData, RootContext} from '../interfaces/view';
|
||||||
import {getTNode} from '../util';
|
import {getTNode} from '../util';
|
||||||
|
|
||||||
export const EMPTY_ARR: any[] = [];
|
import {CorePlayerHandler} from './core_player_handler';
|
||||||
export const EMPTY_OBJ: {[key: string]: any} = {};
|
|
||||||
|
|
||||||
export function createEmptyStylingContext(
|
export function createEmptyStylingContext(
|
||||||
element?: RElement | null, sanitizer?: StyleSanitizeFn | null,
|
element?: RElement | null, sanitizer?: StyleSanitizeFn | null,
|
||||||
|
@ -75,7 +75,10 @@ export function getStylingContext(index: number, viewData: LViewData): StylingCo
|
||||||
// This is an LViewData or an LContainer
|
// This is an LViewData or an LContainer
|
||||||
const stylingTemplate = getTNode(index, viewData).stylingTemplate;
|
const stylingTemplate = getTNode(index, viewData).stylingTemplate;
|
||||||
|
|
||||||
if (wrapper !== viewData) storageIndex = HOST;
|
if (wrapper !== viewData) {
|
||||||
|
storageIndex = HOST;
|
||||||
|
}
|
||||||
|
|
||||||
return wrapper[storageIndex] = stylingTemplate ?
|
return wrapper[storageIndex] = stylingTemplate ?
|
||||||
allocStylingContext(slotValue, stylingTemplate) :
|
allocStylingContext(slotValue, stylingTemplate) :
|
||||||
createEmptyStylingContext(slotValue);
|
createEmptyStylingContext(slotValue);
|
||||||
|
@ -87,18 +90,88 @@ function isStylingContext(value: LViewData | LContainer | StylingContext) {
|
||||||
return typeof value[FLAGS] !== 'number' && typeof value[ACTIVE_INDEX] !== 'number';
|
return typeof value[FLAGS] !== 'number' && typeof value[ACTIVE_INDEX] !== 'number';
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getOrCreatePlayerContext(target: {}, context?: LContext | null): PlayerContext {
|
export function addPlayerInternal(
|
||||||
|
playerContext: PlayerContext, rootContext: RootContext, element: HTMLElement,
|
||||||
|
player: Player | null, playerContextIndex: number, ref?: any): boolean {
|
||||||
|
ref = ref || element;
|
||||||
|
if (playerContextIndex) {
|
||||||
|
playerContext[playerContextIndex] = player;
|
||||||
|
} else {
|
||||||
|
playerContext.push(player);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (player) {
|
||||||
|
player.addEventListener(PlayState.Destroyed, () => {
|
||||||
|
const index = playerContext.indexOf(player);
|
||||||
|
const nonFactoryPlayerIndex = playerContext[PlayerIndex.NonBuilderPlayersStart];
|
||||||
|
|
||||||
|
// if the player is being removed from the factory side of the context
|
||||||
|
// (which is where the [style] and [class] bindings do their thing) then
|
||||||
|
// that side of the array cannot be resized since the respective bindings
|
||||||
|
// have pointer index values that point to the associated factory instance
|
||||||
|
if (index) {
|
||||||
|
if (index < nonFactoryPlayerIndex) {
|
||||||
|
playerContext[index] = null;
|
||||||
|
} else {
|
||||||
|
playerContext.splice(index, 1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
player.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const playerHandler =
|
||||||
|
rootContext.playerHandler || (rootContext.playerHandler = new CorePlayerHandler());
|
||||||
|
playerHandler.queuePlayer(player, ref);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlayersInternal(playerContext: PlayerContext): Player[] {
|
||||||
|
const players: Player[] = [];
|
||||||
|
const nonFactoryPlayersStart = playerContext[PlayerIndex.NonBuilderPlayersStart];
|
||||||
|
|
||||||
|
// add all factory-based players (which are apart of [style] and [class] bindings)
|
||||||
|
for (let i = PlayerIndex.PlayerBuildersStartPosition + PlayerIndex.PlayerOffsetPosition;
|
||||||
|
i < nonFactoryPlayersStart; i += PlayerIndex.PlayerAndPlayerBuildersTupleSize) {
|
||||||
|
const player = playerContext[i] as Player | null;
|
||||||
|
if (player) {
|
||||||
|
players.push(player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// add all custom players (not apart of [style] and [class] bindings)
|
||||||
|
for (let i = nonFactoryPlayersStart; i < playerContext.length; i++) {
|
||||||
|
players.push(playerContext[i] as Player);
|
||||||
|
}
|
||||||
|
|
||||||
|
return players;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export function getOrCreatePlayerContext(target: {}, context?: LContext | null): PlayerContext|
|
||||||
|
null {
|
||||||
context = context || getContext(target) !;
|
context = context || getContext(target) !;
|
||||||
if (ngDevMode && !context) {
|
if (!context) {
|
||||||
throw new Error(
|
ngDevMode && throwInvalidRefError();
|
||||||
'Only elements that exist in an Angular application can be used for player access');
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
const {lViewData, nodeIndex} = context;
|
const {lViewData, nodeIndex} = context;
|
||||||
const stylingContext = getStylingContext(nodeIndex - HEADER_OFFSET, lViewData);
|
const stylingContext = getStylingContext(nodeIndex - HEADER_OFFSET, lViewData);
|
||||||
return stylingContext[StylingIndex.PlayerContext] || allocPlayerContext(stylingContext);
|
return getPlayerContext(stylingContext) || allocPlayerContext(stylingContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
function allocPlayerContext(data: StylingContext): PlayerContext {
|
export function getPlayerContext(stylingContext: StylingContext): PlayerContext|null {
|
||||||
return data[StylingIndex.PlayerContext] = [];
|
return stylingContext[StylingIndex.PlayerContext];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function allocPlayerContext(data: StylingContext): PlayerContext {
|
||||||
|
return data[StylingIndex.PlayerContext] =
|
||||||
|
[PlayerIndex.SinglePlayerBuildersStartPosition, null, null, null, null];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function throwInvalidRefError() {
|
||||||
|
throw new Error('Only elements that exist in an Angular application can be used for animations');
|
||||||
}
|
}
|
||||||
|
|
|
@ -8,6 +8,7 @@ ng_module(
|
||||||
name = "animation_world",
|
name = "animation_world",
|
||||||
srcs = ["index.ts"],
|
srcs = ["index.ts"],
|
||||||
tags = ["ivy-only"],
|
tags = ["ivy-only"],
|
||||||
|
type_check = False, # see #26462
|
||||||
deps = [
|
deps = [
|
||||||
"//packages/common",
|
"//packages/common",
|
||||||
"//packages/core",
|
"//packages/core",
|
||||||
|
|
|
@ -6,7 +6,7 @@
|
||||||
"name": "AnimationWorldComponent"
|
"name": "AnimationWorldComponent"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "AnimationWorldComponent_div_Template_4"
|
"name": "AnimationWorldComponent_div_Template_6"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "BINDING_INDEX"
|
"name": "BINDING_INDEX"
|
||||||
|
@ -14,6 +14,9 @@
|
||||||
{
|
{
|
||||||
"name": "BLOOM_MASK"
|
"name": "BLOOM_MASK"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "BoundPlayerFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "CIRCULAR$1"
|
"name": "CIRCULAR$1"
|
||||||
},
|
},
|
||||||
|
@ -32,6 +35,9 @@
|
||||||
{
|
{
|
||||||
"name": "ChangeDetectionStrategy"
|
"name": "ChangeDetectionStrategy"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ClassAndStylePlayerBuilder"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "CorePlayerHandler"
|
"name": "CorePlayerHandler"
|
||||||
},
|
},
|
||||||
|
@ -212,6 +218,9 @@
|
||||||
{
|
{
|
||||||
"name": "ViewRef"
|
"name": "ViewRef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "WebAnimationsPlayer"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "_CLEAN_PROMISE"
|
"name": "_CLEAN_PROMISE"
|
||||||
},
|
},
|
||||||
|
@ -251,6 +260,9 @@
|
||||||
{
|
{
|
||||||
"name": "_c3"
|
"name": "_c3"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "_c4"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "_currentInjector"
|
"name": "_currentInjector"
|
||||||
},
|
},
|
||||||
|
@ -278,6 +290,9 @@
|
||||||
{
|
{
|
||||||
"name": "addPlayer"
|
"name": "addPlayer"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "addPlayerInternal"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "addRemoveViewFromContainer"
|
"name": "addRemoveViewFromContainer"
|
||||||
},
|
},
|
||||||
|
@ -290,6 +305,9 @@
|
||||||
{
|
{
|
||||||
"name": "allocStylingContext"
|
"name": "allocStylingContext"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "animateStyleFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "appendChild"
|
"name": "appendChild"
|
||||||
},
|
},
|
||||||
|
@ -302,6 +320,9 @@
|
||||||
{
|
{
|
||||||
"name": "bind"
|
"name": "bind"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "bindPlayerFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "bindingRootIndex"
|
"name": "bindingRootIndex"
|
||||||
},
|
},
|
||||||
|
@ -644,6 +665,15 @@
|
||||||
{
|
{
|
||||||
"name": "getPipeDef"
|
"name": "getPipeDef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerBuilder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerBuilderIndex"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getPointers"
|
"name": "getPointers"
|
||||||
},
|
},
|
||||||
|
@ -671,9 +701,15 @@
|
||||||
{
|
{
|
||||||
"name": "getRootContext"
|
"name": "getRootContext"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getRootContext$2"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getRootView"
|
"name": "getRootView"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getRootView$1"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getStyleSanitizer"
|
"name": "getStyleSanitizer"
|
||||||
},
|
},
|
||||||
|
@ -698,6 +734,9 @@
|
||||||
{
|
{
|
||||||
"name": "getValue"
|
"name": "getValue"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hasPlayerBuilderChanged"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasValueChanged"
|
"name": "hasValueChanged"
|
||||||
},
|
},
|
||||||
|
@ -797,6 +836,9 @@
|
||||||
{
|
{
|
||||||
"name": "listener"
|
"name": "listener"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "loadContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "locateHostElement"
|
"name": "locateHostElement"
|
||||||
},
|
},
|
||||||
|
@ -809,6 +851,9 @@
|
||||||
{
|
{
|
||||||
"name": "makeParamDecorator"
|
"name": "makeParamDecorator"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "markDirty"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "markDirtyIfOnPush"
|
"name": "markDirtyIfOnPush"
|
||||||
},
|
},
|
||||||
|
@ -900,7 +945,7 @@
|
||||||
"name": "renderEmbeddedTemplate"
|
"name": "renderEmbeddedTemplate"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "renderStyling"
|
"name": "renderStyleAndClassBindings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "resetComponentState"
|
"name": "resetComponentState"
|
||||||
|
@ -932,6 +977,9 @@
|
||||||
{
|
{
|
||||||
"name": "setContextDirty"
|
"name": "setContextDirty"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setContextPlayersDirty"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setCurrentInjector"
|
"name": "setCurrentInjector"
|
||||||
},
|
},
|
||||||
|
@ -953,6 +1001,12 @@
|
||||||
{
|
{
|
||||||
"name": "setInputsFromAttrs"
|
"name": "setInputsFromAttrs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setPlayerBuilder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "setPlayerBuilderIndex"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setProp"
|
"name": "setProp"
|
||||||
},
|
},
|
||||||
|
|
|
@ -9,16 +9,19 @@
|
||||||
import '@angular/core/test/bundling/util/src/reflect_metadata';
|
import '@angular/core/test/bundling/util/src/reflect_metadata';
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
import {Component, ElementRef, NgModule, ɵPlayState as PlayState, ɵPlayer as Player, ɵPlayerHandler as PlayerHandler, ɵaddPlayer as addPlayer, ɵrenderComponent as renderComponent} from '@angular/core';
|
import {Component, ElementRef, NgModule, ɵPlayState as PlayState, ɵPlayer as Player, ɵPlayerHandler as PlayerHandler, ɵaddPlayer as addPlayer, ɵbindPlayerFactory as bindPlayerFactory, ɵmarkDirty as markDirty, ɵrenderComponent as renderComponent} from '@angular/core';
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: 'animation-world',
|
selector: 'animation-world',
|
||||||
template: `
|
template: `
|
||||||
<nav>
|
<nav>
|
||||||
<button (click)="doAnimate()">Populate List</button>
|
<button (click)="animateWithCustomPlayer()">Animate List (custom player)</button>
|
||||||
|
<button (click)="animateWithStyles()">Populate List (style bindings)</button>
|
||||||
</nav>
|
</nav>
|
||||||
<div class="list">
|
<div class="list">
|
||||||
<div *ngFor="let item of items" class="record" [class]="makeClass(item)">
|
<div
|
||||||
|
*ngFor="let item of items" class="record" [class]="makeClass(item)" style="border-radius: 10px"
|
||||||
|
[style]="styles">
|
||||||
{{ item }}
|
{{ item }}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -27,12 +30,18 @@ import {Component, ElementRef, NgModule, ɵPlayState as PlayState, ɵPlayer as P
|
||||||
class AnimationWorldComponent {
|
class AnimationWorldComponent {
|
||||||
items: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
items: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
private _hostElement: HTMLElement;
|
private _hostElement: HTMLElement;
|
||||||
|
public styles: {[key: string]: any}|null = null;
|
||||||
|
|
||||||
constructor(element: ElementRef) { this._hostElement = element.nativeElement; }
|
constructor(element: ElementRef) { this._hostElement = element.nativeElement; }
|
||||||
|
|
||||||
makeClass(index: number) { return `record-${index}`; }
|
makeClass(index: number) { return `record-${index}`; }
|
||||||
|
|
||||||
doAnimate() {
|
animateWithStyles() {
|
||||||
|
this.styles = animateStyleFactory([{opacity: 0}, {opacity: 1}], 300, 'ease-out');
|
||||||
|
markDirty(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
animateWithCustomPlayer() {
|
||||||
const elements = this._hostElement.querySelectorAll('div.record') as any as HTMLElement[];
|
const elements = this._hostElement.querySelectorAll('div.record') as any as HTMLElement[];
|
||||||
for (let i = 0; i < elements.length; i++) {
|
for (let i = 0; i < elements.length; i++) {
|
||||||
const element = elements[i];
|
const element = elements[i];
|
||||||
|
@ -55,13 +64,13 @@ function buildAnimationPlayer(element: HTMLElement, animationName: string, time:
|
||||||
class SimpleKeyframePlayer implements Player {
|
class SimpleKeyframePlayer implements Player {
|
||||||
state = PlayState.Pending;
|
state = PlayState.Pending;
|
||||||
parent: Player|null = null;
|
parent: Player|null = null;
|
||||||
private _animationStyle: string;
|
private _animationStyle: string = '';
|
||||||
private _listeners: {[stateName: string]: (() => any)[]} = {};
|
private _listeners: {[stateName: string]: (() => any)[]} = {};
|
||||||
constructor(private _element: HTMLElement, private _animationName: string, time: string) {
|
constructor(private _element: HTMLElement, private _animationName: string, time: string) {
|
||||||
this._animationStyle = `${time} ${_animationName}`;
|
this._animationStyle = `${time} ${_animationName}`;
|
||||||
}
|
}
|
||||||
private _start() {
|
private _start() {
|
||||||
this._element.style.animation = this._animationStyle;
|
(this._element as any).style.animation = this._animationStyle;
|
||||||
const animationFn = (event: AnimationEvent) => {
|
const animationFn = (event: AnimationEvent) => {
|
||||||
if (event.animationName == this._animationName) {
|
if (event.animationName == this._animationName) {
|
||||||
this._element.removeEventListener('animationend', animationFn);
|
this._element.removeEventListener('animationend', animationFn);
|
||||||
|
@ -134,3 +143,66 @@ class AnimationDebugger implements PlayerHandler {
|
||||||
|
|
||||||
const playerHandler = new AnimationDebugger();
|
const playerHandler = new AnimationDebugger();
|
||||||
renderComponent(AnimationWorldComponent, {playerHandler});
|
renderComponent(AnimationWorldComponent, {playerHandler});
|
||||||
|
|
||||||
|
function animateStyleFactory(keyframes: any[], duration: number, easing: string) {
|
||||||
|
const limit = keyframes.length - 1;
|
||||||
|
const finalKeyframe = keyframes[limit];
|
||||||
|
return bindPlayerFactory((element: HTMLElement, type: number, values: {[key: string]: any}) => {
|
||||||
|
const kf = keyframes.slice(0, limit);
|
||||||
|
kf.push(values);
|
||||||
|
return new WebAnimationsPlayer(element, keyframes, duration, easing);
|
||||||
|
}, finalKeyframe);
|
||||||
|
}
|
||||||
|
|
||||||
|
class WebAnimationsPlayer implements Player {
|
||||||
|
state = PlayState.Pending;
|
||||||
|
parent: Player|null = null;
|
||||||
|
private _listeners: {[stateName: string]: (() => any)[]} = {};
|
||||||
|
constructor(
|
||||||
|
private _element: HTMLElement, private _keyframes: {[key: string]: any}[],
|
||||||
|
private _duration: number, private _easing: string) {}
|
||||||
|
private _start() {
|
||||||
|
const player = this._element.animate(
|
||||||
|
this._keyframes as any[], {duration: this._duration, easing: this._easing, fill: 'both'});
|
||||||
|
player.addEventListener('finish', e => { this.finish(); });
|
||||||
|
}
|
||||||
|
addEventListener(state: PlayState|string, cb: () => any): void {
|
||||||
|
const key = state.toString();
|
||||||
|
const arr = this._listeners[key] = (this._listeners[key] || []);
|
||||||
|
arr.push(cb);
|
||||||
|
}
|
||||||
|
play(): void {
|
||||||
|
if (this.state <= PlayState.Pending) {
|
||||||
|
this._start();
|
||||||
|
}
|
||||||
|
if (this.state != PlayState.Running) {
|
||||||
|
this.state = PlayState.Running;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pause(): void {
|
||||||
|
if (this.state != PlayState.Paused) {
|
||||||
|
this.state = PlayState.Paused;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finish(): void {
|
||||||
|
if (this.state < PlayState.Finished) {
|
||||||
|
this._element.style.animation = '';
|
||||||
|
this.state = PlayState.Finished;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destroy(): void {
|
||||||
|
if (this.state < PlayState.Destroyed) {
|
||||||
|
this.finish();
|
||||||
|
this.state = PlayState.Destroyed;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
capture(): any {}
|
||||||
|
private _emit(state: PlayState) {
|
||||||
|
const arr = this._listeners[state.toString()] || [];
|
||||||
|
arr.forEach(cb => cb());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -8,6 +8,9 @@
|
||||||
{
|
{
|
||||||
"name": "BLOOM_MASK"
|
"name": "BLOOM_MASK"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "BoundPlayerFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "CIRCULAR$1"
|
"name": "CIRCULAR$1"
|
||||||
},
|
},
|
||||||
|
@ -26,6 +29,12 @@
|
||||||
{
|
{
|
||||||
"name": "ChangeDetectionStrategy"
|
"name": "ChangeDetectionStrategy"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ClassAndStylePlayerBuilder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "CorePlayerHandler"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "DECLARATION_VIEW"
|
"name": "DECLARATION_VIEW"
|
||||||
},
|
},
|
||||||
|
@ -344,12 +353,18 @@
|
||||||
{
|
{
|
||||||
"name": "addComponentLogic"
|
"name": "addComponentLogic"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "addPlayerInternal"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "addRemoveViewFromContainer"
|
"name": "addRemoveViewFromContainer"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "addToViewTree"
|
"name": "addToViewTree"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "allocPlayerContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "allocStylingContext"
|
"name": "allocStylingContext"
|
||||||
},
|
},
|
||||||
|
@ -686,6 +701,15 @@
|
||||||
{
|
{
|
||||||
"name": "getPipeDef"
|
"name": "getPipeDef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerBuilder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerBuilderIndex"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getPointers"
|
"name": "getPointers"
|
||||||
},
|
},
|
||||||
|
@ -710,6 +734,12 @@
|
||||||
{
|
{
|
||||||
"name": "getRendererFactory"
|
"name": "getRendererFactory"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getRootContext"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getRootView"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getStyleSanitizer"
|
"name": "getStyleSanitizer"
|
||||||
},
|
},
|
||||||
|
@ -734,6 +764,9 @@
|
||||||
{
|
{
|
||||||
"name": "getValue"
|
"name": "getValue"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hasPlayerBuilderChanged"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasValueChanged"
|
"name": "hasValueChanged"
|
||||||
},
|
},
|
||||||
|
@ -930,7 +963,7 @@
|
||||||
"name": "renderEmbeddedTemplate"
|
"name": "renderEmbeddedTemplate"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "renderStyling"
|
"name": "renderStyleAndClassBindings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "resetComponentState"
|
"name": "resetComponentState"
|
||||||
|
@ -962,6 +995,9 @@
|
||||||
{
|
{
|
||||||
"name": "setContextDirty"
|
"name": "setContextDirty"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setContextPlayersDirty"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setCurrentInjector"
|
"name": "setCurrentInjector"
|
||||||
},
|
},
|
||||||
|
@ -983,6 +1019,12 @@
|
||||||
{
|
{
|
||||||
"name": "setInputsFromAttrs"
|
"name": "setInputsFromAttrs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setPlayerBuilder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "setPlayerBuilderIndex"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setProp"
|
"name": "setProp"
|
||||||
},
|
},
|
||||||
|
|
|
@ -59,6 +59,9 @@
|
||||||
{
|
{
|
||||||
"name": "BROWSER_SANITIZATION_PROVIDERS"
|
"name": "BROWSER_SANITIZATION_PROVIDERS"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "BoundPlayerFactory"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "BrowserDomAdapter"
|
"name": "BrowserDomAdapter"
|
||||||
},
|
},
|
||||||
|
@ -128,6 +131,9 @@
|
||||||
{
|
{
|
||||||
"name": "ChangeDetectorRef"
|
"name": "ChangeDetectorRef"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "ClassAndStylePlayerBuilder"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "CommonModule"
|
"name": "CommonModule"
|
||||||
},
|
},
|
||||||
|
@ -164,6 +170,9 @@
|
||||||
{
|
{
|
||||||
"name": "Console"
|
"name": "Console"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "CorePlayerHandler"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "CurrencyPipe"
|
"name": "CurrencyPipe"
|
||||||
},
|
},
|
||||||
|
@ -1169,6 +1178,9 @@
|
||||||
{
|
{
|
||||||
"name": "addDateMinutes"
|
"name": "addDateMinutes"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "addPlayerInternal"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "addRemoveViewFromContainer"
|
"name": "addRemoveViewFromContainer"
|
||||||
},
|
},
|
||||||
|
@ -1178,6 +1190,9 @@
|
||||||
{
|
{
|
||||||
"name": "adjustBlueprintForNewNode"
|
"name": "adjustBlueprintForNewNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "allocPlayerContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "allocStylingContext"
|
"name": "allocStylingContext"
|
||||||
},
|
},
|
||||||
|
@ -1793,6 +1808,15 @@
|
||||||
{
|
{
|
||||||
"name": "getPlatform"
|
"name": "getPlatform"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerBuilder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerBuilderIndex"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getPlayerContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getPluralCategory"
|
"name": "getPluralCategory"
|
||||||
},
|
},
|
||||||
|
@ -1823,6 +1847,12 @@
|
||||||
{
|
{
|
||||||
"name": "getRendererFactory"
|
"name": "getRendererFactory"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "getRootContext"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "getRootView"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "getStyleSanitizer"
|
"name": "getStyleSanitizer"
|
||||||
},
|
},
|
||||||
|
@ -1868,6 +1898,9 @@
|
||||||
{
|
{
|
||||||
"name": "hasOnDestroy"
|
"name": "hasOnDestroy"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "hasPlayerBuilderChanged"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "hasValueChanged"
|
"name": "hasValueChanged"
|
||||||
},
|
},
|
||||||
|
@ -2262,7 +2295,7 @@
|
||||||
"name": "renderEmbeddedTemplate"
|
"name": "renderEmbeddedTemplate"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "renderStyling"
|
"name": "renderStyleAndClassBindings"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"name": "resetComponentState"
|
"name": "resetComponentState"
|
||||||
|
@ -2315,6 +2348,9 @@
|
||||||
{
|
{
|
||||||
"name": "setContextDirty"
|
"name": "setContextDirty"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setContextPlayersDirty"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setCurrentInjector"
|
"name": "setCurrentInjector"
|
||||||
},
|
},
|
||||||
|
@ -2336,6 +2372,12 @@
|
||||||
{
|
{
|
||||||
"name": "setInputsFromAttrs"
|
"name": "setInputsFromAttrs"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "setPlayerBuilder"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "setPlayerBuilderIndex"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "setProp"
|
"name": "setProp"
|
||||||
},
|
},
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -14,6 +14,8 @@ export class MockPlayer implements Player {
|
||||||
state: PlayState = PlayState.Pending;
|
state: PlayState = PlayState.Pending;
|
||||||
private _listeners: {[state: string]: (() => any)[]} = {};
|
private _listeners: {[state: string]: (() => any)[]} = {};
|
||||||
|
|
||||||
|
constructor(public value?: any) {}
|
||||||
|
|
||||||
play(): void {
|
play(): void {
|
||||||
if (this.state === PlayState.Running) return;
|
if (this.state === PlayState.Running) return;
|
||||||
|
|
||||||
|
|
|
@ -9,9 +9,9 @@ import {RenderFlags} from '@angular/core/src/render3';
|
||||||
|
|
||||||
import {defineComponent, getHostElement} from '../../../src/render3/index';
|
import {defineComponent, getHostElement} from '../../../src/render3/index';
|
||||||
import {element, elementEnd, elementStart, elementStyling, elementStylingApply, load, markDirty} from '../../../src/render3/instructions';
|
import {element, elementEnd, elementStart, elementStyling, elementStylingApply, load, markDirty} from '../../../src/render3/instructions';
|
||||||
import {PlayState, Player, PlayerContext, PlayerHandler} from '../../../src/render3/interfaces/player';
|
import {PlayState, Player, PlayerHandler} from '../../../src/render3/interfaces/player';
|
||||||
import {RElement} from '../../../src/render3/interfaces/renderer';
|
import {RElement} from '../../../src/render3/interfaces/renderer';
|
||||||
import {addPlayer, getPlayers} from '../../../src/render3/player';
|
import {addPlayer, getPlayers} from '../../../src/render3/players';
|
||||||
import {QueryList, query, queryRefresh} from '../../../src/render3/query';
|
import {QueryList, query, queryRefresh} from '../../../src/render3/query';
|
||||||
import {getOrCreatePlayerContext} from '../../../src/render3/styling/util';
|
import {getOrCreatePlayerContext} from '../../../src/render3/styling/util';
|
||||||
import {ComponentFixture} from '../render_util';
|
import {ComponentFixture} from '../render_util';
|
||||||
|
@ -56,14 +56,14 @@ describe('animation player access', () => {
|
||||||
it('should add a player to the element animation context and remove it once it completes', () => {
|
it('should add a player to the element animation context and remove it once it completes', () => {
|
||||||
const element = buildElement();
|
const element = buildElement();
|
||||||
const context = getOrCreatePlayerContext(element);
|
const context = getOrCreatePlayerContext(element);
|
||||||
expect(context).toEqual([]);
|
expect(getPlayers(element)).toEqual([]);
|
||||||
|
|
||||||
const player = new MockPlayer();
|
const player = new MockPlayer();
|
||||||
addPlayer(element, player);
|
addPlayer(element, player);
|
||||||
expect(readPlayers(context)).toEqual([player]);
|
expect(getPlayers(element)).toEqual([player]);
|
||||||
|
|
||||||
player.destroy();
|
player.destroy();
|
||||||
expect(readPlayers(context)).toEqual([]);
|
expect(getPlayers(element)).toEqual([]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should flush all pending animation players after change detection', () => {
|
it('should flush all pending animation players after change detection', () => {
|
||||||
|
@ -226,10 +226,6 @@ function buildElementWithStyling() {
|
||||||
return fixture.hostElement.querySelector('div') as RElement;
|
return fixture.hostElement.querySelector('div') as RElement;
|
||||||
}
|
}
|
||||||
|
|
||||||
function readPlayers(context: PlayerContext): Player[] {
|
|
||||||
return context;
|
|
||||||
}
|
|
||||||
|
|
||||||
class Comp {
|
class Comp {
|
||||||
static ngComponentDef = defineComponent({
|
static ngComponentDef = defineComponent({
|
||||||
type: Comp,
|
type: Comp,
|
||||||
|
|
Loading…
Reference in New Issue