@@ -4,40 +4,120 @@ import React, {
4
4
useLayoutEffect ,
5
5
useImperativeHandle ,
6
6
ReactNode ,
7
+ RefObject ,
7
8
} from 'react'
8
- import { is , toArray , useForceUpdate , useOnce , each , OneOrMore } from 'shared'
9
+ import {
10
+ is ,
11
+ toArray ,
12
+ useForceUpdate ,
13
+ useOnce ,
14
+ each ,
15
+ OneOrMore ,
16
+ Falsy ,
17
+ Indexable ,
18
+ Merge ,
19
+ AnyKey ,
20
+ } from 'shared'
9
21
import { now } from 'shared/globals'
10
22
11
23
import { DEFAULT_PROPS , callProp , interpolateTo } from './helpers'
12
- import { SpringHandle , SpringProps } from './types/spring'
13
- import { Controller } from './Controller'
24
+ import { SpringHandle , AsyncTo , FromProp , SpringValues } from './types/spring'
25
+ import { Controller , ControllerProps } from './Controller'
26
+ import { AnimationProps , AnimationEvents } from './types/animated'
27
+ import { UseSpringProps } from './useSpring'
14
28
15
29
// TODO: convert to "const enum" once Babel supports it
16
- type Phase = number
30
+ export type Phase = number & { __type : 'TransitionPhase' }
17
31
/** This transition is being mounted */
18
- const MOUNT = 0
32
+ const MOUNT = 0 as Phase
19
33
/** This transition is entering or has entered */
20
- const ENTER = 1
34
+ const ENTER = 1 as Phase
21
35
/** This transition had its animations updated */
22
- const UPDATE = 2
36
+ const UPDATE = 2 as Phase
23
37
/** This transition will expire after animating */
24
- const LEAVE = 3
38
+ const LEAVE = 3 as Phase
39
+
40
+ type UnknownProps = Indexable < any >
41
+
42
+ type PhaseProp < Item > =
43
+ | Falsy
44
+ | OneOrMore < UseSpringProps >
45
+ | ( (
46
+ item : Item ,
47
+ index : number
48
+ ) => UseSpringProps | AsyncTo < UnknownProps > | Falsy )
49
+
50
+ type PhaseProps < Item = any , From = { } > = {
51
+ from ?: From &
52
+ (
53
+ | FromProp < UnknownProps >
54
+ | ( ( item : Item , index : number ) => FromProp < UnknownProps > ) )
55
+ initial ?: From &
56
+ (
57
+ | FromProp < UnknownProps >
58
+ | ( ( item : Item , index : number ) => FromProp < UnknownProps > ) )
59
+ enter ?: PhaseProp < Item >
60
+ update ?: PhaseProp < Item >
61
+ leave ?: PhaseProp < Item >
62
+ }
25
63
26
- export type UseTransitionProps < T > = { [ key : string ] : any | T } // TODO
27
- export type ItemsProp < T > = ReadonlyArray < T > | T | null | undefined
28
64
export type ItemKeys < T > =
29
- | ( ( item : T ) => string | number )
30
- | ReadonlyArray < string | number >
31
- | string
32
- | number
65
+ | AnyKey
66
+ | ReadonlyArray < AnyKey >
67
+ | ( ( item : T ) => AnyKey )
33
68
| null
34
69
35
- export function useTransition < T > ( data : OneOrMore < T > , props : any , deps ?: any ) {
70
+ export type UseTransitionProps < Item = any > = Merge <
71
+ AnimationProps & AnimationEvents ,
72
+ {
73
+ /**
74
+ * Used to access the imperative API.
75
+ *
76
+ * Animations never auto-start when `ref` is defined.
77
+ */
78
+ ref ?: RefObject < TransitionHandle >
79
+ key ?: ItemKeys < Item >
80
+ sort ?: ( a : Item , b : Item ) => number
81
+ trail ?: number
82
+ expires ?: number
83
+ }
84
+ >
85
+
86
+ /** The imperative `ref` API */
87
+ export type TransitionHandle = Merge <
88
+ SpringHandle ,
89
+ {
90
+ update ( props : ControllerProps ) : TransitionHandle
91
+ }
92
+ >
93
+
94
+ /** The function returned by `useTransition` */
95
+ export interface TransitionFn < Item = any , Values extends object = any > {
96
+ (
97
+ render : (
98
+ values : Values ,
99
+ item : Item ,
100
+ transition : TransitionState < Item >
101
+ ) => ReactNode
102
+ ) : ReactNode [ ]
103
+ }
104
+
105
+ export function useTransition < Item , From , Props extends object > (
106
+ data : OneOrMore < Item > ,
107
+ props : Props & PhaseProps < Item , From > & UseTransitionProps < Item > ,
108
+ deps ?: any [ ]
109
+ ) : TransitionFn < Item , SpringValues < Props > >
110
+
111
+ export function useTransition (
112
+ data : unknown ,
113
+ props : PhaseProps & UseTransitionProps ,
114
+ deps ?: any [ ]
115
+ ) : TransitionFn {
36
116
const { key, ref, reset, sort, trail = 0 , expires = Infinity } = props
37
117
38
118
// Every item has its own transition.
39
- const items = toArray < unknown > ( data )
40
- const transitions : Transition [ ] = [ ]
119
+ const items = toArray ( data )
120
+ const transitions : TransitionState [ ] = [ ]
41
121
42
122
// Keys help with reusing transitions between renders.
43
123
// The `key` prop can be undefined (which means the items themselves are used
@@ -50,7 +130,7 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
50
130
: toArray ( key )
51
131
52
132
// The "onRest" callbacks need a ref to the latest transitions.
53
- const usedTransitions = useRef < Transition [ ] | null > ( null )
133
+ const usedTransitions = useRef < TransitionState [ ] | null > ( null )
54
134
const prevTransitions = usedTransitions . current
55
135
useLayoutEffect ( ( ) => {
56
136
usedTransitions . current = transitions
@@ -59,7 +139,9 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
59
139
// Destroy all transitions on dismount.
60
140
useOnce ( ( ) => ( ) =>
61
141
each ( usedTransitions . current ! , t => {
62
- if ( t . expiresBy ) clearTimeout ( t . expirationId )
142
+ if ( t . expiresBy != null ) {
143
+ clearTimeout ( t . expirationId )
144
+ }
63
145
t . ctrl . dispose ( )
64
146
} )
65
147
)
@@ -69,7 +151,7 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
69
151
if ( prevTransitions && ! reset )
70
152
each ( prevTransitions , ( t , i ) => {
71
153
// Expired transitions are not rendered.
72
- if ( t . expiresBy ) {
154
+ if ( t . expiresBy != null ) {
73
155
clearTimeout ( t . expirationId )
74
156
} else {
75
157
i = reused [ i ] = keys . indexOf ( t . key )
@@ -113,15 +195,15 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
113
195
// Expired transitions use this to dismount.
114
196
const forceUpdate = useForceUpdate ( )
115
197
116
- const defaultProps : any = { }
198
+ const defaultProps = { } as UnknownProps
117
199
each ( DEFAULT_PROPS , prop => {
118
200
if ( / f u n c t i o n | o b j e c t / . test ( typeof props [ prop ] ) ) {
119
201
defaultProps [ prop ] = props [ prop ]
120
202
}
121
203
} )
122
204
123
205
// Generate changes to apply in useEffect.
124
- const changes = new Map < Transition < T > , Change > ( )
206
+ const changes = new Map < TransitionState , Change > ( )
125
207
each ( transitions , ( t , i ) => {
126
208
let to : any
127
209
let from : any
@@ -151,7 +233,7 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
151
233
}
152
234
153
235
// The payload is used to update the spring props once the current render is committed.
154
- const payload = {
236
+ const payload : ControllerProps = {
155
237
...defaultProps ,
156
238
// When "to" is a function, it can return (1) an array of "useSpring" props,
157
239
// (2) an async function, or (3) an object with any "useSpring" props.
@@ -160,7 +242,7 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
160
242
delay : delay += trail ,
161
243
config : callProp ( props . config || defaultProps . config , t . item , i ) ,
162
244
...( is . obj ( to ) && interpolateTo ( to ) ) ,
163
- } as SpringProps
245
+ }
164
246
165
247
const { onRest } = payload
166
248
payload . onRest = result => {
@@ -176,7 +258,9 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
176
258
const transitions = usedTransitions . current !
177
259
if ( transitions . every ( t => t . ctrl . idle ) ) {
178
260
forceUpdate ( )
179
- } else if ( expires < Infinity ) {
261
+ }
262
+ // When `expires` is infinite, postpone dismount until next render.
263
+ else if ( expires < Infinity ) {
180
264
t . expirationId = setTimeout ( forceUpdate , expires )
181
265
}
182
266
}
@@ -196,7 +280,7 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
196
280
} )
197
281
198
282
const api = useMemo (
199
- ( ) : SpringHandle => ( {
283
+ ( ) : TransitionHandle => ( {
200
284
get controllers ( ) {
201
285
return usedTransitions . current ! . map ( t => t . ctrl )
202
286
} ,
@@ -234,9 +318,9 @@ export function useTransition<T>(data: OneOrMore<T>, props: any, deps?: any) {
234
318
reset ? void 0 : deps
235
319
)
236
320
237
- return ( render : ( props : any , item : T ) => ReactNode ) =>
321
+ return render =>
238
322
transitions . map ( t => {
239
- const elem : any = render ( { ...t . ctrl . springs } , t . item )
323
+ const elem : any = render ( { ...t . ctrl . springs } as any , t . item , t )
240
324
return elem && elem . type ? (
241
325
< elem . type
242
326
{ ...elem . props }
@@ -254,9 +338,9 @@ interface Change {
254
338
payload ?: any
255
339
}
256
340
257
- interface Transition < T = any > {
341
+ export interface TransitionState < Item = any > {
258
342
key : any
259
- item : T
343
+ item : Item
260
344
ctrl : Controller
261
345
phase : Phase
262
346
/** Destroy no later than this date */
0 commit comments