feat(ivy): provide groundwork for animations in core (#25234)
PR Close #25234
This commit is contained in:
parent
a880686081
commit
82a14dc107
@ -166,6 +166,17 @@ export {
|
|||||||
getContext as ɵgetContext
|
getContext as ɵgetContext
|
||||||
} from './render3/context_discovery';
|
} from './render3/context_discovery';
|
||||||
|
|
||||||
|
export {
|
||||||
|
Player as ɵPlayer,
|
||||||
|
PlayState as ɵPlayState,
|
||||||
|
PlayerHandler as ɵPlayerHandler,
|
||||||
|
} from './render3/animations/interfaces';
|
||||||
|
|
||||||
|
export {
|
||||||
|
addPlayer as ɵaddPlayer,
|
||||||
|
getPlayers as ɵgetPlayers,
|
||||||
|
} from './render3/animations/players';
|
||||||
|
|
||||||
// we reexport these symbols just so that they are retained during the dead code elimination
|
// we reexport these symbols just so that they are retained during the dead code elimination
|
||||||
// performed by rollup while it's creating fesm files.
|
// performed by rollup while it's creating fesm files.
|
||||||
//
|
//
|
||||||
|
24
packages/core/src/render3/animations/core_player_handler.ts
Normal file
24
packages/core/src/render3/animations/core_player_handler.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/**
|
||||||
|
* @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 {Player, PlayerHandler} from './interfaces';
|
||||||
|
|
||||||
|
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.play();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this._players.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
queuePlayer(player: Player) { this._players.push(player); }
|
||||||
|
}
|
56
packages/core/src/render3/animations/interfaces.ts
Normal file
56
packages/core/src/render3/animations/interfaces.ts
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A shared interface which contains an animation player
|
||||||
|
*/
|
||||||
|
export interface Player {
|
||||||
|
parent?: Player|null;
|
||||||
|
state: PlayState;
|
||||||
|
play(): void;
|
||||||
|
pause(): void;
|
||||||
|
finish(): void;
|
||||||
|
destroy(): void;
|
||||||
|
addEventListener(state: PlayState|string, cb: (data?: any) => any): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The state of a given player
|
||||||
|
*
|
||||||
|
* Do not change the increasing nature of the numbers since the player
|
||||||
|
* code may compare state by checking if a number is higher or lower than
|
||||||
|
* a certain numeric value.
|
||||||
|
*/
|
||||||
|
export const enum PlayState {Pending = 0, Running = 1, Paused = 2, Finished = 100, Destroyed = 200}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The context that stores all active animation players present on an element.
|
||||||
|
*/
|
||||||
|
export declare type AnimationContext = Player[];
|
||||||
|
export declare type ComponentInstance = {};
|
||||||
|
export declare type DirectiveInstance = {};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Designed to be used as an injection service to capture all animation players.
|
||||||
|
*
|
||||||
|
* When present all animation players will be passed into the flush method below.
|
||||||
|
* This feature is designed to service application-wide animation testing, live
|
||||||
|
* debugging as well as custom animation choreographing tools.
|
||||||
|
*/
|
||||||
|
export interface PlayerHandler {
|
||||||
|
/**
|
||||||
|
* Designed to kick off the player at the end of change detection
|
||||||
|
*/
|
||||||
|
flushPlayers(): void;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param player The player that has been scheduled to run within the application.
|
||||||
|
* @param context The context as to where the player was bound to
|
||||||
|
*/
|
||||||
|
queuePlayer(player: Player, context: ComponentInstance|DirectiveInstance|HTMLElement): void;
|
||||||
|
}
|
72
packages/core/src/render3/animations/players.ts
Normal file
72
packages/core/src/render3/animations/players.ts
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
/**
|
||||||
|
* @license
|
||||||
|
* Copyright Google Inc. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by an MIT-style license that can be
|
||||||
|
* found in the LICENSE file at https://angular.io/license
|
||||||
|
*/
|
||||||
|
import '../ng_dev_mode';
|
||||||
|
|
||||||
|
import {LContext, getContext} from '../context_discovery';
|
||||||
|
import {scheduleTick} from '../instructions';
|
||||||
|
import {LElementNode} from '../interfaces/node';
|
||||||
|
import {RootContextFlags} from '../interfaces/view';
|
||||||
|
import {StylingContext, StylingIndex, createEmptyStylingContext} from '../styling';
|
||||||
|
import {getRootContext} from '../util';
|
||||||
|
|
||||||
|
import {CorePlayerHandler} from './core_player_handler';
|
||||||
|
import {AnimationContext, ComponentInstance, DirectiveInstance, PlayState, Player} from './interfaces';
|
||||||
|
|
||||||
|
export function addPlayer(
|
||||||
|
ref: ComponentInstance | DirectiveInstance | HTMLElement, player: Player): void {
|
||||||
|
const elementContext = getContext(ref) !;
|
||||||
|
const animationContext = getOrCreateAnimationContext(elementContext.native, elementContext) !;
|
||||||
|
animationContext.push(player);
|
||||||
|
|
||||||
|
player.addEventListener(PlayState.Destroyed, () => {
|
||||||
|
const index = animationContext.indexOf(player);
|
||||||
|
if (index >= 0) {
|
||||||
|
animationContext.splice(index, 1);
|
||||||
|
}
|
||||||
|
player.destroy();
|
||||||
|
});
|
||||||
|
|
||||||
|
const rootContext = getRootContext(elementContext.lViewData);
|
||||||
|
const playerHandler =
|
||||||
|
rootContext.playerHandler || (rootContext.playerHandler = new CorePlayerHandler());
|
||||||
|
playerHandler.queuePlayer(player, ref);
|
||||||
|
|
||||||
|
const nothingScheduled = rootContext.flags === RootContextFlags.Empty;
|
||||||
|
|
||||||
|
// change detection may or may not happen therefore
|
||||||
|
// the core code needs to be kicked off to flush the animations
|
||||||
|
rootContext.flags |= RootContextFlags.FlushPlayers;
|
||||||
|
if (nothingScheduled) {
|
||||||
|
scheduleTick(rootContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getPlayers(ref: ComponentInstance | DirectiveInstance | HTMLElement): Player[] {
|
||||||
|
return getOrCreateAnimationContext(ref);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getOrCreateAnimationContext(
|
||||||
|
target: {}, context?: LContext | null): AnimationContext {
|
||||||
|
context = context || getContext(target) !;
|
||||||
|
if (ngDevMode && !context) {
|
||||||
|
throw new Error(
|
||||||
|
'Only elements that exist in an Angular application can be used for animations');
|
||||||
|
}
|
||||||
|
|
||||||
|
const {lViewData, lNodeIndex} = context;
|
||||||
|
const value = lViewData[lNodeIndex];
|
||||||
|
let stylingContext = value as StylingContext;
|
||||||
|
if (!Array.isArray(value)) {
|
||||||
|
stylingContext = lViewData[lNodeIndex] = createEmptyStylingContext(value as LElementNode);
|
||||||
|
}
|
||||||
|
return stylingContext[StylingIndex.AnimationContext] || allocAnimationContext(stylingContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
function allocAnimationContext(data: StylingContext): AnimationContext {
|
||||||
|
return data[StylingIndex.AnimationContext] = [];
|
||||||
|
}
|
@ -12,16 +12,18 @@ import {Type} from '../core';
|
|||||||
import {Injector} from '../di/injector';
|
import {Injector} from '../di/injector';
|
||||||
import {Sanitizer} from '../sanitization/security';
|
import {Sanitizer} from '../sanitization/security';
|
||||||
|
|
||||||
|
import {PlayerHandler} from './animations/interfaces';
|
||||||
import {assertComponentType, assertDefined} from './assert';
|
import {assertComponentType, assertDefined} from './assert';
|
||||||
|
import {getLElementFromComponent, readPatchedLViewData} from './context_discovery';
|
||||||
|
import {getComponentDef} from './definition';
|
||||||
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
import {queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||||
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, getRootView, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions';
|
import {CLEAN_PROMISE, baseDirectiveCreate, createLViewData, createTView, detectChangesInternal, enterView, executeInitAndContentHooks, hostElement, leaveView, locateHostElement, setHostBindings, queueHostBindingForCheck,} from './instructions';
|
||||||
import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition';
|
import {ComponentDef, ComponentDefInternal, ComponentType} from './interfaces/definition';
|
||||||
import {LElementNode} from './interfaces/node';
|
import {LElementNode} from './interfaces/node';
|
||||||
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
import {RElement, RendererFactory3, domRendererFactory3} from './interfaces/renderer';
|
||||||
import {LViewData, LViewFlags, RootContext, INJECTOR, CONTEXT, TVIEW} from './interfaces/view';
|
import {CONTEXT, INJECTOR, LViewData, LViewFlags, RootContext, RootContextFlags, TVIEW} from './interfaces/view';
|
||||||
import {stringify} from './util';
|
import {getRootView, stringify} from './util';
|
||||||
import {getComponentDef} from './definition';
|
|
||||||
import {getLElementFromComponent, readPatchedLViewData} from './context_discovery';
|
|
||||||
|
|
||||||
|
|
||||||
/** Options that control how the component should be bootstrapped. */
|
/** Options that control how the component should be bootstrapped. */
|
||||||
@ -32,6 +34,9 @@ export interface CreateComponentOptions {
|
|||||||
/** A custom sanitizer instance */
|
/** A custom sanitizer instance */
|
||||||
sanitizer?: Sanitizer;
|
sanitizer?: Sanitizer;
|
||||||
|
|
||||||
|
/** A custom animation player handler */
|
||||||
|
playerHandler?: PlayerHandler;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Host element on which the component will be bootstrapped. If not specified,
|
* Host element on which the component will be bootstrapped. If not specified,
|
||||||
* the component definition's `tag` is used to query the existing DOM for the
|
* the component definition's `tag` is used to query the existing DOM for the
|
||||||
@ -109,7 +114,8 @@ export function renderComponent<T>(
|
|||||||
const hostNode = locateHostElement(rendererFactory, opts.host || componentTag);
|
const hostNode = locateHostElement(rendererFactory, opts.host || componentTag);
|
||||||
const rootFlags = componentDef.onPush ? LViewFlags.Dirty | LViewFlags.IsRoot :
|
const rootFlags = componentDef.onPush ? LViewFlags.Dirty | LViewFlags.IsRoot :
|
||||||
LViewFlags.CheckAlways | LViewFlags.IsRoot;
|
LViewFlags.CheckAlways | LViewFlags.IsRoot;
|
||||||
const rootContext = createRootContext(opts.scheduler || requestAnimationFrame.bind(window));
|
const rootContext = createRootContext(
|
||||||
|
opts.scheduler || requestAnimationFrame.bind(window), opts.playerHandler || null);
|
||||||
|
|
||||||
const rootView: LViewData = createLViewData(
|
const rootView: LViewData = createLViewData(
|
||||||
rendererFactory.createRenderer(hostNode, componentDef),
|
rendererFactory.createRenderer(hostNode, componentDef),
|
||||||
@ -157,11 +163,14 @@ export function createRootComponent<T>(
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function createRootContext(scheduler: (workFn: () => void) => void): RootContext {
|
export function createRootContext(
|
||||||
|
scheduler: (workFn: () => void) => void, playerHandler?: PlayerHandler|null): RootContext {
|
||||||
return {
|
return {
|
||||||
components: [],
|
components: [],
|
||||||
scheduler: scheduler,
|
scheduler: scheduler,
|
||||||
clean: CLEAN_PROMISE,
|
clean: CLEAN_PROMISE,
|
||||||
|
playerHandler: playerHandler || null,
|
||||||
|
flags: RootContextFlags.Empty
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -11,7 +11,6 @@ import {assertEqual} from './assert';
|
|||||||
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
import {LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||||
import {RElement} from './interfaces/renderer';
|
import {RElement} from './interfaces/renderer';
|
||||||
import {CONTEXT, DIRECTIVES, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
|
import {CONTEXT, DIRECTIVES, HEADER_OFFSET, LViewData, TVIEW} from './interfaces/view';
|
||||||
import {readElementValue} from './util';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This property will be monkey-patched on elements, components and directives
|
* This property will be monkey-patched on elements, components and directives
|
||||||
@ -401,3 +400,7 @@ function getDirectiveEndIndex(tNode: TNode, startIndex: number): number {
|
|||||||
const count = tNode.flags & TNodeFlags.DirectiveCountMask;
|
const count = tNode.flags & TNodeFlags.DirectiveCountMask;
|
||||||
return count ? (startIndex + count) : -1;
|
return count ? (startIndex + count) : -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function readElementValue(value: LElementNode | any[]): LElementNode {
|
||||||
|
return (Array.isArray(value) ? (value as any as any[])[0] : value) as LElementNode;
|
||||||
|
}
|
||||||
|
@ -13,7 +13,7 @@ import {Sanitizer} from '../sanitization/security';
|
|||||||
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
||||||
|
|
||||||
import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert';
|
import {assertDefined, assertEqual, assertLessThan, assertNotEqual} from './assert';
|
||||||
import {attachPatchData, getLElementFromComponent, readPatchedLViewData} from './context_discovery';
|
import {attachPatchData, getLElementFromComponent, readElementValue, readPatchedLViewData} from './context_discovery';
|
||||||
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
import {throwCyclicDependencyError, throwErrorIfNoChangesMode, throwMultipleComponentError} from './errors';
|
||||||
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
import {executeHooks, executeInitHooks, queueInitHooks, queueLifecycleHooks} from './hooks';
|
||||||
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
import {ACTIVE_INDEX, LContainer, RENDER_PARENT, VIEWS} from './interfaces/container';
|
||||||
@ -23,12 +23,12 @@ import {AttributeMarker, InitialInputData, InitialInputs, LContainerNode, LEleme
|
|||||||
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
import {CssSelectorList, NG_PROJECT_AS_ATTR_NAME} from './interfaces/projection';
|
||||||
import {LQueries} from './interfaces/query';
|
import {LQueries} from './interfaces/query';
|
||||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, RendererFactory3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
|
import {BINDING_INDEX, CLEANUP, CONTAINER_INDEX, CONTENT_QUERIES, CONTEXT, CurrentMatchesList, DECLARATION_VIEW, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, INJECTOR, LViewData, LViewFlags, NEXT, OpaqueViewState, PARENT, QUERIES, RENDERER, RootContext, RootContextFlags, SANITIZER, TAIL, TVIEW, TView} from './interfaces/view';
|
||||||
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
import {assertNodeOfPossibleTypes, assertNodeType} from './node_assert';
|
||||||
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getContainerNode, getHostElementNode, getLViewChild, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation';
|
import {appendChild, appendProjectedNode, createTextNode, findComponentView, getContainerNode, getHostElementNode, getLViewChild, getParentOrContainerNode, getRenderParent, insertView, removeView} from './node_manipulation';
|
||||||
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
import {isNodeMatchingSelectorList, matchingSelectorIndex} from './node_selector_matcher';
|
||||||
import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling';
|
import {StylingContext, allocStylingContext, createStylingContextTemplate, renderStyling as renderElementStyles, updateClassProp as updateElementClassProp, updateStyleProp as updateElementStyleProp, updateStylingMap} from './styling';
|
||||||
import {assertDataInRangeInternal, getLNode, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, readElementValue, stringify} from './util';
|
import {assertDataInRangeInternal, getLNode, getRootContext, getRootView, isContentQueryHost, isDifferent, loadElementInternal, loadInternal, stringify} from './util';
|
||||||
import {ViewRef} from './view_ref';
|
import {ViewRef} from './view_ref';
|
||||||
|
|
||||||
|
|
||||||
@ -2354,9 +2354,14 @@ export function markViewDirty(view: LViewData): void {
|
|||||||
}
|
}
|
||||||
currentView[FLAGS] |= LViewFlags.Dirty;
|
currentView[FLAGS] |= LViewFlags.Dirty;
|
||||||
ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext should be defined');
|
ngDevMode && assertDefined(currentView[CONTEXT], 'rootContext should be defined');
|
||||||
scheduleTick(currentView[CONTEXT] as RootContext);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const rootContext = currentView[CONTEXT] as RootContext;
|
||||||
|
const nothingScheduled = rootContext.flags === RootContextFlags.Empty;
|
||||||
|
rootContext.flags |= RootContextFlags.DetectChanges;
|
||||||
|
if (nothingScheduled) {
|
||||||
|
scheduleTick(rootContext);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Used to schedule change detection on the whole application.
|
* Used to schedule change detection on the whole application.
|
||||||
@ -2374,9 +2379,21 @@ export function scheduleTick<T>(rootContext: RootContext) {
|
|||||||
let res: null|((val: null) => void);
|
let res: null|((val: null) => void);
|
||||||
rootContext.clean = new Promise<null>((r) => res = r);
|
rootContext.clean = new Promise<null>((r) => res = r);
|
||||||
rootContext.scheduler(() => {
|
rootContext.scheduler(() => {
|
||||||
tickRootContext(rootContext);
|
if (rootContext.flags & RootContextFlags.DetectChanges) {
|
||||||
res !(null);
|
rootContext.flags &= ~RootContextFlags.DetectChanges;
|
||||||
|
tickRootContext(rootContext);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootContext.flags & RootContextFlags.FlushPlayers) {
|
||||||
|
rootContext.flags &= ~RootContextFlags.FlushPlayers;
|
||||||
|
const playerHandler = rootContext.playerHandler;
|
||||||
|
if (playerHandler) {
|
||||||
|
playerHandler.flushPlayers();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
rootContext.clean = _CLEAN_PROMISE;
|
rootContext.clean = _CLEAN_PROMISE;
|
||||||
|
res !(null);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2406,22 +2423,6 @@ function tickRootContext(rootContext: RootContext) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Retrieve the root view from any component by walking the parent `LViewData` until
|
|
||||||
* reaching the root `LViewData`.
|
|
||||||
*
|
|
||||||
* @param component any component
|
|
||||||
*/
|
|
||||||
|
|
||||||
export function getRootView(component: any): LViewData {
|
|
||||||
ngDevMode && assertDefined(component, 'component');
|
|
||||||
let lViewData = readPatchedLViewData(component) !;
|
|
||||||
while (lViewData && !(lViewData[FLAGS] & LViewFlags.IsRoot)) {
|
|
||||||
lViewData = lViewData[PARENT] !;
|
|
||||||
}
|
|
||||||
return lViewData;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Synchronously perform change detection on a component (and possibly its sub-components).
|
* Synchronously perform change detection on a component (and possibly its sub-components).
|
||||||
*
|
*
|
||||||
|
@ -9,6 +9,7 @@
|
|||||||
import {Injector} from '../../di/injector';
|
import {Injector} from '../../di/injector';
|
||||||
import {QueryList} from '../../linker';
|
import {QueryList} from '../../linker';
|
||||||
import {Sanitizer} from '../../sanitization/security';
|
import {Sanitizer} from '../../sanitization/security';
|
||||||
|
import {PlayerHandler} from '../animations/interfaces';
|
||||||
|
|
||||||
import {LContainer} from './container';
|
import {LContainer} from './container';
|
||||||
import {ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefList, PipeDefInternal, PipeDefList} from './definition';
|
import {ComponentQuery, ComponentTemplate, DirectiveDefInternal, DirectiveDefList, PipeDefInternal, PipeDefList} from './definition';
|
||||||
@ -499,6 +500,9 @@ export interface TView {
|
|||||||
contentQueries: number[]|null;
|
contentQueries: number[]|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export const enum RootContextFlags {Empty = 0b00, DetectChanges = 0b01, FlushPlayers = 0b10}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* RootContext contains information which is shared for all components which
|
* RootContext contains information which is shared for all components which
|
||||||
* were bootstrapped with {@link renderComponent}.
|
* were bootstrapped with {@link renderComponent}.
|
||||||
@ -522,6 +526,16 @@ export interface RootContext {
|
|||||||
* {@link renderComponent}.
|
* {@link renderComponent}.
|
||||||
*/
|
*/
|
||||||
components: {}[];
|
components: {}[];
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The player flushing handler to kick off all animations
|
||||||
|
*/
|
||||||
|
playerHandler: PlayerHandler|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* What render-related operations to run once a scheduler has been set
|
||||||
|
*/
|
||||||
|
flags: RootContextFlags;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -7,7 +7,7 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {assertDefined} from './assert';
|
import {assertDefined} from './assert';
|
||||||
import {attachPatchData} from './context_discovery';
|
import {attachPatchData, readElementValue} from './context_discovery';
|
||||||
import {callHooks} from './hooks';
|
import {callHooks} from './hooks';
|
||||||
import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
import {LContainer, RENDER_PARENT, VIEWS, unusedValueExportToPlacateAjd as unused1} from './interfaces/container';
|
||||||
import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
import {LContainerNode, LElementContainerNode, LElementNode, LTextNode, TContainerNode, TElementNode, TNode, TNodeFlags, TNodeType, TViewNode, unusedValueExportToPlacateAjd as unused2} from './interfaces/node';
|
||||||
@ -15,7 +15,7 @@ import {unusedValueExportToPlacateAjd as unused3} from './interfaces/projection'
|
|||||||
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
import {ProceduralRenderer3, RComment, RElement, RNode, RText, Renderer3, isProceduralRenderer, unusedValueExportToPlacateAjd as unused4} from './interfaces/renderer';
|
||||||
import {CLEANUP, CONTAINER_INDEX, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
import {CLEANUP, CONTAINER_INDEX, DIRECTIVES, FLAGS, HEADER_OFFSET, HOST_NODE, HookData, LViewData, LViewFlags, NEXT, PARENT, QUERIES, RENDERER, TVIEW, unusedValueExportToPlacateAjd as unused5} from './interfaces/view';
|
||||||
import {assertNodeType} from './node_assert';
|
import {assertNodeType} from './node_assert';
|
||||||
import {getLNode, readElementValue, stringify} from './util';
|
import {getLNode, stringify} from './util';
|
||||||
|
|
||||||
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
const unusedValueToPlacateAjd = unused1 + unused2 + unused3 + unused4 + unused5;
|
||||||
|
|
||||||
|
@ -7,11 +7,11 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
import {StyleSanitizeFn} from '../sanitization/style_sanitizer';
|
||||||
|
import {AnimationContext} from './animations/interfaces';
|
||||||
import {InitialStylingFlags} from './interfaces/definition';
|
import {InitialStylingFlags} from './interfaces/definition';
|
||||||
import {LElementNode} from './interfaces/node';
|
import {LElementNode} from './interfaces/node';
|
||||||
import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces/renderer';
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The styling context acts as a styling manifest (shaped as an array) for determining which
|
* The styling context acts as a styling manifest (shaped as an array) for determining which
|
||||||
* styling properties have been assigned via the provided `updateStylingMap`, `updateStyleProp`
|
* styling properties have been assigned via the provided `updateStylingMap`, `updateStyleProp`
|
||||||
@ -115,41 +115,47 @@ import {Renderer3, RendererStyleFlags3, isProceduralRenderer} from './interfaces
|
|||||||
* `updateStylingMap` can include new CSS properties that will be added to the context).
|
* `updateStylingMap` can include new CSS properties that will be added to the context).
|
||||||
*/
|
*/
|
||||||
export interface StylingContext extends
|
export interface StylingContext extends
|
||||||
Array<InitialStyles|number|string|boolean|LElementNode|StyleSanitizeFn|null> {
|
Array<InitialStyles|number|string|boolean|LElementNode|StyleSanitizeFn|AnimationContext|null> {
|
||||||
/**
|
/**
|
||||||
* Location of element that is used as a target for this context.
|
* Location of element that is used as a target for this context.
|
||||||
*/
|
*/
|
||||||
[0]: LElementNode|null;
|
[StylingIndex.ElementPosition]: LElementNode|null;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Location of animation context (which contains the active players) for this element styling
|
||||||
|
* context.
|
||||||
|
*/
|
||||||
|
[StylingIndex.AnimationContext]: AnimationContext|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The style sanitizer that is used within this context
|
* The style sanitizer that is used within this context
|
||||||
*/
|
*/
|
||||||
[1]: StyleSanitizeFn|null;
|
[StylingIndex.StyleSanitizerPosition]: StyleSanitizeFn|null;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Location of initial data shared by all instances of this style.
|
* Location of initial data shared by all instances of this style.
|
||||||
*/
|
*/
|
||||||
[2]: InitialStyles;
|
[StylingIndex.InitialStylesPosition]: InitialStyles;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A numeric value representing the configuration status (whether the context is dirty or not)
|
* 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
|
* mixed together (using bit shifting) with a index value which tells the starting index value
|
||||||
* of where the multi style entries begin.
|
* of where the multi style entries begin.
|
||||||
*/
|
*/
|
||||||
[3]: number;
|
[StylingIndex.MasterFlagPosition]: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* A numeric value representing the class index offset value. Whenever a single class is
|
* A numeric value representing the class index offset value. Whenever a single class is
|
||||||
* applied (using `elementClassProp`) it should have an styling index value that doesn't
|
* applied (using `elementClassProp`) it should have an styling index value that doesn't
|
||||||
* need to take into account any style values that exist in the context.
|
* need to take into account any style values that exist in the context.
|
||||||
*/
|
*/
|
||||||
[4]: number;
|
[StylingIndex.ClassOffsetPosition]: number;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The last CLASS STRING VALUE that was interpreted by elementStylingMap. This is cached
|
* The last CLASS STRING VALUE that was interpreted by elementStylingMap. This is cached
|
||||||
* So that the algorithm can exit early incase the string has not changed.
|
* So that the algorithm can exit early incase the string has not changed.
|
||||||
*/
|
*/
|
||||||
[5]: string|null;
|
[StylingIndex.CachedCssClassString]: string|null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -185,18 +191,20 @@ export const enum StylingFlags {
|
|||||||
export const enum StylingIndex {
|
export const enum StylingIndex {
|
||||||
// Position of where the initial styles are stored in the styling context
|
// Position of where the initial styles are stored in the styling context
|
||||||
ElementPosition = 0,
|
ElementPosition = 0,
|
||||||
// Position of where the style sanitizer is stored within the styling context
|
|
||||||
StyleSanitizerPosition = 1,
|
|
||||||
// Position of where the initial styles are stored in the styling context
|
// Position of where the initial styles are stored in the styling context
|
||||||
InitialStylesPosition = 2,
|
AnimationContext = 1,
|
||||||
|
// Position of where the style sanitizer is stored within the styling context
|
||||||
|
StyleSanitizerPosition = 2,
|
||||||
|
// Position of where the initial styles are stored in the styling context
|
||||||
|
InitialStylesPosition = 3,
|
||||||
// Index of location where the start of single properties are stored. (`updateStyleProp`)
|
// Index of location where the start of single properties are stored. (`updateStyleProp`)
|
||||||
MasterFlagPosition = 3,
|
MasterFlagPosition = 4,
|
||||||
// Index of location where the class index offset value is located
|
// Index of location where the class index offset value is located
|
||||||
ClassOffsetPosition = 4,
|
ClassOffsetPosition = 5,
|
||||||
// Position of where the last string-based CSS class value was stored
|
// Position of where the last string-based CSS class value was stored
|
||||||
CachedCssClassString = 5,
|
CachedCssClassString = 6,
|
||||||
// Location of single (prop) value entries are stored within the context
|
// Location of single (prop) value entries are stored within the context
|
||||||
SingleStylesStartPosition = 6,
|
SingleStylesStartPosition = 7,
|
||||||
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
|
// Multi and single entries are stored in `StylingContext` as: Flag; PropertyName; PropertyValue
|
||||||
FlagsOffset = 0,
|
FlagsOffset = 0,
|
||||||
PropertyOffset = 1,
|
PropertyOffset = 1,
|
||||||
@ -223,6 +231,12 @@ export function allocStylingContext(
|
|||||||
return context;
|
return context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function createEmptyStylingContext(
|
||||||
|
element?: LElementNode | null, sanitizer?: StyleSanitizeFn | null,
|
||||||
|
initialStylingValues?: InitialStyles): StylingContext {
|
||||||
|
return [element || null, null, sanitizer || null, initialStylingValues || [null], 0, 0, null];
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a styling context template where styling information is stored.
|
* Creates a styling context template where styling information is stored.
|
||||||
* Any styles that are later referenced using `updateStyleProp` must be
|
* Any styles that are later referenced using `updateStyleProp` must be
|
||||||
@ -250,7 +264,8 @@ export function createStylingContextTemplate(
|
|||||||
initialStyleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
initialStyleDeclarations?: (string | boolean | InitialStylingFlags)[] | null,
|
||||||
styleSanitizer?: StyleSanitizeFn | null): StylingContext {
|
styleSanitizer?: StyleSanitizeFn | null): StylingContext {
|
||||||
const initialStylingValues: InitialStyles = [null];
|
const initialStylingValues: InitialStyles = [null];
|
||||||
const context: StylingContext = [null, styleSanitizer || null, initialStylingValues, 0, 0, null];
|
const context: StylingContext =
|
||||||
|
createEmptyStylingContext(null, styleSanitizer, initialStylingValues);
|
||||||
|
|
||||||
// we use two maps since a class name might collide with a CSS style prop
|
// we use two maps since a class name might collide with a CSS style prop
|
||||||
const stylesLookup: {[key: string]: number} = {};
|
const stylesLookup: {[key: string]: number} = {};
|
||||||
|
@ -7,9 +7,13 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
import {devModeEqual} from '../change_detection/change_detection_util';
|
import {devModeEqual} from '../change_detection/change_detection_util';
|
||||||
import {assertLessThan} from './assert';
|
|
||||||
|
import {assertDefined, assertLessThan} from './assert';
|
||||||
|
import {readElementValue, readPatchedLViewData} from './context_discovery';
|
||||||
import {LContainerNode, LElementContainerNode, LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
import {LContainerNode, LElementContainerNode, LElementNode, TNode, TNodeFlags} from './interfaces/node';
|
||||||
import {HEADER_OFFSET, LViewData, TData} from './interfaces/view';
|
import {CONTEXT, FLAGS, HEADER_OFFSET, LViewData, LViewFlags, PARENT, RootContext, TData} from './interfaces/view';
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns whether the values are different from a change detection stand point.
|
* Returns whether the values are different from a change detection stand point.
|
||||||
@ -87,10 +91,6 @@ export function loadElementInternal(index: number, arr: LViewData): LElementNode
|
|||||||
return readElementValue(value);
|
return readElementValue(value);
|
||||||
}
|
}
|
||||||
|
|
||||||
export function readElementValue(value: LElementNode | any[]): LElementNode {
|
|
||||||
return (Array.isArray(value) ? (value as any as any[])[0] : value) as LElementNode;
|
|
||||||
}
|
|
||||||
|
|
||||||
export function getLNode(tNode: TNode, hostView: LViewData): LElementNode|LContainerNode|
|
export function getLNode(tNode: TNode, hostView: LViewData): LElementNode|LContainerNode|
|
||||||
LElementContainerNode {
|
LElementContainerNode {
|
||||||
return readElementValue(hostView[tNode.index]);
|
return readElementValue(hostView[tNode.index]);
|
||||||
@ -103,3 +103,21 @@ export function isContentQueryHost(tNode: TNode): boolean {
|
|||||||
export function isComponent(tNode: TNode): boolean {
|
export function isComponent(tNode: TNode): boolean {
|
||||||
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
|
return (tNode.flags & TNodeFlags.isComponent) === TNodeFlags.isComponent;
|
||||||
}
|
}
|
||||||
|
/**
|
||||||
|
* Retrieve the root view from any component by walking the parent `LViewData` until
|
||||||
|
* reaching the root `LViewData`.
|
||||||
|
*
|
||||||
|
* @param component any component
|
||||||
|
*/
|
||||||
|
export function getRootView(target: LViewData | {}): LViewData {
|
||||||
|
ngDevMode && assertDefined(target, 'component');
|
||||||
|
let lViewData = Array.isArray(target) ? (target as LViewData) : readPatchedLViewData(target) !;
|
||||||
|
while (lViewData && !(lViewData[FLAGS] & LViewFlags.IsRoot)) {
|
||||||
|
lViewData = lViewData[PARENT] !;
|
||||||
|
}
|
||||||
|
return lViewData;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getRootContext(viewOrComponent: LViewData | {}): RootContext {
|
||||||
|
return getRootView(viewOrComponent)[CONTEXT] as RootContext;
|
||||||
|
}
|
||||||
|
53
packages/core/test/bundling/animation_world/BUILD.bazel
Normal file
53
packages/core/test/bundling/animation_world/BUILD.bazel
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
package(default_visibility = ["//visibility:public"])
|
||||||
|
|
||||||
|
load("//tools:defaults.bzl", "jasmine_node_test", "ng_module", "ng_rollup_bundle", "ts_library")
|
||||||
|
load("//tools/symbol-extractor:index.bzl", "js_expected_symbol_test")
|
||||||
|
load("//tools/http-server:http_server.bzl", "http_server")
|
||||||
|
|
||||||
|
ng_module(
|
||||||
|
name = "animation_world",
|
||||||
|
srcs = ["index.ts"],
|
||||||
|
tags = ["ivy-only"],
|
||||||
|
deps = [
|
||||||
|
"//packages/common",
|
||||||
|
"//packages/core",
|
||||||
|
"//packages/core/test/bundling/util:reflect_metadata",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
ng_rollup_bundle(
|
||||||
|
name = "bundle",
|
||||||
|
# TODO(alexeagle): This is inconsistent.
|
||||||
|
# We try to teach users to always have their workspace at the start of a
|
||||||
|
# path, to disambiguate from other workspaces.
|
||||||
|
# Here, the rule implementation is looking in an execroot where the layout
|
||||||
|
# has an "external" directory for external dependencies.
|
||||||
|
# This should probably start with "angular/" and let the rule deal with it.
|
||||||
|
entry_point = "packages/core/test/bundling/animation_world/index.js",
|
||||||
|
tags = ["ivy-only"],
|
||||||
|
deps = [
|
||||||
|
":animation_world",
|
||||||
|
"//packages/core",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
js_expected_symbol_test(
|
||||||
|
name = "symbol_test",
|
||||||
|
src = ":bundle.min_debug.js",
|
||||||
|
golden = ":bundle.golden_symbols.json",
|
||||||
|
tags = [
|
||||||
|
"ivy-local",
|
||||||
|
"ivy-only",
|
||||||
|
],
|
||||||
|
)
|
||||||
|
|
||||||
|
http_server(
|
||||||
|
name = "devserver",
|
||||||
|
data = [
|
||||||
|
"animation_world.css",
|
||||||
|
"base.css",
|
||||||
|
"index.html",
|
||||||
|
":bundle.min.js",
|
||||||
|
":bundle.min_debug.js",
|
||||||
|
],
|
||||||
|
)
|
@ -0,0 +1,49 @@
|
|||||||
|
html, body {
|
||||||
|
padding:0;
|
||||||
|
margin:0;
|
||||||
|
font-family: Verdana;
|
||||||
|
}
|
||||||
|
|
||||||
|
animation-world {
|
||||||
|
display:block;
|
||||||
|
margin: 0 auto;
|
||||||
|
max-width:1000px;
|
||||||
|
border:2px solid black;
|
||||||
|
}
|
||||||
|
|
||||||
|
animation-world nav {
|
||||||
|
padding:1em;
|
||||||
|
border-bottom:2px solid black;
|
||||||
|
background:#eee;
|
||||||
|
}
|
||||||
|
|
||||||
|
animation-world .list {
|
||||||
|
display:grid;
|
||||||
|
grid-template-columns: 33% 33% 34%;
|
||||||
|
grid-template-rows: 33% 33% 34%;
|
||||||
|
height:1000px;
|
||||||
|
}
|
||||||
|
animation-world .record {
|
||||||
|
line-height:333px;
|
||||||
|
text-align:center;
|
||||||
|
font-weight: bold;
|
||||||
|
font-size:50px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.record-1 { color:white; background: green;}
|
||||||
|
.record-2 { color:white; background: red;}
|
||||||
|
.record-3 { color:white; background: blue;}
|
||||||
|
.record-4 { color:white; background: orange;}
|
||||||
|
.record-5 { color:white; background: purple;}
|
||||||
|
.record-6 { color:white; background: black;}
|
||||||
|
.record-7 { color:white; background: silver;}
|
||||||
|
.record-8 { color:white; background: teal;}
|
||||||
|
.record-9 { color:white; background: pink;}
|
||||||
|
|
||||||
|
@keyframes fadeInOut {
|
||||||
|
from { opacity: 0; background: white; }
|
||||||
|
33% { opacity: 1; background: black; }
|
||||||
|
66% { opacity: 0.5; background: black; }
|
||||||
|
100% { opacity: 1 }
|
||||||
|
}
|
141
packages/core/test/bundling/animation_world/base.css
Normal file
141
packages/core/test/bundling/animation_world/base.css
Normal file
@ -0,0 +1,141 @@
|
|||||||
|
hr {
|
||||||
|
margin: 20px 0;
|
||||||
|
border: 0;
|
||||||
|
border-top: 1px dashed #c5c5c5;
|
||||||
|
border-bottom: 1px dashed #f7f7f7;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn a {
|
||||||
|
font-weight: normal;
|
||||||
|
text-decoration: none;
|
||||||
|
color: #b83f45;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn a:hover {
|
||||||
|
text-decoration: underline;
|
||||||
|
color: #787e7e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h3,
|
||||||
|
.learn h4,
|
||||||
|
.learn h5 {
|
||||||
|
margin: 10px 0;
|
||||||
|
font-weight: 500;
|
||||||
|
line-height: 1.2;
|
||||||
|
color: #000;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h3 {
|
||||||
|
font-size: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h4 {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn h5 {
|
||||||
|
margin-bottom: 0;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn ul {
|
||||||
|
padding: 0;
|
||||||
|
margin: 0 0 30px 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn li {
|
||||||
|
line-height: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn p {
|
||||||
|
font-size: 15px;
|
||||||
|
font-weight: 300;
|
||||||
|
line-height: 1.3;
|
||||||
|
margin-top: 0;
|
||||||
|
margin-bottom: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
#issue-count {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote {
|
||||||
|
border: none;
|
||||||
|
margin: 20px 0 60px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote p {
|
||||||
|
font-style: italic;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote p:before {
|
||||||
|
content: '“';
|
||||||
|
font-size: 50px;
|
||||||
|
opacity: .15;
|
||||||
|
position: absolute;
|
||||||
|
top: -20px;
|
||||||
|
left: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote p:after {
|
||||||
|
content: '”';
|
||||||
|
font-size: 50px;
|
||||||
|
opacity: .15;
|
||||||
|
position: absolute;
|
||||||
|
bottom: -42px;
|
||||||
|
right: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote footer {
|
||||||
|
position: absolute;
|
||||||
|
bottom: -40px;
|
||||||
|
right: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote footer img {
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.quote footer a {
|
||||||
|
margin-left: 5px;
|
||||||
|
vertical-align: middle;
|
||||||
|
}
|
||||||
|
|
||||||
|
.speech-bubble {
|
||||||
|
position: relative;
|
||||||
|
padding: 10px;
|
||||||
|
background: rgba(0, 0, 0, .04);
|
||||||
|
border-radius: 5px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.speech-bubble:after {
|
||||||
|
content: '';
|
||||||
|
position: absolute;
|
||||||
|
top: 100%;
|
||||||
|
right: 30px;
|
||||||
|
border: 13px solid transparent;
|
||||||
|
border-top-color: rgba(0, 0, 0, .04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn-bar > .learn {
|
||||||
|
position: absolute;
|
||||||
|
width: 272px;
|
||||||
|
top: 8px;
|
||||||
|
left: -300px;
|
||||||
|
padding: 10px;
|
||||||
|
border-radius: 5px;
|
||||||
|
background-color: rgba(255, 255, 255, .6);
|
||||||
|
transition-property: left;
|
||||||
|
transition-duration: 500ms;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 899px) {
|
||||||
|
.learn-bar {
|
||||||
|
width: auto;
|
||||||
|
padding-left: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.learn-bar > .learn {
|
||||||
|
left: 8px;
|
||||||
|
}
|
||||||
|
}
|
File diff suppressed because it is too large
Load Diff
32
packages/core/test/bundling/animation_world/index.html
Normal file
32
packages/core/test/bundling/animation_world/index.html
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<head>
|
||||||
|
<link rel="stylesheet" href="base.css">
|
||||||
|
<link rel="stylesheet" href="animation_world.css">
|
||||||
|
<title>Angular Hello World Example</title>
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<!-- The Angular application will be bootstrapped into this element. -->
|
||||||
|
<animation-world></animation-world>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Script tag which bootstraps the application. Use `?debug` in URL to select
|
||||||
|
the debug version of the script.
|
||||||
|
|
||||||
|
There are two scripts sources: `bundle.min.js` and `bundle.min_debug.js` You can
|
||||||
|
switch between which bundle the browser loads to experiment with the application.
|
||||||
|
|
||||||
|
- `bundle.min.js`: Is what the site would serve to their users. It has gone
|
||||||
|
through rollup, build-optimizer, and uglify with tree shaking.
|
||||||
|
- `bundle.min_debug.js`: Is what the developer would like to see when debugging
|
||||||
|
the application. It has also done through full pipeline of rollup, build-optimizer,
|
||||||
|
and uglify, however special flags were passed to uglify to prevent inlining and
|
||||||
|
property renaming.
|
||||||
|
-->
|
||||||
|
<script>
|
||||||
|
document.write('<script src="' +
|
||||||
|
(document.location.search.endsWith('debug') ? '/bundle.min_debug.js' : '/bundle.min.js') +
|
||||||
|
'"></' + 'script>');
|
||||||
|
</script>
|
||||||
|
</body>
|
||||||
|
</html>
|
136
packages/core/test/bundling/animation_world/index.ts
Normal file
136
packages/core/test/bundling/animation_world/index.ts
Normal file
@ -0,0 +1,136 @@
|
|||||||
|
/**
|
||||||
|
* @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 '@angular/core/test/bundling/util/src/reflect_metadata';
|
||||||
|
|
||||||
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {Component, ElementRef, NgModule, ɵPlayState as PlayState, ɵPlayer as Player, ɵPlayerHandler as PlayerHandler, ɵaddPlayer as addPlayer, ɵrenderComponent as renderComponent} from '@angular/core';
|
||||||
|
|
||||||
|
@Component({
|
||||||
|
selector: 'animation-world',
|
||||||
|
template: `
|
||||||
|
<nav>
|
||||||
|
<button (click)="doAnimate()">Populate List</button>
|
||||||
|
</nav>
|
||||||
|
<div class="list">
|
||||||
|
<div *ngFor="let item of items" class="record" [class]="makeClass(item)">
|
||||||
|
{{ item }}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
`,
|
||||||
|
})
|
||||||
|
class AnimationWorldComponent {
|
||||||
|
items: any[] = [1, 2, 3, 4, 5, 6, 7, 8, 9];
|
||||||
|
private _hostElement: HTMLElement;
|
||||||
|
|
||||||
|
constructor(element: ElementRef) { this._hostElement = element.nativeElement; }
|
||||||
|
|
||||||
|
makeClass(index: number) { return `record-${index}`; }
|
||||||
|
|
||||||
|
doAnimate() {
|
||||||
|
const elements = this._hostElement.querySelectorAll('div.record') as any as HTMLElement[];
|
||||||
|
for (let i = 0; i < elements.length; i++) {
|
||||||
|
const element = elements[i];
|
||||||
|
const delay = i * 100;
|
||||||
|
const player = buildAnimationPlayer(element, 'fadeInOut', `500ms ease-out ${delay}ms both`);
|
||||||
|
addPlayer(element, player);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@NgModule({declarations: [AnimationWorldComponent], imports: [CommonModule]})
|
||||||
|
class AnimationWorldModule {
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function buildAnimationPlayer(element: HTMLElement, animationName: string, time: string): Player {
|
||||||
|
return new SimpleKeyframePlayer(element, animationName, time);
|
||||||
|
}
|
||||||
|
|
||||||
|
class SimpleKeyframePlayer implements Player {
|
||||||
|
state = PlayState.Pending;
|
||||||
|
parent: Player|null = null;
|
||||||
|
private _animationStyle: string;
|
||||||
|
private _listeners: {[stateName: string]: (() => any)[]} = {};
|
||||||
|
constructor(private _element: HTMLElement, private _animationName: string, time: string) {
|
||||||
|
this._animationStyle = `${time} ${_animationName}`;
|
||||||
|
}
|
||||||
|
private _start() {
|
||||||
|
this._element.style.animation = this._animationStyle;
|
||||||
|
const animationFn = (event: AnimationEvent) => {
|
||||||
|
if (event.animationName == this._animationName) {
|
||||||
|
this._element.removeEventListener('animationend', animationFn);
|
||||||
|
this.finish();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
this._element.addEventListener('animationend', animationFn);
|
||||||
|
}
|
||||||
|
addEventListener(state: PlayState|string, cb: () => any): void {
|
||||||
|
const key = state.toString();
|
||||||
|
const arr = this._listeners[key] = (this._listeners[key] || []);
|
||||||
|
arr.push(cb);
|
||||||
|
}
|
||||||
|
play(): void {
|
||||||
|
if (this.state <= PlayState.Pending) {
|
||||||
|
this._start();
|
||||||
|
}
|
||||||
|
if (this.state != PlayState.Running) {
|
||||||
|
setAnimationPlayState(this._element, 'running');
|
||||||
|
this.state = PlayState.Running;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pause(): void {
|
||||||
|
if (this.state != PlayState.Paused) {
|
||||||
|
setAnimationPlayState(this._element, 'paused');
|
||||||
|
this.state = PlayState.Paused;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finish(): void {
|
||||||
|
if (this.state < PlayState.Finished) {
|
||||||
|
this._element.style.animation = '';
|
||||||
|
this.state = PlayState.Finished;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
destroy(): void {
|
||||||
|
if (this.state < PlayState.Destroyed) {
|
||||||
|
this.finish();
|
||||||
|
this.state = PlayState.Destroyed;
|
||||||
|
this._emit(this.state);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
capture(): any {}
|
||||||
|
private _emit(state: PlayState) {
|
||||||
|
const arr = this._listeners[state.toString()] || [];
|
||||||
|
arr.forEach(cb => cb());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function setAnimationPlayState(element: HTMLElement, state: string) {
|
||||||
|
element.style.animationPlayState = state;
|
||||||
|
}
|
||||||
|
|
||||||
|
class AnimationDebugger implements PlayerHandler {
|
||||||
|
private _players: Player[] = [];
|
||||||
|
|
||||||
|
flushPlayers() {
|
||||||
|
this._players.forEach(player => {
|
||||||
|
if (!player.parent) {
|
||||||
|
player.play();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this._players.length = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
queuePlayer(player: Player): void { this._players.push(player); }
|
||||||
|
}
|
||||||
|
|
||||||
|
const playerHandler = new AnimationDebugger();
|
||||||
|
renderComponent(AnimationWorldComponent, {playerHandler});
|
@ -416,6 +416,9 @@
|
|||||||
{
|
{
|
||||||
"name": "createEmbeddedViewAndNode"
|
"name": "createEmbeddedViewAndNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "createEmptyStylingContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "createLContainer"
|
"name": "createLContainer"
|
||||||
},
|
},
|
||||||
|
@ -1286,6 +1286,9 @@
|
|||||||
{
|
{
|
||||||
"name": "createEmbeddedViewAndNode"
|
"name": "createEmbeddedViewAndNode"
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "createEmptyStylingContext"
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "createInjector"
|
"name": "createInjector"
|
||||||
},
|
},
|
||||||
|
@ -0,0 +1,60 @@
|
|||||||
|
/**
|
||||||
|
* @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 {CorePlayerHandler} from '../../../src/render3/animations/core_player_handler';
|
||||||
|
import {PlayState} from '../../../src/render3/animations/interfaces';
|
||||||
|
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);
|
||||||
|
});
|
||||||
|
});
|
58
packages/core/test/render3/animations/mock_player.ts
Normal file
58
packages/core/test/render3/animations/mock_player.ts
Normal file
@ -0,0 +1,58 @@
|
|||||||
|
/**
|
||||||
|
* @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/animations/interfaces';
|
||||||
|
|
||||||
|
export class MockPlayer implements Player {
|
||||||
|
parent: Player|null = null;
|
||||||
|
|
||||||
|
log: string[] = [];
|
||||||
|
state: PlayState = PlayState.Pending;
|
||||||
|
private _listeners: {[state: string]: (() => 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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
311
packages/core/test/render3/animations/players_spec.ts
Normal file
311
packages/core/test/render3/animations/players_spec.ts
Normal file
@ -0,0 +1,311 @@
|
|||||||
|
/**
|
||||||
|
* @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 {RenderFlags} from '@angular/core/src/render3';
|
||||||
|
import {AnimationContext, PlayState, Player, PlayerHandler} from '../../../src/render3/animations/interfaces';
|
||||||
|
import {addPlayer, getOrCreateAnimationContext, getPlayers} from '../../../src/render3/animations/players';
|
||||||
|
import {QUERY_READ_FROM_NODE, defineComponent, getHostElement} from '../../../src/render3/index';
|
||||||
|
import {element, elementEnd, elementStart, elementStyling, elementStylingApply, load, markDirty} from '../../../src/render3/instructions';
|
||||||
|
import {RElement} from '../../../src/render3/interfaces/renderer';
|
||||||
|
import {QueryList, query, queryRefresh} from '../../../src/render3/query';
|
||||||
|
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 = getOrCreateAnimationContext(element);
|
||||||
|
expect(context).toEqual([]);
|
||||||
|
|
||||||
|
const player = new MockPlayer();
|
||||||
|
addPlayer(element, player);
|
||||||
|
expect(readPlayers(context)).toEqual([player]);
|
||||||
|
|
||||||
|
player.destroy();
|
||||||
|
expect(readPlayers(context)).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;
|
||||||
|
}
|
||||||
|
|
||||||
|
function readPlayers(context: AnimationContext): Player[] {
|
||||||
|
return context;
|
||||||
|
}
|
||||||
|
|
||||||
|
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');
|
||||||
|
elementStyling(['fooClass']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
elementStylingApply(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
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(1, 'div');
|
||||||
|
element(2, 'child-comp', ['child', ''], ['child', 'child']);
|
||||||
|
elementEnd();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
viewQuery: function(rf: RenderFlags, ctx: SuperComp) {
|
||||||
|
if (rf & RenderFlags.Create) {
|
||||||
|
query(0, ['child'], true, QUERY_READ_FROM_NODE);
|
||||||
|
}
|
||||||
|
if (rf & RenderFlags.Update) {
|
||||||
|
let tmp: any;
|
||||||
|
queryRefresh(tmp = load<QueryList<any>>(0)) && (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); }
|
||||||
|
}
|
@ -9,6 +9,7 @@
|
|||||||
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
|
import {stringifyElement} from '@angular/platform-browser/testing/src/browser_util';
|
||||||
|
|
||||||
import {Injector} from '../../src/di/injector';
|
import {Injector} from '../../src/di/injector';
|
||||||
|
import {PlayerHandler} from '../../src/render3/animations/interfaces';
|
||||||
import {CreateComponentOptions} from '../../src/render3/component';
|
import {CreateComponentOptions} from '../../src/render3/component';
|
||||||
import {getContext, isComponentInstance} from '../../src/render3/context_discovery';
|
import {getContext, isComponentInstance} from '../../src/render3/context_discovery';
|
||||||
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
|
import {extractDirectiveDef, extractPipeDef} from '../../src/render3/definition';
|
||||||
@ -103,9 +104,12 @@ export class ComponentFixture<T> extends BaseFixture {
|
|||||||
component: T;
|
component: T;
|
||||||
requestAnimationFrame: {(fn: () => void): void; flush(): void; queue: (() => void)[];};
|
requestAnimationFrame: {(fn: () => void): void; flush(): void; queue: (() => void)[];};
|
||||||
|
|
||||||
constructor(
|
constructor(private componentType: ComponentType<T>, opts: {
|
||||||
private componentType: ComponentType<T>,
|
injector?: Injector,
|
||||||
opts: {injector?: Injector, sanitizer?: Sanitizer, rendererFactory?: RendererFactory3} = {}) {
|
sanitizer?: Sanitizer,
|
||||||
|
rendererFactory?: RendererFactory3,
|
||||||
|
playerHandler?: PlayerHandler
|
||||||
|
} = {}) {
|
||||||
super();
|
super();
|
||||||
this.requestAnimationFrame = function(fn: () => void) {
|
this.requestAnimationFrame = function(fn: () => void) {
|
||||||
requestAnimationFrame.queue.push(fn);
|
requestAnimationFrame.queue.push(fn);
|
||||||
@ -122,7 +126,8 @@ export class ComponentFixture<T> extends BaseFixture {
|
|||||||
scheduler: this.requestAnimationFrame,
|
scheduler: this.requestAnimationFrame,
|
||||||
injector: opts.injector,
|
injector: opts.injector,
|
||||||
sanitizer: opts.sanitizer,
|
sanitizer: opts.sanitizer,
|
||||||
rendererFactory: opts.rendererFactory || domRendererFactory3
|
rendererFactory: opts.rendererFactory || domRendererFactory3,
|
||||||
|
playerHandler: opts.playerHandler
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -109,7 +109,7 @@ describe('styling', () => {
|
|||||||
describe('createStylingContextTemplate', () => {
|
describe('createStylingContextTemplate', () => {
|
||||||
it('should initialize empty template', () => {
|
it('should initialize empty template', () => {
|
||||||
const template = initContext();
|
const template = initContext();
|
||||||
expect(template).toEqual([element, null, [null], cleanStyle(0, 6), 0, null]);
|
expect(template).toEqual([element, null, null, [null], cleanStyle(0, 7), 0, null]);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should initialize static styles', () => {
|
it('should initialize static styles', () => {
|
||||||
@ -118,28 +118,29 @@ describe('styling', () => {
|
|||||||
expect(template).toEqual([
|
expect(template).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null, 'red', '10px'],
|
[null, 'red', '10px'],
|
||||||
dirtyStyle(0, 12), //
|
dirtyStyle(0, 13), //
|
||||||
0,
|
0,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(1, 12),
|
cleanStyle(1, 13),
|
||||||
'color',
|
'color',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(2, 15),
|
cleanStyle(2, 16),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyStyle(1, 6),
|
dirtyStyle(1, 7),
|
||||||
'color',
|
'color',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
dirtyStyle(2, 9),
|
dirtyStyle(2, 10),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -320,28 +321,29 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 12), //
|
dirtyStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(0, 12),
|
cleanStyle(0, 13),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 15),
|
cleanStyle(0, 16),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyStyle(0, 6),
|
dirtyStyle(0, 7),
|
||||||
'width',
|
'width',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
dirtyStyle(0, 9),
|
dirtyStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
'100px',
|
'100px',
|
||||||
]);
|
]);
|
||||||
@ -352,33 +354,34 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 12), //
|
dirtyStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(0, 12),
|
cleanStyle(0, 13),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 18),
|
cleanStyle(0, 19),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyStyle(0, 6),
|
dirtyStyle(0, 7),
|
||||||
'width',
|
'width',
|
||||||
'200px',
|
'200px',
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
'0',
|
'0',
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
dirtyStyle(0, 9),
|
dirtyStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -387,33 +390,34 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
cleanStyle(0, 12), //
|
cleanStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(0, 12),
|
cleanStyle(0, 13),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 18),
|
cleanStyle(0, 19),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'width',
|
'width',
|
||||||
'200px',
|
'200px',
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
'0',
|
'0',
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -424,33 +428,34 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 12), //
|
dirtyStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyle(0, 12),
|
dirtyStyle(0, 13),
|
||||||
'width',
|
'width',
|
||||||
'300px',
|
'300px',
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 18),
|
cleanStyle(0, 19),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -461,33 +466,34 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 12), //
|
dirtyStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyle(0, 12),
|
dirtyStyle(0, 13),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 18),
|
cleanStyle(0, 19),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -503,33 +509,34 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 9), //
|
dirtyStyle(0, 10), //
|
||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(0, 18),
|
cleanStyle(0, 19),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'width',
|
'width',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'height',
|
'height',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
'0.5',
|
'0.5',
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -540,33 +547,34 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 9), //
|
dirtyStyle(0, 10), //
|
||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(0, 18),
|
cleanStyle(0, 19),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -579,38 +587,39 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 9), //
|
dirtyStyle(0, 10), //
|
||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(0, 21),
|
cleanStyle(0, 22),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'borderWidth',
|
'borderWidth',
|
||||||
'5px',
|
'5px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #21
|
// #22
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -620,38 +629,39 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 9), //
|
dirtyStyle(0, 10), //
|
||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyle(0, 21),
|
dirtyStyle(0, 22),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
'200px',
|
'200px',
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'borderWidth',
|
'borderWidth',
|
||||||
'5px',
|
'5px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #21
|
// #22
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -661,43 +671,44 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 9), //
|
dirtyStyle(0, 10), //
|
||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyle(0, 24),
|
dirtyStyle(0, 25),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
'200px',
|
'200px',
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'borderWidth',
|
'borderWidth',
|
||||||
'15px',
|
'15px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'borderColor',
|
'borderColor',
|
||||||
'red',
|
'red',
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #21
|
// #22
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'opacity',
|
'opacity',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #24
|
// #25
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'lineHeight',
|
'lineHeight',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -716,23 +727,24 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 9), //
|
dirtyStyle(0, 10), //
|
||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyle(0, 12),
|
dirtyStyle(0, 13),
|
||||||
'height',
|
'height',
|
||||||
'200px',
|
'200px',
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyle(),
|
dirtyStyle(),
|
||||||
'width',
|
'width',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -742,23 +754,24 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null],
|
[null],
|
||||||
cleanStyle(0, 9), //
|
cleanStyle(0, 10), //
|
||||||
1,
|
1,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(0, 12),
|
cleanStyle(0, 13),
|
||||||
'height',
|
'height',
|
||||||
'200px',
|
'200px',
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(),
|
cleanStyle(),
|
||||||
'width',
|
'width',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyle(0, 6),
|
cleanStyle(0, 7),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -776,29 +789,30 @@ describe('styling', () => {
|
|||||||
|
|
||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
|
null,
|
||||||
styleSanitizer,
|
styleSanitizer,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 12), //
|
dirtyStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyleWithSanitization(0, 12),
|
dirtyStyleWithSanitization(0, 13),
|
||||||
'border-image',
|
'border-image',
|
||||||
'url(foo.jpg)',
|
'url(foo.jpg)',
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
dirtyStyle(0, 15),
|
dirtyStyle(0, 16),
|
||||||
'border-width',
|
'border-width',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyleWithSanitization(0, 6),
|
cleanStyleWithSanitization(0, 7),
|
||||||
'border-image',
|
'border-image',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'border-width',
|
'border-width',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -807,34 +821,35 @@ describe('styling', () => {
|
|||||||
|
|
||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
|
null,
|
||||||
styleSanitizer,
|
styleSanitizer,
|
||||||
[null],
|
[null],
|
||||||
dirtyStyle(0, 12), //
|
dirtyStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyleWithSanitization(0, 15),
|
dirtyStyleWithSanitization(0, 16),
|
||||||
'border-image',
|
'border-image',
|
||||||
'url(foo.jpg)',
|
'url(foo.jpg)',
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
dirtyStyle(0, 18),
|
dirtyStyle(0, 19),
|
||||||
'border-width',
|
'border-width',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyStyleWithSanitization(0, 0),
|
dirtyStyleWithSanitization(0, 0),
|
||||||
'background-image',
|
'background-image',
|
||||||
'unsafe',
|
'unsafe',
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyleWithSanitization(0, 6),
|
cleanStyleWithSanitization(0, 7),
|
||||||
'border-image',
|
'border-image',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'border-width',
|
'border-width',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -843,34 +858,35 @@ describe('styling', () => {
|
|||||||
|
|
||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
|
null,
|
||||||
styleSanitizer,
|
styleSanitizer,
|
||||||
[null],
|
[null],
|
||||||
cleanStyle(0, 12), //
|
cleanStyle(0, 13), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyleWithSanitization(0, 15),
|
cleanStyleWithSanitization(0, 16),
|
||||||
'border-image',
|
'border-image',
|
||||||
'url(foo.jpg)',
|
'url(foo.jpg)',
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 18),
|
cleanStyle(0, 19),
|
||||||
'border-width',
|
'border-width',
|
||||||
'100px',
|
'100px',
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanStyleWithSanitization(0, 0),
|
cleanStyleWithSanitization(0, 0),
|
||||||
'background-image',
|
'background-image',
|
||||||
'unsafe',
|
'unsafe',
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanStyleWithSanitization(0, 6),
|
cleanStyleWithSanitization(0, 7),
|
||||||
'border-image',
|
'border-image',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'border-width',
|
'border-width',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -883,20 +899,20 @@ describe('styling', () => {
|
|||||||
const template =
|
const template =
|
||||||
initContext(null, [InitialStylingFlags.VALUES_MODE, 'one', true, 'two', true]);
|
initContext(null, [InitialStylingFlags.VALUES_MODE, 'one', true, 'two', true]);
|
||||||
expect(template).toEqual([
|
expect(template).toEqual([
|
||||||
element, null, [null, true, true], dirtyStyle(0, 12), //
|
element, null, null, [null, true, true], dirtyStyle(0, 13), //
|
||||||
0, null,
|
0, null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanClass(1, 12), 'one', null,
|
cleanClass(1, 13), 'one', null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanClass(2, 15), 'two', null,
|
cleanClass(2, 16), 'two', null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
dirtyClass(1, 6), 'one', null,
|
dirtyClass(1, 7), 'one', null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
dirtyClass(2, 9), 'two', null
|
dirtyClass(2, 10), 'two', null
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -948,48 +964,49 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null, '100px', true],
|
[null, '100px', true],
|
||||||
dirtyStyle(0, 18), //
|
dirtyStyle(0, 19), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(1, 18),
|
cleanStyle(1, 19),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 21),
|
cleanStyle(0, 22),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanClass(2, 24),
|
cleanClass(2, 25),
|
||||||
'wide',
|
'wide',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanClass(0, 27),
|
cleanClass(0, 28),
|
||||||
'tall',
|
'tall',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
dirtyStyle(1, 6),
|
dirtyStyle(1, 7),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #21
|
// #22
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #24
|
// #25
|
||||||
dirtyClass(2, 12),
|
dirtyClass(2, 13),
|
||||||
'wide',
|
'wide',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #27
|
// #28
|
||||||
cleanClass(0, 15),
|
cleanClass(0, 16),
|
||||||
'tall',
|
'tall',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -1000,58 +1017,59 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null, '100px', true],
|
[null, '100px', true],
|
||||||
dirtyStyle(0, 18), //
|
dirtyStyle(0, 19), //
|
||||||
2,
|
2,
|
||||||
'tall round',
|
'tall round',
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
cleanStyle(1, 18),
|
cleanStyle(1, 19),
|
||||||
'width',
|
'width',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 33),
|
cleanStyle(0, 34),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanClass(2, 30),
|
cleanClass(2, 31),
|
||||||
'wide',
|
'wide',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanClass(0, 24),
|
cleanClass(0, 25),
|
||||||
'tall',
|
'tall',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
dirtyStyle(1, 6),
|
dirtyStyle(1, 7),
|
||||||
'width',
|
'width',
|
||||||
'200px',
|
'200px',
|
||||||
|
|
||||||
// #21
|
// #22
|
||||||
dirtyStyle(0, 0),
|
dirtyStyle(0, 0),
|
||||||
'opacity',
|
'opacity',
|
||||||
'0.5',
|
'0.5',
|
||||||
|
|
||||||
// #24
|
// #25
|
||||||
dirtyClass(0, 15),
|
dirtyClass(0, 16),
|
||||||
'tall',
|
'tall',
|
||||||
true,
|
true,
|
||||||
|
|
||||||
// #27
|
// #28
|
||||||
dirtyClass(0, 0),
|
dirtyClass(0, 0),
|
||||||
'round',
|
'round',
|
||||||
true,
|
true,
|
||||||
|
|
||||||
// #30
|
// #31
|
||||||
cleanClass(2, 12),
|
cleanClass(2, 13),
|
||||||
'wide',
|
'wide',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #33
|
// #34
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
@ -1066,58 +1084,59 @@ describe('styling', () => {
|
|||||||
expect(stylingContext).toEqual([
|
expect(stylingContext).toEqual([
|
||||||
element,
|
element,
|
||||||
null,
|
null,
|
||||||
|
null,
|
||||||
[null, '100px', true],
|
[null, '100px', true],
|
||||||
dirtyStyle(0, 18), //
|
dirtyStyle(0, 19), //
|
||||||
2,
|
2,
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #6
|
// #7
|
||||||
dirtyStyle(1, 18),
|
dirtyStyle(1, 19),
|
||||||
'width',
|
'width',
|
||||||
'300px',
|
'300px',
|
||||||
|
|
||||||
// #9
|
// #10
|
||||||
cleanStyle(0, 33),
|
cleanStyle(0, 34),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #12
|
// #13
|
||||||
cleanClass(2, 24),
|
cleanClass(2, 25),
|
||||||
'wide',
|
'wide',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #15
|
// #16
|
||||||
cleanClass(0, 21),
|
cleanClass(0, 22),
|
||||||
'tall',
|
'tall',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #18
|
// #19
|
||||||
cleanStyle(1, 6),
|
cleanStyle(1, 7),
|
||||||
'width',
|
'width',
|
||||||
'500px',
|
'500px',
|
||||||
|
|
||||||
// #21
|
// #22
|
||||||
cleanClass(0, 15),
|
cleanClass(0, 16),
|
||||||
'tall',
|
'tall',
|
||||||
true,
|
true,
|
||||||
|
|
||||||
// #24
|
// #25
|
||||||
cleanClass(2, 12),
|
cleanClass(2, 13),
|
||||||
'wide',
|
'wide',
|
||||||
true,
|
true,
|
||||||
|
|
||||||
// #27
|
// #28
|
||||||
dirtyClass(0, 0),
|
dirtyClass(0, 0),
|
||||||
'round',
|
'round',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #30
|
// #31
|
||||||
dirtyStyle(0, 0),
|
dirtyStyle(0, 0),
|
||||||
'opacity',
|
'opacity',
|
||||||
null,
|
null,
|
||||||
|
|
||||||
// #33
|
// #34
|
||||||
cleanStyle(0, 9),
|
cleanStyle(0, 10),
|
||||||
'height',
|
'height',
|
||||||
null,
|
null,
|
||||||
]);
|
]);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user