@pyreon/kinetic 0.24.5 → 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.
Files changed (37) hide show
  1. package/package.json +10 -12
  2. package/src/Collapse.tsx +0 -166
  3. package/src/Stagger.tsx +0 -63
  4. package/src/Transition.tsx +0 -280
  5. package/src/TransitionGroup.tsx +0 -139
  6. package/src/__tests__/Collapse.test.tsx +0 -803
  7. package/src/__tests__/GroupRenderer.test.tsx +0 -434
  8. package/src/__tests__/StaggerRenderer.test.tsx +0 -523
  9. package/src/__tests__/Transition.ssr.test.tsx +0 -183
  10. package/src/__tests__/Transition.test.tsx +0 -403
  11. package/src/__tests__/TransitionItem.test.tsx +0 -514
  12. package/src/__tests__/kinetic-modes.ssr.test.tsx +0 -214
  13. package/src/__tests__/kinetic.browser.test.tsx +0 -327
  14. package/src/__tests__/kinetic.test.tsx +0 -565
  15. package/src/__tests__/presets.test.ts +0 -46
  16. package/src/__tests__/stagger-component-children-hydration.test.tsx +0 -191
  17. package/src/__tests__/top-level-transition-stagger-function-children.test.tsx +0 -141
  18. package/src/__tests__/useAnimationEnd.test.ts +0 -194
  19. package/src/__tests__/useReducedMotion.test.ts +0 -160
  20. package/src/__tests__/useTransitionState.test.ts +0 -132
  21. package/src/__tests__/utils.test.ts +0 -139
  22. package/src/index.ts +0 -15
  23. package/src/jsx-augment.d.ts +0 -12
  24. package/src/kinetic/CollapseRenderer.tsx +0 -216
  25. package/src/kinetic/GroupRenderer.tsx +0 -149
  26. package/src/kinetic/StaggerRenderer.tsx +0 -94
  27. package/src/kinetic/TransitionItem.tsx +0 -250
  28. package/src/kinetic/TransitionRenderer.tsx +0 -230
  29. package/src/kinetic/createKineticComponent.tsx +0 -224
  30. package/src/kinetic/types.ts +0 -149
  31. package/src/kinetic.ts +0 -25
  32. package/src/presets.ts +0 -66
  33. package/src/types.ts +0 -118
  34. package/src/useAnimationEnd.ts +0 -59
  35. package/src/useReducedMotion.ts +0 -28
  36. package/src/useTransitionState.ts +0 -62
  37. 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
- })