@pyreon/kinetic 0.24.4 → 0.24.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +10 -12
- package/src/Collapse.tsx +0 -166
- package/src/Stagger.tsx +0 -63
- package/src/Transition.tsx +0 -280
- package/src/TransitionGroup.tsx +0 -139
- package/src/__tests__/Collapse.test.tsx +0 -803
- package/src/__tests__/GroupRenderer.test.tsx +0 -434
- package/src/__tests__/StaggerRenderer.test.tsx +0 -523
- package/src/__tests__/Transition.ssr.test.tsx +0 -183
- package/src/__tests__/Transition.test.tsx +0 -403
- package/src/__tests__/TransitionItem.test.tsx +0 -514
- package/src/__tests__/kinetic-modes.ssr.test.tsx +0 -214
- package/src/__tests__/kinetic.browser.test.tsx +0 -327
- package/src/__tests__/kinetic.test.tsx +0 -565
- package/src/__tests__/presets.test.ts +0 -46
- package/src/__tests__/stagger-component-children-hydration.test.tsx +0 -191
- package/src/__tests__/top-level-transition-stagger-function-children.test.tsx +0 -141
- package/src/__tests__/useAnimationEnd.test.ts +0 -194
- package/src/__tests__/useReducedMotion.test.ts +0 -160
- package/src/__tests__/useTransitionState.test.ts +0 -132
- package/src/__tests__/utils.test.ts +0 -139
- package/src/index.ts +0 -15
- package/src/jsx-augment.d.ts +0 -12
- package/src/kinetic/CollapseRenderer.tsx +0 -216
- package/src/kinetic/GroupRenderer.tsx +0 -149
- package/src/kinetic/StaggerRenderer.tsx +0 -94
- package/src/kinetic/TransitionItem.tsx +0 -250
- package/src/kinetic/TransitionRenderer.tsx +0 -230
- package/src/kinetic/createKineticComponent.tsx +0 -224
- package/src/kinetic/types.ts +0 -149
- package/src/kinetic.ts +0 -25
- package/src/presets.ts +0 -66
- package/src/types.ts +0 -118
- package/src/useAnimationEnd.ts +0 -59
- package/src/useReducedMotion.ts +0 -28
- package/src/useTransitionState.ts +0 -62
- package/src/utils.ts +0 -113
|
@@ -1,403 +0,0 @@
|
|
|
1
|
-
import type { VNode } from '@pyreon/core'
|
|
2
|
-
import { h } from '@pyreon/core'
|
|
3
|
-
import { signal } from '@pyreon/reactivity'
|
|
4
|
-
|
|
5
|
-
let _reducedMotion = false
|
|
6
|
-
|
|
7
|
-
vi.mock('../useReducedMotion', () => ({
|
|
8
|
-
useReducedMotion: () => () => _reducedMotion,
|
|
9
|
-
}))
|
|
10
|
-
|
|
11
|
-
import Transition from '../Transition'
|
|
12
|
-
|
|
13
|
-
// Mock rAF for deterministic double-rAF testing
|
|
14
|
-
let rafCallbacks: (() => void)[] = []
|
|
15
|
-
const originalRaf = globalThis.requestAnimationFrame
|
|
16
|
-
const originalCaf = globalThis.cancelAnimationFrame
|
|
17
|
-
|
|
18
|
-
beforeEach(() => {
|
|
19
|
-
vi.useFakeTimers()
|
|
20
|
-
rafCallbacks = []
|
|
21
|
-
|
|
22
|
-
vi.stubGlobal(
|
|
23
|
-
'requestAnimationFrame',
|
|
24
|
-
vi.fn((cb: () => void) => {
|
|
25
|
-
rafCallbacks.push(cb)
|
|
26
|
-
return rafCallbacks.length
|
|
27
|
-
}),
|
|
28
|
-
)
|
|
29
|
-
|
|
30
|
-
vi.stubGlobal('cancelAnimationFrame', vi.fn())
|
|
31
|
-
})
|
|
32
|
-
|
|
33
|
-
afterEach(() => {
|
|
34
|
-
vi.useRealTimers()
|
|
35
|
-
vi.stubGlobal('requestAnimationFrame', originalRaf)
|
|
36
|
-
vi.stubGlobal('cancelAnimationFrame', originalCaf)
|
|
37
|
-
})
|
|
38
|
-
|
|
39
|
-
const flushRaf = () => {
|
|
40
|
-
const cbs = [...rafCallbacks]
|
|
41
|
-
rafCallbacks = []
|
|
42
|
-
for (const cb of cbs) cb()
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const fireTransitionEnd = (el: HTMLElement) => {
|
|
46
|
-
const event = new Event('transitionend', { bubbles: true })
|
|
47
|
-
Object.defineProperty(event, 'target', { value: el })
|
|
48
|
-
el.dispatchEvent(event)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
/**
|
|
52
|
-
* Recursively finds and invokes all refs in a VNode tree,
|
|
53
|
-
* wiring them to the given element.
|
|
54
|
-
*/
|
|
55
|
-
const wireRef = (vnode: VNode | null, el: HTMLElement) => {
|
|
56
|
-
if (!vnode) return
|
|
57
|
-
const visitNode = (node: VNode) => {
|
|
58
|
-
const nodeProps = node.props as Record<string, unknown>
|
|
59
|
-
if (typeof nodeProps?.ref === 'function') {
|
|
60
|
-
;(nodeProps.ref as (element: HTMLElement | null) => void)(el)
|
|
61
|
-
} else if (nodeProps?.ref && typeof nodeProps.ref === 'object') {
|
|
62
|
-
;(nodeProps.ref as { current: HTMLElement | null }).current = el
|
|
63
|
-
}
|
|
64
|
-
if (node.children) {
|
|
65
|
-
const ch = Array.isArray(node.children) ? node.children : [node.children]
|
|
66
|
-
for (const c of ch) {
|
|
67
|
-
if (c && typeof c === 'object' && 'type' in (c as object)) visitNode(c as VNode)
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
if (nodeProps?.children) {
|
|
71
|
-
const pc = Array.isArray(nodeProps.children) ? nodeProps.children : [nodeProps.children]
|
|
72
|
-
for (const c of pc) {
|
|
73
|
-
if (c && typeof c === 'object' && 'type' in (c as object)) visitNode(c as VNode)
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (
|
|
77
|
-
nodeProps?.fallback &&
|
|
78
|
-
typeof nodeProps.fallback === 'object' &&
|
|
79
|
-
'type' in (nodeProps.fallback as object)
|
|
80
|
-
) {
|
|
81
|
-
visitNode(nodeProps.fallback as VNode)
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
visitNode(vnode)
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
/**
|
|
88
|
-
* Helper: call Transition and wire up a mock element to the merged ref.
|
|
89
|
-
*/
|
|
90
|
-
const setupTransition = (props: Record<string, unknown>) => {
|
|
91
|
-
const el = document.createElement('div')
|
|
92
|
-
// Real h() instead of a mock literal — same VNode shape as
|
|
93
|
-
// production, so the runtime sees real h()-normalised output.
|
|
94
|
-
const child = h('div', { 'data-testid': 'child' }, 'Hello') as VNode
|
|
95
|
-
|
|
96
|
-
const vnode = Transition({
|
|
97
|
-
...props,
|
|
98
|
-
children: child,
|
|
99
|
-
} as any)
|
|
100
|
-
|
|
101
|
-
wireRef(vnode, el)
|
|
102
|
-
|
|
103
|
-
return { vnode, el }
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
describe('Transition', () => {
|
|
107
|
-
it('returns a VNode when show=true', () => {
|
|
108
|
-
const show = signal(true)
|
|
109
|
-
const child = h('div', null, 'Hello') as VNode
|
|
110
|
-
const vnode = Transition({ show, children: child })
|
|
111
|
-
expect(vnode).not.toBeNull()
|
|
112
|
-
})
|
|
113
|
-
|
|
114
|
-
it('returns a VNode with Show component', () => {
|
|
115
|
-
const show = signal(true)
|
|
116
|
-
const child = h('div', null, 'Hello') as VNode
|
|
117
|
-
const vnode = Transition({ show, children: child })
|
|
118
|
-
expect(vnode).not.toBeNull()
|
|
119
|
-
// The outermost VNode should be a Show component
|
|
120
|
-
expect(typeof vnode?.type).toBe('function')
|
|
121
|
-
})
|
|
122
|
-
|
|
123
|
-
it('fires onEnter callback when entering starts', () => {
|
|
124
|
-
const show = signal(false)
|
|
125
|
-
const onEnter = vi.fn()
|
|
126
|
-
|
|
127
|
-
setupTransition({ show, onEnter })
|
|
128
|
-
|
|
129
|
-
expect(onEnter).not.toHaveBeenCalled()
|
|
130
|
-
|
|
131
|
-
show.set(true)
|
|
132
|
-
expect(onEnter).toHaveBeenCalledTimes(1)
|
|
133
|
-
})
|
|
134
|
-
|
|
135
|
-
it('fires onLeave callback when leaving starts', () => {
|
|
136
|
-
const show = signal(true)
|
|
137
|
-
const onLeave = vi.fn()
|
|
138
|
-
|
|
139
|
-
setupTransition({ show, onLeave })
|
|
140
|
-
|
|
141
|
-
show.set(false)
|
|
142
|
-
expect(onLeave).toHaveBeenCalledTimes(1)
|
|
143
|
-
})
|
|
144
|
-
|
|
145
|
-
it('fires onAfterEnter after transitionend', () => {
|
|
146
|
-
const show = signal(false)
|
|
147
|
-
const onAfterEnter = vi.fn()
|
|
148
|
-
|
|
149
|
-
const { el } = setupTransition({ show, onAfterEnter })
|
|
150
|
-
|
|
151
|
-
show.set(true)
|
|
152
|
-
expect(onAfterEnter).not.toHaveBeenCalled()
|
|
153
|
-
|
|
154
|
-
flushRaf()
|
|
155
|
-
flushRaf()
|
|
156
|
-
fireTransitionEnd(el)
|
|
157
|
-
|
|
158
|
-
expect(onAfterEnter).toHaveBeenCalledTimes(1)
|
|
159
|
-
})
|
|
160
|
-
|
|
161
|
-
it('fires onAfterLeave after transitionend', () => {
|
|
162
|
-
const show = signal(true)
|
|
163
|
-
const onAfterLeave = vi.fn()
|
|
164
|
-
|
|
165
|
-
const { el } = setupTransition({ show, onAfterLeave })
|
|
166
|
-
|
|
167
|
-
show.set(false)
|
|
168
|
-
expect(onAfterLeave).not.toHaveBeenCalled()
|
|
169
|
-
|
|
170
|
-
flushRaf()
|
|
171
|
-
flushRaf()
|
|
172
|
-
fireTransitionEnd(el)
|
|
173
|
-
|
|
174
|
-
expect(onAfterLeave).toHaveBeenCalledTimes(1)
|
|
175
|
-
})
|
|
176
|
-
|
|
177
|
-
it('applies enter classes on entering', () => {
|
|
178
|
-
const show = signal(false)
|
|
179
|
-
const { el } = setupTransition({
|
|
180
|
-
show,
|
|
181
|
-
enter: 't-enter',
|
|
182
|
-
enterFrom: 't-enter-from',
|
|
183
|
-
enterTo: 't-enter-to',
|
|
184
|
-
})
|
|
185
|
-
|
|
186
|
-
show.set(true)
|
|
187
|
-
|
|
188
|
-
expect(el.classList.contains('t-enter')).toBe(true)
|
|
189
|
-
expect(el.classList.contains('t-enter-from')).toBe(true)
|
|
190
|
-
expect(el.classList.contains('t-enter-to')).toBe(false)
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
it('swaps enterFrom to enterTo after double rAF', () => {
|
|
194
|
-
const show = signal(false)
|
|
195
|
-
const { el } = setupTransition({
|
|
196
|
-
show,
|
|
197
|
-
enter: 't-enter',
|
|
198
|
-
enterFrom: 't-enter-from',
|
|
199
|
-
enterTo: 't-enter-to',
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
show.set(true)
|
|
203
|
-
|
|
204
|
-
flushRaf()
|
|
205
|
-
flushRaf()
|
|
206
|
-
|
|
207
|
-
expect(el.classList.contains('t-enter')).toBe(true)
|
|
208
|
-
expect(el.classList.contains('t-enter-from')).toBe(false)
|
|
209
|
-
expect(el.classList.contains('t-enter-to')).toBe(true)
|
|
210
|
-
})
|
|
211
|
-
|
|
212
|
-
it('cleans up enter classes after transitionend', () => {
|
|
213
|
-
const show = signal(false)
|
|
214
|
-
const { el } = setupTransition({
|
|
215
|
-
show,
|
|
216
|
-
enter: 't-enter',
|
|
217
|
-
enterFrom: 't-enter-from',
|
|
218
|
-
enterTo: 't-enter-to',
|
|
219
|
-
})
|
|
220
|
-
|
|
221
|
-
show.set(true)
|
|
222
|
-
flushRaf()
|
|
223
|
-
flushRaf()
|
|
224
|
-
fireTransitionEnd(el)
|
|
225
|
-
|
|
226
|
-
// enter class should be removed on entered stage
|
|
227
|
-
expect(el.classList.contains('t-enter')).toBe(false)
|
|
228
|
-
})
|
|
229
|
-
|
|
230
|
-
it('applies style-object transitions on entering', () => {
|
|
231
|
-
const show = signal(false)
|
|
232
|
-
const { el } = setupTransition({
|
|
233
|
-
show,
|
|
234
|
-
enterStyle: { opacity: 0 },
|
|
235
|
-
enterToStyle: { opacity: 1 },
|
|
236
|
-
enterTransition: 'opacity 300ms ease',
|
|
237
|
-
})
|
|
238
|
-
|
|
239
|
-
show.set(true)
|
|
240
|
-
|
|
241
|
-
expect(el.style.opacity).toBe('0')
|
|
242
|
-
expect(el.style.transition).toBe('opacity 300ms ease')
|
|
243
|
-
|
|
244
|
-
flushRaf()
|
|
245
|
-
flushRaf()
|
|
246
|
-
|
|
247
|
-
expect(el.style.opacity).toBe('1')
|
|
248
|
-
})
|
|
249
|
-
|
|
250
|
-
it('applies leave classes on leaving', () => {
|
|
251
|
-
const show = signal(true)
|
|
252
|
-
const { el } = setupTransition({
|
|
253
|
-
show,
|
|
254
|
-
leave: 't-leave',
|
|
255
|
-
leaveFrom: 't-leave-from',
|
|
256
|
-
leaveTo: 't-leave-to',
|
|
257
|
-
})
|
|
258
|
-
|
|
259
|
-
show.set(false)
|
|
260
|
-
|
|
261
|
-
expect(el.classList.contains('t-leave')).toBe(true)
|
|
262
|
-
expect(el.classList.contains('t-leave-from')).toBe(true)
|
|
263
|
-
|
|
264
|
-
flushRaf()
|
|
265
|
-
flushRaf()
|
|
266
|
-
|
|
267
|
-
expect(el.classList.contains('t-leave-from')).toBe(false)
|
|
268
|
-
expect(el.classList.contains('t-leave-to')).toBe(true)
|
|
269
|
-
})
|
|
270
|
-
|
|
271
|
-
it('applies leave style transitions', () => {
|
|
272
|
-
const show = signal(true)
|
|
273
|
-
const { el } = setupTransition({
|
|
274
|
-
show,
|
|
275
|
-
leaveStyle: { opacity: 1 },
|
|
276
|
-
leaveToStyle: { opacity: 0 },
|
|
277
|
-
leaveTransition: 'opacity 200ms ease-in',
|
|
278
|
-
})
|
|
279
|
-
|
|
280
|
-
show.set(false)
|
|
281
|
-
|
|
282
|
-
expect(el.style.opacity).toBe('1')
|
|
283
|
-
expect(el.style.transition).toBe('opacity 200ms ease-in')
|
|
284
|
-
|
|
285
|
-
flushRaf()
|
|
286
|
-
flushRaf()
|
|
287
|
-
|
|
288
|
-
expect(el.style.opacity).toBe('0')
|
|
289
|
-
})
|
|
290
|
-
|
|
291
|
-
it('appear=true fires onEnter on initial mount', () => {
|
|
292
|
-
const show = signal(true)
|
|
293
|
-
const onEnter = vi.fn()
|
|
294
|
-
|
|
295
|
-
setupTransition({ show, appear: true, onEnter })
|
|
296
|
-
|
|
297
|
-
expect(onEnter).toHaveBeenCalledTimes(1)
|
|
298
|
-
})
|
|
299
|
-
|
|
300
|
-
it('timeout fallback completes transition when transitionend never fires', () => {
|
|
301
|
-
const show = signal(false)
|
|
302
|
-
const onAfterEnter = vi.fn()
|
|
303
|
-
|
|
304
|
-
setupTransition({ show, timeout: 1000, onAfterEnter })
|
|
305
|
-
|
|
306
|
-
show.set(true)
|
|
307
|
-
|
|
308
|
-
expect(onAfterEnter).not.toHaveBeenCalled()
|
|
309
|
-
|
|
310
|
-
vi.advanceTimersByTime(1000)
|
|
311
|
-
|
|
312
|
-
expect(onAfterEnter).toHaveBeenCalledTimes(1)
|
|
313
|
-
})
|
|
314
|
-
|
|
315
|
-
it('cleans up transition style on entered stage', () => {
|
|
316
|
-
const show = signal(false)
|
|
317
|
-
const { el } = setupTransition({
|
|
318
|
-
show,
|
|
319
|
-
enter: 't-enter',
|
|
320
|
-
enterTransition: 'opacity 300ms ease',
|
|
321
|
-
enterStyle: { opacity: 0 },
|
|
322
|
-
enterToStyle: { opacity: 1 },
|
|
323
|
-
})
|
|
324
|
-
|
|
325
|
-
show.set(true)
|
|
326
|
-
expect(el.style.transition).toBe('opacity 300ms ease')
|
|
327
|
-
expect(el.classList.contains('t-enter')).toBe(true)
|
|
328
|
-
|
|
329
|
-
flushRaf()
|
|
330
|
-
flushRaf()
|
|
331
|
-
fireTransitionEnd(el)
|
|
332
|
-
|
|
333
|
-
// After entering -> entered, transition reset and enter class removed
|
|
334
|
-
expect(el.style.transition).toBe('')
|
|
335
|
-
expect(el.classList.contains('t-enter')).toBe(false)
|
|
336
|
-
})
|
|
337
|
-
})
|
|
338
|
-
|
|
339
|
-
describe('Transition — reduced motion', () => {
|
|
340
|
-
beforeEach(() => {
|
|
341
|
-
vi.useFakeTimers()
|
|
342
|
-
rafCallbacks = []
|
|
343
|
-
_reducedMotion = true
|
|
344
|
-
|
|
345
|
-
vi.stubGlobal(
|
|
346
|
-
'requestAnimationFrame',
|
|
347
|
-
vi.fn((cb: () => void) => {
|
|
348
|
-
rafCallbacks.push(cb)
|
|
349
|
-
return rafCallbacks.length
|
|
350
|
-
}),
|
|
351
|
-
)
|
|
352
|
-
vi.stubGlobal('cancelAnimationFrame', vi.fn())
|
|
353
|
-
})
|
|
354
|
-
|
|
355
|
-
afterEach(() => {
|
|
356
|
-
vi.useRealTimers()
|
|
357
|
-
vi.stubGlobal('requestAnimationFrame', originalRaf)
|
|
358
|
-
vi.stubGlobal('cancelAnimationFrame', originalCaf)
|
|
359
|
-
_reducedMotion = false
|
|
360
|
-
})
|
|
361
|
-
|
|
362
|
-
it('reduced motion: entering fires onEnter and onAfterEnter immediately', () => {
|
|
363
|
-
const show = signal(false)
|
|
364
|
-
const onEnter = vi.fn()
|
|
365
|
-
const onAfterEnter = vi.fn()
|
|
366
|
-
|
|
367
|
-
setupTransition({ show, onEnter, onAfterEnter })
|
|
368
|
-
|
|
369
|
-
show.set(true)
|
|
370
|
-
|
|
371
|
-
expect(onEnter).toHaveBeenCalledTimes(1)
|
|
372
|
-
expect(onAfterEnter).toHaveBeenCalledTimes(1)
|
|
373
|
-
})
|
|
374
|
-
|
|
375
|
-
it('reduced motion: leaving fires onLeave and onAfterLeave immediately', () => {
|
|
376
|
-
const show = signal(true)
|
|
377
|
-
const onLeave = vi.fn()
|
|
378
|
-
const onAfterLeave = vi.fn()
|
|
379
|
-
|
|
380
|
-
setupTransition({ show, onLeave, onAfterLeave })
|
|
381
|
-
|
|
382
|
-
show.set(false)
|
|
383
|
-
|
|
384
|
-
expect(onLeave).toHaveBeenCalledTimes(1)
|
|
385
|
-
expect(onAfterLeave).toHaveBeenCalledTimes(1)
|
|
386
|
-
})
|
|
387
|
-
|
|
388
|
-
it('reduced motion: does not use rAF or apply CSS classes', () => {
|
|
389
|
-
const show = signal(false)
|
|
390
|
-
const { el } = setupTransition({
|
|
391
|
-
show,
|
|
392
|
-
enter: 't-enter',
|
|
393
|
-
enterFrom: 't-enter-from',
|
|
394
|
-
enterTo: 't-enter-to',
|
|
395
|
-
})
|
|
396
|
-
|
|
397
|
-
show.set(true)
|
|
398
|
-
|
|
399
|
-
expect(el.classList.contains('t-enter')).toBe(false)
|
|
400
|
-
expect(el.classList.contains('t-enter-from')).toBe(false)
|
|
401
|
-
expect(rafCallbacks.length).toBe(0)
|
|
402
|
-
})
|
|
403
|
-
})
|