diff --git a/modules/@angular/compiler-cli/integrationtest/src/animate.ts b/modules/@angular/compiler-cli/integrationtest/src/animate.ts new file mode 100644 index 0000000000..2044716dd1 --- /dev/null +++ b/modules/@angular/compiler-cli/integrationtest/src/animate.ts @@ -0,0 +1,34 @@ +import {Component, trigger, state, animate, transition, style} from '@angular/core'; + +@Component({ + selector: "animate-cmp", + animations: [ + trigger('openClose', [ + state('closed, void', + style({ height:"0px", color: "maroon", borderColor: "maroon" })), + state('open', + style({ height:"*", borderColor:"green", color:"green" })), + transition("* => *", animate(500)) + ]) + ], + template: ` + + +
+
+ Look at this box +
+ ` +}) +export class AnimateCmp { + stateExpression:string; + constructor() { + this.setAsClosed(); + } + setAsOpen() { + this.stateExpression = 'open'; + } + setAsClosed() { + this.stateExpression = 'closed'; + } +} diff --git a/modules/@angular/compiler-cli/integrationtest/test/animate_spec.ts b/modules/@angular/compiler-cli/integrationtest/test/animate_spec.ts new file mode 100644 index 0000000000..8c2f8ed1ca --- /dev/null +++ b/modules/@angular/compiler-cli/integrationtest/test/animate_spec.ts @@ -0,0 +1,40 @@ +require('reflect-metadata'); +require('@angular/platform-server/src/parse5_adapter.js').Parse5DomAdapter.makeCurrent(); +require('zone.js/dist/zone-node.js'); +require('zone.js/dist/long-stack-trace-zone.js'); + +import {AnimateCmpNgFactory} from '../src/animate.ngfactory'; +import {AUTO_STYLE, ReflectiveInjector, DebugElement, getDebugNode} from '@angular/core'; +import {browserPlatform, BROWSER_APP_PROVIDERS} from '@angular/platform-browser'; + +describe("template codegen output", () => { + it("should apply the animate states to the element", (done) => { + const appInjector = ReflectiveInjector.resolveAndCreate(BROWSER_APP_PROVIDERS, + browserPlatform().injector); + var comp = AnimateCmpNgFactory.create(appInjector); + var debugElement = getDebugNode(comp.location.nativeElement); + + // the open-close-container is a child of the main container + // if the template changes then please update the location below + var targetDebugElement = debugElement.children[3]; + + comp.instance.setAsOpen(); + comp.changeDetectorRef.detectChanges(); + + setTimeout(() => { + expect(targetDebugElement.styles['height']).toEqual(AUTO_STYLE); + expect(targetDebugElement.styles['borderColor']).toEqual('green'); + expect(targetDebugElement.styles['color']).toEqual('green'); + + comp.instance.setAsClosed(); + comp.changeDetectorRef.detectChanges(); + + setTimeout(() => { + expect(targetDebugElement.styles['height']).toEqual("0px"); + expect(targetDebugElement.styles['borderColor']).toEqual('maroon'); + expect(targetDebugElement.styles['color']).toEqual('maroon'); + done(); + }, 0); + }, 0); + }); +}); diff --git a/modules/@angular/compiler-cli/src/reflector_host.ts b/modules/@angular/compiler-cli/src/reflector_host.ts index 35b5c1aaf8..69bcb4bfbe 100644 --- a/modules/@angular/compiler-cli/src/reflector_host.ts +++ b/modules/@angular/compiler-cli/src/reflector_host.ts @@ -19,6 +19,7 @@ export class NodeReflectorHost implements StaticReflectorHost, ImportGenerator { coreDecorators: '@angular/core/src/metadata', diDecorators: '@angular/core/src/di/decorators', diMetadata: '@angular/core/src/di/metadata', + animationMetadata: '@angular/core/src/animation/metadata', provider: '@angular/core/src/di/provider' }; } diff --git a/modules/@angular/compiler-cli/src/static_reflector.ts b/modules/@angular/compiler-cli/src/static_reflector.ts index cb9e61c32f..f5ec5cd615 100644 --- a/modules/@angular/compiler-cli/src/static_reflector.ts +++ b/modules/@angular/compiler-cli/src/static_reflector.ts @@ -20,6 +20,14 @@ import { SelfMetadata, SkipSelfMetadata, InjectMetadata, + trigger, + state, + transition, + sequence, + group, + animate, + style, + keyframes } from "@angular/core"; import {ReflectorReader} from "./core_private"; @@ -50,7 +58,7 @@ export interface StaticReflectorHost { getStaticSymbol(declarationFile: string, name: string): StaticSymbol; angularImportLocations(): - {coreDecorators: string, diDecorators: string, diMetadata: string, provider: string}; + {coreDecorators: string, diDecorators: string, diMetadata: string, animationMetadata: string, provider: string}; } /** @@ -181,8 +189,19 @@ export class StaticReflector implements ReflectorReader { }); } + private registerFunction(type: StaticSymbol, fn: any): void { + this.conversionMap.set(type, (context: StaticSymbol, args: any[]) => { + let argValues: any[] = []; + args.forEach((arg, index) => { + let argValue = this.simplify(context, arg); + argValues.push(argValue); + }); + return fn.apply(null, argValues); + }); + } + private initializeConversionMap(): void { - const {coreDecorators, diDecorators, diMetadata, provider} = this.host.angularImportLocations(); + const {coreDecorators, diDecorators, diMetadata, animationMetadata, provider} = this.host.angularImportLocations(); this.registerDecoratorOrConstructor(this.host.findDeclaration(provider, 'Provider'), Provider); this.registerDecoratorOrConstructor(this.host.findDeclaration(diDecorators, 'Host'), @@ -235,6 +254,15 @@ export class StaticReflector implements ReflectorReader { SkipSelfMetadata); this.registerDecoratorOrConstructor(this.host.findDeclaration(diMetadata, 'OptionalMetadata'), OptionalMetadata); + + this.registerFunction(this.host.findDeclaration(animationMetadata, 'trigger'), trigger); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'state'), state); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'transition'), transition); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'style'), style); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'animate'), animate); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'keyframes'), keyframes); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'sequence'), sequence); + this.registerFunction(this.host.findDeclaration(animationMetadata, 'group'), group); } /** @internal */ diff --git a/modules/@angular/compiler-cli/test/static_reflector_spec.ts b/modules/@angular/compiler-cli/test/static_reflector_spec.ts index 70bc017d4b..d1c75ba09f 100644 --- a/modules/@angular/compiler-cli/test/static_reflector_spec.ts +++ b/modules/@angular/compiler-cli/test/static_reflector_spec.ts @@ -15,6 +15,8 @@ import { StaticSymbol } from '@angular/compiler-cli/src/static_reflector'; +import {transition, sequence, group, trigger, state, style, animate, keyframes} from '@angular/core'; + describe('StaticReflector', () => { let noContext = new StaticSymbol('', ''); let host: StaticReflectorHost; @@ -62,6 +64,19 @@ describe('StaticReflector', () => { expect(annotation.selector).toEqual('my-hero-detail'); expect(annotation.directives) .toEqual([[host.findDeclaration('angular2/src/common/directives/ng_for', 'NgFor')]]); + expect(annotation.animations).toEqual([ + trigger("myAnimation", [ + state("state1", style({ "background": "white" })), + transition("* => *", sequence([ + group([ + animate("1s 0.5s", keyframes([ + style({ "background": "blue"}), + style({ "background": "red"}) + ])) + ]) + ])) + ]) + ]); }); it('should throw and exception for unsupported metadata versions', () => { @@ -252,6 +267,7 @@ class MockReflectorHost implements StaticReflectorHost { coreDecorators: 'angular2/src/core/metadata', diDecorators: 'angular2/src/core/di/decorators', diMetadata: 'angular2/src/core/di/metadata', + animationMetadata: 'angular2/src/core/animation/metadata', provider: 'angular2/src/core/di/provider' }; } @@ -405,11 +421,100 @@ class MockReflectorHost implements StaticReflectorHost { "name": "FORM_DIRECTIVES", "module": "angular2/src/common/forms/directives" } + ], + "animations": [{ + "__symbolic": "call", + "expression": { + "__symbolic": "reference", + "name": "trigger", + "module": "angular2/src/core/animation/metadata" + }, + "arguments": [ + "myAnimation", + [{ "__symbolic": "call", + "expression": { + "__symbolic": "reference", + "name": "state", + "module": "angular2/src/core/animation/metadata" + }, + "arguments": [ + "state1", + { "__symbolic": "call", + "expression": { + "__symbolic": "reference", + "name": "style", + "module": "angular2/src/core/animation/metadata" + }, + "arguments": [ + { "background":"white" } + ] + } + ] + }, { + "__symbolic": "call", + "expression": { + "__symbolic":"reference", + "name":"transition", + "module": "angular2/src/core/animation/metadata" + }, + "arguments": [ + "* => *", + { + "__symbolic":"call", + "expression":{ + "__symbolic":"reference", + "name":"sequence", + "module": "angular2/src/core/animation/metadata" + }, + "arguments":[[{ "__symbolic": "call", + "expression": { + "__symbolic":"reference", + "name":"group", + "module": "angular2/src/core/animation/metadata" + }, + "arguments":[[{ + "__symbolic": "call", + "expression": { + "__symbolic":"reference", + "name":"animate", + "module": "angular2/src/core/animation/metadata" + }, + "arguments":[ + "1s 0.5s", + { "__symbolic": "call", + "expression": { + "__symbolic":"reference", + "name":"keyframes", + "module": "angular2/src/core/animation/metadata" + }, + "arguments":[[{ "__symbolic": "call", + "expression": { + "__symbolic":"reference", + "name":"style", + "module": "angular2/src/core/animation/metadata" + }, + "arguments":[ { "background": "blue"} ] + }, { + "__symbolic": "call", + "expression": { + "__symbolic":"reference", + "name":"style", + "module": "angular2/src/core/animation/metadata" + }, + "arguments":[ { "background": "red"} ] + }]] + } + ] + }]] + }]] + } + ] + } + ] ] - } - ] - } - ], + }] + }] + }], "members": { "hero": [ { diff --git a/modules/@angular/compiler/core_private.ts b/modules/@angular/compiler/core_private.ts index f63d28e797..9151e5de27 100644 --- a/modules/@angular/compiler/core_private.ts +++ b/modules/@angular/compiler/core_private.ts @@ -74,10 +74,13 @@ export type AnimationGroupPlayer = t.AnimationGroupPlayer; export var AnimationGroupPlayer: typeof t.AnimationGroupPlayer = r.AnimationGroupPlayer; export type AnimationKeyframe = t.AnimationKeyframe; export var AnimationKeyframe: typeof t.AnimationKeyframe = r.AnimationKeyframe; -export type AnimationStyleUtil = t.AnimationStyleUtil; -export var AnimationStyleUtil: typeof t.AnimationStyleUtil = r.AnimationStyleUtil; -export type AnimationStylrs = t.AnimationStyles; +export type AnimationStyles = t.AnimationStyles; export var AnimationStyles: typeof t.AnimationStyles = r.AnimationStyles; export var ANY_STATE = r.ANY_STATE; export var EMPTY_STATE = r.EMPTY_STATE; export var FILL_STYLE_FLAG = r.FILL_STYLE_FLAG; +export var balanceAnimationStyles: typeof t.balanceAnimationStyles = r.balanceAnimationStyles; +export var balanceAnimationKeyframes: typeof t.balanceAnimationKeyframes = r.balanceAnimationKeyframes; +export var flattenStyles: typeof t.flattenStyles = r.flattenStyles; +export var clearStyles: typeof t.clearStyles = r.clearStyles; +export var collectAndResolveStyles: typeof r.collectAndResolveStyles = r.collectAndResolveStyles; diff --git a/modules/@angular/compiler/src/animation/animation_compiler.ts b/modules/@angular/compiler/src/animation/animation_compiler.ts index fc8cc3d0ca..e29e2c12ac 100644 --- a/modules/@angular/compiler/src/animation/animation_compiler.ts +++ b/modules/@angular/compiler/src/animation/animation_compiler.ts @@ -249,7 +249,7 @@ class _AnimationBuilder implements AnimationAstVisitor { statements.push( _ANIMATION_FACTORY_RENDERER_VAR.callMethod('setElementStyles', [ _ANIMATION_FACTORY_ELEMENT_VAR, - o.importExpr(Identifiers.clearAnimationStyles).callFn([_ANIMATION_START_STATE_STYLES_VAR]) + o.importExpr(Identifiers.clearStyles).callFn([_ANIMATION_START_STATE_STYLES_VAR]) ]).toStmt()); ast.stateTransitions.forEach(transAst => statements.push(transAst.visit(this, context))); @@ -287,7 +287,7 @@ class _AnimationBuilder implements AnimationAstVisitor { ]).toStmt()); return o.fn([ - new o.FnParam(_ANIMATION_FACTORY_VIEW_VAR.name, o.importType(Identifiers.AppView)), + new o.FnParam(_ANIMATION_FACTORY_VIEW_VAR.name, o.importType(Identifiers.AppView, [o.DYNAMIC_TYPE])), new o.FnParam(_ANIMATION_FACTORY_ELEMENT_VAR.name, o.DYNAMIC_TYPE), new o.FnParam(_ANIMATION_CURRENT_STATE_VAR.name, o.DYNAMIC_TYPE), new o.FnParam(_ANIMATION_NEXT_STATE_VAR.name, o.DYNAMIC_TYPE) diff --git a/modules/@angular/compiler/src/identifiers.ts b/modules/@angular/compiler/src/identifiers.ts index d656002a71..a880f9abf9 100644 --- a/modules/@angular/compiler/src/identifiers.ts +++ b/modules/@angular/compiler/src/identifiers.ts @@ -45,7 +45,10 @@ import { NoOpAnimationPlayer as NoOpAnimationPlayer_, AnimationGroupPlayer as AnimationGroupPlayer_, AnimationSequencePlayer as AnimationSequencePlayer_, - AnimationStyleUtil, + balanceAnimationStyles as impBalanceAnimationStyles, + balanceAnimationKeyframes as impBalanceAnimationKeyframes, + clearStyles as impClearStyles, + collectAndResolveStyles as impCollectAndResolveStyles, SecurityContext } from '../core_private'; @@ -245,22 +248,22 @@ export class Identifiers { static balanceAnimationStyles = new CompileIdentifierMetadata({ name: 'balanceAnimationStyles', moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL, - runtime: AnimationStyleUtil.balanceStyles + runtime: impBalanceAnimationStyles }); static balanceAnimationKeyframes = new CompileIdentifierMetadata({ name: 'balanceAnimationKeyframes', moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL, - runtime: AnimationStyleUtil.balanceKeyframes + runtime: impBalanceAnimationKeyframes }); - static clearAnimationStyles = new CompileIdentifierMetadata({ - name: 'clearAnimationStyles', + static clearStyles = new CompileIdentifierMetadata({ + name: 'clearStyles', moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL, - runtime: AnimationStyleUtil.clearStyles + runtime: impClearStyles }); static collectAndResolveStyles = new CompileIdentifierMetadata({ name: 'collectAndResolveStyles', moduleUrl: ANIMATION_STYLE_UTIL_ASSET_URL, - runtime: AnimationStyleUtil.collectAndResolveStyles + runtime: impCollectAndResolveStyles }); } diff --git a/modules/@angular/compiler/test/animation/animation_parser_spec.ts b/modules/@angular/compiler/test/animation/animation_parser_spec.ts index 9b37b5831e..f44e7a77e0 100644 --- a/modules/@angular/compiler/test/animation/animation_parser_spec.ts +++ b/modules/@angular/compiler/test/animation/animation_parser_spec.ts @@ -44,7 +44,7 @@ import { AnimationStepAst } from '../../src/animation/animation_ast'; -import {FILL_STYLE_FLAG, AnimationStyleUtil} from '../../core_private'; +import {FILL_STYLE_FLAG, flattenStyles} from '../../core_private'; import {StringMapWrapper} from '../../src/facade/collection'; @@ -348,7 +348,7 @@ export function main() { var kf1 = keyframesStep.keyframes[0]; var kf2 = keyframesStep.keyframes[1]; - expect(AnimationStyleUtil.flattenStyles(kf1.styles.styles)).toEqual({ + expect(flattenStyles(kf1.styles.styles)).toEqual({ "color": "red", "background": FILL_STYLE_FLAG }); @@ -368,7 +368,7 @@ export function main() { var kf2 = keyframesStep.keyframes[1]; var kf3 = keyframesStep.keyframes[2]; - expect(AnimationStyleUtil.flattenStyles(kf3.styles.styles)).toEqual({ + expect(flattenStyles(kf3.styles.styles)).toEqual({ "background": "blue", "color": "red", "border-color": "white" @@ -390,7 +390,7 @@ export function main() { var kf3 = keyframesStep.keyframes[2]; expect(kf1.offset).toEqual(0); - expect(AnimationStyleUtil.flattenStyles(kf1.styles.styles)).toEqual({ + expect(flattenStyles(kf1.styles.styles)).toEqual({ "font-size": FILL_STYLE_FLAG, "background": FILL_STYLE_FLAG, "color": FILL_STYLE_FLAG @@ -412,7 +412,7 @@ export function main() { var kf3 = keyframesStep.keyframes[2]; expect(kf3.offset).toEqual(1); - expect(AnimationStyleUtil.flattenStyles(kf3.styles.styles)).toEqual({ + expect(flattenStyles(kf3.styles.styles)).toEqual({ "color": "orange", "background": "red", "transform": "rotate(360deg)", diff --git a/modules/@angular/core/private_export.ts b/modules/@angular/core/private_export.ts index c9db551a98..5a72553425 100644 --- a/modules/@angular/core/private_export.ts +++ b/modules/@angular/core/private_export.ts @@ -35,8 +35,8 @@ import { import {AnimationSequencePlayer as AnimationSequencePlayer_} from './src/animation/animation_sequence_player'; import {AnimationGroupPlayer as AnimationGroupPlayer_} from './src/animation/animation_group_player'; import {AnimationKeyframe as AnimationKeyframe_} from './src/animation/animation_keyframe'; -import {AnimationStyleUtil as AnimationStyleUtil_} from './src/animation/animation_style_util'; import {AnimationStyles as AnimationStyles_} from './src/animation/animation_styles'; +import * as animationUtils from './src/animation/animation_style_util'; import { ANY_STATE as ANY_STATE_, EMPTY_STATE as EMPTY_STATE_, @@ -121,8 +121,11 @@ export declare namespace __core_private_types__ { export var AnimationGroupPlayer: typeof AnimationGroupPlayer_; export type AnimationKeyframe = AnimationKeyframe_; export var AnimationKeyframe: typeof AnimationKeyframe_; - export type AnimationStyleUtil = AnimationStyleUtil_; - export var AnimationStyleUtil: typeof AnimationStyleUtil_; + export var balanceAnimationStyles: typeof animationUtils.balanceAnimationStyles; + export var balanceAnimationKeyframes: typeof animationUtils.balanceAnimationKeyframes; + export var flattenStyles: typeof animationUtils.flattenStyles; + export var clearStyles: typeof animationUtils.clearStyles; + export var collectAndResolveStyles: typeof animationUtils.collectAndResolveStyles; export type AnimationStyles = AnimationStyles_; export var AnimationStyles: typeof AnimationStyles_; export var ANY_STATE: typeof ANY_STATE_; @@ -187,7 +190,11 @@ export var __core_private__ = { AnimationSequencePlayer: AnimationSequencePlayer_, AnimationGroupPlayer: AnimationGroupPlayer_, AnimationKeyframe: AnimationKeyframe_, - AnimationStyleUtil: AnimationStyleUtil_, + balanceAnimationStyles: animationUtils.balanceAnimationStyles, + balanceAnimationKeyframes: animationUtils.balanceAnimationKeyframes, + flattenStyles: animationUtils.flattenStyles, + clearStyles: animationUtils.clearStyles, + collectAndResolveStyles: animationUtils.collectAndResolveStyles, AnimationStyles: AnimationStyles_, ANY_STATE: ANY_STATE_, EMPTY_STATE: EMPTY_STATE_, diff --git a/modules/@angular/core/src/animation/animation_style_util.ts b/modules/@angular/core/src/animation/animation_style_util.ts index 03ef5cf8d7..c05dbed583 100644 --- a/modules/@angular/core/src/animation/animation_style_util.ts +++ b/modules/@angular/core/src/animation/animation_style_util.ts @@ -3,111 +3,110 @@ import {ListWrapper, StringMapWrapper} from '../facade/collection'; import {AUTO_STYLE} from './metadata'; import {FILL_STYLE_FLAG} from './animation_constants'; -export class AnimationStyleUtil { - static balanceStyles(previousStyles: {[key: string]: string|number}, - newStyles: {[key: string]: string|number}, - nullValue = null): {[key: string]: string|number} { - var finalStyles: {[key: string]: string|number} = {}; +export function balanceAnimationStyles(previousStyles: {[key: string]: string|number}, + newStyles: {[key: string]: string|number}, + nullValue = null): {[key: string]: string} { + var finalStyles: {[key: string]: string} = {}; - StringMapWrapper.forEach(newStyles, (value, prop) => { + StringMapWrapper.forEach(newStyles, (value, prop) => { + finalStyles[prop] = value.toString(); + }); + + StringMapWrapper.forEach(previousStyles, (value, prop) => { + if (!isPresent(finalStyles[prop])) { + finalStyles[prop] = nullValue; + } + }); + + return finalStyles; +} + +export function balanceAnimationKeyframes(collectedStyles: {[key: string]: string|number}, + finalStateStyles: {[key: string]: string|number}, + keyframes: any[]): any[] { + var limit = keyframes.length - 1; + var firstKeyframe = keyframes[0]; + + // phase 1: copy all the styles from the first keyframe into the lookup map + var flatenedFirstKeyframeStyles = flattenStyles(firstKeyframe.styles.styles); + + var extraFirstKeyframeStyles = {}; + var hasExtraFirstStyles = false; + StringMapWrapper.forEach(collectedStyles, (value, prop) => { + // if the style is already defined in the first keyframe then + // we do not replace it. + if (!flatenedFirstKeyframeStyles[prop]) { + flatenedFirstKeyframeStyles[prop] = value; + extraFirstKeyframeStyles[prop] = value; + hasExtraFirstStyles = true; + } + }); + + var keyframeCollectedStyles = StringMapWrapper.merge({}, flatenedFirstKeyframeStyles); + + // phase 2: normalize the final keyframe + var finalKeyframe = keyframes[limit]; + ListWrapper.insert(finalKeyframe.styles.styles, 0, finalStateStyles); + + var flatenedFinalKeyframeStyles = flattenStyles(finalKeyframe.styles.styles); + var extraFinalKeyframeStyles = {}; + var hasExtraFinalStyles = false; + StringMapWrapper.forEach(keyframeCollectedStyles, (value, prop) => { + if (!isPresent(flatenedFinalKeyframeStyles[prop])) { + extraFinalKeyframeStyles[prop] = AUTO_STYLE; + hasExtraFinalStyles = true; + } + }); + + if (hasExtraFinalStyles) { + finalKeyframe.styles.styles.push(extraFinalKeyframeStyles); + } + + StringMapWrapper.forEach(flatenedFinalKeyframeStyles, (value, prop) => { + if (!isPresent(flatenedFirstKeyframeStyles[prop])) { + extraFirstKeyframeStyles[prop] = AUTO_STYLE; + hasExtraFirstStyles = true; + } + }); + + if (hasExtraFirstStyles) { + firstKeyframe.styles.styles.push(extraFirstKeyframeStyles); + } + + return keyframes; +} + +export function clearStyles(styles: {[key: string]: string|number}): {[key: string]: string} { + var finalStyles: {[key: string]: string} = {}; + StringMapWrapper.keys(styles).forEach(key => { + finalStyles[key] = null; + }); + return finalStyles; +} + +export function collectAndResolveStyles(collection: {[key: string]: string|number}, styles: {[key: string]: string|number}[]) { + return styles.map(entry => { + var stylesObj = {}; + StringMapWrapper.forEach(entry, (value, prop) => { + if (value == FILL_STYLE_FLAG) { + value = collection[prop]; + if (!isPresent(value)) { + value = AUTO_STYLE; + } + } + collection[prop] = value; + stylesObj[prop] = value; + }); + return stylesObj; + }); +} + +export function flattenStyles(styles: {[key: string]: string|number}[]) { + var finalStyles = {}; + styles.forEach(entry => { + StringMapWrapper.forEach(entry, (value, prop) => { finalStyles[prop] = value; }); - - StringMapWrapper.forEach(previousStyles, (value, prop) => { - if (!isPresent(finalStyles[prop])) { - finalStyles[prop] = nullValue; - } - }); - - return finalStyles; - } - static balanceKeyframes(collectedStyles: {[key: string]: string|number}, - finalStateStyles: {[key: string]: string|number}, - keyframes: any[]): any[] { - var limit = keyframes.length - 1; - var firstKeyframe = keyframes[0]; - - // phase 1: copy all the styles from the first keyframe into the lookup map - var flatenedFirstKeyframeStyles = AnimationStyleUtil.flattenStyles(firstKeyframe.styles.styles); - - var extraFirstKeyframeStyles = {}; - var hasExtraFirstStyles = false; - StringMapWrapper.forEach(collectedStyles, (value, prop) => { - // if the style is already defined in the first keyframe then - // we do not replace it. - if (!flatenedFirstKeyframeStyles[prop]) { - flatenedFirstKeyframeStyles[prop] = value; - extraFirstKeyframeStyles[prop] = value; - hasExtraFirstStyles = true; - } - }); - - var keyframeCollectedStyles = StringMapWrapper.merge({}, flatenedFirstKeyframeStyles); - - // phase 2: normalize the final keyframe - var finalKeyframe = keyframes[limit]; - ListWrapper.insert(finalKeyframe.styles.styles, 0, finalStateStyles); - - var flatenedFinalKeyframeStyles = AnimationStyleUtil.flattenStyles(finalKeyframe.styles.styles); - var extraFinalKeyframeStyles = {}; - var hasExtraFinalStyles = false; - StringMapWrapper.forEach(keyframeCollectedStyles, (value, prop) => { - if (!isPresent(flatenedFinalKeyframeStyles[prop])) { - extraFinalKeyframeStyles[prop] = AUTO_STYLE; - hasExtraFinalStyles = true; - } - }); - - if (hasExtraFinalStyles) { - finalKeyframe.styles.styles.push(extraFinalKeyframeStyles); - } - - StringMapWrapper.forEach(flatenedFinalKeyframeStyles, (value, prop) => { - if (!isPresent(flatenedFirstKeyframeStyles[prop])) { - extraFirstKeyframeStyles[prop] = AUTO_STYLE; - hasExtraFirstStyles = true; - } - }); - - if (hasExtraFirstStyles) { - firstKeyframe.styles.styles.push(extraFirstKeyframeStyles); - } - - return keyframes; - } - - static clearStyles(styles: {[key: string]: string|number}): {[key: string]: string|number} { - var finalStyles: {[key: string]: string|number} = {}; - StringMapWrapper.keys(styles).forEach(key => { - finalStyles[key] = null; - }); - return finalStyles; - } - - static collectAndResolveStyles(collection: {[key: string]: string|number}, styles: {[key: string]: string|number}[]) { - return styles.map(entry => { - var stylesObj = {}; - StringMapWrapper.forEach(entry, (value, prop) => { - if (value == FILL_STYLE_FLAG) { - value = collection[prop]; - if (!isPresent(value)) { - value = AUTO_STYLE; - } - } - collection[prop] = value; - stylesObj[prop] = value; - }); - return stylesObj; - }); - } - - static flattenStyles(styles: {[key: string]: string|number}[]) { - var finalStyles = {}; - styles.forEach(entry => { - StringMapWrapper.forEach(entry, (value, prop) => { - finalStyles[prop] = value; - }); - }); - return finalStyles; - } + }); + return finalStyles; } diff --git a/modules/@angular/core/test/animation/animation_integration_spec.ts b/modules/@angular/core/test/animation/animation_integration_spec.ts index c2ab427e8d..a7e1623980 100644 --- a/modules/@angular/core/test/animation/animation_integration_spec.ts +++ b/modules/@angular/core/test/animation/animation_integration_spec.ts @@ -33,8 +33,6 @@ import {AnimationDriver} from '../../src/animation/animation_driver'; import {MockAnimationDriver} from '../../testing/animation/mock_animation_driver'; import {trigger, state, transition, keyframes, style, animate, group, sequence, AnimationEntryMetadata} from '../../src/animation/metadata'; -import {AnimationStyleUtil} from '../../src/animation/animation_style_util'; - import {AUTO_STYLE} from '../../src/animation/metadata'; export function main() { diff --git a/modules/@angular/core/test/animation/animation_style_util_spec.ts b/modules/@angular/core/test/animation/animation_style_util_spec.ts index 474b452abd..bb320e7ee6 100644 --- a/modules/@angular/core/test/animation/animation_style_util_spec.ts +++ b/modules/@angular/core/test/animation/animation_style_util_spec.ts @@ -21,7 +21,7 @@ import {getDOM} from '@angular/platform-browser/src/dom/dom_adapter'; import {isPresent} from '../../src/facade/lang'; import {MockAnimationPlayer} from '../../testing/animation/mock_animation_player'; -import {AnimationStyleUtil} from '../../src/animation/animation_style_util'; +import * as animationUtils from '../../src/animation/animation_style_util'; import {AnimationKeyframe} from '../../src/animation/animation_keyframe'; import {AnimationStyles} from '../../src/animation/animation_styles'; @@ -29,14 +29,14 @@ import {FILL_STYLE_FLAG} from '../../src/animation/animation_constants'; import {AUTO_STYLE} from '../../src/animation/metadata'; export function main() { - describe('AnimationStyleUtil', function() { + describe('Animation Style Utils', function() { - describe('balanceStyles', () => { + describe('balanceAnimationStyles', () => { it('should set all non-shared styles to the provided null value between the two sets of styles', () => { var styles = { opacity: 0, color: 'red' }; var newStyles = { background: 'red' }; var flag = '*'; - var result = AnimationStyleUtil.balanceStyles(styles, newStyles, flag); + var result = animationUtils.balanceAnimationStyles(styles, newStyles, flag); expect(result).toEqual({ opacity:flag, color:flag, @@ -47,17 +47,17 @@ export function main() { it('should handle an empty set of styles', () => { var value = '*'; - expect(AnimationStyleUtil.balanceStyles({}, { opacity: 0 }, value)).toEqual({ - opacity: 0 + expect(animationUtils.balanceAnimationStyles({}, { opacity: '0' }, value)).toEqual({ + opacity: '0' }); - expect(AnimationStyleUtil.balanceStyles({ opacity: 0 }, {}, value)).toEqual({ + expect(animationUtils.balanceAnimationStyles({ opacity: '0' }, {}, value)).toEqual({ opacity: value }); }); }); - describe('balanceKeyframes', () => { + describe('balanceAnimationKeyframes', () => { it('should balance both the starting and final keyframes with thep provided styles', () => { var collectedStyles = { width: 100, @@ -74,9 +74,9 @@ export function main() { new AnimationKeyframe(1, new AnimationStyles([{ background: 'blue', left: '100px', top: '100px' }])) ]; - var result = AnimationStyleUtil.balanceKeyframes(collectedStyles, finalStyles, keyframes); + var result = animationUtils.balanceAnimationKeyframes(collectedStyles, finalStyles, keyframes); - expect(AnimationStyleUtil.flattenStyles(result[0].styles.styles)).toEqual({ + expect(animationUtils.flattenStyles(result[0].styles.styles)).toEqual({ "width": 100, "height": 100, "opacity": 1, @@ -86,7 +86,7 @@ export function main() { "top": '*' }); - expect(AnimationStyleUtil.flattenStyles(result[1].styles.styles)).toEqual({ + expect(animationUtils.flattenStyles(result[1].styles.styles)).toEqual({ "width": '*', "height": '*', "opacity": '*', @@ -103,15 +103,15 @@ export function main() { new AnimationKeyframe(1, new AnimationStyles([{ width: 100 }])) ]; - var result = AnimationStyleUtil.balanceKeyframes({}, {}, keyframes); + var result = animationUtils.balanceAnimationKeyframes({}, {}, keyframes); - expect(AnimationStyleUtil.flattenStyles(result[0].styles.styles)).toEqual({ + expect(animationUtils.flattenStyles(result[0].styles.styles)).toEqual({ "height": 100, "opacity": 1, "width": "*" }); - expect(AnimationStyleUtil.flattenStyles(result[1].styles.styles)).toEqual({ + expect(animationUtils.flattenStyles(result[1].styles.styles)).toEqual({ "width": 100, "height": "*", "opacity": "*" @@ -131,11 +131,11 @@ export function main() { "width": null, "color": null }; - expect(AnimationStyleUtil.clearStyles(styles)).toEqual(expectedResult); + expect(animationUtils.clearStyles(styles)).toEqual(expectedResult); }); it('should handle an empty set of styles', () => { - expect(AnimationStyleUtil.clearStyles({})).toEqual({}); + expect(animationUtils.clearStyles({})).toEqual({}); }); }); @@ -153,13 +153,13 @@ export function main() { var collection: {[key: string]: string|number} = {}; - expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles1)).toEqual(styles1); + expect(animationUtils.collectAndResolveStyles(collection, styles1)).toEqual(styles1); expect(collection).toEqual({ "opacity": 0, "width": 100 }); - expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles2)).toEqual(styles2); + expect(animationUtils.collectAndResolveStyles(collection, styles2)).toEqual(styles2); expect(collection).toEqual({ "opacity": 1, "width": 100, @@ -180,12 +180,12 @@ export function main() { var collection = {}; - expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles1)).toEqual([{ + expect(animationUtils.collectAndResolveStyles(collection, styles1)).toEqual([{ "opacity": 0, "width": AUTO_STYLE }]); - expect(AnimationStyleUtil.collectAndResolveStyles(collection, styles2)).toEqual([{ + expect(animationUtils.collectAndResolveStyles(collection, styles2)).toEqual([{ "opacity": 0, "height": 999 }]); diff --git a/modules/@angular/platform-browser/core_private.ts b/modules/@angular/platform-browser/core_private.ts index a87fe807ae..38ff8a0823 100644 --- a/modules/@angular/platform-browser/core_private.ts +++ b/modules/@angular/platform-browser/core_private.ts @@ -28,7 +28,10 @@ export type AnimationGroupPlayer = t.AnimationGroupPlayer; export var AnimationGroupPlayer: typeof t.AnimationGroupPlayer = r.AnimationGroupPlayer; export type AnimationKeyframe = t.AnimationKeyframe; export var AnimationKeyframe: typeof t.AnimationKeyframe = r.AnimationKeyframe; -export type AnimationStyleUtil = t.AnimationStyleUtil; -export var AnimationStyleUtil: typeof t.AnimationStyleUtil = r.AnimationStyleUtil; export type AnimationStyles = t.AnimationStyles; export var AnimationStyles: typeof t.AnimationStyles = r.AnimationStyles; +export var balanceAnimationStyles: typeof t.balanceAnimationStyles = r.balanceAnimationStyles; +export var balanceAnimationKeyframes: typeof t.balanceAnimationKeyframes = r.balanceAnimationKeyframes; +export var flattenStyles: typeof t.flattenStyles = r.flattenStyles; +export var clearStyles: typeof t.clearStyles = r.clearStyles; +export var collectAndResolveStyles: typeof r.collectAndResolveStyles = r.collectAndResolveStyles; diff --git a/modules/@angular/platform-server/src/parse5_adapter.ts b/modules/@angular/platform-server/src/parse5_adapter.ts index 44e2d5915c..b72cc0fb81 100644 --- a/modules/@angular/platform-server/src/parse5_adapter.ts +++ b/modules/@angular/platform-server/src/parse5_adapter.ts @@ -553,7 +553,7 @@ export class Parse5DomAdapter extends DomAdapter { setGlobalVar(path: string, value: any) { setValueOnPath(global, path, value); } requestAnimationFrame(callback): number { return setTimeout(callback, 0); } cancelAnimationFrame(id: number) { clearTimeout(id); } - supportsWebAnimation(): boolean { return true; } + supportsWebAnimation(): boolean { return false; } performanceNow(): number { return DateWrapper.toMillis(DateWrapper.now()); } getAnimationPrefix(): string { return ''; } getTransitionEnd(): string { return 'transitionend'; } @@ -567,6 +567,7 @@ export class Parse5DomAdapter extends DomAdapter { supportsCookies(): boolean { return false; } getCookie(name: string): string { throw new Error('not implemented'); } setCookie(name: string, value: string) { throw new Error('not implemented'); } + animate(element: any, keyframes: any[], options: any): any { throw new Error('not implemented'); } } // TODO: build a proper list, this one is all the keys of a HTMLInputElement diff --git a/tools/public_api_guard/public_api_spec.ts b/tools/public_api_guard/public_api_spec.ts index c2977b675e..30e88fc8df 100644 --- a/tools/public_api_guard/public_api_spec.ts +++ b/tools/public_api_guard/public_api_spec.ts @@ -181,6 +181,7 @@ const CORE = [ 'DebugElement.queryAll(predicate:Predicate):DebugElement[]', 'DebugElement.queryAllNodes(predicate:Predicate):DebugNode[]', 'DebugElement.removeChild(child:DebugNode):any', + 'DebugElement.styles:{[key:string]:string}', 'DebugElement.triggerEventHandler(eventName:string, eventObj:any):any', 'DebugNode', 'DebugNode.componentInstance:any', @@ -1651,6 +1652,7 @@ const PLATFORM_SERVER = [ 'Parse5DomAdapter', 'Parse5DomAdapter.addClass(element:any, className:string):any', 'Parse5DomAdapter.adoptNode(node:any):any', + 'Parse5DomAdapter.animate(element:any, keyframes:any[], options:any):any', 'Parse5DomAdapter.appendChild(el:any, node:any):any', 'Parse5DomAdapter.attrToPropMap:any', 'Parse5DomAdapter.attributeMap(element:any):Map',