diff --git a/packages/core/src/core_render3_private_export.ts b/packages/core/src/core_render3_private_export.ts index 97fe44fedf..4d707d5423 100644 --- a/packages/core/src/core_render3_private_export.ts +++ b/packages/core/src/core_render3_private_export.ts @@ -162,12 +162,12 @@ export { Player as ɵPlayer, PlayState as ɵPlayState, PlayerHandler as ɵPlayerHandler, -} from './render3/animations/interfaces'; +} from './render3/interfaces/player'; export { addPlayer as ɵaddPlayer, getPlayers as ɵgetPlayers, -} from './render3/animations/players'; +} from './render3/player'; // we reexport these symbols just so that they are retained during the dead code elimination // performed by rollup while it's creating fesm files. diff --git a/packages/core/src/render3/animations/players.ts b/packages/core/src/render3/animations/players.ts deleted file mode 100644 index d1362f00ec..0000000000 --- a/packages/core/src/render3/animations/players.ts +++ /dev/null @@ -1,72 +0,0 @@ -/** - * @license - * Copyright Google Inc. All Rights Reserved. - * - * Use of this source code is governed by an MIT-style license that can be - * found in the LICENSE file at https://angular.io/license - */ -import '../ng_dev_mode'; - -import {LContext, getContext} from '../context_discovery'; -import {scheduleTick} from '../instructions'; -import {LElementNode} from '../interfaces/node'; -import {RootContextFlags} from '../interfaces/view'; -import {StylingContext, StylingIndex, createEmptyStylingContext} from '../styling'; -import {getRootContext} from '../util'; - -import {CorePlayerHandler} from './core_player_handler'; -import {AnimationContext, ComponentInstance, DirectiveInstance, PlayState, Player} from './interfaces'; - -export function addPlayer( - ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void { - const elementContext = getContext(ref) !; - const animationContext = getOrCreateAnimationContext(elementContext.native, elementContext) !; - animationContext.push(player); - - 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); - } -} - -export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] { - return getOrCreateAnimationContext(ref); -} - -export function getOrCreateAnimationContext( - target: {}, context?: LContext | null): AnimationContext { - context = context || getContext(target) !; - if (ngDevMode && !context) { - throw new Error( - 'Only elements that exist in an Angular application can be used for animations'); - } - - const {lViewData, lNodeIndex} = context; - const value = lViewData[lNodeIndex]; - let stylingContext = value as StylingContext; - if (!Array.isArray(value)) { - stylingContext = lViewData[lNodeIndex] = createEmptyStylingContext(value as LElementNode); - } - return stylingContext[StylingIndex.AnimationContext] || allocAnimationContext(stylingContext); -} - -function allocAnimationContext(data: StylingContext): AnimationContext { - return data[StylingIndex.AnimationContext] = []; -} diff --git a/packages/core/src/render3/component.ts b/packages/core/src/render3/component.ts index d9dd57ba5a..c12bd444fc 100644 --- a/packages/core/src/render3/component.ts +++ b/packages/core/src/render3/component.ts @@ -12,11 +12,12 @@ import {Type} from '../core'; import {Injector} from '../di/injector'; import {Sanitizer} from '../sanitization/security'; -import {PlayerHandler} from './animations/interfaces'; import {assertComponentType, assertDefined} from './assert'; import {getLElementFromComponent, readPatchedLViewData} from './context_discovery'; import {getComponentDef} from './definition'; import {queueInitHooks, queueLifecycleHooks} from './hooks'; +import {PlayerHandler} from './interfaces/player'; + import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions'; import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition'; import {LElementNode} from './interfaces/node'; diff --git a/packages/core/src/render3/instructions.ts b/packages/core/src/render3/instructions.ts index d88f049d6f..ebe7fee981 100644 --- a/packages/core/src/render3/instructions.ts +++ b/packages/core/src/render3/instructions.ts @@ -11,6 +11,7 @@ import './ng_dev_mode'; import {QueryList} from '../linker'; import {Sanitizer} from '../sanitization/security'; import {StyleSanitizeFn} from '../sanitization/style_sanitizer'; + import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert'; import {attachPatchData, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery'; import {getRootView} from './discovery_utils'; @@ -23,13 +24,15 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection'; import {LQueries} from './interfaces/query'; import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer'; +import {StylingContext} from './interfaces/styling'; import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view'; import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert'; import {appendChild, appendProjectedNode, createTextNode, findComponentView, getHostElementNode, getLViewChild, getRenderParent, insertView, removeView} from './node_manipulation'; import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher'; -import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling'; +import {allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling/class_and_style_bindings'; import {assertDataInRangeInternal, getLNode, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, stringify} from './util'; + /** * A permanent marker promise which signifies that the current CD tree is * clean. diff --git a/packages/core/src/render3/interfaces/node.ts b/packages/core/src/render3/interfaces/node.ts index e1ef79ef49..2ea0bc64a4 100644 --- a/packages/core/src/render3/interfaces/node.ts +++ b/packages/core/src/render3/interfaces/node.ts @@ -6,12 +6,11 @@ * found in the LICENSE file at https://angular.io/license */ -import {StylingContext} from '../styling'; - import {LContainer} from './container'; import {LInjector} from './injector'; import {LQueries} from './query'; import {RComment, RElement, RText} from './renderer'; +import {StylingContext} from './styling'; import {LViewData, TView} from './view'; diff --git a/packages/core/src/render3/animations/interfaces.ts b/packages/core/src/render3/interfaces/player.ts similarity index 97% rename from packages/core/src/render3/animations/interfaces.ts rename to packages/core/src/render3/interfaces/player.ts index a55705a19d..b14a44707e 100644 --- a/packages/core/src/render3/animations/interfaces.ts +++ b/packages/core/src/render3/interfaces/player.ts @@ -31,7 +31,7 @@ export const enum PlayState {Pending = 0, Running = 1, Paused = 2, Finished = 10 /** * The context that stores all active animation players present on an element. */ -export declare type AnimationContext = Player[]; +export declare type PlayerContext = Player[]; export declare type ComponentInstance = {}; export declare type DirectiveInstance = {}; diff --git a/packages/core/src/render3/interfaces/styling.ts b/packages/core/src/render3/interfaces/styling.ts new file mode 100644 index 0000000000..3b66e72731 --- /dev/null +++ b/packages/core/src/render3/interfaces/styling.ts @@ -0,0 +1,231 @@ +/** + * @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 {StyleSanitizeFn} from '../../sanitization/style_sanitizer'; + +import {LElementNode} from './node'; +import {PlayerContext} from './player'; + + + +/** + * 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` + * and `updateClassProp` functions. There are also two initialization functions + * `allocStylingContext` and `createStylingContextTemplate` which are used to initialize + * and/or clone the context. + * + * The context is an array where the first two cells are used for static data (initial styling) + * and dirty flags / index offsets). The remaining set of cells is used for multi (map) and single + * (prop) style values. + * + * each value from here onwards is mapped as so: + * [i] = mutation/type flag for the style/class value + * [i + 1] = prop string (or null incase it has been removed) + * [i + 2] = value string (or null incase it has been removed) + * + * There are three types of styling types stored in this context: + * initial: any styles that are passed in once the context is created + * (these are stored in the first cell of the array and the first + * value of this array is always `null` even if no initial styling exists. + * the `null` value is there so that any new styles have a parent to point + * to. This way we can always assume that there is a parent.) + * + * single: any styles that are updated using `updateStyleProp` or `updateClassProp` (fixed set) + * + * multi: any styles that are updated using `updateStylingMap` (dynamic set) + * + * Note that context is only used to collect style information. Only when `renderStyling` + * is called is when the styling payload will be rendered (or built as a key/value map). + * + * When the context is created, depending on what initial styling values are passed in, the + * context itself will be pre-filled with slots based on the initial style properties. Say + * for example we have a series of initial styles that look like so: + * + * style="width:100px; height:200px;" + * class="foo" + * + * Then the initial state of the context (once initialized) will look like so: + * + * ``` + * context = [ + * element, + * playerContext | null, + * styleSanitizer | null, + * [null, '100px', '200px', true], // property names are not needed since they have already been + * written to DOM. + * + * configMasterVal, + * 1, // this instructs how many `style` values there are so that class index values can be + * offsetted + * { classOne: true, classTwo: false } | 'classOne classTwo' | null // last class value provided + * into updateStylingMap + * { styleOne: '100px', styleTwo: 0 } | null // last style value provided into updateStylingMap + * + * // 8 + * 'width', + * pointers(1, 15); // Point to static `width`: `100px` and multi `width`. + * null, + * + * // 11 + * 'height', + * pointers(2, 18); // Point to static `height`: `200px` and multi `height`. + * null, + * + * // 14 + * 'foo', + * pointers(1, 21); // Point to static `foo`: `true` and multi `foo`. + * null, + * + * // 17 + * 'width', + * pointers(1, 6); // Point to static `width`: `100px` and single `width`. + * null, + * + * // 21 + * 'height', + * pointers(2, 9); // Point to static `height`: `200px` and single `height`. + * null, + * + * // 24 + * 'foo', + * pointers(3, 12); // Point to static `foo`: `true` and single `foo`. + * null, + * ] + * + * function pointers(staticIndex: number, dynamicIndex: number) { + * // combine the two indices into a single word. + * return (staticIndex << StylingFlags.BitCountSize) | + * (dynamicIndex << (StylingIndex.BitCountSize + StylingFlags.BitCountSize)); + * } + * ``` + * + * The values are duplicated so that space is set aside for both multi ([style] and [class]) + * and single ([style.prop] or [class.named]) values. The respective config values + * (configValA, configValB, etc...) are a combination of the StylingFlags with two index + * values: the `initialIndex` (which points to the index location of the style value in + * the initial styles array in slot 0) and the `dynamicIndex` (which points to the + * matching single/multi index position in the context array for the same prop). + * + * This means that every time `updateStyleProp` or `updateClassProp` are called then they + * must be called using an index value (not a property string) which references the index + * value of the initial style prop/class when the context was created. This also means that + * `updateStyleProp` or `updateClassProp` cannot be called with a new property (only + * `updateStylingMap` can include new CSS properties that will be added to the context). + */ +export interface StylingContext extends + Array { + /** + * Location of element that is used as a target for this context. + */ + [StylingIndex.ElementPosition]: LElementNode|null; + + /** + * Location of animation context (which contains the active players) for this element styling + * context. + */ + [StylingIndex.PlayerContext]: PlayerContext|null; + + /** + * The style sanitizer that is used within this context + */ + [StylingIndex.StyleSanitizerPosition]: StyleSanitizeFn|null; + + /** + * Location of initial data shared by all instances of this style. + */ + [StylingIndex.InitialStylesPosition]: InitialStyles; + + /** + * A numeric value representing the configuration status (whether the context is dirty or not) + * mixed together (using bit shifting) with a index value which tells the starting index value + * of where the multi style entries begin. + */ + [StylingIndex.MasterFlagPosition]: number; + + /** + * A numeric value representing the class index offset value. Whenever a single class is + * applied (using `elementClassProp`) it should have an styling index value that doesn't + * need to take into account any style values that exist in the context. + */ + [StylingIndex.ClassOffsetPosition]: number; + + /** + * The last class value that was interpreted by elementStylingMap. This is cached + * So that the algorithm can exit early incase the value has not changed. + */ + [StylingIndex.PreviousMultiClassValue]: {[key: string]: any}|string|null; + + /** + * The last style value that was interpreted by elementStylingMap. This is cached + * So that the algorithm can exit early incase the value has not changed. + */ + [StylingIndex.PreviousMultiStyleValue]: {[key: string]: any}|null; +} + +/** + * The initial styles is populated whether or not there are any initial styles passed into + * the context during allocation. The 0th value must be null so that index values of `0` within + * the context flags can always point to a null value safely when nothing is set. + * + * All other entries in this array are of `string` value and correspond to the values that + * were extracted from the `style=""` attribute in the HTML code for the provided template. + */ +export interface InitialStyles extends Array { [0]: null; } + +/** + * Used to set the context to be dirty or not both on the master flag (position 1) + * or for each single/multi property that exists in the context. + */ +export const enum StylingFlags { + // Implies no configurations + None = 0b000, + // Whether or not the entry or context itself is dirty + Dirty = 0b001, + // Whether or not this is a class-based assignment + Class = 0b010, + // Whether or not a sanitizer was applied to this property + Sanitize = 0b100, + // The max amount of bits used to represent these configuration values + BitCountSize = 3, + // There are only three bits here + BitMask = 0b111 +} + +/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */ +export const enum StylingIndex { + // Position of where the initial styles are stored in the styling context + ElementPosition = 0, + // Position of where the initial styles are stored in the styling context + PlayerContext = 1, + // Position of where the style sanitizer is stored within the styling context + StyleSanitizerPosition = 2, + // Position of where the initial styles are stored in the styling context + InitialStylesPosition = 3, + // Index of location where the start of single properties are stored. (`updateStyleProp`) + MasterFlagPosition = 4, + // Index of location where the class index offset value is located + ClassOffsetPosition = 5, + // Position of where the last string-based CSS class value was stored + PreviousMultiClassValue = 6, + // Position of where the last string-based CSS class value was stored + PreviousMultiStyleValue = 7, + // Location of single (prop) value entries are stored within the context + SingleStylesStartPosition = 8, + // Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue + FlagsOffset = 0, + PropertyOffset = 1, + ValueOffset = 2, + // Size of each multi or single entry (flag + prop + value) + Size = 3, + // Each flag has a binary digit length of this value + BitCountSize = 14, // (32 - 3) / 2 = ~14 + // The binary digit value as a mask + BitMask = 0b11111111111111 // 14 bits +} diff --git a/packages/core/src/render3/interfaces/view.ts b/packages/core/src/render3/interfaces/view.ts index a150f773a3..d4bd36e9a8 100644 --- a/packages/core/src/render3/interfaces/view.ts +++ b/packages/core/src/render3/interfaces/view.ts @@ -9,7 +9,7 @@ import {Injector} from '../../di/injector'; import {QueryList} from '../../linker'; import {Sanitizer} from '../../sanitization/security'; -import {PlayerHandler} from '../animations/interfaces'; +import {PlayerHandler} from '../interfaces/player'; import {LContainer} from './container'; import {ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefList, PipeDefInternal, PipeDefList} from './definition'; diff --git a/packages/core/src/render3/player.ts b/packages/core/src/render3/player.ts new file mode 100644 index 0000000000..bd79d42ed9 --- /dev/null +++ b/packages/core/src/render3/player.ts @@ -0,0 +1,47 @@ +/** + * @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 {getContext} from './context_discovery'; +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'; + +export function addPlayer( + ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void { + const elementContext = getContext(ref) !; + const animationContext = getOrCreatePlayerContext(elementContext.native, elementContext) !; + animationContext.push(player); + + 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); + } +} + +export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] { + return getOrCreatePlayerContext(ref); +} diff --git a/packages/core/src/render3/styling.ts b/packages/core/src/render3/styling/class_and_style_bindings.ts similarity index 75% rename from packages/core/src/render3/styling.ts rename to packages/core/src/render3/styling/class_and_style_bindings.ts index 172cc83068..d380b25177 100644 --- a/packages/core/src/render3/styling.ts +++ b/packages/core/src/render3/styling/class_and_style_bindings.ts @@ -6,227 +6,13 @@ * found in the LICENSE file at https://angular.io/license */ -import {StyleSanitizeFn} from '../sanitization/style_sanitizer'; -import {AnimationContext} from './animations/interfaces'; -import {InitialStylingFlags} from './interfaces/definition'; -import {LElementNode} from './interfaces/node'; -import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer'; +import {StyleSanitizeFn} from '../../sanitization/style_sanitizer'; -/** - * 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` - * and `updateClassProp` functions. There are also two initialization functions - * `allocStylingContext` and `createStylingContextTemplate` which are used to initialize - * and/or clone the context. - * - * The context is an array where the first two cells are used for static data (initial styling) - * and dirty flags / index offsets). The remaining set of cells is used for multi (map) and single - * (prop) style values. - * - * each value from here onwards is mapped as so: - * [i] = mutation/type flag for the style/class value - * [i + 1] = prop string (or null incase it has been removed) - * [i + 2] = value string (or null incase it has been removed) - * - * There are three types of styling types stored in this context: - * initial: any styles that are passed in once the context is created - * (these are stored in the first cell of the array and the first - * value of this array is always `null` even if no initial styling exists. - * the `null` value is there so that any new styles have a parent to point - * to. This way we can always assume that there is a parent.) - * - * single: any styles that are updated using `updateStyleProp` or `updateClassProp` (fixed set) - * - * multi: any styles that are updated using `updateStylingMap` (dynamic set) - * - * Note that context is only used to collect style information. Only when `renderStyling` - * is called is when the styling payload will be rendered (or built as a key/value map). - * - * When the context is created, depending on what initial styling values are passed in, the - * context itself will be pre-filled with slots based on the initial style properties. Say - * for example we have a series of initial styles that look like so: - * - * style="width:100px; height:200px;" - * class="foo" - * - * Then the initial state of the context (once initialized) will look like so: - * - * ``` - * context = [ - * element, - * playerContext | null, - * styleSanitizer | null, - * [null, '100px', '200px', true], // property names are not needed since they have already been - * written to DOM. - * - * configMasterVal, - * 1, // this instructs how many `style` values there are so that class index values can be - * offsetted - * { classOne: true, classTwo: false } | 'classOne classTwo' | null // last class value provided into updateStylingMap - * { styleOne: '100px', styleTwo: 0 } | null // last style value provided into updateStylingMap - * - * // 8 - * 'width', - * pointers(1, 15); // Point to static `width`: `100px` and multi `width`. - * null, - * - * // 11 - * 'height', - * pointers(2, 18); // Point to static `height`: `200px` and multi `height`. - * null, - * - * // 14 - * 'foo', - * pointers(1, 21); // Point to static `foo`: `true` and multi `foo`. - * null, - * - * // 17 - * 'width', - * pointers(1, 6); // Point to static `width`: `100px` and single `width`. - * null, - * - * // 21 - * 'height', - * pointers(2, 9); // Point to static `height`: `200px` and single `height`. - * null, - * - * // 24 - * 'foo', - * pointers(3, 12); // Point to static `foo`: `true` and single `foo`. - * null, - * ] - * - * function pointers(staticIndex: number, dynamicIndex: number) { - * // combine the two indices into a single word. - * return (staticIndex << StylingFlags.BitCountSize) | - * (dynamicIndex << (StylingIndex.BitCountSize + StylingFlags.BitCountSize)); - * } - * ``` - * - * The values are duplicated so that space is set aside for both multi ([style] and [class]) - * and single ([style.prop] or [class.named]) values. The respective config values - * (configValA, configValB, etc...) are a combination of the StylingFlags with two index - * values: the `initialIndex` (which points to the index location of the style value in - * the initial styles array in slot 0) and the `dynamicIndex` (which points to the - * matching single/multi index position in the context array for the same prop). - * - * This means that every time `updateStyleProp` or `updateClassProp` are called then they - * must be called using an index value (not a property string) which references the index - * value of the initial style prop/class when the context was created. This also means that - * `updateStyleProp` or `updateClassProp` cannot be called with a new property (only - * `updateStylingMap` can include new CSS properties that will be added to the context). - */ -export interface StylingContext extends - Array { - /** - * Location of element that is used as a target for this context. - */ - [StylingIndex.ElementPosition]: LElementNode|null; - - /** - * Location of animation context (which contains the active players) for this element styling - * context. - */ - [StylingIndex.AnimationContext]: AnimationContext|null; - - /** - * The style sanitizer that is used within this context - */ - [StylingIndex.StyleSanitizerPosition]: StyleSanitizeFn|null; - - /** - * Location of initial data shared by all instances of this style. - */ - [StylingIndex.InitialStylesPosition]: InitialStyles; - - /** - * A numeric value representing the configuration status (whether the context is dirty or not) - * mixed together (using bit shifting) with a index value which tells the starting index value - * of where the multi style entries begin. - */ - [StylingIndex.MasterFlagPosition]: number; - - /** - * A numeric value representing the class index offset value. Whenever a single class is - * applied (using `elementClassProp`) it should have an styling index value that doesn't - * need to take into account any style values that exist in the context. - */ - [StylingIndex.ClassOffsetPosition]: number; - - /** - * The last class value that was interpreted by elementStylingMap. This is cached - * So that the algorithm can exit early incase the value has not changed. - */ - [StylingIndex.PreviousMultiClassValue]: {[key: string]: any}|string|null; - - /** - * The last style value that was interpreted by elementStylingMap. This is cached - * So that the algorithm can exit early incase the value has not changed. - */ - [StylingIndex.PreviousMultiStyleValue]: {[key: string]: any}|null; -} - -/** - * The initial styles is populated whether or not there are any initial styles passed into - * the context during allocation. The 0th value must be null so that index values of `0` within - * the context flags can always point to a null value safely when nothing is set. - * - * All other entries in this array are of `string` value and correspond to the values that - * were extracted from the `style=""` attribute in the HTML code for the provided template. - */ -export interface InitialStyles extends Array { [0]: null; } - -/** - * Used to set the context to be dirty or not both on the master flag (position 1) - * or for each single/multi property that exists in the context. - */ -export const enum StylingFlags { - // Implies no configurations - None = 0b000, - // Whether or not the entry or context itself is dirty - Dirty = 0b001, - // Whether or not this is a class-based assignment - Class = 0b010, - // Whether or not a sanitizer was applied to this property - Sanitize = 0b100, - // The max amount of bits used to represent these configuration values - BitCountSize = 3, - // There are only three bits here - BitMask = 0b111 -} - -/** Used as numeric pointer values to determine what cells to update in the `StylingContext` */ -export const enum StylingIndex { - // Position of where the initial styles are stored in the styling context - ElementPosition = 0, - // Position of where the initial styles are stored in the styling context - AnimationContext = 1, - // Position of where the style sanitizer is stored within the styling context - StyleSanitizerPosition = 2, - // Position of where the initial styles are stored in the styling context - InitialStylesPosition = 3, - // Index of location where the start of single properties are stored. (`updateStyleProp`) - MasterFlagPosition = 4, - // Index of location where the class index offset value is located - ClassOffsetPosition = 5, - // Position of where the last string-based CSS class value was stored - PreviousMultiClassValue = 6, - // Position of where the last string-based CSS class value was stored - PreviousMultiStyleValue = 7, - // Location of single (prop) value entries are stored within the context - SingleStylesStartPosition = 8, - // Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue - FlagsOffset = 0, - PropertyOffset = 1, - ValueOffset = 2, - // Size of each multi or single entry (flag + prop + value) - Size = 3, - // Each flag has a binary digit length of this value - BitCountSize = 14, // (32 - 3) / 2 = ~14 - // The binary digit value as a mask - BitMask = 0b11111111111111 // 14 bits -} +import {InitialStylingFlags} from '../interfaces/definition'; +import {LElementNode} from '../interfaces/node'; +import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from '../interfaces/renderer'; +import {InitialStyles, StylingContext, StylingFlags, StylingIndex} from '../interfaces/styling'; +import {createEmptyStylingContext, EMPTY_ARR, EMPTY_OBJ} from './util'; /** * Used clone a copy of a pre-computed template of a styling context. @@ -242,14 +28,6 @@ export function allocStylingContext( return context; } -export function createEmptyStylingContext( - element?: LElementNode | null, sanitizer?: StyleSanitizeFn | null, - initialStylingValues?: InitialStyles): StylingContext { - return [ - element || null, null, sanitizer || null, initialStylingValues || [null], 0, 0, null, null - ]; -} - /** * Creates a styling context template where styling information is stored. * Any styles that are later referenced using `updateStyleProp` must be @@ -377,8 +155,6 @@ export function createStylingContextTemplate( return context; } -const EMPTY_ARR: any[] = []; -const EMPTY_OBJ: {[key: string]: any} = {}; /** * Sets and resolves all `multi` styling on an `StylingContext` so that they can be * applied to the element once `renderStyling` is called. diff --git a/packages/core/src/render3/animations/core_player_handler.ts b/packages/core/src/render3/styling/core_player_handler.ts similarity index 90% rename from packages/core/src/render3/animations/core_player_handler.ts rename to packages/core/src/render3/styling/core_player_handler.ts index d9941b99d9..36866dda90 100644 --- a/packages/core/src/render3/animations/core_player_handler.ts +++ b/packages/core/src/render3/styling/core_player_handler.ts @@ -5,7 +5,7 @@ * 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 {Player, PlayerHandler} from './interfaces'; +import {Player, PlayerHandler} from '../interfaces/player'; export class CorePlayerHandler implements PlayerHandler { private _players: Player[] = []; diff --git a/packages/core/src/render3/styling/util.ts b/packages/core/src/render3/styling/util.ts new file mode 100644 index 0000000000..1d652bb051 --- /dev/null +++ b/packages/core/src/render3/styling/util.ts @@ -0,0 +1,43 @@ +/** + * @license + * Copyright Google Inc. All Rights Reserved. + * + * Use of this source code is governed by an MIT-style license that can be + * found in the LICENSE file at https://angular.io/license + */ +import {StyleSanitizeFn} from '../../sanitization/style_sanitizer'; +import {LContext, getContext} from '../context_discovery'; +import {LElementNode} from '../interfaces/node'; +import {PlayerContext} from '../interfaces/player'; +import {InitialStyles, StylingContext, StylingIndex} from '../interfaces/styling'; + +export const EMPTY_ARR: any[] = []; +export const EMPTY_OBJ: {[key: string]: any} = {}; + +export function createEmptyStylingContext( + element?: LElementNode | null, sanitizer?: StyleSanitizeFn | null, + initialStylingValues?: InitialStyles): StylingContext { + return [ + element || null, null, sanitizer || null, initialStylingValues || [null], 0, 0, null, null + ]; +} + +export function getOrCreatePlayerContext(target: {}, context?: LContext | null): PlayerContext { + context = context || getContext(target) !; + if (ngDevMode && !context) { + throw new Error( + 'Only elements that exist in an Angular application can be used for player access'); + } + + const {lViewData, lNodeIndex} = context; + const value = lViewData[lNodeIndex]; + let stylingContext = value as StylingContext; + if (!Array.isArray(value)) { + stylingContext = lViewData[lNodeIndex] = createEmptyStylingContext(value as LElementNode); + } + return stylingContext[StylingIndex.PlayerContext] || allocPlayerContext(stylingContext); +} + +function allocPlayerContext(data: StylingContext): PlayerContext { + return data[StylingIndex.PlayerContext] = []; +} diff --git a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json index 33f28bf3f6..b2980a5f73 100644 --- a/packages/core/test/bundling/animation_world/bundle.golden_symbols.json +++ b/packages/core/test/bundling/animation_world/bundle.golden_symbols.json @@ -276,7 +276,7 @@ "name": "addToViewTree" }, { - "name": "allocAnimationContext" + "name": "allocPlayerContext" }, { "name": "allocStylingContext" @@ -602,9 +602,6 @@ { "name": "getMultiStartIndex" }, - { - "name": "getOrCreateAnimationContext" - }, { "name": "getOrCreateInjectable" }, @@ -614,6 +611,9 @@ { "name": "getOrCreateNodeInjectorForNode" }, + { + "name": "getOrCreatePlayerContext" + }, { "name": "getOrCreateRenderer2" }, diff --git a/packages/core/test/render3/integration_spec.ts b/packages/core/test/render3/integration_spec.ts index 5a3b0a18c4..3256e03db7 100644 --- a/packages/core/test/render3/integration_spec.ts +++ b/packages/core/test/render3/integration_spec.ts @@ -21,7 +21,7 @@ import {Sanitizer, SecurityContext} from '../../src/sanitization/security'; import {NgIf} from './common_with_def'; import {ComponentFixture, TemplateFixture, createComponent, renderToHtml} from './render_util'; import {MONKEY_PATCH_KEY_NAME, getContext} from '../../src/render3/context_discovery'; -import {StylingIndex} from '../../src/render3/styling'; +import {StylingIndex} from '../../src/render3/interfaces/styling'; import {directiveInject} from '../../src/render3/di'; describe('render3 integration test', () => { diff --git a/packages/core/test/render3/render_util.ts b/packages/core/test/render3/render_util.ts index 0582ad8c9e..8136be1b23 100644 --- a/packages/core/test/render3/render_util.ts +++ b/packages/core/test/render3/render_util.ts @@ -14,7 +14,6 @@ import {stringifyElement} from '@angular/platform-browser/testing/src/browser_ut import {Injector} from '../../src/di/injector'; import {R3_CHANGE_DETECTOR_REF_FACTORY, R3_ELEMENT_REF_FACTORY, R3_TEMPLATE_REF_FACTORY, R3_VIEW_CONTAINER_REF_FACTORY} from '../../src/ivy_switch/runtime/ivy_switch_on'; -import {PlayerHandler} from '../../src/render3/animations/interfaces'; import {CreateComponentOptions} from '../../src/render3/component'; import {getContext, isComponentInstance} from '../../src/render3/context_discovery'; import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition'; @@ -23,6 +22,7 @@ import {ComponentTemplate, ComponentType, DirectiveDefInternal, DirectiveType, P import {renderTemplate} from '../../src/render3/instructions'; import {DirectiveDefList, DirectiveTypesOrFactory, PipeDefInternal, PipeDefList, PipeTypesOrFactory} from '../../src/render3/interfaces/definition'; import {LElementNode} from '../../src/render3/interfaces/node'; +import {PlayerHandler} from '../../src/render3/interfaces/player'; import {RElement, RText, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer'; import {Sanitizer} from '../../src/sanitization/security'; import {Type} from '../../src/type'; diff --git a/packages/core/test/render3/animations/core_player_handler_spec.ts b/packages/core/test/render3/styling/core_player_handler_spec.ts similarity index 92% rename from packages/core/test/render3/animations/core_player_handler_spec.ts rename to packages/core/test/render3/styling/core_player_handler_spec.ts index 2119476e88..b9ac3ad12c 100644 --- a/packages/core/test/render3/animations/core_player_handler_spec.ts +++ b/packages/core/test/render3/styling/core_player_handler_spec.ts @@ -5,8 +5,8 @@ * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.io/license */ -import {CorePlayerHandler} from '../../../src/render3/animations/core_player_handler'; -import {PlayState} from '../../../src/render3/animations/interfaces'; +import {CorePlayerHandler} from '../../../src/render3/styling/core_player_handler'; +import {PlayState} from '../../../src/render3/interfaces/player'; import {MockPlayer} from './mock_player'; describe('CorePlayerHandler', () => { diff --git a/packages/core/test/render3/animations/mock_player.ts b/packages/core/test/render3/styling/mock_player.ts similarity index 94% rename from packages/core/test/render3/animations/mock_player.ts rename to packages/core/test/render3/styling/mock_player.ts index 1836adeff4..5238a943a0 100644 --- a/packages/core/test/render3/animations/mock_player.ts +++ b/packages/core/test/render3/styling/mock_player.ts @@ -5,7 +5,7 @@ * 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 {PlayState, Player} from '../../../src/render3/animations/interfaces'; +import {PlayState, Player} from '../../../src/render3/interfaces/player'; export class MockPlayer implements Player { parent: Player|null = null; diff --git a/packages/core/test/render3/animations/players_spec.ts b/packages/core/test/render3/styling/players_spec.ts similarity index 95% rename from packages/core/test/render3/animations/players_spec.ts rename to packages/core/test/render3/styling/players_spec.ts index bf09f3474f..60839b8248 100644 --- a/packages/core/test/render3/animations/players_spec.ts +++ b/packages/core/test/render3/styling/players_spec.ts @@ -5,13 +5,13 @@ * 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 {ElementRef, TemplateRef} from '@angular/core'; import {RenderFlags} from '@angular/core/src/render3'; -import {AnimationContext, PlayState, Player, PlayerHandler} from '../../../src/render3/animations/interfaces'; -import {addPlayer, getOrCreateAnimationContext, getPlayers} from '../../../src/render3/animations/players'; +import {addPlayer, getPlayers} from '../../../src/render3/player'; +import {getOrCreatePlayerContext} from '../../../src/render3/styling/util'; import {QUERY_READ_FROM_NODE, defineComponent, getHostElement} from '../../../src/render3/index'; import {element, elementEnd, elementStart, elementStyling, elementStylingApply, load, markDirty} from '../../../src/render3/instructions'; +import {PlayerContext, PlayState, Player, PlayerHandler} from '../../../src/render3/interfaces/player'; import {RElement} from '../../../src/render3/interfaces/renderer'; import {QueryList, query, queryRefresh} from '../../../src/render3/query'; import {ComponentFixture} from '../render_util'; @@ -55,7 +55,7 @@ describe('animation player access', () => { it('should add a player to the element animation context and remove it once it completes', () => { const element = buildElement(); - const context = getOrCreateAnimationContext(element); + const context = getOrCreatePlayerContext(element); expect(context).toEqual([]); const player = new MockPlayer(); @@ -226,7 +226,7 @@ function buildElementWithStyling() { return fixture.hostElement.querySelector('div') as RElement; } -function readPlayers(context: AnimationContext): Player[] { +function readPlayers(context: PlayerContext): Player[] { return context; } diff --git a/packages/core/test/render3/styling_spec.ts b/packages/core/test/render3/styling/styling_spec.ts similarity index 97% rename from packages/core/test/render3/styling_spec.ts rename to packages/core/test/render3/styling/styling_spec.ts index 344c4a7e2f..662d1b24c3 100644 --- a/packages/core/test/render3/styling_spec.ts +++ b/packages/core/test/render3/styling/styling_spec.ts @@ -5,15 +5,16 @@ * 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 {elementEnd, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap} from '../../src/render3/instructions'; -import {InitialStylingFlags, RenderFlags} from '../../src/render3/interfaces/definition'; -import {LElementNode} from '../../src/render3/interfaces/node'; -import {Renderer3} from '../../src/render3/interfaces/renderer'; -import {StylingContext, StylingFlags, StylingIndex, allocStylingContext, createStylingContextTemplate, isContextDirty, renderStyling as _renderStyling, setContextDirty, updateClassProp, updateStyleProp, updateStylingMap} from '../../src/render3/styling'; -import {defaultStyleSanitizer} from '../../src/sanitization/sanitization'; -import {StyleSanitizeFn} from '../../src/sanitization/style_sanitizer'; +import {elementEnd, elementStart, elementStyleProp, elementStyling, elementStylingApply, elementStylingMap} from '../../../src/render3/instructions'; +import {InitialStylingFlags, RenderFlags} from '../../../src/render3/interfaces/definition'; +import {LElementNode} from '../../../src/render3/interfaces/node'; +import {Renderer3} from '../../../src/render3/interfaces/renderer'; +import {StylingContext, StylingFlags, StylingIndex} from '../../../src/render3/interfaces/styling'; +import {allocStylingContext, createStylingContextTemplate, isContextDirty, renderStyling as _renderStyling, setContextDirty, updateClassProp, updateStyleProp, updateStylingMap} from '../../../src/render3/styling/class_and_style_bindings'; +import {defaultStyleSanitizer} from '../../../src/sanitization/sanitization'; +import {StyleSanitizeFn} from '../../../src/sanitization/style_sanitizer'; -import {renderToHtml} from './render_util'; +import {renderToHtml} from '../render_util'; describe('styling', () => { let element: LElementNode|null = null;