11import { Directive , Output , EventEmitter } from "angular2/core" ;
22import { StateService } from "../state/stateService" ;
33import { UiSref } from "./uiSref" ;
4- import { UIRouter } from "../router" ;
54import { Node } from "../path/node" ;
65import { TransitionService } from "../transition/transitionService" ;
76import { Transition } from "../transition/transition" ;
87import { TargetState } from "../state/targetState" ;
98import { TreeChanges } from "../transition/interface" ;
109import { State } from "../state/stateObject" ;
11- import { anyTrueR , tail } from "../common/common" ;
10+ import { anyTrueR , tail , unnestR } from "../common/common" ;
1211import { UIRouterGlobals } from "../globals" ;
12+ import { Param } from "../params/param" ;
13+ import { PathFactory } from "../path/pathFactory" ;
1314
15+ /**
16+ * uiSref status booleans
17+ */
1418export interface SrefStatus {
19+ /** The sref's target state (or one of its children) is currently active */
1520 active : boolean ;
21+ /** The sref's target state is currently active */
1622 exact : boolean ;
23+ /** A transition is entering the sref's target state */
1724 entering : boolean ;
25+ /** A transition is exiting the sref's target state */
1826 exiting : boolean ;
1927}
2028
2129/**
22- * Emits events when the uiSref status changes
30+ * A directive (which pairs with a [[UiSref]]) and emits events when the UiSref status changes.
31+ *
32+ * The event emitted is of type [[SrefStatus]], and has boolean values for `active`, `exact`, `entering`, and `exiting`
33+ *
34+ * The values from this event can be captured and stored on a component, then applied (perhaps using ngClass).
2335 *
2436 * This API is subject to change.
2537 */
@@ -41,26 +53,29 @@ export class UiSrefStatus {
4153 private _globals : UIRouterGlobals ,
4254 private _stateService : StateService ,
4355 public sref : UiSref ) {
44- this . _deregisterHook = transitionService . onStart ( { } , ( $transition$ ) => this . _transition ( $transition$ ) ) ;
56+ this . _deregisterHook = transitionService . onStart ( { } , $transition$ => this . processTransition ( $transition$ ) ) ;
4557 }
4658
4759 ngOnInit ( ) {
4860 let lastTrans = this . _globals . transitionHistory . peekTail ( ) ;
4961 if ( lastTrans != null ) {
50- this . _transition ( lastTrans ) ;
62+ this . processTransition ( lastTrans ) ;
5163 }
5264 }
5365
5466 ngOnDestroy ( ) {
55- this . _deregisterHook ( )
67+ if ( this . _deregisterHook ) {
68+ this . _deregisterHook ( ) ;
69+ }
70+ this . _deregisterHook = null ;
5671 }
5772
5873 private _setStatus ( status : SrefStatus ) {
5974 this . status = status ;
6075 this . uiSrefStatus . emit ( status ) ;
6176 }
6277
63- private _transition ( $transition$ : Transition ) {
78+ private processTransition ( $transition$ : Transition ) {
6479 let sref = this . sref ;
6580
6681 let status : SrefStatus = < any > {
@@ -70,28 +85,58 @@ export class UiSrefStatus {
7085 exiting : false
7186 } ;
7287
73- let srefTarget : TargetState = this . _stateService . target ( sref . state , sref . params , sref . options ) ;
88+ let srefTarget : TargetState = this . _stateService . target ( sref . state , sref . params , sref . getOptions ( ) ) ;
7489 if ( ! srefTarget . exists ( ) ) {
7590 return this . _setStatus ( status ) ;
7691 }
7792
78- let tc : TreeChanges = $transition$ . treeChanges ( ) ;
79- let state : State = srefTarget . $state ( ) ;
80- const isTarget = ( node : Node ) => node . state === state ;
8193
82- status . active = tc . from . map ( isTarget ) . reduce ( anyTrueR , false ) ;
83- status . exact = tail ( tc . from . map ( isTarget ) ) === true ;
84- status . entering = tc . entering . map ( isTarget ) . reduce ( anyTrueR , false ) ;
85- status . exiting = tc . exiting . map ( isTarget ) . reduce ( anyTrueR , false ) ;
94+ /**
95+ * Returns a Predicate<Node[]> that returns true when the target state (and any param values)
96+ * match the (tail of) the path, and the path's param values
97+ */
98+ const pathMatches = ( target : TargetState ) => {
99+ let state : State = target . $state ( ) ;
100+ let targetParamVals = target . params ( ) ;
101+ let targetPath : Node [ ] = PathFactory . buildPath ( target ) ;
102+ let paramSchema : Param [ ] = targetPath . map ( node => node . paramSchema )
103+ . reduce ( unnestR , [ ] )
104+ . filter ( ( param : Param ) => targetParamVals . hasOwnProperty ( param . id ) ) ;
105+
106+ return ( path : Node [ ] ) => {
107+ let tailNode = tail ( path ) ;
108+ if ( ! tailNode || tailNode . state !== state ) return false ;
109+ var paramValues = PathFactory . paramValues ( path ) ;
110+ return Param . equals ( paramSchema , paramValues , targetParamVals ) ;
111+ } ;
112+ } ;
113+
114+ const isTarget = pathMatches ( srefTarget ) ;
115+
116+ /**
117+ * Given path: [c, d] appendTo: [a, b]),
118+ * Expands the path to [c], [c, d]
119+ * Then appends each to [a,b,] and returns: [a, b, c], [a, b, c, d]
120+ */
121+ function spreadToSubPaths ( path : Node [ ] , appendTo : Node [ ] = [ ] ) : Node [ ] [ ] {
122+ return path . map ( node => appendTo . concat ( PathFactory . subPath ( path , node . state ) ) ) ;
123+ }
124+
125+ let tc : TreeChanges = $transition$ . treeChanges ( ) ;
126+ status . active = spreadToSubPaths ( tc . from ) . map ( isTarget ) . reduce ( anyTrueR , false ) ;
127+ status . exact = isTarget ( tc . from ) ;
128+ status . entering = spreadToSubPaths ( tc . entering , tc . retained ) . map ( isTarget ) . reduce ( anyTrueR , false ) ;
129+ status . exiting = spreadToSubPaths ( tc . exiting , tc . retained ) . map ( isTarget ) . reduce ( anyTrueR , false ) ;
86130
87131 if ( $transition$ . isActive ( ) ) {
88132 this . _setStatus ( status ) ;
89133 }
90134
91135 let update = ( currentPath : Node [ ] ) => ( ) => {
136+ if ( this . _deregisterHook == null ) return ; // destroyed
92137 if ( ! $transition$ . isActive ( ) ) return ; // superseded
93- status . active = currentPath . map ( isTarget ) . reduce ( anyTrueR , false ) ;
94- status . exact = tail ( currentPath . map ( isTarget ) ) === true ;
138+ status . active = spreadToSubPaths ( currentPath ) . map ( isTarget ) . reduce ( anyTrueR , false ) ;
139+ status . exact = isTarget ( currentPath ) ;
95140 status . entering = status . exiting = false ;
96141 this . _setStatus ( status ) ;
97142 } ;
0 commit comments