feat(animate): adds basic support for CSS animations on enter and leave
Closes #3876
This commit is contained in:
parent
effbb54f3d
commit
39ce9d3397
@ -2,3 +2,4 @@ export * from './core';
|
|||||||
export * from './profile';
|
export * from './profile';
|
||||||
export * from './lifecycle_hooks';
|
export * from './lifecycle_hooks';
|
||||||
export * from './bootstrap';
|
export * from './bootstrap';
|
||||||
|
export * from './animate';
|
||||||
|
5
modules/angular2/animate.ts
Normal file
5
modules/angular2/animate.ts
Normal file
@ -0,0 +1,5 @@
|
|||||||
|
export {Animation} from './src/animate/animation';
|
||||||
|
export {AnimationBuilder} from './src/animate/animation_builder';
|
||||||
|
export {BrowserDetails} from './src/animate/browser_details';
|
||||||
|
export {CssAnimationBuilder} from './src/animate/css_animation_builder';
|
||||||
|
export {CssAnimationOptions} from './src/animate/css_animation_options';
|
188
modules/angular2/src/animate/animation.ts
Normal file
188
modules/angular2/src/animate/animation.ts
Normal file
@ -0,0 +1,188 @@
|
|||||||
|
import {
|
||||||
|
DateWrapper,
|
||||||
|
StringWrapper,
|
||||||
|
RegExpWrapper,
|
||||||
|
NumberWrapper
|
||||||
|
} from 'angular2/src/core/facade/lang';
|
||||||
|
import {Math} from 'angular2/src/core/facade/math';
|
||||||
|
import {camelCaseToDashCase} from 'angular2/src/core/render/dom/util';
|
||||||
|
import {StringMapWrapper} from 'angular2/src/core/facade/collection';
|
||||||
|
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||||
|
|
||||||
|
import {BrowserDetails} from './browser_details';
|
||||||
|
import {CssAnimationOptions} from './css_animation_options';
|
||||||
|
|
||||||
|
export class Animation {
|
||||||
|
/** functions to be called upon completion */
|
||||||
|
callbacks: Function[] = [];
|
||||||
|
|
||||||
|
/** the duration (ms) of the animation (whether from CSS or manually set) */
|
||||||
|
computedDuration: number;
|
||||||
|
|
||||||
|
/** the animation delay (ms) (whether from CSS or manually set) */
|
||||||
|
computedDelay: number;
|
||||||
|
|
||||||
|
/** timestamp of when the animation started */
|
||||||
|
startTime: number;
|
||||||
|
|
||||||
|
/** functions for removing event listeners */
|
||||||
|
eventClearFunctions: Function[] = [];
|
||||||
|
|
||||||
|
/** flag used to track whether or not the animation has finished */
|
||||||
|
completed: boolean = false;
|
||||||
|
|
||||||
|
/** total amount of time that the animation should take including delay */
|
||||||
|
get totalTime(): number {
|
||||||
|
let delay = this.computedDelay != null ? this.computedDelay : 0;
|
||||||
|
let duration = this.computedDuration != null ? this.computedDuration : 0;
|
||||||
|
return delay + duration;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Stores the start time and starts the animation
|
||||||
|
* @param element
|
||||||
|
* @param data
|
||||||
|
* @param browserDetails
|
||||||
|
*/
|
||||||
|
constructor(public element: HTMLElement, public data: CssAnimationOptions,
|
||||||
|
public browserDetails: BrowserDetails) {
|
||||||
|
this.startTime = DateWrapper.toMillis(DateWrapper.now());
|
||||||
|
this.setup();
|
||||||
|
this.wait(timestamp => this.start());
|
||||||
|
}
|
||||||
|
|
||||||
|
wait(callback: Function) {
|
||||||
|
// Firefox requires 2 frames for some reason
|
||||||
|
this.browserDetails.raf(callback, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets up the initial styles before the animation is started
|
||||||
|
*/
|
||||||
|
setup(): void {
|
||||||
|
if (this.data.fromStyles != null) this.applyStyles(this.data.fromStyles);
|
||||||
|
if (this.data.duration != null)
|
||||||
|
this.applyStyles({'transitionDuration': this.data.duration.toString() + 'ms'});
|
||||||
|
if (this.data.delay != null)
|
||||||
|
this.applyStyles({'transitionDelay': this.data.delay.toString() + 'ms'});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* After the initial setup has occurred, this method adds the animation styles
|
||||||
|
*/
|
||||||
|
start(): void {
|
||||||
|
this.addClasses(this.data.classesToAdd);
|
||||||
|
this.addClasses(this.data.animationClasses);
|
||||||
|
this.removeClasses(this.data.classesToRemove);
|
||||||
|
if (this.data.toStyles != null) this.applyStyles(this.data.toStyles);
|
||||||
|
var computedStyles = DOM.getComputedStyle(this.element);
|
||||||
|
this.computedDelay =
|
||||||
|
Math.max(this.parseDurationString(computedStyles.getPropertyValue('transition-delay')),
|
||||||
|
this.parseDurationString(this.element.style.getPropertyValue('transition-delay')));
|
||||||
|
this.computedDuration = Math.max(
|
||||||
|
this.parseDurationString(computedStyles.getPropertyValue('transition-duration')),
|
||||||
|
this.parseDurationString(this.element.style.getPropertyValue('transition-duration')));
|
||||||
|
this.addEvents();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Applies the provided styles to the element
|
||||||
|
* @param styles
|
||||||
|
*/
|
||||||
|
applyStyles(styles: StringMap<string, any>): void {
|
||||||
|
StringMapWrapper.forEach(styles, (value, key) => {
|
||||||
|
DOM.setStyle(this.element, camelCaseToDashCase(key), value.toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds the provided classes to the element
|
||||||
|
* @param classes
|
||||||
|
*/
|
||||||
|
addClasses(classes: string[]): void {
|
||||||
|
for (let i = 0, len = classes.length; i < len; i++) DOM.addClass(this.element, classes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the provided classes from the element
|
||||||
|
* @param classes
|
||||||
|
*/
|
||||||
|
removeClasses(classes: string[]): void {
|
||||||
|
for (let i = 0, len = classes.length; i < len; i++) DOM.removeClass(this.element, classes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds events to track when animations have finished
|
||||||
|
*/
|
||||||
|
addEvents(): void {
|
||||||
|
if (this.totalTime > 0) {
|
||||||
|
this.eventClearFunctions.push(DOM.onAndCancel(
|
||||||
|
this.element, 'transitionend', (event: any) => this.handleAnimationEvent(event)));
|
||||||
|
} else {
|
||||||
|
this.handleAnimationCompleted();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
handleAnimationEvent(event: any): void {
|
||||||
|
let elapsedTime = Math.round(event.elapsedTime * 1000);
|
||||||
|
if (!this.browserDetails.elapsedTimeIncludesDelay) elapsedTime += this.computedDelay;
|
||||||
|
event.stopPropagation();
|
||||||
|
if (elapsedTime >= this.totalTime) this.handleAnimationCompleted();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Runs all animation callbacks and removes temporary classes
|
||||||
|
*/
|
||||||
|
handleAnimationCompleted(): void {
|
||||||
|
this.removeClasses(this.data.animationClasses);
|
||||||
|
this.callbacks.forEach(callback => callback());
|
||||||
|
this.callbacks = [];
|
||||||
|
this.eventClearFunctions.forEach(fn => fn());
|
||||||
|
this.eventClearFunctions = [];
|
||||||
|
this.completed = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds animation callbacks to be called upon completion
|
||||||
|
* @param callback
|
||||||
|
* @returns {Animation}
|
||||||
|
*/
|
||||||
|
onComplete(callback: Function): Animation {
|
||||||
|
if (this.completed) {
|
||||||
|
callback();
|
||||||
|
} else {
|
||||||
|
this.callbacks.push(callback);
|
||||||
|
}
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Converts the duration string to the number of milliseconds
|
||||||
|
* @param duration
|
||||||
|
* @returns {number}
|
||||||
|
*/
|
||||||
|
parseDurationString(duration: string): number {
|
||||||
|
var maxValue = 0;
|
||||||
|
// duration must have at least 2 characters to be valid. (number + type)
|
||||||
|
if (duration == null || duration.length < 2) {
|
||||||
|
return maxValue;
|
||||||
|
} else if (duration.substring(duration.length - 2) == 'ms') {
|
||||||
|
let value = NumberWrapper.parseInt(this.stripLetters(duration), 10);
|
||||||
|
if (value > maxValue) maxValue = value;
|
||||||
|
} else if (duration.substring(duration.length - 1) == 's') {
|
||||||
|
let ms = NumberWrapper.parseFloat(this.stripLetters(duration)) * 1000;
|
||||||
|
let value = Math.floor(ms);
|
||||||
|
if (value > maxValue) maxValue = value;
|
||||||
|
}
|
||||||
|
return maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Strips the letters from the duration string
|
||||||
|
* @param str
|
||||||
|
* @returns {string}
|
||||||
|
*/
|
||||||
|
stripLetters(str: string): string {
|
||||||
|
return StringWrapper.replaceAll(str, RegExpWrapper.create('[^0-9]+$', ''), '');
|
||||||
|
}
|
||||||
|
}
|
19
modules/angular2/src/animate/animation_builder.ts
Normal file
19
modules/angular2/src/animate/animation_builder.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
|
||||||
|
import {CssAnimationBuilder} from './css_animation_builder';
|
||||||
|
import {BrowserDetails} from './browser_details';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class AnimationBuilder {
|
||||||
|
/**
|
||||||
|
* Used for DI
|
||||||
|
* @param browserDetails
|
||||||
|
*/
|
||||||
|
constructor(public browserDetails: BrowserDetails) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Creates a new CSS Animation
|
||||||
|
* @returns {CssAnimationBuilder}
|
||||||
|
*/
|
||||||
|
css(): CssAnimationBuilder { return new CssAnimationBuilder(this.browserDetails); }
|
||||||
|
}
|
54
modules/angular2/src/animate/browser_details.ts
Normal file
54
modules/angular2/src/animate/browser_details.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
import {Math} from 'angular2/src/core/facade/math';
|
||||||
|
import {DOM} from 'angular2/src/core/dom/dom_adapter';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class BrowserDetails {
|
||||||
|
elapsedTimeIncludesDelay = false;
|
||||||
|
|
||||||
|
constructor() { this.doesElapsedTimeIncludesDelay(); }
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Determines if `event.elapsedTime` includes transition delay in the current browser. At this
|
||||||
|
* time, Chrome and Opera seem to be the only browsers that include this.
|
||||||
|
*/
|
||||||
|
doesElapsedTimeIncludesDelay(): void {
|
||||||
|
var div = DOM.createElement('div');
|
||||||
|
DOM.setAttribute(div, 'style', `position: absolute; top: -9999px; left: -9999px; width: 1px;
|
||||||
|
height: 1px; transition: all 1ms linear 1ms;`);
|
||||||
|
// Firefox requires that we wait for 2 frames for some reason
|
||||||
|
this.raf(timestamp => {
|
||||||
|
DOM.on(div, 'transitionend', (event: any) => {
|
||||||
|
var elapsed = Math.round(event.elapsedTime * 1000);
|
||||||
|
this.elapsedTimeIncludesDelay = elapsed == 2;
|
||||||
|
DOM.remove(div);
|
||||||
|
});
|
||||||
|
DOM.setStyle(div, 'width', '2px');
|
||||||
|
}, 2);
|
||||||
|
}
|
||||||
|
|
||||||
|
raf(callback: Function, frames: number = 1): Function {
|
||||||
|
var queue: RafQueue = new RafQueue(callback, frames);
|
||||||
|
return () => queue.cancel();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class RafQueue {
|
||||||
|
currentFrameId: number;
|
||||||
|
constructor(public callback: Function, public frames: number) { this._raf(); }
|
||||||
|
private _raf() {
|
||||||
|
this.currentFrameId = DOM.requestAnimationFrame(timestamp => this._nextFrame(timestamp));
|
||||||
|
}
|
||||||
|
private _nextFrame(timestamp: number) {
|
||||||
|
this.frames--;
|
||||||
|
if (this.frames > 0) {
|
||||||
|
this._raf();
|
||||||
|
} else {
|
||||||
|
this.callback(timestamp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
cancel() {
|
||||||
|
DOM.cancelAnimationFrame(this.currentFrameId);
|
||||||
|
this.currentFrameId = null;
|
||||||
|
}
|
||||||
|
}
|
93
modules/angular2/src/animate/css_animation_builder.ts
Normal file
93
modules/angular2/src/animate/css_animation_builder.ts
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
import {CssAnimationOptions} from './css_animation_options';
|
||||||
|
import {Animation} from './animation';
|
||||||
|
import {BrowserDetails} from './browser_details';
|
||||||
|
|
||||||
|
export class CssAnimationBuilder {
|
||||||
|
/** @type {CssAnimationOptions} */
|
||||||
|
data: CssAnimationOptions = new CssAnimationOptions();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Accepts public properties for CssAnimationBuilder
|
||||||
|
*/
|
||||||
|
constructor(public browserDetails: BrowserDetails) {}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a temporary class that will be removed at the end of the animation
|
||||||
|
* @param className
|
||||||
|
*/
|
||||||
|
addAnimationClass(className: string): CssAnimationBuilder {
|
||||||
|
this.data.animationClasses.push(className);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Adds a class that will remain on the element after the animation has finished
|
||||||
|
* @param className
|
||||||
|
*/
|
||||||
|
addClass(className: string): CssAnimationBuilder {
|
||||||
|
this.data.classesToAdd.push(className);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes a class from the element
|
||||||
|
* @param className
|
||||||
|
*/
|
||||||
|
removeClass(className: string): CssAnimationBuilder {
|
||||||
|
this.data.classesToRemove.push(className);
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the animation duration (and overrides any defined through CSS)
|
||||||
|
* @param duration
|
||||||
|
*/
|
||||||
|
setDuration(duration: number): CssAnimationBuilder {
|
||||||
|
this.data.duration = duration;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the animation delay (and overrides any defined through CSS)
|
||||||
|
* @param delay
|
||||||
|
*/
|
||||||
|
setDelay(delay: number): CssAnimationBuilder {
|
||||||
|
this.data.delay = delay;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets styles for both the initial state and the destination state
|
||||||
|
* @param from
|
||||||
|
* @param to
|
||||||
|
*/
|
||||||
|
setStyles(from: StringMap<string, any>, to: StringMap<string, any>): CssAnimationBuilder {
|
||||||
|
return this.setFromStyles(from).setToStyles(to);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the initial styles for the animation
|
||||||
|
* @param from
|
||||||
|
*/
|
||||||
|
setFromStyles(from: StringMap<string, any>): CssAnimationBuilder {
|
||||||
|
this.data.fromStyles = from;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Sets the destination styles for the animation
|
||||||
|
* @param to
|
||||||
|
*/
|
||||||
|
setToStyles(to: StringMap<string, any>): CssAnimationBuilder {
|
||||||
|
this.data.toStyles = to;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Starts the animation and returns a promise
|
||||||
|
* @param element
|
||||||
|
*/
|
||||||
|
start(element: HTMLElement): Animation {
|
||||||
|
return new Animation(element, this.data, this.browserDetails);
|
||||||
|
}
|
||||||
|
}
|
22
modules/angular2/src/animate/css_animation_options.ts
Normal file
22
modules/angular2/src/animate/css_animation_options.ts
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
export class CssAnimationOptions {
|
||||||
|
/** initial styles for the element */
|
||||||
|
fromStyles: StringMap<string, any>;
|
||||||
|
|
||||||
|
/** destination styles for the element */
|
||||||
|
toStyles: StringMap<string, any>;
|
||||||
|
|
||||||
|
/** classes to be added to the element */
|
||||||
|
classesToAdd: string[] = [];
|
||||||
|
|
||||||
|
/** classes to be removed from the element */
|
||||||
|
classesToRemove: string[] = [];
|
||||||
|
|
||||||
|
/** classes to be added for the duration of the animation */
|
||||||
|
animationClasses: string[] = [];
|
||||||
|
|
||||||
|
/** override the duration of the animation (in milliseconds) */
|
||||||
|
duration: number;
|
||||||
|
|
||||||
|
/** override the transition delay (in milliseconds) */
|
||||||
|
delay: number;
|
||||||
|
}
|
@ -86,6 +86,8 @@ import {APP_COMPONENT_REF_PROMISE, APP_COMPONENT} from './application_tokens';
|
|||||||
import {wtfInit} from './profile/wtf_init';
|
import {wtfInit} from './profile/wtf_init';
|
||||||
import {EXCEPTION_BINDING} from './platform_bindings';
|
import {EXCEPTION_BINDING} from './platform_bindings';
|
||||||
import {ApplicationRef} from './application_ref';
|
import {ApplicationRef} from './application_ref';
|
||||||
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
|
import {BrowserDetails} from 'angular2/src/animate/browser_details';
|
||||||
|
|
||||||
var _rootInjector: Injector;
|
var _rootInjector: Injector;
|
||||||
|
|
||||||
@ -161,6 +163,8 @@ function _injectorBindings(appComponentType): Array<Type | Binding | any[]> {
|
|||||||
Testability,
|
Testability,
|
||||||
AnchorBasedAppRootUrl,
|
AnchorBasedAppRootUrl,
|
||||||
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
bind(AppRootUrl).toAlias(AnchorBasedAppRootUrl),
|
||||||
|
BrowserDetails,
|
||||||
|
AnimationBuilder,
|
||||||
FORM_BINDINGS
|
FORM_BINDINGS
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -451,6 +451,8 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
|||||||
return element.dataset[name];
|
return element.dataset[name];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getComputedStyle(elem) => elem.getComputedStyle();
|
||||||
|
|
||||||
// TODO(tbosch): move this into a separate environment class once we have it
|
// TODO(tbosch): move this into a separate environment class once we have it
|
||||||
setGlobalVar(String path, value) {
|
setGlobalVar(String path, value) {
|
||||||
var parts = path.split('.');
|
var parts = path.split('.');
|
||||||
@ -465,6 +467,14 @@ class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
|||||||
}
|
}
|
||||||
obj[parts.removeAt(0)] = value;
|
obj[parts.removeAt(0)] = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(callback) {
|
||||||
|
return window.requestAnimationFrame(callback);
|
||||||
|
}
|
||||||
|
|
||||||
|
cancelAnimationFrame(id) {
|
||||||
|
window.cancelAnimationFrame(id);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
var baseElement = null;
|
var baseElement = null;
|
||||||
|
@ -317,8 +317,11 @@ export class BrowserDomAdapter extends GenericBrowserDomAdapter {
|
|||||||
this.setAttribute(element, 'data-' + name, value);
|
this.setAttribute(element, 'data-' + name, value);
|
||||||
}
|
}
|
||||||
getData(element, name: string): string { return this.getAttribute(element, 'data-' + name); }
|
getData(element, name: string): string { return this.getAttribute(element, 'data-' + name); }
|
||||||
|
getComputedStyle(element): any { return getComputedStyle(element); }
|
||||||
// TODO(tbosch): move this into a separate environment class once we have it
|
// TODO(tbosch): move this into a separate environment class once we have it
|
||||||
setGlobalVar(path: string, value: any) { setValueOnPath(global, path, value); }
|
setGlobalVar(path: string, value: any) { setValueOnPath(global, path, value); }
|
||||||
|
requestAnimationFrame(callback): number { return window.requestAnimationFrame(callback); }
|
||||||
|
cancelAnimationFrame(id: number) { window.cancelAnimationFrame(id); }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -133,6 +133,9 @@ export class DomAdapter {
|
|||||||
resetBaseElement(): void { throw _abstract(); }
|
resetBaseElement(): void { throw _abstract(); }
|
||||||
getUserAgent(): string { throw _abstract(); }
|
getUserAgent(): string { throw _abstract(); }
|
||||||
setData(element, name: string, value: string) { throw _abstract(); }
|
setData(element, name: string, value: string) { throw _abstract(); }
|
||||||
|
getComputedStyle(element): any { throw _abstract(); }
|
||||||
getData(element, name: string): string { throw _abstract(); }
|
getData(element, name: string): string { throw _abstract(); }
|
||||||
setGlobalVar(name: string, value: any) { throw _abstract(); }
|
setGlobalVar(name: string, value: any) { throw _abstract(); }
|
||||||
|
requestAnimationFrame(callback): number { throw _abstract(); }
|
||||||
|
cancelAnimationFrame(id) { throw _abstract(); }
|
||||||
}
|
}
|
||||||
|
@ -412,6 +412,10 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||||||
this.setAttribute(element, 'data-${name}', value);
|
this.setAttribute(element, 'data-${name}', value);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getComputedStyle(element) {
|
||||||
|
throw 'not implemented';
|
||||||
|
}
|
||||||
|
|
||||||
String getData(Element element, String name) {
|
String getData(Element element, String name) {
|
||||||
return this.getAttribute(element, 'data-${name}');
|
return this.getAttribute(element, 'data-${name}');
|
||||||
}
|
}
|
||||||
@ -420,4 +424,11 @@ class Html5LibDomAdapter implements DomAdapter {
|
|||||||
setGlobalVar(String name, value) {
|
setGlobalVar(String name, value) {
|
||||||
// noop on the server
|
// noop on the server
|
||||||
}
|
}
|
||||||
|
|
||||||
|
requestAnimationFrame(callback) {
|
||||||
|
throw 'not implemented';
|
||||||
|
}
|
||||||
|
cancelAnimationFrame(id) {
|
||||||
|
throw 'not implemented';
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -539,9 +539,12 @@ export class Parse5DomAdapter extends DomAdapter {
|
|||||||
getLocation(): Location { throw 'not implemented'; }
|
getLocation(): Location { throw 'not implemented'; }
|
||||||
getUserAgent(): string { return "Fake user agent"; }
|
getUserAgent(): string { return "Fake user agent"; }
|
||||||
getData(el, name: string): string { return this.getAttribute(el, 'data-' + name); }
|
getData(el, name: string): string { return this.getAttribute(el, 'data-' + name); }
|
||||||
|
getComputedStyle(el): any { throw 'not implemented'; }
|
||||||
setData(el, name: string, value: string) { this.setAttribute(el, 'data-' + name, value); }
|
setData(el, name: string, value: string) { this.setAttribute(el, 'data-' + name, value); }
|
||||||
// TODO(tbosch): move this into a separate environment class once we have it
|
// TODO(tbosch): move this into a separate environment class once we have it
|
||||||
setGlobalVar(path: string, value: any) { setValueOnPath(global, path, value); }
|
setGlobalVar(path: string, value: any) { setValueOnPath(global, path, value); }
|
||||||
|
requestAnimationFrame(callback): number { return setTimeout(callback, 0); }
|
||||||
|
cancelAnimationFrame(id: number) { clearTimeout(id); }
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: build a proper list, this one is all the keys of a HTMLInputElement
|
// TODO: build a proper list, this one is all the keys of a HTMLInputElement
|
||||||
|
@ -19,4 +19,6 @@ class Math {
|
|||||||
static num ceil(num a) => a.ceil();
|
static num ceil(num a) => a.ceil();
|
||||||
|
|
||||||
static num sqrt(num x) => math.sqrt(x);
|
static num sqrt(num x) => math.sqrt(x);
|
||||||
|
|
||||||
|
static num round(num x) => x.round();
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,5 @@
|
|||||||
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
|
import {Inject, Injectable, OpaqueToken} from 'angular2/src/core/di';
|
||||||
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
import {isPresent, isBlank, RegExpWrapper, CONST_EXPR} from 'angular2/src/core/facade/lang';
|
import {isPresent, isBlank, RegExpWrapper, CONST_EXPR} from 'angular2/src/core/facade/lang';
|
||||||
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
import {BaseException, WrappedException} from 'angular2/src/core/facade/exceptions';
|
||||||
|
|
||||||
@ -42,7 +43,7 @@ export class DomRenderer extends Renderer {
|
|||||||
* @private
|
* @private
|
||||||
*/
|
*/
|
||||||
constructor(private _eventManager: EventManager,
|
constructor(private _eventManager: EventManager,
|
||||||
private _domSharedStylesHost: DomSharedStylesHost,
|
private _domSharedStylesHost: DomSharedStylesHost, private _animate: AnimationBuilder,
|
||||||
private _templateCloner: TemplateCloner, @Inject(DOCUMENT) document) {
|
private _templateCloner: TemplateCloner, @Inject(DOCUMENT) document) {
|
||||||
super();
|
super();
|
||||||
this._document = document;
|
this._document = document;
|
||||||
@ -94,7 +95,51 @@ export class DomRenderer extends Renderer {
|
|||||||
var previousFragmentNodes = resolveInternalDomFragment(previousFragmentRef);
|
var previousFragmentNodes = resolveInternalDomFragment(previousFragmentRef);
|
||||||
if (previousFragmentNodes.length > 0) {
|
if (previousFragmentNodes.length > 0) {
|
||||||
var sibling = previousFragmentNodes[previousFragmentNodes.length - 1];
|
var sibling = previousFragmentNodes[previousFragmentNodes.length - 1];
|
||||||
moveNodesAfterSibling(sibling, resolveInternalDomFragment(fragmentRef));
|
let nodes = resolveInternalDomFragment(fragmentRef);
|
||||||
|
moveNodesAfterSibling(sibling, nodes);
|
||||||
|
this.animateNodesEnter(nodes);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Iterates through all nodes being added to the DOM and animates them if necessary
|
||||||
|
* @param nodes
|
||||||
|
*/
|
||||||
|
animateNodesEnter(nodes: Node[]) {
|
||||||
|
for (let i = 0; i < nodes.length; i++) this.animateNodeEnter(nodes[i]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Performs animations if necessary
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
animateNodeEnter(node: Node) {
|
||||||
|
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
||||||
|
DOM.addClass(node, 'ng-enter');
|
||||||
|
this._animate.css()
|
||||||
|
.addAnimationClass('ng-enter-active')
|
||||||
|
.start(<HTMLElement>node)
|
||||||
|
.onComplete(() => { DOM.removeClass(node, 'ng-enter'); });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* If animations are necessary, performs animations then removes the element; otherwise, it just
|
||||||
|
* removes the element.
|
||||||
|
* @param node
|
||||||
|
*/
|
||||||
|
animateNodeLeave(node: Node) {
|
||||||
|
if (DOM.isElementNode(node) && DOM.hasClass(node, 'ng-animate')) {
|
||||||
|
DOM.addClass(node, 'ng-leave');
|
||||||
|
this._animate.css()
|
||||||
|
.addAnimationClass('ng-leave-active')
|
||||||
|
.start(<HTMLElement>node)
|
||||||
|
.onComplete(() => {
|
||||||
|
DOM.removeClass(node, 'ng-leave');
|
||||||
|
DOM.remove(node);
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
DOM.remove(node);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +149,9 @@ export class DomRenderer extends Renderer {
|
|||||||
}
|
}
|
||||||
var parentView = resolveInternalDomView(elementRef.renderView);
|
var parentView = resolveInternalDomView(elementRef.renderView);
|
||||||
var element = parentView.boundElements[elementRef.renderBoundElementIndex];
|
var element = parentView.boundElements[elementRef.renderBoundElementIndex];
|
||||||
moveNodesAfterSibling(element, resolveInternalDomFragment(fragmentRef));
|
var nodes = resolveInternalDomFragment(fragmentRef);
|
||||||
|
moveNodesAfterSibling(element, nodes);
|
||||||
|
this.animateNodesEnter(nodes);
|
||||||
}
|
}
|
||||||
|
|
||||||
_detachFragmentScope = wtfCreateScope('DomRenderer#detachFragment()');
|
_detachFragmentScope = wtfCreateScope('DomRenderer#detachFragment()');
|
||||||
@ -112,7 +159,7 @@ export class DomRenderer extends Renderer {
|
|||||||
var s = this._detachFragmentScope();
|
var s = this._detachFragmentScope();
|
||||||
var fragmentNodes = resolveInternalDomFragment(fragmentRef);
|
var fragmentNodes = resolveInternalDomFragment(fragmentRef);
|
||||||
for (var i = 0; i < fragmentNodes.length; i++) {
|
for (var i = 0; i < fragmentNodes.length; i++) {
|
||||||
DOM.remove(fragmentNodes[i]);
|
this.animateNodeLeave(fragmentNodes[i]);
|
||||||
}
|
}
|
||||||
wtfLeave(s);
|
wtfLeave(s);
|
||||||
}
|
}
|
||||||
|
33
modules/angular2/src/mock/animation_builder_mock.ts
Normal file
33
modules/angular2/src/mock/animation_builder_mock.ts
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import {Injectable} from 'angular2/src/core/di';
|
||||||
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
|
import {CssAnimationBuilder} from 'angular2/src/animate/css_animation_builder';
|
||||||
|
import {CssAnimationOptions} from 'angular2/src/animate/css_animation_options';
|
||||||
|
import {Animation} from 'angular2/src/animate/animation';
|
||||||
|
import {BrowserDetails} from 'angular2/src/animate/browser_details';
|
||||||
|
|
||||||
|
@Injectable()
|
||||||
|
export class MockAnimationBuilder extends AnimationBuilder {
|
||||||
|
constructor() { super(null); }
|
||||||
|
css(): MockCssAnimationBuilder { return new MockCssAnimationBuilder(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockCssAnimationBuilder extends CssAnimationBuilder {
|
||||||
|
constructor() { super(null); }
|
||||||
|
start(element: HTMLElement): Animation { return new MockAnimation(element, this.data); }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockBrowserAbstraction extends BrowserDetails {
|
||||||
|
doesElapsedTimeIncludesDelay(): void { this.elapsedTimeIncludesDelay = false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
class MockAnimation extends Animation {
|
||||||
|
private _callback: Function;
|
||||||
|
constructor(element: HTMLElement, data: CssAnimationOptions) {
|
||||||
|
super(element, data, new MockBrowserAbstraction());
|
||||||
|
}
|
||||||
|
wait(callback: Function) { this._callback = callback; }
|
||||||
|
flush() {
|
||||||
|
this._callback(0);
|
||||||
|
this._callback = null;
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,7 @@
|
|||||||
import {bind, Binding} from 'angular2/src/core/di';
|
import {bind, Binding} from 'angular2/src/core/di';
|
||||||
import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
|
import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
|
||||||
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
|
import {MockAnimationBuilder} from 'angular2/src/mock/animation_builder_mock';
|
||||||
|
|
||||||
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
import {Compiler, CompilerCache} from 'angular2/src/core/compiler/compiler';
|
||||||
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
@ -149,6 +151,7 @@ function _getAppBindings() {
|
|||||||
StyleInliner,
|
StyleInliner,
|
||||||
TestComponentBuilder,
|
TestComponentBuilder,
|
||||||
bind(NgZone).toClass(MockNgZone),
|
bind(NgZone).toClass(MockNgZone),
|
||||||
|
bind(AnimationBuilder).toClass(MockAnimationBuilder),
|
||||||
EventManager,
|
EventManager,
|
||||||
new Binding(EVENT_MANAGER_PLUGINS, {toClass: DomEventsPlugin, multi: true})
|
new Binding(EVENT_MANAGER_PLUGINS, {toClass: DomEventsPlugin, multi: true})
|
||||||
];
|
];
|
||||||
|
@ -2,6 +2,8 @@
|
|||||||
// There should be a way to refactor application so that this file is unnecessary. See #3277
|
// There should be a way to refactor application so that this file is unnecessary. See #3277
|
||||||
import {Injector, bind, Binding} from "angular2/src/core/di";
|
import {Injector, bind, Binding} from "angular2/src/core/di";
|
||||||
import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
|
import {DEFAULT_PIPES} from 'angular2/src/core/pipes';
|
||||||
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
|
import {BrowserDetails} from 'angular2/src/animate/browser_details';
|
||||||
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
import {Reflector, reflector} from 'angular2/src/core/reflection/reflection';
|
||||||
import {
|
import {
|
||||||
Parser,
|
Parser,
|
||||||
@ -140,7 +142,9 @@ function _injectorBindings(): any[] {
|
|||||||
MessageBasedXHRImpl,
|
MessageBasedXHRImpl,
|
||||||
MessageBasedRenderer,
|
MessageBasedRenderer,
|
||||||
ServiceMessageBrokerFactory,
|
ServiceMessageBrokerFactory,
|
||||||
ClientMessageBrokerFactory
|
ClientMessageBrokerFactory,
|
||||||
|
BrowserDetails,
|
||||||
|
AnimationBuilder,
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
119
modules/angular2/test/animate/animation_builder_spec.ts
Normal file
119
modules/angular2/test/animate/animation_builder_spec.ts
Normal file
@ -0,0 +1,119 @@
|
|||||||
|
import {el, describe, it, expect, inject, SpyObject} from 'angular2/test_lib';
|
||||||
|
import {AnimationBuilder} from 'angular2/src/animate/animation_builder';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
describe("AnimationBuilder", () => {
|
||||||
|
|
||||||
|
it('should have data object', inject([AnimationBuilder], animate => {
|
||||||
|
var animateCss = animate.css();
|
||||||
|
expect(animateCss.data).toBeDefined();
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should allow you to add classes', inject([AnimationBuilder], animate => {
|
||||||
|
var animateCss = animate.css();
|
||||||
|
animateCss.addClass('some-class');
|
||||||
|
expect(animateCss.data.classesToAdd).toEqual(['some-class']);
|
||||||
|
animateCss.addClass('another-class');
|
||||||
|
expect(animateCss.data.classesToAdd).toEqual(['some-class', 'another-class']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should allow you to add temporary classes', inject([AnimationBuilder], animate => {
|
||||||
|
var animateCss = animate.css();
|
||||||
|
animateCss.addAnimationClass('some-class');
|
||||||
|
expect(animateCss.data.animationClasses).toEqual(['some-class']);
|
||||||
|
animateCss.addAnimationClass('another-class');
|
||||||
|
expect(animateCss.data.animationClasses).toEqual(['some-class', 'another-class']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should allow you to remove classes', inject([AnimationBuilder], animate => {
|
||||||
|
var animateCss = animate.css();
|
||||||
|
animateCss.removeClass('some-class');
|
||||||
|
expect(animateCss.data.classesToRemove).toEqual(['some-class']);
|
||||||
|
animateCss.removeClass('another-class');
|
||||||
|
expect(animateCss.data.classesToRemove).toEqual(['some-class', 'another-class']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support chaining', inject([AnimationBuilder], animate => {
|
||||||
|
var animateCss = animate.css()
|
||||||
|
.addClass('added-class')
|
||||||
|
.removeClass('removed-class')
|
||||||
|
.addAnimationClass('temp-class')
|
||||||
|
.addClass('another-added-class');
|
||||||
|
expect(animateCss.data.classesToAdd).toEqual(['added-class', 'another-added-class']);
|
||||||
|
expect(animateCss.data.classesToRemove).toEqual(['removed-class']);
|
||||||
|
expect(animateCss.data.animationClasses).toEqual(['temp-class']);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support duration and delay', inject([AnimationBuilder], (animate) => {
|
||||||
|
var animateCss = animate.css();
|
||||||
|
animateCss.setDelay(100).setDuration(200);
|
||||||
|
expect(animateCss.data.duration).toBe(200);
|
||||||
|
expect(animateCss.data.delay).toBe(100);
|
||||||
|
|
||||||
|
var element = el('<div></div>');
|
||||||
|
var runner = animateCss.start(element);
|
||||||
|
runner.flush();
|
||||||
|
|
||||||
|
expect(runner.computedDelay).toBe(100);
|
||||||
|
expect(runner.computedDuration).toBe(200);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support from styles', inject([AnimationBuilder], animate => {
|
||||||
|
var animateCss = animate.css();
|
||||||
|
animateCss.setFromStyles({'backgroundColor': 'blue'});
|
||||||
|
expect(animateCss.data.fromStyles).toBeDefined();
|
||||||
|
|
||||||
|
var element = el('<div></div>');
|
||||||
|
animateCss.start(element);
|
||||||
|
|
||||||
|
expect(element.style.getPropertyValue('background-color')).toEqual('blue');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should support duration and delay defined in CSS', inject([AnimationBuilder], (animate) => {
|
||||||
|
var animateCss = animate.css();
|
||||||
|
var element = el('<div style="transition: 0.5s ease 250ms;"></div>');
|
||||||
|
var runner = animateCss.start(element);
|
||||||
|
runner.flush();
|
||||||
|
|
||||||
|
expect(runner.computedDuration).toEqual(500);
|
||||||
|
expect(runner.computedDelay).toEqual(250);
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should add classes', inject([AnimationBuilder], (animate) => {
|
||||||
|
var animateCss = animate.css().addClass('one').addClass('two');
|
||||||
|
var element = el('<div></div>');
|
||||||
|
var runner = animateCss.start(element);
|
||||||
|
|
||||||
|
expect(element).not.toHaveCssClass('one');
|
||||||
|
expect(element).not.toHaveCssClass('two');
|
||||||
|
|
||||||
|
runner.flush();
|
||||||
|
|
||||||
|
expect(element).toHaveCssClass('one');
|
||||||
|
expect(element).toHaveCssClass('two');
|
||||||
|
}));
|
||||||
|
|
||||||
|
it('should call `onComplete` method after animations have finished',
|
||||||
|
inject([AnimationBuilder], (animate) => {
|
||||||
|
var spyObject = new SpyObject();
|
||||||
|
var callback = spyObject.spy('animationFinished');
|
||||||
|
var runner = animate.css()
|
||||||
|
.addClass('one')
|
||||||
|
.addClass('two')
|
||||||
|
.setDuration(100)
|
||||||
|
.start(el('<div></div>'))
|
||||||
|
.onComplete(callback);
|
||||||
|
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
runner.flush();
|
||||||
|
|
||||||
|
expect(callback).not.toHaveBeenCalled();
|
||||||
|
|
||||||
|
runner.handleAnimationCompleted();
|
||||||
|
|
||||||
|
expect(callback).toHaveBeenCalled();
|
||||||
|
}));
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
@ -21,7 +21,6 @@ transformers:
|
|||||||
# The build currently fails on material files because there is not yet
|
# The build currently fails on material files because there is not yet
|
||||||
# support for transforming cross-package urls. (see issue #2982)
|
# support for transforming cross-package urls. (see issue #2982)
|
||||||
- 'web/src/material/**'
|
- 'web/src/material/**'
|
||||||
- 'web/src/zippy_component/**'
|
|
||||||
# No need to transform the dart:mirrors specific entrypoints
|
# No need to transform the dart:mirrors specific entrypoints
|
||||||
- '**/index_dynamic.dart'
|
- '**/index_dynamic.dart'
|
||||||
entry_points:
|
entry_points:
|
||||||
@ -39,6 +38,7 @@ transformers:
|
|||||||
- web/src/web_workers/todo/background_index.dart
|
- web/src/web_workers/todo/background_index.dart
|
||||||
- web/src/web_workers/message_broker/background_index.dart
|
- web/src/web_workers/message_broker/background_index.dart
|
||||||
- web/src/web_workers/kitchen_sink/background_index.dart
|
- web/src/web_workers/kitchen_sink/background_index.dart
|
||||||
|
- web/src/zippy_component/index.dart
|
||||||
|
|
||||||
# These entrypoints are disabled untl the transformer supports UI bootstrap (issue #3971)
|
# These entrypoints are disabled untl the transformer supports UI bootstrap (issue #3971)
|
||||||
# - web/src/web_workers/message_broker/index.dart
|
# - web/src/web_workers/message_broker/index.dart
|
||||||
@ -53,7 +53,6 @@ transformers:
|
|||||||
# - web/src/material/progress-linear/index.dart
|
# - web/src/material/progress-linear/index.dart
|
||||||
# - web/src/material/radio/index.dart
|
# - web/src/material/radio/index.dart
|
||||||
# - web/src/material/switcher/index.dart
|
# - web/src/material/switcher/index.dart
|
||||||
# - web/src/zippy_component/index.dart
|
|
||||||
#
|
#
|
||||||
# This entrypoint is not needed:
|
# This entrypoint is not needed:
|
||||||
# - web/src/material/demo_common.dart
|
# - web/src/material/demo_common.dart
|
||||||
|
14
modules/examples/src/animate/animate-app.ts
Normal file
14
modules/examples/src/animate/animate-app.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {Component, View, NgIf} from 'angular2/angular2';
|
||||||
|
|
||||||
|
@Component({selector: 'animate-app'})
|
||||||
|
@View({
|
||||||
|
directives: [NgIf],
|
||||||
|
template: `
|
||||||
|
<h1>The box is {{visible ? 'visible' : 'hidden'}}</h1>
|
||||||
|
<div class="ng-animate box" *ng-if="visible"></div>
|
||||||
|
<button (click)="visible = !visible">Animate</button>
|
||||||
|
`
|
||||||
|
})
|
||||||
|
export class AnimateApp {
|
||||||
|
visible: boolean = false;
|
||||||
|
}
|
25
modules/examples/src/animate/css/app.css
Normal file
25
modules/examples/src/animate/css/app.css
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
body {
|
||||||
|
padding: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.box {
|
||||||
|
width: 100px;
|
||||||
|
height: 100px;
|
||||||
|
background-color: red;
|
||||||
|
transition: all 0.35s ease-in-out;
|
||||||
|
}
|
||||||
|
.box.blue {
|
||||||
|
background-color: blue;
|
||||||
|
}
|
||||||
|
.box.ng-enter {
|
||||||
|
opacity: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
|
.box.ng-enter-active {
|
||||||
|
opacity: 1;
|
||||||
|
height: 100px;
|
||||||
|
}
|
||||||
|
.box.ng-leave-active {
|
||||||
|
opacity: 0;
|
||||||
|
height: 0;
|
||||||
|
}
|
10
modules/examples/src/animate/index.html
Normal file
10
modules/examples/src/animate/index.html
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
<!doctype html>
|
||||||
|
<html>
|
||||||
|
<title>Animation Example</title>
|
||||||
|
<link rel="stylesheet" type="text/css" href="./css/app.css" />
|
||||||
|
<base href="/examples/src/animate/">
|
||||||
|
<body>
|
||||||
|
<animate-app>Loading...</animate-app>
|
||||||
|
$SCRIPTS$
|
||||||
|
</body>
|
||||||
|
</html>
|
6
modules/examples/src/animate/index.ts
Normal file
6
modules/examples/src/animate/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
import {AnimateApp} from './animate-app';
|
||||||
|
import {bootstrap} from 'angular2/bootstrap';
|
||||||
|
|
||||||
|
export function main() {
|
||||||
|
bootstrap(AnimateApp);
|
||||||
|
}
|
@ -40,6 +40,7 @@ const kServedPaths = [
|
|||||||
'benchmarks_external/src/static_tree',
|
'benchmarks_external/src/static_tree',
|
||||||
|
|
||||||
// Relative (to /modules) paths to example directories
|
// Relative (to /modules) paths to example directories
|
||||||
|
'examples/src/animate',
|
||||||
'examples/src/benchpress',
|
'examples/src/benchpress',
|
||||||
'examples/src/model_driven_forms',
|
'examples/src/model_driven_forms',
|
||||||
'examples/src/template_driven_forms',
|
'examples/src/template_driven_forms',
|
||||||
|
@ -20,6 +20,7 @@ module.exports = function makeNodeTree(destinationPath) {
|
|||||||
include: ['angular2/**', 'benchpress/**', '**/e2e_test/**'],
|
include: ['angular2/**', 'benchpress/**', '**/e2e_test/**'],
|
||||||
exclude: [
|
exclude: [
|
||||||
// the following code and tests are not compatible with CJS/node environment
|
// the following code and tests are not compatible with CJS/node environment
|
||||||
|
'angular2/test/animate/**',
|
||||||
'angular2/test/core/zone/**',
|
'angular2/test/core/zone/**',
|
||||||
'angular2/test/test_lib/fake_async_spec.ts',
|
'angular2/test/test_lib/fake_async_spec.ts',
|
||||||
'angular2/test/core/render/xhr_impl_spec.ts',
|
'angular2/test/core/render/xhr_impl_spec.ts',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user