1
1
/** @coreapi @module transition */ /** for typedoc */
2
- import { extend , removeFrom , allTrueR , tail } from "../common/common" ;
2
+ import { extend , removeFrom , allTrueR , tail , uniqR , pushTo , equals , values , identity } from "../common/common" ;
3
3
import { isString , isFunction } from "../common/predicates" ;
4
4
import { PathNode } from "../path/node" ;
5
- import { TransitionStateHookFn , TransitionHookFn } from "./interface" ; // has or is using
5
+ import {
6
+ TransitionStateHookFn , TransitionHookFn , TransitionHookPhase , TransitionHookScope , IHookRegistry
7
+ } from "./interface" ; // has or is using
6
8
7
9
import {
8
10
HookRegOptions , HookMatchCriteria , IHookRegistration , TreeChanges ,
9
11
HookMatchCriterion , IMatchingNodes , HookFn
10
12
} from "./interface" ;
11
13
import { Glob } from "../common/glob" ;
12
14
import { State } from "../state/stateObject" ;
13
- import { TransitionHookType } from "./transitionHookType" ;
15
+ import { TransitionEventType } from "./transitionEventType" ;
16
+ import { TransitionService } from "./transitionService" ;
14
17
15
18
/**
16
19
* Determines if the given state matches the matchCriteria
@@ -49,53 +52,100 @@ export function matchState(state: State, criterion: HookMatchCriterion) {
49
52
* The registration data for a registered transition hook
50
53
*/
51
54
export class RegisteredHook implements RegisteredHook {
52
- hookType : TransitionHookType ;
53
- callback : HookFn ;
54
55
matchCriteria : HookMatchCriteria ;
55
56
priority : number ;
56
57
bind : any ;
57
58
_deregistered : boolean ;
58
59
59
- constructor ( hookType : TransitionHookType ,
60
+ constructor ( public tranSvc : TransitionService ,
61
+ public eventType : TransitionEventType ,
62
+ public callback : HookFn ,
60
63
matchCriteria : HookMatchCriteria ,
61
- callback : HookFn ,
62
- options : HookRegOptions = < any > { } ) {
63
- this . hookType = hookType ;
64
- this . callback = callback ;
65
- this . matchCriteria = extend ( { to : true , from : true , exiting : true , retained : true , entering : true } , matchCriteria ) ;
64
+ options : HookRegOptions = { } as any ) {
65
+ this . matchCriteria = extend ( this . _getDefaultMatchCriteria ( ) , matchCriteria ) ;
66
66
this . priority = options . priority || 0 ;
67
67
this . bind = options . bind || null ;
68
68
this . _deregistered = false ;
69
69
}
70
70
71
- private static _matchingNodes ( nodes : PathNode [ ] , criterion : HookMatchCriterion ) : PathNode [ ] {
71
+ /**
72
+ * Given an array of PathNodes, and a HookMatchCriteria, returns an array containing
73
+ * the PathNodes that the criteria matches, or null if there were no matching nodes.
74
+ *
75
+ * Returning null is significant to distinguish between the default
76
+ * "match-all criterion value" of `true` compared to a () => true,
77
+ * when the nodes is an empty array.
78
+ *
79
+ * This is useful to allow a transition match criteria of `entering: true`
80
+ * to still match a transition, even when `entering === []`. Contrast that
81
+ * with `entering: (state) => true` which only matches when a state is actually
82
+ * being entered.
83
+ */
84
+ private _matchingNodes ( nodes : PathNode [ ] , criterion : HookMatchCriterion ) : PathNode [ ] {
72
85
if ( criterion === true ) return nodes ;
73
86
let matching = nodes . filter ( node => matchState ( node . state , criterion ) ) ;
74
87
return matching . length ? matching : null ;
75
88
}
76
89
90
+ /**
91
+ * Returns an object which has all the criteria match paths as keys and `true` as values, i.e.:
92
+ *
93
+ * { to: true, from: true, entering: true, exiting: true, retained: true }
94
+ */
95
+ private _getDefaultMatchCriteria ( ) : HookMatchCriteria {
96
+ return this . tranSvc . _pluginapi . getTransitionEventTypes ( )
97
+ . map ( type => type . criteriaMatchPath )
98
+ . reduce < any [ ] > ( uniqR , [ ] )
99
+ . reduce ( ( acc , path ) => ( acc [ path ] = true , acc ) , { } ) ;
100
+ }
101
+
102
+ /**
103
+ * For all the criteria match paths in all TransitionHookTypes,
104
+ * return an object where: keys are pathname, vals are TransitionHookScope
105
+ */
106
+ private _getPathScopes ( ) : { [ key : string ] : TransitionHookScope } {
107
+ return this . tranSvc . _pluginapi . getTransitionEventTypes ( ) . reduce ( ( paths , type ) => {
108
+ paths [ type . criteriaMatchPath ] = type . hookScope ;
109
+ return paths
110
+ } , { } ) ;
111
+ }
112
+
113
+ /**
114
+ * Create a IMatchingNodes object from the TransitionHookTypes that basically looks like this:
115
+ *
116
+ * let matches: IMatchingNodes = {
117
+ * to: _matchingNodes([tail(treeChanges.to)], mc.to),
118
+ * from: _matchingNodes([tail(treeChanges.from)], mc.from),
119
+ * exiting: _matchingNodes(treeChanges.exiting, mc.exiting),
120
+ * retained: _matchingNodes(treeChanges.retained, mc.retained),
121
+ * entering: _matchingNodes(treeChanges.entering, mc.entering),
122
+ * };
123
+ */
124
+ private _getMatchingNodes ( treeChanges : TreeChanges ) : IMatchingNodes {
125
+ let pathScopes : { [ key : string ] : TransitionHookScope } = this . _getPathScopes ( ) ;
126
+
127
+ return Object . keys ( pathScopes ) . reduce ( ( mn : IMatchingNodes , pathName : string ) => {
128
+ // STATE scope criteria matches against every node in the path.
129
+ // TRANSITION scope criteria matches against only the last node in the path
130
+ let isStateHook = pathScopes [ pathName ] === TransitionHookScope . STATE ;
131
+ let nodes : PathNode [ ] = isStateHook ? treeChanges [ pathName ] : [ tail ( treeChanges [ pathName ] ) ] ;
132
+
133
+ mn [ pathName ] = this . _matchingNodes ( nodes , this . matchCriteria [ pathName ] ) ;
134
+ return mn ;
135
+ } , { } as IMatchingNodes ) ;
136
+ }
137
+
77
138
/**
78
139
* Determines if this hook's [[matchCriteria]] match the given [[TreeChanges]]
79
140
*
80
141
* @returns an IMatchingNodes object, or null. If an IMatchingNodes object is returned, its values
81
142
* are the matching [[PathNode]]s for each [[HookMatchCriterion]] (to, from, exiting, retained, entering)
82
143
*/
83
144
matches ( treeChanges : TreeChanges ) : IMatchingNodes {
84
- let mc = this . matchCriteria , _matchingNodes = RegisteredHook . _matchingNodes ;
85
-
86
- let matches : IMatchingNodes = {
87
- to : _matchingNodes ( [ tail ( treeChanges . to ) ] , mc . to ) ,
88
- from : _matchingNodes ( [ tail ( treeChanges . from ) ] , mc . from ) ,
89
- exiting : _matchingNodes ( treeChanges . exiting , mc . exiting ) ,
90
- retained : _matchingNodes ( treeChanges . retained , mc . retained ) ,
91
- entering : _matchingNodes ( treeChanges . entering , mc . entering ) ,
92
- } ;
145
+ let matches = this . _getMatchingNodes ( treeChanges ) ;
93
146
94
147
// Check if all the criteria matched the TreeChanges object
95
- let allMatched : boolean = [ "to" , "from" , "exiting" , "retained" , "entering" ]
96
- . map ( prop => matches [ prop ] )
97
- . reduce ( allTrueR , true ) ;
98
-
148
+ let allMatched = values ( matches ) . every ( identity ) ;
99
149
return allMatched ? matches : null ;
100
150
}
101
151
}
@@ -106,17 +156,23 @@ export interface RegisteredHooks {
106
156
}
107
157
108
158
/** @hidden Return a registration function of the requested type. */
109
- export function makeHookRegistrationFn ( registeredHooks : RegisteredHooks , type : TransitionHookType ) : IHookRegistration {
110
- let name = type . name ;
111
- registeredHooks [ name ] = [ ] ;
159
+ export function makeEvent ( registry : IHookRegistry , transitionService : TransitionService , eventType : TransitionEventType ) {
160
+ // Create the object which holds the registered transition hooks.
161
+ let _registeredHooks = registry . _registeredHooks = ( registry . _registeredHooks || { } ) ;
162
+ let hooks = _registeredHooks [ eventType . name ] = [ ] ;
112
163
113
- return function ( matchObject , callback , options = { } ) {
114
- let registeredHook = new RegisteredHook ( type , matchObject , callback , options ) ;
115
- registeredHooks [ name ] . push ( registeredHook ) ;
164
+ // Create hook registration function on the IHookRegistry for the event
165
+ registry [ eventType . name ] = hookRegistrationFn ;
166
+
167
+ function hookRegistrationFn ( matchObject , callback , options = { } ) {
168
+ let registeredHook = new RegisteredHook ( transitionService , eventType , callback , matchObject , options ) ;
169
+ hooks . push ( registeredHook ) ;
116
170
117
171
return function deregisterEventHook ( ) {
118
172
registeredHook . _deregistered = true ;
119
- removeFrom ( registeredHooks [ name ] ) ( registeredHook ) ;
173
+ removeFrom ( hooks ) ( registeredHook ) ;
120
174
} ;
121
- } ;
175
+ }
176
+
177
+ return hookRegistrationFn ;
122
178
}
0 commit comments