refactor(ivy): remove all old styling code prior to refactor (#31193)
In the previous patch () all the existing styling code was turned off in favor of using the new refactored ivy styling code. This patch is a follow up patch to that and removes all old, unused styling code from the render3 directory. PR Close #31193
This commit is contained in:
parent
0e68c7edf9
commit
f50dede8f7
|
@ -12,7 +12,7 @@
|
|||
"master": {
|
||||
"uncompressed": {
|
||||
"runtime": 1440,
|
||||
"main": 14912,
|
||||
"main": 14021,
|
||||
"polyfills": 43567
|
||||
}
|
||||
}
|
||||
|
|
|
@ -249,15 +249,6 @@ export {
|
|||
LContext as ɵLContext,
|
||||
} from './render3/interfaces/context';
|
||||
|
||||
export {
|
||||
bindPlayerFactory as ɵbindPlayerFactory,
|
||||
} from './render3/styling/player_factory';
|
||||
|
||||
export {
|
||||
addPlayer as ɵaddPlayer,
|
||||
getPlayers as ɵgetPlayers,
|
||||
} from './render3/players';
|
||||
|
||||
// we reexport these symbols just so that they are retained during the dead code elimination
|
||||
// performed by rollup while it's creating fesm files.
|
||||
//
|
||||
|
|
|
@ -17,15 +17,13 @@ import {assertComponentType} from './assert';
|
|||
import {getComponentDef} from './definition';
|
||||
import {diPublicInInjector, getOrCreateNodeInjectorForNode} from './di';
|
||||
import {registerPostOrderHooks, registerPreOrderHooks} from './hooks';
|
||||
import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTNode, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews, renderInitialStyling} from './instructions/shared';
|
||||
import {CLEAN_PROMISE, addToViewTree, createLView, createTView, getOrCreateTNode, getOrCreateTView, initNodeFlags, instantiateRootComponent, invokeHostBindingsInCreationMode, locateHostElement, queueComponentIndexForCheck, refreshDescendantViews} from './instructions/shared';
|
||||
import {ComponentDef, ComponentType, RenderFlags} from './interfaces/definition';
|
||||
import {TElementNode, TNode, TNodeFlags, TNodeType} from './interfaces/node';
|
||||
import {PlayerHandler} from './interfaces/player';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||
import {CONTEXT, FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, RENDERER, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {applyOnCreateInstructions} from './node_util';
|
||||
import {CONTEXT, FLAGS, HEADER_OFFSET, LView, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||
import {enterView, getPreviousOrParentTNode, leaveView, resetComponentState, setActiveHostElement} from './state';
|
||||
import {renderInitialClasses, renderInitialStyles} from './styling/class_and_style_bindings';
|
||||
import {publishDefaultGlobalUtils} from './util/global_utils';
|
||||
import {defaultScheduler, stringifyForError} from './util/misc_utils';
|
||||
import {getRootContext} from './util/view_traversal_utils';
|
||||
|
@ -225,17 +223,10 @@ export function createRootComponent<T>(
|
|||
const expando = tView.expandoInstructions !;
|
||||
invokeHostBindingsInCreationMode(
|
||||
componentDef, expando, component, rootTNode, tView.firstTemplatePass);
|
||||
rootTNode.onElementCreationFns && applyOnCreateInstructions(rootTNode);
|
||||
|
||||
setActiveHostElement(null);
|
||||
}
|
||||
|
||||
if (rootTNode.classes !== null || rootTNode.styles !== null) {
|
||||
const native = componentView[HOST] !as RElement;
|
||||
const renderer = componentView[RENDERER];
|
||||
renderInitialStyling(renderer, native, rootTNode);
|
||||
}
|
||||
|
||||
return component;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,5 +16,4 @@
|
|||
*/
|
||||
|
||||
export {markDirty} from './instructions/all';
|
||||
export {getPlayers} from './players';
|
||||
export {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent} from './util/discovery_utils';
|
||||
|
|
|
@ -24,7 +24,6 @@ import {COMMENT_MARKER, ELEMENT_MARKER, I18nMutateOpCode, I18nMutateOpCodes, I18
|
|||
import {TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeType, TProjectionNode} from './interfaces/node';
|
||||
import {RComment, RElement, RText} from './interfaces/renderer';
|
||||
import {SanitizerFn} from './interfaces/sanitization';
|
||||
import {StylingContext} from './interfaces/styling';
|
||||
import {isLContainer} from './interfaces/type_checks';
|
||||
import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, TView, T_HOST} from './interfaces/view';
|
||||
import {appendChild, appendProjectedNodes, createTextNode, nativeRemoveNode} from './node_manipulation';
|
||||
|
@ -915,7 +914,7 @@ function removeNode(index: number, viewData: LView) {
|
|||
nativeRemoveNode(viewData[RENDERER], removedPhRNode);
|
||||
}
|
||||
|
||||
const slotValue = ɵɵload(index) as RElement | RComment | LContainer | StylingContext;
|
||||
const slotValue = ɵɵload(index) as RElement | RComment | LContainer;
|
||||
if (isLContainer(slotValue)) {
|
||||
const lContainer = slotValue as LContainer;
|
||||
if (removedPhTNode.type !== TNodeType.Container) {
|
||||
|
|
|
@ -43,7 +43,7 @@ export * from './projection';
|
|||
export * from './property';
|
||||
export * from './property_interpolation';
|
||||
export * from './select';
|
||||
export {ɵɵstyling, ɵɵstyleSanitizer, ɵɵstyleMap, ɵɵclassMap, ɵɵstyleProp, ɵɵclassProp, ɵɵstylingApply} from '../styling_next/instructions';
|
||||
export * from '../styling_next/instructions';
|
||||
export * from './text';
|
||||
export * from './text_interpolation';
|
||||
export * from './class_map_interpolation';
|
||||
|
|
|
@ -16,7 +16,6 @@ import {isContentQueryHost} from '../interfaces/type_checks';
|
|||
import {BINDING_INDEX, HEADER_OFFSET, LView, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeType} from '../node_assert';
|
||||
import {appendChild} from '../node_manipulation';
|
||||
import {applyOnCreateInstructions} from '../node_util';
|
||||
import {decreaseElementDepthCount, getElementDepthCount, getIsParent, getLView, getPreviousOrParentTNode, getSelectedIndex, increaseElementDepthCount, setIsNotParent, setPreviousOrParentTNode} from '../state';
|
||||
import {registerInitialStylingOnTNode} from '../styling_next/instructions';
|
||||
import {StylingMapArray, TStylingContext} from '../styling_next/interfaces';
|
||||
|
@ -120,12 +119,8 @@ export function ɵɵelementEnd(): void {
|
|||
}
|
||||
|
||||
const tNode = previousOrParentTNode;
|
||||
|
||||
// this is required for all host-level styling-related instructions to run
|
||||
// in the correct order
|
||||
tNode.onElementCreationFns && applyOnCreateInstructions(tNode);
|
||||
|
||||
ngDevMode && assertNodeType(tNode, TNodeType.Element);
|
||||
|
||||
const lView = getLView();
|
||||
const tView = lView[TVIEW];
|
||||
|
||||
|
|
|
@ -14,7 +14,6 @@ import {isContentQueryHost} from '../interfaces/type_checks';
|
|||
import {BINDING_INDEX, HEADER_OFFSET, RENDERER, TVIEW, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeType} from '../node_assert';
|
||||
import {appendChild} from '../node_manipulation';
|
||||
import {applyOnCreateInstructions} from '../node_util';
|
||||
import {getIsParent, getLView, getPreviousOrParentTNode, setIsNotParent, setPreviousOrParentTNode} from '../state';
|
||||
import {registerInitialStylingOnTNode} from '../styling_next/instructions';
|
||||
|
||||
|
@ -95,10 +94,6 @@ export function ɵɵelementContainerEnd(): void {
|
|||
|
||||
ngDevMode && assertNodeType(previousOrParentTNode, TNodeType.ElementContainer);
|
||||
|
||||
// this is required for all host-level styling-related instructions to run
|
||||
// in the correct order
|
||||
previousOrParentTNode.onElementCreationFns && applyOnCreateInstructions(previousOrParentTNode);
|
||||
|
||||
registerPostOrderHooks(tView, previousOrParentTNode);
|
||||
|
||||
if (tView.firstTemplatePass && tView.queries !== null &&
|
||||
|
|
|
@ -17,8 +17,6 @@ import {PropertyAliases, TContainerNode, TElementNode, TNode as ITNode, TNode, T
|
|||
import {SelectorFlags} from '../interfaces/projection';
|
||||
import {TQueries} from '../interfaces/query';
|
||||
import {RComment, RElement, RNode} from '../interfaces/renderer';
|
||||
import {StylingContext} from '../interfaces/styling';
|
||||
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, HookData, INJECTOR, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, SANITIZER, TData, TVIEW, TView as ITView, TView, T_HOST} from '../interfaces/view';
|
||||
import {TStylingContext} from '../styling_next/interfaces';
|
||||
import {DebugStyling as DebugNewStyling, NodeStylingDebug} from '../styling_next/styling_debug';
|
||||
|
@ -129,9 +127,7 @@ export const TNodeConstructor = class TNode implements ITNode {
|
|||
public projectionNext: ITNode|null, //
|
||||
public child: ITNode|null, //
|
||||
public parent: TElementNode|TContainerNode|null, //
|
||||
public stylingTemplate: StylingContext|null, //
|
||||
public projection: number|(ITNode|RNode[])[]|null, //
|
||||
public onElementCreationFns: Function[]|null, //
|
||||
public styles: TStylingContext|null, //
|
||||
public classes: TStylingContext|null, //
|
||||
) {}
|
||||
|
@ -331,7 +327,6 @@ export function toDebugNodes(tNode: TNode | null, lView: LView): DebugNode[]|nul
|
|||
const rawValue = lView[tNode.index];
|
||||
const native = unwrapRNode(rawValue);
|
||||
const componentLViewDebug = toDebug(readLViewValue(rawValue));
|
||||
|
||||
const styles = isStylingContext(tNode.styles) ?
|
||||
new NodeStylingDebug(tNode.styles as any as TStylingContext, lView) :
|
||||
null;
|
||||
|
@ -362,7 +357,7 @@ export class LContainerDebug {
|
|||
}
|
||||
get parent(): LViewDebug|LContainerDebug|null { return toDebug(this._raw_lContainer[PARENT]); }
|
||||
get movedViews(): LView[]|null { return this._raw_lContainer[MOVED_VIEWS]; }
|
||||
get host(): RElement|RComment|StylingContext|LView { return this._raw_lContainer[HOST]; }
|
||||
get host(): RElement|RComment|LView { return this._raw_lContainer[HOST]; }
|
||||
get native(): RComment { return this._raw_lContainer[NATIVE]; }
|
||||
get __other__() {
|
||||
return {
|
||||
|
|
|
@ -25,17 +25,14 @@ import {INJECTOR_BLOOM_PARENT_SIZE, NodeInjectorFactory} from '../interfaces/inj
|
|||
import {AttributeMarker, InitialInputData, InitialInputs, LocalRefExtractor, PropertyAliasValue, PropertyAliases, TAttributes, TContainerNode, TElementContainerNode, TElementNode, TIcuContainerNode, TNode, TNodeFlags, TNodeProviderIndexes, TNodeType, TProjectionNode, TViewNode} from '../interfaces/node';
|
||||
import {RComment, RElement, RText, Renderer3, RendererFactory3, isProceduralRenderer} from '../interfaces/renderer';
|
||||
import {SanitizerFn} from '../interfaces/sanitization';
|
||||
import {StylingContext} from '../interfaces/styling';
|
||||
import {isComponent, isComponentDef, isContentQueryHost, isLContainer, isRootView} from '../interfaces/type_checks';
|
||||
import {BINDING_INDEX, CHILD_HEAD, CHILD_TAIL, CLEANUP, CONTEXT, DECLARATION_VIEW, ExpandoInstructions, FLAGS, HEADER_OFFSET, HOST, INJECTOR, InitPhaseState, LView, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, RENDERER_FACTORY, RootContext, RootContextFlags, SANITIZER, TData, TVIEW, TView, T_HOST} from '../interfaces/view';
|
||||
import {assertNodeOfPossibleTypes, assertNodeType} from '../node_assert';
|
||||
import {isNodeMatchingSelectorList} from '../node_selector_matcher';
|
||||
import {enterView, getBindingsEnabled, getCheckNoChangesMode, getIsParent, getLView, getNamespace, getPreviousOrParentTNode, getSelectedIndex, incrementActiveDirectiveId, isCreationMode, leaveView, namespaceHTMLInternal, setActiveHostElement, setBindingRoot, setCheckNoChangesMode, setCurrentDirectiveDef, setCurrentQueryIndex, setIsParent, setPreviousOrParentTNode, setSelectedIndex} from '../state';
|
||||
import {initializeStaticContext as initializeStaticStylingContext} from '../styling/class_and_style_bindings';
|
||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../styling/util';
|
||||
import {renderStylingMap} from '../styling_next/bindings';
|
||||
import {getInitialStylingValue, getStylingMapArray} from '../styling_next/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {ANIMATION_PROP_PREFIX, isAnimationProp} from '../util/attrs_utils';
|
||||
import {INTERPOLATION_DELIMITER, renderStringify, stringifyForError} from '../util/misc_utils';
|
||||
import {getLViewParent, getRootContext} from '../util/view_traversal_utils';
|
||||
import {getComponentViewByIndex, getNativeByIndex, getNativeByTNode, getTNode, readPatchedLView, resetPreOrderHookFlags, unwrapRNode, viewAttachedToChangeDetector} from '../util/view_utils';
|
||||
|
@ -760,11 +757,9 @@ export function createTNode(
|
|||
null, // projectionNext: ITNode|null
|
||||
null, // child: ITNode|null
|
||||
tParent, // parent: TElementNode|TContainerNode|null
|
||||
null, // stylingTemplate: StylingContext|null
|
||||
null, // projection: number|(ITNode|RNode[])[]|null
|
||||
null, // onElementCreationFns: Function[]|null
|
||||
null, // newStyles: TStylingContext|null
|
||||
null, // newClasses: TStylingContext|null
|
||||
null, // styles: TStylingContext|null
|
||||
null, // classes: TStylingContext|null
|
||||
) :
|
||||
{
|
||||
type: type,
|
||||
|
@ -787,9 +782,7 @@ export function createTNode(
|
|||
projectionNext: null,
|
||||
child: null,
|
||||
parent: tParent,
|
||||
stylingTemplate: null,
|
||||
projection: null,
|
||||
onElementCreationFns: null,
|
||||
styles: null,
|
||||
classes: null,
|
||||
};
|
||||
|
@ -1474,8 +1467,8 @@ const LContainerArray: any = ngDevMode && createNamedArrayType('LContainer');
|
|||
* @returns LContainer
|
||||
*/
|
||||
export function createLContainer(
|
||||
hostNative: RElement | RComment | StylingContext | LView, currentView: LView, native: RComment,
|
||||
tNode: TNode, isForViewContainerRef?: boolean): LContainer {
|
||||
hostNative: RElement | RComment | LView, currentView: LView, native: RComment, tNode: TNode,
|
||||
isForViewContainerRef?: boolean): LContainer {
|
||||
ngDevMode && assertDomNode(native);
|
||||
ngDevMode && assertLView(currentView);
|
||||
// https://jsperf.com/array-literal-vs-new-array-really
|
||||
|
|
|
@ -1,376 +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 {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||
import {assertEqual} from '../../util/assert';
|
||||
import {TNode, TNodeType} from '../interfaces/node';
|
||||
import {PlayerFactory} from '../interfaces/player';
|
||||
import {FLAGS, HEADER_OFFSET, LView, LViewFlags, RENDERER, RootContextFlags} from '../interfaces/view';
|
||||
import {getActiveDirectiveId, getActiveDirectiveSuperClassDepth, getLView, getPreviousOrParentTNode, getSelectedIndex} from '../state';
|
||||
import {getInitialClassNameValue, renderStyling, updateClassMap, updateClassProp as updateclassProp, updateContextWithBindings, updateStyleMap, updateStyleProp as updatestyleProp} from '../styling/class_and_style_bindings';
|
||||
import {ParamsOf, enqueueHostInstruction, registerHostDirective} from '../styling/host_instructions_queue';
|
||||
import {BoundPlayerFactory} from '../styling/player_factory';
|
||||
import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from '../styling/shared';
|
||||
import {getCachedStylingContext, setCachedStylingContext} from '../styling/state';
|
||||
import {allocateOrUpdateDirectiveIntoContext, createEmptyStylingContext, forceClassesAsString, forceStylesAsString, getStylingContextFromLView} from '../styling/util';
|
||||
import {hasClassInput, hasStyleInput} from '../styling_next/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {renderStringify} from '../util/misc_utils';
|
||||
import {getRootContext} from '../util/view_traversal_utils';
|
||||
import {getTNode} from '../util/view_utils';
|
||||
|
||||
import {scheduleTick, setInputsForProperty} from './shared';
|
||||
|
||||
|
||||
|
||||
/*
|
||||
* The contents of this file include the instructions for all styling-related
|
||||
* operations in Angular.
|
||||
*
|
||||
* The instructions present in this file are:
|
||||
*
|
||||
* Template level styling instructions:
|
||||
* - styling
|
||||
* - styleMap
|
||||
* - classMap
|
||||
* - styleProp
|
||||
* - classProp
|
||||
* - stylingApply
|
||||
*/
|
||||
|
||||
/**
|
||||
* Allocates style and class binding properties on the element during creation mode.
|
||||
*
|
||||
* This instruction is meant to be called during creation mode to register all
|
||||
* dynamic style and class bindings on the element. Note that this is only used
|
||||
* for binding values (see `elementStart` to learn how to assign static styling
|
||||
* values to an element).
|
||||
*
|
||||
* @param classBindingNames An array containing bindable class names.
|
||||
* The `classProp` instruction refers to the class name by index in
|
||||
* this array (i.e. `['foo', 'bar']` means `foo=0` and `bar=1`).
|
||||
* @param styleBindingNames An array containing bindable style properties.
|
||||
* The `styleProp` instruction refers to the class name by index in
|
||||
* this array (i.e. `['width', 'height']` means `width=0` and `height=1`).
|
||||
* @param styleSanitizer An optional sanitizer function that will be used to sanitize any CSS
|
||||
* style values that are applied to the element (during rendering).
|
||||
*
|
||||
* Note that this will allocate the provided style/class bindings to the host element if
|
||||
* this function is called within a host binding.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstyling(
|
||||
classBindingNames?: string[] | null, styleBindingNames?: string[] | null,
|
||||
styleSanitizer?: StyleSanitizeFn | null): void {
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
if (!tNode.stylingTemplate) {
|
||||
tNode.stylingTemplate = createEmptyStylingContext();
|
||||
}
|
||||
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
if (directiveStylingIndex) {
|
||||
// despite the binding being applied in a queue (below), the allocation
|
||||
// of the directive into the context happens right away. The reason for
|
||||
// this is to retain the ordering of the directives (which is important
|
||||
// for the prioritization of bindings).
|
||||
allocateOrUpdateDirectiveIntoContext(tNode.stylingTemplate, directiveStylingIndex);
|
||||
|
||||
const fns = tNode.onElementCreationFns = tNode.onElementCreationFns || [];
|
||||
fns.push(() => {
|
||||
initStyling(
|
||||
tNode, classBindingNames, styleBindingNames, styleSanitizer, directiveStylingIndex);
|
||||
registerHostDirective(tNode.stylingTemplate !, directiveStylingIndex);
|
||||
});
|
||||
} else {
|
||||
// calling the function below ensures that the template's binding values
|
||||
// are applied as the first set of bindings into the context. If any other
|
||||
// styling bindings are set on the same element (by directives and/or
|
||||
// components) then they will be applied at the end of the `elementEnd`
|
||||
// instruction (because directives are created first before styling is
|
||||
// executed for a new element).
|
||||
initStyling(
|
||||
tNode, classBindingNames, styleBindingNames, styleSanitizer,
|
||||
DEFAULT_TEMPLATE_DIRECTIVE_INDEX);
|
||||
}
|
||||
}
|
||||
|
||||
function initStyling(
|
||||
tNode: TNode, classBindingNames: string[] | null | undefined,
|
||||
styleBindingNames: string[] | null | undefined,
|
||||
styleSanitizer: StyleSanitizeFn | null | undefined, directiveStylingIndex: number): void {
|
||||
updateContextWithBindings(
|
||||
tNode.stylingTemplate !, directiveStylingIndex, classBindingNames, styleBindingNames,
|
||||
styleSanitizer);
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update a style binding on an element with the provided value.
|
||||
*
|
||||
* If the style value is falsy then it will be removed from the element
|
||||
* (or assigned a different value depending if there are any styles placed
|
||||
* on the element with `styleMap` or any static styles that are
|
||||
* present from when the element was created with `styling`).
|
||||
*
|
||||
* Note that the styling element is updated as part of `stylingApply`.
|
||||
*
|
||||
* @param styleIndex Index of style to update. This index value refers to the
|
||||
* index of the style in the style bindings array that was passed into
|
||||
* `styling`.
|
||||
* @param value New value to write (falsy to remove).
|
||||
* @param suffix Optional suffix. Used with scalar values to add unit such as `px`.
|
||||
* Note that when a suffix is provided then the underlying sanitizer will
|
||||
* be ignored.
|
||||
* @param forceOverride Whether or not to update the styling value immediately
|
||||
* (despite the other bindings possibly having priority)
|
||||
*
|
||||
* Note that this will apply the provided style value to the host element if this function is called
|
||||
* within a host binding.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstyleProp(
|
||||
styleIndex: number, value: string | number | String | PlayerFactory | null,
|
||||
suffix?: string | null, forceOverride?: boolean): void {
|
||||
stylePropInternal(
|
||||
getLView(), getSelectedIndex(), styleIndex, getActiveDirectiveStylingIndex(), value, suffix,
|
||||
forceOverride);
|
||||
}
|
||||
|
||||
export function stylePropInternal(
|
||||
lView: LView, selectedIndex: number, styleIndex: number, directiveStylingIndex: number,
|
||||
value: string | number | String | PlayerFactory | null, suffix?: string | null,
|
||||
forceOverride?: boolean): void {
|
||||
const valueToAdd = resolveStylePropValue(value, suffix);
|
||||
const stylingContext = getStylingContext(selectedIndex, lView);
|
||||
if (directiveStylingIndex) {
|
||||
const args: ParamsOf<typeof updatestyleProp> =
|
||||
[stylingContext, styleIndex, valueToAdd, directiveStylingIndex, forceOverride];
|
||||
enqueueHostInstruction(stylingContext, directiveStylingIndex, updatestyleProp, args);
|
||||
} else {
|
||||
updatestyleProp(
|
||||
stylingContext, styleIndex, valueToAdd, DEFAULT_TEMPLATE_DIRECTIVE_INDEX, forceOverride);
|
||||
}
|
||||
}
|
||||
|
||||
function resolveStylePropValue(
|
||||
value: string | number | String | PlayerFactory | null, suffix: string | null | undefined) {
|
||||
let valueToAdd: string|null = null;
|
||||
if (value !== null) {
|
||||
if (suffix) {
|
||||
// when a suffix is applied then it will bypass
|
||||
// sanitization entirely (b/c a new string is created)
|
||||
valueToAdd = renderStringify(value) + suffix;
|
||||
} else {
|
||||
// sanitization happens by dealing with a String value
|
||||
// this means that the string value will be passed through
|
||||
// into the style rendering later (which is where the value
|
||||
// will be sanitized before it is applied)
|
||||
valueToAdd = value as any as string;
|
||||
}
|
||||
}
|
||||
return valueToAdd;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update a class binding on an element with the provided value.
|
||||
*
|
||||
* This instruction is meant to handle the `[class.foo]="exp"` case and,
|
||||
* therefore, the class binding itself must already be allocated using
|
||||
* `styling` within the creation block.
|
||||
*
|
||||
* @param classIndex Index of class to toggle. This index value refers to the
|
||||
* index of the class in the class bindings array that was passed into
|
||||
* `styling` (which is meant to be called before this
|
||||
* function is).
|
||||
* @param value A true/false value which will turn the class on or off.
|
||||
* @param forceOverride Whether or not this value will be applied regardless
|
||||
* of where it is being set within the styling priority structure.
|
||||
*
|
||||
* Note that this will apply the provided class value to the host element if this function
|
||||
* is called within a host binding.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵclassProp(
|
||||
classIndex: number, value: boolean | PlayerFactory, forceOverride?: boolean): void {
|
||||
const index = getSelectedIndex();
|
||||
const input = (value instanceof BoundPlayerFactory) ?
|
||||
(value as BoundPlayerFactory<boolean|null>) :
|
||||
booleanOrNull(value);
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
const stylingContext = getStylingContext(index, getLView());
|
||||
if (directiveStylingIndex) {
|
||||
const args: ParamsOf<typeof updateclassProp> =
|
||||
[stylingContext, classIndex, input, directiveStylingIndex, forceOverride];
|
||||
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateclassProp, args);
|
||||
} else {
|
||||
updateclassProp(
|
||||
stylingContext, classIndex, input, DEFAULT_TEMPLATE_DIRECTIVE_INDEX, forceOverride);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function booleanOrNull(value: any): boolean|null {
|
||||
if (typeof value === 'boolean') return value;
|
||||
return value ? true : null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update style bindings using an object literal on an element.
|
||||
*
|
||||
* This instruction is meant to apply styling via the `[style]="exp"` template bindings.
|
||||
* When styles are applied to the element they will then be updated with respect to
|
||||
* any styles/classes set via `styleProp`. If any styles are set to falsy
|
||||
* then they will be removed from the element.
|
||||
*
|
||||
* Note that the styling instruction will not be applied until `stylingApply` is called.
|
||||
*
|
||||
* @param styles A key/value style map of the styles that will be applied to the given element.
|
||||
* Any missing styles (that have already been applied to the element beforehand) will be
|
||||
* removed (unset) from the element's styling.
|
||||
*
|
||||
* Note that this will apply the provided styleMap value to the host element if this function
|
||||
* is called within a host binding.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstyleMap(styles: {[styleName: string]: any} | NO_CHANGE | null): void {
|
||||
const index = getSelectedIndex();
|
||||
const lView = getLView();
|
||||
const stylingContext = getStylingContext(index, lView);
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
if (directiveStylingIndex) {
|
||||
const args: ParamsOf<typeof updateStyleMap> = [stylingContext, styles, directiveStylingIndex];
|
||||
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateStyleMap, args);
|
||||
} else {
|
||||
const tNode = getTNode(index, lView);
|
||||
|
||||
// inputs are only evaluated from a template binding into a directive, therefore,
|
||||
// there should not be a situation where a directive host bindings function
|
||||
// evaluates the inputs (this should only happen in the template function)
|
||||
if (hasStyleInput(tNode) && styles !== NO_CHANGE) {
|
||||
const initialStyles = getInitialClassNameValue(stylingContext);
|
||||
const styleInputVal =
|
||||
(initialStyles.length ? (initialStyles + ' ') : '') + forceStylesAsString(styles);
|
||||
setInputsForProperty(lView, tNode.inputs !['style'] !, styleInputVal);
|
||||
styles = NO_CHANGE;
|
||||
}
|
||||
updateStyleMap(stylingContext, styles);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Update class bindings using an object literal or class-string on an element.
|
||||
*
|
||||
* This instruction is meant to apply styling via the `[class]="exp"` template bindings.
|
||||
* When classes are applied to the element they will then be updated with
|
||||
* respect to any styles/classes set via `classProp`. If any
|
||||
* classes are set to falsy then they will be removed from the element.
|
||||
*
|
||||
* Note that the styling instruction will not be applied until `stylingApply` is called.
|
||||
* Note that this will the provided classMap value to the host element if this function is called
|
||||
* within a host binding.
|
||||
*
|
||||
* @param classes A key/value map or string of CSS classes that will be added to the
|
||||
* given element. Any missing classes (that have already been applied to the element
|
||||
* beforehand) will be removed (unset) from the element's list of CSS classes.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵclassMap(classes: {[styleName: string]: any} | string | null): void {
|
||||
classMapInternal(getLView(), getSelectedIndex(), getActiveDirectiveStylingIndex(), classes);
|
||||
}
|
||||
|
||||
export function classMapInternal(
|
||||
lView: LView, selectedIndex: number, directiveStylingIndex: number,
|
||||
classes: {[styleName: string]: any} | string | null) {
|
||||
const stylingContext = getStylingContext(selectedIndex, lView);
|
||||
if (directiveStylingIndex) {
|
||||
const args: ParamsOf<typeof updateClassMap> = [stylingContext, classes, directiveStylingIndex];
|
||||
enqueueHostInstruction(stylingContext, directiveStylingIndex, updateClassMap, args);
|
||||
} else {
|
||||
const tNode = getTNode(selectedIndex, lView);
|
||||
// inputs are only evaluated from a template binding into a directive, therefore,
|
||||
// there should not be a situation where a directive host bindings function
|
||||
// evaluates the inputs (this should only happen in the template function)
|
||||
if (hasClassInput(tNode)) {
|
||||
const initialClasses = getInitialClassNameValue(stylingContext);
|
||||
const classInputVal =
|
||||
(initialClasses.length ? (initialClasses + ' ') : '') + forceClassesAsString(classes);
|
||||
setInputsForProperty(lView, tNode.inputs !['class'] !, classInputVal);
|
||||
classes = NO_CHANGE;
|
||||
}
|
||||
updateClassMap(stylingContext, classes);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Apply all style and class binding values to the element.
|
||||
*
|
||||
* This instruction is meant to be run after `styleMap`, `classMap`,
|
||||
* `styleProp` or `classProp` instructions have been run and will
|
||||
* only apply styling to the element if any styling bindings have been updated.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstylingApply(): void {
|
||||
const index = getSelectedIndex();
|
||||
const directiveStylingIndex =
|
||||
getActiveDirectiveStylingIndex() || DEFAULT_TEMPLATE_DIRECTIVE_INDEX;
|
||||
const lView = getLView();
|
||||
const tNode = getTNode(index, lView);
|
||||
|
||||
// if a non-element value is being processed then we can't render values
|
||||
// on the element at all therefore by setting the renderer to null then
|
||||
// the styling apply code knows not to actually apply the values...
|
||||
const renderer = tNode.type === TNodeType.Element ? lView[RENDERER] : null;
|
||||
const isFirstRender = (lView[FLAGS] & LViewFlags.FirstLViewPass) !== 0;
|
||||
const stylingContext = getStylingContext(index, lView);
|
||||
|
||||
const totalPlayersQueued = renderStyling(
|
||||
stylingContext, renderer, lView, isFirstRender, null, null, directiveStylingIndex);
|
||||
if (totalPlayersQueued > 0) {
|
||||
const rootContext = getRootContext(lView);
|
||||
scheduleTick(rootContext, RootContextFlags.FlushPlayers);
|
||||
}
|
||||
|
||||
// because select(n) may not run between every instruction, the cached styling
|
||||
// context may not get cleared between elements. The reason for this is because
|
||||
// styling bindings (like `[style]` and `[class]`) are not recognized as property
|
||||
// bindings by default so a select(n) instruction is not generated. To ensure the
|
||||
// context is loaded correctly for the next element the cache below is pre-emptively
|
||||
// cleared because there is no code in Angular that applies more styling code after a
|
||||
// styling flush has occurred. Note that this will be fixed once FW-1254 lands.
|
||||
setCachedStylingContext(null);
|
||||
}
|
||||
|
||||
export function getActiveDirectiveStylingIndex() {
|
||||
// whenever a directive's hostBindings function is called a uniqueId value
|
||||
// is assigned. Normally this is enough to help distinguish one directive
|
||||
// from another for the styling context, but there are situations where a
|
||||
// sub-class directive could inherit and assign styling in concert with a
|
||||
// parent directive. To help the styling code distinguish between a parent
|
||||
// sub-classed directive the inheritance depth is taken into account as well.
|
||||
return getActiveDirectiveId() + getActiveDirectiveSuperClassDepth();
|
||||
}
|
||||
|
||||
function getStylingContext(index: number, lView: LView) {
|
||||
let context = getCachedStylingContext();
|
||||
if (!context) {
|
||||
context = getStylingContextFromLView(index + HEADER_OFFSET, lView);
|
||||
setCachedStylingContext(context);
|
||||
} else if (ngDevMode) {
|
||||
const actualContext = getStylingContextFromLView(index + HEADER_OFFSET, lView);
|
||||
assertEqual(context, actualContext, 'The cached styling context is invalid');
|
||||
}
|
||||
return context;
|
||||
}
|
|
@ -6,14 +6,11 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
import {StylingMapArray, TStylingContext} from '../styling_next/interfaces';
|
||||
|
||||
import {CssSelector} from './projection';
|
||||
import {RNode} from './renderer';
|
||||
import {StylingContext} from './styling';
|
||||
import {LView, TView} from './view';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* TNodeType corresponds to the {@link TNode} `type` property.
|
||||
*/
|
||||
|
@ -405,7 +402,6 @@ export interface TNode {
|
|||
*/
|
||||
parent: TElementNode|TContainerNode|null;
|
||||
|
||||
stylingTemplate: StylingContext|null;
|
||||
/**
|
||||
* List of projected TNodes for a given component host element OR index into the said nodes.
|
||||
*
|
||||
|
@ -447,19 +443,6 @@ export interface TNode {
|
|||
*/
|
||||
projection: (TNode|RNode[])[]|number|null;
|
||||
|
||||
/**
|
||||
* A buffer of functions that will be called once `elementEnd` (or `element`) completes.
|
||||
*
|
||||
* Due to the nature of how directives work in Angular, some directive code may
|
||||
* need to fire after any template-level code runs. If present, this array will
|
||||
* be flushed (each function will be invoked) once the associated element is
|
||||
* created.
|
||||
*
|
||||
* If an element is created multiple times then this function will be populated
|
||||
* with functions each time the creation block is called.
|
||||
*/
|
||||
onElementCreationFns: Function[]|null;
|
||||
|
||||
/**
|
||||
* A collection of all style bindings and/or static style values for an element.
|
||||
*
|
||||
|
|
|
@ -1,790 +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 {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||
import {RElement} from '../interfaces/renderer';
|
||||
import {LContainer} from './container';
|
||||
import {PlayerContext} from './player';
|
||||
import {LView} from './view';
|
||||
|
||||
|
||||
/**
|
||||
* 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. It also stores the static style/class values that were
|
||||
* extracted from the template by the compiler.
|
||||
*
|
||||
* A context is created by Angular when:
|
||||
* 1. An element contains static styling values (like style="..." or class="...")
|
||||
* 2. An element contains single property binding values (like [style.prop]="x" or
|
||||
* [class.prop]="y")
|
||||
* 3. An element contains multi property binding values (like [style]="x" or [class]="y")
|
||||
* 4. A directive contains host bindings for static, single or multi styling properties/bindings.
|
||||
* 5. An animation player is added to an element via `addPlayer`
|
||||
*
|
||||
* Note that even if an element contains static styling then this context will be created and
|
||||
* attached to it. The reason why this happens (instead of treating styles/classes as regular
|
||||
* HTML attributes) is because the style/class bindings must be able to default themselves back
|
||||
* to their respective static values when they are set to null.
|
||||
*
|
||||
* Say for example we have this:
|
||||
* ```
|
||||
* <!-- when `myWidthExp=null` then a width of `100px`
|
||||
* will be used a default value for width -->
|
||||
* <div style="width:100px" [style.width]="myWidthExp"></div>
|
||||
* ```
|
||||
*
|
||||
* Even in the situation where there are no bindings, the static styling is still placed into the
|
||||
* context because there may be another directive on the same element that has styling.
|
||||
*
|
||||
* When Angular initializes styling data for an element then it will first register the static
|
||||
* styling values on the element using one of these two instructions:
|
||||
*
|
||||
* 1. elementStart or element (within the template function of a component)
|
||||
* 2. elementHostAttrs (for directive host bindings)
|
||||
*
|
||||
* In either case, a styling context will be created and stored within an element's `LViewData`.
|
||||
* Once the styling context is created then single and multi properties can be stored within it.
|
||||
* For this to happen, the following function needs to be called:
|
||||
*
|
||||
* `styling` (called with style properties, class properties and a sanitizer + a directive
|
||||
* instance).
|
||||
*
|
||||
* When this instruction is called it will populate the styling context with the provided style
|
||||
* and class names into the context.
|
||||
*
|
||||
* The context itself looks like this:
|
||||
*
|
||||
* context = [
|
||||
* // 0-8: header values (about 8 entries of configuration data)
|
||||
* // 9+: this is where each entry is stored:
|
||||
* ]
|
||||
*
|
||||
* Let's say we have the following template code:
|
||||
*
|
||||
* ```
|
||||
* <div class="foo bar"
|
||||
* style="width:200px; color:red"
|
||||
* [style.width]="myWidthExp"
|
||||
* [style.height]="myHeightExp"
|
||||
* [class.baz]="myBazExp">
|
||||
* ```
|
||||
*
|
||||
* The context generated from these values will look like this (note that
|
||||
* for each binding name (the class and style bindings) the values will
|
||||
* be inserted twice into the array (once for single property entries and
|
||||
* again for multi property entries).
|
||||
*
|
||||
* context = [
|
||||
* // 0-8: header values (about 8 entries of configuration data)
|
||||
* // 9+: this is where each entry is stored:
|
||||
*
|
||||
* // SINGLE PROPERTIES
|
||||
* configForWidth,
|
||||
* 'width'
|
||||
* myWidthExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForHeight,
|
||||
* 'height'
|
||||
* myHeightExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForBazClass,
|
||||
* 'baz
|
||||
* myBazClassExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* // MULTI PROPERTIES
|
||||
* configForWidth,
|
||||
* 'width'
|
||||
* myWidthExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForHeight,
|
||||
* 'height'
|
||||
* myHeightExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
*
|
||||
* configForBazClass,
|
||||
* 'baz
|
||||
* myBazClassExp, // the binding value not the binding itself
|
||||
* 0, // the directive owner
|
||||
* ]
|
||||
*
|
||||
* The configuration values are left out of the example above because
|
||||
* the ordering of them could change between code patches. Please read the
|
||||
* documentation below to get a better understand of what the configuration
|
||||
* values are and how they work.
|
||||
*
|
||||
* Each time a binding property is updated (whether it be through a single
|
||||
* property instruction like `styleProp`, `classProp`,
|
||||
* `styleMap` or `classMap`) then the values in the context will be updated as
|
||||
* well.
|
||||
*
|
||||
* If for example `[style.width]` updates to `555px` then its value will be reflected
|
||||
* in the context as so:
|
||||
*
|
||||
* context = [
|
||||
* // ...
|
||||
* configForWidth, // this will be marked DIRTY
|
||||
* 'width'
|
||||
* '555px',
|
||||
* 0,
|
||||
* //..
|
||||
* ]
|
||||
*
|
||||
* The context and directive data will also be marked dirty.
|
||||
*
|
||||
* Despite the context being updated, nothing has been rendered on screen (not styles or
|
||||
* classes have been set on the element). To kick off rendering for an element the following
|
||||
* function needs to be run `stylingApply`.
|
||||
*
|
||||
* `stylingApply` will run through the context and find each dirty value and render them onto
|
||||
* the element. Once complete, all styles/classes will be set to clean. Because of this, the render
|
||||
* function will now know not to rerun itself again if called again unless new style/class values
|
||||
* have changed.
|
||||
*
|
||||
* ## Directives
|
||||
* Directive style/class values (which are provided through host bindings) are also supported and
|
||||
* housed within the same styling context as are template-level style/class properties/bindings
|
||||
* So long as they are all assigned to the same element, both directive-level and template-level
|
||||
* styling bindings share the same context.
|
||||
*
|
||||
* Each of the following instructions supports accepting a directive instance as an input parameter:
|
||||
*
|
||||
* - `elementHostAttrs`
|
||||
* - `styling`
|
||||
* - `styleProp`
|
||||
* - `classProp`
|
||||
* - `styleMap`
|
||||
* - `classMap`
|
||||
* - `stylingApply`
|
||||
*
|
||||
* Each time a directive value is passed in, it will be converted into an index by examining the
|
||||
* directive registry (which lives in the context configuration area). The index is then used
|
||||
* to help single style properties figure out where a value is located in the context.
|
||||
*
|
||||
*
|
||||
* ## Single-level styling bindings (`[style.prop]` and `[class.name]`)
|
||||
*
|
||||
* Both `[style.prop]` and `[class.name]` bindings are run through the `updateStyleProp`
|
||||
* and `updateClassProp` functions respectively. They work by examining the provided
|
||||
* `offset` value and are able to locate the exact spot in the context where the
|
||||
* matching style is located.
|
||||
*
|
||||
* Both `[style.prop]` and `[class.name]` bindings are able to process these values
|
||||
* from directive host bindings. When evaluated (from the host binding function) the
|
||||
* `directiveRef` value is then passed in.
|
||||
*
|
||||
* If two directives or a directive + a template binding both write to the same style/class
|
||||
* binding then the styling context code will decide which one wins based on the following
|
||||
* rule:
|
||||
*
|
||||
* 1. If the template binding has a value then it always wins
|
||||
* 2. Otherwise whichever first-registered directive that has that value first will win
|
||||
*
|
||||
* The code example helps make this clear:
|
||||
*
|
||||
* ```
|
||||
* <!--
|
||||
* <div [style.width]="myWidth"
|
||||
* [my-width-directive]="'600px'">
|
||||
* -->
|
||||
*
|
||||
* @Directive({
|
||||
* selector: '[my-width-directive']
|
||||
* })
|
||||
* class MyWidthDirective {
|
||||
* @Input('my-width-directive')
|
||||
* @HostBinding('style.width')
|
||||
* public width = null;
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* Since there is a style binding for width present on the element (`[style.width]`) then
|
||||
* it will always win over the width binding that is present as a host binding within
|
||||
* the `MyWidthDirective`. However, if `[style.width]` renders as `null` (so `myWidth=null`)
|
||||
* then the `MyWidthDirective` will be able to write to the `width` style within the context.
|
||||
* Simply put, whichever directive writes to a value first ends up having ownership of it as
|
||||
* long as the template didn't set anything.
|
||||
*
|
||||
* The way in which the ownership is facilitated is through index value. The earliest directives
|
||||
* get the smallest index values (with 0 being reserved for the template element bindings). Each
|
||||
* time a value is written from a directive or the template bindings, the value itself gets
|
||||
* assigned the directive index value in its data. If another directive writes a value again then
|
||||
* its directive index gets compared against the directive index that exists on the element. Only
|
||||
* when the new value's directive index is less than the existing directive index then the new
|
||||
* value will be written to the context. But, if the existing value is null then the new value is
|
||||
* written by the less important directive.
|
||||
*
|
||||
* Each directive also has its own sanitizer and dirty flags. These values are consumed within the
|
||||
* rendering function.
|
||||
*
|
||||
*
|
||||
* ## Multi-level styling bindings (`[style]` and `[class]`)
|
||||
*
|
||||
* Multi-level styling bindings are treated as less important (less specific) as single-level
|
||||
* bindings (things like `[style.prop]` and `[class.name]`).
|
||||
*
|
||||
* Multi-level bindings are still applied to the context in a similar way as are single level
|
||||
* bindings, but this process works by diffing the new multi-level values (which are key/value
|
||||
* maps) against the existing set of styles that live in the context. Each time a new map value
|
||||
* is detected (via identity check) then it will loop through the values and figure out what
|
||||
* has changed and reorder the context array to match the ordering of the keys. This reordering
|
||||
* of the context makes sure that follow-up traversals of the context when updated against the
|
||||
* key/value map are as close as possible to o(n) (where "n" is the size of the key/value map).
|
||||
*
|
||||
* If a `directiveRef` value is passed in then the styling algorithm code will take the directive's
|
||||
* prioritization index into account and update the values with respect to more important
|
||||
* directives. This means that if a value such as `width` is updated in two different `[style]`
|
||||
* bindings (say one on the template and another within a directive that sits on the same element)
|
||||
* then the algorithm will decide how to update the value based on the following heuristic:
|
||||
*
|
||||
* 1. If the template binding has a value then it always wins
|
||||
* 2. If not then whichever first-registered directive that has that value first will win
|
||||
*
|
||||
* It will also update the value if it was set to `null` by a previous directive (or the template).
|
||||
*
|
||||
* Each time a value is updated (or removed) then the context will change shape to better match
|
||||
* the ordering of the styling data as well as the ordering of each directive that contains styling
|
||||
* data. (See `patchStylingMapIntoContext` inside of class_and_style_bindings.ts to better
|
||||
* understand how this works.)
|
||||
*
|
||||
* ## Rendering
|
||||
* The rendering mechanism (when the styling data is applied on screen) occurs via the
|
||||
* `stylingApply` function and is designed to run after **all** styling functions have been
|
||||
* evaluated. The rendering algorithm will loop over the context and only apply the styles that are
|
||||
* flagged as dirty (either because they are new, updated or have been removed via multi or
|
||||
* single bindings).
|
||||
*/
|
||||
export interface StylingContext extends
|
||||
Array<{[key: string]: any}|number|string|boolean|RElement|StyleSanitizeFn|PlayerContext|null> {
|
||||
/**
|
||||
* Location of element that is used as a target for this context.
|
||||
*/
|
||||
[StylingIndex.ElementPosition]: LContainer|LView|RElement|null;
|
||||
|
||||
/**
|
||||
* 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;
|
||||
|
||||
/**
|
||||
* Location of the collection of directives for this context
|
||||
*/
|
||||
[StylingIndex.DirectiveRegistryPosition]: DirectiveRegistryValues;
|
||||
|
||||
/**
|
||||
* Location of all static styles values
|
||||
*/
|
||||
[StylingIndex.InitialStyleValuesPosition]: InitialStylingValues;
|
||||
|
||||
/**
|
||||
* Location of all static class values
|
||||
*/
|
||||
[StylingIndex.InitialClassValuesPosition]: InitialStylingValues;
|
||||
|
||||
/**
|
||||
* A numeric value representing the class index offset value. Whenever a single class is
|
||||
* applied (using `classProp`) it should have an styling index value that doesn't
|
||||
* need to take into account any style values that exist in the context.
|
||||
*/
|
||||
[StylingIndex.SinglePropOffsetPositions]: SinglePropOffsetValues;
|
||||
|
||||
/**
|
||||
* The last class value that was interpreted by `styleMap`. This is cached
|
||||
* So that the algorithm can exit early incase the value has not changed.
|
||||
*/
|
||||
[StylingIndex.CachedMultiClasses]: any|MapBasedOffsetValues;
|
||||
|
||||
/**
|
||||
* The last style value that was interpreted by `classMap`. This is cached
|
||||
* So that the algorithm can exit early incase the value has not changed.
|
||||
*/
|
||||
[StylingIndex.CachedMultiStyles]: any|MapBasedOffsetValues;
|
||||
|
||||
/**
|
||||
* A queue of all hostStyling instructions.
|
||||
*
|
||||
* This array (queue) is populated only when host-level styling instructions
|
||||
* (e.g. `hostStyleMap` and `hostClassProp`) are used to apply style and
|
||||
* class values via host bindings to the host element. Despite these being
|
||||
* standard angular instructions, they are not designed to immediately apply
|
||||
* their values to the styling context when executed. What happens instead is
|
||||
* a queue is constructed and each instruction is populated into the queue.
|
||||
* Then, once the style/class values are set to flush (via `stylingApply` or
|
||||
* `hostStylingApply`), the queue is flushed and the values are rendered onto
|
||||
* the host element.
|
||||
*/
|
||||
[StylingIndex.HostInstructionsQueue]: HostInstructionsQueue|null;
|
||||
|
||||
/**
|
||||
* Location of animation context (which contains the active players) for this element styling
|
||||
* context.
|
||||
*/
|
||||
[StylingIndex.PlayerContext]: PlayerContext|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* A queue of all host-related styling instructions (these are buffered and evaluated just before
|
||||
* the styling is applied).
|
||||
*
|
||||
* This queue is used when any `hostStyling` instructions are executed from the `hostBindings`
|
||||
* function. Template-level styling functions (e.g. `styleMap` and `classProp`)
|
||||
* do not make use of this queue (they are applied to the styling context immediately).
|
||||
*
|
||||
* Due to the nature of how components/directives are evaluated, directives (both parent and
|
||||
* subclass directives) may not apply their styling at the right time for the styling
|
||||
* algorithm code to prioritize them. Therefore, all host-styling instructions are queued up
|
||||
* (buffered) into the array below and are automatically sorted in terms of priority. The
|
||||
* priority for host-styling is as follows:
|
||||
*
|
||||
* 1. The template (this doesn't get queued, but gets evaluated immediately)
|
||||
* 2. Any directives present on the host
|
||||
* 2a) first child directive styling bindings are updated
|
||||
* 2b) then any parent directives
|
||||
* 3. Component host bindings
|
||||
*
|
||||
* Angular runs change detection for each of these cases in a different order. Because of this
|
||||
* the array below is populated with each of the host styling functions + their arguments.
|
||||
*
|
||||
* context[HostInstructionsQueue] = [
|
||||
* directiveIndex,
|
||||
* hostStylingFn,
|
||||
* [argumentsForFn],
|
||||
* ...
|
||||
* anotherDirectiveIndex, <-- this has a lower priority (a higher directive index)
|
||||
* anotherHostStylingFn,
|
||||
* [argumentsForFn],
|
||||
* ]
|
||||
*
|
||||
* When `renderStyling` is called (within `class_and_host_bindings.ts`) then the queue is
|
||||
* drained and each of the instructions are executed. Once complete the queue is empty then
|
||||
* the style/class binding code is rendered on the element (which is what happens normally
|
||||
* inside of `renderStyling`).
|
||||
*
|
||||
* Right now each directive's hostBindings function, as well the template function, both
|
||||
* call `stylingApply()` and `hostStylingApply()`. The fact that this is called
|
||||
* multiple times for the same element (b/c of change detection) causes some issues. To avoid
|
||||
* having styling code be rendered on an element multiple times, the `HostInstructionsQueue`
|
||||
* reserves a slot for a reference pointing to the very last directive that was registered and
|
||||
* only allows for styling to be applied once that directive is encountered (which will happen
|
||||
* as the last update for that element).
|
||||
*/
|
||||
export interface HostInstructionsQueue extends Array<number|Function|any[]> { [0]: number; }
|
||||
|
||||
/**
|
||||
* Used as a reference for any values contained within `HostInstructionsQueue`.
|
||||
*/
|
||||
export const enum HostInstructionsQueueIndex {
|
||||
LastRegisteredDirectiveIndexPosition = 0,
|
||||
ValuesStartPosition = 1,
|
||||
DirectiveIndexOffset = 0,
|
||||
InstructionFnOffset = 1,
|
||||
ParamsOffset = 2,
|
||||
Size = 3,
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as a styling array to house static class and style values that were extracted
|
||||
* by the compiler and placed in the animation context via `elementStart` and
|
||||
* `elementHostAttrs`.
|
||||
*
|
||||
* See [InitialStylingValuesIndex] for a breakdown of how all this works.
|
||||
*/
|
||||
export interface InitialStylingValues extends Array<string|boolean|number|null> {
|
||||
[InitialStylingValuesIndex.DefaultNullValuePosition]: null;
|
||||
[InitialStylingValuesIndex.CachedStringValuePosition]: string|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Used as an offset/position index to figure out where initial styling
|
||||
* values are located.
|
||||
*
|
||||
* Used as a reference point to provide markers to all static styling
|
||||
* values (the initial style and class values on an element) within an
|
||||
* array within the `StylingContext`. This array contains key/value pairs
|
||||
* where the key is the style property name or className and the value is
|
||||
* the style value or whether or not a class is present on the elment.
|
||||
*
|
||||
* The first value is always null so that a initial index value of
|
||||
* `0` will always point to a null value.
|
||||
*
|
||||
* The second value is also always null unless a string-based representation
|
||||
* of the styling data was constructed (it gets cached in this slot).
|
||||
*
|
||||
* If a <div> elements contains a list of static styling values like so:
|
||||
*
|
||||
* <div class="foo bar baz" style="width:100px; height:200px;">
|
||||
*
|
||||
* Then the initial styles for that will look like so:
|
||||
*
|
||||
* Styles:
|
||||
* ```
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, null, 'width', '100px', height, '200px'
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Classes:
|
||||
* ```
|
||||
* StylingContext[InitialClassesIndex] = [
|
||||
* null, null, 'foo', true, 'bar', true, 'baz', true
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Initial style and class entries have their own arrays. This is because
|
||||
* it's easier to add to the end of one array and not then have to update
|
||||
* every context entries' pointer index to the newly offseted values.
|
||||
*
|
||||
* When property bindinds are added to a context then initial style/class
|
||||
* values will also be inserted into the array. This is to create a space
|
||||
* in the situation when a follow-up directive inserts static styling into
|
||||
* the array. By default, style values are `null` and class values are
|
||||
* `false` when inserted by property bindings.
|
||||
*
|
||||
* For example:
|
||||
* ```
|
||||
* <div class="foo bar baz"
|
||||
* [class.car]="myCarExp"
|
||||
* style="width:100px; height:200px;"
|
||||
* [style.opacity]="myOpacityExp">
|
||||
* ```
|
||||
*
|
||||
* Will construct initial styling values that look like:
|
||||
*
|
||||
* Styles:
|
||||
* ```
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, null, 'width', '100px', height, '200px', 'opacity', null
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Classes:
|
||||
* ```
|
||||
* StylingContext[InitialClassesIndex] = [
|
||||
* null, null, 'foo', true, 'bar', true, 'baz', true, 'car', false
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Now if a directive comes along and introduces `car` as a static
|
||||
* class value or `opacity` then those values will be filled into
|
||||
* the initial styles array.
|
||||
*
|
||||
* For example:
|
||||
*
|
||||
* ```
|
||||
* @Directive({
|
||||
* selector: 'opacity-car-directive',
|
||||
* host: {
|
||||
* 'style': 'opacity:0.5',
|
||||
* 'class': 'car'
|
||||
* }
|
||||
* })
|
||||
* class OpacityCarDirective {}
|
||||
* ```
|
||||
*
|
||||
* This will render itself as:
|
||||
*
|
||||
* Styles:
|
||||
* ```
|
||||
* StylingContext[InitialStylesIndex] = [
|
||||
* null, null, 'width', '100px', height, '200px', 'opacity', '0.5'
|
||||
* ]
|
||||
* ```
|
||||
*
|
||||
* Classes:
|
||||
* ```
|
||||
* StylingContext[InitialClassesIndex] = [
|
||||
* null, null, 'foo', true, 'bar', true, 'baz', true, 'car', true
|
||||
* ]
|
||||
* ```
|
||||
*/
|
||||
export const enum InitialStylingValuesIndex {
|
||||
/**
|
||||
* The first value is always `null` so that `styles[0] == null` for unassigned values
|
||||
*/
|
||||
DefaultNullValuePosition = 0,
|
||||
|
||||
/**
|
||||
* Used for non-styling code to examine what the style or className string is:
|
||||
* styles: ['width', '100px', 0, 'opacity', null, 0, 'height', '200px', 0]
|
||||
* => initialStyles[CachedStringValuePosition] = 'width:100px;height:200px';
|
||||
* classes: ['foo', true, 0, 'bar', false, 0, 'baz', true, 0]
|
||||
* => initialClasses[CachedStringValuePosition] = 'foo bar';
|
||||
*
|
||||
* Note that this value is `null` by default and it will only be populated
|
||||
* once `getInitialStyleStringValue` or `getInitialClassNameValue` is executed.
|
||||
*/
|
||||
CachedStringValuePosition = 1,
|
||||
|
||||
/**
|
||||
* Where the style or class values start in the tuple
|
||||
*/
|
||||
KeyValueStartPosition = 2,
|
||||
|
||||
/**
|
||||
* The offset value (index + offset) for the property value for each style/class entry
|
||||
*/
|
||||
PropOffset = 0,
|
||||
|
||||
/**
|
||||
* The offset value (index + offset) for the style/class value for each style/class entry
|
||||
*/
|
||||
ValueOffset = 1,
|
||||
|
||||
/**
|
||||
* The offset value (index + offset) for the style/class directive owner for each style/class
|
||||
entry
|
||||
*/
|
||||
DirectiveOwnerOffset = 2,
|
||||
|
||||
/**
|
||||
* The first bit set aside to mark if the initial style was already rendere
|
||||
*/
|
||||
AppliedFlagBitPosition = 0b0,
|
||||
AppliedFlagBitLength = 1,
|
||||
|
||||
/**
|
||||
* The total size for each style/class entry (prop + value + directiveOwner)
|
||||
*/
|
||||
Size = 3
|
||||
}
|
||||
|
||||
/**
|
||||
* An array located in the StylingContext that houses all directive instances and additional
|
||||
* data about them.
|
||||
*
|
||||
* Each entry in this array represents a source of where style/class binding values could
|
||||
* come from. By default, there is always at least one directive here with a null value and
|
||||
* that represents bindings that live directly on an element in the template (not host bindings).
|
||||
*
|
||||
* Each successive entry in the array is an actual instance of a directive as well as some
|
||||
* additional info about that entry.
|
||||
*
|
||||
* An entry within this array has the following values:
|
||||
* [0] = The instance of the directive (the first entry is null because its reserved for the
|
||||
* template)
|
||||
* [1] = The pointer that tells where the single styling (stuff like [class.foo] and [style.prop])
|
||||
* offset values are located. This value will allow for a binding instruction to find exactly
|
||||
* where a style is located.
|
||||
* [2] = Whether or not the directive has any styling values that are dirty. This is used as
|
||||
* reference within the `renderStyling` function to decide whether to skip iterating
|
||||
* through the context when rendering is executed.
|
||||
* [3] = The styleSanitizer instance that is assigned to the directive. Although it's unlikely,
|
||||
* a directive could introduce its own special style sanitizer and for this reach each
|
||||
* directive will get its own space for it (if null then the very first sanitizer is used).
|
||||
*
|
||||
* Each time a new directive is added it will insert these four values at the end of the array.
|
||||
* When this array is examined then the resulting directiveIndex will be resolved by dividing the
|
||||
* index value by the size of the array entries (so if DirA is at spot 8 then its index will be 2).
|
||||
*/
|
||||
export interface DirectiveRegistryValues extends Array<null|{}|boolean|number|StyleSanitizeFn> {
|
||||
[DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset]: number;
|
||||
[DirectiveRegistryValuesIndex.StyleSanitizerOffset]: StyleSanitizeFn|null;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum that outlines the offset/position values for each directive entry and its data
|
||||
* that are housed inside of [DirectiveRegistryValues].
|
||||
*/
|
||||
export const enum DirectiveRegistryValuesIndex {
|
||||
SinglePropValuesIndexOffset = 0,
|
||||
StyleSanitizerOffset = 1,
|
||||
Size = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* An array that contains the index pointer values for every single styling property
|
||||
* that exists in the context and for every directive. It also contains the total
|
||||
* single styles and single classes that exists in the context as the first two values.
|
||||
*
|
||||
* Let's say we have the following template code:
|
||||
*
|
||||
* <div [style.width]="myWidth"
|
||||
* [style.height]="myHeight"
|
||||
* [class.flipped]="flipClass"
|
||||
* directive-with-opacity>
|
||||
* directive-with-foo-bar-classes>
|
||||
*
|
||||
* We have two directive and template-binding sources,
|
||||
* 2 + 1 styles and 1 + 1 classes. When the bindings are
|
||||
* registered the SinglePropOffsets array will look like so:
|
||||
*
|
||||
* s_0/c_0 = template directive value
|
||||
* s_1/c_1 = directive one (directive-with-opacity)
|
||||
* s_2/c_2 = directive two (directive-with-foo-bar-classes)
|
||||
*
|
||||
* [3, 2, 2, 1, s_00, s01, c_01, 1, 0, s_10, 0, 1, c_20
|
||||
*/
|
||||
export interface SinglePropOffsetValues extends Array<number> {
|
||||
[SinglePropOffsetValuesIndex.StylesCountPosition]: number;
|
||||
[SinglePropOffsetValuesIndex.ClassesCountPosition]: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum that outlines the offset/position values for each single prop/class entry
|
||||
* that are housed inside of [SinglePropOffsetValues].
|
||||
*/
|
||||
export const enum SinglePropOffsetValuesIndex {
|
||||
StylesCountPosition = 0,
|
||||
ClassesCountPosition = 1,
|
||||
ValueStartPosition = 2
|
||||
}
|
||||
|
||||
/**
|
||||
* Used a reference for all multi styling values (values that are assigned via the
|
||||
* `[style]` and `[class]` bindings).
|
||||
*
|
||||
* Single-styling properties (things set via `[style.prop]` and `[class.name]` bindings)
|
||||
* are not handled using the same approach as multi-styling bindings (such as `[style]`
|
||||
* `[class]` bindings).
|
||||
*
|
||||
* Multi-styling bindings rely on a diffing algorithm to figure out what properties have been added,
|
||||
* removed and modified. Multi-styling properties are also evaluated across directives--which means
|
||||
* that Angular supports having multiple directives all write to the same `[style]` and `[class]`
|
||||
* bindings (using host bindings) even if the `[style]` and/or `[class]` bindings are being written
|
||||
* to on the template element.
|
||||
*
|
||||
* All multi-styling values that are written to an element (whether it be from the template or any
|
||||
* directives attached to the element) are all written into the `MapBasedOffsetValues` array. (Note
|
||||
* that there are two arrays: one for styles and another for classes.)
|
||||
*
|
||||
* This array is shaped in the following way:
|
||||
*
|
||||
* [0] = The total amount of unique multi-style or multi-class entries that exist currently in the
|
||||
* context.
|
||||
* [1+] = Contains an entry of four values ... Each entry is a value assigned by a
|
||||
* `[style]`/`[class]`
|
||||
* binding (we call this a **source**).
|
||||
*
|
||||
* An example entry looks like so (at a given `i` index):
|
||||
* [i + 0] = Whether or not the value is dirty
|
||||
*
|
||||
* [i + 1] = The index of where the map-based values
|
||||
* (for this **source**) start within the context
|
||||
*
|
||||
* [i + 2] = The untouched, last set value of the binding
|
||||
*
|
||||
* [i + 3] = The total amount of unqiue binding values that were
|
||||
* extracted and set into the context. (Note that this value does
|
||||
* not reflect the total amount of values within the binding
|
||||
* value (since it's a map), but instead reflects the total values
|
||||
* that were not used by another directive).
|
||||
*
|
||||
* Each time a directive (or template) writes a value to a `[class]`/`[style]` binding then the
|
||||
* styling diffing algorithm code will decide whether or not to update the value based on the
|
||||
* following rules:
|
||||
*
|
||||
* 1. If a more important directive (either the template or a directive that was registered
|
||||
* beforehand) has written a specific styling value into the context then any follow-up styling
|
||||
* values (set by another directive via its `[style]` and/or `[class]` host binding) will not be
|
||||
* able to set it. This is because the former directive has priorty.
|
||||
* 2. Only if a former directive has set a specific styling value to null (whether by actually
|
||||
* setting it to null or not including it in is map value) then a less imporatant directive can
|
||||
* set its own value.
|
||||
*
|
||||
* ## How the map-based styling algorithm updates itself
|
||||
*/
|
||||
export interface MapBasedOffsetValues extends Array<any> {
|
||||
[MapBasedOffsetValuesIndex.EntriesCountPosition]: number;
|
||||
}
|
||||
|
||||
export const enum MapBasedOffsetValuesIndex {
|
||||
EntriesCountPosition = 0,
|
||||
ValuesStartPosition = 1,
|
||||
DirtyFlagOffset = 0,
|
||||
PositionStartOffset = 1,
|
||||
ValueOffset = 2,
|
||||
ValueCountOffset = 3,
|
||||
Size = 4
|
||||
}
|
||||
|
||||
/**
|
||||
* 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 = 0b00000,
|
||||
// Whether or not the entry or context itself is dirty
|
||||
Dirty = 0b00001,
|
||||
// Whether or not this is a class-based assignment
|
||||
Class = 0b00010,
|
||||
// Whether or not a sanitizer was applied to this property
|
||||
Sanitize = 0b00100,
|
||||
// Whether or not any player builders within need to produce new players
|
||||
PlayerBuildersDirty = 0b01000,
|
||||
// The max amount of bits used to represent these configuration values
|
||||
BindingAllocationLocked = 0b10000,
|
||||
BitCountSize = 5,
|
||||
// There are only five bits here
|
||||
BitMask = 0b11111
|
||||
}
|
||||
|
||||
/** 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
|
||||
// This index must align with HOST, see interfaces/view.ts
|
||||
ElementPosition = 0,
|
||||
// Index of location where the start of single properties are stored. (`updateStyleProp`)
|
||||
MasterFlagPosition = 1,
|
||||
// Position of where the registered directives exist for this styling context
|
||||
DirectiveRegistryPosition = 2,
|
||||
// Position of where the initial styles are stored in the styling context
|
||||
InitialStyleValuesPosition = 3,
|
||||
InitialClassValuesPosition = 4,
|
||||
// Index of location where the class index offset value is located
|
||||
SinglePropOffsetPositions = 5,
|
||||
// Position of where the last string-based CSS class value was stored (or a cached version of the
|
||||
// initial styles when a [class] directive is present)
|
||||
CachedMultiClasses = 6,
|
||||
// Position of where the last string-based CSS class value was stored
|
||||
CachedMultiStyles = 7,
|
||||
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
|
||||
// Position of where the initial styles are stored in the styling context
|
||||
HostInstructionsQueue = 8,
|
||||
PlayerContext = 9,
|
||||
// Location of single (prop) value entries are stored within the context
|
||||
SingleStylesStartPosition = 10,
|
||||
FlagsOffset = 0,
|
||||
PropertyOffset = 1,
|
||||
ValueOffset = 2,
|
||||
PlayerBuilderIndexOffset = 3,
|
||||
// Size of each multi or single entry (flag + prop + value + playerBuilderIndex)
|
||||
Size = 4,
|
||||
// Each flag has a binary digit length of this value
|
||||
BitCountSize = 14, // (32 - 4) / 2 = ~14
|
||||
// The binary digit value as a mask
|
||||
BitMask = 0b11111111111111, // 14 bits
|
||||
}
|
||||
|
||||
/**
|
||||
* An enum that outlines the bit flag data for directive owner and player index
|
||||
* values that exist within en entry that lives in the StylingContext.
|
||||
*
|
||||
* The values here split a number value into two sets of bits:
|
||||
* - The first 16 bits are used to store the directiveIndex that owns this style value
|
||||
* - The other 16 bits are used to store the playerBuilderIndex that is attached to this style
|
||||
*/
|
||||
export const enum DirectiveOwnerAndPlayerBuilderIndex {
|
||||
BitCountSize = 16,
|
||||
BitMask = 0b1111111111111111
|
||||
}
|
||||
|
||||
/**
|
||||
* The default directive styling index value for template-based bindings.
|
||||
*
|
||||
* All host-level bindings (e.g. `hostStyleProp` and `hostClassMap`) are
|
||||
* assigned a directive styling index value based on the current directive
|
||||
* uniqueId and the directive super-class inheritance depth. But for template
|
||||
* bindings they always have the same directive styling index value.
|
||||
*/
|
||||
export const DEFAULT_TEMPLATE_DIRECTIVE_INDEX = 0;
|
|
@ -11,37 +11,25 @@ import {ComponentDef, DirectiveDef} from '..';
|
|||
import {LContainer, TYPE} from './container';
|
||||
import {TNode, TNodeFlags} from './node';
|
||||
import {RNode} from './renderer';
|
||||
import {StylingContext} from './styling';
|
||||
import {FLAGS, LView, LViewFlags} from './view';
|
||||
|
||||
|
||||
/**
|
||||
* True if `value` is `LView`.
|
||||
* @param value wrapped value of `RNode`, `LView`, `LContainer`, `StylingContext`
|
||||
* @param value wrapped value of `RNode`, `LView`, `LContainer`
|
||||
*/
|
||||
export function isLView(value: RNode | LView | LContainer | StylingContext | {} | null):
|
||||
value is LView {
|
||||
export function isLView(value: RNode | LView | LContainer | {} | null): value is LView {
|
||||
return Array.isArray(value) && typeof value[TYPE] === 'object';
|
||||
}
|
||||
|
||||
/**
|
||||
* True if `value` is `LContainer`.
|
||||
* @param value wrapped value of `RNode`, `LView`, `LContainer`, `StylingContext`
|
||||
* @param value wrapped value of `RNode`, `LView`, `LContainer`
|
||||
*/
|
||||
export function isLContainer(value: RNode | LView | LContainer | StylingContext | {} | null):
|
||||
value is LContainer {
|
||||
export function isLContainer(value: RNode | LView | LContainer | {} | null): value is LContainer {
|
||||
return Array.isArray(value) && value[TYPE] === true;
|
||||
}
|
||||
|
||||
/**
|
||||
* True if `value` is `StylingContext`.
|
||||
* @param value wrapped value of `RNode`, `LView`, `LContainer`, `StylingContext`
|
||||
*/
|
||||
export function isStylingContext(value: RNode | LView | LContainer | StylingContext | {} | null):
|
||||
value is StylingContext {
|
||||
return Array.isArray(value) && typeof value[TYPE] === 'number';
|
||||
}
|
||||
|
||||
export function isContentQueryHost(tNode: TNode): boolean {
|
||||
return (tNode.flags & TNodeFlags.hasContentQuery) !== 0;
|
||||
}
|
||||
|
|
|
@ -11,21 +11,6 @@ import {TContainerNode, TElementNode, TNode} from './interfaces/node';
|
|||
import {DECLARATION_VIEW, LView, T_HOST} from './interfaces/view';
|
||||
import {getParentInjectorViewOffset} from './util/injector_utils';
|
||||
|
||||
export function applyOnCreateInstructions(tNode: TNode) {
|
||||
// there may be some instructions that need to run in a specific
|
||||
// order because the CREATE block in a directive runs before the
|
||||
// CREATE block in a template. To work around this instructions
|
||||
// can get access to the function array below and defer any code
|
||||
// to run after the element is created.
|
||||
let fns: Function[]|null;
|
||||
if (fns = tNode.onElementCreationFns) {
|
||||
for (let i = 0; i < fns.length; i++) {
|
||||
fns[i]();
|
||||
}
|
||||
tNode.onElementCreationFns = null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unwraps a parent injector location number to find the view offset from the current injector,
|
||||
* then walks up the declaration view tree until the TNode of the parent injector is found.
|
||||
|
|
|
@ -1,67 +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 '../util/ng_dev_mode';
|
||||
|
||||
import {getLContext} from './context_discovery';
|
||||
import {scheduleTick} from './instructions/shared';
|
||||
import {ComponentInstance, DirectiveInstance, Player} from './interfaces/player';
|
||||
import {RootContextFlags} from './interfaces/view';
|
||||
import {addPlayerInternal, getOrCreatePlayerContext, getPlayerContext, getPlayersInternal, getStylingContextFromLView, throwInvalidRefError} from './styling/util';
|
||||
import {getRootContext} from './util/view_traversal_utils';
|
||||
|
||||
|
||||
/**
|
||||
* 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(
|
||||
ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void {
|
||||
const context = getLContext(ref);
|
||||
if (!context) {
|
||||
ngDevMode && throwInvalidRefError();
|
||||
return;
|
||||
}
|
||||
|
||||
const element = context.native as HTMLElement;
|
||||
const lView = context.lView;
|
||||
const playerContext = getOrCreatePlayerContext(element, context) !;
|
||||
const rootContext = getRootContext(lView);
|
||||
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]`).
|
||||
*
|
||||
* @publicApi
|
||||
*/
|
||||
export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] {
|
||||
const context = getLContext(ref);
|
||||
if (!context) {
|
||||
ngDevMode && throwInvalidRefError();
|
||||
return [];
|
||||
}
|
||||
|
||||
const stylingContext = getStylingContextFromLView(context.nodeIndex, context.lView);
|
||||
const playerContext = stylingContext ? getPlayerContext(stylingContext) : null;
|
||||
return playerContext ? getPlayersInternal(playerContext) : [];
|
||||
}
|
|
@ -14,7 +14,6 @@ import {executeHooks} from './hooks';
|
|||
import {ComponentDef, DirectiveDef} from './interfaces/definition';
|
||||
import {TElementNode, TNode, TViewNode} from './interfaces/node';
|
||||
import {BINDING_INDEX, CONTEXT, DECLARATION_VIEW, FLAGS, InitPhaseState, LView, LViewFlags, OpaqueViewState, TVIEW} from './interfaces/view';
|
||||
import {setCachedStylingContext} from './styling/state';
|
||||
import {resetAllStylingState, resetStylingState} from './styling_next/state';
|
||||
import {resetPreOrderHookFlags} from './util/view_utils';
|
||||
|
||||
|
@ -489,7 +488,6 @@ export function leaveView(newView: LView, safeToRunHooks: boolean): void {
|
|||
lView[BINDING_INDEX] = tView.bindingStartIndex;
|
||||
}
|
||||
}
|
||||
setCachedStylingContext(null);
|
||||
enterView(newView, null);
|
||||
}
|
||||
|
||||
|
@ -514,10 +512,6 @@ export function getSelectedIndex() {
|
|||
export function setSelectedIndex(index: number) {
|
||||
_selectedIndex = index;
|
||||
|
||||
// remove the styling context from the cache
|
||||
// because we are now on a different element
|
||||
setCachedStylingContext(null);
|
||||
|
||||
// we have now jumped to another element
|
||||
// therefore the state is stale
|
||||
resetStylingState();
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,24 +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 {PlayState, Player, PlayerHandler} from '../interfaces/player';
|
||||
|
||||
export class CorePlayerHandler implements PlayerHandler {
|
||||
private _players: Player[] = [];
|
||||
|
||||
flushPlayers() {
|
||||
for (let i = 0; i < this._players.length; i++) {
|
||||
const player = this._players[i];
|
||||
if (!player.parent && player.state === PlayState.Pending) {
|
||||
player.play();
|
||||
}
|
||||
}
|
||||
this._players.length = 0;
|
||||
}
|
||||
|
||||
queuePlayer(player: Player) { this._players.push(player); }
|
||||
}
|
|
@ -1,98 +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 {HostInstructionsQueue, HostInstructionsQueueIndex, StylingContext, StylingIndex} from '../interfaces/styling';
|
||||
import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from '../styling/shared';
|
||||
|
||||
/*
|
||||
* This file contains the logic to defer all hostBindings-related styling code to run
|
||||
* at a later point, instead of immediately (as is the case with how template-level
|
||||
* styling instructions are run).
|
||||
*
|
||||
* Certain styling instructions, present within directives, components and sub-classed
|
||||
* directives, are evaluated at different points (depending on priority) and will therefore
|
||||
* not be applied to the styling context of an element immediately. They are instead
|
||||
* designed to be applied just before styling is applied to an element.
|
||||
*
|
||||
* (The priority for when certain host-related styling operations are executed is discussed
|
||||
* more within `interfaces/styling.ts`.)
|
||||
*/
|
||||
|
||||
export function registerHostDirective(context: StylingContext, directiveIndex: number) {
|
||||
let buffer = context[StylingIndex.HostInstructionsQueue];
|
||||
if (!buffer) {
|
||||
buffer = context[StylingIndex.HostInstructionsQueue] = [DEFAULT_TEMPLATE_DIRECTIVE_INDEX];
|
||||
}
|
||||
buffer[HostInstructionsQueueIndex.LastRegisteredDirectiveIndexPosition] = directiveIndex;
|
||||
}
|
||||
|
||||
/**
|
||||
* Queues a styling instruction to be run just before `renderStyling()` is executed.
|
||||
*/
|
||||
export function enqueueHostInstruction<T extends Function>(
|
||||
context: StylingContext, priority: number, instructionFn: T, instructionFnArgs: ParamsOf<T>) {
|
||||
const buffer: HostInstructionsQueue|null = context[StylingIndex.HostInstructionsQueue];
|
||||
// Buffer may be null if host element is a template node. In this case, just ignore the style.
|
||||
if (buffer != null) {
|
||||
const index = findNextInsertionIndex(buffer, priority);
|
||||
buffer.splice(index, 0, priority, instructionFn, instructionFnArgs);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Figures out where exactly to to insert the next host instruction queue entry.
|
||||
*/
|
||||
function findNextInsertionIndex(buffer: HostInstructionsQueue, priority: number): number {
|
||||
for (let i = HostInstructionsQueueIndex.ValuesStartPosition; i < buffer.length;
|
||||
i += HostInstructionsQueueIndex.Size) {
|
||||
const p = buffer[i + HostInstructionsQueueIndex.DirectiveIndexOffset] as number;
|
||||
if (p > priority) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return buffer.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterates through the host instructions queue (if present within the provided
|
||||
* context) and executes each queued instruction entry.
|
||||
*/
|
||||
export function flushQueue(this: unknown, context: StylingContext): void {
|
||||
const buffer = context[StylingIndex.HostInstructionsQueue];
|
||||
if (buffer) {
|
||||
for (let i = HostInstructionsQueueIndex.ValuesStartPosition; i < buffer.length;
|
||||
i += HostInstructionsQueueIndex.Size) {
|
||||
const fn = buffer[i + HostInstructionsQueueIndex.InstructionFnOffset] as Function;
|
||||
const args = buffer[i + HostInstructionsQueueIndex.ParamsOffset] as any[];
|
||||
fn.apply(this, args);
|
||||
}
|
||||
buffer.length = HostInstructionsQueueIndex.ValuesStartPosition;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Determines whether or not to allow the host instructions queue to be flushed or not.
|
||||
*
|
||||
* Because the hostBindings function code is unaware of the presence of other host bindings
|
||||
* (as well as the template function) then styling is evaluated multiple times per element.
|
||||
* To prevent style and class values from being applied to the element multiple times, a
|
||||
* flush is only allowed when the last directive (the directive that was registered into
|
||||
* the styling context) attempts to render its styling.
|
||||
*/
|
||||
export function allowFlush(context: StylingContext, directiveIndex: number): boolean {
|
||||
const buffer = context[StylingIndex.HostInstructionsQueue];
|
||||
if (buffer) {
|
||||
return buffer[HostInstructionsQueueIndex.LastRegisteredDirectiveIndexPosition] ===
|
||||
directiveIndex;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Infers the parameters of a given function into a typed array.
|
||||
*/
|
||||
export type ParamsOf<T> = T extends(...args: infer T) => any ? T : never;
|
|
@ -1,33 +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 {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) {}
|
||||
}
|
|
@ -1,17 +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
|
||||
*/
|
||||
|
||||
/**
|
||||
* The default directive styling index value for template-based bindings.
|
||||
*
|
||||
* All host-level bindings (e.g. `hostStyleProp` and `hostStyleMap`) are
|
||||
* assigned a directive styling index value based on the current directive
|
||||
* uniqueId and the directive super-class inheritance depth. But for template
|
||||
* bindings they always have the same directive styling index value.
|
||||
*/
|
||||
export const DEFAULT_TEMPLATE_DIRECTIVE_INDEX = 0;
|
|
@ -1,30 +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 {StylingContext} from '../interfaces/styling';
|
||||
|
||||
let stylingContext: StylingContext|null = null;
|
||||
|
||||
/**
|
||||
* Gets the most recent styling context value.
|
||||
*
|
||||
* Note that only one styling context is stored at a given time.
|
||||
*/
|
||||
export function getCachedStylingContext() {
|
||||
return stylingContext;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the most recent styling context value.
|
||||
*
|
||||
* Note that only one styling context is stored at a given time.
|
||||
*
|
||||
* @param context The styling context value that will be stored
|
||||
*/
|
||||
export function setCachedStylingContext(context: StylingContext | null) {
|
||||
stylingContext = context;
|
||||
}
|
|
@ -1,263 +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 '../../util/ng_dev_mode';
|
||||
|
||||
import {StyleSanitizeFn} from '../../sanitization/style_sanitizer';
|
||||
import {getLContext} from '../context_discovery';
|
||||
import {LContainer} from '../interfaces/container';
|
||||
import {LContext} from '../interfaces/context';
|
||||
import {TNode, TNodeFlags} from '../interfaces/node';
|
||||
import {PlayState, Player, PlayerContext, PlayerIndex} from '../interfaces/player';
|
||||
import {RElement} from '../interfaces/renderer';
|
||||
import {DirectiveRegistryValuesIndex, InitialStylingValues, StylingContext, StylingFlags, StylingIndex} from '../interfaces/styling';
|
||||
import {isStylingContext} from '../interfaces/type_checks';
|
||||
import {HEADER_OFFSET, HOST, LView, RootContext} from '../interfaces/view';
|
||||
import {getTNode} from '../util/view_utils';
|
||||
|
||||
import {CorePlayerHandler} from './core_player_handler';
|
||||
import {DEFAULT_TEMPLATE_DIRECTIVE_INDEX} from './shared';
|
||||
|
||||
export const ANIMATION_PROP_PREFIX = '@';
|
||||
|
||||
export function createEmptyStylingContext(
|
||||
wrappedElement?: LContainer | LView | RElement | null, sanitizer?: StyleSanitizeFn | null,
|
||||
initialStyles?: InitialStylingValues | null,
|
||||
initialClasses?: InitialStylingValues | null): StylingContext {
|
||||
const context: StylingContext = [
|
||||
wrappedElement || null, // Element
|
||||
0, // MasterFlags
|
||||
[] as any, // DirectiveRefs (this gets filled below)
|
||||
initialStyles || [null, null], // InitialStyles
|
||||
initialClasses || [null, null], // InitialClasses
|
||||
[0, 0], // SinglePropOffsets
|
||||
[0], // CachedMultiClassValue
|
||||
[0], // CachedMultiStyleValue
|
||||
null, // HostBuffer
|
||||
null, // PlayerContext
|
||||
];
|
||||
|
||||
// whenever a context is created there is always a `null` directive
|
||||
// that is registered (which is a placeholder for the "template").
|
||||
allocateOrUpdateDirectiveIntoContext(context, DEFAULT_TEMPLATE_DIRECTIVE_INDEX);
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allocates (registers) a directive into the directive registry within the provided styling
|
||||
* context.
|
||||
*
|
||||
* For each and every `[style]`, `[style.prop]`, `[class]`, `[class.name]` binding
|
||||
* (as well as static style and class attributes) a directive, component or template
|
||||
* is marked as the owner. When an owner is determined (this happens when the template
|
||||
* is first passed over) the directive owner is allocated into the styling context. When
|
||||
* this happens, each owner gets its own index value. This then ensures that once any
|
||||
* style and/or class binding are assigned into the context then they are marked to
|
||||
* that directive's index value.
|
||||
*
|
||||
* @param context the target StylingContext
|
||||
* @param directiveRef the directive that will be allocated into the context
|
||||
* @returns the index where the directive was inserted into
|
||||
*/
|
||||
export function allocateOrUpdateDirectiveIntoContext(
|
||||
context: StylingContext, directiveIndex: number, singlePropValuesIndex: number = -1,
|
||||
styleSanitizer?: StyleSanitizeFn | null | undefined): void {
|
||||
const directiveRegistry = context[StylingIndex.DirectiveRegistryPosition];
|
||||
|
||||
const index = directiveIndex * DirectiveRegistryValuesIndex.Size;
|
||||
// we preemptively make space into the directives array and then
|
||||
// assign values slot-by-slot to ensure that if the directive ordering
|
||||
// changes then it will still function
|
||||
const limit = index + DirectiveRegistryValuesIndex.Size;
|
||||
for (let i = directiveRegistry.length; i < limit; i += DirectiveRegistryValuesIndex.Size) {
|
||||
// -1 is used to signal that the directive has been allocated, but
|
||||
// no actual style or class bindings have been registered yet...
|
||||
directiveRegistry.push(-1, null);
|
||||
}
|
||||
|
||||
const propValuesStartPosition = index + DirectiveRegistryValuesIndex.SinglePropValuesIndexOffset;
|
||||
if (singlePropValuesIndex >= 0 && directiveRegistry[propValuesStartPosition] === -1) {
|
||||
directiveRegistry[propValuesStartPosition] = singlePropValuesIndex;
|
||||
directiveRegistry[index + DirectiveRegistryValuesIndex.StyleSanitizerOffset] =
|
||||
styleSanitizer || null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Used clone a copy of a pre-computed template of a styling context.
|
||||
*
|
||||
* A pre-computed template is designed to be computed once for a given element
|
||||
* (instructions.ts has logic for caching this).
|
||||
*/
|
||||
export function allocStylingContext(
|
||||
element: RElement | null, templateStyleContext: StylingContext): StylingContext {
|
||||
// each instance gets a copy
|
||||
const context = templateStyleContext.slice() as any as StylingContext;
|
||||
|
||||
// the HEADER values contain arrays which also need
|
||||
// to be copied over into the new context
|
||||
for (let i = 0; i < StylingIndex.SingleStylesStartPosition; i++) {
|
||||
const value = templateStyleContext[i];
|
||||
if (Array.isArray(value)) {
|
||||
context[i] = value.slice();
|
||||
}
|
||||
}
|
||||
|
||||
context[StylingIndex.ElementPosition] = element;
|
||||
|
||||
// this will prevent any other directives from extending the context
|
||||
context[StylingIndex.MasterFlagPosition] |= StylingFlags.BindingAllocationLocked;
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retrieve the `StylingContext` at a given index.
|
||||
*
|
||||
* This method lazily creates the `StylingContext`. This is because in most cases
|
||||
* we have styling without any bindings. Creating `StylingContext` eagerly would mean that
|
||||
* every style declaration such as `<div style="color: red">` would result `StyleContext`
|
||||
* which would create unnecessary memory pressure.
|
||||
*
|
||||
* @param index Index of the style allocation. See: `styling`.
|
||||
* @param viewData The view to search for the styling context
|
||||
*/
|
||||
export function getStylingContextFromLView(index: number, viewData: LView): StylingContext {
|
||||
let storageIndex = index;
|
||||
let slotValue: LContainer|LView|StylingContext|RElement = viewData[storageIndex];
|
||||
let wrapper: LContainer|LView|StylingContext = viewData;
|
||||
|
||||
while (Array.isArray(slotValue)) {
|
||||
wrapper = slotValue;
|
||||
slotValue = slotValue[HOST] as LView | StylingContext | RElement;
|
||||
}
|
||||
|
||||
if (isStylingContext(wrapper)) {
|
||||
return wrapper;
|
||||
} else {
|
||||
// This is an LView or an LContainer
|
||||
const stylingTemplate = getTNode(index - HEADER_OFFSET, viewData).stylingTemplate;
|
||||
|
||||
if (wrapper !== viewData) {
|
||||
storageIndex = HOST;
|
||||
}
|
||||
|
||||
return wrapper[storageIndex] = stylingTemplate ?
|
||||
allocStylingContext(slotValue, stylingTemplate) :
|
||||
createEmptyStylingContext(slotValue);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export function isAnimationProp(name: string): boolean {
|
||||
return name[0] === ANIMATION_PROP_PREFIX;
|
||||
}
|
||||
|
||||
export function forceClassesAsString(classes: string | {[key: string]: any} | null | undefined):
|
||||
string {
|
||||
if (classes && typeof classes !== 'string') {
|
||||
classes = Object.keys(classes).join(' ');
|
||||
}
|
||||
return (classes as string) || '';
|
||||
}
|
||||
|
||||
export function forceStylesAsString(styles: {[key: string]: any} | null | undefined): string {
|
||||
let str = '';
|
||||
if (styles) {
|
||||
const props = Object.keys(styles);
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
const prop = props[i];
|
||||
str += (i ? ';' : '') + `${prop}:${styles[prop]}`;
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
||||
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 a part 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 a part 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 || getLContext(target) !;
|
||||
if (!context) {
|
||||
ngDevMode && throwInvalidRefError();
|
||||
return null;
|
||||
}
|
||||
|
||||
const {lView, nodeIndex} = context;
|
||||
const stylingContext = getStylingContextFromLView(nodeIndex, lView);
|
||||
return getPlayerContext(stylingContext) || allocPlayerContext(stylingContext);
|
||||
}
|
||||
|
||||
export function getPlayerContext(stylingContext: StylingContext): PlayerContext|null {
|
||||
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');
|
||||
}
|
|
@ -11,16 +11,15 @@ import {AttributeMarker, TAttributes, TNode, TNodeType} from '../interfaces/node
|
|||
import {RElement} from '../interfaces/renderer';
|
||||
import {BINDING_INDEX, LView, RENDERER, TVIEW} from '../interfaces/view';
|
||||
import {getActiveDirectiveId, getActiveDirectiveSuperClassDepth, getActiveDirectiveSuperClassHeight, getCurrentStyleSanitizer, getLView, getPreviousOrParentTNode, getSelectedIndex, setCurrentStyleSanitizer} from '../state';
|
||||
import {forceClassesAsString, forceStylesAsString} from '../styling/util';
|
||||
import {NO_CHANGE} from '../tokens';
|
||||
import {renderStringify} from '../util/misc_utils';
|
||||
import {getNativeByTNode, getTNode} from '../util/view_utils';
|
||||
|
||||
import {flushStyling, updateClassBinding, updateStyleBinding} from './bindings';
|
||||
import {StylingMapArrayIndex, TStylingContext} from './interfaces';
|
||||
import {StylingMapArray, StylingMapArrayIndex, TStylingContext} from './interfaces';
|
||||
import {activateStylingMapFeature, addItemToStylingMap, normalizeIntoStylingMap, stylingMapToString} from './map_based_bindings';
|
||||
import {attachStylingDebugObject} from './styling_debug';
|
||||
import {allocTStylingContext, concatString, getInitialStylingValue, getStylingMapArray, hasClassInput, hasStyleInput, hasValueChanged, isContextLocked, isStylingContext, updateLastDirectiveIndex as _updateLastDirectiveIndex} from './util';
|
||||
import {allocTStylingContext, concatString, forceClassesAsString, forceStylesAsString, getInitialStylingValue, getStylingMapArray, hasClassInput, hasStyleInput, hasValueChanged, isContextLocked, isStylingContext, updateLastDirectiveIndex as _updateLastDirectiveIndex} from './util';
|
||||
|
||||
|
||||
|
||||
|
@ -49,19 +48,9 @@ import {allocTStylingContext, concatString, getInitialStylingValue, getStylingMa
|
|||
* @codeGenApi
|
||||
*/
|
||||
export function ɵɵstyling() {
|
||||
const lView = getLView();
|
||||
const tView = lView[TVIEW];
|
||||
const tView = getLView()[TVIEW];
|
||||
if (tView.firstTemplatePass) {
|
||||
const tNode = getPreviousOrParentTNode();
|
||||
const directiveStylingIndex = getActiveDirectiveStylingIndex();
|
||||
|
||||
// temporary workaround until `select(n)` is fully compatible
|
||||
if (directiveStylingIndex) {
|
||||
const fns = tNode.onElementCreationFns = tNode.onElementCreationFns || [];
|
||||
fns.push(() => updateLastDirectiveIndex(tNode, directiveStylingIndex));
|
||||
} else {
|
||||
updateLastDirectiveIndex(tNode, directiveStylingIndex);
|
||||
}
|
||||
updateLastDirectiveIndex(getPreviousOrParentTNode(), getActiveDirectiveStylingIndex());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -389,16 +378,12 @@ function normalizeStylingDirectiveInputValue(
|
|||
}
|
||||
|
||||
/**
|
||||
* Temporary function to bridge styling functionality between this new
|
||||
* refactor (which is here inside of `styling_next/`) and the old
|
||||
* implementation (which lives inside of `styling/`).
|
||||
* Flushes all styling code to the element.
|
||||
*
|
||||
* The new styling refactor ensures that styling flushing is called
|
||||
* automatically when a template function exits or a follow-up element
|
||||
* is visited (i.e. when `select(n)` is called). Because the `select(n)`
|
||||
* instruction is not fully implemented yet (it doesn't actually execute
|
||||
* host binding instruction code at the right time), this means that a
|
||||
* styling apply function is still needed.
|
||||
* This function is designed to be called from the template and hostBindings
|
||||
* functions and may be called multiple times depending whether multiple
|
||||
* sources of styling exist. If called multiple times, only the last call
|
||||
* to `stlyingApply()` will render styling to the element.
|
||||
*
|
||||
* @codeGenApi
|
||||
*/
|
||||
|
@ -447,18 +432,27 @@ export function registerInitialStylingOnTNode(
|
|||
}
|
||||
|
||||
if (classes && classes.length > StylingMapArrayIndex.ValuesStartPosition) {
|
||||
classes[StylingMapArrayIndex.RawValuePosition] = stylingMapToString(classes, true);
|
||||
tNode.classes = classes;
|
||||
if (!tNode.classes) {
|
||||
tNode.classes = classes;
|
||||
}
|
||||
updateRawValueOnContext(tNode.classes, stylingMapToString(classes, true));
|
||||
}
|
||||
|
||||
if (styles && styles.length > StylingMapArrayIndex.ValuesStartPosition) {
|
||||
styles[StylingMapArrayIndex.RawValuePosition] = stylingMapToString(styles, false);
|
||||
tNode.styles = styles;
|
||||
if (!tNode.styles) {
|
||||
tNode.styles = styles;
|
||||
}
|
||||
updateRawValueOnContext(tNode.styles, stylingMapToString(styles, false));
|
||||
}
|
||||
|
||||
return hasAdditionalInitialStyling;
|
||||
}
|
||||
|
||||
function updateRawValueOnContext(context: TStylingContext | StylingMapArray, value: string) {
|
||||
const stylingMapArr = getStylingMapArray(context) !;
|
||||
stylingMapArr[StylingMapArrayIndex.RawValuePosition] = value;
|
||||
}
|
||||
|
||||
export function getActiveDirectiveStylingIndex(): number {
|
||||
// whenever a directive's hostBindings function is called a uniqueId value
|
||||
// is assigned. Normally this is enough to help distinguish one directive
|
||||
|
|
|
@ -284,7 +284,7 @@ import {LView} from '../interfaces/view';
|
|||
export interface TStylingContext extends
|
||||
Array<number|string|number|boolean|null|StylingMapArray|{}> {
|
||||
/** Initial value position for static styles */
|
||||
[TStylingContextIndex.InitialStylingValuePosition]: StylingMapArray|null;
|
||||
[TStylingContextIndex.InitialStylingValuePosition]: StylingMapArray;
|
||||
|
||||
/** Configuration data for the context */
|
||||
[TStylingContextIndex.ConfigPosition]: TStylingConfigFlags;
|
||||
|
|
|
@ -11,6 +11,7 @@ import {isDifferent} from '../util/misc_utils';
|
|||
import {StylingMapArray, StylingMapArrayIndex, TStylingConfigFlags, TStylingContext, TStylingContextIndex, TStylingContextPropConfigFlags} from './interfaces';
|
||||
|
||||
const MAP_BASED_ENTRY_PROP_NAME = '--MAP--';
|
||||
const TEMPLATE_DIRECTIVE_INDEX = 0;
|
||||
|
||||
/**
|
||||
* Creates a new instance of the `TStylingContext`.
|
||||
|
@ -27,17 +28,14 @@ export function allocTStylingContext(initialStyling?: StylingMapArray | null): T
|
|||
// (this means that when map-based values are applied then sanitization will
|
||||
// be checked against each property).
|
||||
const mapBasedConfig = TStylingContextPropConfigFlags.SanitizationRequired;
|
||||
const context: TStylingContext = [
|
||||
initialStyling || null,
|
||||
return [
|
||||
initialStyling || [''], // empty initial-styling map value
|
||||
TStylingConfigFlags.Initial,
|
||||
// the LastDirectiveIndex value in the context is used to track which directive is the last
|
||||
// to call `stylingApply()`. The `-1` value implies that no directive has been set yet.
|
||||
-1,
|
||||
TEMPLATE_DIRECTIVE_INDEX,
|
||||
mapBasedConfig,
|
||||
0,
|
||||
MAP_BASED_ENTRY_PROP_NAME,
|
||||
];
|
||||
return context;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -54,12 +52,17 @@ export function allocTStylingContext(initialStyling?: StylingMapArray | null): T
|
|||
*/
|
||||
export function updateLastDirectiveIndex(
|
||||
context: TStylingContext, lastDirectiveIndex: number): void {
|
||||
const currentValue = context[TStylingContextIndex.LastDirectiveIndexPosition];
|
||||
if (lastDirectiveIndex !== currentValue) {
|
||||
context[TStylingContextIndex.LastDirectiveIndexPosition] = lastDirectiveIndex;
|
||||
if (currentValue === 0 && lastDirectiveIndex > 0) {
|
||||
if (lastDirectiveIndex === TEMPLATE_DIRECTIVE_INDEX) {
|
||||
const currentValue = context[TStylingContextIndex.LastDirectiveIndexPosition];
|
||||
if (currentValue > TEMPLATE_DIRECTIVE_INDEX) {
|
||||
// This means that a directive or two contained a host bindings function, but
|
||||
// now the template function also contains styling. When this combination of sources
|
||||
// comes up then we need to tell the context to store the state between updates
|
||||
// (because host bindings evaluation happens after template binding evaluation).
|
||||
markContextToPersistState(context);
|
||||
}
|
||||
} else {
|
||||
context[TStylingContextIndex.LastDirectiveIndexPosition] = lastDirectiveIndex;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -228,3 +231,23 @@ export function setMapValue(
|
|||
export function getMapValue(map: StylingMapArray, index: number): string|null {
|
||||
return map[index + StylingMapArrayIndex.ValueOffset] as string | null;
|
||||
}
|
||||
|
||||
export function forceClassesAsString(classes: string | {[key: string]: any} | null | undefined):
|
||||
string {
|
||||
if (classes && typeof classes !== 'string') {
|
||||
classes = Object.keys(classes).join(' ');
|
||||
}
|
||||
return (classes as string) || '';
|
||||
}
|
||||
|
||||
export function forceStylesAsString(styles: {[key: string]: any} | null | undefined): string {
|
||||
let str = '';
|
||||
if (styles) {
|
||||
const props = Object.keys(styles);
|
||||
for (let i = 0; i < props.length; i++) {
|
||||
const prop = props[i];
|
||||
str = concatString(str, `${prop}:${styles[prop]}`, ';');
|
||||
}
|
||||
}
|
||||
return str;
|
||||
}
|
||||
|
|
|
@ -10,8 +10,6 @@ import {CssSelector} from '../interfaces/projection';
|
|||
import {ProceduralRenderer3, RElement, isProceduralRenderer} from '../interfaces/renderer';
|
||||
import {RENDERER} from '../interfaces/view';
|
||||
import {getLView} from '../state';
|
||||
import {isAnimationProp} from '../styling/util';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
|
@ -115,3 +113,9 @@ export function isNameOnlyAttributeMarker(marker: string | AttributeMarker | Css
|
|||
return marker === AttributeMarker.Bindings || marker === AttributeMarker.Template ||
|
||||
marker === AttributeMarker.I18n;
|
||||
}
|
||||
|
||||
export const ANIMATION_PROP_PREFIX = '@';
|
||||
|
||||
export function isAnimationProp(name: string): boolean {
|
||||
return name[0] === ANIMATION_PROP_PREFIX;
|
||||
}
|
||||
|
|
|
@ -8,7 +8,7 @@
|
|||
import {assertDefined} from '../../util/assert';
|
||||
import {global} from '../../util/global';
|
||||
|
||||
import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getPlayers, getRootComponents, getViewComponent, markDirty} from '../global_utils_api';
|
||||
import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent, markDirty} from '../global_utils_api';
|
||||
|
||||
|
||||
|
||||
|
@ -48,7 +48,6 @@ export function publishDefaultGlobalUtils() {
|
|||
publishGlobalUtil('getInjector', getInjector);
|
||||
publishGlobalUtil('getRootComponents', getRootComponents);
|
||||
publishGlobalUtil('getDirectives', getDirectives);
|
||||
publishGlobalUtil('getPlayers', getPlayers);
|
||||
publishGlobalUtil('markDirty', markDirty);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,25 +12,21 @@ import {LContainer, TYPE} from '../interfaces/container';
|
|||
import {LContext, MONKEY_PATCH_KEY_NAME} from '../interfaces/context';
|
||||
import {TNode} from '../interfaces/node';
|
||||
import {RNode} from '../interfaces/renderer';
|
||||
import {StylingContext} from '../interfaces/styling';
|
||||
import {isLContainer, isLView} from '../interfaces/type_checks';
|
||||
import {FLAGS, HEADER_OFFSET, HOST, LView, LViewFlags, PARENT, PREORDER_HOOK_FLAGS, TData, TVIEW} from '../interfaces/view';
|
||||
|
||||
|
||||
|
||||
/**
|
||||
* For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`,
|
||||
* `StylingContext`) in same location in `LView`. This is because we don't want to pre-allocate
|
||||
* space for it because the storage is sparse. This file contains utilities for dealing with such
|
||||
* data types.
|
||||
* For efficiency reasons we often put several different data types (`RNode`, `LView`, `LContainer`)
|
||||
* in same location in `LView`. This is because we don't want to pre-allocate space for it
|
||||
* because the storage is sparse. This file contains utilities for dealing with such data types.
|
||||
*
|
||||
* How do we know what is stored at a given location in `LView`.
|
||||
* - `Array.isArray(value) === false` => `RNode` (The normal storage value)
|
||||
* - `Array.isArray(value) === true` => then the `value[0]` represents the wrapped value.
|
||||
* - `typeof value[TYPE] === 'object'` => `LView`
|
||||
* - This happens when we have a component at a given location
|
||||
* - `typeof value[TYPE] === 'number'` => `StylingContext`
|
||||
* - This happens when we have style/class binding at a given location.
|
||||
* - `typeof value[TYPE] === true` => `LContainer`
|
||||
* - This happens when we have `LContainer` binding at a given location.
|
||||
*
|
||||
|
@ -77,22 +73,6 @@ export function unwrapLContainer(value: RNode | LView | LContainer): LContainer|
|
|||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns `StylingContext` or `null` if not found.
|
||||
* @param value wrapped value of `RNode`, `LView`, `LContainer`, `StylingContext`
|
||||
*/
|
||||
export function unwrapStylingContext(value: RNode | LView | LContainer | StylingContext):
|
||||
StylingContext|null {
|
||||
while (Array.isArray(value)) {
|
||||
// This check is same as `isStylingContext()` but we don't call at as we don't want to call
|
||||
// `Array.isArray()` twice and give JITer more work for inlining.
|
||||
if (typeof value[TYPE] === 'number') return value as StylingContext;
|
||||
value = value[HOST] as any;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Retrieves an element value from the provided `viewData`, by unwrapping
|
||||
* from any containers, component views, or style contexts.
|
||||
|
|
|
@ -555,6 +555,35 @@ describe('styling', () => {
|
|||
expect(capturedMyClassBindingValue !).toEqual('foo');
|
||||
});
|
||||
|
||||
onlyInIvy('only ivy balances styling across directives and component host bindings')
|
||||
.it('should allow multiple directives to set dynamic and static classes independent of one another',
|
||||
() => {
|
||||
@Component({
|
||||
template: `
|
||||
<div dir-one dir-two></div>
|
||||
`
|
||||
})
|
||||
class Cmp {
|
||||
}
|
||||
|
||||
@Directive({selector: '[dir-one]', host: {'[class.dir-one]': 'dirOneExp'}})
|
||||
class DirOne {
|
||||
dirOneExp = true;
|
||||
}
|
||||
|
||||
@Directive({selector: '[dir-two]', host: {'class': 'dir-two'}})
|
||||
class DirTwo {
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp, DirOne, DirTwo]});
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
fixture.detectChanges();
|
||||
|
||||
const element = fixture.nativeElement.querySelector('div');
|
||||
expect(element.classList.contains('dir-one')).toBeTruthy();
|
||||
expect(element.classList.contains('dir-two')).toBeTruthy();
|
||||
});
|
||||
|
||||
describe('NgClass', () => {
|
||||
|
||||
// We had a bug where NgClass would not allocate sufficient slots for host bindings,
|
||||
|
@ -601,7 +630,5 @@ describe('styling', () => {
|
|||
expect(fixture.debugElement.nativeElement.textContent).toContain('Hello');
|
||||
|
||||
});
|
||||
|
||||
|
||||
});
|
||||
});
|
||||
|
|
|
@ -9,7 +9,7 @@
|
|||
import '@angular/core/test/bundling/util/src/reflect_metadata';
|
||||
|
||||
import {CommonModule} from '@angular/common';
|
||||
import {Component, Directive, ElementRef, HostBinding, HostListener, 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';
|
||||
import {Component, Directive, ElementRef, HostBinding, HostListener, NgModule, ɵmarkDirty as markDirty, ɵrenderComponent as renderComponent} from '@angular/core';
|
||||
|
||||
@Directive({
|
||||
selector: '[make-color-grey]',
|
||||
|
|
|
@ -182,9 +182,6 @@
|
|||
{
|
||||
"name": "appendChild"
|
||||
},
|
||||
{
|
||||
"name": "applyOnCreateInstructions"
|
||||
},
|
||||
{
|
||||
"name": "attachPatchData"
|
||||
},
|
||||
|
@ -695,6 +692,9 @@
|
|||
{
|
||||
"name": "unwrapRNode"
|
||||
},
|
||||
{
|
||||
"name": "updateRawValueOnContext"
|
||||
},
|
||||
{
|
||||
"name": "viewAttachedToChangeDetector"
|
||||
},
|
||||
|
|
|
@ -104,9 +104,6 @@
|
|||
{
|
||||
"name": "RENDERER_FACTORY"
|
||||
},
|
||||
{
|
||||
"name": "RendererStyleFlags3"
|
||||
},
|
||||
{
|
||||
"name": "SANITIZER"
|
||||
},
|
||||
|
@ -158,9 +155,6 @@
|
|||
{
|
||||
"name": "appendChild"
|
||||
},
|
||||
{
|
||||
"name": "applyOnCreateInstructions"
|
||||
},
|
||||
{
|
||||
"name": "attachPatchData"
|
||||
},
|
||||
|
@ -287,12 +281,6 @@
|
|||
{
|
||||
"name": "getLViewParent"
|
||||
},
|
||||
{
|
||||
"name": "getMapProp"
|
||||
},
|
||||
{
|
||||
"name": "getMapValue"
|
||||
},
|
||||
{
|
||||
"name": "getNativeAnchorNode"
|
||||
},
|
||||
|
@ -347,9 +335,6 @@
|
|||
{
|
||||
"name": "getSelectedIndex"
|
||||
},
|
||||
{
|
||||
"name": "getStylingMapArray"
|
||||
},
|
||||
{
|
||||
"name": "hasParentInjector"
|
||||
},
|
||||
|
@ -395,9 +380,6 @@
|
|||
{
|
||||
"name": "isRootView"
|
||||
},
|
||||
{
|
||||
"name": "isStylingContext"
|
||||
},
|
||||
{
|
||||
"name": "leaveView"
|
||||
},
|
||||
|
@ -458,15 +440,9 @@
|
|||
{
|
||||
"name": "renderEmbeddedTemplate"
|
||||
},
|
||||
{
|
||||
"name": "renderInitialStyling"
|
||||
},
|
||||
{
|
||||
"name": "renderStringify"
|
||||
},
|
||||
{
|
||||
"name": "renderStylingMap"
|
||||
},
|
||||
{
|
||||
"name": "resetAllStylingState"
|
||||
},
|
||||
|
@ -488,9 +464,6 @@
|
|||
{
|
||||
"name": "setBindingRoot"
|
||||
},
|
||||
{
|
||||
"name": "setClass"
|
||||
},
|
||||
{
|
||||
"name": "setCurrentDirectiveDef"
|
||||
},
|
||||
|
@ -518,9 +491,6 @@
|
|||
{
|
||||
"name": "setSelectedIndex"
|
||||
},
|
||||
{
|
||||
"name": "setStyle"
|
||||
},
|
||||
{
|
||||
"name": "setTNodeAndViewData"
|
||||
},
|
||||
|
|
|
@ -230,6 +230,9 @@
|
|||
{
|
||||
"name": "SkipSelf"
|
||||
},
|
||||
{
|
||||
"name": "TEMPLATE_DIRECTIVE_INDEX"
|
||||
},
|
||||
{
|
||||
"name": "TNODE"
|
||||
},
|
||||
|
@ -461,9 +464,6 @@
|
|||
{
|
||||
"name": "appendChild"
|
||||
},
|
||||
{
|
||||
"name": "applyOnCreateInstructions"
|
||||
},
|
||||
{
|
||||
"name": "applyStyling"
|
||||
},
|
||||
|
@ -1400,6 +1400,9 @@
|
|||
{
|
||||
"name": "updateLastDirectiveIndex"
|
||||
},
|
||||
{
|
||||
"name": "updateRawValueOnContext"
|
||||
},
|
||||
{
|
||||
"name": "updateStyleBinding"
|
||||
},
|
||||
|
|
|
@ -7,7 +7,6 @@
|
|||
*/
|
||||
import {ɵmarkDirty as markDirty} from '@angular/core';
|
||||
|
||||
import {getPlayers} from '../../src/render3/players';
|
||||
import {getComponent, getContext, getDirectives, getHostElement, getInjector, getListeners, getRootComponents, getViewComponent} from '../../src/render3/util/discovery_utils';
|
||||
import {GLOBAL_PUBLISH_EXPANDO_KEY, GlobalDevModeContainer, publishDefaultGlobalUtils, publishGlobalUtil} from '../../src/render3/util/global_utils';
|
||||
import {global} from '../../src/util/global';
|
||||
|
@ -46,8 +45,6 @@ describe('global utils', () => {
|
|||
it('should publish getInjector', () => { assertPublished('getInjector', getInjector); });
|
||||
|
||||
it('should publish markDirty', () => { assertPublished('markDirty', markDirty); });
|
||||
|
||||
it('should publish getPlayers', () => { assertPublished('getPlayers', getPlayers); });
|
||||
});
|
||||
});
|
||||
|
||||
|
|
|
@ -13,7 +13,6 @@ import {ɵɵallocHostVars, ɵɵcontainer, ɵɵcontainerRefreshEnd, ɵɵcontainer
|
|||
import {MONKEY_PATCH_KEY_NAME} from '../../src/render3/interfaces/context';
|
||||
import {RenderFlags} from '../../src/render3/interfaces/definition';
|
||||
import {RElement, Renderer3, RendererFactory3, domRendererFactory3} from '../../src/render3/interfaces/renderer';
|
||||
import {StylingIndex} from '../../src/render3/interfaces/styling';
|
||||
import {CONTEXT, HEADER_OFFSET} from '../../src/render3/interfaces/view';
|
||||
import {ɵɵsanitizeUrl} from '../../src/sanitization/sanitization';
|
||||
import {Sanitizer, SecurityContext} from '../../src/sanitization/security';
|
||||
|
|
|
@ -11,7 +11,6 @@ import {createTNode} from '@angular/core/src/render3/instructions/shared';
|
|||
import {AttributeMarker, TAttributes, TNode, TNodeType} from '../../src/render3/interfaces/node';
|
||||
import {CssSelector, CssSelectorList, SelectorFlags} from '../../src/render3/interfaces/projection';
|
||||
import {getProjectAsAttrValue, isNodeMatchingSelector, isNodeMatchingSelectorList} from '../../src/render3/node_selector_matcher';
|
||||
import {initializeStaticContext} from '../../src/render3/styling/class_and_style_bindings';
|
||||
|
||||
function testLStaticData(tagName: string, attrs: TAttributes | null): TNode {
|
||||
return createTNode(null !, null, TNodeType.Element, 0, tagName, attrs);
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,61 +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 {PlayState} from '../../../src/render3/interfaces/player';
|
||||
import {CorePlayerHandler} from '../../../src/render3/styling/core_player_handler';
|
||||
|
||||
import {MockPlayer} from './mock_player';
|
||||
|
||||
describe('CorePlayerHandler', () => {
|
||||
it('should kick off any animation players that have been queued once flushed', () => {
|
||||
const handler = new CorePlayerHandler();
|
||||
const p1 = new MockPlayer();
|
||||
const p2 = new MockPlayer();
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
|
||||
handler.queuePlayer(p1);
|
||||
handler.queuePlayer(p2);
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
|
||||
handler.flushPlayers();
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Running);
|
||||
expect(p2.state).toEqual(PlayState.Running);
|
||||
});
|
||||
|
||||
it('should only kick off animation players that have not been adopted by a parent player once flushed',
|
||||
() => {
|
||||
const handler = new CorePlayerHandler();
|
||||
const pRoot = new MockPlayer();
|
||||
const p1 = new MockPlayer();
|
||||
const p2 = new MockPlayer();
|
||||
|
||||
expect(pRoot.state).toEqual(PlayState.Pending);
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
|
||||
handler.queuePlayer(pRoot);
|
||||
handler.queuePlayer(p1);
|
||||
handler.queuePlayer(p2);
|
||||
|
||||
expect(pRoot.state).toEqual(PlayState.Pending);
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
|
||||
p1.parent = pRoot;
|
||||
|
||||
handler.flushPlayers();
|
||||
|
||||
expect(pRoot.state).toEqual(PlayState.Running);
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Running);
|
||||
});
|
||||
});
|
|
@ -1,61 +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 {PlayState, Player} from '../../../src/render3/interfaces/player';
|
||||
|
||||
export class MockPlayer implements Player {
|
||||
parent: Player|null = null;
|
||||
|
||||
data: any;
|
||||
log: string[] = [];
|
||||
state: PlayState = PlayState.Pending;
|
||||
private _listeners: {[state: string]: (() => any)[]} = {};
|
||||
|
||||
constructor(public value?: any) {}
|
||||
|
||||
play(): void {
|
||||
if (this.state === PlayState.Running) return;
|
||||
|
||||
this.state = PlayState.Running;
|
||||
this._emit(PlayState.Running);
|
||||
}
|
||||
|
||||
pause(): void {
|
||||
if (this.state === PlayState.Paused) return;
|
||||
|
||||
this.state = PlayState.Paused;
|
||||
this._emit(PlayState.Paused);
|
||||
}
|
||||
|
||||
finish(): void {
|
||||
if (this.state >= PlayState.Finished) return;
|
||||
|
||||
this.state = PlayState.Finished;
|
||||
this._emit(PlayState.Finished);
|
||||
}
|
||||
|
||||
destroy(): void {
|
||||
if (this.state >= PlayState.Destroyed) return;
|
||||
|
||||
this.state = PlayState.Destroyed;
|
||||
this._emit(PlayState.Destroyed);
|
||||
}
|
||||
|
||||
addEventListener(state: PlayState|number, cb: () => any): void {
|
||||
const key = state.toString();
|
||||
const arr = this._listeners[key] || (this._listeners[key] = []);
|
||||
arr.push(cb);
|
||||
}
|
||||
|
||||
private _emit(state: PlayState) {
|
||||
const callbacks = this._listeners[state] || [];
|
||||
for (let i = 0; i < callbacks.length; i++) {
|
||||
const cb = callbacks[i];
|
||||
cb();
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,312 +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 {QueryList} from '@angular/core';
|
||||
import {RenderFlags} from '@angular/core/src/render3';
|
||||
|
||||
import {getHostElement, ɵɵdefineComponent, ɵɵloadViewQuery, ɵɵviewQuery} from '../../../src/render3/index';
|
||||
import {markDirty, ɵɵelement, ɵɵelementEnd, ɵɵelementStart, ɵɵselect, ɵɵstyling, ɵɵstylingApply} from '../../../src/render3/instructions/all';
|
||||
import {PlayState, Player, PlayerHandler} from '../../../src/render3/interfaces/player';
|
||||
import {RElement} from '../../../src/render3/interfaces/renderer';
|
||||
import {addPlayer, getPlayers} from '../../../src/render3/players';
|
||||
import {ɵɵqueryRefresh} from '../../../src/render3/query';
|
||||
import {getOrCreatePlayerContext} from '../../../src/render3/styling/util';
|
||||
import {ComponentFixture} from '../render_util';
|
||||
|
||||
import {MockPlayer} from './mock_player';
|
||||
|
||||
describe('animation player access', () => {
|
||||
it('should add a player to the element', () => {
|
||||
const element = buildElement();
|
||||
expect(getPlayers(element)).toEqual([]);
|
||||
|
||||
const player = new MockPlayer();
|
||||
addPlayer(element, player);
|
||||
expect(getPlayers(element)).toEqual([player]);
|
||||
});
|
||||
|
||||
it('should add a player to the component host element', () => {
|
||||
const fixture = buildSuperComponent();
|
||||
const superComp = fixture.component;
|
||||
const component = superComp.query.first as Comp;
|
||||
|
||||
expect(component.name).toEqual('child-comp');
|
||||
expect(getPlayers(component)).toEqual([]);
|
||||
|
||||
const player = new MockPlayer();
|
||||
addPlayer(component, player);
|
||||
expect(getPlayers(component)).toEqual([player]);
|
||||
|
||||
const hostElement = getHostElement(component);
|
||||
expect(getPlayers(hostElement)).toEqual([player]);
|
||||
});
|
||||
|
||||
it('should add a player to an element that already contains styling', () => {
|
||||
const element = buildElementWithStyling();
|
||||
expect(getPlayers(element)).toEqual([]);
|
||||
|
||||
const player = new MockPlayer();
|
||||
addPlayer(element, player);
|
||||
expect(getPlayers(element)).toEqual([player]);
|
||||
});
|
||||
|
||||
it('should add a player to the element animation context and remove it once it completes', () => {
|
||||
const element = buildElement();
|
||||
const context = getOrCreatePlayerContext(element);
|
||||
expect(getPlayers(element)).toEqual([]);
|
||||
|
||||
const player = new MockPlayer();
|
||||
addPlayer(element, player);
|
||||
expect(getPlayers(element)).toEqual([player]);
|
||||
|
||||
player.destroy();
|
||||
expect(getPlayers(element)).toEqual([]);
|
||||
});
|
||||
|
||||
it('should flush all pending animation players after change detection', () => {
|
||||
const fixture = buildComponent();
|
||||
const element = fixture.hostElement.querySelector('div') !;
|
||||
|
||||
const player = new MockPlayer();
|
||||
addPlayer(element, player);
|
||||
|
||||
expect(player.state).toEqual(PlayState.Pending);
|
||||
fixture.update();
|
||||
expect(player.state).toEqual(PlayState.Running);
|
||||
});
|
||||
|
||||
it('should flush all animations in the given animation handler is apart of the component', () => {
|
||||
const handler = new MockPlayerHandler();
|
||||
|
||||
const fixture = new ComponentFixture(Comp, {playerHandler: handler});
|
||||
fixture.update();
|
||||
|
||||
const element = fixture.hostElement.querySelector('div') !;
|
||||
|
||||
const p1 = new MockPlayer();
|
||||
const p2 = new MockPlayer();
|
||||
|
||||
addPlayer(element, p1);
|
||||
addPlayer(element, p2);
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
|
||||
fixture.update();
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
|
||||
expect(handler.lastFlushedPlayers).toEqual([p1, p2]);
|
||||
});
|
||||
|
||||
it('should only play animation players that are not associated with a parent player', () => {
|
||||
const fixture = buildComponent();
|
||||
const element = fixture.hostElement.querySelector('div') !;
|
||||
|
||||
const p1 = new MockPlayer();
|
||||
const p2 = new MockPlayer();
|
||||
const pParent = new MockPlayer();
|
||||
p1.parent = pParent;
|
||||
|
||||
addPlayer(element, p1);
|
||||
addPlayer(element, p2);
|
||||
addPlayer(element, pParent);
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
expect(pParent.state).toEqual(PlayState.Pending);
|
||||
|
||||
fixture.update();
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Running);
|
||||
expect(pParent.state).toEqual(PlayState.Running);
|
||||
});
|
||||
|
||||
it('should not replay any previously queued players once change detection has run', () => {
|
||||
const fixture = buildComponent();
|
||||
const element = fixture.hostElement.querySelector('div') !;
|
||||
|
||||
const p1 = new MockPlayer();
|
||||
const p2 = new MockPlayer();
|
||||
const p3 = new MockPlayer();
|
||||
|
||||
addPlayer(element, p1);
|
||||
addPlayer(element, p2);
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
expect(p3.state).toEqual(PlayState.Pending);
|
||||
|
||||
fixture.update();
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Running);
|
||||
expect(p2.state).toEqual(PlayState.Running);
|
||||
expect(p3.state).toEqual(PlayState.Pending);
|
||||
|
||||
p1.pause();
|
||||
p2.pause();
|
||||
addPlayer(element, p3);
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Paused);
|
||||
expect(p2.state).toEqual(PlayState.Paused);
|
||||
expect(p3.state).toEqual(PlayState.Pending);
|
||||
|
||||
fixture.update();
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Paused);
|
||||
expect(p2.state).toEqual(PlayState.Paused);
|
||||
expect(p3.state).toEqual(PlayState.Running);
|
||||
});
|
||||
|
||||
it('should not run change detection on a template if only players are being added', () => {
|
||||
const fixture = buildComponent();
|
||||
const element = fixture.hostElement.querySelector('div') !;
|
||||
|
||||
let dcCount = 0;
|
||||
fixture.component.logger = () => { dcCount++; };
|
||||
|
||||
const p1 = new MockPlayer();
|
||||
addPlayer(element, p1);
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Pending);
|
||||
expect(dcCount).toEqual(0);
|
||||
|
||||
fixture.requestAnimationFrame.flush();
|
||||
|
||||
expect(p1.state).toEqual(PlayState.Running);
|
||||
expect(dcCount).toEqual(0);
|
||||
|
||||
const p2 = new MockPlayer();
|
||||
addPlayer(element, p2);
|
||||
markDirty(fixture.component);
|
||||
|
||||
expect(p2.state).toEqual(PlayState.Pending);
|
||||
|
||||
fixture.requestAnimationFrame.flush();
|
||||
|
||||
expect(p2.state).toEqual(PlayState.Running);
|
||||
expect(p1.state).toEqual(PlayState.Running);
|
||||
expect(dcCount).toEqual(1);
|
||||
|
||||
const p3 = new MockPlayer();
|
||||
addPlayer(element, p3);
|
||||
|
||||
fixture.requestAnimationFrame.flush();
|
||||
|
||||
expect(p3.state).toEqual(PlayState.Running);
|
||||
expect(p2.state).toEqual(PlayState.Running);
|
||||
expect(p1.state).toEqual(PlayState.Running);
|
||||
|
||||
expect(dcCount).toEqual(1);
|
||||
});
|
||||
});
|
||||
|
||||
function buildElement() {
|
||||
return buildComponent().hostElement.querySelector('div') as RElement;
|
||||
}
|
||||
|
||||
function buildComponent() {
|
||||
const fixture = new ComponentFixture(Comp);
|
||||
fixture.update();
|
||||
return fixture;
|
||||
}
|
||||
|
||||
function buildSuperComponent() {
|
||||
const fixture = new ComponentFixture(SuperComp);
|
||||
fixture.update();
|
||||
return fixture;
|
||||
}
|
||||
|
||||
function buildElementWithStyling() {
|
||||
const fixture = new ComponentFixture(CompWithStyling);
|
||||
fixture.update();
|
||||
return fixture.hostElement.querySelector('div') as RElement;
|
||||
}
|
||||
|
||||
class Comp {
|
||||
static ngComponentDef = ɵɵdefineComponent({
|
||||
type: Comp,
|
||||
exportAs: ['child'],
|
||||
selectors: [['child-comp']],
|
||||
factory: () => new Comp(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: Comp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵelement(0, 'div');
|
||||
}
|
||||
ctx.logger();
|
||||
}
|
||||
});
|
||||
|
||||
name = 'child-comp';
|
||||
logger: () => any = () => {};
|
||||
}
|
||||
|
||||
class CompWithStyling {
|
||||
static ngComponentDef = ɵɵdefineComponent({
|
||||
type: CompWithStyling,
|
||||
exportAs: ['child-styled'],
|
||||
selectors: [['child-styled-comp']],
|
||||
factory: () => new CompWithStyling(),
|
||||
consts: 1,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: CompWithStyling) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵelementStart(0, 'div');
|
||||
ɵɵstyling();
|
||||
ɵɵelementEnd();
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
ɵɵstylingApply();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
name = 'child-styled-comp';
|
||||
}
|
||||
|
||||
class SuperComp {
|
||||
static ngComponentDef = ɵɵdefineComponent({
|
||||
type: SuperComp,
|
||||
selectors: [['super-comp']],
|
||||
factory: () => new SuperComp(),
|
||||
consts: 3,
|
||||
vars: 0,
|
||||
template: (rf: RenderFlags, ctx: SuperComp) => {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵelementStart(0, 'div');
|
||||
ɵɵelement(1, 'child-comp', ['child', ''], ['child', 'child']);
|
||||
ɵɵelementEnd();
|
||||
}
|
||||
},
|
||||
viewQuery: function(rf: RenderFlags, ctx: SuperComp) {
|
||||
if (rf & RenderFlags.Create) {
|
||||
ɵɵviewQuery(['child'], true, null);
|
||||
}
|
||||
if (rf & RenderFlags.Update) {
|
||||
let tmp: any;
|
||||
ɵɵqueryRefresh(tmp = ɵɵloadViewQuery<QueryList<any>>()) &&
|
||||
(ctx.query = tmp as QueryList<any>);
|
||||
}
|
||||
},
|
||||
directives: [Comp]
|
||||
});
|
||||
|
||||
name = 'super-comp';
|
||||
query !: QueryList<any>;
|
||||
}
|
||||
|
||||
class MockPlayerHandler implements PlayerHandler {
|
||||
players: Player[] = [];
|
||||
lastFlushedPlayers: Player[] = [];
|
||||
flushPlayers(): void {
|
||||
this.lastFlushedPlayers = [...this.players];
|
||||
this.players = [];
|
||||
}
|
||||
queuePlayer(player: Player): void { this.players.push(player); }
|
||||
}
|
|
@ -7,9 +7,7 @@
|
|||
*/
|
||||
|
||||
import {createLContainer, createLView, createTNode, createTView} from '@angular/core/src/render3/instructions/shared';
|
||||
import {isLContainer, isLView, isStylingContext} from '@angular/core/src/render3/interfaces/type_checks';
|
||||
import {createEmptyStylingContext} from '@angular/core/src/render3/styling/util';
|
||||
import {unwrapLContainer, unwrapLView, unwrapRNode, unwrapStylingContext} from '@angular/core/src/render3/util/view_utils';
|
||||
import {isLContainer, isLView} from '@angular/core/src/render3/interfaces/type_checks';
|
||||
|
||||
describe('view_utils', () => {
|
||||
it('should verify unwrap methods', () => {
|
||||
|
@ -18,18 +16,11 @@ describe('view_utils', () => {
|
|||
const lView = createLView(null, tView, {}, 0, div, null, {} as any, {} as any, null, null);
|
||||
const tNode = createTNode(null !, null, 3, 0, 'div', []);
|
||||
const lContainer = createLContainer(lView, lView, div, tNode, true);
|
||||
const styleContext = createEmptyStylingContext(lContainer, null, null, null);
|
||||
|
||||
expect(isLView(lView)).toBe(true);
|
||||
expect(isLView(lContainer)).toBe(false);
|
||||
expect(isLView(styleContext)).toBe(false);
|
||||
|
||||
expect(isLContainer(lView)).toBe(false);
|
||||
expect(isLContainer(lContainer)).toBe(true);
|
||||
expect(isLContainer(styleContext)).toBe(false);
|
||||
|
||||
expect(isStylingContext(lView)).toBe(false);
|
||||
expect(isStylingContext(lContainer)).toBe(false);
|
||||
expect(isStylingContext(styleContext)).toBe(true);
|
||||
});
|
||||
});
|
||||
|
|
|
@ -10,8 +10,6 @@ export declare function getInjector(target: {}): Injector;
|
|||
|
||||
export declare function getListeners(element: Element): Listener[];
|
||||
|
||||
export declare function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[];
|
||||
|
||||
export declare function getRootComponents(target: {}): any[];
|
||||
|
||||
export declare function getViewComponent<T = {}>(element: Element | {}): T | null;
|
||||
|
|
Loading…
Reference in New Issue