@pyreon/kinetic 0.12.13 → 0.12.15

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/lib/index.js CHANGED
@@ -297,11 +297,16 @@ const removeClasses = (el, classes) => {
297
297
  /**
298
298
  * Executes callback after two animation frames (double-rAF).
299
299
  * Ensures the browser paints the current state before applying changes,
300
- * which is required for CSS transitions to trigger.
300
+ * which is required for CSS transitions to trigger. Returns 0 on SSR —
301
+ * the typeof-window guard makes the SSR-safety contract explicit (callers
302
+ * are always browser-only via `onMount`, but the rule can't AST-trace it).
301
303
  */
302
- const nextFrame = (callback) => requestAnimationFrame(() => {
303
- requestAnimationFrame(callback);
304
- });
304
+ const nextFrame = (callback) => {
305
+ if (typeof requestAnimationFrame === "undefined") return 0;
306
+ return requestAnimationFrame(() => {
307
+ requestAnimationFrame(callback);
308
+ });
309
+ };
305
310
  /** Merges two CSSProperties objects, with `b` taking precedence. */
306
311
  const mergeStyles = (a, b) => {
307
312
  if (!a && !b) return void 0;
@@ -374,33 +379,36 @@ const applyReducedMotion$1 = (stage, callbacks, complete) => {
374
379
  *
375
380
  * Uses cloneVNode to inject ref onto the child — the child must accept ref.
376
381
  */
377
- const TransitionItem = ({ show, appear = false, unmount = true, timeout = 5e3, enter, enterFrom, enterTo, leave, leaveFrom, leaveTo, enterStyle, enterToStyle, enterTransition, leaveStyle, leaveToStyle, leaveTransition, onEnter, onAfterEnter, onLeave, onAfterLeave, children }) => {
382
+ const TransitionItem = (props) => {
383
+ const appear = props.appear ?? false;
384
+ const unmount = props.unmount ?? true;
385
+ const timeout = props.timeout ?? 5e3;
378
386
  const reducedMotion = useReducedMotion();
379
387
  const { stage, ref: stateRef, shouldMount, complete } = useTransitionState({
380
- show,
388
+ show: props.show,
381
389
  appear
382
390
  });
383
391
  const elementRef = createRef();
384
- const mergedRef = mergeRefs(elementRef, stateRef, children.props?.ref);
392
+ const mergedRef = mergeRefs(elementRef, stateRef, props.children.props?.ref);
385
393
  const callbacks = {
386
- onEnter,
387
- onAfterEnter,
388
- onLeave,
389
- onAfterLeave
394
+ onEnter: props.onEnter,
395
+ onAfterEnter: props.onAfterEnter,
396
+ onLeave: props.onLeave,
397
+ onAfterLeave: props.onAfterLeave
390
398
  };
391
399
  const transitionConfig = {
392
- enter,
393
- enterFrom,
394
- enterTo,
395
- leave,
396
- leaveFrom,
397
- leaveTo,
398
- enterStyle,
399
- enterToStyle,
400
- enterTransition,
401
- leaveStyle,
402
- leaveToStyle,
403
- leaveTransition
400
+ enter: props.enter,
401
+ enterFrom: props.enterFrom,
402
+ enterTo: props.enterTo,
403
+ leave: props.leave,
404
+ leaveFrom: props.leaveFrom,
405
+ leaveTo: props.leaveTo,
406
+ enterStyle: props.enterStyle,
407
+ enterToStyle: props.enterToStyle,
408
+ enterTransition: props.enterTransition,
409
+ leaveStyle: props.leaveStyle,
410
+ leaveToStyle: props.leaveToStyle,
411
+ leaveTransition: props.leaveTransition
404
412
  };
405
413
  useAnimationEnd({
406
414
  ref: elementRef,
@@ -430,17 +438,17 @@ const TransitionItem = ({ show, appear = false, unmount = true, timeout = 5e3, e
430
438
  return () => cancelAnimationFrame(frameId);
431
439
  }
432
440
  if (currentStage === "entered") {
433
- removeClasses(el, enter);
441
+ removeClasses(el, props.enter);
434
442
  el.style.transition = "";
435
443
  }
436
444
  }, { immediate: true });
437
445
  return /* @__PURE__ */ jsx(Show, {
438
446
  when: shouldMount,
439
- fallback: unmount ? null : cloneVNode(children, {
447
+ fallback: unmount ? null : cloneVNode(props.children, {
440
448
  ref: mergedRef,
441
- style: mergeStyles(children.props?.style, { display: "none" })
449
+ style: mergeStyles(props.children.props?.style, { display: "none" })
442
450
  }),
443
- children: cloneVNode(children, { ref: mergedRef })
451
+ children: cloneVNode(props.children, { ref: mergedRef })
444
452
  });
445
453
  };
446
454
 
@@ -610,22 +618,22 @@ const applyReducedMotion = (stage, cbs, complete) => {
610
618
  * Renders a single element with CSS transition enter/exit animation.
611
619
  * Uses h(config.tag) — no cloneElement needed.
612
620
  */
613
- const TransitionRenderer = ({ config, htmlProps, show, appear, unmount, timeout, callbacks, children }) => {
621
+ const TransitionRenderer = (props) => {
614
622
  const reducedMotion = useReducedMotion();
615
623
  const { stage, ref: stateRef, shouldMount, complete } = useTransitionState({
616
- show,
617
- appear: appear ?? config.appear ?? false
624
+ show: props.show,
625
+ appear: props.appear ?? props.config.appear ?? false
618
626
  });
619
627
  const elementRef = createRef();
620
628
  const mergedRef = mergeRefs(elementRef, stateRef);
621
- const effectiveUnmount = unmount ?? config.unmount ?? true;
629
+ const effectiveUnmount = props.unmount ?? props.config.unmount ?? true;
622
630
  useAnimationEnd({
623
631
  ref: elementRef,
624
632
  active: () => (stage() === "entering" || stage() === "leaving") && !reducedMotion(),
625
- timeout: timeout ?? config.timeout ?? 5e3,
633
+ timeout: props.timeout ?? props.config.timeout ?? 5e3,
626
634
  onEnd: () => {
627
- if (stage() === "entering") callbacks.onAfterEnter?.();
628
- else if (stage() === "leaving") callbacks.onAfterLeave?.();
635
+ if (stage() === "entering") props.callbacks.onAfterEnter?.();
636
+ else if (stage() === "leaving") props.callbacks.onAfterLeave?.();
629
637
  complete();
630
638
  }
631
639
  });
@@ -633,38 +641,38 @@ const TransitionRenderer = ({ config, htmlProps, show, appear, unmount, timeout,
633
641
  const el = elementRef.current;
634
642
  if (!el) return;
635
643
  if (reducedMotion()) {
636
- applyReducedMotion(currentStage, callbacks, complete);
644
+ applyReducedMotion(currentStage, props.callbacks, complete);
637
645
  return;
638
646
  }
639
647
  if (currentStage === "entering") {
640
- callbacks.onEnter?.();
641
- const frameId = applyEnter(el, config);
648
+ props.callbacks.onEnter?.();
649
+ const frameId = applyEnter(el, props.config);
642
650
  return () => cancelAnimationFrame(frameId);
643
651
  }
644
652
  if (currentStage === "leaving") {
645
- callbacks.onLeave?.();
646
- const frameId = applyLeave(el, config);
653
+ props.callbacks.onLeave?.();
654
+ const frameId = applyLeave(el, props.config);
647
655
  return () => cancelAnimationFrame(frameId);
648
656
  }
649
657
  if (currentStage === "entered") {
650
- removeClasses(el, config.enter);
658
+ removeClasses(el, props.config.enter);
651
659
  el.style.transition = "";
652
660
  }
653
661
  }, { immediate: true });
654
662
  return /* @__PURE__ */ jsx(Show, {
655
663
  when: shouldMount,
656
- fallback: effectiveUnmount ? null : h(config.tag, {
664
+ fallback: effectiveUnmount ? null : h(props.config.tag, {
657
665
  ref: mergedRef,
658
- ...htmlProps,
666
+ ...props.htmlProps,
659
667
  style: {
660
- ...htmlProps.style ?? {},
668
+ ...props.htmlProps.style ?? {},
661
669
  display: "none"
662
670
  }
663
- }, children),
664
- children: h(config.tag, {
671
+ }, props.children),
672
+ children: h(props.config.tag, {
665
673
  ref: mergedRef,
666
- ...htmlProps
667
- }, children)
674
+ ...props.htmlProps
675
+ }, props.children)
668
676
  });
669
677
  };
670
678
 
package/lib/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":["h","applyEnter","applyLeave","applyReducedMotion","isVNode"],"sources":["../src/useAnimationEnd.ts","../src/useReducedMotion.ts","../../../core/core/lib/jsx-runtime.js","../src/kinetic/CollapseRenderer.tsx","../src/useTransitionState.ts","../src/utils.ts","../src/kinetic/TransitionItem.tsx","../src/kinetic/GroupRenderer.tsx","../src/kinetic/StaggerRenderer.tsx","../src/kinetic/TransitionRenderer.tsx","../src/kinetic/createKineticComponent.tsx","../src/kinetic.ts","../src/presets.ts"],"sourcesContent":["import type { Ref } from '@pyreon/core'\nimport { watch } from '@pyreon/reactivity'\n\nconst DEFAULT_TIMEOUT = 5000\n\nexport type UseAnimationEnd = (options: {\n ref: Ref<HTMLElement>\n onEnd: () => void\n active: () => boolean\n timeout?: number | undefined\n}) => void\n\nconst useAnimationEnd: UseAnimationEnd = ({ ref, onEnd, active, timeout = DEFAULT_TIMEOUT }) => {\n let called = false\n\n watch(\n active,\n (isActive) => {\n if (!isActive) {\n called = false\n return\n }\n\n const el = ref.current\n if (!el) return\n\n called = false\n\n const done = () => {\n if (called) return\n called = true\n el.removeEventListener('transitionend', handleEnd)\n el.removeEventListener('animationend', handleEnd)\n clearTimeout(timer)\n onEnd()\n }\n\n const handleEnd = (e: Event) => {\n // Ignore bubbled events from children\n if (e.target !== el) return\n done()\n }\n\n el.addEventListener('transitionend', handleEnd)\n el.addEventListener('animationend', handleEnd)\n\n const timer = setTimeout(done, timeout)\n\n return () => {\n el.removeEventListener('transitionend', handleEnd)\n el.removeEventListener('animationend', handleEnd)\n clearTimeout(timer)\n }\n },\n { immediate: true },\n )\n}\n\nexport default useAnimationEnd\n","import { onMount, onUnmount } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\n\n/**\n * Inline reduced-motion check for kinetic package.\n * Avoids depending on @pyreon/hooks for a single media query.\n */\nexport function useReducedMotion(): () => boolean {\n const matches = signal(false)\n let mql: MediaQueryList | undefined\n\n const onChange = (e: MediaQueryListEvent) => {\n matches.set(e.matches)\n }\n\n onMount(() => {\n mql = window.matchMedia('(prefers-reduced-motion: reduce)')\n matches.set(mql.matches)\n mql.addEventListener('change', onChange)\n return undefined\n })\n\n onUnmount(() => {\n mql?.removeEventListener('change', onChange)\n })\n\n return matches\n}\n","//#region src/h.ts\n/** Marker for fragment nodes — renders children without a wrapper element */\nconst Fragment = Symbol(\"Pyreon.Fragment\");\n/**\n* Hyperscript function — the compiled output of JSX.\n* `<div class=\"x\">hello</div>` → `h(\"div\", { class: \"x\" }, \"hello\")`\n*\n* Generic on P so TypeScript validates props match the component's signature\n* at the call site, then stores the result in the loosely-typed VNode.\n*/\n/** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */\nconst EMPTY_PROPS = {};\nfunction h(type, props, ...children) {\n\treturn {\n\t\ttype,\n\t\tprops: props ?? EMPTY_PROPS,\n\t\tchildren: normalizeChildren(children),\n\t\tkey: props?.key ?? null\n\t};\n}\nfunction normalizeChildren(children) {\n\tfor (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);\n\treturn children;\n}\nfunction flattenChildren(children) {\n\tconst result = [];\n\tfor (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));\n\telse result.push(child);\n\treturn result;\n}\n\n//#endregion\n//#region src/jsx-runtime.ts\n/**\n* JSX automatic runtime.\n*\n* When tsconfig has `\"jsxImportSource\": \"@pyreon/core\"`, the TS/bundler compiler\n* rewrites JSX to imports from this file automatically:\n* <div class=\"x\" /> → jsx(\"div\", { class: \"x\" })\n*/\nfunction jsx(type, props, key) {\n\tconst { children, ...rest } = props;\n\tconst propsWithKey = key != null ? {\n\t\t...rest,\n\t\tkey\n\t} : rest;\n\tif (typeof type === \"function\") return h(type, children !== void 0 ? {\n\t\t...propsWithKey,\n\t\tchildren\n\t} : propsWithKey);\n\treturn h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);\n}\nconst jsxs = jsx;\n\n//#endregion\nexport { Fragment, jsx, jsxs };\n//# sourceMappingURL=jsx-runtime.js.map","import type { VNode } from '@pyreon/core'\nimport { createRef, h, Show } from '@pyreon/core'\nimport { runUntracked, signal, watch } from '@pyreon/reactivity'\nimport type { CSSProperties, TransitionCallbacks, TransitionStage } from '../types'\nimport useAnimationEnd from '../useAnimationEnd'\nimport { useReducedMotion } from '../useReducedMotion'\nimport type { KineticConfig } from './types'\n\ntype CollapseRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n show: () => boolean\n appear?: boolean | undefined\n timeout?: number | undefined\n transition?: string | undefined\n callbacks: Partial<TransitionCallbacks>\n children: VNode | VNode[]\n}\n\n/**\n * Renders a height-animated collapse. The config.tag becomes the outer\n * wrapper (overflow:hidden + animated height). An inner div measures\n * scrollHeight for the target value.\n */\nconst CollapseRenderer = ({\n config,\n htmlProps,\n show,\n appear,\n timeout,\n transition,\n callbacks,\n children,\n}: CollapseRendererProps): VNode | null => {\n const reducedMotion = useReducedMotion()\n let wrapperRef: { current: HTMLElement | null } = createRef<HTMLElement>()\n const contentRef = createRef<HTMLDivElement>()\n\n const effectiveAppear = appear ?? config.appear ?? false\n const effectiveTimeout = timeout ?? config.timeout ?? 5000\n const effectiveTransition = transition ?? config.transition ?? 'height 300ms ease'\n\n const initialShow = show()\n const needsAppear = effectiveAppear && initialShow\n const stage = signal<TransitionStage>(initialShow ? 'entered' : 'hidden')\n let isInitialMount = true\n let appearTriggered = false\n\n // Intercept ref assignment to trigger appear after all refs are wired\n if (needsAppear) {\n const orig = wrapperRef\n const proxy = { current: null as HTMLElement | null }\n Object.defineProperty(proxy, 'current', {\n get() {\n return orig.current\n },\n set(node: HTMLElement | null) {\n orig.current = node\n if (node && !appearTriggered) {\n appearTriggered = true\n queueMicrotask(() => stage.set('entering'))\n }\n },\n })\n wrapperRef = proxy\n }\n\n // State machine transitions\n watch(\n show,\n (showVal) => {\n if (isInitialMount) {\n isInitialMount = false\n // appear case is handled by ref proxy above\n return\n }\n\n const currentStage = runUntracked(() => stage())\n if (showVal && (currentStage === 'hidden' || currentStage === 'leaving')) {\n stage.set('entering')\n } else if (!showVal && (currentStage === 'entered' || currentStage === 'entering')) {\n stage.set('leaving')\n }\n },\n { immediate: true },\n )\n\n // Animate height\n watch(\n () => stage(),\n (currentStage) => {\n const wrapper = wrapperRef.current\n const content = contentRef.current\n if (!wrapper || !content) return\n\n if (reducedMotion()) {\n if (currentStage === 'entering') {\n callbacks.onEnter?.()\n wrapper.style.height = 'auto'\n wrapper.style.overflow = ''\n callbacks.onAfterEnter?.()\n stage.set('entered')\n } else if (currentStage === 'leaving') {\n callbacks.onLeave?.()\n wrapper.style.height = '0px'\n wrapper.style.overflow = 'hidden'\n callbacks.onAfterLeave?.()\n stage.set('hidden')\n }\n return\n }\n\n if (currentStage === 'entering') {\n callbacks.onEnter?.()\n const height = content.scrollHeight\n wrapper.style.transition = 'none'\n wrapper.style.height = '0px'\n wrapper.style.overflow = 'hidden'\n // Force reflow\n void wrapper.offsetHeight\n wrapper.style.transition = effectiveTransition\n wrapper.style.height = `${height}px`\n }\n\n if (currentStage === 'leaving') {\n callbacks.onLeave?.()\n const height = content.scrollHeight\n wrapper.style.transition = 'none'\n wrapper.style.height = `${height}px`\n wrapper.style.overflow = 'hidden'\n // Force reflow\n void wrapper.offsetHeight\n wrapper.style.transition = effectiveTransition\n wrapper.style.height = '0px'\n }\n },\n { immediate: true },\n )\n\n useAnimationEnd({\n ref: wrapperRef,\n active: () => (stage() === 'entering' || stage() === 'leaving') && !reducedMotion(),\n timeout: effectiveTimeout,\n onEnd: () => {\n const wrapper = wrapperRef.current\n if (stage() === 'entering') {\n if (wrapper) {\n wrapper.style.height = 'auto'\n wrapper.style.overflow = ''\n wrapper.style.transition = ''\n }\n callbacks.onAfterEnter?.()\n stage.set('entered')\n } else if (stage() === 'leaving') {\n callbacks.onAfterLeave?.()\n stage.set('hidden')\n }\n },\n })\n\n const shouldRender = () => stage() !== 'hidden'\n\n const wrapperStyle: CSSProperties = {\n ...((htmlProps.style as CSSProperties) ?? {}),\n ...(stage() !== 'entered' ? { overflow: 'hidden' } : {}),\n ...(stage() === 'hidden' ? { height: '0px' } : stage() === 'entered' ? { height: 'auto' } : {}),\n }\n\n return h(\n config.tag,\n { ref: wrapperRef, ...htmlProps, style: wrapperStyle },\n <Show when={shouldRender}>\n <div ref={contentRef}>{children}</div>\n </Show>,\n )\n}\n\nexport default CollapseRenderer\n","import { createRef } from '@pyreon/core'\nimport { runUntracked, signal, watch } from '@pyreon/reactivity'\nimport type { TransitionStage, TransitionStateResult } from './types'\n\nexport type UseTransitionState = (options: {\n show: () => boolean\n appear?: boolean | undefined\n}) => TransitionStateResult\n\nconst useTransitionState: UseTransitionState = ({ show, appear = false }) => {\n const initialShow = show()\n // When appear=true and show starts true, mount the element (stage='entered')\n // but defer the enter animation until the ref is connected.\n const needsAppear = appear && initialShow\n const stage = signal<TransitionStage>(initialShow ? 'entered' : 'hidden')\n const elementRef = createRef<HTMLElement>()\n let isInitialMount = true\n let appearTriggered = false\n\n // Ref callback that triggers the appear animation once the element is wired\n const refCallback = (node: HTMLElement | null) => {\n elementRef.current = node\n if (node && needsAppear && !appearTriggered) {\n appearTriggered = true\n stage.set('entering')\n }\n }\n\n watch(\n show,\n (showVal) => {\n if (isInitialMount) {\n isInitialMount = false\n // appear case is handled by refCallback above\n return\n }\n\n const currentStage = runUntracked(() => stage())\n if (showVal && (currentStage === 'hidden' || currentStage === 'leaving')) {\n stage.set('entering')\n } else if (!showVal && (currentStage === 'entered' || currentStage === 'entering')) {\n stage.set('leaving')\n }\n },\n { immediate: true },\n )\n\n const complete = () => {\n const current = stage()\n if (current === 'entering') stage.set('entered')\n if (current === 'leaving') stage.set('hidden')\n }\n\n return {\n stage,\n ref: refCallback,\n shouldMount: () => stage() !== 'hidden',\n complete,\n }\n}\n\nexport default useTransitionState\n","import type { Ref, VNode } from '@pyreon/core'\nimport type { CSSProperties } from './types'\n\nconst splitCache = new Map<string, string[]>()\nconst splitClasses = (classes: string): string[] => {\n let cached = splitCache.get(classes)\n if (!cached) {\n cached = classes.split(/\\s+/).filter(Boolean)\n splitCache.set(classes, cached)\n }\n return cached\n}\n\n/** Adds space-separated CSS classes to an element. */\nexport const addClasses = (el: HTMLElement, classes: string | undefined) => {\n if (!classes) return\n const list = splitClasses(classes)\n if (list.length > 0) el.classList.add(...list)\n}\n\n/** Removes space-separated CSS classes from an element. */\nexport const removeClasses = (el: HTMLElement, classes: string | undefined) => {\n if (!classes) return\n const list = splitClasses(classes)\n if (list.length > 0) el.classList.remove(...list)\n}\n\n/**\n * Executes callback after two animation frames (double-rAF).\n * Ensures the browser paints the current state before applying changes,\n * which is required for CSS transitions to trigger.\n */\nexport const nextFrame = (callback: () => void): number =>\n requestAnimationFrame(() => {\n requestAnimationFrame(callback)\n })\n\n/** Merges two className strings, filtering undefined/empty. */\nexport const mergeClassNames = (\n existing: string | undefined,\n additional: string | undefined,\n): string | undefined => {\n const parts = [existing, additional].filter(Boolean)\n return parts.length > 0 ? parts.join(' ') : undefined\n}\n\n/** Merges two CSSProperties objects, with `b` taking precedence. */\nexport const mergeStyles = (\n a: CSSProperties | undefined,\n b: CSSProperties | undefined,\n): CSSProperties | undefined => {\n if (!a && !b) return undefined\n if (!a) return b\n if (!b) return a\n return { ...a, ...b }\n}\n\n// ─── Ref & Motion Utilities ─────────────────────────────────\n\ntype RefCallback<T> = (node: T | null) => void\ntype RefLike<T> = RefCallback<T> | Ref<T>\n\n/** Merges multiple refs (callback or object) into a single callback ref. */\nexport const mergeRefs = <T>(...refs: (RefLike<T> | undefined)[]): ((node: T | null) => void) => {\n return (node: T | null) => {\n for (const ref of refs) {\n if (!ref) continue\n if (typeof ref === 'function') {\n ref(node)\n } else {\n ;(ref as { current: unknown }).current = node\n }\n }\n }\n}\n\n/** Clones a VNode with merged props. */\nexport const cloneVNode = (vnode: VNode, extraProps: Record<string, unknown>): VNode => ({\n ...vnode,\n props: { ...vnode.props, ...extraProps },\n})\n","import type { VNode } from '@pyreon/core'\nimport { createRef, Show } from '@pyreon/core'\nimport { watch } from '@pyreon/reactivity'\nimport type { ClassTransitionProps, StyleTransitionProps, TransitionCallbacks } from '../types'\nimport useAnimationEnd from '../useAnimationEnd'\nimport { useReducedMotion } from '../useReducedMotion'\nimport useTransitionState from '../useTransitionState'\nimport { addClasses, cloneVNode, mergeRefs, mergeStyles, nextFrame, removeClasses } from '../utils'\n\ntype TransitionItemProps = ClassTransitionProps &\n StyleTransitionProps &\n TransitionCallbacks & {\n show: () => boolean\n appear?: boolean | undefined\n unmount?: boolean | undefined\n timeout?: number | undefined\n delay?: number | undefined\n children: VNode\n }\n\nconst applyEnter = (el: HTMLElement, config: ClassTransitionProps & StyleTransitionProps) => {\n addClasses(el, config.enter)\n addClasses(el, config.enterFrom)\n if (config.enterStyle) Object.assign(el.style, config.enterStyle)\n if (config.enterTransition) el.style.transition = config.enterTransition\n\n return nextFrame(() => {\n removeClasses(el, config.enterFrom)\n addClasses(el, config.enterTo)\n if (config.enterToStyle) Object.assign(el.style, config.enterToStyle)\n })\n}\n\nconst applyLeave = (el: HTMLElement, config: ClassTransitionProps & StyleTransitionProps) => {\n removeClasses(el, config.enter)\n removeClasses(el, config.enterTo)\n\n addClasses(el, config.leave)\n addClasses(el, config.leaveFrom)\n if (config.leaveStyle) Object.assign(el.style, config.leaveStyle)\n if (config.leaveTransition) el.style.transition = config.leaveTransition\n\n return nextFrame(() => {\n removeClasses(el, config.leaveFrom)\n addClasses(el, config.leaveTo)\n if (config.leaveToStyle) Object.assign(el.style, config.leaveToStyle)\n })\n}\n\nconst applyReducedMotion = (\n stage: string,\n callbacks: Partial<TransitionCallbacks>,\n complete: () => void,\n) => {\n if (stage === 'entering') {\n callbacks.onEnter?.()\n callbacks.onAfterEnter?.()\n complete()\n } else if (stage === 'leaving') {\n callbacks.onLeave?.()\n callbacks.onAfterLeave?.()\n complete()\n }\n}\n\n/**\n * Internal per-child transition component. Used by StaggerRenderer and\n * GroupRenderer to give each child its own animation state.\n *\n * Uses cloneVNode to inject ref onto the child — the child must accept ref.\n */\nconst TransitionItem = ({\n show,\n appear = false,\n unmount = true,\n timeout = 5000,\n enter,\n enterFrom,\n enterTo,\n leave,\n leaveFrom,\n leaveTo,\n enterStyle,\n enterToStyle,\n enterTransition,\n leaveStyle,\n leaveToStyle,\n leaveTransition,\n onEnter,\n onAfterEnter,\n onLeave,\n onAfterLeave,\n children,\n}: TransitionItemProps): VNode | null => {\n const reducedMotion = useReducedMotion()\n const { stage, ref: stateRef, shouldMount, complete } = useTransitionState({ show, appear })\n\n const elementRef = createRef<HTMLElement>()\n const mergedRef = mergeRefs(\n elementRef,\n stateRef,\n (children.props as Record<string, unknown>)?.ref as\n | ((el: HTMLElement | null) => void)\n | undefined,\n )\n\n const callbacks = {\n onEnter,\n onAfterEnter,\n onLeave,\n onAfterLeave,\n }\n\n const transitionConfig = {\n enter,\n enterFrom,\n enterTo,\n leave,\n leaveFrom,\n leaveTo,\n enterStyle,\n enterToStyle,\n enterTransition,\n leaveStyle,\n leaveToStyle,\n leaveTransition,\n }\n\n useAnimationEnd({\n ref: elementRef,\n active: () => (stage() === 'entering' || stage() === 'leaving') && !reducedMotion(),\n timeout,\n onEnd: () => {\n if (stage() === 'entering') {\n callbacks.onAfterEnter?.()\n } else if (stage() === 'leaving') {\n callbacks.onAfterLeave?.()\n }\n complete()\n },\n })\n\n watch(\n () => stage(),\n (currentStage) => {\n const el = elementRef.current\n if (!el) return\n\n if (reducedMotion()) {\n applyReducedMotion(currentStage, callbacks, complete)\n return\n }\n\n if (currentStage === 'entering') {\n callbacks.onEnter?.()\n const frameId = applyEnter(el, transitionConfig)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'leaving') {\n callbacks.onLeave?.()\n const frameId = applyLeave(el, transitionConfig)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'entered') {\n removeClasses(el, enter)\n el.style.transition = ''\n }\n },\n { immediate: true },\n )\n\n return (\n <Show\n when={shouldMount}\n fallback={\n unmount\n ? null\n : cloneVNode(children, {\n ref: mergedRef,\n style: mergeStyles(\n (children.props as Record<string, unknown>)?.style as\n | Record<string, string | number | undefined>\n | undefined,\n { display: 'none' },\n ),\n })\n }\n >\n {cloneVNode(children, { ref: mergedRef })}\n </Show>\n )\n}\n\nexport default TransitionItem\n","import type { VNode } from '@pyreon/core'\nimport { h } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\nimport type { TransitionCallbacks } from '../types'\nimport TransitionItem from './TransitionItem'\nimport type { KineticConfig } from './types'\n\ntype GroupRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n appear?: boolean | undefined\n timeout?: number | undefined\n callbacks: Partial<TransitionCallbacks>\n /**\n * Children can be a static array OR a reactive accessor `() => VNode[]`.\n * When passed as an accessor, GroupRenderer tracks changes and\n * animates entering/leaving children automatically.\n */\n children: VNode[] | (() => VNode[])\n}\n\ntype KeyedChild = { key: string | number; element: VNode }\n\nconst isVNode = (child: unknown): child is VNode =>\n child != null && typeof child === 'object' && 'type' in (child as object)\n\nconst getKeyedChildren = (children: VNode[]): KeyedChild[] => {\n const result: KeyedChild[] = []\n for (const child of children) {\n if (isVNode(child)) {\n const key = (child as VNode & { key?: string | number }).key\n if (key != null) {\n result.push({ key, element: child })\n }\n }\n }\n return result\n}\n\n/**\n * Renders children with key-based enter/exit animation (no `show` prop).\n * Children that appear (new key) animate in. Children that disappear\n * (removed key) stay in DOM during leave animation, then unmount.\n * config.tag wraps all children as a container element.\n *\n * In Pyreon, components run once. Pass children as a reactive accessor\n * `() => VNode[]` for the group to detect changes and animate entries/exits.\n */\nconst GroupRenderer = ({\n config,\n htmlProps,\n appear,\n timeout,\n callbacks,\n children,\n}: GroupRendererProps): VNode | null => {\n const effectiveAppear = appear ?? config.appear ?? false\n const effectiveTimeout = timeout ?? config.timeout ?? 5000\n\n const prevMap = new Map<string | number, VNode>()\n const leavingMap = new Map<string | number, VNode>()\n const forceUpdate = signal(0)\n\n // Normalize children to an accessor\n const getChildren = typeof children === 'function' ? (children as () => VNode[]) : () => children\n\n // Track initial keys for appear animation logic\n const initialKeyed = getKeyedChildren(getChildren())\n const initialKeys = new Set(initialKeyed.map((c) => c.key))\n for (const { key, element } of initialKeyed) {\n prevMap.set(key, element)\n }\n\n const handleAfterLeave = (key: string | number) => {\n leavingMap.delete(key)\n callbacks.onAfterLeave?.()\n forceUpdate.update((c) => c + 1)\n }\n\n // Reactive accessor — re-evaluates when children() or forceUpdate changes\n return (() => {\n forceUpdate()\n\n const currentChildren = getChildren()\n const currentKeyed = getKeyedChildren(currentChildren)\n const currentMap = new Map<string | number, VNode>()\n for (const { key, element } of currentKeyed) {\n currentMap.set(key, element)\n }\n\n // Detect leaving children\n for (const [key, child] of prevMap) {\n if (!currentMap.has(key) && !leavingMap.has(key)) {\n leavingMap.set(key, child)\n }\n }\n\n // Cancel leave if child reappears\n for (const key of currentMap.keys()) {\n leavingMap.delete(key)\n }\n\n // Update prev for next diff\n prevMap.clear()\n for (const [key, element] of currentMap) {\n prevMap.set(key, element)\n }\n\n // Merge current + leaving\n const allEntries: KeyedChild[] = [...currentKeyed]\n for (const [key, element] of leavingMap) {\n allEntries.push({ key, element })\n }\n\n const groupedChildren = allEntries.map(({ key, element }) => {\n const isInitial = initialKeys.has(key)\n const isShowing = currentMap.has(key)\n\n return (\n <TransitionItem\n show={() => isShowing}\n appear={isInitial ? effectiveAppear : true}\n timeout={effectiveTimeout}\n enterStyle={config.enterStyle}\n enterToStyle={config.enterToStyle}\n enterTransition={config.enterTransition}\n leaveStyle={config.leaveStyle}\n leaveToStyle={config.leaveToStyle}\n leaveTransition={config.leaveTransition}\n enter={config.enter}\n enterFrom={config.enterFrom}\n enterTo={config.enterTo}\n leave={config.leave}\n leaveFrom={config.leaveFrom}\n leaveTo={config.leaveTo}\n onAfterLeave={() => handleAfterLeave(key)}\n >\n {element}\n </TransitionItem>\n )\n })\n\n return h(config.tag, { ...htmlProps }, ...groupedChildren)\n }) as unknown as VNode\n}\n\nexport default GroupRenderer\n","import type { VNode } from '@pyreon/core'\nimport { h } from '@pyreon/core'\nimport type { CSSProperties, TransitionCallbacks } from '../types'\nimport { cloneVNode } from '../utils'\nimport TransitionItem from './TransitionItem'\nimport type { KineticConfig } from './types'\n\ntype StaggerRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n show: () => boolean\n appear?: boolean | undefined\n timeout?: number | undefined\n interval?: number | undefined\n reverseLeave?: boolean | undefined\n callbacks: Partial<TransitionCallbacks>\n children: VNode[]\n}\n\nconst isVNode = (child: unknown): child is VNode =>\n child != null && typeof child === 'object' && 'type' in (child as object)\n\n/**\n * Renders children with staggered enter/exit animation.\n * config.tag wraps the staggered children as a container element.\n * Each child is individually animated via TransitionItem.\n */\nconst StaggerRenderer = ({\n config,\n htmlProps,\n show,\n appear,\n timeout,\n interval,\n reverseLeave,\n callbacks,\n children,\n}: StaggerRendererProps): VNode | null => {\n const effectiveAppear = appear ?? config.appear ?? false\n const effectiveTimeout = timeout ?? config.timeout ?? 5000\n const effectiveInterval = interval ?? config.interval ?? 50\n const effectiveReverseLeave = reverseLeave ?? config.reverseLeave ?? false\n\n const childArray = (Array.isArray(children) ? children : [children]).filter(isVNode)\n const count = childArray.length\n\n const staggeredChildren = childArray.map((child, index) => {\n const staggerIndex = !show() && effectiveReverseLeave ? count - 1 - index : index\n const delay = staggerIndex * effectiveInterval\n\n return (\n <TransitionItem\n key={(child as VNode & { key?: string | number }).key ?? index}\n show={show}\n appear={effectiveAppear}\n timeout={effectiveTimeout + delay}\n enterStyle={config.enterStyle}\n enterToStyle={config.enterToStyle}\n enterTransition={config.enterTransition}\n leaveStyle={config.leaveStyle}\n leaveToStyle={config.leaveToStyle}\n leaveTransition={config.leaveTransition}\n enter={config.enter}\n enterFrom={config.enterFrom}\n enterTo={config.enterTo}\n leave={config.leave}\n leaveFrom={config.leaveFrom}\n leaveTo={config.leaveTo}\n onAfterLeave={\n index === (effectiveReverseLeave ? 0 : count - 1) ? callbacks.onAfterLeave : undefined\n }\n >\n {cloneVNode(child, {\n style: {\n ...((child.props as Record<string, unknown>)?.style as CSSProperties | undefined),\n '--stagger-index': staggerIndex,\n '--stagger-interval': `${effectiveInterval}ms`,\n transitionDelay: `${delay}ms`,\n } as CSSProperties,\n })}\n </TransitionItem>\n )\n })\n\n return h(config.tag, { ...htmlProps }, ...staggeredChildren)\n}\n\nexport default StaggerRenderer\n","import type { VNode } from '@pyreon/core'\nimport { createRef, h, Show } from '@pyreon/core'\nimport { watch } from '@pyreon/reactivity'\nimport type { CSSProperties, TransitionCallbacks } from '../types'\nimport useAnimationEnd from '../useAnimationEnd'\nimport { useReducedMotion } from '../useReducedMotion'\nimport useTransitionState from '../useTransitionState'\nimport { addClasses, mergeRefs, nextFrame, removeClasses } from '../utils'\nimport type { KineticConfig } from './types'\n\ntype TransitionRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n show: () => boolean\n appear?: boolean | undefined\n unmount?: boolean | undefined\n timeout?: number | undefined\n callbacks: Partial<TransitionCallbacks>\n children: VNode | VNode[]\n}\n\nconst applyEnter = (el: HTMLElement, config: KineticConfig) => {\n addClasses(el, config.enter)\n addClasses(el, config.enterFrom)\n if (config.enterStyle) Object.assign(el.style, config.enterStyle)\n if (config.enterTransition) el.style.transition = config.enterTransition\n\n return nextFrame(() => {\n removeClasses(el, config.enterFrom)\n addClasses(el, config.enterTo)\n if (config.enterToStyle) Object.assign(el.style, config.enterToStyle)\n })\n}\n\nconst applyLeave = (el: HTMLElement, config: KineticConfig) => {\n removeClasses(el, config.enter)\n removeClasses(el, config.enterTo)\n\n addClasses(el, config.leave)\n addClasses(el, config.leaveFrom)\n if (config.leaveStyle) Object.assign(el.style, config.leaveStyle)\n if (config.leaveTransition) el.style.transition = config.leaveTransition\n\n return nextFrame(() => {\n removeClasses(el, config.leaveFrom)\n addClasses(el, config.leaveTo)\n if (config.leaveToStyle) Object.assign(el.style, config.leaveToStyle)\n })\n}\n\nconst applyReducedMotion = (\n stage: string,\n cbs: Partial<TransitionCallbacks>,\n complete: () => void,\n) => {\n if (stage === 'entering') {\n cbs.onEnter?.()\n cbs.onAfterEnter?.()\n complete()\n } else if (stage === 'leaving') {\n cbs.onLeave?.()\n cbs.onAfterLeave?.()\n complete()\n }\n}\n\n/**\n * Renders a single element with CSS transition enter/exit animation.\n * Uses h(config.tag) — no cloneElement needed.\n */\nconst TransitionRenderer = ({\n config,\n htmlProps,\n show,\n appear,\n unmount,\n timeout,\n callbacks,\n children,\n}: TransitionRendererProps): VNode | null => {\n const reducedMotion = useReducedMotion()\n const {\n stage,\n ref: stateRef,\n shouldMount,\n complete,\n } = useTransitionState({\n show,\n appear: appear ?? config.appear ?? false,\n })\n\n const elementRef = createRef<HTMLElement>()\n const mergedRef = mergeRefs(elementRef, stateRef)\n\n const effectiveUnmount = unmount ?? config.unmount ?? true\n const effectiveTimeout = timeout ?? config.timeout ?? 5000\n\n useAnimationEnd({\n ref: elementRef,\n active: () => (stage() === 'entering' || stage() === 'leaving') && !reducedMotion(),\n timeout: effectiveTimeout,\n onEnd: () => {\n if (stage() === 'entering') {\n callbacks.onAfterEnter?.()\n } else if (stage() === 'leaving') {\n callbacks.onAfterLeave?.()\n }\n complete()\n },\n })\n\n watch(\n () => stage(),\n (currentStage) => {\n const el = elementRef.current\n if (!el) return\n\n if (reducedMotion()) {\n applyReducedMotion(currentStage, callbacks, complete)\n return\n }\n\n if (currentStage === 'entering') {\n callbacks.onEnter?.()\n const frameId = applyEnter(el, config)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'leaving') {\n callbacks.onLeave?.()\n const frameId = applyLeave(el, config)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'entered') {\n removeClasses(el, config.enter)\n el.style.transition = ''\n }\n },\n { immediate: true },\n )\n\n return (\n <Show\n when={shouldMount}\n fallback={\n effectiveUnmount\n ? null\n : h(\n config.tag,\n {\n ref: mergedRef,\n ...htmlProps,\n style: {\n ...((htmlProps.style as CSSProperties) ?? {}),\n display: 'none',\n },\n },\n children,\n )\n }\n >\n {h(config.tag, { ref: mergedRef, ...htmlProps }, children)}\n </Show>\n )\n}\n\nexport default TransitionRenderer\n","import type { VNode } from '@pyreon/core'\nimport type { CSSProperties, TransitionCallbacks } from '../types'\nimport CollapseRenderer from './CollapseRenderer'\nimport GroupRenderer from './GroupRenderer'\nimport StaggerRenderer from './StaggerRenderer'\nimport TransitionRenderer from './TransitionRenderer'\nimport type { ClassConfig, KineticComponent, KineticConfig, KineticMode } from './types'\n\n/** Keys that are kinetic-specific and should not be forwarded as HTML attrs. */\nconst KINETIC_KEYS = new Set([\n 'show',\n 'appear',\n 'unmount',\n 'timeout',\n 'transition',\n 'interval',\n 'reverseLeave',\n 'onEnter',\n 'onAfterEnter',\n 'onLeave',\n 'onAfterLeave',\n])\n\n/**\n * Core factory. Creates a component that delegates to the appropriate\n * renderer based on config.mode, then attaches immutable chain methods\n * via Object.assign.\n */\nconst createKineticComponent = <Tag extends string, Mode extends KineticMode = 'transition'>(\n config: KineticConfig,\n): KineticComponent<Tag, Mode> => {\n const Component = (props: Record<string, unknown>): VNode | null => {\n // Separate kinetic-specific props from HTML pass-through props\n const htmlProps: Record<string, unknown> = {}\n const kineticProps: Record<string, unknown> = {}\n\n for (const key in props) {\n if (KINETIC_KEYS.has(key)) {\n kineticProps[key] = props[key]\n } else {\n htmlProps[key] = props[key]\n }\n }\n\n const {\n show,\n appear,\n unmount,\n timeout,\n transition,\n interval,\n reverseLeave,\n onEnter,\n onAfterEnter,\n onLeave,\n onAfterLeave,\n } = kineticProps as {\n show?: () => boolean\n appear?: boolean\n unmount?: boolean\n timeout?: number\n transition?: string\n interval?: number\n reverseLeave?: boolean\n } & Partial<TransitionCallbacks>\n\n const callbacks: Partial<TransitionCallbacks> = {\n onEnter: onEnter ?? config.onEnter,\n onAfterEnter: onAfterEnter ?? config.onAfterEnter,\n onLeave: onLeave ?? config.onLeave,\n onAfterLeave: onAfterLeave ?? config.onAfterLeave,\n }\n\n // Extract children from htmlProps (it's not an HTML attribute)\n const { children, ...restHtml } = htmlProps\n\n if (config.mode === 'collapse') {\n return (\n <CollapseRenderer\n config={config}\n htmlProps={restHtml}\n show={show as () => boolean}\n appear={appear}\n timeout={timeout}\n transition={transition}\n callbacks={callbacks}\n >\n {children as VNode | VNode[]}\n </CollapseRenderer>\n )\n }\n\n if (config.mode === 'stagger') {\n return (\n <StaggerRenderer\n config={config}\n htmlProps={restHtml}\n show={show as () => boolean}\n appear={appear}\n timeout={timeout}\n interval={interval}\n reverseLeave={reverseLeave}\n callbacks={callbacks}\n >\n {children as VNode[]}\n </StaggerRenderer>\n )\n }\n\n if (config.mode === 'group') {\n return (\n <GroupRenderer\n config={config}\n htmlProps={restHtml}\n appear={appear}\n timeout={timeout}\n callbacks={callbacks}\n >\n {children as VNode[]}\n </GroupRenderer>\n )\n }\n\n // Default: transition mode\n return (\n <TransitionRenderer\n config={config}\n htmlProps={restHtml}\n show={show as () => boolean}\n appear={appear}\n unmount={unmount}\n timeout={timeout}\n callbacks={callbacks}\n >\n {children as VNode | VNode[]}\n </TransitionRenderer>\n )\n }\n\n Component.displayName = `kinetic(${config.tag})`\n\n // Immutable chain methods — each returns a new component with merged config.\n return Object.assign(Component, {\n preset: (preset: Record<string, unknown>) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n ...preset,\n } as KineticConfig),\n\n enter: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, enterStyle: styles }),\n\n enterTo: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, enterToStyle: styles }),\n\n enterTransition: (value: string) =>\n createKineticComponent<Tag, Mode>({ ...config, enterTransition: value }),\n\n leave: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, leaveStyle: styles }),\n\n leaveTo: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, leaveToStyle: styles }),\n\n leaveTransition: (value: string) =>\n createKineticComponent<Tag, Mode>({ ...config, leaveTransition: value }),\n\n enterClass: ({ active, from, to }: ClassConfig) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n enter: active,\n enterFrom: from,\n enterTo: to,\n }),\n\n leaveClass: ({ active, from, to }: ClassConfig) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n leave: active,\n leaveFrom: from,\n leaveTo: to,\n }),\n\n config: (opts: Record<string, unknown>) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n ...opts,\n } as KineticConfig),\n\n on: (cbs: Partial<TransitionCallbacks>) =>\n createKineticComponent<Tag, Mode>({ ...config, ...cbs }),\n\n collapse: (opts?: { transition?: string }) =>\n createKineticComponent<Tag, 'collapse'>({\n ...config,\n mode: 'collapse',\n ...opts,\n }),\n\n stagger: (opts?: { interval?: number; reverseLeave?: boolean }) =>\n createKineticComponent<Tag, 'stagger'>({\n ...config,\n mode: 'stagger',\n ...opts,\n }),\n\n group: () => createKineticComponent<Tag, 'group'>({ ...config, mode: 'group' }),\n }) as unknown as KineticComponent<Tag, Mode>\n}\n\nexport default createKineticComponent\n","import createKineticComponent from './kinetic/createKineticComponent'\nimport type { KineticComponent } from './kinetic/types'\n\n/**\n * Creates a reusable animated component via immutable chaining.\n *\n * @example\n * ```tsx\n * // Transition (default)\n * const FadeDiv = kinetic('div').preset(fade)\n *\n * // Collapse\n * const Accordion = kinetic('div').collapse()\n *\n * // Stagger\n * const StaggerList = kinetic('ul').preset(slideUp).stagger({ interval: 50 })\n *\n * // Group (key-based enter/exit)\n * const AnimatedList = kinetic('ul').preset(fade).group()\n * ```\n */\nconst kinetic = <Tag extends string>(tag: Tag): KineticComponent<Tag, 'transition'> =>\n createKineticComponent<Tag, 'transition'>({ tag, mode: 'transition' })\n\nexport default kinetic\n","import type { ClassTransitionProps, StyleTransitionProps } from './types'\n\nexport type Preset = StyleTransitionProps & ClassTransitionProps\n\nexport const fade: Preset = {\n enterStyle: { opacity: 0 },\n enterToStyle: { opacity: 1 },\n enterTransition: 'opacity 300ms ease-out',\n leaveStyle: { opacity: 1 },\n leaveToStyle: { opacity: 0 },\n leaveTransition: 'opacity 200ms ease-in',\n}\n\nexport const scaleIn: Preset = {\n enterStyle: { opacity: 0, transform: 'scale(0.95)' },\n enterToStyle: { opacity: 1, transform: 'scale(1)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'scale(1)' },\n leaveToStyle: { opacity: 0, transform: 'scale(0.95)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideUp: Preset = {\n enterStyle: { opacity: 0, transform: 'translateY(16px)' },\n enterToStyle: { opacity: 1, transform: 'translateY(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateY(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateY(16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideDown: Preset = {\n enterStyle: { opacity: 0, transform: 'translateY(-16px)' },\n enterToStyle: { opacity: 1, transform: 'translateY(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateY(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateY(-16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideLeft: Preset = {\n enterStyle: { opacity: 0, transform: 'translateX(16px)' },\n enterToStyle: { opacity: 1, transform: 'translateX(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateX(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateX(16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideRight: Preset = {\n enterStyle: { opacity: 0, transform: 'translateX(-16px)' },\n enterToStyle: { opacity: 1, transform: 'translateX(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateX(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateX(-16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const presets = {\n fade,\n scaleIn,\n slideUp,\n slideDown,\n slideLeft,\n slideRight,\n} as const\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":["h","applyEnter","applyLeave","applyReducedMotion","isVNode"],"sources":["../src/useAnimationEnd.ts","../src/useReducedMotion.ts","../../../core/core/lib/jsx-runtime.js","../src/kinetic/CollapseRenderer.tsx","../src/useTransitionState.ts","../src/utils.ts","../src/kinetic/TransitionItem.tsx","../src/kinetic/GroupRenderer.tsx","../src/kinetic/StaggerRenderer.tsx","../src/kinetic/TransitionRenderer.tsx","../src/kinetic/createKineticComponent.tsx","../src/kinetic.ts","../src/presets.ts"],"sourcesContent":["import type { Ref } from '@pyreon/core'\nimport { watch } from '@pyreon/reactivity'\n\nconst DEFAULT_TIMEOUT = 5000\n\nexport type UseAnimationEnd = (options: {\n ref: Ref<HTMLElement>\n onEnd: () => void\n active: () => boolean\n timeout?: number | undefined\n}) => void\n\nconst useAnimationEnd: UseAnimationEnd = ({ ref, onEnd, active, timeout = DEFAULT_TIMEOUT }) => {\n let called = false\n\n watch(\n active,\n (isActive) => {\n if (!isActive) {\n called = false\n return\n }\n\n const el = ref.current\n if (!el) return\n\n called = false\n\n const done = () => {\n if (called) return\n called = true\n el.removeEventListener('transitionend', handleEnd)\n el.removeEventListener('animationend', handleEnd)\n clearTimeout(timer)\n onEnd()\n }\n\n const handleEnd = (e: Event) => {\n // Ignore bubbled events from children\n if (e.target !== el) return\n done()\n }\n\n el.addEventListener('transitionend', handleEnd)\n el.addEventListener('animationend', handleEnd)\n\n const timer = setTimeout(done, timeout)\n\n return () => {\n el.removeEventListener('transitionend', handleEnd)\n el.removeEventListener('animationend', handleEnd)\n clearTimeout(timer)\n }\n },\n { immediate: true },\n )\n}\n\nexport default useAnimationEnd\n","import { onMount, onUnmount } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\n\n/**\n * Inline reduced-motion check for kinetic package.\n * Avoids depending on @pyreon/hooks for a single media query.\n */\nexport function useReducedMotion(): () => boolean {\n const matches = signal(false)\n let mql: MediaQueryList | undefined\n\n const onChange = (e: MediaQueryListEvent) => {\n matches.set(e.matches)\n }\n\n onMount(() => {\n mql = window.matchMedia('(prefers-reduced-motion: reduce)')\n matches.set(mql.matches)\n mql.addEventListener('change', onChange)\n return undefined\n })\n\n onUnmount(() => {\n mql?.removeEventListener('change', onChange)\n })\n\n return matches\n}\n","//#region src/h.ts\n/** Marker for fragment nodes — renders children without a wrapper element */\nconst Fragment = Symbol(\"Pyreon.Fragment\");\n/**\n* Hyperscript function — the compiled output of JSX.\n* `<div class=\"x\">hello</div>` → `h(\"div\", { class: \"x\" }, \"hello\")`\n*\n* Generic on P so TypeScript validates props match the component's signature\n* at the call site, then stores the result in the loosely-typed VNode.\n*/\n/** Shared empty props sentinel — identity-checked in mountElement to skip applyProps. */\nconst EMPTY_PROPS = {};\nfunction h(type, props, ...children) {\n\treturn {\n\t\ttype,\n\t\tprops: props ?? EMPTY_PROPS,\n\t\tchildren: normalizeChildren(children),\n\t\tkey: props?.key ?? null\n\t};\n}\nfunction normalizeChildren(children) {\n\tfor (let i = 0; i < children.length; i++) if (Array.isArray(children[i])) return flattenChildren(children);\n\treturn children;\n}\nfunction flattenChildren(children) {\n\tconst result = [];\n\tfor (const child of children) if (Array.isArray(child)) result.push(...flattenChildren(child));\n\telse result.push(child);\n\treturn result;\n}\n\n//#endregion\n//#region src/jsx-runtime.ts\n/**\n* JSX automatic runtime.\n*\n* When tsconfig has `\"jsxImportSource\": \"@pyreon/core\"`, the TS/bundler compiler\n* rewrites JSX to imports from this file automatically:\n* <div class=\"x\" /> → jsx(\"div\", { class: \"x\" })\n*/\nfunction jsx(type, props, key) {\n\tconst { children, ...rest } = props;\n\tconst propsWithKey = key != null ? {\n\t\t...rest,\n\t\tkey\n\t} : rest;\n\tif (typeof type === \"function\") return h(type, children !== void 0 ? {\n\t\t...propsWithKey,\n\t\tchildren\n\t} : propsWithKey);\n\treturn h(type, propsWithKey, ...children === void 0 ? [] : Array.isArray(children) ? children : [children]);\n}\nconst jsxs = jsx;\n\n//#endregion\nexport { Fragment, jsx, jsxs };\n//# sourceMappingURL=jsx-runtime.js.map","import type { VNode } from '@pyreon/core'\nimport { createRef, h, Show } from '@pyreon/core'\nimport { runUntracked, signal, watch } from '@pyreon/reactivity'\nimport type { CSSProperties, TransitionCallbacks, TransitionStage } from '../types'\nimport useAnimationEnd from '../useAnimationEnd'\nimport { useReducedMotion } from '../useReducedMotion'\nimport type { KineticConfig } from './types'\n\ntype CollapseRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n show: () => boolean\n appear?: boolean | undefined\n timeout?: number | undefined\n transition?: string | undefined\n callbacks: Partial<TransitionCallbacks>\n children: VNode | VNode[]\n}\n\n/**\n * Renders a height-animated collapse. The config.tag becomes the outer\n * wrapper (overflow:hidden + animated height). An inner div measures\n * scrollHeight for the target value.\n */\nconst CollapseRenderer = ({\n config,\n htmlProps,\n show,\n appear,\n timeout,\n transition,\n callbacks,\n children,\n}: CollapseRendererProps): VNode | null => {\n const reducedMotion = useReducedMotion()\n let wrapperRef: { current: HTMLElement | null } = createRef<HTMLElement>()\n const contentRef = createRef<HTMLDivElement>()\n\n const effectiveAppear = appear ?? config.appear ?? false\n const effectiveTimeout = timeout ?? config.timeout ?? 5000\n const effectiveTransition = transition ?? config.transition ?? 'height 300ms ease'\n\n const initialShow = show()\n const needsAppear = effectiveAppear && initialShow\n const stage = signal<TransitionStage>(initialShow ? 'entered' : 'hidden')\n let isInitialMount = true\n let appearTriggered = false\n\n // Intercept ref assignment to trigger appear after all refs are wired\n if (needsAppear) {\n const orig = wrapperRef\n const proxy = { current: null as HTMLElement | null }\n Object.defineProperty(proxy, 'current', {\n get() {\n return orig.current\n },\n set(node: HTMLElement | null) {\n orig.current = node\n if (node && !appearTriggered) {\n appearTriggered = true\n queueMicrotask(() => stage.set('entering'))\n }\n },\n })\n wrapperRef = proxy\n }\n\n // State machine transitions\n watch(\n show,\n (showVal) => {\n if (isInitialMount) {\n isInitialMount = false\n // appear case is handled by ref proxy above\n return\n }\n\n const currentStage = runUntracked(() => stage())\n if (showVal && (currentStage === 'hidden' || currentStage === 'leaving')) {\n stage.set('entering')\n } else if (!showVal && (currentStage === 'entered' || currentStage === 'entering')) {\n stage.set('leaving')\n }\n },\n { immediate: true },\n )\n\n // Animate height\n watch(\n () => stage(),\n (currentStage) => {\n const wrapper = wrapperRef.current\n const content = contentRef.current\n if (!wrapper || !content) return\n\n if (reducedMotion()) {\n if (currentStage === 'entering') {\n callbacks.onEnter?.()\n wrapper.style.height = 'auto'\n wrapper.style.overflow = ''\n callbacks.onAfterEnter?.()\n stage.set('entered')\n } else if (currentStage === 'leaving') {\n callbacks.onLeave?.()\n wrapper.style.height = '0px'\n wrapper.style.overflow = 'hidden'\n callbacks.onAfterLeave?.()\n stage.set('hidden')\n }\n return\n }\n\n if (currentStage === 'entering') {\n callbacks.onEnter?.()\n const height = content.scrollHeight\n wrapper.style.transition = 'none'\n wrapper.style.height = '0px'\n wrapper.style.overflow = 'hidden'\n // Force reflow\n void wrapper.offsetHeight\n wrapper.style.transition = effectiveTransition\n wrapper.style.height = `${height}px`\n }\n\n if (currentStage === 'leaving') {\n callbacks.onLeave?.()\n const height = content.scrollHeight\n wrapper.style.transition = 'none'\n wrapper.style.height = `${height}px`\n wrapper.style.overflow = 'hidden'\n // Force reflow\n void wrapper.offsetHeight\n wrapper.style.transition = effectiveTransition\n wrapper.style.height = '0px'\n }\n },\n { immediate: true },\n )\n\n useAnimationEnd({\n ref: wrapperRef,\n active: () => (stage() === 'entering' || stage() === 'leaving') && !reducedMotion(),\n timeout: effectiveTimeout,\n onEnd: () => {\n const wrapper = wrapperRef.current\n if (stage() === 'entering') {\n if (wrapper) {\n wrapper.style.height = 'auto'\n wrapper.style.overflow = ''\n wrapper.style.transition = ''\n }\n callbacks.onAfterEnter?.()\n stage.set('entered')\n } else if (stage() === 'leaving') {\n callbacks.onAfterLeave?.()\n stage.set('hidden')\n }\n },\n })\n\n const shouldRender = () => stage() !== 'hidden'\n\n const wrapperStyle: CSSProperties = {\n ...((htmlProps.style as CSSProperties) ?? {}),\n ...(stage() !== 'entered' ? { overflow: 'hidden' } : {}),\n ...(stage() === 'hidden' ? { height: '0px' } : stage() === 'entered' ? { height: 'auto' } : {}),\n }\n\n return h(\n config.tag,\n { ref: wrapperRef, ...htmlProps, style: wrapperStyle },\n <Show when={shouldRender}>\n <div ref={contentRef}>{children}</div>\n </Show>,\n )\n}\n\nexport default CollapseRenderer\n","import { createRef } from '@pyreon/core'\nimport { runUntracked, signal, watch } from '@pyreon/reactivity'\nimport type { TransitionStage, TransitionStateResult } from './types'\n\nexport type UseTransitionState = (options: {\n show: () => boolean\n appear?: boolean | undefined\n}) => TransitionStateResult\n\nconst useTransitionState: UseTransitionState = ({ show, appear = false }) => {\n const initialShow = show()\n // When appear=true and show starts true, mount the element (stage='entered')\n // but defer the enter animation until the ref is connected.\n const needsAppear = appear && initialShow\n const stage = signal<TransitionStage>(initialShow ? 'entered' : 'hidden')\n const elementRef = createRef<HTMLElement>()\n let isInitialMount = true\n let appearTriggered = false\n\n // Ref callback that triggers the appear animation once the element is wired\n const refCallback = (node: HTMLElement | null) => {\n elementRef.current = node\n if (node && needsAppear && !appearTriggered) {\n appearTriggered = true\n stage.set('entering')\n }\n }\n\n watch(\n show,\n (showVal) => {\n if (isInitialMount) {\n isInitialMount = false\n // appear case is handled by refCallback above\n return\n }\n\n const currentStage = runUntracked(() => stage())\n if (showVal && (currentStage === 'hidden' || currentStage === 'leaving')) {\n stage.set('entering')\n } else if (!showVal && (currentStage === 'entered' || currentStage === 'entering')) {\n stage.set('leaving')\n }\n },\n { immediate: true },\n )\n\n const complete = () => {\n const current = stage()\n if (current === 'entering') stage.set('entered')\n if (current === 'leaving') stage.set('hidden')\n }\n\n return {\n stage,\n ref: refCallback,\n shouldMount: () => stage() !== 'hidden',\n complete,\n }\n}\n\nexport default useTransitionState\n","import type { Ref, VNode } from '@pyreon/core'\nimport type { CSSProperties } from './types'\n\nconst splitCache = new Map<string, string[]>()\nconst splitClasses = (classes: string): string[] => {\n let cached = splitCache.get(classes)\n if (!cached) {\n cached = classes.split(/\\s+/).filter(Boolean)\n splitCache.set(classes, cached)\n }\n return cached\n}\n\n/** Adds space-separated CSS classes to an element. */\nexport const addClasses = (el: HTMLElement, classes: string | undefined) => {\n if (!classes) return\n const list = splitClasses(classes)\n if (list.length > 0) el.classList.add(...list)\n}\n\n/** Removes space-separated CSS classes from an element. */\nexport const removeClasses = (el: HTMLElement, classes: string | undefined) => {\n if (!classes) return\n const list = splitClasses(classes)\n if (list.length > 0) el.classList.remove(...list)\n}\n\n/**\n * Executes callback after two animation frames (double-rAF).\n * Ensures the browser paints the current state before applying changes,\n * which is required for CSS transitions to trigger. Returns 0 on SSR —\n * the typeof-window guard makes the SSR-safety contract explicit (callers\n * are always browser-only via `onMount`, but the rule can't AST-trace it).\n */\nexport const nextFrame = (callback: () => void): number => {\n if (typeof requestAnimationFrame === 'undefined') return 0\n return requestAnimationFrame(() => {\n requestAnimationFrame(callback)\n })\n}\n\n/** Merges two className strings, filtering undefined/empty. */\nexport const mergeClassNames = (\n existing: string | undefined,\n additional: string | undefined,\n): string | undefined => {\n const parts = [existing, additional].filter(Boolean)\n return parts.length > 0 ? parts.join(' ') : undefined\n}\n\n/** Merges two CSSProperties objects, with `b` taking precedence. */\nexport const mergeStyles = (\n a: CSSProperties | undefined,\n b: CSSProperties | undefined,\n): CSSProperties | undefined => {\n if (!a && !b) return undefined\n if (!a) return b\n if (!b) return a\n return { ...a, ...b }\n}\n\n// ─── Ref & Motion Utilities ─────────────────────────────────\n\ntype RefCallback<T> = (node: T | null) => void\ntype RefLike<T> = RefCallback<T> | Ref<T>\n\n/** Merges multiple refs (callback or object) into a single callback ref. */\nexport const mergeRefs = <T>(...refs: (RefLike<T> | undefined)[]): ((node: T | null) => void) => {\n return (node: T | null) => {\n for (const ref of refs) {\n if (!ref) continue\n if (typeof ref === 'function') {\n ref(node)\n } else {\n ;(ref as { current: unknown }).current = node\n }\n }\n }\n}\n\n/** Clones a VNode with merged props. */\nexport const cloneVNode = (vnode: VNode, extraProps: Record<string, unknown>): VNode => ({\n ...vnode,\n props: { ...vnode.props, ...extraProps },\n})\n","import type { VNode } from '@pyreon/core'\nimport { createRef, Show } from '@pyreon/core'\nimport { watch } from '@pyreon/reactivity'\nimport type { ClassTransitionProps, StyleTransitionProps, TransitionCallbacks } from '../types'\nimport useAnimationEnd from '../useAnimationEnd'\nimport { useReducedMotion } from '../useReducedMotion'\nimport useTransitionState from '../useTransitionState'\nimport { addClasses, cloneVNode, mergeRefs, mergeStyles, nextFrame, removeClasses } from '../utils'\n\ntype TransitionItemProps = ClassTransitionProps &\n StyleTransitionProps &\n TransitionCallbacks & {\n show: () => boolean\n appear?: boolean | undefined\n unmount?: boolean | undefined\n timeout?: number | undefined\n delay?: number | undefined\n children: VNode\n }\n\nconst applyEnter = (el: HTMLElement, config: ClassTransitionProps & StyleTransitionProps) => {\n addClasses(el, config.enter)\n addClasses(el, config.enterFrom)\n if (config.enterStyle) Object.assign(el.style, config.enterStyle)\n if (config.enterTransition) el.style.transition = config.enterTransition\n\n return nextFrame(() => {\n removeClasses(el, config.enterFrom)\n addClasses(el, config.enterTo)\n if (config.enterToStyle) Object.assign(el.style, config.enterToStyle)\n })\n}\n\nconst applyLeave = (el: HTMLElement, config: ClassTransitionProps & StyleTransitionProps) => {\n removeClasses(el, config.enter)\n removeClasses(el, config.enterTo)\n\n addClasses(el, config.leave)\n addClasses(el, config.leaveFrom)\n if (config.leaveStyle) Object.assign(el.style, config.leaveStyle)\n if (config.leaveTransition) el.style.transition = config.leaveTransition\n\n return nextFrame(() => {\n removeClasses(el, config.leaveFrom)\n addClasses(el, config.leaveTo)\n if (config.leaveToStyle) Object.assign(el.style, config.leaveToStyle)\n })\n}\n\nconst applyReducedMotion = (\n stage: string,\n callbacks: Partial<TransitionCallbacks>,\n complete: () => void,\n) => {\n if (stage === 'entering') {\n callbacks.onEnter?.()\n callbacks.onAfterEnter?.()\n complete()\n } else if (stage === 'leaving') {\n callbacks.onLeave?.()\n callbacks.onAfterLeave?.()\n complete()\n }\n}\n\n/**\n * Internal per-child transition component. Used by StaggerRenderer and\n * GroupRenderer to give each child its own animation state.\n *\n * Uses cloneVNode to inject ref onto the child — the child must accept ref.\n */\nconst TransitionItem = (props: TransitionItemProps): VNode | null => {\n const appear = props.appear ?? false\n const unmount = props.unmount ?? true\n const timeout = props.timeout ?? 5000\n const reducedMotion = useReducedMotion()\n const { stage, ref: stateRef, shouldMount, complete } = useTransitionState({\n show: props.show,\n appear,\n })\n\n const elementRef = createRef<HTMLElement>()\n const mergedRef = mergeRefs(\n elementRef,\n stateRef,\n (props.children.props as Record<string, unknown>)?.ref as\n | ((el: HTMLElement | null) => void)\n | undefined,\n )\n\n const callbacks = {\n onEnter: props.onEnter,\n onAfterEnter: props.onAfterEnter,\n onLeave: props.onLeave,\n onAfterLeave: props.onAfterLeave,\n }\n\n const transitionConfig = {\n enter: props.enter,\n enterFrom: props.enterFrom,\n enterTo: props.enterTo,\n leave: props.leave,\n leaveFrom: props.leaveFrom,\n leaveTo: props.leaveTo,\n enterStyle: props.enterStyle,\n enterToStyle: props.enterToStyle,\n enterTransition: props.enterTransition,\n leaveStyle: props.leaveStyle,\n leaveToStyle: props.leaveToStyle,\n leaveTransition: props.leaveTransition,\n }\n\n useAnimationEnd({\n ref: elementRef,\n active: () => (stage() === 'entering' || stage() === 'leaving') && !reducedMotion(),\n timeout,\n onEnd: () => {\n if (stage() === 'entering') {\n callbacks.onAfterEnter?.()\n } else if (stage() === 'leaving') {\n callbacks.onAfterLeave?.()\n }\n complete()\n },\n })\n\n watch(\n () => stage(),\n (currentStage) => {\n const el = elementRef.current\n if (!el) return\n\n if (reducedMotion()) {\n applyReducedMotion(currentStage, callbacks, complete)\n return\n }\n\n if (currentStage === 'entering') {\n callbacks.onEnter?.()\n const frameId = applyEnter(el, transitionConfig)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'leaving') {\n callbacks.onLeave?.()\n const frameId = applyLeave(el, transitionConfig)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'entered') {\n removeClasses(el, props.enter)\n el.style.transition = ''\n }\n },\n { immediate: true },\n )\n\n return (\n <Show\n when={shouldMount}\n fallback={\n unmount\n ? null\n : cloneVNode(props.children, {\n ref: mergedRef,\n style: mergeStyles(\n (props.children.props as Record<string, unknown>)?.style as\n | Record<string, string | number | undefined>\n | undefined,\n { display: 'none' },\n ),\n })\n }\n >\n {cloneVNode(props.children, { ref: mergedRef })}\n </Show>\n )\n}\n\nexport default TransitionItem\n","import type { VNode } from '@pyreon/core'\nimport { h } from '@pyreon/core'\nimport { signal } from '@pyreon/reactivity'\nimport type { TransitionCallbacks } from '../types'\nimport TransitionItem from './TransitionItem'\nimport type { KineticConfig } from './types'\n\ntype GroupRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n appear?: boolean | undefined\n timeout?: number | undefined\n callbacks: Partial<TransitionCallbacks>\n /**\n * Children can be a static array OR a reactive accessor `() => VNode[]`.\n * When passed as an accessor, GroupRenderer tracks changes and\n * animates entering/leaving children automatically.\n */\n children: VNode[] | (() => VNode[])\n}\n\ntype KeyedChild = { key: string | number; element: VNode }\n\nconst isVNode = (child: unknown): child is VNode =>\n child != null && typeof child === 'object' && 'type' in (child as object)\n\nconst getKeyedChildren = (children: VNode[]): KeyedChild[] => {\n const result: KeyedChild[] = []\n for (const child of children) {\n if (isVNode(child)) {\n const key = (child as VNode & { key?: string | number }).key\n if (key != null) {\n result.push({ key, element: child })\n }\n }\n }\n return result\n}\n\n/**\n * Renders children with key-based enter/exit animation (no `show` prop).\n * Children that appear (new key) animate in. Children that disappear\n * (removed key) stay in DOM during leave animation, then unmount.\n * config.tag wraps all children as a container element.\n *\n * In Pyreon, components run once. Pass children as a reactive accessor\n * `() => VNode[]` for the group to detect changes and animate entries/exits.\n */\nconst GroupRenderer = ({\n config,\n htmlProps,\n appear,\n timeout,\n callbacks,\n children,\n}: GroupRendererProps): VNode | null => {\n const effectiveAppear = appear ?? config.appear ?? false\n const effectiveTimeout = timeout ?? config.timeout ?? 5000\n\n const prevMap = new Map<string | number, VNode>()\n const leavingMap = new Map<string | number, VNode>()\n const forceUpdate = signal(0)\n\n // Normalize children to an accessor\n const getChildren = typeof children === 'function' ? (children as () => VNode[]) : () => children\n\n // Track initial keys for appear animation logic\n const initialKeyed = getKeyedChildren(getChildren())\n const initialKeys = new Set(initialKeyed.map((c) => c.key))\n for (const { key, element } of initialKeyed) {\n prevMap.set(key, element)\n }\n\n const handleAfterLeave = (key: string | number) => {\n leavingMap.delete(key)\n callbacks.onAfterLeave?.()\n forceUpdate.update((c) => c + 1)\n }\n\n // Reactive accessor — re-evaluates when children() or forceUpdate changes\n return (() => {\n forceUpdate()\n\n const currentChildren = getChildren()\n const currentKeyed = getKeyedChildren(currentChildren)\n const currentMap = new Map<string | number, VNode>()\n for (const { key, element } of currentKeyed) {\n currentMap.set(key, element)\n }\n\n // Detect leaving children\n for (const [key, child] of prevMap) {\n if (!currentMap.has(key) && !leavingMap.has(key)) {\n leavingMap.set(key, child)\n }\n }\n\n // Cancel leave if child reappears\n for (const key of currentMap.keys()) {\n leavingMap.delete(key)\n }\n\n // Update prev for next diff\n prevMap.clear()\n for (const [key, element] of currentMap) {\n prevMap.set(key, element)\n }\n\n // Merge current + leaving\n const allEntries: KeyedChild[] = [...currentKeyed]\n for (const [key, element] of leavingMap) {\n allEntries.push({ key, element })\n }\n\n const groupedChildren = allEntries.map(({ key, element }) => {\n const isInitial = initialKeys.has(key)\n const isShowing = currentMap.has(key)\n\n return (\n <TransitionItem\n show={() => isShowing}\n appear={isInitial ? effectiveAppear : true}\n timeout={effectiveTimeout}\n enterStyle={config.enterStyle}\n enterToStyle={config.enterToStyle}\n enterTransition={config.enterTransition}\n leaveStyle={config.leaveStyle}\n leaveToStyle={config.leaveToStyle}\n leaveTransition={config.leaveTransition}\n enter={config.enter}\n enterFrom={config.enterFrom}\n enterTo={config.enterTo}\n leave={config.leave}\n leaveFrom={config.leaveFrom}\n leaveTo={config.leaveTo}\n onAfterLeave={() => handleAfterLeave(key)}\n >\n {element}\n </TransitionItem>\n )\n })\n\n return h(config.tag, { ...htmlProps }, ...groupedChildren)\n }) as unknown as VNode\n}\n\nexport default GroupRenderer\n","import type { VNode } from '@pyreon/core'\nimport { h } from '@pyreon/core'\nimport type { CSSProperties, TransitionCallbacks } from '../types'\nimport { cloneVNode } from '../utils'\nimport TransitionItem from './TransitionItem'\nimport type { KineticConfig } from './types'\n\ntype StaggerRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n show: () => boolean\n appear?: boolean | undefined\n timeout?: number | undefined\n interval?: number | undefined\n reverseLeave?: boolean | undefined\n callbacks: Partial<TransitionCallbacks>\n children: VNode[]\n}\n\nconst isVNode = (child: unknown): child is VNode =>\n child != null && typeof child === 'object' && 'type' in (child as object)\n\n/**\n * Renders children with staggered enter/exit animation.\n * config.tag wraps the staggered children as a container element.\n * Each child is individually animated via TransitionItem.\n */\nconst StaggerRenderer = ({\n config,\n htmlProps,\n show,\n appear,\n timeout,\n interval,\n reverseLeave,\n callbacks,\n children,\n}: StaggerRendererProps): VNode | null => {\n const effectiveAppear = appear ?? config.appear ?? false\n const effectiveTimeout = timeout ?? config.timeout ?? 5000\n const effectiveInterval = interval ?? config.interval ?? 50\n const effectiveReverseLeave = reverseLeave ?? config.reverseLeave ?? false\n\n const childArray = (Array.isArray(children) ? children : [children]).filter(isVNode)\n const count = childArray.length\n\n const staggeredChildren = childArray.map((child, index) => {\n const staggerIndex = !show() && effectiveReverseLeave ? count - 1 - index : index\n const delay = staggerIndex * effectiveInterval\n\n return (\n <TransitionItem\n key={(child as VNode & { key?: string | number }).key ?? index}\n show={show}\n appear={effectiveAppear}\n timeout={effectiveTimeout + delay}\n enterStyle={config.enterStyle}\n enterToStyle={config.enterToStyle}\n enterTransition={config.enterTransition}\n leaveStyle={config.leaveStyle}\n leaveToStyle={config.leaveToStyle}\n leaveTransition={config.leaveTransition}\n enter={config.enter}\n enterFrom={config.enterFrom}\n enterTo={config.enterTo}\n leave={config.leave}\n leaveFrom={config.leaveFrom}\n leaveTo={config.leaveTo}\n onAfterLeave={\n index === (effectiveReverseLeave ? 0 : count - 1) ? callbacks.onAfterLeave : undefined\n }\n >\n {cloneVNode(child, {\n style: {\n ...((child.props as Record<string, unknown>)?.style as CSSProperties | undefined),\n '--stagger-index': staggerIndex,\n '--stagger-interval': `${effectiveInterval}ms`,\n transitionDelay: `${delay}ms`,\n } as CSSProperties,\n })}\n </TransitionItem>\n )\n })\n\n return h(config.tag, { ...htmlProps }, ...staggeredChildren)\n}\n\nexport default StaggerRenderer\n","import type { VNode } from '@pyreon/core'\nimport { createRef, h, Show } from '@pyreon/core'\nimport { watch } from '@pyreon/reactivity'\nimport type { CSSProperties, TransitionCallbacks } from '../types'\nimport useAnimationEnd from '../useAnimationEnd'\nimport { useReducedMotion } from '../useReducedMotion'\nimport useTransitionState from '../useTransitionState'\nimport { addClasses, mergeRefs, nextFrame, removeClasses } from '../utils'\nimport type { KineticConfig } from './types'\n\ntype TransitionRendererProps = {\n config: KineticConfig\n htmlProps: Record<string, unknown>\n show: () => boolean\n appear?: boolean | undefined\n unmount?: boolean | undefined\n timeout?: number | undefined\n callbacks: Partial<TransitionCallbacks>\n children: VNode | VNode[]\n}\n\nconst applyEnter = (el: HTMLElement, config: KineticConfig) => {\n addClasses(el, config.enter)\n addClasses(el, config.enterFrom)\n if (config.enterStyle) Object.assign(el.style, config.enterStyle)\n if (config.enterTransition) el.style.transition = config.enterTransition\n\n return nextFrame(() => {\n removeClasses(el, config.enterFrom)\n addClasses(el, config.enterTo)\n if (config.enterToStyle) Object.assign(el.style, config.enterToStyle)\n })\n}\n\nconst applyLeave = (el: HTMLElement, config: KineticConfig) => {\n removeClasses(el, config.enter)\n removeClasses(el, config.enterTo)\n\n addClasses(el, config.leave)\n addClasses(el, config.leaveFrom)\n if (config.leaveStyle) Object.assign(el.style, config.leaveStyle)\n if (config.leaveTransition) el.style.transition = config.leaveTransition\n\n return nextFrame(() => {\n removeClasses(el, config.leaveFrom)\n addClasses(el, config.leaveTo)\n if (config.leaveToStyle) Object.assign(el.style, config.leaveToStyle)\n })\n}\n\nconst applyReducedMotion = (\n stage: string,\n cbs: Partial<TransitionCallbacks>,\n complete: () => void,\n) => {\n if (stage === 'entering') {\n cbs.onEnter?.()\n cbs.onAfterEnter?.()\n complete()\n } else if (stage === 'leaving') {\n cbs.onLeave?.()\n cbs.onAfterLeave?.()\n complete()\n }\n}\n\n/**\n * Renders a single element with CSS transition enter/exit animation.\n * Uses h(config.tag) — no cloneElement needed.\n */\nconst TransitionRenderer = (props: TransitionRendererProps): VNode | null => {\n const reducedMotion = useReducedMotion()\n const {\n stage,\n ref: stateRef,\n shouldMount,\n complete,\n } = useTransitionState({\n show: props.show,\n appear: props.appear ?? props.config.appear ?? false,\n })\n\n const elementRef = createRef<HTMLElement>()\n const mergedRef = mergeRefs(elementRef, stateRef)\n\n const effectiveUnmount = props.unmount ?? props.config.unmount ?? true\n const effectiveTimeout = props.timeout ?? props.config.timeout ?? 5000\n\n useAnimationEnd({\n ref: elementRef,\n active: () => (stage() === 'entering' || stage() === 'leaving') && !reducedMotion(),\n timeout: effectiveTimeout,\n onEnd: () => {\n if (stage() === 'entering') {\n props.callbacks.onAfterEnter?.()\n } else if (stage() === 'leaving') {\n props.callbacks.onAfterLeave?.()\n }\n complete()\n },\n })\n\n watch(\n () => stage(),\n (currentStage) => {\n const el = elementRef.current\n if (!el) return\n\n if (reducedMotion()) {\n applyReducedMotion(currentStage, props.callbacks, complete)\n return\n }\n\n if (currentStage === 'entering') {\n props.callbacks.onEnter?.()\n const frameId = applyEnter(el, props.config)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'leaving') {\n props.callbacks.onLeave?.()\n const frameId = applyLeave(el, props.config)\n return () => cancelAnimationFrame(frameId)\n }\n\n if (currentStage === 'entered') {\n removeClasses(el, props.config.enter)\n el.style.transition = ''\n }\n },\n { immediate: true },\n )\n\n return (\n <Show\n when={shouldMount}\n fallback={\n effectiveUnmount\n ? null\n : h(\n props.config.tag,\n {\n ref: mergedRef,\n ...props.htmlProps,\n style: {\n ...((props.htmlProps.style as CSSProperties) ?? {}),\n display: 'none',\n },\n },\n props.children,\n )\n }\n >\n {h(props.config.tag, { ref: mergedRef, ...props.htmlProps }, props.children)}\n </Show>\n )\n}\n\nexport default TransitionRenderer\n","import type { VNode } from '@pyreon/core'\nimport type { CSSProperties, TransitionCallbacks } from '../types'\nimport CollapseRenderer from './CollapseRenderer'\nimport GroupRenderer from './GroupRenderer'\nimport StaggerRenderer from './StaggerRenderer'\nimport TransitionRenderer from './TransitionRenderer'\nimport type { ClassConfig, KineticComponent, KineticConfig, KineticMode } from './types'\n\n/** Keys that are kinetic-specific and should not be forwarded as HTML attrs. */\nconst KINETIC_KEYS = new Set([\n 'show',\n 'appear',\n 'unmount',\n 'timeout',\n 'transition',\n 'interval',\n 'reverseLeave',\n 'onEnter',\n 'onAfterEnter',\n 'onLeave',\n 'onAfterLeave',\n])\n\n/**\n * Core factory. Creates a component that delegates to the appropriate\n * renderer based on config.mode, then attaches immutable chain methods\n * via Object.assign.\n */\nconst createKineticComponent = <Tag extends string, Mode extends KineticMode = 'transition'>(\n config: KineticConfig,\n): KineticComponent<Tag, Mode> => {\n const Component = (props: Record<string, unknown>): VNode | null => {\n // Separate kinetic-specific props from HTML pass-through props\n const htmlProps: Record<string, unknown> = {}\n const kineticProps: Record<string, unknown> = {}\n\n for (const key in props) {\n if (KINETIC_KEYS.has(key)) {\n kineticProps[key] = props[key]\n } else {\n htmlProps[key] = props[key]\n }\n }\n\n const {\n show,\n appear,\n unmount,\n timeout,\n transition,\n interval,\n reverseLeave,\n onEnter,\n onAfterEnter,\n onLeave,\n onAfterLeave,\n } = kineticProps as {\n show?: () => boolean\n appear?: boolean\n unmount?: boolean\n timeout?: number\n transition?: string\n interval?: number\n reverseLeave?: boolean\n } & Partial<TransitionCallbacks>\n\n const callbacks: Partial<TransitionCallbacks> = {\n onEnter: onEnter ?? config.onEnter,\n onAfterEnter: onAfterEnter ?? config.onAfterEnter,\n onLeave: onLeave ?? config.onLeave,\n onAfterLeave: onAfterLeave ?? config.onAfterLeave,\n }\n\n // Extract children from htmlProps (it's not an HTML attribute)\n const { children, ...restHtml } = htmlProps\n\n if (config.mode === 'collapse') {\n return (\n <CollapseRenderer\n config={config}\n htmlProps={restHtml}\n show={show as () => boolean}\n appear={appear}\n timeout={timeout}\n transition={transition}\n callbacks={callbacks}\n >\n {children as VNode | VNode[]}\n </CollapseRenderer>\n )\n }\n\n if (config.mode === 'stagger') {\n return (\n <StaggerRenderer\n config={config}\n htmlProps={restHtml}\n show={show as () => boolean}\n appear={appear}\n timeout={timeout}\n interval={interval}\n reverseLeave={reverseLeave}\n callbacks={callbacks}\n >\n {children as VNode[]}\n </StaggerRenderer>\n )\n }\n\n if (config.mode === 'group') {\n return (\n <GroupRenderer\n config={config}\n htmlProps={restHtml}\n appear={appear}\n timeout={timeout}\n callbacks={callbacks}\n >\n {children as VNode[]}\n </GroupRenderer>\n )\n }\n\n // Default: transition mode\n return (\n <TransitionRenderer\n config={config}\n htmlProps={restHtml}\n show={show as () => boolean}\n appear={appear}\n unmount={unmount}\n timeout={timeout}\n callbacks={callbacks}\n >\n {children as VNode | VNode[]}\n </TransitionRenderer>\n )\n }\n\n Component.displayName = `kinetic(${config.tag})`\n\n // Immutable chain methods — each returns a new component with merged config.\n return Object.assign(Component, {\n preset: (preset: Record<string, unknown>) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n ...preset,\n } as KineticConfig),\n\n enter: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, enterStyle: styles }),\n\n enterTo: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, enterToStyle: styles }),\n\n enterTransition: (value: string) =>\n createKineticComponent<Tag, Mode>({ ...config, enterTransition: value }),\n\n leave: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, leaveStyle: styles }),\n\n leaveTo: (styles: CSSProperties) =>\n createKineticComponent<Tag, Mode>({ ...config, leaveToStyle: styles }),\n\n leaveTransition: (value: string) =>\n createKineticComponent<Tag, Mode>({ ...config, leaveTransition: value }),\n\n enterClass: ({ active, from, to }: ClassConfig) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n enter: active,\n enterFrom: from,\n enterTo: to,\n }),\n\n leaveClass: ({ active, from, to }: ClassConfig) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n leave: active,\n leaveFrom: from,\n leaveTo: to,\n }),\n\n config: (opts: Record<string, unknown>) =>\n createKineticComponent<Tag, Mode>({\n ...config,\n ...opts,\n } as KineticConfig),\n\n on: (cbs: Partial<TransitionCallbacks>) =>\n createKineticComponent<Tag, Mode>({ ...config, ...cbs }),\n\n collapse: (opts?: { transition?: string }) =>\n createKineticComponent<Tag, 'collapse'>({\n ...config,\n mode: 'collapse',\n ...opts,\n }),\n\n stagger: (opts?: { interval?: number; reverseLeave?: boolean }) =>\n createKineticComponent<Tag, 'stagger'>({\n ...config,\n mode: 'stagger',\n ...opts,\n }),\n\n group: () => createKineticComponent<Tag, 'group'>({ ...config, mode: 'group' }),\n }) as unknown as KineticComponent<Tag, Mode>\n}\n\nexport default createKineticComponent\n","import createKineticComponent from './kinetic/createKineticComponent'\nimport type { KineticComponent } from './kinetic/types'\n\n/**\n * Creates a reusable animated component via immutable chaining.\n *\n * @example\n * ```tsx\n * // Transition (default)\n * const FadeDiv = kinetic('div').preset(fade)\n *\n * // Collapse\n * const Accordion = kinetic('div').collapse()\n *\n * // Stagger\n * const StaggerList = kinetic('ul').preset(slideUp).stagger({ interval: 50 })\n *\n * // Group (key-based enter/exit)\n * const AnimatedList = kinetic('ul').preset(fade).group()\n * ```\n */\nconst kinetic = <Tag extends string>(tag: Tag): KineticComponent<Tag, 'transition'> =>\n createKineticComponent<Tag, 'transition'>({ tag, mode: 'transition' })\n\nexport default kinetic\n","import type { ClassTransitionProps, StyleTransitionProps } from './types'\n\nexport type Preset = StyleTransitionProps & ClassTransitionProps\n\nexport const fade: Preset = {\n enterStyle: { opacity: 0 },\n enterToStyle: { opacity: 1 },\n enterTransition: 'opacity 300ms ease-out',\n leaveStyle: { opacity: 1 },\n leaveToStyle: { opacity: 0 },\n leaveTransition: 'opacity 200ms ease-in',\n}\n\nexport const scaleIn: Preset = {\n enterStyle: { opacity: 0, transform: 'scale(0.95)' },\n enterToStyle: { opacity: 1, transform: 'scale(1)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'scale(1)' },\n leaveToStyle: { opacity: 0, transform: 'scale(0.95)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideUp: Preset = {\n enterStyle: { opacity: 0, transform: 'translateY(16px)' },\n enterToStyle: { opacity: 1, transform: 'translateY(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateY(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateY(16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideDown: Preset = {\n enterStyle: { opacity: 0, transform: 'translateY(-16px)' },\n enterToStyle: { opacity: 1, transform: 'translateY(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateY(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateY(-16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideLeft: Preset = {\n enterStyle: { opacity: 0, transform: 'translateX(16px)' },\n enterToStyle: { opacity: 1, transform: 'translateX(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateX(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateX(16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const slideRight: Preset = {\n enterStyle: { opacity: 0, transform: 'translateX(-16px)' },\n enterToStyle: { opacity: 1, transform: 'translateX(0)' },\n enterTransition: 'opacity 300ms ease-out, transform 300ms ease-out',\n leaveStyle: { opacity: 1, transform: 'translateX(0)' },\n leaveToStyle: { opacity: 0, transform: 'translateX(-16px)' },\n leaveTransition: 'opacity 200ms ease-in, transform 200ms ease-in',\n}\n\nexport const presets = {\n fade,\n scaleIn,\n slideUp,\n slideDown,\n slideLeft,\n slideRight,\n} as const\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pyreon/kinetic",
3
- "version": "0.12.13",
3
+ "version": "0.12.15",
4
4
  "description": "CSS-transition-based animation components for Pyreon",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -35,18 +35,24 @@
35
35
  "build:watch": "bun run vl_rolldown_build-watch",
36
36
  "lint": "oxlint .",
37
37
  "test": "vitest run",
38
+ "test:browser": "vitest run --config ./vitest.browser.config.ts",
38
39
  "test:coverage": "vitest run --coverage",
39
40
  "test:watch": "vitest",
40
41
  "typecheck": "tsc --noEmit"
41
42
  },
42
43
  "devDependencies": {
43
- "@pyreon/typescript": "^0.12.13",
44
+ "@pyreon/core": "^0.12.15",
45
+ "@pyreon/reactivity": "^0.12.15",
46
+ "@pyreon/runtime-dom": "^0.12.15",
47
+ "@pyreon/test-utils": "^0.12.10",
48
+ "@pyreon/typescript": "^0.12.15",
49
+ "@vitest/browser-playwright": "^4.1.4",
44
50
  "@vitus-labs/tools-rolldown": "^1.15.3"
45
51
  },
46
52
  "peerDependencies": {
47
- "@pyreon/core": "^0.12.13",
48
- "@pyreon/reactivity": "^0.12.13",
49
- "@pyreon/runtime-dom": "^0.12.13"
53
+ "@pyreon/core": "^0.12.15",
54
+ "@pyreon/reactivity": "^0.12.15",
55
+ "@pyreon/runtime-dom": "^0.12.15"
50
56
  },
51
57
  "engines": {
52
58
  "node": ">= 22"
@@ -0,0 +1,58 @@
1
+ /** @jsxImportSource @pyreon/core */
2
+ import { describe, expect, it } from 'vitest'
3
+ import { signal } from '@pyreon/reactivity'
4
+ import { flush, mountInBrowser } from '@pyreon/test-utils/browser'
5
+ import { nextFrame, mergeClassNames } from '../utils'
6
+ import Transition from '../Transition'
7
+
8
+ describe('@pyreon/kinetic browser smoke', () => {
9
+ it('Transition mounts a visible child into real DOM', async () => {
10
+ const show = signal(true)
11
+ const { container, unmount } = mountInBrowser(
12
+ <Transition show={show}>
13
+ <div data-id="t">hello</div>
14
+ </Transition>,
15
+ )
16
+ const el = container.querySelector('[data-id="t"]')
17
+ expect(el?.textContent).toBe('hello')
18
+ unmount()
19
+ })
20
+
21
+ it('Transition fires onLeave when show signal goes true → false', async () => {
22
+ let onLeaveCalls = 0
23
+ const show = signal(true)
24
+ const { unmount } = mountInBrowser(
25
+ <Transition show={show} onLeave={() => { onLeaveCalls++ }}>
26
+ <div data-id="t">hi</div>
27
+ </Transition>,
28
+ )
29
+ expect(onLeaveCalls).toBe(0)
30
+ show.set(false)
31
+ await flush()
32
+ await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => r(null))))
33
+ // onLeave fires once the leave transition starts — asserts the
34
+ // signal → stage machine → lifecycle callback path ran end-to-end.
35
+ expect(onLeaveCalls).toBe(1)
36
+ unmount()
37
+ })
38
+
39
+ it('nextFrame schedules a callback via requestAnimationFrame', async () => {
40
+ let fired = false
41
+ nextFrame(() => {
42
+ fired = true
43
+ })
44
+ await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(() => r(null))))
45
+ expect(fired).toBe(true)
46
+ })
47
+
48
+ it('mergeClassNames filters empty + joins', () => {
49
+ expect(mergeClassNames('a', 'b')).toBe('a b')
50
+ expect(mergeClassNames('a', undefined)).toBe('a')
51
+ expect(mergeClassNames(undefined, undefined)).toBe(undefined)
52
+ })
53
+
54
+ it('runs in a real browser — `typeof process` is undefined, `import.meta.env.DEV` is true', () => {
55
+ expect(typeof process).toBe('undefined')
56
+ expect(import.meta.env.DEV).toBe(true)
57
+ })
58
+ })
@@ -69,61 +69,45 @@ const applyReducedMotion = (
69
69
  *
70
70
  * Uses cloneVNode to inject ref onto the child — the child must accept ref.
71
71
  */
72
- const TransitionItem = ({
73
- show,
74
- appear = false,
75
- unmount = true,
76
- timeout = 5000,
77
- enter,
78
- enterFrom,
79
- enterTo,
80
- leave,
81
- leaveFrom,
82
- leaveTo,
83
- enterStyle,
84
- enterToStyle,
85
- enterTransition,
86
- leaveStyle,
87
- leaveToStyle,
88
- leaveTransition,
89
- onEnter,
90
- onAfterEnter,
91
- onLeave,
92
- onAfterLeave,
93
- children,
94
- }: TransitionItemProps): VNode | null => {
72
+ const TransitionItem = (props: TransitionItemProps): VNode | null => {
73
+ const appear = props.appear ?? false
74
+ const unmount = props.unmount ?? true
75
+ const timeout = props.timeout ?? 5000
95
76
  const reducedMotion = useReducedMotion()
96
- const { stage, ref: stateRef, shouldMount, complete } = useTransitionState({ show, appear })
77
+ const { stage, ref: stateRef, shouldMount, complete } = useTransitionState({
78
+ show: props.show,
79
+ appear,
80
+ })
97
81
 
98
82
  const elementRef = createRef<HTMLElement>()
99
83
  const mergedRef = mergeRefs(
100
84
  elementRef,
101
85
  stateRef,
102
- (children.props as Record<string, unknown>)?.ref as
86
+ (props.children.props as Record<string, unknown>)?.ref as
103
87
  | ((el: HTMLElement | null) => void)
104
88
  | undefined,
105
89
  )
106
90
 
107
91
  const callbacks = {
108
- onEnter,
109
- onAfterEnter,
110
- onLeave,
111
- onAfterLeave,
92
+ onEnter: props.onEnter,
93
+ onAfterEnter: props.onAfterEnter,
94
+ onLeave: props.onLeave,
95
+ onAfterLeave: props.onAfterLeave,
112
96
  }
113
97
 
114
98
  const transitionConfig = {
115
- enter,
116
- enterFrom,
117
- enterTo,
118
- leave,
119
- leaveFrom,
120
- leaveTo,
121
- enterStyle,
122
- enterToStyle,
123
- enterTransition,
124
- leaveStyle,
125
- leaveToStyle,
126
- leaveTransition,
99
+ enter: props.enter,
100
+ enterFrom: props.enterFrom,
101
+ enterTo: props.enterTo,
102
+ leave: props.leave,
103
+ leaveFrom: props.leaveFrom,
104
+ leaveTo: props.leaveTo,
105
+ enterStyle: props.enterStyle,
106
+ enterToStyle: props.enterToStyle,
107
+ enterTransition: props.enterTransition,
108
+ leaveStyle: props.leaveStyle,
109
+ leaveToStyle: props.leaveToStyle,
110
+ leaveTransition: props.leaveTransition,
127
111
  }
128
112
 
129
113
  useAnimationEnd({
@@ -164,7 +148,7 @@ const TransitionItem = ({
164
148
  }
165
149
 
166
150
  if (currentStage === 'entered') {
167
- removeClasses(el, enter)
151
+ removeClasses(el, props.enter)
168
152
  el.style.transition = ''
169
153
  }
170
154
  },
@@ -177,10 +161,10 @@ const TransitionItem = ({
177
161
  fallback={
178
162
  unmount
179
163
  ? null
180
- : cloneVNode(children, {
164
+ : cloneVNode(props.children, {
181
165
  ref: mergedRef,
182
166
  style: mergeStyles(
183
- (children.props as Record<string, unknown>)?.style as
167
+ (props.children.props as Record<string, unknown>)?.style as
184
168
  | Record<string, string | number | undefined>
185
169
  | undefined,
186
170
  { display: 'none' },
@@ -188,7 +172,7 @@ const TransitionItem = ({
188
172
  })
189
173
  }
190
174
  >
191
- {cloneVNode(children, { ref: mergedRef })}
175
+ {cloneVNode(props.children, { ref: mergedRef })}
192
176
  </Show>
193
177
  )
194
178
  }
@@ -68,16 +68,7 @@ const applyReducedMotion = (
68
68
  * Renders a single element with CSS transition enter/exit animation.
69
69
  * Uses h(config.tag) — no cloneElement needed.
70
70
  */
71
- const TransitionRenderer = ({
72
- config,
73
- htmlProps,
74
- show,
75
- appear,
76
- unmount,
77
- timeout,
78
- callbacks,
79
- children,
80
- }: TransitionRendererProps): VNode | null => {
71
+ const TransitionRenderer = (props: TransitionRendererProps): VNode | null => {
81
72
  const reducedMotion = useReducedMotion()
82
73
  const {
83
74
  stage,
@@ -85,15 +76,15 @@ const TransitionRenderer = ({
85
76
  shouldMount,
86
77
  complete,
87
78
  } = useTransitionState({
88
- show,
89
- appear: appear ?? config.appear ?? false,
79
+ show: props.show,
80
+ appear: props.appear ?? props.config.appear ?? false,
90
81
  })
91
82
 
92
83
  const elementRef = createRef<HTMLElement>()
93
84
  const mergedRef = mergeRefs(elementRef, stateRef)
94
85
 
95
- const effectiveUnmount = unmount ?? config.unmount ?? true
96
- const effectiveTimeout = timeout ?? config.timeout ?? 5000
86
+ const effectiveUnmount = props.unmount ?? props.config.unmount ?? true
87
+ const effectiveTimeout = props.timeout ?? props.config.timeout ?? 5000
97
88
 
98
89
  useAnimationEnd({
99
90
  ref: elementRef,
@@ -101,9 +92,9 @@ const TransitionRenderer = ({
101
92
  timeout: effectiveTimeout,
102
93
  onEnd: () => {
103
94
  if (stage() === 'entering') {
104
- callbacks.onAfterEnter?.()
95
+ props.callbacks.onAfterEnter?.()
105
96
  } else if (stage() === 'leaving') {
106
- callbacks.onAfterLeave?.()
97
+ props.callbacks.onAfterLeave?.()
107
98
  }
108
99
  complete()
109
100
  },
@@ -116,24 +107,24 @@ const TransitionRenderer = ({
116
107
  if (!el) return
117
108
 
118
109
  if (reducedMotion()) {
119
- applyReducedMotion(currentStage, callbacks, complete)
110
+ applyReducedMotion(currentStage, props.callbacks, complete)
120
111
  return
121
112
  }
122
113
 
123
114
  if (currentStage === 'entering') {
124
- callbacks.onEnter?.()
125
- const frameId = applyEnter(el, config)
115
+ props.callbacks.onEnter?.()
116
+ const frameId = applyEnter(el, props.config)
126
117
  return () => cancelAnimationFrame(frameId)
127
118
  }
128
119
 
129
120
  if (currentStage === 'leaving') {
130
- callbacks.onLeave?.()
131
- const frameId = applyLeave(el, config)
121
+ props.callbacks.onLeave?.()
122
+ const frameId = applyLeave(el, props.config)
132
123
  return () => cancelAnimationFrame(frameId)
133
124
  }
134
125
 
135
126
  if (currentStage === 'entered') {
136
- removeClasses(el, config.enter)
127
+ removeClasses(el, props.config.enter)
137
128
  el.style.transition = ''
138
129
  }
139
130
  },
@@ -147,20 +138,20 @@ const TransitionRenderer = ({
147
138
  effectiveUnmount
148
139
  ? null
149
140
  : h(
150
- config.tag,
141
+ props.config.tag,
151
142
  {
152
143
  ref: mergedRef,
153
- ...htmlProps,
144
+ ...props.htmlProps,
154
145
  style: {
155
- ...((htmlProps.style as CSSProperties) ?? {}),
146
+ ...((props.htmlProps.style as CSSProperties) ?? {}),
156
147
  display: 'none',
157
148
  },
158
149
  },
159
- children,
150
+ props.children,
160
151
  )
161
152
  }
162
153
  >
163
- {h(config.tag, { ref: mergedRef, ...htmlProps }, children)}
154
+ {h(props.config.tag, { ref: mergedRef, ...props.htmlProps }, props.children)}
164
155
  </Show>
165
156
  )
166
157
  }
package/src/utils.ts CHANGED
@@ -28,12 +28,16 @@ export const removeClasses = (el: HTMLElement, classes: string | undefined) => {
28
28
  /**
29
29
  * Executes callback after two animation frames (double-rAF).
30
30
  * Ensures the browser paints the current state before applying changes,
31
- * which is required for CSS transitions to trigger.
31
+ * which is required for CSS transitions to trigger. Returns 0 on SSR —
32
+ * the typeof-window guard makes the SSR-safety contract explicit (callers
33
+ * are always browser-only via `onMount`, but the rule can't AST-trace it).
32
34
  */
33
- export const nextFrame = (callback: () => void): number =>
34
- requestAnimationFrame(() => {
35
+ export const nextFrame = (callback: () => void): number => {
36
+ if (typeof requestAnimationFrame === 'undefined') return 0
37
+ return requestAnimationFrame(() => {
35
38
  requestAnimationFrame(callback)
36
39
  })
40
+ }
37
41
 
38
42
  /** Merges two className strings, filtering undefined/empty. */
39
43
  export const mergeClassNames = (