fix(animations): always normalize style properties and values during compilation (#12755)
Closes #11582 Closes #12481 Closes #12755
This commit is contained in:
parent
3dc61779f0
commit
a0e9fde653
|
@ -138,7 +138,8 @@ export class CodeGenerator {
|
||||||
new compiler.DirectiveWrapperCompiler(
|
new compiler.DirectiveWrapperCompiler(
|
||||||
config, expressionParser, elementSchemaRegistry, console),
|
config, expressionParser, elementSchemaRegistry, console),
|
||||||
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
new compiler.NgModuleCompiler(), new compiler.TypeScriptEmitter(reflectorHost),
|
||||||
cliOptions.locale, cliOptions.i18nFormat);
|
cliOptions.locale, cliOptions.i18nFormat,
|
||||||
|
new compiler.AnimationParser(elementSchemaRegistry));
|
||||||
|
|
||||||
return new CodeGenerator(
|
return new CodeGenerator(
|
||||||
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
options, program, compilerHost, staticReflector, offlineCompiler, reflectorHost);
|
||||||
|
|
|
@ -52,5 +52,6 @@ export * from './src/selector';
|
||||||
export * from './src/style_compiler';
|
export * from './src/style_compiler';
|
||||||
export * from './src/template_parser/template_parser';
|
export * from './src/template_parser/template_parser';
|
||||||
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
export {ViewCompiler} from './src/view_compiler/view_compiler';
|
||||||
|
export {AnimationParser} from './src/animation/animation_parser';
|
||||||
|
|
||||||
// This file only reexports content of the `src` folder. Keep it that way.
|
// This file only reexports content of the `src` folder. Keep it that way.
|
||||||
|
|
|
@ -6,11 +6,14 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import {Injectable} from '@angular/core';
|
||||||
|
|
||||||
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
import {CompileAnimationAnimateMetadata, CompileAnimationEntryMetadata, CompileAnimationGroupMetadata, CompileAnimationKeyframesSequenceMetadata, CompileAnimationMetadata, CompileAnimationSequenceMetadata, CompileAnimationStateDeclarationMetadata, CompileAnimationStateTransitionMetadata, CompileAnimationStyleMetadata, CompileAnimationWithStepsMetadata, CompileDirectiveMetadata} from '../compile_metadata';
|
||||||
import {StringMapWrapper} from '../facade/collection';
|
import {StringMapWrapper} from '../facade/collection';
|
||||||
import {isBlank, isPresent} from '../facade/lang';
|
import {isBlank, isPresent} from '../facade/lang';
|
||||||
import {ParseError} from '../parse_util';
|
import {ParseError} from '../parse_util';
|
||||||
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
import {ANY_STATE, FILL_STYLE_FLAG} from '../private_import_core';
|
||||||
|
import {ElementSchemaRegistry} from '../schema/element_schema_registry';
|
||||||
|
|
||||||
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
import {AnimationAst, AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStateDeclarationAst, AnimationStateTransitionAst, AnimationStateTransitionExpression, AnimationStepAst, AnimationStylesAst, AnimationWithStepsAst} from './animation_ast';
|
||||||
import {StylesCollection} from './styles_collection';
|
import {StylesCollection} from './styles_collection';
|
||||||
|
@ -32,7 +35,10 @@ export class AnimationEntryParseResult {
|
||||||
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
constructor(public ast: AnimationEntryAst, public errors: AnimationParseError[]) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
export class AnimationParser {
|
export class AnimationParser {
|
||||||
|
constructor(private _schema: ElementSchemaRegistry) {}
|
||||||
|
|
||||||
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
parseComponent(component: CompileDirectiveMetadata): AnimationEntryAst[] {
|
||||||
const errors: string[] = [];
|
const errors: string[] = [];
|
||||||
const componentName = component.type.name;
|
const componentName = component.type.name;
|
||||||
|
@ -73,7 +79,7 @@ export class AnimationParser {
|
||||||
var stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
|
var stateDeclarationAsts: AnimationStateDeclarationAst[] = [];
|
||||||
entry.definitions.forEach(def => {
|
entry.definitions.forEach(def => {
|
||||||
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
if (def instanceof CompileAnimationStateDeclarationMetadata) {
|
||||||
_parseAnimationDeclarationStates(def, errors).forEach(ast => {
|
_parseAnimationDeclarationStates(def, this._schema, errors).forEach(ast => {
|
||||||
stateDeclarationAsts.push(ast);
|
stateDeclarationAsts.push(ast);
|
||||||
stateStyles[ast.stateName] = ast.styles;
|
stateStyles[ast.stateName] = ast.styles;
|
||||||
});
|
});
|
||||||
|
@ -82,8 +88,8 @@ export class AnimationParser {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
var stateTransitionAsts =
|
var stateTransitionAsts = transitions.map(
|
||||||
transitions.map(transDef => _parseAnimationStateTransition(transDef, stateStyles, errors));
|
transDef => _parseAnimationStateTransition(transDef, stateStyles, this._schema, errors));
|
||||||
|
|
||||||
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
var ast = new AnimationEntryAst(entry.name, stateDeclarationAsts, stateTransitionAsts);
|
||||||
return new AnimationEntryParseResult(ast, errors);
|
return new AnimationEntryParseResult(ast, errors);
|
||||||
|
@ -91,27 +97,17 @@ export class AnimationParser {
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseAnimationDeclarationStates(
|
function _parseAnimationDeclarationStates(
|
||||||
stateMetadata: CompileAnimationStateDeclarationMetadata,
|
stateMetadata: CompileAnimationStateDeclarationMetadata, schema: ElementSchemaRegistry,
|
||||||
errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
|
errors: AnimationParseError[]): AnimationStateDeclarationAst[] {
|
||||||
var styleValues: Styles[] = [];
|
var normalizedStyles = _normalizeStyleMetadata(stateMetadata.styles, {}, schema, errors, false);
|
||||||
stateMetadata.styles.styles.forEach(stylesEntry => {
|
var defStyles = new AnimationStylesAst(normalizedStyles);
|
||||||
// TODO (matsko): change this when we get CSS class integration support
|
|
||||||
if (typeof stylesEntry === 'object' && stylesEntry !== null) {
|
|
||||||
styleValues.push(stylesEntry as Styles);
|
|
||||||
} else {
|
|
||||||
errors.push(new AnimationParseError(
|
|
||||||
`State based animations cannot contain references to other states`));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
var defStyles = new AnimationStylesAst(styleValues);
|
|
||||||
|
|
||||||
var states = stateMetadata.stateNameExpr.split(/\s*,\s*/);
|
var states = stateMetadata.stateNameExpr.split(/\s*,\s*/);
|
||||||
return states.map(state => new AnimationStateDeclarationAst(state, defStyles));
|
return states.map(state => new AnimationStateDeclarationAst(state, defStyles));
|
||||||
}
|
}
|
||||||
|
|
||||||
function _parseAnimationStateTransition(
|
function _parseAnimationStateTransition(
|
||||||
transitionStateMetadata: CompileAnimationStateTransitionMetadata,
|
transitionStateMetadata: CompileAnimationStateTransitionMetadata,
|
||||||
stateStyles: {[key: string]: AnimationStylesAst},
|
stateStyles: {[key: string]: AnimationStylesAst}, schema: ElementSchemaRegistry,
|
||||||
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
errors: AnimationParseError[]): AnimationStateTransitionAst {
|
||||||
var styles = new StylesCollection();
|
var styles = new StylesCollection();
|
||||||
var transitionExprs: AnimationStateTransitionExpression[] = [];
|
var transitionExprs: AnimationStateTransitionExpression[] = [];
|
||||||
|
@ -119,7 +115,7 @@ function _parseAnimationStateTransition(
|
||||||
transitionStates.forEach(
|
transitionStates.forEach(
|
||||||
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
expr => { transitionExprs.push(..._parseAnimationTransitionExpr(expr, errors)); });
|
||||||
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
var entry = _normalizeAnimationEntry(transitionStateMetadata.steps);
|
||||||
var animation = _normalizeStyleSteps(entry, stateStyles, errors);
|
var animation = _normalizeStyleSteps(entry, stateStyles, schema, errors);
|
||||||
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
var animationAst = _parseTransitionAnimation(animation, 0, styles, stateStyles, errors);
|
||||||
if (errors.length == 0) {
|
if (errors.length == 0) {
|
||||||
_fillAnimationAstStartingKeyframes(animationAst, styles, errors);
|
_fillAnimationAstStartingKeyframes(animationAst, styles, errors);
|
||||||
|
@ -176,13 +172,31 @@ function _normalizeAnimationEntry(entry: CompileAnimationMetadata | CompileAnima
|
||||||
|
|
||||||
function _normalizeStyleMetadata(
|
function _normalizeStyleMetadata(
|
||||||
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationStyleMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): {[key: string]: string | number}[] {
|
schema: ElementSchemaRegistry, errors: AnimationParseError[],
|
||||||
|
permitStateReferences: boolean): {[key: string]: string | number}[] {
|
||||||
var normalizedStyles: {[key: string]: string | number}[] = [];
|
var normalizedStyles: {[key: string]: string | number}[] = [];
|
||||||
entry.styles.forEach(styleEntry => {
|
entry.styles.forEach(styleEntry => {
|
||||||
if (typeof styleEntry === 'string') {
|
if (typeof styleEntry === 'string') {
|
||||||
|
if (permitStateReferences) {
|
||||||
normalizedStyles.push(..._resolveStylesFromState(<string>styleEntry, stateStyles, errors));
|
normalizedStyles.push(..._resolveStylesFromState(<string>styleEntry, stateStyles, errors));
|
||||||
} else {
|
} else {
|
||||||
normalizedStyles.push(<{[key: string]: string | number}>styleEntry);
|
errors.push(new AnimationParseError(
|
||||||
|
`State based animations cannot contain references to other states`));
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
var stylesObj = <Styles>styleEntry;
|
||||||
|
var normalizedStylesObj: Styles = {};
|
||||||
|
Object.keys(stylesObj).forEach(propName => {
|
||||||
|
var normalizedProp = schema.normalizeAnimationStyleProperty(propName);
|
||||||
|
var normalizedOutput =
|
||||||
|
schema.normalizeAnimationStyleValue(normalizedProp, propName, stylesObj[propName]);
|
||||||
|
var normalizationError = normalizedOutput['error'];
|
||||||
|
if (normalizationError) {
|
||||||
|
errors.push(new AnimationParseError(normalizationError));
|
||||||
|
}
|
||||||
|
normalizedStylesObj[normalizedProp] = normalizedOutput['value'];
|
||||||
|
});
|
||||||
|
normalizedStyles.push(normalizedStylesObj);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
return normalizedStyles;
|
return normalizedStyles;
|
||||||
|
@ -190,8 +204,8 @@ function _normalizeStyleMetadata(
|
||||||
|
|
||||||
function _normalizeStyleSteps(
|
function _normalizeStyleSteps(
|
||||||
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): CompileAnimationMetadata {
|
schema: ElementSchemaRegistry, errors: AnimationParseError[]): CompileAnimationMetadata {
|
||||||
var steps = _normalizeStyleStepEntry(entry, stateStyles, errors);
|
var steps = _normalizeStyleStepEntry(entry, stateStyles, schema, errors);
|
||||||
return (entry instanceof CompileAnimationGroupMetadata) ?
|
return (entry instanceof CompileAnimationGroupMetadata) ?
|
||||||
new CompileAnimationGroupMetadata(steps) :
|
new CompileAnimationGroupMetadata(steps) :
|
||||||
new CompileAnimationSequenceMetadata(steps);
|
new CompileAnimationSequenceMetadata(steps);
|
||||||
|
@ -213,7 +227,7 @@ function _mergeAnimationStyles(
|
||||||
|
|
||||||
function _normalizeStyleStepEntry(
|
function _normalizeStyleStepEntry(
|
||||||
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
entry: CompileAnimationMetadata, stateStyles: {[key: string]: AnimationStylesAst},
|
||||||
errors: AnimationParseError[]): CompileAnimationMetadata[] {
|
schema: ElementSchemaRegistry, errors: AnimationParseError[]): CompileAnimationMetadata[] {
|
||||||
var steps: CompileAnimationMetadata[];
|
var steps: CompileAnimationMetadata[];
|
||||||
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
if (entry instanceof CompileAnimationWithStepsMetadata) {
|
||||||
steps = entry.steps;
|
steps = entry.steps;
|
||||||
|
@ -232,7 +246,8 @@ function _normalizeStyleStepEntry(
|
||||||
if (!isPresent(combinedStyles)) {
|
if (!isPresent(combinedStyles)) {
|
||||||
combinedStyles = [];
|
combinedStyles = [];
|
||||||
}
|
}
|
||||||
_normalizeStyleMetadata(<CompileAnimationStyleMetadata>step, stateStyles, errors)
|
_normalizeStyleMetadata(
|
||||||
|
<CompileAnimationStyleMetadata>step, stateStyles, schema, errors, true)
|
||||||
.forEach(entry => { _mergeAnimationStyles(combinedStyles, entry); });
|
.forEach(entry => { _mergeAnimationStyles(combinedStyles, entry); });
|
||||||
} else {
|
} else {
|
||||||
// it is important that we create a metadata entry of the combined styles
|
// it is important that we create a metadata entry of the combined styles
|
||||||
|
@ -250,13 +265,14 @@ function _normalizeStyleStepEntry(
|
||||||
var animateStyleValue = (<CompileAnimationAnimateMetadata>step).styles;
|
var animateStyleValue = (<CompileAnimationAnimateMetadata>step).styles;
|
||||||
if (animateStyleValue instanceof CompileAnimationStyleMetadata) {
|
if (animateStyleValue instanceof CompileAnimationStyleMetadata) {
|
||||||
animateStyleValue.styles =
|
animateStyleValue.styles =
|
||||||
_normalizeStyleMetadata(animateStyleValue, stateStyles, errors);
|
_normalizeStyleMetadata(animateStyleValue, stateStyles, schema, errors, true);
|
||||||
} else if (animateStyleValue instanceof CompileAnimationKeyframesSequenceMetadata) {
|
} else if (animateStyleValue instanceof CompileAnimationKeyframesSequenceMetadata) {
|
||||||
animateStyleValue.steps.forEach(
|
animateStyleValue.steps.forEach(step => {
|
||||||
step => { step.styles = _normalizeStyleMetadata(step, stateStyles, errors); });
|
step.styles = _normalizeStyleMetadata(step, stateStyles, schema, errors, true);
|
||||||
|
});
|
||||||
}
|
}
|
||||||
} else if (step instanceof CompileAnimationWithStepsMetadata) {
|
} else if (step instanceof CompileAnimationWithStepsMetadata) {
|
||||||
let innerSteps = _normalizeStyleStepEntry(step, stateStyles, errors);
|
let innerSteps = _normalizeStyleStepEntry(step, stateStyles, schema, errors);
|
||||||
step = step instanceof CompileAnimationGroupMetadata ?
|
step = step instanceof CompileAnimationGroupMetadata ?
|
||||||
new CompileAnimationGroupMetadata(innerSteps) :
|
new CompileAnimationGroupMetadata(innerSteps) :
|
||||||
new CompileAnimationSequenceMetadata(innerSteps);
|
new CompileAnimationSequenceMetadata(innerSteps);
|
||||||
|
|
|
@ -8,6 +8,7 @@
|
||||||
|
|
||||||
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
import {COMPILER_OPTIONS, Compiler, CompilerFactory, CompilerOptions, Inject, Injectable, Optional, PLATFORM_INITIALIZER, PlatformRef, Provider, ReflectiveInjector, TRANSLATIONS, TRANSLATIONS_FORMAT, Type, ViewEncapsulation, createPlatformFactory, isDevMode, platformCore} from '@angular/core';
|
||||||
|
|
||||||
|
import {AnimationParser} from './animation/animation_parser';
|
||||||
import {CompilerConfig} from './config';
|
import {CompilerConfig} from './config';
|
||||||
import {DirectiveNormalizer} from './directive_normalizer';
|
import {DirectiveNormalizer} from './directive_normalizer';
|
||||||
import {DirectiveResolver} from './directive_resolver';
|
import {DirectiveResolver} from './directive_resolver';
|
||||||
|
@ -74,7 +75,8 @@ export const COMPILER_PROVIDERS: Array<any|Type<any>|{[k: string]: any}|any[]> =
|
||||||
UrlResolver,
|
UrlResolver,
|
||||||
DirectiveResolver,
|
DirectiveResolver,
|
||||||
PipeResolver,
|
PipeResolver,
|
||||||
NgModuleResolver
|
NgModuleResolver,
|
||||||
|
AnimationParser
|
||||||
];
|
];
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -109,7 +109,6 @@ export function analyzeNgModules(
|
||||||
}
|
}
|
||||||
|
|
||||||
export class OfflineCompiler {
|
export class OfflineCompiler {
|
||||||
private _animationParser = new AnimationParser();
|
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -118,7 +117,8 @@ export class OfflineCompiler {
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
private _dirWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
private _ngModuleCompiler: NgModuleCompiler, private _outputEmitter: OutputEmitter,
|
||||||
private _localeId: string, private _translationFormat: string) {}
|
private _localeId: string, private _translationFormat: string,
|
||||||
|
private _animationParser: AnimationParser) {}
|
||||||
|
|
||||||
clearCache() {
|
clearCache() {
|
||||||
this._directiveNormalizer.clearCache();
|
this._directiveNormalizer.clearCache();
|
||||||
|
|
|
@ -43,7 +43,6 @@ export class RuntimeCompiler implements Compiler {
|
||||||
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
private _compiledHostTemplateCache = new Map<Type<any>, CompiledTemplate>();
|
||||||
private _compiledDirectiveWrapperCache = new Map<Type<any>, Type<any>>();
|
private _compiledDirectiveWrapperCache = new Map<Type<any>, Type<any>>();
|
||||||
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
|
private _compiledNgModuleCache = new Map<Type<any>, NgModuleFactory<any>>();
|
||||||
private _animationParser = new AnimationParser();
|
|
||||||
private _animationCompiler = new AnimationCompiler();
|
private _animationCompiler = new AnimationCompiler();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
|
@ -52,7 +51,7 @@ export class RuntimeCompiler implements Compiler {
|
||||||
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
private _styleCompiler: StyleCompiler, private _viewCompiler: ViewCompiler,
|
||||||
private _ngModuleCompiler: NgModuleCompiler,
|
private _ngModuleCompiler: NgModuleCompiler,
|
||||||
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
|
private _directiveWrapperCompiler: DirectiveWrapperCompiler,
|
||||||
private _compilerConfig: CompilerConfig) {}
|
private _compilerConfig: CompilerConfig, private _animationParser: AnimationParser) {}
|
||||||
|
|
||||||
get injector(): Injector { return this._injector; }
|
get injector(): Injector { return this._injector; }
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,9 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CUSTOM_ELEMENTS_SCHEMA, Injectable, NO_ERRORS_SCHEMA, SchemaMetadata, SecurityContext} from '@angular/core';
|
import {AUTO_STYLE, CUSTOM_ELEMENTS_SCHEMA, Injectable, NO_ERRORS_SCHEMA, SchemaMetadata, SecurityContext} from '@angular/core';
|
||||||
|
|
||||||
|
import {dashCaseToCamelCase} from '../util';
|
||||||
|
|
||||||
import {SECURITY_SCHEMA} from './dom_security_schema';
|
import {SECURITY_SCHEMA} from './dom_security_schema';
|
||||||
import {ElementSchemaRegistry} from './element_schema_registry';
|
import {ElementSchemaRegistry} from './element_schema_registry';
|
||||||
|
@ -373,4 +375,64 @@ export class DomElementSchemaRegistry extends ElementSchemaRegistry {
|
||||||
}
|
}
|
||||||
|
|
||||||
allKnownElementNames(): string[] { return Object.keys(this._schema); }
|
allKnownElementNames(): string[] { return Object.keys(this._schema); }
|
||||||
|
|
||||||
|
normalizeAnimationStyleProperty(propName: string): string {
|
||||||
|
return dashCaseToCamelCase(propName);
|
||||||
|
}
|
||||||
|
|
||||||
|
normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string|number):
|
||||||
|
{error: string, value: string} {
|
||||||
|
var unit: string = '';
|
||||||
|
var strVal = val.toString().trim();
|
||||||
|
var errorMsg: string = null;
|
||||||
|
|
||||||
|
if (_isPixelDimensionStyle(camelCaseProp) && val !== 0 && val !== '0') {
|
||||||
|
if (typeof val === 'number') {
|
||||||
|
unit = 'px';
|
||||||
|
} else {
|
||||||
|
let valAndSuffixMatch = val.match(/^[+-]?[\d\.]+([a-z]*)$/);
|
||||||
|
if (valAndSuffixMatch && valAndSuffixMatch[1].length == 0) {
|
||||||
|
errorMsg = `Please provide a CSS unit value for ${userProvidedProp}:${val}`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {error: errorMsg, value: strVal + unit};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function _isPixelDimensionStyle(prop: string): boolean {
|
||||||
|
switch (prop) {
|
||||||
|
case 'width':
|
||||||
|
case 'height':
|
||||||
|
case 'minWidth':
|
||||||
|
case 'minHeight':
|
||||||
|
case 'maxWidth':
|
||||||
|
case 'maxHeight':
|
||||||
|
case 'left':
|
||||||
|
case 'top':
|
||||||
|
case 'bottom':
|
||||||
|
case 'right':
|
||||||
|
case 'fontSize':
|
||||||
|
case 'outlineWidth':
|
||||||
|
case 'outlineOffset':
|
||||||
|
case 'paddingTop':
|
||||||
|
case 'paddingLeft':
|
||||||
|
case 'paddingBottom':
|
||||||
|
case 'paddingRight':
|
||||||
|
case 'marginTop':
|
||||||
|
case 'marginLeft':
|
||||||
|
case 'marginBottom':
|
||||||
|
case 'marginRight':
|
||||||
|
case 'borderRadius':
|
||||||
|
case 'borderWidth':
|
||||||
|
case 'borderTopWidth':
|
||||||
|
case 'borderLeftWidth':
|
||||||
|
case 'borderRightWidth':
|
||||||
|
case 'borderBottomWidth':
|
||||||
|
case 'textIndent':
|
||||||
|
return true;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -18,4 +18,8 @@ export abstract class ElementSchemaRegistry {
|
||||||
abstract getDefaultComponentElementName(): string;
|
abstract getDefaultComponentElementName(): string;
|
||||||
abstract validateProperty(name: string): {error: boolean, msg?: string};
|
abstract validateProperty(name: string): {error: boolean, msg?: string};
|
||||||
abstract validateAttribute(name: string): {error: boolean, msg?: string};
|
abstract validateAttribute(name: string): {error: boolean, msg?: string};
|
||||||
|
abstract normalizeAnimationStyleProperty(propName: string): string;
|
||||||
|
abstract normalizeAnimationStyleValue(
|
||||||
|
camelCaseProp: string, userProvidedProp: string,
|
||||||
|
val: string|number): {error: string, value: string};
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,11 +11,16 @@ import {isBlank, isPrimitive, isStrictStringMap} from './facade/lang';
|
||||||
export const MODULE_SUFFIX = '';
|
export const MODULE_SUFFIX = '';
|
||||||
|
|
||||||
const CAMEL_CASE_REGEXP = /([A-Z])/g;
|
const CAMEL_CASE_REGEXP = /([A-Z])/g;
|
||||||
|
const DASH_CASE_REGEXP = /-+([a-z0-9])/g;
|
||||||
|
|
||||||
export function camelCaseToDashCase(input: string): string {
|
export function camelCaseToDashCase(input: string): string {
|
||||||
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
return input.replace(CAMEL_CASE_REGEXP, (...m: any[]) => '-' + m[1].toLowerCase());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function dashCaseToCamelCase(input: string): string {
|
||||||
|
return input.replace(DASH_CASE_REGEXP, (...m: any[]) => m[1].toUpperCase());
|
||||||
|
}
|
||||||
|
|
||||||
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
export function splitAtColon(input: string, defaultValues: string[]): string[] {
|
||||||
return _splitAt(input, ':', defaultValues);
|
return _splitAt(input, ':', defaultValues);
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,14 +12,19 @@ import {AnimationCompiler, AnimationEntryCompileResult} from '../../src/animatio
|
||||||
import {AnimationParser} from '../../src/animation/animation_parser';
|
import {AnimationParser} from '../../src/animation/animation_parser';
|
||||||
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '../../src/compile_metadata';
|
import {CompileAnimationEntryMetadata, CompileDirectiveMetadata, CompileTemplateMetadata, CompileTypeMetadata} from '../../src/compile_metadata';
|
||||||
import {CompileMetadataResolver} from '../../src/metadata_resolver';
|
import {CompileMetadataResolver} from '../../src/metadata_resolver';
|
||||||
|
import {ElementSchemaRegistry} from '../../src/schema/element_schema_registry';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
describe('RuntimeAnimationCompiler', () => {
|
describe('RuntimeAnimationCompiler', () => {
|
||||||
var resolver: CompileMetadataResolver;
|
var resolver: CompileMetadataResolver;
|
||||||
beforeEach(
|
var parser: AnimationParser;
|
||||||
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
|
beforeEach(inject(
|
||||||
|
[CompileMetadataResolver, ElementSchemaRegistry],
|
||||||
|
(res: CompileMetadataResolver, schema: ElementSchemaRegistry) => {
|
||||||
|
resolver = res;
|
||||||
|
parser = new AnimationParser(schema);
|
||||||
|
}));
|
||||||
|
|
||||||
const parser = new AnimationParser();
|
|
||||||
const compiler = new AnimationCompiler();
|
const compiler = new AnimationCompiler();
|
||||||
|
|
||||||
var compileAnimations =
|
var compileAnimations =
|
||||||
|
|
|
@ -13,6 +13,7 @@ import {expect} from '@angular/platform-browser/testing/matchers';
|
||||||
import {AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast';
|
import {AnimationEntryAst, AnimationGroupAst, AnimationKeyframeAst, AnimationSequenceAst, AnimationStepAst, AnimationStylesAst} from '../../src/animation/animation_ast';
|
||||||
import {AnimationParser} from '../../src/animation/animation_parser';
|
import {AnimationParser} from '../../src/animation/animation_parser';
|
||||||
import {CompileMetadataResolver} from '../../src/metadata_resolver';
|
import {CompileMetadataResolver} from '../../src/metadata_resolver';
|
||||||
|
import {ElementSchemaRegistry} from '../../src/schema/element_schema_registry';
|
||||||
import {FILL_STYLE_FLAG, flattenStyles} from '../private_import_core';
|
import {FILL_STYLE_FLAG, flattenStyles} from '../private_import_core';
|
||||||
|
|
||||||
export function main() {
|
export function main() {
|
||||||
|
@ -39,13 +40,18 @@ export function main() {
|
||||||
};
|
};
|
||||||
|
|
||||||
var resolver: CompileMetadataResolver;
|
var resolver: CompileMetadataResolver;
|
||||||
beforeEach(
|
var schema: ElementSchemaRegistry;
|
||||||
inject([CompileMetadataResolver], (res: CompileMetadataResolver) => { resolver = res; }));
|
beforeEach(inject(
|
||||||
|
[CompileMetadataResolver, ElementSchemaRegistry],
|
||||||
|
(res: CompileMetadataResolver, sch: ElementSchemaRegistry) => {
|
||||||
|
resolver = res;
|
||||||
|
schema = sch;
|
||||||
|
}));
|
||||||
|
|
||||||
var parseAnimation = (data: AnimationMetadata[]) => {
|
var parseAnimation = (data: AnimationMetadata[]) => {
|
||||||
const entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]);
|
const entry = trigger('myAnimation', [transition('state1 => state2', sequence(data))]);
|
||||||
const compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry);
|
const compiledAnimationEntry = resolver.getAnimationEntryMetadata(entry);
|
||||||
const parser = new AnimationParser();
|
const parser = new AnimationParser(schema);
|
||||||
return parser.parseEntry(compiledAnimationEntry);
|
return parser.parseEntry(compiledAnimationEntry);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -59,21 +65,21 @@ export function main() {
|
||||||
|
|
||||||
it('should merge repeated style steps into a single style ast step entry', () => {
|
it('should merge repeated style steps into a single style ast step entry', () => {
|
||||||
var ast = parseAnimationAst([
|
var ast = parseAnimationAst([
|
||||||
style({'color': 'black'}), style({'background': 'red'}), style({'opacity': 0}),
|
style({'color': 'black'}), style({'background': 'red'}), style({'opacity': '0'}),
|
||||||
animate(1000, style({'color': 'white', 'background': 'black', 'opacity': 1}))
|
animate(1000, style({'color': 'white', 'background': 'black', 'opacity': '1'}))
|
||||||
]);
|
]);
|
||||||
|
|
||||||
expect(ast.steps.length).toEqual(1);
|
expect(ast.steps.length).toEqual(1);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
expect(step.startingStyles.styles[0])
|
expect(step.startingStyles.styles[0])
|
||||||
.toEqual({'color': 'black', 'background': 'red', 'opacity': 0});
|
.toEqual({'color': 'black', 'background': 'red', 'opacity': '0'});
|
||||||
|
|
||||||
expect(step.keyframes[0].styles.styles[0])
|
expect(step.keyframes[0].styles.styles[0])
|
||||||
.toEqual({'color': 'black', 'background': 'red', 'opacity': 0});
|
.toEqual({'color': 'black', 'background': 'red', 'opacity': '0'});
|
||||||
|
|
||||||
expect(step.keyframes[1].styles.styles[0])
|
expect(step.keyframes[1].styles.styles[0])
|
||||||
.toEqual({'color': 'white', 'background': 'black', 'opacity': 1});
|
.toEqual({'color': 'white', 'background': 'black', 'opacity': '1'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should animate only the styles requested within an animation step', () => {
|
it('should animate only the styles requested within an animation step', () => {
|
||||||
|
@ -93,7 +99,7 @@ export function main() {
|
||||||
|
|
||||||
it('should populate the starting and duration times propertly', () => {
|
it('should populate the starting and duration times propertly', () => {
|
||||||
var ast = parseAnimationAst([
|
var ast = parseAnimationAst([
|
||||||
style({'color': 'black', 'opacity': 1}),
|
style({'color': 'black', 'opacity': '1'}),
|
||||||
animate(1000, style({'color': 'red'})),
|
animate(1000, style({'color': 'red'})),
|
||||||
animate(4000, style({'color': 'yellow'})),
|
animate(4000, style({'color': 'yellow'})),
|
||||||
sequence(
|
sequence(
|
||||||
|
@ -144,13 +150,13 @@ export function main() {
|
||||||
it('should apply the correct animate() styles when parallel animations are active and use the same properties',
|
it('should apply the correct animate() styles when parallel animations are active and use the same properties',
|
||||||
() => {
|
() => {
|
||||||
var details = parseAnimation([
|
var details = parseAnimation([
|
||||||
style({'opacity': 0, 'color': 'red'}), group([
|
style({'opacity': '0', 'color': 'red'}), group([
|
||||||
sequence([
|
sequence([
|
||||||
animate(2000, style({'color': 'black'})),
|
animate(2000, style({'color': 'black'})),
|
||||||
animate(2000, style({'opacity': 0.5})),
|
animate(2000, style({'opacity': '0.5'})),
|
||||||
]),
|
]),
|
||||||
sequence([
|
sequence([
|
||||||
animate(2000, style({'opacity': 0.8})),
|
animate(2000, style({'opacity': '0.8'})),
|
||||||
animate(2000, style({'color': 'blue'}))
|
animate(2000, style({'color': 'blue'}))
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
|
@ -169,10 +175,10 @@ export function main() {
|
||||||
expect(collectStepStyles(sq1a1)).toEqual([{'color': 'red'}, {'color': 'black'}]);
|
expect(collectStepStyles(sq1a1)).toEqual([{'color': 'red'}, {'color': 'black'}]);
|
||||||
|
|
||||||
var sq1a2 = <AnimationStepAst>sq1.steps[1];
|
var sq1a2 = <AnimationStepAst>sq1.steps[1];
|
||||||
expect(collectStepStyles(sq1a2)).toEqual([{'opacity': 0.8}, {'opacity': 0.5}]);
|
expect(collectStepStyles(sq1a2)).toEqual([{'opacity': '0.8'}, {'opacity': '0.5'}]);
|
||||||
|
|
||||||
var sq2a1 = <AnimationStepAst>sq2.steps[0];
|
var sq2a1 = <AnimationStepAst>sq2.steps[0];
|
||||||
expect(collectStepStyles(sq2a1)).toEqual([{'opacity': 0}, {'opacity': 0.8}]);
|
expect(collectStepStyles(sq2a1)).toEqual([{'opacity': '0'}, {'opacity': '0.8'}]);
|
||||||
|
|
||||||
var sq2a2 = <AnimationStepAst>sq2.steps[1];
|
var sq2a2 = <AnimationStepAst>sq2.steps[1];
|
||||||
expect(collectStepStyles(sq2a2)).toEqual([{'color': 'black'}, {'color': 'blue'}]);
|
expect(collectStepStyles(sq2a2)).toEqual([{'color': 'black'}, {'color': 'blue'}]);
|
||||||
|
@ -180,8 +186,8 @@ export function main() {
|
||||||
|
|
||||||
it('should throw errors when animations animate a CSS property at the same time', () => {
|
it('should throw errors when animations animate a CSS property at the same time', () => {
|
||||||
var animation1 = parseAnimation([
|
var animation1 = parseAnimation([
|
||||||
style({'opacity': 0}),
|
style({'opacity': '0'}),
|
||||||
group([animate(1000, style({'opacity': 1})), animate(2000, style({'opacity': 0.5}))])
|
group([animate(1000, style({'opacity': '1'})), animate(2000, style({'opacity': '0.5'}))])
|
||||||
]);
|
]);
|
||||||
|
|
||||||
var errors1 = animation1.errors;
|
var errors1 = animation1.errors;
|
||||||
|
@ -205,22 +211,23 @@ export function main() {
|
||||||
|
|
||||||
it('should return an error when an animation style contains an invalid timing value', () => {
|
it('should return an error when an animation style contains an invalid timing value', () => {
|
||||||
var errors = parseAnimationAndGetErrors(
|
var errors = parseAnimationAndGetErrors(
|
||||||
[style({'opacity': 0}), animate('one second', style({'opacity': 1}))]);
|
[style({'opacity': '0'}), animate('one second', style({'opacity': '1'}))]);
|
||||||
expect(errors[0].msg).toContainError(`The provided timing value "one second" is invalid.`);
|
expect(errors[0].msg).toContainError(`The provided timing value "one second" is invalid.`);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should collect and return any errors collected when parsing the metadata', () => {
|
it('should collect and return any errors collected when parsing the metadata', () => {
|
||||||
var errors = parseAnimationAndGetErrors([
|
var errors = parseAnimationAndGetErrors([
|
||||||
style({'opacity': 0}), animate('one second', style({'opacity': 1})), style({'opacity': 0}),
|
style({'opacity': '0'}), animate('one second', style({'opacity': '1'})),
|
||||||
animate('one second', null), style({'background': 'red'})
|
style({'opacity': '0'}), animate('one second', null), style({'background': 'red'})
|
||||||
]);
|
]);
|
||||||
expect(errors.length).toBeGreaterThan(1);
|
expect(errors.length).toBeGreaterThan(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should normalize a series of keyframe styles into a list of offset steps', () => {
|
it('should normalize a series of keyframe styles into a list of offset steps', () => {
|
||||||
var ast = parseAnimationAst([animate(1000, keyframes([
|
var ast =
|
||||||
style({'width': 0}), style({'width': 25}),
|
parseAnimationAst([animate(1000, keyframes([
|
||||||
style({'width': 50}), style({'width': 75})
|
style({'width': '0'}), style({'width': '25px'}),
|
||||||
|
style({'width': '50px'}), style({'width': '75px'})
|
||||||
]))]);
|
]))]);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
|
@ -233,10 +240,10 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use an existing collection of offset steps if provided', () => {
|
it('should use an existing collection of offset steps if provided', () => {
|
||||||
var ast = parseAnimationAst(
|
var ast = parseAnimationAst([animate(
|
||||||
[animate(1000, keyframes([
|
1000, keyframes([
|
||||||
style({'height': 0, 'offset': 0}), style({'height': 25, 'offset': 0.6}),
|
style({'height': '0', 'offset': 0}), style({'height': '25px', 'offset': 0.6}),
|
||||||
style({'height': 50, 'offset': 0.7}), style({'height': 75, 'offset': 1})
|
style({'height': '50px', 'offset': 0.7}), style({'height': '75px', 'offset': 1})
|
||||||
]))]);
|
]))]);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
|
@ -251,24 +258,25 @@ export function main() {
|
||||||
it('should sort the provided collection of steps that contain offsets', () => {
|
it('should sort the provided collection of steps that contain offsets', () => {
|
||||||
var ast = parseAnimationAst([animate(
|
var ast = parseAnimationAst([animate(
|
||||||
1000, keyframes([
|
1000, keyframes([
|
||||||
style({'opacity': 0, 'offset': 0.9}), style({'opacity': .25, 'offset': 0}),
|
style({'opacity': '0', 'offset': 0.9}), style({'opacity': '0.25', 'offset': 0}),
|
||||||
style({'opacity': .50, 'offset': 1}), style({'opacity': .75, 'offset': 0.91})
|
style({'opacity': '0.50', 'offset': 1}),
|
||||||
|
style({'opacity': '0.75', 'offset': 0.91})
|
||||||
]))]);
|
]))]);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
expect(step.keyframes.length).toEqual(4);
|
expect(step.keyframes.length).toEqual(4);
|
||||||
|
|
||||||
expect(step.keyframes[0].offset).toEqual(0);
|
expect(step.keyframes[0].offset).toEqual(0);
|
||||||
expect(step.keyframes[0].styles.styles[0]['opacity']).toEqual(.25);
|
expect(step.keyframes[0].styles.styles[0]['opacity']).toEqual('0.25');
|
||||||
|
|
||||||
expect(step.keyframes[1].offset).toEqual(0.9);
|
expect(step.keyframes[1].offset).toEqual(0.9);
|
||||||
expect(step.keyframes[1].styles.styles[0]['opacity']).toEqual(0);
|
expect(step.keyframes[1].styles.styles[0]['opacity']).toEqual('0');
|
||||||
|
|
||||||
expect(step.keyframes[2].offset).toEqual(0.91);
|
expect(step.keyframes[2].offset).toEqual(0.91);
|
||||||
expect(step.keyframes[2].styles.styles[0]['opacity']).toEqual(.75);
|
expect(step.keyframes[2].styles.styles[0]['opacity']).toEqual('0.75');
|
||||||
|
|
||||||
expect(step.keyframes[3].offset).toEqual(1);
|
expect(step.keyframes[3].offset).toEqual(1);
|
||||||
expect(step.keyframes[3].styles.styles[0]['opacity']).toEqual(.50);
|
expect(step.keyframes[3].styles.styles[0]['opacity']).toEqual('0.50');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw an error if a partial amount of keyframes contain an offset', () => {
|
it('should throw an error if a partial amount of keyframes contain an offset', () => {
|
||||||
|
@ -302,7 +310,7 @@ export function main() {
|
||||||
it('should copy over any missing styles to the final keyframe if not already defined', () => {
|
it('should copy over any missing styles to the final keyframe if not already defined', () => {
|
||||||
var ast = parseAnimationAst([animate(
|
var ast = parseAnimationAst([animate(
|
||||||
1000, keyframes([
|
1000, keyframes([
|
||||||
style({'color': 'white', 'border-color': 'white'}),
|
style({'color': 'white', 'borderColor': 'white'}),
|
||||||
style({'color': 'red', 'background': 'blue'}), style({'background': 'blue'})
|
style({'color': 'red', 'background': 'blue'}), style({'background': 'blue'})
|
||||||
]))]);
|
]))]);
|
||||||
|
|
||||||
|
@ -312,19 +320,16 @@ export function main() {
|
||||||
var kf3 = keyframesStep.keyframes[2];
|
var kf3 = keyframesStep.keyframes[2];
|
||||||
|
|
||||||
expect(flattenStyles(kf3.styles.styles))
|
expect(flattenStyles(kf3.styles.styles))
|
||||||
.toEqual({'background': 'blue', 'color': 'red', 'border-color': 'white'});
|
.toEqual({'background': 'blue', 'color': 'red', 'borderColor': 'white'});
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should create an initial keyframe if not detected and place all keyframes styles there',
|
it('should create an initial keyframe if not detected and place all keyframes styles there',
|
||||||
() => {
|
() => {
|
||||||
var ast = parseAnimationAst(
|
var ast = parseAnimationAst([animate(
|
||||||
[animate(1000, keyframes([
|
1000, keyframes([
|
||||||
style({'color': 'white', 'background': 'black', 'offset': 0.5}), style({
|
style({'color': 'white', 'background': 'black', 'offset': 0.5}),
|
||||||
'color': 'orange',
|
style(
|
||||||
'background': 'red',
|
{'color': 'orange', 'background': 'red', 'fontSize': '100px', 'offset': 1})
|
||||||
'font-size': '100px',
|
|
||||||
'offset': 1
|
|
||||||
})
|
|
||||||
]))]);
|
]))]);
|
||||||
|
|
||||||
var keyframesStep = <AnimationStepAst>ast.steps[0];
|
var keyframesStep = <AnimationStepAst>ast.steps[0];
|
||||||
|
@ -335,7 +340,7 @@ export function main() {
|
||||||
|
|
||||||
expect(kf1.offset).toEqual(0);
|
expect(kf1.offset).toEqual(0);
|
||||||
expect(flattenStyles(kf1.styles.styles)).toEqual({
|
expect(flattenStyles(kf1.styles.styles)).toEqual({
|
||||||
'font-size': FILL_STYLE_FLAG,
|
'fontSize': FILL_STYLE_FLAG,
|
||||||
'background': FILL_STYLE_FLAG,
|
'background': FILL_STYLE_FLAG,
|
||||||
'color': FILL_STYLE_FLAG
|
'color': FILL_STYLE_FLAG
|
||||||
});
|
});
|
||||||
|
@ -353,7 +358,7 @@ export function main() {
|
||||||
style({
|
style({
|
||||||
'color': 'orange',
|
'color': 'orange',
|
||||||
'background': 'red',
|
'background': 'red',
|
||||||
'font-size': '100px',
|
'fontSize': '100px',
|
||||||
'offset': 0.5
|
'offset': 0.5
|
||||||
})
|
})
|
||||||
]))]);
|
]))]);
|
||||||
|
@ -369,13 +374,13 @@ export function main() {
|
||||||
'color': 'orange',
|
'color': 'orange',
|
||||||
'background': 'red',
|
'background': 'red',
|
||||||
'transform': 'rotate(360deg)',
|
'transform': 'rotate(360deg)',
|
||||||
'font-size': '100px'
|
'fontSize': '100px'
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('easing / duration / delay', () => {
|
describe('easing / duration / delay', () => {
|
||||||
it('should parse simple string-based values', () => {
|
it('should parse simple string-based values', () => {
|
||||||
var ast = parseAnimationAst([animate('1s .5s ease-out', style({'opacity': 1}))]);
|
var ast = parseAnimationAst([animate('1s .5s ease-out', style({'opacity': '1'}))]);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
expect(step.duration).toEqual(1000);
|
expect(step.duration).toEqual(1000);
|
||||||
|
@ -384,7 +389,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse a numeric duration value', () => {
|
it('should parse a numeric duration value', () => {
|
||||||
var ast = parseAnimationAst([animate(666, style({'opacity': 1}))]);
|
var ast = parseAnimationAst([animate(666, style({'opacity': '1'}))]);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
expect(step.duration).toEqual(666);
|
expect(step.duration).toEqual(666);
|
||||||
|
@ -393,7 +398,7 @@ export function main() {
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should parse an easing value without a delay', () => {
|
it('should parse an easing value without a delay', () => {
|
||||||
var ast = parseAnimationAst([animate('5s linear', style({'opacity': 1}))]);
|
var ast = parseAnimationAst([animate('5s linear', style({'opacity': '1'}))]);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
expect(step.duration).toEqual(5000);
|
expect(step.duration).toEqual(5000);
|
||||||
|
@ -403,7 +408,7 @@ export function main() {
|
||||||
|
|
||||||
it('should parse a complex easing value', () => {
|
it('should parse a complex easing value', () => {
|
||||||
var ast =
|
var ast =
|
||||||
parseAnimationAst([animate('30ms cubic-bezier(0, 0,0, .69)', style({'opacity': 1}))]);
|
parseAnimationAst([animate('30ms cubic-bezier(0, 0,0, .69)', style({'opacity': '1'}))]);
|
||||||
|
|
||||||
var step = <AnimationStepAst>ast.steps[0];
|
var step = <AnimationStepAst>ast.steps[0];
|
||||||
expect(step.duration).toEqual(30);
|
expect(step.duration).toEqual(30);
|
||||||
|
|
|
@ -192,5 +192,44 @@ If 'onAnything' is a directive input, make sure the directive is imported by the
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
describe('normalizeAnimationStyleProperty', () => {
|
||||||
|
it('should normalize the given CSS property to camelCase', () => {
|
||||||
|
expect(registry.normalizeAnimationStyleProperty('border-radius')).toBe('borderRadius');
|
||||||
|
expect(registry.normalizeAnimationStyleProperty('zIndex')).toBe('zIndex');
|
||||||
|
expect(registry.normalizeAnimationStyleProperty('-webkit-animation'))
|
||||||
|
.toBe('WebkitAnimation');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('normalizeAnimationStyleValue', () => {
|
||||||
|
it('should normalize the given dimensional CSS style value to contain a PX value when numeric',
|
||||||
|
() => {
|
||||||
|
expect(
|
||||||
|
registry.normalizeAnimationStyleValue('borderRadius', 'border-radius', 10)['value'])
|
||||||
|
.toBe('10px');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should not normalize any values that are of zero', () => {
|
||||||
|
expect(registry.normalizeAnimationStyleValue('opacity', 'opacity', 0)['value']).toBe('0');
|
||||||
|
expect(registry.normalizeAnimationStyleValue('width', 'width', 0)['value']).toBe('0');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should retain the given dimensional CSS style value\'s unit if it already exists', () => {
|
||||||
|
expect(
|
||||||
|
registry.normalizeAnimationStyleValue('borderRadius', 'border-radius', '10em')['value'])
|
||||||
|
.toBe('10em');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should trim the provided CSS style value', () => {
|
||||||
|
expect(registry.normalizeAnimationStyleValue('color', 'color', ' red ')['value'])
|
||||||
|
.toBe('red');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should stringify all non dimensional numeric style values', () => {
|
||||||
|
expect(registry.normalizeAnimationStyleValue('zIndex', 'zIndex', 10)['value']).toBe('10');
|
||||||
|
expect(registry.normalizeAnimationStyleValue('opacity', 'opacity', 0.5)['value'])
|
||||||
|
.toBe('0.5');
|
||||||
|
});
|
||||||
|
});
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,4 +54,10 @@ export class MockSchemaRegistry implements ElementSchemaRegistry {
|
||||||
return {error: false};
|
return {error: false};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
normalizeAnimationStyleProperty(propName: string): string { return propName; }
|
||||||
|
normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string|number):
|
||||||
|
{error: string, value: string} {
|
||||||
|
return {error: null, value: val.toString()};
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,6 +7,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {CommonModule} from '@angular/common';
|
import {CommonModule} from '@angular/common';
|
||||||
|
import {DomElementSchemaRegistry, ElementSchemaRegistry} from '@angular/compiler';
|
||||||
import {AnimationDriver} from '@angular/platform-browser/src/dom/animation_driver';
|
import {AnimationDriver} from '@angular/platform-browser/src/dom/animation_driver';
|
||||||
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter';
|
||||||
import {MockAnimationDriver} from '@angular/platform-browser/testing/mock_animation_driver';
|
import {MockAnimationDriver} from '@angular/platform-browser/testing/mock_animation_driver';
|
||||||
|
@ -52,7 +53,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
'myAnimation',
|
'myAnimation',
|
||||||
[transition(
|
[transition(
|
||||||
'void => *',
|
'void => *',
|
||||||
[style({'opacity': 0}), animate(500, style({'opacity': 1}))])])]
|
[style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -68,8 +69,8 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
|
|
||||||
var keyframes2 = driver.log[0]['keyframeLookup'];
|
var keyframes2 = driver.log[0]['keyframeLookup'];
|
||||||
expect(keyframes2.length).toEqual(2);
|
expect(keyframes2.length).toEqual(2);
|
||||||
expect(keyframes2[0]).toEqual([0, {'opacity': 0}]);
|
expect(keyframes2[0]).toEqual([0, {'opacity': '0'}]);
|
||||||
expect(keyframes2[1]).toEqual([1, {'opacity': 1}]);
|
expect(keyframes2[1]).toEqual([1, {'opacity': '1'}]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should trigger a state change animation from state => void', fakeAsync(() => {
|
it('should trigger a state change animation from state => void', fakeAsync(() => {
|
||||||
|
@ -82,7 +83,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
'myAnimation',
|
'myAnimation',
|
||||||
[transition(
|
[transition(
|
||||||
'* => void',
|
'* => void',
|
||||||
[style({'opacity': 1}), animate(500, style({'opacity': 0}))])])]
|
[style({'opacity': '1'}), animate(500, style({'opacity': '0'}))])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -102,11 +103,13 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
|
|
||||||
var keyframes2 = driver.log[0]['keyframeLookup'];
|
var keyframes2 = driver.log[0]['keyframeLookup'];
|
||||||
expect(keyframes2.length).toEqual(2);
|
expect(keyframes2.length).toEqual(2);
|
||||||
expect(keyframes2[0]).toEqual([0, {'opacity': 1}]);
|
expect(keyframes2[0]).toEqual([0, {'opacity': '1'}]);
|
||||||
expect(keyframes2[1]).toEqual([1, {'opacity': 0}]);
|
expect(keyframes2[1]).toEqual([1, {'opacity': '0'}]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should animate the element when the expression changes between states', fakeAsync(() => {
|
it('should animate the element when the expression changes between states',
|
||||||
|
fakeAsync(
|
||||||
|
() => {
|
||||||
TestBed.overrideComponent(DummyIfCmp, {
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
set: {
|
set: {
|
||||||
template: `
|
template: `
|
||||||
|
@ -115,8 +118,8 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
animations: [
|
animations: [
|
||||||
trigger('myAnimation', [
|
trigger('myAnimation', [
|
||||||
transition('* => state1', [
|
transition('* => state1', [
|
||||||
style({'background': 'red'}),
|
style({'backgroundColor': 'red'}),
|
||||||
animate('0.5s 1s ease-out', style({'background': 'blue'}))
|
animate('0.5s 1s ease-out', style({'backgroundColor': 'blue'}))
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
|
@ -139,11 +142,11 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
expect(animation1['easing']).toEqual('ease-out');
|
expect(animation1['easing']).toEqual('ease-out');
|
||||||
|
|
||||||
var startingStyles = animation1['startingStyles'];
|
var startingStyles = animation1['startingStyles'];
|
||||||
expect(startingStyles).toEqual({'background': 'red'});
|
expect(startingStyles).toEqual({'backgroundColor': 'red'});
|
||||||
|
|
||||||
var kf = animation1['keyframeLookup'];
|
var kf = animation1['keyframeLookup'];
|
||||||
expect(kf[0]).toEqual([0, {'background': 'red'}]);
|
expect(kf[0]).toEqual([0, {'backgroundColor': 'red'}]);
|
||||||
expect(kf[1]).toEqual([1, {'background': 'blue'}]);
|
expect(kf[1]).toEqual([1, {'backgroundColor': 'blue'}]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
describe('animation aliases', () => {
|
describe('animation aliases', () => {
|
||||||
|
@ -154,10 +157,12 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
<div *ngIf="exp" [@myAnimation]="exp"></div>
|
<div *ngIf="exp" [@myAnimation]="exp"></div>
|
||||||
`,
|
`,
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'myAnimation',
|
'myAnimation', [transition(
|
||||||
[transition(
|
|
||||||
':enter',
|
':enter',
|
||||||
[style({'opacity': 0}), animate('500ms', style({opacity: 1}))])])]
|
[
|
||||||
|
style({'opacity': '0'}),
|
||||||
|
animate('500ms', style({'opacity': '1'}))
|
||||||
|
])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -181,7 +186,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
`,
|
`,
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'myAnimation',
|
'myAnimation',
|
||||||
[transition(':leave', [animate('999ms', style({opacity: 0}))])])]
|
[transition(':leave', [animate('999ms', style({'opacity': '0'}))])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -211,7 +216,8 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
`,
|
`,
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'myAnimation',
|
'myAnimation',
|
||||||
[transition(':dont_leave_me', [animate('444ms', style({opacity: 0}))])])]
|
[transition(
|
||||||
|
':dont_leave_me', [animate('444ms', style({'opacity': '0'}))])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -265,6 +271,135 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
expect(result['keyframeLookup']).toEqual([[0, {'opacity': '1'}], [1, {'opacity': '0'}]]);
|
expect(result['keyframeLookup']).toEqual([[0, {'opacity': '1'}], [1, {'opacity': '0'}]]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
describe('schema normalization', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
|
set: {
|
||||||
|
template: `<div [@myAnimation] *ngIf="exp"></div>`,
|
||||||
|
animations: [trigger(
|
||||||
|
'myAnimation',
|
||||||
|
[
|
||||||
|
state('*', style({'border-width': '10px', 'height': 111})),
|
||||||
|
state('void', style({'z-index': '20'})),
|
||||||
|
transition('* => *', [
|
||||||
|
style({ height: '200px ', '-webkit-border-radius': '10px' }),
|
||||||
|
animate('500ms')
|
||||||
|
])
|
||||||
|
])]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('via DomElementSchemaRegistry', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule({
|
||||||
|
providers: [{provide: ElementSchemaRegistry, useClass: DomElementSchemaRegistry}]
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should normalize all CSS style properties to camelCase during compile time if a DOM schema is used',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const driver = TestBed.get(AnimationDriver) as MockAnimationDriver;
|
||||||
|
let fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
var cmp = fixture.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
var result = driver.log.pop();
|
||||||
|
var styleProps1 = Object.keys(result['keyframeLookup'][0][1]).sort();
|
||||||
|
var styleProps2 = Object.keys(result['keyframeLookup'][1][1]).sort();
|
||||||
|
|
||||||
|
var expectedProps = ['WebkitBorderRadius', 'borderWidth', 'height', 'zIndex'];
|
||||||
|
expect(styleProps1)
|
||||||
|
.toEqual(expectedProps); // the start/end styles are always balanced
|
||||||
|
expect(styleProps2).toEqual(expectedProps);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should normalize all dimensional CSS style values to `px` values if the value is a number',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const driver = TestBed.get(AnimationDriver) as MockAnimationDriver;
|
||||||
|
let fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
var cmp = fixture.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
var result = driver.log.pop();
|
||||||
|
|
||||||
|
var styleVals1 = result['keyframeLookup'][0][1];
|
||||||
|
expect(styleVals1['zIndex']).toEqual('20');
|
||||||
|
expect(styleVals1['height']).toEqual('200px');
|
||||||
|
|
||||||
|
var styleVals2 = result['keyframeLookup'][1][1];
|
||||||
|
expect(styleVals2['borderWidth']).toEqual('10px');
|
||||||
|
expect(styleVals2['height']).toEqual('111px');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should throw an error when a string-based dimensional style value is used that does not contain a unit value is detected',
|
||||||
|
fakeAsync(() => {
|
||||||
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
|
set: {
|
||||||
|
template: `<div [@myAnimation] *ngIf="exp"></div>`,
|
||||||
|
animations: [trigger(
|
||||||
|
'myAnimation',
|
||||||
|
[state('*', style({width: '123'})), transition('* => *', animate(500))])]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(() => {
|
||||||
|
TestBed.createComponent(DummyIfCmp);
|
||||||
|
}).toThrowError(/Please provide a CSS unit value for width:123/);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('not using DomElementSchemaRegistry', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
TestBed.configureTestingModule(
|
||||||
|
{providers: [{provide: ElementSchemaRegistry, useClass: _NaiveElementSchema}]});
|
||||||
|
|
||||||
|
it('should not normalize any CSS style properties to camelCase during compile time if a DOM schema is used',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const driver = TestBed.get(AnimationDriver) as MockAnimationDriver;
|
||||||
|
let fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
var cmp = fixture.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
var result = driver.log.pop();
|
||||||
|
var styleProps1 = Object.keys(result['keyframeLookup'][0][1]).sort();
|
||||||
|
var styleProps2 = Object.keys(result['keyframeLookup'][1][1]).sort();
|
||||||
|
|
||||||
|
var expectedProps = ['-webkit-border-radius', 'border-width', 'height', 'z-index'];
|
||||||
|
expect(styleProps1)
|
||||||
|
.toEqual(expectedProps); // the start/end styles are always balanced
|
||||||
|
expect(styleProps2).toEqual(expectedProps);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should not normalize nay dimensional CSS style values to `px` values if the value is a number',
|
||||||
|
fakeAsync(() => {
|
||||||
|
const driver = TestBed.get(AnimationDriver) as MockAnimationDriver;
|
||||||
|
let fixture = TestBed.createComponent(DummyIfCmp);
|
||||||
|
var cmp = fixture.componentInstance;
|
||||||
|
cmp.exp = true;
|
||||||
|
fixture.detectChanges();
|
||||||
|
flushMicrotasks();
|
||||||
|
|
||||||
|
var result = driver.log.pop();
|
||||||
|
|
||||||
|
var styleVals1 = result['keyframeLookup'][0][1];
|
||||||
|
expect(styleVals1['z-index']).toEqual('20');
|
||||||
|
expect(styleVals1['height']).toEqual('200px');
|
||||||
|
|
||||||
|
var styleVals2 = result['keyframeLookup'][1][1];
|
||||||
|
expect(styleVals2['border-width']).toEqual('10px');
|
||||||
|
expect(styleVals2['height']).toEqual(111);
|
||||||
|
}));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
it('should combine repeated style steps into a single step', fakeAsync(() => {
|
it('should combine repeated style steps into a single step', fakeAsync(() => {
|
||||||
TestBed.overrideComponent(DummyIfCmp, {
|
TestBed.overrideComponent(DummyIfCmp, {
|
||||||
set: {
|
set: {
|
||||||
|
@ -277,11 +412,11 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
style({'background': 'red'}),
|
style({'background': 'red'}),
|
||||||
style({'width': '100px'}),
|
style({'width': '100px'}),
|
||||||
style({'background': 'gold'}),
|
style({'background': 'gold'}),
|
||||||
style({'height': 111}),
|
style({'height': '111px'}),
|
||||||
animate('999ms', style({'width': '200px', 'background': 'blue'})),
|
animate('999ms', style({'width': '200px', 'background': 'blue'})),
|
||||||
style({'opacity': '1'}),
|
style({'opacity': '1'}),
|
||||||
style({'border-width': '100px'}),
|
style({'borderWidth': '100px'}),
|
||||||
animate('999ms', style({'opacity': '0', 'height': '200px', 'border-width': '10px'}))
|
animate('999ms', style({'opacity': '0', 'height': '200px', 'borderWidth': '10px'}))
|
||||||
])
|
])
|
||||||
])
|
])
|
||||||
]
|
]
|
||||||
|
@ -303,7 +438,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
expect(animation1['delay']).toEqual(0);
|
expect(animation1['delay']).toEqual(0);
|
||||||
expect(animation1['easing']).toEqual(null);
|
expect(animation1['easing']).toEqual(null);
|
||||||
expect(animation1['startingStyles'])
|
expect(animation1['startingStyles'])
|
||||||
.toEqual({'background': 'gold', 'width': '100px', 'height': 111});
|
.toEqual({'background': 'gold', 'width': '100px', 'height': '111px'});
|
||||||
|
|
||||||
var keyframes1 = animation1['keyframeLookup'];
|
var keyframes1 = animation1['keyframeLookup'];
|
||||||
expect(keyframes1[0]).toEqual([0, {'background': 'gold', 'width': '100px'}]);
|
expect(keyframes1[0]).toEqual([0, {'background': 'gold', 'width': '100px'}]);
|
||||||
|
@ -313,14 +448,14 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
expect(animation2['duration']).toEqual(999);
|
expect(animation2['duration']).toEqual(999);
|
||||||
expect(animation2['delay']).toEqual(0);
|
expect(animation2['delay']).toEqual(0);
|
||||||
expect(animation2['easing']).toEqual(null);
|
expect(animation2['easing']).toEqual(null);
|
||||||
expect(animation2['startingStyles']).toEqual({'opacity': '1', 'border-width': '100px'});
|
expect(animation2['startingStyles']).toEqual({'opacity': '1', 'borderWidth': '100px'});
|
||||||
|
|
||||||
var keyframes2 = animation2['keyframeLookup'];
|
var keyframes2 = animation2['keyframeLookup'];
|
||||||
expect(keyframes2[0]).toEqual([
|
expect(keyframes2[0]).toEqual([
|
||||||
0, {'opacity': '1', 'height': 111, 'border-width': '100px'}
|
0, {'opacity': '1', 'height': '111px', 'borderWidth': '100px'}
|
||||||
]);
|
]);
|
||||||
expect(keyframes2[1]).toEqual([
|
expect(keyframes2[1]).toEqual([
|
||||||
1, {'opacity': '0', 'height': '200px', 'border-width': '10px'}
|
1, {'opacity': '0', 'height': '200px', 'borderWidth': '10px'}
|
||||||
]);
|
]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
@ -515,10 +650,10 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
|
|
||||||
var kf = driver.log[0]['keyframeLookup'];
|
var kf = driver.log[0]['keyframeLookup'];
|
||||||
expect(kf.length).toEqual(4);
|
expect(kf.length).toEqual(4);
|
||||||
expect(kf[0]).toEqual([0, {'width': 0}]);
|
expect(kf[0]).toEqual([0, {'width': '0'}]);
|
||||||
expect(kf[1]).toEqual([0.25, {'width': 100}]);
|
expect(kf[1]).toEqual([0.25, {'width': '100px'}]);
|
||||||
expect(kf[2]).toEqual([0.75, {'width': 200}]);
|
expect(kf[2]).toEqual([0.75, {'width': '200px'}]);
|
||||||
expect(kf[3]).toEqual([1, {'width': 300}]);
|
expect(kf[3]).toEqual([1, {'width': '300px'}]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should fetch any keyframe styles that are not defined in the first keyframe from the previous entries or getCompuedStyle',
|
it('should fetch any keyframe styles that are not defined in the first keyframe from the previous entries or getCompuedStyle',
|
||||||
|
@ -535,7 +670,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
animate(1000, style({'color': 'silver'})),
|
animate(1000, style({'color': 'silver'})),
|
||||||
animate(1000, keyframes([
|
animate(1000, keyframes([
|
||||||
style([{'color': 'gold', offset: 0.25}]),
|
style([{'color': 'gold', offset: 0.25}]),
|
||||||
style([{'color': 'bronze', 'background-color': 'teal', offset: 0.50}]),
|
style([{'color': 'bronze', 'backgroundColor': 'teal', offset: 0.50}]),
|
||||||
style([{'color': 'platinum', offset: 0.75}]),
|
style([{'color': 'platinum', offset: 0.75}]),
|
||||||
style([{'color': 'diamond', offset: 1}])
|
style([{'color': 'diamond', offset: 1}])
|
||||||
]))
|
]))
|
||||||
|
@ -554,11 +689,11 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
|
|
||||||
var kf = driver.log[1]['keyframeLookup'];
|
var kf = driver.log[1]['keyframeLookup'];
|
||||||
expect(kf.length).toEqual(5);
|
expect(kf.length).toEqual(5);
|
||||||
expect(kf[0]).toEqual([0, {'color': 'silver', 'background-color': AUTO_STYLE}]);
|
expect(kf[0]).toEqual([0, {'color': 'silver', 'backgroundColor': AUTO_STYLE}]);
|
||||||
expect(kf[1]).toEqual([0.25, {'color': 'gold'}]);
|
expect(kf[1]).toEqual([0.25, {'color': 'gold'}]);
|
||||||
expect(kf[2]).toEqual([0.50, {'color': 'bronze', 'background-color': 'teal'}]);
|
expect(kf[2]).toEqual([0.50, {'color': 'bronze', 'backgroundColor': 'teal'}]);
|
||||||
expect(kf[3]).toEqual([0.75, {'color': 'platinum'}]);
|
expect(kf[3]).toEqual([0.75, {'color': 'platinum'}]);
|
||||||
expect(kf[4]).toEqual([1, {'color': 'diamond', 'background-color': 'teal'}]);
|
expect(kf[4]).toEqual([1, {'color': 'diamond', 'backgroundColor': 'teal'}]);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -573,7 +708,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
'myAnimation',
|
'myAnimation',
|
||||||
[transition(
|
[transition(
|
||||||
'* => *',
|
'* => *',
|
||||||
[style({'opacity': 0}), animate(500, style({'opacity': 1}))])])]
|
[style({'opacity': '0'}), animate(500, style({'opacity': '1'}))])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -607,7 +742,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
animations: [
|
animations: [
|
||||||
trigger('myAnimation', [
|
trigger('myAnimation', [
|
||||||
transition('void => *', [
|
transition('void => *', [
|
||||||
style({'background': 'red', 'opacity': 0.5}),
|
style({'background': 'red', 'opacity': '0.5'}),
|
||||||
animate(500, style({'background': 'black'})),
|
animate(500, style({'background': 'black'})),
|
||||||
group([
|
group([
|
||||||
animate(500, style({'background': 'black'})),
|
animate(500, style({'background': 'black'})),
|
||||||
|
@ -714,7 +849,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
`,
|
`,
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'myAnimation',
|
'myAnimation',
|
||||||
[transition('* => void', [animate(1000, style({'opacity': 0}))])])]
|
[transition('* => void', [animate(1000, style({'opacity': '0'}))])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -750,8 +885,8 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
[trigger('myAnimation', [transition(
|
[trigger('myAnimation', [transition(
|
||||||
'* => *',
|
'* => *',
|
||||||
[
|
[
|
||||||
animate(1000, style({'opacity': 0})),
|
animate(1000, style({'opacity': '0'})),
|
||||||
animate(1000, style({'opacity': 1}))
|
animate(1000, style({'opacity': '1'}))
|
||||||
])])]
|
])])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -766,12 +901,12 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
var animation1 = driver.log[0];
|
var animation1 = driver.log[0];
|
||||||
var keyframes1 = animation1['keyframeLookup'];
|
var keyframes1 = animation1['keyframeLookup'];
|
||||||
expect(keyframes1[0]).toEqual([0, {'opacity': AUTO_STYLE}]);
|
expect(keyframes1[0]).toEqual([0, {'opacity': AUTO_STYLE}]);
|
||||||
expect(keyframes1[1]).toEqual([1, {'opacity': 0}]);
|
expect(keyframes1[1]).toEqual([1, {'opacity': '0'}]);
|
||||||
|
|
||||||
var animation2 = driver.log[1];
|
var animation2 = driver.log[1];
|
||||||
var keyframes2 = animation2['keyframeLookup'];
|
var keyframes2 = animation2['keyframeLookup'];
|
||||||
expect(keyframes2[0]).toEqual([0, {'opacity': 0}]);
|
expect(keyframes2[0]).toEqual([0, {'opacity': '0'}]);
|
||||||
expect(keyframes2[1]).toEqual([1, {'opacity': 1}]);
|
expect(keyframes2[1]).toEqual([1, {'opacity': '1'}]);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
it('should perform two transitions in parallel if defined in different state triggers',
|
it('should perform two transitions in parallel if defined in different state triggers',
|
||||||
|
@ -783,9 +918,10 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
`,
|
`,
|
||||||
animations: [
|
animations: [
|
||||||
trigger(
|
trigger(
|
||||||
'one', [transition(
|
'one',
|
||||||
|
[transition(
|
||||||
'state1 => state2',
|
'state1 => state2',
|
||||||
[style({'opacity': 0}), animate(1000, style({'opacity': 1}))])]),
|
[style({'opacity': '0'}), animate(1000, style({'opacity': '1'}))])]),
|
||||||
trigger(
|
trigger(
|
||||||
'two',
|
'two',
|
||||||
[transition(
|
[transition(
|
||||||
|
@ -1661,8 +1797,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'status',
|
'status',
|
||||||
[
|
[
|
||||||
state('final', style({'top': '100px'})),
|
state('final', style({'top': 100})), transition('* => final', [animate(1000)])
|
||||||
transition('* => final', [animate(1000)])
|
|
||||||
])]
|
])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1778,8 +1913,8 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'status',
|
'status',
|
||||||
[
|
[
|
||||||
state('void', style({'width': '0px'})),
|
state('void', style({'width': 0})),
|
||||||
state('final', style({'width': '100px'})),
|
state('final', style({'width': 100})),
|
||||||
])]
|
])]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
@ -1909,7 +2044,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
animations: [trigger(
|
animations: [trigger(
|
||||||
'status',
|
'status',
|
||||||
[
|
[
|
||||||
state('void', style({'height': '100px', 'opacity': 0})),
|
state('void', style({'height': '100px', 'opacity': '0'})),
|
||||||
state('final', style({'height': '333px', 'width': '200px'})),
|
state('final', style({'height': '333px', 'width': '200px'})),
|
||||||
transition('void => final', [animate(1000)])
|
transition('void => final', [animate(1000)])
|
||||||
])]
|
])]
|
||||||
|
@ -1927,7 +2062,7 @@ function declareTests({useJit}: {useJit: boolean}) {
|
||||||
var animation = driver.log.pop();
|
var animation = driver.log.pop();
|
||||||
var kf = animation['keyframeLookup'];
|
var kf = animation['keyframeLookup'];
|
||||||
|
|
||||||
expect(kf[0]).toEqual([0, {'height': '100px', 'opacity': 0, 'width': AUTO_STYLE}]);
|
expect(kf[0]).toEqual([0, {'height': '100px', 'opacity': '0', 'width': AUTO_STYLE}]);
|
||||||
|
|
||||||
expect(kf[1]).toEqual([1, {'height': '333px', 'opacity': AUTO_STYLE, 'width': '200px'}]);
|
expect(kf[1]).toEqual([1, {'height': '333px', 'opacity': AUTO_STYLE, 'width': '200px'}]);
|
||||||
});
|
});
|
||||||
|
@ -2006,3 +2141,12 @@ class BrokenDummyLoadingCmp {
|
||||||
exp = false;
|
exp = false;
|
||||||
callback = () => {};
|
callback = () => {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class _NaiveElementSchema extends DomElementSchemaRegistry {
|
||||||
|
normalizeAnimationStyleProperty(propName: string): string { return propName; }
|
||||||
|
|
||||||
|
normalizeAnimationStyleValue(camelCaseProp: string, userProvidedProp: string, val: string|number):
|
||||||
|
{error: string, value: string} {
|
||||||
|
return {error: null, value: <string>val};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -6,13 +6,10 @@
|
||||||
* found in the LICENSE file at https://angular.io/license
|
* found in the LICENSE file at https://angular.io/license
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AUTO_STYLE} from '@angular/core';
|
|
||||||
|
|
||||||
import {isPresent} from '../facade/lang';
|
import {isPresent} from '../facade/lang';
|
||||||
import {AnimationKeyframe, AnimationStyles} from '../private_import_core';
|
import {AnimationKeyframe, AnimationStyles} from '../private_import_core';
|
||||||
|
|
||||||
import {AnimationDriver} from './animation_driver';
|
import {AnimationDriver} from './animation_driver';
|
||||||
import {dashCaseToCamelCase} from './util';
|
|
||||||
import {WebAnimationsPlayer} from './web_animations_player';
|
import {WebAnimationsPlayer} from './web_animations_player';
|
||||||
|
|
||||||
export class WebAnimationsDriver implements AnimationDriver {
|
export class WebAnimationsDriver implements AnimationDriver {
|
||||||
|
@ -63,14 +60,8 @@ function _populateStyles(
|
||||||
element: any, styles: AnimationStyles,
|
element: any, styles: AnimationStyles,
|
||||||
defaultStyles: {[key: string]: string | number}): {[key: string]: string | number} {
|
defaultStyles: {[key: string]: string | number}): {[key: string]: string | number} {
|
||||||
var data: {[key: string]: string | number} = {};
|
var data: {[key: string]: string | number} = {};
|
||||||
styles.styles.forEach((entry) => {
|
styles.styles.forEach(
|
||||||
Object.keys(entry).forEach(prop => {
|
(entry) => { Object.keys(entry).forEach(prop => { data[prop] = entry[prop]; }); });
|
||||||
const val = entry[prop];
|
|
||||||
var formattedProp = dashCaseToCamelCase(prop);
|
|
||||||
data[formattedProp] =
|
|
||||||
val == AUTO_STYLE ? val : val.toString() + _resolveStyleUnit(val, prop, formattedProp);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
Object.keys(defaultStyles).forEach(prop => {
|
Object.keys(defaultStyles).forEach(prop => {
|
||||||
if (!isPresent(data[prop])) {
|
if (!isPresent(data[prop])) {
|
||||||
data[prop] = defaultStyles[prop];
|
data[prop] = defaultStyles[prop];
|
||||||
|
@ -78,66 +69,3 @@ function _populateStyles(
|
||||||
});
|
});
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|
||||||
function _resolveStyleUnit(
|
|
||||||
val: string | number, userProvidedProp: string, formattedProp: string): string {
|
|
||||||
var unit = '';
|
|
||||||
if (_isPixelDimensionStyle(formattedProp) && val != 0 && val != '0') {
|
|
||||||
if (typeof val === 'number') {
|
|
||||||
unit = 'px';
|
|
||||||
} else if (_findDimensionalSuffix(val.toString()).length == 0) {
|
|
||||||
throw new Error('Please provide a CSS unit value for ' + userProvidedProp + ':' + val);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return unit;
|
|
||||||
}
|
|
||||||
|
|
||||||
const _$0 = 48;
|
|
||||||
const _$9 = 57;
|
|
||||||
const _$PERIOD = 46;
|
|
||||||
|
|
||||||
function _findDimensionalSuffix(value: string): string {
|
|
||||||
for (var i = 0; i < value.length; i++) {
|
|
||||||
var c = value.charCodeAt(i);
|
|
||||||
if ((c >= _$0 && c <= _$9) || c == _$PERIOD) continue;
|
|
||||||
return value.substring(i, value.length);
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
}
|
|
||||||
|
|
||||||
function _isPixelDimensionStyle(prop: string): boolean {
|
|
||||||
switch (prop) {
|
|
||||||
case 'width':
|
|
||||||
case 'height':
|
|
||||||
case 'minWidth':
|
|
||||||
case 'minHeight':
|
|
||||||
case 'maxWidth':
|
|
||||||
case 'maxHeight':
|
|
||||||
case 'left':
|
|
||||||
case 'top':
|
|
||||||
case 'bottom':
|
|
||||||
case 'right':
|
|
||||||
case 'fontSize':
|
|
||||||
case 'outlineWidth':
|
|
||||||
case 'outlineOffset':
|
|
||||||
case 'paddingTop':
|
|
||||||
case 'paddingLeft':
|
|
||||||
case 'paddingBottom':
|
|
||||||
case 'paddingRight':
|
|
||||||
case 'marginTop':
|
|
||||||
case 'marginLeft':
|
|
||||||
case 'marginBottom':
|
|
||||||
case 'marginRight':
|
|
||||||
case 'borderRadius':
|
|
||||||
case 'borderWidth':
|
|
||||||
case 'borderTopWidth':
|
|
||||||
case 'borderLeftWidth':
|
|
||||||
case 'borderRightWidth':
|
|
||||||
case 'borderBottomWidth':
|
|
||||||
case 'textIndent':
|
|
||||||
return true;
|
|
||||||
|
|
||||||
default:
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
|
@ -45,46 +45,6 @@ export function main() {
|
||||||
elm = el('<div></div>');
|
elm = el('<div></div>');
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should convert all styles to camelcase', () => {
|
|
||||||
var startingStyles = _makeStyles({'border-top-right': '40px'});
|
|
||||||
var styles = [
|
|
||||||
_makeKeyframe(0, {'max-width': '100px', 'height': '200px'}),
|
|
||||||
_makeKeyframe(1, {'font-size': '555px'})
|
|
||||||
];
|
|
||||||
|
|
||||||
var player = driver.animate(elm, startingStyles, styles, 0, 0, 'linear');
|
|
||||||
var details = _formatOptions(player);
|
|
||||||
var startKeyframe = details['keyframes'][0];
|
|
||||||
var firstKeyframe = details['keyframes'][1];
|
|
||||||
var lastKeyframe = details['keyframes'][2];
|
|
||||||
|
|
||||||
expect(startKeyframe['borderTopRight']).toEqual('40px');
|
|
||||||
|
|
||||||
expect(firstKeyframe['maxWidth']).toEqual('100px');
|
|
||||||
expect(firstKeyframe['max-width']).toBeFalsy();
|
|
||||||
expect(firstKeyframe['height']).toEqual('200px');
|
|
||||||
|
|
||||||
expect(lastKeyframe['fontSize']).toEqual('555px');
|
|
||||||
expect(lastKeyframe['font-size']).toBeFalsy();
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should auto prefix numeric properties with a `px` value', () => {
|
|
||||||
var startingStyles = _makeStyles({'borderTopWidth': 40});
|
|
||||||
var styles = [_makeKeyframe(0, {'font-size': 100}), _makeKeyframe(1, {'height': '555em'})];
|
|
||||||
|
|
||||||
var player = driver.animate(elm, startingStyles, styles, 0, 0, 'linear');
|
|
||||||
var details = _formatOptions(player);
|
|
||||||
var startKeyframe = details['keyframes'][0];
|
|
||||||
var firstKeyframe = details['keyframes'][1];
|
|
||||||
var lastKeyframe = details['keyframes'][2];
|
|
||||||
|
|
||||||
expect(startKeyframe['borderTopWidth']).toEqual('40px');
|
|
||||||
|
|
||||||
expect(firstKeyframe['fontSize']).toEqual('100px');
|
|
||||||
|
|
||||||
expect(lastKeyframe['height']).toEqual('555em');
|
|
||||||
});
|
|
||||||
|
|
||||||
it('should use a fill mode of `both`', () => {
|
it('should use a fill mode of `both`', () => {
|
||||||
var startingStyles = _makeStyles({});
|
var startingStyles = _makeStyles({});
|
||||||
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
var styles = [_makeKeyframe(0, {'color': 'green'}), _makeKeyframe(1, {'color': 'red'})];
|
||||||
|
|
Loading…
Reference in New Issue