feat(animations): expose `element` and `params` within transition matchers (#22693)
PR Close #22693
This commit is contained in:
parent
db56836425
commit
58b94e6f5e
|
@ -46,7 +46,8 @@ export interface StateAst extends Ast<AnimationMetadataType.State> {
|
|||
}
|
||||
|
||||
export interface TransitionAst extends Ast<AnimationMetadataType.Transition> {
|
||||
matchers: ((fromState: string, toState: string) => boolean)[];
|
||||
matchers: ((fromState: string, toState: string, element: any, params: {[key: string]:
|
||||
any}) => boolean)[];
|
||||
animation: Ast<AnimationMetadataType>;
|
||||
queryCount: number;
|
||||
depCount: number;
|
||||
|
|
|
@ -6,7 +6,8 @@
|
|||
* found in the LICENSE file at https://angular.io/license
|
||||
*/
|
||||
export const ANY_STATE = '*';
|
||||
export declare type TransitionMatcherFn = (fromState: any, toState: any) => boolean;
|
||||
export declare type TransitionMatcherFn =
|
||||
(fromState: any, toState: any, element: any, params: {[key: string]: any}) => boolean;
|
||||
|
||||
export function parseTransitionExpr(
|
||||
transitionValue: string | TransitionMatcherFn, errors: string[]): TransitionMatcherFn[] {
|
||||
|
|
|
@ -24,8 +24,8 @@ export class AnimationTransitionFactory {
|
|||
private _triggerName: string, public ast: TransitionAst,
|
||||
private _stateStyles: {[stateName: string]: AnimationStateStyles}) {}
|
||||
|
||||
match(currentState: any, nextState: any): boolean {
|
||||
return oneOrMoreTransitionsMatch(this.ast.matchers, currentState, nextState);
|
||||
match(currentState: any, nextState: any, element: any, params: {[key: string]: any}): boolean {
|
||||
return oneOrMoreTransitionsMatch(this.ast.matchers, currentState, nextState, element, params);
|
||||
}
|
||||
|
||||
buildStyles(stateName: string, params: {[key: string]: any}, errors: any[]) {
|
||||
|
@ -89,8 +89,9 @@ export class AnimationTransitionFactory {
|
|||
}
|
||||
|
||||
function oneOrMoreTransitionsMatch(
|
||||
matchFns: TransitionMatcherFn[], currentState: any, nextState: any): boolean {
|
||||
return matchFns.some(fn => fn(currentState, nextState));
|
||||
matchFns: TransitionMatcherFn[], currentState: any, nextState: any, element: any,
|
||||
params: {[key: string]: any}): boolean {
|
||||
return matchFns.some(fn => fn(currentState, nextState, element, params));
|
||||
}
|
||||
|
||||
export class AnimationStateStyles {
|
||||
|
|
|
@ -47,8 +47,10 @@ export class AnimationTrigger {
|
|||
|
||||
get containsQueries() { return this.ast.queryCount > 0; }
|
||||
|
||||
matchTransition(currentState: any, nextState: any): AnimationTransitionFactory|null {
|
||||
const entry = this.transitionFactories.find(f => f.match(currentState, nextState));
|
||||
matchTransition(currentState: any, nextState: any, element: any, params: {[key: string]: any}):
|
||||
AnimationTransitionFactory|null {
|
||||
const entry =
|
||||
this.transitionFactories.find(f => f.match(currentState, nextState, element, params));
|
||||
return entry || null;
|
||||
}
|
||||
|
||||
|
|
|
@ -248,7 +248,8 @@ export class AnimationTransitionNamespace {
|
|||
}
|
||||
});
|
||||
|
||||
let transition = trigger.matchTransition(fromState.value, toState.value);
|
||||
let transition =
|
||||
trigger.matchTransition(fromState.value, toState.value, element, toState.params);
|
||||
let isFallbackTransition = false;
|
||||
if (!transition) {
|
||||
if (!defaultToFallback) return;
|
||||
|
|
|
@ -103,7 +103,7 @@ import {makeTrigger} from '../shared';
|
|||
it('should null when no results are found', () => {
|
||||
const result = makeTrigger('name', [transition('a => b', animate(1111))]);
|
||||
|
||||
const trigger = result.matchTransition('b', 'a');
|
||||
const trigger = result.matchTransition('b', 'a', {}, {});
|
||||
expect(trigger).toBeFalsy();
|
||||
});
|
||||
|
||||
|
@ -226,7 +226,8 @@ function buildTransition(
|
|||
trigger: AnimationTrigger, element: any, fromState: any, toState: any,
|
||||
fromOptions?: AnimationOptions, toOptions?: AnimationOptions): AnimationTransitionInstruction|
|
||||
null {
|
||||
const trans = trigger.matchTransition(fromState, toState) !;
|
||||
const params = toOptions && toOptions.params || {};
|
||||
const trans = trigger.matchTransition(fromState, toState, element, params) !;
|
||||
if (trans) {
|
||||
const driver = new MockAnimationDriver();
|
||||
return trans.build(
|
||||
|
|
|
@ -115,7 +115,9 @@ export interface AnimationStateMetadata extends AnimationMetadata {
|
|||
* @experimental Animation support is experimental.
|
||||
*/
|
||||
export interface AnimationTransitionMetadata extends AnimationMetadata {
|
||||
expr: string|((fromState: string, toState: string) => boolean);
|
||||
expr: string|
|
||||
((fromState: string, toState: string, element?: any,
|
||||
params?: {[key: string]: any}) => boolean);
|
||||
animation: AnimationMetadata|AnimationMetadata[];
|
||||
options: AnimationOptions|null;
|
||||
}
|
||||
|
@ -294,6 +296,38 @@ export interface AnimationStaggerMetadata extends AnimationMetadata {
|
|||
* <div [@myAnimationTrigger]="myStatusExp">...</div>
|
||||
* ```
|
||||
*
|
||||
* ### Using an inline function
|
||||
* The `transition` animation method also supports reading an inline function which can decide
|
||||
* if its associated animation should be run.
|
||||
*
|
||||
* ```
|
||||
* // this method will be run each time the `myAnimationTrigger`
|
||||
* // trigger value changes...
|
||||
* function myInlineMatcherFn(fromState: string, toState: string, element: any, params: {[key:
|
||||
string]: any}): boolean {
|
||||
* // notice that `element` and `params` are also available here
|
||||
* return toState == 'yes-please-animate';
|
||||
* }
|
||||
*
|
||||
* @Component({
|
||||
* selector: 'my-component',
|
||||
* templateUrl: 'my-component-tpl.html',
|
||||
* animations: [
|
||||
* trigger('myAnimationTrigger', [
|
||||
* transition(myInlineMatcherFn, [
|
||||
* // the animation sequence code
|
||||
* ]),
|
||||
* ])
|
||||
* ]
|
||||
* })
|
||||
* class MyComponent {
|
||||
* myStatusExp = "yes-please-animate";
|
||||
* }
|
||||
* ```
|
||||
*
|
||||
* The inline method will be run each time the trigger
|
||||
* value changes
|
||||
*
|
||||
* ## Disable Animations
|
||||
* A special animation control binding called `@.disabled` can be placed on an element which will
|
||||
then disable animations for any inner animation triggers situated within the element as well as
|
||||
|
@ -844,7 +878,8 @@ export function keyframes(steps: AnimationStyleMetadata[]): AnimationKeyframesSe
|
|||
* @experimental Animation support is experimental.
|
||||
*/
|
||||
export function transition(
|
||||
stateChangeExpr: string | ((fromState: string, toState: string) => boolean),
|
||||
stateChangeExpr: string | ((fromState: string, toState: string, element?: any,
|
||||
params?: {[key: string]: any}) => boolean),
|
||||
steps: AnimationMetadata | AnimationMetadata[],
|
||||
options: AnimationOptions | null = null): AnimationTransitionMetadata {
|
||||
return {type: AnimationMetadataType.Transition, expr: stateChangeExpr, animation: steps, options};
|
||||
|
|
|
@ -38,7 +38,8 @@ export interface AnimationStateMetadata extends AnimationMetadata {
|
|||
* @deprecated This symbol has moved. Please Import from @angular/animations instead!
|
||||
*/
|
||||
export interface AnimationTransitionMetadata extends AnimationMetadata {
|
||||
expr: string|((fromState: string, toState: string) => boolean);
|
||||
expr: string|
|
||||
((fromState: string, toState: string, element: any, params: {[key: string]: any}) => boolean);
|
||||
animation: AnimationMetadata|AnimationMetadata[];
|
||||
}
|
||||
|
||||
|
|
|
@ -301,12 +301,15 @@ const DEFAULT_COMPONENT_ID = '1';
|
|||
|
||||
it('should allow a transition to use a function to determine what method to run', () => {
|
||||
let valueToMatch = '';
|
||||
const transitionFn =
|
||||
(fromState: string, toState: string) => { return toState == valueToMatch; };
|
||||
let capturedElement: any;
|
||||
const transitionFn = (fromState: string, toState: string, element: any) => {
|
||||
capturedElement = element;
|
||||
return toState == valueToMatch;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'if-cmp',
|
||||
template: '<div [@myAnimation]="exp"></div>',
|
||||
template: '<div #element [@myAnimation]="exp"></div>',
|
||||
animations: [
|
||||
trigger('myAnimation', [transition(
|
||||
transitionFn,
|
||||
|
@ -314,6 +317,8 @@ const DEFAULT_COMPONENT_ID = '1';
|
|||
]
|
||||
})
|
||||
class Cmp {
|
||||
@ViewChild('element')
|
||||
element: any;
|
||||
exp: any = '';
|
||||
}
|
||||
|
||||
|
@ -323,11 +328,13 @@ const DEFAULT_COMPONENT_ID = '1';
|
|||
const cmp = fixture.componentInstance;
|
||||
valueToMatch = cmp.exp = 'something';
|
||||
fixture.detectChanges();
|
||||
const element = cmp.element.nativeElement;
|
||||
|
||||
let players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
let [p1] = players;
|
||||
expect(p1.totalTime).toEqual(1234);
|
||||
expect(capturedElement).toEqual(element);
|
||||
resetLog();
|
||||
|
||||
valueToMatch = 'something-else';
|
||||
|
@ -338,6 +345,49 @@ const DEFAULT_COMPONENT_ID = '1';
|
|||
expect(players.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should allow a transition to use a function to determine what method to run and expose any parameter values',
|
||||
() => {
|
||||
const transitionFn =
|
||||
(fromState: string, toState: string, element: any, params: {[key: string]: any}) => {
|
||||
return params['doMatch'] == true;
|
||||
};
|
||||
|
||||
@Component({
|
||||
selector: 'if-cmp',
|
||||
template: '<div [@myAnimation]="{value:exp, params: {doMatch:doMatch}}"></div>',
|
||||
animations: [
|
||||
trigger(
|
||||
'myAnimation',
|
||||
[transition(
|
||||
transitionFn, [style({opacity: 0}), animate(3333, style({opacity: 1}))])]),
|
||||
]
|
||||
})
|
||||
class Cmp {
|
||||
doMatch = false;
|
||||
exp: any = '';
|
||||
}
|
||||
|
||||
TestBed.configureTestingModule({declarations: [Cmp]});
|
||||
|
||||
const fixture = TestBed.createComponent(Cmp);
|
||||
const cmp = fixture.componentInstance;
|
||||
cmp.doMatch = true;
|
||||
fixture.detectChanges();
|
||||
|
||||
let players = getLog();
|
||||
expect(players.length).toEqual(1);
|
||||
let [p1] = players;
|
||||
expect(p1.totalTime).toEqual(3333);
|
||||
resetLog();
|
||||
|
||||
cmp.doMatch = false;
|
||||
cmp.exp = 'this-wont-match';
|
||||
fixture.detectChanges();
|
||||
|
||||
players = getLog();
|
||||
expect(players.length).toEqual(0);
|
||||
});
|
||||
|
||||
it('should allow a state value to be `0`', () => {
|
||||
@Component({
|
||||
selector: 'if-cmp',
|
||||
|
|
|
@ -173,7 +173,9 @@ export interface AnimationStyleMetadata extends AnimationMetadata {
|
|||
/** @experimental */
|
||||
export interface AnimationTransitionMetadata extends AnimationMetadata {
|
||||
animation: AnimationMetadata | AnimationMetadata[];
|
||||
expr: string | ((fromState: string, toState: string) => boolean);
|
||||
expr: string | ((fromState: string, toState: string, element?: any, params?: {
|
||||
[key: string]: any;
|
||||
}) => boolean);
|
||||
options: AnimationOptions | null;
|
||||
}
|
||||
|
||||
|
@ -241,7 +243,9 @@ export declare function style(tokens: '*' | {
|
|||
}>): AnimationStyleMetadata;
|
||||
|
||||
/** @experimental */
|
||||
export declare function transition(stateChangeExpr: string | ((fromState: string, toState: string) => boolean), steps: AnimationMetadata | AnimationMetadata[], options?: AnimationOptions | null): AnimationTransitionMetadata;
|
||||
export declare function transition(stateChangeExpr: string | ((fromState: string, toState: string, element?: any, params?: {
|
||||
[key: string]: any;
|
||||
}) => boolean), steps: AnimationMetadata | AnimationMetadata[], options?: AnimationOptions | null): AnimationTransitionMetadata;
|
||||
|
||||
/** @experimental */
|
||||
export declare function trigger(name: string, definitions: AnimationMetadata[]): AnimationTriggerMetadata;
|
||||
|
|
|
@ -94,7 +94,9 @@ export interface AnimationTransitionEvent {
|
|||
/** @deprecated */
|
||||
export interface AnimationTransitionMetadata extends AnimationMetadata {
|
||||
animation: AnimationMetadata | AnimationMetadata[];
|
||||
expr: string | ((fromState: string, toState: string) => boolean);
|
||||
expr: string | ((fromState: string, toState: string, element: any, params: {
|
||||
[key: string]: any;
|
||||
}) => boolean);
|
||||
}
|
||||
|
||||
/** @deprecated */
|
||||
|
|
Loading…
Reference in New Issue