angular/packages/animations/browser/src/dsl/animation_transition_factory.ts
Joey Perrott d1ea1f4c7f build: update license headers to reference Google LLC (#37205)
Update the license headers throughout the repository to reference Google LLC
rather than Google Inc, for the required license headers.

PR Close #37205
2020-05-26 14:26:58 -04:00

128 lines
5.1 KiB
TypeScript

/**
* @license
* Copyright Google LLC 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 {AnimationOptions, ɵStyleData} from '@angular/animations';
import {AnimationDriver} from '../render/animation_driver';
import {getOrSetAsInMap} from '../render/shared';
import {copyObj, interpolateParams, iteratorToArray, mergeAnimationOptions} from '../util';
import {StyleAst, TransitionAst} from './animation_ast';
import {buildAnimationTimelines} from './animation_timeline_builder';
import {TransitionMatcherFn} from './animation_transition_expr';
import {AnimationTransitionInstruction, createTransitionInstruction} from './animation_transition_instruction';
import {ElementInstructionMap} from './element_instruction_map';
const EMPTY_OBJECT = {};
export class AnimationTransitionFactory {
constructor(
private _triggerName: string, public ast: TransitionAst,
private _stateStyles: {[stateName: string]: AnimationStateStyles}) {}
match(currentState: any, nextState: any, element: any, params: {[key: string]: any}): boolean {
return oneOrMoreTransitionsMatch(this.ast.matchers, currentState, nextState, element, params);
}
buildStyles(stateName: string, params: {[key: string]: any}, errors: any[]) {
const backupStateStyler = this._stateStyles['*'];
const stateStyler = this._stateStyles[stateName];
const backupStyles = backupStateStyler ? backupStateStyler.buildStyles(params, errors) : {};
return stateStyler ? stateStyler.buildStyles(params, errors) : backupStyles;
}
build(
driver: AnimationDriver, element: any, currentState: any, nextState: any,
enterClassName: string, leaveClassName: string, currentOptions?: AnimationOptions,
nextOptions?: AnimationOptions, subInstructions?: ElementInstructionMap,
skipAstBuild?: boolean): AnimationTransitionInstruction {
const errors: any[] = [];
const transitionAnimationParams = this.ast.options && this.ast.options.params || EMPTY_OBJECT;
const currentAnimationParams = currentOptions && currentOptions.params || EMPTY_OBJECT;
const currentStateStyles = this.buildStyles(currentState, currentAnimationParams, errors);
const nextAnimationParams = nextOptions && nextOptions.params || EMPTY_OBJECT;
const nextStateStyles = this.buildStyles(nextState, nextAnimationParams, errors);
const queriedElements = new Set<any>();
const preStyleMap = new Map<any, {[prop: string]: boolean}>();
const postStyleMap = new Map<any, {[prop: string]: boolean}>();
const isRemoval = nextState === 'void';
const animationOptions = {params: {...transitionAnimationParams, ...nextAnimationParams}};
const timelines = skipAstBuild ?
[] :
buildAnimationTimelines(
driver, element, this.ast.animation, enterClassName, leaveClassName, currentStateStyles,
nextStateStyles, animationOptions, subInstructions, errors);
let totalTime = 0;
timelines.forEach(tl => {
totalTime = Math.max(tl.duration + tl.delay, totalTime);
});
if (errors.length) {
return createTransitionInstruction(
element, this._triggerName, currentState, nextState, isRemoval, currentStateStyles,
nextStateStyles, [], [], preStyleMap, postStyleMap, totalTime, errors);
}
timelines.forEach(tl => {
const elm = tl.element;
const preProps = getOrSetAsInMap(preStyleMap, elm, {});
tl.preStyleProps.forEach(prop => preProps[prop] = true);
const postProps = getOrSetAsInMap(postStyleMap, elm, {});
tl.postStyleProps.forEach(prop => postProps[prop] = true);
if (elm !== element) {
queriedElements.add(elm);
}
});
const queriedElementsList = iteratorToArray(queriedElements.values());
return createTransitionInstruction(
element, this._triggerName, currentState, nextState, isRemoval, currentStateStyles,
nextStateStyles, timelines, queriedElementsList, preStyleMap, postStyleMap, totalTime);
}
}
function oneOrMoreTransitionsMatch(
matchFns: TransitionMatcherFn[], currentState: any, nextState: any, element: any,
params: {[key: string]: any}): boolean {
return matchFns.some(fn => fn(currentState, nextState, element, params));
}
export class AnimationStateStyles {
constructor(private styles: StyleAst, private defaultParams: {[key: string]: any}) {}
buildStyles(params: {[key: string]: any}, errors: string[]): ɵStyleData {
const finalStyles: ɵStyleData = {};
const combinedParams = copyObj(this.defaultParams);
Object.keys(params).forEach(key => {
const value = params[key];
if (value != null) {
combinedParams[key] = value;
}
});
this.styles.styles.forEach(value => {
if (typeof value !== 'string') {
const styleObj = value as any;
Object.keys(styleObj).forEach(prop => {
let val = styleObj[prop];
if (val.length > 1) {
val = interpolateParams(val, combinedParams, errors);
}
finalStyles[prop] = val;
});
}
});
return finalStyles;
}
}