@dloizides/ui-motion 1.2.1 → 1.4.0
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/CHANGELOG.md +13 -0
- package/README.md +28 -0
- package/dist/index.d.mts +28 -1
- package/dist/index.d.ts +28 -1
- package/dist/index.js +58 -0
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +56 -1
- package/dist/index.mjs.map +1 -1
- package/dist/logo/index.d.mts +44 -0
- package/dist/logo/index.d.ts +44 -0
- package/dist/logo/index.js +123 -0
- package/dist/logo/index.js.map +1 -0
- package/dist/logo/index.mjs +116 -0
- package/dist/logo/index.mjs.map +1 -0
- package/package.json +20 -2
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/motionTokens.ts","../src/motionUtils.ts","../src/constants.ts","../src/hooks/useEnterExit.ts","../src/hooks/useFocusLift.ts","../src/flip/computeFlipDeltas.ts","../src/FadeIn/FadeIn.tsx","../src/Collapse/Collapse.tsx","../src/PressableScale/PressableScale.tsx","../src/Skeleton/Skeleton.tsx","../src/flip/flipPlayback.ts","../src/flip/Reorder.tsx"],"names":["anim","useReducedMotion","useRef","Animated","useEffect","useMemo","Platform","useState","jsx","useCallback","styles","StyleSheet","Easing","View"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * motionTokens — the RN-ready motion scale, DERIVED from `@dloizides/design-tokens`.\n *\n * design-tokens ships framework-agnostic VALUES (durations in ms + easings as a\n * `css` string plus `[x1,y1,x2,y2]` bezier control points). This module turns the\n * bezier tuples into RN `Easing.bezier(...)` functions ONCE, so every primitive in\n * this package animates on the same curves the web/CSS side uses — one source, two\n * runtimes. Nothing here is app-specific; motion is uniform across portals.\n */\nimport { Easing, type EasingFunction } from 'react-native';\n\nimport {\n DEFAULT_MOTION,\n type Motion,\n type MotionDurations,\n type MotionEasings,\n} from '@dloizides/design-tokens';\n\n/** Re-export the raw token group so consumers can read durations/css strings directly. */\nexport { DEFAULT_MOTION };\nexport type { Motion, MotionDurations, MotionEasings };\n\n/** Durations in ms (numbers). `instant` (0) is the reduced-motion collapse target. */\nexport const motionDurations: MotionDurations = DEFAULT_MOTION.durations;\n\n/** The easing token group in its raw (`css` + `bezier`) form. */\nexport const motionEasingTokens: MotionEasings = DEFAULT_MOTION.easings;\n\n/** The named RN easing curves, one per {@link MotionEasings} key. */\nexport type MotionEasingName = keyof MotionEasings;\n\n/**\n * RN `EasingFunction`s built from the design-token bezier tuples. Built once at\n * module load; the same three functions are reused by every primitive.\n */\nexport const motionEasings: Record<MotionEasingName, EasingFunction> = {\n standard: Easing.bezier(...motionEasingTokens.standard.bezier),\n decelerate: Easing.bezier(...motionEasingTokens.decelerate.bezier),\n accelerate: Easing.bezier(...motionEasingTokens.accelerate.bezier),\n};\n","/**\n * motionUtils — the pure decision logic behind the primitives, extracted so it can\n * be unit-tested WITHOUT driving RN `Animated` (which needs a running animation\n * frame loop). Each primitive imports these and only wires the result into an\n * `Animated.Value`; the interesting branching lives here where a plain assertion\n * can pin it.\n */\nimport { Platform } from 'react-native';\n\nimport { motionDurations } from './motionTokens';\n\n/**\n * The animation duration a primitive should actually use. Collapses to\n * `instant` (0) under reduced-motion — the single source of the \"every animation\n * collapses to instant\" rule the whole package promises. `0` means \"jump to the\n * end state on the next frame\", i.e. no visible motion.\n */\nexport function resolveDuration(duration: number, reduced: boolean): number {\n return reduced ? motionDurations.instant : duration;\n}\n\n/**\n * Whether RN `Animated` should use the native driver. Layout props (height) can\n * NEVER be native-driven, so callers animating height must pass `false` regardless\n * — this helper is for opacity/transform animations, which are native-drivable on\n * native and must fall back to the JS driver on web.\n */\nexport function shouldUseNativeDriver(): boolean {\n return Platform.OS !== 'web';\n}\n\n/**\n * The target height for a {@link Collapse}: the measured content height when open,\n * `0` when closed. `undefined` while the content has not been measured yet — the\n * caller leaves height `auto` for that first layout pass so the content can report\n * its natural size.\n */\nexport function collapseTargetHeight(open: boolean, measured: number | null): number | undefined {\n if (measured === null) return undefined;\n return open ? measured : 0;\n}\n\n/**\n * Whether a finished collapse animation leaves the region SETTLED CLOSED — the state in which the\n * content is taken out of the accessibility tree, the tab order and hit-testing.\n *\n * Both halves matter. `!open` alone is true from the animation's FIRST frame, so hiding on it would\n * make the content vanish instead of collapse. `finished` alone is true for a completed EXPAND. And\n * `finished` is false when a reopen interrupted the collapse, where hiding would hide an element\n * that is already on its way back open.\n *\n * This lives here, and not inline in the animation callback, because that callback is unreachable\n * from a jsdom test: `onLayout` needs a `ResizeObserver`, so `Collapse` never measures and never\n * animates there. A test asserting the rule through the DOM would be asserting nothing.\n */\nexport function collapseSettlesClosed(open: boolean, finished: boolean): boolean {\n return finished && !open;\n}\n\n/**\n * The scale a {@link PressableScale} should animate to. `1` (no scale) whenever\n * reduced-motion is on OR the control is not pressed; the pressed scale otherwise.\n */\nexport function resolveScale(pressed: boolean, reduced: boolean, pressedScale: number): number {\n if (reduced) return 1;\n return pressed ? pressedScale : 1;\n}\n\n/**\n * The `[from, to]` translateX range for a {@link Skeleton} shimmer sweep across a\n * block of the given pixel width — the highlight travels from just off the left\n * edge to just off the right edge.\n */\nexport function shimmerRange(width: number): readonly [number, number] {\n return [-width, width];\n}\n","/**\n * Default testID strings for the motion primitives. Consumers can override each\n * via the component's `testID` prop; these are the fallbacks so Playwright/RTL\n * selectors have a stable default.\n */\nexport const MOTION_TEST_IDS = {\n fadeIn: 'fade-in',\n collapse: 'collapse',\n pressableScale: 'pressable-scale',\n skeleton: 'skeleton',\n reorder: 'reorder',\n} as const;\n\n/** Default press-in scale for {@link PressableScale}. */\nexport const DEFAULT_PRESSED_SCALE = 0.96;\n\n/** Default enter translate-Y (px) for {@link FadeIn}. */\nexport const DEFAULT_FADE_TRANSLATE_Y = 8;\n\n/** Default shimmer sweep duration (ms) for {@link Skeleton}. */\nexport const DEFAULT_SKELETON_DURATION_MS = 1200;\n\n/** Default focused scale for {@link useFocusLift}. */\nexport const DEFAULT_FOCUS_SCALE = 1.06;\n\n/** Default focused lift (px, upward) for {@link useFocusLift}. */\nexport const DEFAULT_FOCUS_LIFT_PX = 8;\n\n/** Default per-item stagger (ms) for {@link Reorder}. */\nexport const DEFAULT_FLIP_STAGGER_MS = 18;\n\n/** Default maximum number of items {@link Reorder} animates; the rest snap. */\nexport const DEFAULT_FLIP_CAP = 8;\n","/**\n * useEnterExit — the reusable enter/exit animation promoted from the ToastHost\n * pattern in `@dloizides/ui-feedback` (fade + optional translate/scale via\n * `Animated`, native driver off on web).\n *\n * The problem it solves: an exiting element must stay MOUNTED until its fade-out\n * finishes, otherwise it vanishes instantly. Callers can't easily track that, so\n * the hook owns a `mounted` flag alongside the animated `style` — keep the node in\n * the tree while `mounted` is true, and it will unmount itself once the exit\n * completes.\n *\n * Reduced-motion (via `@dloizides/rn-web-hooks`) collapses the duration to 0, so\n * both enter and exit are instant and `mounted` flips synchronously.\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\nimport { Animated } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { resolveDuration, shouldUseNativeDriver } from '../motionUtils';\n\nexport interface UseEnterExitOptions {\n /** Whether the element should be visible (entered). */\n visible: boolean;\n /** Enter/exit duration in ms. Defaults to the `base` motion duration (200). */\n duration?: number;\n /**\n * Initial translate-Y offset (px) the element enters from / exits to. `0`\n * (default) disables the translate, leaving a pure fade.\n */\n translateY?: number;\n /**\n * Initial scale the element enters from / exits to. `1` (default) disables the\n * scale, leaving a pure fade.\n */\n fromScale?: number;\n}\n\nexport interface UseEnterExitResult {\n /** Animated style to spread onto an `Animated.View`. */\n style: {\n opacity: Animated.Value;\n transform: Animated.WithAnimatedArray<{ translateY: Animated.AnimatedInterpolation<number> } | { scale: Animated.AnimatedInterpolation<number> }>;\n };\n /** True while the element must remain in the tree (entered OR exiting). */\n mounted: boolean;\n}\n\nexport function useEnterExit(options: UseEnterExitOptions): UseEnterExitResult {\n const { visible, duration = motionDurations.base, translateY = 0, fromScale = 1 } = options;\n\n const reduced = useReducedMotion();\n const progress = useRef(new Animated.Value(visible ? 1 : 0)).current;\n const [mounted, setMounted] = useState(visible);\n\n useEffect(() => {\n const effectiveDuration = resolveDuration(duration, reduced);\n\n if (visible) {\n // Enter: mount immediately, then animate in.\n setMounted(true);\n if (effectiveDuration === 0) {\n progress.setValue(1);\n return undefined;\n }\n const anim = Animated.timing(progress, {\n toValue: 1,\n duration: effectiveDuration,\n easing: motionEasings.decelerate,\n useNativeDriver: shouldUseNativeDriver(),\n });\n anim.start();\n return () => anim.stop();\n }\n\n // Exit: animate out, then unmount once finished.\n if (effectiveDuration === 0) {\n progress.setValue(0);\n setMounted(false);\n return undefined;\n }\n const anim = Animated.timing(progress, {\n toValue: 0,\n duration: effectiveDuration,\n easing: motionEasings.accelerate,\n useNativeDriver: shouldUseNativeDriver(),\n });\n anim.start(({ finished }) => {\n if (finished) setMounted(false);\n });\n return () => anim.stop();\n }, [visible, duration, reduced, progress]);\n\n const style = useMemo(() => {\n const transform: Array<\n | { translateY: Animated.AnimatedInterpolation<number> }\n | { scale: Animated.AnimatedInterpolation<number> }\n > = [];\n if (translateY !== 0) {\n transform.push({\n translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [translateY, 0] }),\n });\n }\n if (fromScale !== 1) {\n transform.push({\n scale: progress.interpolate({ inputRange: [0, 1], outputRange: [fromScale, 1] }),\n });\n }\n return { opacity: progress, transform };\n }, [progress, translateY, fromScale]);\n\n return { style, mounted };\n}\n","/**\n * useFocusLift — a focused tile lifts and grows slightly on a spring; losing focus\n * springs it back. Focus changes mid-flight RETARGET the running spring (the new\n * `Animated.spring` inherits the previous spring's velocity), so rapid focus moves\n * across a grid stay continuous instead of restarting from rest.\n *\n * Under reduced motion the tile does not move at all — the caller signals focus\n * with a ring/shadow swap instead. Transform only, so it is native-drivable.\n */\nimport { useEffect, useMemo, useRef } from 'react';\n\nimport { Animated, type ViewStyle } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { DEFAULT_FOCUS_LIFT_PX, DEFAULT_FOCUS_SCALE } from '../constants';\nimport { shouldUseNativeDriver } from '../motionUtils';\n\nexport interface FocusLiftSpring {\n stiffness: number;\n damping: number;\n}\n\nexport interface FocusLiftOptions {\n /** Scale while focused. Default 1.06. */\n scale?: number;\n /** Upward lift (px) while focused. Default 8. */\n lift?: number;\n}\n\nexport interface FocusLiftTarget {\n scale: number;\n translateY: number;\n}\n\nexport interface UseFocusLiftResult {\n /** Animated style to spread onto an `Animated.View`. */\n style: Animated.WithAnimatedObject<ViewStyle>;\n}\n\nconst REST: FocusLiftTarget = { scale: 1, translateY: 0 };\n\n/** The transform a tile should settle at. Pure — the hook only springs toward it. */\nexport function focusLiftTarget(focused: boolean, reduced: boolean, opts: FocusLiftOptions): FocusLiftTarget {\n if (reduced || !focused) return REST;\n return { scale: opts.scale ?? DEFAULT_FOCUS_SCALE, translateY: -(opts.lift ?? DEFAULT_FOCUS_LIFT_PX) };\n}\n\nexport function useFocusLift(\n focused: boolean,\n spring: FocusLiftSpring,\n opts: FocusLiftOptions = {},\n): UseFocusLiftResult {\n const reduced = useReducedMotion();\n const target = focusLiftTarget(focused, reduced, opts);\n const scale = useRef(new Animated.Value(target.scale)).current;\n const translateY = useRef(new Animated.Value(target.translateY)).current;\n const { stiffness, damping } = spring;\n\n useEffect(() => {\n if (reduced) {\n scale.stopAnimation();\n translateY.stopAnimation();\n scale.setValue(target.scale);\n translateY.setValue(target.translateY);\n return;\n }\n // No stop() in a cleanup: stopping clears the running animation and with it the\n // velocity a retargeted spring would inherit.\n const config = { stiffness, damping, mass: 1, useNativeDriver: shouldUseNativeDriver() };\n Animated.spring(scale, { ...config, toValue: target.scale }).start();\n Animated.spring(translateY, { ...config, toValue: target.translateY }).start();\n }, [reduced, target.scale, target.translateY, stiffness, damping, scale, translateY]);\n\n useEffect(\n () => () => {\n scale.stopAnimation();\n translateY.stopAnimation();\n },\n [scale, translateY],\n );\n\n const style = useMemo(() => ({ transform: [{ translateY }, { scale }] }), [scale, translateY]);\n return { style };\n}\n","/**\n * computeFlipDeltas — the FLIP \"invert\" step, kept pure so it can be unit-tested\n * without driving RN `Animated` or a layout pass.\n *\n * Given every item's slot BEFORE and AFTER a reorder, it returns, for each item that\n * moved, the offset it must start from so it visually sits in its old slot — the\n * caller then springs that offset to 0 (\"play\"). Items are staggered in their NEW\n * reading order (row-first: y, then x). New items (no previous slot) and items that\n * did not move are omitted. Past `cap` animated items the rest are omitted too, so\n * they snap straight to their new slot — a long list does not play a long wave.\n */\nexport interface LayoutBox {\n x: number;\n y: number;\n}\n\nexport interface FlipDelta {\n key: string;\n dx: number;\n dy: number;\n delayMs: number;\n}\n\nexport interface FlipOptions {\n /** Delay (ms) added per animated item, in new reading order. */\n staggerMs: number;\n /** Maximum number of items that animate; the rest snap. */\n cap: number;\n}\n\nexport function computeFlipDeltas(\n prev: ReadonlyMap<string, LayoutBox>,\n next: ReadonlyMap<string, LayoutBox>,\n opts: FlipOptions,\n): FlipDelta[] {\n const ordered = [...next.entries()].sort(([, a], [, b]) => a.y - b.y || a.x - b.x);\n const moved: FlipDelta[] = [];\n for (const [key, box] of ordered) {\n if (moved.length >= opts.cap) break;\n const before = prev.get(key);\n if (before === undefined) continue;\n const dx = before.x - box.x;\n const dy = before.y - box.y;\n if (dx === 0 && dy === 0) continue;\n moved.push({ key, dx, dy, delayMs: moved.length * opts.staggerMs });\n }\n return moved;\n}\n","/**\n * FadeIn — fades its children in (with a small upward translate) once, on mount.\n * The lightweight, mount-only sibling of {@link useEnterExit} (which handles the\n * full enter/exit lifecycle). Under reduced-motion it renders its children at rest\n * immediately — no fade, no translate.\n */\nimport React, { useEffect, useMemo, useRef } from 'react';\n\nimport { Animated, type StyleProp, type ViewStyle } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { shouldUseNativeDriver } from '../motionUtils';\nimport { DEFAULT_FADE_TRANSLATE_Y, MOTION_TEST_IDS } from '../constants';\n\nexport interface FadeInProps {\n children: React.ReactNode;\n /** Fade duration in ms. Defaults to the `base` motion duration (200). */\n duration?: number;\n /** Delay before the fade starts, in ms. Default 0. */\n delay?: number;\n /** Enter translate-Y offset (px). `0` disables the translate. Default 8. */\n translateY?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nexport const FadeIn = ({\n children,\n duration = motionDurations.base,\n delay = 0,\n translateY = DEFAULT_FADE_TRANSLATE_Y,\n style,\n testID = MOTION_TEST_IDS.fadeIn,\n}: FadeInProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const progress = useRef(new Animated.Value(reduced ? 1 : 0)).current;\n\n useEffect(() => {\n if (reduced) {\n progress.setValue(1);\n return undefined;\n }\n const anim = Animated.timing(progress, {\n toValue: 1,\n duration,\n delay,\n easing: motionEasings.decelerate,\n useNativeDriver: shouldUseNativeDriver(),\n });\n anim.start();\n return () => anim.stop();\n }, [reduced, duration, delay, progress]);\n\n const animatedStyle = useMemo((): Animated.WithAnimatedObject<ViewStyle> => {\n if (translateY === 0) {\n return { opacity: progress };\n }\n return {\n opacity: progress,\n transform: [{ translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [translateY, 0] }) }],\n };\n }, [progress, translateY]);\n\n return (\n <Animated.View style={[animatedStyle, style]} testID={testID}>\n {children}\n </Animated.View>\n );\n};\n\nexport default FadeIn;\n","/**\n * Collapse — an animated height expand/collapse that WORKS ON WEB.\n *\n * WHY NOT LayoutAnimation: RN's `LayoutAnimation` (what ui-layout's Accordion uses)\n * is a NATIVE-only no-op under react-native-web, so an app that relied on it got no\n * animation on the web build. Collapse instead measures the content's natural height\n * via `onLayout` and animates a real `Animated.Value` height — the same code path on\n * web and native. Height is a layout prop, so the native driver can never be used\n * here (`useNativeDriver: false`).\n *\n * The children stay MOUNTED at all times, so their state survives open/close and a\n * consumer's `aria-controls` always resolves to a real node. MOUNTED IS NOT EXPOSED,\n * though: once the collapse has settled shut the content is hidden from the\n * accessibility tree, the tab order and hit-testing.\n *\n * THAT DISTINCTION IS A BUG FIX, and this comment used to assert the bug (\"screen\n * readers still reach them\"). `overflow: hidden` + `height: 0` CLIPS; it does not hide.\n * The clipped child keeps its own non-empty box, so it stayed focusable by Tab,\n * announced by screen readers, and reported VISIBLE by Playwright — while the owning\n * header said `aria-expanded=\"false\"`. Found via an aml-v2 E2E test that could see a\n * collapsed settings panel.\n *\n * Reduced-motion snaps to the target height with no animation.\n */\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport {\n Animated,\n type LayoutChangeEvent,\n Platform,\n StyleSheet,\n type StyleProp,\n View,\n type ViewStyle,\n} from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { collapseSettlesClosed, collapseTargetHeight, resolveDuration } from '../motionUtils';\nimport { MOTION_TEST_IDS } from '../constants';\n\nconst IS_WEB = Platform.OS === 'web';\n\nconst styles = StyleSheet.create({\n outer: {\n overflow: 'hidden',\n },\n});\n\n/**\n * WEB ONLY, and the whole point of the fix: `visibility: hidden` is what actually makes a collapsed\n * region CLOSED — it takes the content out of the accessibility tree AND the tab order, and both\n * browsers and Playwright then report it hidden. `overflow: hidden` + `height: 0` does none of that.\n *\n * Deliberately NOT `display: none`, which would achieve the same hiding and then zero the `onLayout`\n * measurement this component animates back to — so reopening would animate 0 → 0. `visibility`\n * keeps the layout box, and therefore the measurement, intact.\n */\n// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- `visibility` is a react-native-web style; RN's own ViewStyle does not declare it.\nconst HIDDEN_WHEN_SETTLED_CLOSED = { visibility: 'hidden' } as unknown as ViewStyle;\n\nexport interface CollapseProps {\n /** Whether the content is expanded. */\n open: boolean;\n children: React.ReactNode;\n /** Expand/collapse duration in ms. Defaults to the `base` motion duration (200). */\n duration?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nexport const Collapse = ({\n open,\n children,\n duration = motionDurations.base,\n style,\n testID = MOTION_TEST_IDS.collapse,\n}: CollapseProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const [measured, setMeasured] = useState<number | null>(null);\n // \"Closed AND finished closing\". Distinct from `!open`, which is true from the first frame of the\n // collapse animation — hiding then would make the content vanish instead of collapse.\n const [settledClosed, setSettledClosed] = useState(!open);\n const heightAnim = useRef(new Animated.Value(0)).current;\n // Skip the FIRST animation: on mount we snap to the initial state, then animate\n // only subsequent open/close changes.\n const initialized = useRef(false);\n\n const onContentLayout = useCallback((event: LayoutChangeEvent): void => {\n const nextHeight = event.nativeEvent.layout.height;\n setMeasured((previous) => (previous === nextHeight ? previous : nextHeight));\n }, []);\n\n useEffect(() => {\n // Opening is immediate — the content has to be reachable for the whole expand, not just after it.\n if (open) setSettledClosed(false);\n\n const target = collapseTargetHeight(open, measured);\n if (target === undefined) {\n // Nothing has been measured yet, so there is no height to animate and the close is\n // INSTANT — settled the moment it is asked for. Returning early without settling (as this\n // did first) left a closed region exposed for the whole pre-layout window, which is exactly\n // the defect this state exists to fix: hiding is a property of `open`, not of measurement.\n if (!open) setSettledClosed(true);\n return undefined;\n }\n\n // Two more instant paths: the FIRST effect snaps to the initial state rather than animating\n // it, and reduced motion resolves every duration to 0.\n const effectiveDuration = resolveDuration(duration, reduced);\n if (!initialized.current || effectiveDuration === 0) {\n heightAnim.setValue(target);\n initialized.current = true;\n if (!open) setSettledClosed(true);\n return undefined;\n }\n\n const anim = Animated.timing(heightAnim, {\n toValue: target,\n duration: effectiveDuration,\n easing: motionEasings.standard,\n // Height is a layout prop — the native driver cannot animate it.\n useNativeDriver: false,\n });\n anim.start(({ finished }) => {\n if (collapseSettlesClosed(open, finished)) setSettledClosed(true);\n });\n return () => anim.stop();\n }, [open, measured, duration, reduced, heightAnim]);\n\n // Until measured, leave height `auto` (undefined) so the content reports its\n // natural size; after that, drive the animated height.\n const outerHeight = measured === null ? undefined : heightAnim;\n\n return (\n <Animated.View\n accessibilityElementsHidden={settledClosed}\n aria-hidden={settledClosed ? true : undefined}\n importantForAccessibility={settledClosed ? 'no-hide-descendants' : 'auto'}\n pointerEvents={settledClosed ? 'none' : 'auto'}\n // The hide comes AFTER the caller's `style`: whether a closed region is exposed is a\n // correctness property of Collapse, not something a consumer should override by accident.\n style={[\n styles.outer,\n { height: outerHeight },\n style,\n settledClosed && IS_WEB ? HIDDEN_WHEN_SETTLED_CLOSED : null,\n ]}\n testID={testID}\n >\n <View onLayout={onContentLayout}>{children}</View>\n </Animated.View>\n );\n};\n\nexport default Collapse;\n","/**\n * PressableScale — a `Pressable` that scales down on press-in and springs back on\n * press-out, giving tactile feedback without the flat opacity dip of a bare\n * `activeOpacity`. Built to replace `activeOpacity` usage in `@dloizides/ui-buttons`.\n *\n * It forwards EVERY `Pressable` prop (onPress, accessibility*, disabled, hitSlop,\n * …) so it is a drop-in wrapper; the scale is applied to an inner `Animated.View`\n * so the outer `Pressable` keeps its full press/a11y behaviour. Under reduced-motion\n * the scale is suppressed entirely (the element stays at scale 1).\n */\nimport React, { useCallback, useRef } from 'react';\n\nimport {\n Animated,\n type GestureResponderEvent,\n Pressable,\n type PressableProps,\n type PressableStateCallbackType,\n type StyleProp,\n type ViewStyle,\n} from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { shouldUseNativeDriver } from '../motionUtils';\nimport { DEFAULT_PRESSED_SCALE, MOTION_TEST_IDS } from '../constants';\n\nexport interface PressableScaleProps extends PressableProps {\n /** Scale to shrink to on press-in. Default 0.96. */\n pressedScale?: number;\n /** Style applied to the inner animated wrapper that carries the scale transform. */\n innerStyle?: StyleProp<ViewStyle>;\n}\n\nexport const PressableScale = ({\n pressedScale = DEFAULT_PRESSED_SCALE,\n innerStyle,\n onPressIn,\n onPressOut,\n children,\n testID = MOTION_TEST_IDS.pressableScale,\n ...rest\n}: PressableScaleProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const scale = useRef(new Animated.Value(1)).current;\n\n const animateTo = useCallback(\n (toValue: number): void => {\n if (reduced) {\n scale.setValue(1);\n return;\n }\n Animated.timing(scale, {\n toValue,\n duration: motionDurations.fast,\n easing: motionEasings.standard,\n useNativeDriver: shouldUseNativeDriver(),\n }).start();\n },\n [reduced, scale],\n );\n\n const handlePressIn = useCallback(\n (event: GestureResponderEvent): void => {\n animateTo(pressedScale);\n onPressIn?.(event);\n },\n [animateTo, pressedScale, onPressIn],\n );\n\n const handlePressOut = useCallback(\n (event: GestureResponderEvent): void => {\n animateTo(1);\n onPressOut?.(event);\n },\n [animateTo, onPressOut],\n );\n\n const renderInner = (state: PressableStateCallbackType): React.ReactNode => (\n <Animated.View style={[{ transform: [{ scale }] }, innerStyle]}>\n {typeof children === 'function' ? children(state) : children}\n </Animated.View>\n );\n\n return (\n <Pressable {...rest} testID={testID} onPressIn={handlePressIn} onPressOut={handlePressOut}>\n {renderInner}\n </Pressable>\n );\n};\n\nexport default PressableScale;\n","/**\n * Skeleton — a rounded placeholder block with a highlight band that sweeps across\n * it, upgrading the opacity-pulse skeleton in `@dloizides/ui-feedback` to a moving\n * shimmer (a stronger \"content is loading\" signal).\n *\n * The block measures its own pixel width via `onLayout`, then loops a highlight\n * `Animated.View` from just off the left edge to just off the right edge (transform\n * translateX — native-drivable on native, JS driver on web). Under reduced-motion\n * the sweep is suppressed and a static block is shown, so it never animates against\n * the user's OS preference.\n */\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport {\n Animated,\n Easing,\n type LayoutChangeEvent,\n StyleSheet,\n type StyleProp,\n View,\n type ViewStyle,\n} from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { shouldUseNativeDriver, shimmerRange } from '../motionUtils';\nimport { DEFAULT_SKELETON_DURATION_MS, MOTION_TEST_IDS } from '../constants';\n\nconst DEFAULT_HEIGHT = 16;\nconst DEFAULT_BORDER_RADIUS = 4;\nconst DEFAULT_BG = 'rgba(0, 0, 0, 0.08)';\nconst DEFAULT_HIGHLIGHT = 'rgba(255, 255, 255, 0.35)';\nconst HIGHLIGHT_WIDTH_RATIO = 0.4;\n\nconst styles = StyleSheet.create({\n block: {\n overflow: 'hidden',\n },\n highlight: {\n position: 'absolute',\n top: 0,\n bottom: 0,\n },\n});\n\nexport interface SkeletonProps {\n /** Block width. Number (px) or percentage string. Default '100%'. */\n width?: number | string;\n /** Block height in px. Default 16. */\n height?: number;\n /** Corner radius in px. Default 4. */\n borderRadius?: number;\n /** Shimmer sweep duration in ms. Default 1200. */\n duration?: number;\n /** Base block colour. */\n backgroundColor?: string;\n /** Sweeping highlight colour. */\n highlightColor?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nexport const Skeleton = ({\n width = '100%',\n height = DEFAULT_HEIGHT,\n borderRadius = DEFAULT_BORDER_RADIUS,\n duration = DEFAULT_SKELETON_DURATION_MS,\n backgroundColor = DEFAULT_BG,\n highlightColor = DEFAULT_HIGHLIGHT,\n style,\n testID = MOTION_TEST_IDS.skeleton,\n}: SkeletonProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const shimmer = useRef(new Animated.Value(0)).current;\n const [measuredWidth, setMeasuredWidth] = useState(0);\n\n const onLayout = useCallback((event: LayoutChangeEvent): void => {\n const nextWidth = event.nativeEvent.layout.width;\n setMeasuredWidth((previous) => (previous === nextWidth ? previous : nextWidth));\n }, []);\n\n useEffect(() => {\n if (reduced || measuredWidth === 0) return undefined;\n shimmer.setValue(0);\n const anim = Animated.loop(\n Animated.timing(shimmer, {\n toValue: 1,\n duration,\n easing: Easing.linear,\n useNativeDriver: shouldUseNativeDriver(),\n }),\n );\n anim.start();\n return () => anim.stop();\n }, [reduced, measuredWidth, duration, shimmer]);\n\n const [from, to] = shimmerRange(measuredWidth);\n const translateX = shimmer.interpolate({ inputRange: [0, 1], outputRange: [from, to] });\n const highlightWidth = measuredWidth * HIGHLIGHT_WIDTH_RATIO;\n\n return (\n <View\n accessibilityState={{ busy: true }}\n onLayout={onLayout}\n style={[styles.block, { width: width as ViewStyle['width'], height, borderRadius, backgroundColor }, style]}\n testID={testID}\n >\n {reduced || measuredWidth === 0 ? null : (\n <Animated.View\n style={[styles.highlight, { width: highlightWidth, backgroundColor: highlightColor, transform: [{ translateX }] }]}\n />\n )}\n </View>\n );\n};\n\nexport default Skeleton;\n","/**\n * flipPlayback — the \"play\" half of FLIP, shared by both measurement paths of\n * {@link Reorder} (web: synchronous DOM read; native: `onLayout`).\n */\nimport { Animated } from 'react-native';\n\nimport { shouldUseNativeDriver } from '../motionUtils';\nimport { computeFlipDeltas, type FlipOptions, type LayoutBox } from './computeFlipDeltas';\n\nexport interface FlipPlayOptions extends FlipOptions {\n spring: { stiffness: number; damping: number };\n}\n\nconst ORIGIN = { x: 0, y: 0 };\n\n/**\n * Slot of every registered host node, read from `offsetLeft`/`offsetTop`. Those are\n * layout positions: they ignore transforms, so an item caught mid-flight still\n * reports its slot, not where it is drawn. Nodes without offsets (native) are skipped.\n */\nexport function measureSlots(nodes: ReadonlyMap<string, unknown>): Map<string, LayoutBox> {\n const slots = new Map<string, LayoutBox>();\n for (const [key, node] of nodes) {\n const el = node as { offsetLeft?: unknown; offsetTop?: unknown } | null;\n if (typeof el?.offsetLeft === 'number' && typeof el.offsetTop === 'number') {\n slots.set(key, { x: el.offsetLeft, y: el.offsetTop });\n }\n }\n return slots;\n}\n\n/**\n * Jump every moved item to its inverted offset, then spring it to 0. The invert adds\n * the item's current offset, so an item already in flight continues from where it is\n * drawn. Moved items past `cap` snap to 0 — including any stale in-flight offset.\n * `setValue` runs synchronously (JS driver), so called from a layout effect the invert\n * is applied before the browser paints the new slots.\n */\nexport function playFlip(\n offsets: ReadonlyMap<string, Animated.ValueXY>,\n prev: ReadonlyMap<string, LayoutBox>,\n next: ReadonlyMap<string, LayoutBox>,\n opts: FlipPlayOptions,\n): void {\n const moved = computeFlipDeltas(prev, next, { staggerMs: opts.staggerMs, cap: Number.POSITIVE_INFINITY });\n moved.forEach(({ key, dx, dy, delayMs }, index) => {\n const xy = offsets.get(key);\n if (xy === undefined) return;\n xy.stopAnimation((current) => {\n if (index >= opts.cap) {\n xy.setValue(ORIGIN);\n return;\n }\n xy.setValue({ x: current.x + dx, y: current.y + dy });\n Animated.spring(xy, {\n toValue: ORIGIN,\n stiffness: opts.spring.stiffness,\n damping: opts.spring.damping,\n mass: 1,\n delay: delayMs,\n useNativeDriver: shouldUseNativeDriver(),\n }).start();\n });\n });\n}\n","/**\n * Reorder — a keyed list whose moved items spring from their old slot to the new one\n * when `itemKeys` changes order (FLIP; maths in computeFlipDeltas, play in playFlip).\n * - web: RN-web `onLayout` (ResizeObserver) misses position-only moves, so OLD slots are\n * read from the DOM during the render that changes the order (DOM still holds the last\n * commit) and NEW slots in `useLayoutEffect` — after the DOM mutation, before paint.\n * - native: `onLayout` reports moves; the flip plays one frame after they arrive.\n * Reduced motion: nothing animates (the caller crossfades). Transform only.\n */\nimport React, { useEffect, useLayoutEffect, useRef } from 'react';\n\nimport { Animated, type LayoutChangeEvent, Platform, View } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { DEFAULT_FLIP_CAP, DEFAULT_FLIP_STAGGER_MS, MOTION_TEST_IDS } from '../constants';\nimport type { LayoutBox } from './computeFlipDeltas';\nimport { measureSlots, playFlip } from './flipPlayback';\n\nexport interface ReorderProps {\n /** Stable item keys, in display order. */\n itemKeys: readonly string[];\n /** Renders the content for one key. */\n children: (key: string) => React.ReactNode;\n spring: { stiffness: number; damping: number };\n /** Per-item stagger (ms). Default 18. */\n staggerMs?: number;\n /** Maximum animated items; the rest snap. Default 8. */\n cap?: number;\n /** Container testID; each item gets `${testID}-item-${key}`. */\n testID?: string;\n}\n\nexport function Reorder({\n itemKeys, children, spring, staggerMs = DEFAULT_FLIP_STAGGER_MS, cap = DEFAULT_FLIP_CAP,\n testID = MOTION_TEST_IDS.reorder,\n}: ReorderProps): React.ReactElement {\n const reduced = useReducedMotion();\n const web = Platform.OS === 'web';\n const layouts = useRef(new Map<string, LayoutBox>()).current;\n const nodes = useRef(new Map<string, unknown>()).current;\n const offsets = useRef(new Map<string, Animated.ValueXY>()).current;\n const snapshot = useRef<Map<string, LayoutBox> | null>(null);\n const measuredOrder = useRef<string | null>(null);\n const frame = useRef<number | null>(null);\n const order = JSON.stringify(itemKeys);\n const opts = { staggerMs, cap, spring };\n\n if (web && measuredOrder.current !== order) {\n measuredOrder.current = order;\n snapshot.current = measureSlots(nodes);\n }\n\n const offsetFor = (key: string): Animated.ValueXY => {\n if (!offsets.has(key)) offsets.set(key, new Animated.ValueXY({ x: 0, y: 0 }));\n return offsets.get(key) as Animated.ValueXY;\n };\n\n const takeSnapshot = (): Map<string, LayoutBox> | null => {\n const prev = snapshot.current; snapshot.current = null; return prev;\n };\n\n useLayoutEffect(() => {\n const live = new Set(itemKeys);\n for (const key of [...offsets.keys()]) {\n if (live.has(key)) continue;\n layouts.delete(key);\n offsets.delete(key);\n }\n if (!web) {\n snapshot.current = reduced ? null : new Map(layouts);\n return;\n }\n const prev = takeSnapshot();\n if (prev !== null && !reduced) playFlip(offsets, prev, measureSlots(nodes), opts);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed on the ORDER, not the array identity\n }, [order, reduced]);\n\n useEffect(() => {\n if (!reduced) return;\n for (const xy of offsets.values()) {\n xy.stopAnimation();\n xy.setValue({ x: 0, y: 0 });\n }\n }, [reduced, offsets]);\n\n useEffect(() => () => { if (frame.current !== null) cancelAnimationFrame(frame.current); }, []);\n\n const playNative = (): void => {\n frame.current = null;\n const prev = takeSnapshot();\n if (prev !== null) playFlip(offsets, prev, layouts, opts);\n };\n\n const onItemLayout = (key: string) => (event: LayoutChangeEvent): void => {\n const { x, y } = event.nativeEvent.layout;\n layouts.set(key, { x, y });\n if (snapshot.current !== null && frame.current === null) frame.current = requestAnimationFrame(playNative);\n };\n\n const registerNode = (key: string) => (node: unknown): void => {\n if (node === null) nodes.delete(key); else nodes.set(key, node);\n };\n\n return (\n <View testID={testID}>\n {itemKeys.map((key) => (\n <Animated.View\n key={key}\n ref={registerNode(key)}\n testID={`${testID}-item-${key}`}\n onLayout={web ? undefined : onItemLayout(key)}\n style={{ transform: offsetFor(key).getTranslateTransform() }}\n >\n {children(key)}\n </Animated.View>\n ))}\n </View>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/motionTokens.ts","../src/motionUtils.ts","../src/constants.ts","../src/hooks/useEnterExit.ts","../src/hooks/useFocusLift.ts","../src/useSequence.ts","../src/flip/computeFlipDeltas.ts","../src/FadeIn/FadeIn.tsx","../src/Collapse/Collapse.tsx","../src/PressableScale/PressableScale.tsx","../src/Skeleton/Skeleton.tsx","../src/flip/flipPlayback.ts","../src/flip/Reorder.tsx"],"names":["anim","useReducedMotion","useRef","Animated","useEffect","useMemo","Easing","Platform","useState","useCallback","jsx","styles","StyleSheet","View"],"mappings":"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","file":"index.mjs","sourcesContent":["/**\n * motionTokens — the RN-ready motion scale, DERIVED from `@dloizides/design-tokens`.\n *\n * design-tokens ships framework-agnostic VALUES (durations in ms + easings as a\n * `css` string plus `[x1,y1,x2,y2]` bezier control points). This module turns the\n * bezier tuples into RN `Easing.bezier(...)` functions ONCE, so every primitive in\n * this package animates on the same curves the web/CSS side uses — one source, two\n * runtimes. Nothing here is app-specific; motion is uniform across portals.\n */\nimport { Easing, type EasingFunction } from 'react-native';\n\nimport {\n DEFAULT_MOTION,\n type Motion,\n type MotionDurations,\n type MotionEasings,\n} from '@dloizides/design-tokens';\n\n/** Re-export the raw token group so consumers can read durations/css strings directly. */\nexport { DEFAULT_MOTION };\nexport type { Motion, MotionDurations, MotionEasings };\n\n/** Durations in ms (numbers). `instant` (0) is the reduced-motion collapse target. */\nexport const motionDurations: MotionDurations = DEFAULT_MOTION.durations;\n\n/** The easing token group in its raw (`css` + `bezier`) form. */\nexport const motionEasingTokens: MotionEasings = DEFAULT_MOTION.easings;\n\n/** The named RN easing curves, one per {@link MotionEasings} key. */\nexport type MotionEasingName = keyof MotionEasings;\n\n/**\n * RN `EasingFunction`s built from the design-token bezier tuples. Built once at\n * module load; the same three functions are reused by every primitive.\n */\nexport const motionEasings: Record<MotionEasingName, EasingFunction> = {\n standard: Easing.bezier(...motionEasingTokens.standard.bezier),\n decelerate: Easing.bezier(...motionEasingTokens.decelerate.bezier),\n accelerate: Easing.bezier(...motionEasingTokens.accelerate.bezier),\n};\n","/**\n * motionUtils — the pure decision logic behind the primitives, extracted so it can\n * be unit-tested WITHOUT driving RN `Animated` (which needs a running animation\n * frame loop). Each primitive imports these and only wires the result into an\n * `Animated.Value`; the interesting branching lives here where a plain assertion\n * can pin it.\n */\nimport { Platform } from 'react-native';\n\nimport { motionDurations } from './motionTokens';\n\n/**\n * The animation duration a primitive should actually use. Collapses to\n * `instant` (0) under reduced-motion — the single source of the \"every animation\n * collapses to instant\" rule the whole package promises. `0` means \"jump to the\n * end state on the next frame\", i.e. no visible motion.\n */\nexport function resolveDuration(duration: number, reduced: boolean): number {\n return reduced ? motionDurations.instant : duration;\n}\n\n/**\n * Whether RN `Animated` should use the native driver. Layout props (height) can\n * NEVER be native-driven, so callers animating height must pass `false` regardless\n * — this helper is for opacity/transform animations, which are native-drivable on\n * native and must fall back to the JS driver on web.\n */\nexport function shouldUseNativeDriver(): boolean {\n return Platform.OS !== 'web';\n}\n\n/**\n * The target height for a {@link Collapse}: the measured content height when open,\n * `0` when closed. `undefined` while the content has not been measured yet — the\n * caller leaves height `auto` for that first layout pass so the content can report\n * its natural size.\n */\nexport function collapseTargetHeight(open: boolean, measured: number | null): number | undefined {\n if (measured === null) return undefined;\n return open ? measured : 0;\n}\n\n/**\n * Whether a finished collapse animation leaves the region SETTLED CLOSED — the state in which the\n * content is taken out of the accessibility tree, the tab order and hit-testing.\n *\n * Both halves matter. `!open` alone is true from the animation's FIRST frame, so hiding on it would\n * make the content vanish instead of collapse. `finished` alone is true for a completed EXPAND. And\n * `finished` is false when a reopen interrupted the collapse, where hiding would hide an element\n * that is already on its way back open.\n *\n * This lives here, and not inline in the animation callback, because that callback is unreachable\n * from a jsdom test: `onLayout` needs a `ResizeObserver`, so `Collapse` never measures and never\n * animates there. A test asserting the rule through the DOM would be asserting nothing.\n */\nexport function collapseSettlesClosed(open: boolean, finished: boolean): boolean {\n return finished && !open;\n}\n\n/**\n * The scale a {@link PressableScale} should animate to. `1` (no scale) whenever\n * reduced-motion is on OR the control is not pressed; the pressed scale otherwise.\n */\nexport function resolveScale(pressed: boolean, reduced: boolean, pressedScale: number): number {\n if (reduced) return 1;\n return pressed ? pressedScale : 1;\n}\n\n/**\n * The `[from, to]` translateX range for a {@link Skeleton} shimmer sweep across a\n * block of the given pixel width — the highlight travels from just off the left\n * edge to just off the right edge.\n */\nexport function shimmerRange(width: number): readonly [number, number] {\n return [-width, width];\n}\n","/**\n * Default testID strings for the motion primitives. Consumers can override each\n * via the component's `testID` prop; these are the fallbacks so Playwright/RTL\n * selectors have a stable default.\n */\nexport const MOTION_TEST_IDS = {\n fadeIn: 'fade-in',\n collapse: 'collapse',\n pressableScale: 'pressable-scale',\n skeleton: 'skeleton',\n reorder: 'reorder',\n} as const;\n\n/** Default press-in scale for {@link PressableScale}. */\nexport const DEFAULT_PRESSED_SCALE = 0.96;\n\n/** Default enter translate-Y (px) for {@link FadeIn}. */\nexport const DEFAULT_FADE_TRANSLATE_Y = 8;\n\n/** Default shimmer sweep duration (ms) for {@link Skeleton}. */\nexport const DEFAULT_SKELETON_DURATION_MS = 1200;\n\n/** Default focused scale for {@link useFocusLift}. */\nexport const DEFAULT_FOCUS_SCALE = 1.06;\n\n/** Default focused lift (px, upward) for {@link useFocusLift}. */\nexport const DEFAULT_FOCUS_LIFT_PX = 8;\n\n/** Default per-item stagger (ms) for {@link Reorder}. */\nexport const DEFAULT_FLIP_STAGGER_MS = 18;\n\n/** Default maximum number of items {@link Reorder} animates; the rest snap. */\nexport const DEFAULT_FLIP_CAP = 8;\n\n/** Default share of the total each item of {@link useSequence} spends animating. */\nexport const DEFAULT_SEQUENCE_ITEM_FRACTION = 0.25;\n","/**\n * useEnterExit — the reusable enter/exit animation promoted from the ToastHost\n * pattern in `@dloizides/ui-feedback` (fade + optional translate/scale via\n * `Animated`, native driver off on web).\n *\n * The problem it solves: an exiting element must stay MOUNTED until its fade-out\n * finishes, otherwise it vanishes instantly. Callers can't easily track that, so\n * the hook owns a `mounted` flag alongside the animated `style` — keep the node in\n * the tree while `mounted` is true, and it will unmount itself once the exit\n * completes.\n *\n * Reduced-motion (via `@dloizides/rn-web-hooks`) collapses the duration to 0, so\n * both enter and exit are instant and `mounted` flips synchronously.\n */\nimport { useEffect, useMemo, useRef, useState } from 'react';\n\nimport { Animated } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { resolveDuration, shouldUseNativeDriver } from '../motionUtils';\n\nexport interface UseEnterExitOptions {\n /** Whether the element should be visible (entered). */\n visible: boolean;\n /** Enter/exit duration in ms. Defaults to the `base` motion duration (200). */\n duration?: number;\n /**\n * Initial translate-Y offset (px) the element enters from / exits to. `0`\n * (default) disables the translate, leaving a pure fade.\n */\n translateY?: number;\n /**\n * Initial scale the element enters from / exits to. `1` (default) disables the\n * scale, leaving a pure fade.\n */\n fromScale?: number;\n}\n\nexport interface UseEnterExitResult {\n /** Animated style to spread onto an `Animated.View`. */\n style: {\n opacity: Animated.Value;\n transform: Animated.WithAnimatedArray<{ translateY: Animated.AnimatedInterpolation<number> } | { scale: Animated.AnimatedInterpolation<number> }>;\n };\n /** True while the element must remain in the tree (entered OR exiting). */\n mounted: boolean;\n}\n\nexport function useEnterExit(options: UseEnterExitOptions): UseEnterExitResult {\n const { visible, duration = motionDurations.base, translateY = 0, fromScale = 1 } = options;\n\n const reduced = useReducedMotion();\n const progress = useRef(new Animated.Value(visible ? 1 : 0)).current;\n const [mounted, setMounted] = useState(visible);\n\n useEffect(() => {\n const effectiveDuration = resolveDuration(duration, reduced);\n\n if (visible) {\n // Enter: mount immediately, then animate in.\n setMounted(true);\n if (effectiveDuration === 0) {\n progress.setValue(1);\n return undefined;\n }\n const anim = Animated.timing(progress, {\n toValue: 1,\n duration: effectiveDuration,\n easing: motionEasings.decelerate,\n useNativeDriver: shouldUseNativeDriver(),\n });\n anim.start();\n return () => anim.stop();\n }\n\n // Exit: animate out, then unmount once finished.\n if (effectiveDuration === 0) {\n progress.setValue(0);\n setMounted(false);\n return undefined;\n }\n const anim = Animated.timing(progress, {\n toValue: 0,\n duration: effectiveDuration,\n easing: motionEasings.accelerate,\n useNativeDriver: shouldUseNativeDriver(),\n });\n anim.start(({ finished }) => {\n if (finished) setMounted(false);\n });\n return () => anim.stop();\n }, [visible, duration, reduced, progress]);\n\n const style = useMemo(() => {\n const transform: Array<\n | { translateY: Animated.AnimatedInterpolation<number> }\n | { scale: Animated.AnimatedInterpolation<number> }\n > = [];\n if (translateY !== 0) {\n transform.push({\n translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [translateY, 0] }),\n });\n }\n if (fromScale !== 1) {\n transform.push({\n scale: progress.interpolate({ inputRange: [0, 1], outputRange: [fromScale, 1] }),\n });\n }\n return { opacity: progress, transform };\n }, [progress, translateY, fromScale]);\n\n return { style, mounted };\n}\n","/**\n * useFocusLift — a focused tile lifts and grows slightly on a spring; losing focus\n * springs it back. Focus changes mid-flight RETARGET the running spring (the new\n * `Animated.spring` inherits the previous spring's velocity), so rapid focus moves\n * across a grid stay continuous instead of restarting from rest.\n *\n * Under reduced motion the tile does not move at all — the caller signals focus\n * with a ring/shadow swap instead. Transform only, so it is native-drivable.\n */\nimport { useEffect, useMemo, useRef } from 'react';\n\nimport { Animated, type ViewStyle } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { DEFAULT_FOCUS_LIFT_PX, DEFAULT_FOCUS_SCALE } from '../constants';\nimport { shouldUseNativeDriver } from '../motionUtils';\n\nexport interface FocusLiftSpring {\n stiffness: number;\n damping: number;\n}\n\nexport interface FocusLiftOptions {\n /** Scale while focused. Default 1.06. */\n scale?: number;\n /** Upward lift (px) while focused. Default 8. */\n lift?: number;\n}\n\nexport interface FocusLiftTarget {\n scale: number;\n translateY: number;\n}\n\nexport interface UseFocusLiftResult {\n /** Animated style to spread onto an `Animated.View`. */\n style: Animated.WithAnimatedObject<ViewStyle>;\n}\n\nconst REST: FocusLiftTarget = { scale: 1, translateY: 0 };\n\n/** The transform a tile should settle at. Pure — the hook only springs toward it. */\nexport function focusLiftTarget(focused: boolean, reduced: boolean, opts: FocusLiftOptions): FocusLiftTarget {\n if (reduced || !focused) return REST;\n return { scale: opts.scale ?? DEFAULT_FOCUS_SCALE, translateY: -(opts.lift ?? DEFAULT_FOCUS_LIFT_PX) };\n}\n\nexport function useFocusLift(\n focused: boolean,\n spring: FocusLiftSpring,\n opts: FocusLiftOptions = {},\n): UseFocusLiftResult {\n const reduced = useReducedMotion();\n const target = focusLiftTarget(focused, reduced, opts);\n const scale = useRef(new Animated.Value(target.scale)).current;\n const translateY = useRef(new Animated.Value(target.translateY)).current;\n const { stiffness, damping } = spring;\n\n useEffect(() => {\n if (reduced) {\n scale.stopAnimation();\n translateY.stopAnimation();\n scale.setValue(target.scale);\n translateY.setValue(target.translateY);\n return;\n }\n // No stop() in a cleanup: stopping clears the running animation and with it the\n // velocity a retargeted spring would inherit.\n const config = { stiffness, damping, mass: 1, useNativeDriver: shouldUseNativeDriver() };\n Animated.spring(scale, { ...config, toValue: target.scale }).start();\n Animated.spring(translateY, { ...config, toValue: target.translateY }).start();\n }, [reduced, target.scale, target.translateY, stiffness, damping, scale, translateY]);\n\n useEffect(\n () => () => {\n scale.stopAnimation();\n translateY.stopAnimation();\n },\n [scale, translateY],\n );\n\n const style = useMemo(() => ({ transform: [{ translateY }, { scale }] }), [scale, translateY]);\n return { style };\n}\n","import { useCallback, useEffect, useRef } from 'react';\n\nimport { Animated, Easing } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { DEFAULT_SEQUENCE_ITEM_FRACTION } from './constants';\nimport { motionEasings } from './motionTokens';\nimport { shouldUseNativeDriver } from './motionUtils';\n\nconst START = 0;\nconst FINAL = 1;\nconst MIN_ITEM_MS = 1;\nconst NOT_PLAYED = Symbol('sequence-not-played');\n\nexport interface SequenceItemRange {\n startMs: number;\n endMs: number;\n}\n\nexport interface UseSequenceOptions {\n /** Fixed length of the whole sequence, whatever the list length. */\n totalMs: number;\n /** Time each item spends animating. Default: a quarter of `totalMs`. */\n itemMs?: number;\n /** A new value replays the sequence from the first item. */\n playKey?: unknown;\n /** When false the items start final and wait for a new `playKey`. Default true. */\n playOnMount?: boolean;\n}\n\nexport interface UseSequenceResult {\n /** Whole-sequence progress, 0 to 1 and linear in time. */\n progress: Animated.Value;\n /** The 0-1 animated value of one item. */\n progressAt: (index: number) => Animated.AnimatedInterpolation<number>;\n}\n\n/** When one item of the list animates, so that the last item ends exactly at `totalMs`. */\nexport function sequenceItemRange(index: number, count: number, totalMs: number, itemMs: number): SequenceItemRange {\n if (totalMs <= 0) return { startMs: 0, endMs: 0 };\n if (count <= 1) return { startMs: 0, endMs: totalMs };\n const span = Math.min(Math.max(itemMs, MIN_ITEM_MS), totalMs);\n const position = Math.min(Math.max(index, 0), count - 1);\n const startMs = (position * (totalMs - span)) / (count - 1);\n return { startMs, endMs: startMs + span };\n}\n\n/** Staggers along any ordered list inside a fixed total; reduced motion shows the final state. */\nexport function useSequence(items: readonly unknown[], options: UseSequenceOptions): UseSequenceResult {\n const { totalMs, itemMs = totalMs * DEFAULT_SEQUENCE_ITEM_FRACTION, playKey, playOnMount = true } = options;\n const reduced = useReducedMotion();\n const startsFinal = reduced || !playOnMount;\n const progress = useRef(new Animated.Value(startsFinal ? FINAL : START)).current;\n const settledKey = useRef<unknown>(playOnMount ? NOT_PLAYED : playKey);\n const count = items.length;\n\n useEffect(() => {\n if (reduced || totalMs <= 0 || settledKey.current === playKey) {\n settledKey.current = playKey;\n progress.stopAnimation();\n progress.setValue(FINAL);\n return undefined;\n }\n progress.setValue(START);\n const run = Animated.timing(progress, {\n toValue: FINAL,\n duration: totalMs,\n easing: Easing.linear,\n isInteraction: false,\n useNativeDriver: shouldUseNativeDriver(),\n });\n run.start(({ finished }) => {\n if (finished) settledKey.current = playKey;\n });\n return () => run.stop();\n }, [playKey, progress, reduced, totalMs]);\n\n const progressAt = useCallback(\n (index: number) => {\n const { startMs, endMs } = sequenceItemRange(index, count, totalMs, itemMs);\n const divisor = totalMs > 0 ? totalMs : FINAL;\n return progress.interpolate({\n inputRange: [startMs / divisor, endMs / divisor],\n outputRange: [START, FINAL],\n easing: motionEasings.standard,\n extrapolate: 'clamp',\n });\n },\n [count, itemMs, progress, totalMs],\n );\n\n return { progress, progressAt };\n}\n","/**\n * computeFlipDeltas — the FLIP \"invert\" step, kept pure so it can be unit-tested\n * without driving RN `Animated` or a layout pass.\n *\n * Given every item's slot BEFORE and AFTER a reorder, it returns, for each item that\n * moved, the offset it must start from so it visually sits in its old slot — the\n * caller then springs that offset to 0 (\"play\"). Items are staggered in their NEW\n * reading order (row-first: y, then x). New items (no previous slot) and items that\n * did not move are omitted. Past `cap` animated items the rest are omitted too, so\n * they snap straight to their new slot — a long list does not play a long wave.\n */\nexport interface LayoutBox {\n x: number;\n y: number;\n}\n\nexport interface FlipDelta {\n key: string;\n dx: number;\n dy: number;\n delayMs: number;\n}\n\nexport interface FlipOptions {\n /** Delay (ms) added per animated item, in new reading order. */\n staggerMs: number;\n /** Maximum number of items that animate; the rest snap. */\n cap: number;\n}\n\nexport function computeFlipDeltas(\n prev: ReadonlyMap<string, LayoutBox>,\n next: ReadonlyMap<string, LayoutBox>,\n opts: FlipOptions,\n): FlipDelta[] {\n const ordered = [...next.entries()].sort(([, a], [, b]) => a.y - b.y || a.x - b.x);\n const moved: FlipDelta[] = [];\n for (const [key, box] of ordered) {\n if (moved.length >= opts.cap) break;\n const before = prev.get(key);\n if (before === undefined) continue;\n const dx = before.x - box.x;\n const dy = before.y - box.y;\n if (dx === 0 && dy === 0) continue;\n moved.push({ key, dx, dy, delayMs: moved.length * opts.staggerMs });\n }\n return moved;\n}\n","/**\n * FadeIn — fades its children in (with a small upward translate) once, on mount.\n * The lightweight, mount-only sibling of {@link useEnterExit} (which handles the\n * full enter/exit lifecycle). Under reduced-motion it renders its children at rest\n * immediately — no fade, no translate.\n */\nimport React, { useEffect, useMemo, useRef } from 'react';\n\nimport { Animated, type StyleProp, type ViewStyle } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { shouldUseNativeDriver } from '../motionUtils';\nimport { DEFAULT_FADE_TRANSLATE_Y, MOTION_TEST_IDS } from '../constants';\n\nexport interface FadeInProps {\n children: React.ReactNode;\n /** Fade duration in ms. Defaults to the `base` motion duration (200). */\n duration?: number;\n /** Delay before the fade starts, in ms. Default 0. */\n delay?: number;\n /** Enter translate-Y offset (px). `0` disables the translate. Default 8. */\n translateY?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nexport const FadeIn = ({\n children,\n duration = motionDurations.base,\n delay = 0,\n translateY = DEFAULT_FADE_TRANSLATE_Y,\n style,\n testID = MOTION_TEST_IDS.fadeIn,\n}: FadeInProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const progress = useRef(new Animated.Value(reduced ? 1 : 0)).current;\n\n useEffect(() => {\n if (reduced) {\n progress.setValue(1);\n return undefined;\n }\n const anim = Animated.timing(progress, {\n toValue: 1,\n duration,\n delay,\n easing: motionEasings.decelerate,\n useNativeDriver: shouldUseNativeDriver(),\n });\n anim.start();\n return () => anim.stop();\n }, [reduced, duration, delay, progress]);\n\n const animatedStyle = useMemo((): Animated.WithAnimatedObject<ViewStyle> => {\n if (translateY === 0) {\n return { opacity: progress };\n }\n return {\n opacity: progress,\n transform: [{ translateY: progress.interpolate({ inputRange: [0, 1], outputRange: [translateY, 0] }) }],\n };\n }, [progress, translateY]);\n\n return (\n <Animated.View style={[animatedStyle, style]} testID={testID}>\n {children}\n </Animated.View>\n );\n};\n\nexport default FadeIn;\n","/**\n * Collapse — an animated height expand/collapse that WORKS ON WEB.\n *\n * WHY NOT LayoutAnimation: RN's `LayoutAnimation` (what ui-layout's Accordion uses)\n * is a NATIVE-only no-op under react-native-web, so an app that relied on it got no\n * animation on the web build. Collapse instead measures the content's natural height\n * via `onLayout` and animates a real `Animated.Value` height — the same code path on\n * web and native. Height is a layout prop, so the native driver can never be used\n * here (`useNativeDriver: false`).\n *\n * The children stay MOUNTED at all times, so their state survives open/close and a\n * consumer's `aria-controls` always resolves to a real node. MOUNTED IS NOT EXPOSED,\n * though: once the collapse has settled shut the content is hidden from the\n * accessibility tree, the tab order and hit-testing.\n *\n * THAT DISTINCTION IS A BUG FIX, and this comment used to assert the bug (\"screen\n * readers still reach them\"). `overflow: hidden` + `height: 0` CLIPS; it does not hide.\n * The clipped child keeps its own non-empty box, so it stayed focusable by Tab,\n * announced by screen readers, and reported VISIBLE by Playwright — while the owning\n * header said `aria-expanded=\"false\"`. Found via an aml-v2 E2E test that could see a\n * collapsed settings panel.\n *\n * Reduced-motion snaps to the target height with no animation.\n */\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport {\n Animated,\n type LayoutChangeEvent,\n Platform,\n StyleSheet,\n type StyleProp,\n View,\n type ViewStyle,\n} from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { collapseSettlesClosed, collapseTargetHeight, resolveDuration } from '../motionUtils';\nimport { MOTION_TEST_IDS } from '../constants';\n\nconst IS_WEB = Platform.OS === 'web';\n\nconst styles = StyleSheet.create({\n outer: {\n overflow: 'hidden',\n },\n});\n\n/**\n * WEB ONLY, and the whole point of the fix: `visibility: hidden` is what actually makes a collapsed\n * region CLOSED — it takes the content out of the accessibility tree AND the tab order, and both\n * browsers and Playwright then report it hidden. `overflow: hidden` + `height: 0` does none of that.\n *\n * Deliberately NOT `display: none`, which would achieve the same hiding and then zero the `onLayout`\n * measurement this component animates back to — so reopening would animate 0 → 0. `visibility`\n * keeps the layout box, and therefore the measurement, intact.\n */\n// eslint-disable-next-line @typescript-eslint/consistent-type-assertions -- `visibility` is a react-native-web style; RN's own ViewStyle does not declare it.\nconst HIDDEN_WHEN_SETTLED_CLOSED = { visibility: 'hidden' } as unknown as ViewStyle;\n\nexport interface CollapseProps {\n /** Whether the content is expanded. */\n open: boolean;\n children: React.ReactNode;\n /** Expand/collapse duration in ms. Defaults to the `base` motion duration (200). */\n duration?: number;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nexport const Collapse = ({\n open,\n children,\n duration = motionDurations.base,\n style,\n testID = MOTION_TEST_IDS.collapse,\n}: CollapseProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const [measured, setMeasured] = useState<number | null>(null);\n // \"Closed AND finished closing\". Distinct from `!open`, which is true from the first frame of the\n // collapse animation — hiding then would make the content vanish instead of collapse.\n const [settledClosed, setSettledClosed] = useState(!open);\n const heightAnim = useRef(new Animated.Value(0)).current;\n // Skip the FIRST animation: on mount we snap to the initial state, then animate\n // only subsequent open/close changes.\n const initialized = useRef(false);\n\n const onContentLayout = useCallback((event: LayoutChangeEvent): void => {\n const nextHeight = event.nativeEvent.layout.height;\n setMeasured((previous) => (previous === nextHeight ? previous : nextHeight));\n }, []);\n\n useEffect(() => {\n // Opening is immediate — the content has to be reachable for the whole expand, not just after it.\n if (open) setSettledClosed(false);\n\n const target = collapseTargetHeight(open, measured);\n if (target === undefined) {\n // Nothing has been measured yet, so there is no height to animate and the close is\n // INSTANT — settled the moment it is asked for. Returning early without settling (as this\n // did first) left a closed region exposed for the whole pre-layout window, which is exactly\n // the defect this state exists to fix: hiding is a property of `open`, not of measurement.\n if (!open) setSettledClosed(true);\n return undefined;\n }\n\n // Two more instant paths: the FIRST effect snaps to the initial state rather than animating\n // it, and reduced motion resolves every duration to 0.\n const effectiveDuration = resolveDuration(duration, reduced);\n if (!initialized.current || effectiveDuration === 0) {\n heightAnim.setValue(target);\n initialized.current = true;\n if (!open) setSettledClosed(true);\n return undefined;\n }\n\n const anim = Animated.timing(heightAnim, {\n toValue: target,\n duration: effectiveDuration,\n easing: motionEasings.standard,\n // Height is a layout prop — the native driver cannot animate it.\n useNativeDriver: false,\n });\n anim.start(({ finished }) => {\n if (collapseSettlesClosed(open, finished)) setSettledClosed(true);\n });\n return () => anim.stop();\n }, [open, measured, duration, reduced, heightAnim]);\n\n // Until measured, leave height `auto` (undefined) so the content reports its\n // natural size; after that, drive the animated height.\n const outerHeight = measured === null ? undefined : heightAnim;\n\n return (\n <Animated.View\n accessibilityElementsHidden={settledClosed}\n aria-hidden={settledClosed ? true : undefined}\n importantForAccessibility={settledClosed ? 'no-hide-descendants' : 'auto'}\n pointerEvents={settledClosed ? 'none' : 'auto'}\n // The hide comes AFTER the caller's `style`: whether a closed region is exposed is a\n // correctness property of Collapse, not something a consumer should override by accident.\n style={[\n styles.outer,\n { height: outerHeight },\n style,\n settledClosed && IS_WEB ? HIDDEN_WHEN_SETTLED_CLOSED : null,\n ]}\n testID={testID}\n >\n <View onLayout={onContentLayout}>{children}</View>\n </Animated.View>\n );\n};\n\nexport default Collapse;\n","/**\n * PressableScale — a `Pressable` that scales down on press-in and springs back on\n * press-out, giving tactile feedback without the flat opacity dip of a bare\n * `activeOpacity`. Built to replace `activeOpacity` usage in `@dloizides/ui-buttons`.\n *\n * It forwards EVERY `Pressable` prop (onPress, accessibility*, disabled, hitSlop,\n * …) so it is a drop-in wrapper; the scale is applied to an inner `Animated.View`\n * so the outer `Pressable` keeps its full press/a11y behaviour. Under reduced-motion\n * the scale is suppressed entirely (the element stays at scale 1).\n */\nimport React, { useCallback, useRef } from 'react';\n\nimport {\n Animated,\n type GestureResponderEvent,\n Pressable,\n type PressableProps,\n type PressableStateCallbackType,\n type StyleProp,\n type ViewStyle,\n} from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { motionDurations, motionEasings } from '../motionTokens';\nimport { shouldUseNativeDriver } from '../motionUtils';\nimport { DEFAULT_PRESSED_SCALE, MOTION_TEST_IDS } from '../constants';\n\nexport interface PressableScaleProps extends PressableProps {\n /** Scale to shrink to on press-in. Default 0.96. */\n pressedScale?: number;\n /** Style applied to the inner animated wrapper that carries the scale transform. */\n innerStyle?: StyleProp<ViewStyle>;\n}\n\nexport const PressableScale = ({\n pressedScale = DEFAULT_PRESSED_SCALE,\n innerStyle,\n onPressIn,\n onPressOut,\n children,\n testID = MOTION_TEST_IDS.pressableScale,\n ...rest\n}: PressableScaleProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const scale = useRef(new Animated.Value(1)).current;\n\n const animateTo = useCallback(\n (toValue: number): void => {\n if (reduced) {\n scale.setValue(1);\n return;\n }\n Animated.timing(scale, {\n toValue,\n duration: motionDurations.fast,\n easing: motionEasings.standard,\n useNativeDriver: shouldUseNativeDriver(),\n }).start();\n },\n [reduced, scale],\n );\n\n const handlePressIn = useCallback(\n (event: GestureResponderEvent): void => {\n animateTo(pressedScale);\n onPressIn?.(event);\n },\n [animateTo, pressedScale, onPressIn],\n );\n\n const handlePressOut = useCallback(\n (event: GestureResponderEvent): void => {\n animateTo(1);\n onPressOut?.(event);\n },\n [animateTo, onPressOut],\n );\n\n const renderInner = (state: PressableStateCallbackType): React.ReactNode => (\n <Animated.View style={[{ transform: [{ scale }] }, innerStyle]}>\n {typeof children === 'function' ? children(state) : children}\n </Animated.View>\n );\n\n return (\n <Pressable {...rest} testID={testID} onPressIn={handlePressIn} onPressOut={handlePressOut}>\n {renderInner}\n </Pressable>\n );\n};\n\nexport default PressableScale;\n","/**\n * Skeleton — a rounded placeholder block with a highlight band that sweeps across\n * it, upgrading the opacity-pulse skeleton in `@dloizides/ui-feedback` to a moving\n * shimmer (a stronger \"content is loading\" signal).\n *\n * The block measures its own pixel width via `onLayout`, then loops a highlight\n * `Animated.View` from just off the left edge to just off the right edge (transform\n * translateX — native-drivable on native, JS driver on web). Under reduced-motion\n * the sweep is suppressed and a static block is shown, so it never animates against\n * the user's OS preference.\n */\nimport React, { useCallback, useEffect, useRef, useState } from 'react';\n\nimport {\n Animated,\n Easing,\n type LayoutChangeEvent,\n StyleSheet,\n type StyleProp,\n View,\n type ViewStyle,\n} from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { shouldUseNativeDriver, shimmerRange } from '../motionUtils';\nimport { DEFAULT_SKELETON_DURATION_MS, MOTION_TEST_IDS } from '../constants';\n\nconst DEFAULT_HEIGHT = 16;\nconst DEFAULT_BORDER_RADIUS = 4;\nconst DEFAULT_BG = 'rgba(0, 0, 0, 0.08)';\nconst DEFAULT_HIGHLIGHT = 'rgba(255, 255, 255, 0.35)';\nconst HIGHLIGHT_WIDTH_RATIO = 0.4;\n\nconst styles = StyleSheet.create({\n block: {\n overflow: 'hidden',\n },\n highlight: {\n position: 'absolute',\n top: 0,\n bottom: 0,\n },\n});\n\nexport interface SkeletonProps {\n /** Block width. Number (px) or percentage string. Default '100%'. */\n width?: number | string;\n /** Block height in px. Default 16. */\n height?: number;\n /** Corner radius in px. Default 4. */\n borderRadius?: number;\n /** Shimmer sweep duration in ms. Default 1200. */\n duration?: number;\n /** Base block colour. */\n backgroundColor?: string;\n /** Sweeping highlight colour. */\n highlightColor?: string;\n style?: StyleProp<ViewStyle>;\n testID?: string;\n}\n\nexport const Skeleton = ({\n width = '100%',\n height = DEFAULT_HEIGHT,\n borderRadius = DEFAULT_BORDER_RADIUS,\n duration = DEFAULT_SKELETON_DURATION_MS,\n backgroundColor = DEFAULT_BG,\n highlightColor = DEFAULT_HIGHLIGHT,\n style,\n testID = MOTION_TEST_IDS.skeleton,\n}: SkeletonProps): React.ReactElement => {\n const reduced = useReducedMotion();\n const shimmer = useRef(new Animated.Value(0)).current;\n const [measuredWidth, setMeasuredWidth] = useState(0);\n\n const onLayout = useCallback((event: LayoutChangeEvent): void => {\n const nextWidth = event.nativeEvent.layout.width;\n setMeasuredWidth((previous) => (previous === nextWidth ? previous : nextWidth));\n }, []);\n\n useEffect(() => {\n if (reduced || measuredWidth === 0) return undefined;\n shimmer.setValue(0);\n const anim = Animated.loop(\n Animated.timing(shimmer, {\n toValue: 1,\n duration,\n easing: Easing.linear,\n useNativeDriver: shouldUseNativeDriver(),\n }),\n );\n anim.start();\n return () => anim.stop();\n }, [reduced, measuredWidth, duration, shimmer]);\n\n const [from, to] = shimmerRange(measuredWidth);\n const translateX = shimmer.interpolate({ inputRange: [0, 1], outputRange: [from, to] });\n const highlightWidth = measuredWidth * HIGHLIGHT_WIDTH_RATIO;\n\n return (\n <View\n accessibilityState={{ busy: true }}\n onLayout={onLayout}\n style={[styles.block, { width: width as ViewStyle['width'], height, borderRadius, backgroundColor }, style]}\n testID={testID}\n >\n {reduced || measuredWidth === 0 ? null : (\n <Animated.View\n style={[styles.highlight, { width: highlightWidth, backgroundColor: highlightColor, transform: [{ translateX }] }]}\n />\n )}\n </View>\n );\n};\n\nexport default Skeleton;\n","/**\n * flipPlayback — the \"play\" half of FLIP, shared by both measurement paths of\n * {@link Reorder} (web: synchronous DOM read; native: `onLayout`).\n */\nimport { Animated } from 'react-native';\n\nimport { shouldUseNativeDriver } from '../motionUtils';\nimport { computeFlipDeltas, type FlipOptions, type LayoutBox } from './computeFlipDeltas';\n\nexport interface FlipPlayOptions extends FlipOptions {\n spring: { stiffness: number; damping: number };\n}\n\nconst ORIGIN = { x: 0, y: 0 };\n\n/**\n * Slot of every registered host node, read from `offsetLeft`/`offsetTop`. Those are\n * layout positions: they ignore transforms, so an item caught mid-flight still\n * reports its slot, not where it is drawn. Nodes without offsets (native) are skipped.\n */\nexport function measureSlots(nodes: ReadonlyMap<string, unknown>): Map<string, LayoutBox> {\n const slots = new Map<string, LayoutBox>();\n for (const [key, node] of nodes) {\n const el = node as { offsetLeft?: unknown; offsetTop?: unknown } | null;\n if (typeof el?.offsetLeft === 'number' && typeof el.offsetTop === 'number') {\n slots.set(key, { x: el.offsetLeft, y: el.offsetTop });\n }\n }\n return slots;\n}\n\n/**\n * Jump every moved item to its inverted offset, then spring it to 0. The invert adds\n * the item's current offset, so an item already in flight continues from where it is\n * drawn. Moved items past `cap` snap to 0 — including any stale in-flight offset.\n * `setValue` runs synchronously (JS driver), so called from a layout effect the invert\n * is applied before the browser paints the new slots.\n */\nexport function playFlip(\n offsets: ReadonlyMap<string, Animated.ValueXY>,\n prev: ReadonlyMap<string, LayoutBox>,\n next: ReadonlyMap<string, LayoutBox>,\n opts: FlipPlayOptions,\n): void {\n const moved = computeFlipDeltas(prev, next, { staggerMs: opts.staggerMs, cap: Number.POSITIVE_INFINITY });\n moved.forEach(({ key, dx, dy, delayMs }, index) => {\n const xy = offsets.get(key);\n if (xy === undefined) return;\n xy.stopAnimation((current) => {\n if (index >= opts.cap) {\n xy.setValue(ORIGIN);\n return;\n }\n xy.setValue({ x: current.x + dx, y: current.y + dy });\n Animated.spring(xy, {\n toValue: ORIGIN,\n stiffness: opts.spring.stiffness,\n damping: opts.spring.damping,\n mass: 1,\n delay: delayMs,\n useNativeDriver: shouldUseNativeDriver(),\n }).start();\n });\n });\n}\n","/**\n * Reorder — a keyed list whose moved items spring from their old slot to the new one\n * when `itemKeys` changes order (FLIP; maths in computeFlipDeltas, play in playFlip).\n * - web: RN-web `onLayout` (ResizeObserver) misses position-only moves, so OLD slots are\n * read from the DOM during the render that changes the order (DOM still holds the last\n * commit) and NEW slots in `useLayoutEffect` — after the DOM mutation, before paint.\n * - native: `onLayout` reports moves; the flip plays one frame after they arrive.\n * Reduced motion: nothing animates (the caller crossfades). Transform only.\n */\nimport React, { useEffect, useLayoutEffect, useRef } from 'react';\n\nimport { Animated, type LayoutChangeEvent, Platform, View } from 'react-native';\n\nimport { useReducedMotion } from '@dloizides/rn-web-hooks';\n\nimport { DEFAULT_FLIP_CAP, DEFAULT_FLIP_STAGGER_MS, MOTION_TEST_IDS } from '../constants';\nimport type { LayoutBox } from './computeFlipDeltas';\nimport { measureSlots, playFlip } from './flipPlayback';\n\nexport interface ReorderProps {\n /** Stable item keys, in display order. */\n itemKeys: readonly string[];\n /** Renders the content for one key. */\n children: (key: string) => React.ReactNode;\n spring: { stiffness: number; damping: number };\n /** Per-item stagger (ms). Default 18. */\n staggerMs?: number;\n /** Maximum animated items; the rest snap. Default 8. */\n cap?: number;\n /** Container testID; each item gets `${testID}-item-${key}`. */\n testID?: string;\n}\n\nexport function Reorder({\n itemKeys, children, spring, staggerMs = DEFAULT_FLIP_STAGGER_MS, cap = DEFAULT_FLIP_CAP,\n testID = MOTION_TEST_IDS.reorder,\n}: ReorderProps): React.ReactElement {\n const reduced = useReducedMotion();\n const web = Platform.OS === 'web';\n const layouts = useRef(new Map<string, LayoutBox>()).current;\n const nodes = useRef(new Map<string, unknown>()).current;\n const offsets = useRef(new Map<string, Animated.ValueXY>()).current;\n const snapshot = useRef<Map<string, LayoutBox> | null>(null);\n const measuredOrder = useRef<string | null>(null);\n const frame = useRef<number | null>(null);\n const order = JSON.stringify(itemKeys);\n const opts = { staggerMs, cap, spring };\n\n if (web && measuredOrder.current !== order) {\n measuredOrder.current = order;\n snapshot.current = measureSlots(nodes);\n }\n\n const offsetFor = (key: string): Animated.ValueXY => {\n if (!offsets.has(key)) offsets.set(key, new Animated.ValueXY({ x: 0, y: 0 }));\n return offsets.get(key) as Animated.ValueXY;\n };\n\n const takeSnapshot = (): Map<string, LayoutBox> | null => {\n const prev = snapshot.current; snapshot.current = null; return prev;\n };\n\n useLayoutEffect(() => {\n const live = new Set(itemKeys);\n for (const key of [...offsets.keys()]) {\n if (live.has(key)) continue;\n layouts.delete(key);\n offsets.delete(key);\n }\n if (!web) {\n snapshot.current = reduced ? null : new Map(layouts);\n return;\n }\n const prev = takeSnapshot();\n if (prev !== null && !reduced) playFlip(offsets, prev, measureSlots(nodes), opts);\n // eslint-disable-next-line react-hooks/exhaustive-deps -- keyed on the ORDER, not the array identity\n }, [order, reduced]);\n\n useEffect(() => {\n if (!reduced) return;\n for (const xy of offsets.values()) {\n xy.stopAnimation();\n xy.setValue({ x: 0, y: 0 });\n }\n }, [reduced, offsets]);\n\n useEffect(() => () => { if (frame.current !== null) cancelAnimationFrame(frame.current); }, []);\n\n const playNative = (): void => {\n frame.current = null;\n const prev = takeSnapshot();\n if (prev !== null) playFlip(offsets, prev, layouts, opts);\n };\n\n const onItemLayout = (key: string) => (event: LayoutChangeEvent): void => {\n const { x, y } = event.nativeEvent.layout;\n layouts.set(key, { x, y });\n if (snapshot.current !== null && frame.current === null) frame.current = requestAnimationFrame(playNative);\n };\n\n const registerNode = (key: string) => (node: unknown): void => {\n if (node === null) nodes.delete(key); else nodes.set(key, node);\n };\n\n return (\n <View testID={testID}>\n {itemKeys.map((key) => (\n <Animated.View\n key={key}\n ref={registerNode(key)}\n testID={`${testID}-item-${key}`}\n onLayout={web ? undefined : onItemLayout(key)}\n style={{ transform: offsetFor(key).getTranslateTransform() }}\n >\n {children(key)}\n </Animated.View>\n ))}\n </View>\n );\n}\n"]}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { MotionDurations, MotionEasings } from '@dloizides/design-tokens';
|
|
2
|
+
|
|
3
|
+
type LogoDurationToken = keyof MotionDurations;
|
|
4
|
+
type LogoEasingToken = keyof MotionEasings;
|
|
5
|
+
/** One beat: animate every `[data-motion-layer="<layer>"]` node through `keyframes`. */
|
|
6
|
+
interface LogoBeat {
|
|
7
|
+
layer: string;
|
|
8
|
+
keyframes: Keyframe[];
|
|
9
|
+
duration: LogoDurationToken | number;
|
|
10
|
+
easing?: LogoEasingToken;
|
|
11
|
+
delay?: number;
|
|
12
|
+
}
|
|
13
|
+
type LogoTimeline = readonly LogoBeat[];
|
|
14
|
+
interface PlayLogoOptions {
|
|
15
|
+
reducedMotion?: boolean;
|
|
16
|
+
iterations?: number;
|
|
17
|
+
}
|
|
18
|
+
interface LogoPlayback {
|
|
19
|
+
finished: Promise<void>;
|
|
20
|
+
cancel: () => void;
|
|
21
|
+
}
|
|
22
|
+
type BindMicroOptions = Pick<PlayLogoOptions, 'reducedMotion'>;
|
|
23
|
+
|
|
24
|
+
/** Plays a logo timeline on the mark's `data-motion-layer` nodes via the Web Animations API. */
|
|
25
|
+
declare function playLogo(svg: Element, timeline: LogoTimeline, options?: PlayLogoOptions): LogoPlayback;
|
|
26
|
+
|
|
27
|
+
/** What starts a logo micro-animation bound with `bindMicro`. */
|
|
28
|
+
declare const enum MicroTrigger {
|
|
29
|
+
Hover = "hover",
|
|
30
|
+
Focus = "focus",
|
|
31
|
+
Loading = "loading"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Binds a micro-animation to hover, focus or loading; returns the unbind function. */
|
|
35
|
+
declare function bindMicro(svg: Element, timeline: LogoTimeline, trigger: MicroTrigger | `${MicroTrigger}`, options?: BindMicroOptions): () => void;
|
|
36
|
+
|
|
37
|
+
/** A duration token (`fast`, `base`, `slow`, `instant`) to ms; a number passes through. */
|
|
38
|
+
declare function resolveDuration(duration: LogoDurationToken | number): number;
|
|
39
|
+
/** An easing token to its css timing function; no token means `standard`. */
|
|
40
|
+
declare function resolveEasing(easing: LogoEasingToken | undefined): string;
|
|
41
|
+
|
|
42
|
+
declare const MOTION_LAYER_ATTRIBUTE = "data-motion-layer";
|
|
43
|
+
|
|
44
|
+
export { type BindMicroOptions, type LogoBeat, type LogoDurationToken, type LogoEasingToken, type LogoPlayback, type LogoTimeline, MOTION_LAYER_ATTRIBUTE, MicroTrigger, type PlayLogoOptions, bindMicro, playLogo, resolveDuration, resolveEasing };
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { MotionDurations, MotionEasings } from '@dloizides/design-tokens';
|
|
2
|
+
|
|
3
|
+
type LogoDurationToken = keyof MotionDurations;
|
|
4
|
+
type LogoEasingToken = keyof MotionEasings;
|
|
5
|
+
/** One beat: animate every `[data-motion-layer="<layer>"]` node through `keyframes`. */
|
|
6
|
+
interface LogoBeat {
|
|
7
|
+
layer: string;
|
|
8
|
+
keyframes: Keyframe[];
|
|
9
|
+
duration: LogoDurationToken | number;
|
|
10
|
+
easing?: LogoEasingToken;
|
|
11
|
+
delay?: number;
|
|
12
|
+
}
|
|
13
|
+
type LogoTimeline = readonly LogoBeat[];
|
|
14
|
+
interface PlayLogoOptions {
|
|
15
|
+
reducedMotion?: boolean;
|
|
16
|
+
iterations?: number;
|
|
17
|
+
}
|
|
18
|
+
interface LogoPlayback {
|
|
19
|
+
finished: Promise<void>;
|
|
20
|
+
cancel: () => void;
|
|
21
|
+
}
|
|
22
|
+
type BindMicroOptions = Pick<PlayLogoOptions, 'reducedMotion'>;
|
|
23
|
+
|
|
24
|
+
/** Plays a logo timeline on the mark's `data-motion-layer` nodes via the Web Animations API. */
|
|
25
|
+
declare function playLogo(svg: Element, timeline: LogoTimeline, options?: PlayLogoOptions): LogoPlayback;
|
|
26
|
+
|
|
27
|
+
/** What starts a logo micro-animation bound with `bindMicro`. */
|
|
28
|
+
declare const enum MicroTrigger {
|
|
29
|
+
Hover = "hover",
|
|
30
|
+
Focus = "focus",
|
|
31
|
+
Loading = "loading"
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** Binds a micro-animation to hover, focus or loading; returns the unbind function. */
|
|
35
|
+
declare function bindMicro(svg: Element, timeline: LogoTimeline, trigger: MicroTrigger | `${MicroTrigger}`, options?: BindMicroOptions): () => void;
|
|
36
|
+
|
|
37
|
+
/** A duration token (`fast`, `base`, `slow`, `instant`) to ms; a number passes through. */
|
|
38
|
+
declare function resolveDuration(duration: LogoDurationToken | number): number;
|
|
39
|
+
/** An easing token to its css timing function; no token means `standard`. */
|
|
40
|
+
declare function resolveEasing(easing: LogoEasingToken | undefined): string;
|
|
41
|
+
|
|
42
|
+
declare const MOTION_LAYER_ATTRIBUTE = "data-motion-layer";
|
|
43
|
+
|
|
44
|
+
export { type BindMicroOptions, type LogoBeat, type LogoDurationToken, type LogoEasingToken, type LogoPlayback, type LogoTimeline, MOTION_LAYER_ATTRIBUTE, MicroTrigger, type PlayLogoOptions, bindMicro, playLogo, resolveDuration, resolveEasing };
|
|
@@ -0,0 +1,123 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var designTokens = require('@dloizides/design-tokens');
|
|
4
|
+
|
|
5
|
+
// src/logo/constants.ts
|
|
6
|
+
var MOTION_LAYER_ATTRIBUTE = "data-motion-layer";
|
|
7
|
+
var REDUCED_MOTION_QUERY = "(prefers-reduced-motion: reduce)";
|
|
8
|
+
var DEFAULT_LOGO_EASING = "standard";
|
|
9
|
+
|
|
10
|
+
// src/logo/utils/layers.ts
|
|
11
|
+
var FRAME_META_KEYS = /* @__PURE__ */ new Set(["offset", "easing", "composite"]);
|
|
12
|
+
function findLayers(root, layer) {
|
|
13
|
+
const selector = `[${MOTION_LAYER_ATTRIBUTE}=${JSON.stringify(layer)}]`;
|
|
14
|
+
const nodes = Array.from(root.querySelectorAll(selector));
|
|
15
|
+
if (root.matches(selector)) nodes.unshift(root);
|
|
16
|
+
return nodes.filter((node) => "style" in node);
|
|
17
|
+
}
|
|
18
|
+
function applyFinalFrame(element, keyframes) {
|
|
19
|
+
const last = keyframes[keyframes.length - 1];
|
|
20
|
+
if (last === void 0) return;
|
|
21
|
+
const style = element.style;
|
|
22
|
+
Object.entries(last).filter(([key, value]) => !FRAME_META_KEYS.has(key) && value !== null && value !== void 0).forEach(([key, value]) => {
|
|
23
|
+
style[key] = String(value);
|
|
24
|
+
});
|
|
25
|
+
}
|
|
26
|
+
function supportsWebAnimations(element) {
|
|
27
|
+
return typeof element.animate === "function";
|
|
28
|
+
}
|
|
29
|
+
function prefersReducedMotion() {
|
|
30
|
+
if (typeof window === "undefined" || typeof window.matchMedia !== "function") return false;
|
|
31
|
+
return window.matchMedia(REDUCED_MOTION_QUERY).matches;
|
|
32
|
+
}
|
|
33
|
+
function resolveDuration(duration) {
|
|
34
|
+
if (typeof duration === "number") return duration;
|
|
35
|
+
const ms = designTokens.DEFAULT_MOTION.durations[duration];
|
|
36
|
+
if (ms === void 0) throw new Error(`Unknown motion duration token: ${String(duration)}`);
|
|
37
|
+
return ms;
|
|
38
|
+
}
|
|
39
|
+
function resolveEasing(easing) {
|
|
40
|
+
const name = easing ?? DEFAULT_LOGO_EASING;
|
|
41
|
+
const token = designTokens.DEFAULT_MOTION.easings[name];
|
|
42
|
+
if (token === void 0) throw new Error(`Unknown motion easing token: ${String(name)}`);
|
|
43
|
+
return token.css;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// src/logo/utils/playLogo.ts
|
|
47
|
+
var ignoreCancel = () => void 0;
|
|
48
|
+
function playLogo(svg, timeline, options = {}) {
|
|
49
|
+
const still = options.reducedMotion ?? prefersReducedMotion();
|
|
50
|
+
const animations = [];
|
|
51
|
+
timeline.forEach((beat) => {
|
|
52
|
+
findLayers(svg, beat.layer).forEach((element) => {
|
|
53
|
+
if (still || !supportsWebAnimations(element)) {
|
|
54
|
+
applyFinalFrame(element, beat.keyframes);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
animations.push(
|
|
58
|
+
element.animate(beat.keyframes, {
|
|
59
|
+
duration: resolveDuration(beat.duration),
|
|
60
|
+
easing: resolveEasing(beat.easing),
|
|
61
|
+
delay: beat.delay ?? 0,
|
|
62
|
+
fill: "both",
|
|
63
|
+
iterations: options.iterations ?? 1
|
|
64
|
+
})
|
|
65
|
+
);
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
const finished = Promise.all(animations.map((animation) => animation.finished.catch(ignoreCancel))).then(ignoreCancel);
|
|
69
|
+
return { finished, cancel: () => animations.forEach((animation) => animation.cancel()) };
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// src/logo/MicroTrigger.ts
|
|
73
|
+
var MicroTrigger = /* @__PURE__ */ ((MicroTrigger2) => {
|
|
74
|
+
MicroTrigger2["Hover"] = "hover";
|
|
75
|
+
MicroTrigger2["Focus"] = "focus";
|
|
76
|
+
MicroTrigger2["Loading"] = "loading";
|
|
77
|
+
return MicroTrigger2;
|
|
78
|
+
})(MicroTrigger || {});
|
|
79
|
+
|
|
80
|
+
// src/logo/utils/bindMicro.ts
|
|
81
|
+
var START_EVENT = {
|
|
82
|
+
["hover" /* Hover */]: "pointerenter",
|
|
83
|
+
["focus" /* Focus */]: "focusin"
|
|
84
|
+
};
|
|
85
|
+
var STOP_EVENT = {
|
|
86
|
+
["hover" /* Hover */]: "pointerleave",
|
|
87
|
+
["focus" /* Focus */]: "focusout"
|
|
88
|
+
};
|
|
89
|
+
var noop = () => void 0;
|
|
90
|
+
function bindMicro(svg, timeline, trigger, options = {}) {
|
|
91
|
+
if (options.reducedMotion ?? prefersReducedMotion()) return noop;
|
|
92
|
+
let playback;
|
|
93
|
+
const stop = () => {
|
|
94
|
+
playback?.cancel();
|
|
95
|
+
playback = void 0;
|
|
96
|
+
};
|
|
97
|
+
if (trigger === "loading" /* Loading */) {
|
|
98
|
+
playback = playLogo(svg, timeline, { reducedMotion: false, iterations: Infinity });
|
|
99
|
+
return stop;
|
|
100
|
+
}
|
|
101
|
+
const start = () => {
|
|
102
|
+
stop();
|
|
103
|
+
playback = playLogo(svg, timeline, { reducedMotion: false });
|
|
104
|
+
};
|
|
105
|
+
const startEvent = START_EVENT[trigger] ?? "";
|
|
106
|
+
const stopEvent = STOP_EVENT[trigger] ?? "";
|
|
107
|
+
svg.addEventListener(startEvent, start);
|
|
108
|
+
svg.addEventListener(stopEvent, stop);
|
|
109
|
+
return () => {
|
|
110
|
+
svg.removeEventListener(startEvent, start);
|
|
111
|
+
svg.removeEventListener(stopEvent, stop);
|
|
112
|
+
stop();
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
exports.MOTION_LAYER_ATTRIBUTE = MOTION_LAYER_ATTRIBUTE;
|
|
117
|
+
exports.MicroTrigger = MicroTrigger;
|
|
118
|
+
exports.bindMicro = bindMicro;
|
|
119
|
+
exports.playLogo = playLogo;
|
|
120
|
+
exports.resolveDuration = resolveDuration;
|
|
121
|
+
exports.resolveEasing = resolveEasing;
|
|
122
|
+
//# sourceMappingURL=index.js.map
|
|
123
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../../src/logo/constants.ts","../../src/logo/utils/layers.ts","../../src/logo/utils/resolveTiming.ts","../../src/logo/utils/playLogo.ts","../../src/logo/MicroTrigger.ts","../../src/logo/utils/bindMicro.ts"],"names":["DEFAULT_MOTION","MicroTrigger"],"mappings":"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","file":"index.js","sourcesContent":["export const MOTION_LAYER_ATTRIBUTE = 'data-motion-layer';\n\nexport const REDUCED_MOTION_QUERY = '(prefers-reduced-motion: reduce)';\n\nexport const DEFAULT_LOGO_EASING = 'standard';\n","import { MOTION_LAYER_ATTRIBUTE, REDUCED_MOTION_QUERY } from '../constants';\n\nconst FRAME_META_KEYS = new Set(['offset', 'easing', 'composite']);\n\ntype StyledElement = Element & ElementCSSInlineStyle;\n\nexport function findLayers(root: Element, layer: string): StyledElement[] {\n const selector = `[${MOTION_LAYER_ATTRIBUTE}=${JSON.stringify(layer)}]`;\n const nodes = Array.from(root.querySelectorAll(selector));\n if (root.matches(selector)) nodes.unshift(root);\n return nodes.filter((node): node is StyledElement => 'style' in node);\n}\n\nexport function applyFinalFrame(element: StyledElement, keyframes: Keyframe[]): void {\n const last = keyframes[keyframes.length - 1];\n if (last === undefined) return;\n const style = element.style as unknown as Record<string, string>;\n Object.entries(last)\n .filter(([key, value]) => !FRAME_META_KEYS.has(key) && value !== null && value !== undefined)\n .forEach(([key, value]) => {\n style[key] = String(value);\n });\n}\n\nexport function supportsWebAnimations(element: Element): boolean {\n return typeof element.animate === 'function';\n}\n\nexport function prefersReducedMotion(): boolean {\n if (typeof window === 'undefined' || typeof window.matchMedia !== 'function') return false;\n return window.matchMedia(REDUCED_MOTION_QUERY).matches;\n}\n","import { DEFAULT_MOTION } from '@dloizides/design-tokens';\n\nimport { DEFAULT_LOGO_EASING } from '../constants';\nimport type { LogoDurationToken, LogoEasingToken } from '../types';\n\n/** A duration token (`fast`, `base`, `slow`, `instant`) to ms; a number passes through. */\nexport function resolveDuration(duration: LogoDurationToken | number): number {\n if (typeof duration === 'number') return duration;\n const ms = DEFAULT_MOTION.durations[duration] as number | undefined;\n if (ms === undefined) throw new Error(`Unknown motion duration token: ${String(duration)}`);\n return ms;\n}\n\n/** An easing token to its css timing function; no token means `standard`. */\nexport function resolveEasing(easing: LogoEasingToken | undefined): string {\n const name = easing ?? DEFAULT_LOGO_EASING;\n const token = DEFAULT_MOTION.easings[name] as { css: string } | undefined;\n if (token === undefined) throw new Error(`Unknown motion easing token: ${String(name)}`);\n return token.css;\n}\n","import type { LogoPlayback, LogoTimeline, PlayLogoOptions } from '../types';\nimport { applyFinalFrame, findLayers, prefersReducedMotion, supportsWebAnimations } from './layers';\nimport { resolveDuration, resolveEasing } from './resolveTiming';\n\nconst ignoreCancel = (): undefined => undefined;\n\n/** Plays a logo timeline on the mark's `data-motion-layer` nodes via the Web Animations API. */\nexport function playLogo(svg: Element, timeline: LogoTimeline, options: PlayLogoOptions = {}): LogoPlayback {\n const still = options.reducedMotion ?? prefersReducedMotion();\n const animations: Animation[] = [];\n timeline.forEach((beat) => {\n findLayers(svg, beat.layer).forEach((element) => {\n if (still || !supportsWebAnimations(element)) {\n applyFinalFrame(element, beat.keyframes);\n return;\n }\n animations.push(\n element.animate(beat.keyframes, {\n duration: resolveDuration(beat.duration),\n easing: resolveEasing(beat.easing),\n delay: beat.delay ?? 0,\n fill: 'both',\n iterations: options.iterations ?? 1,\n }),\n );\n });\n });\n const finished = Promise.all(animations.map((animation) => animation.finished.catch(ignoreCancel))).then(ignoreCancel);\n return { finished, cancel: () => animations.forEach((animation) => animation.cancel()) };\n}\n","/** What starts a logo micro-animation bound with `bindMicro`. */\nexport const enum MicroTrigger {\n Hover = 'hover',\n Focus = 'focus',\n Loading = 'loading',\n}\n","import { MicroTrigger } from '../MicroTrigger';\nimport type { BindMicroOptions, LogoPlayback, LogoTimeline } from '../types';\nimport { prefersReducedMotion } from './layers';\nimport { playLogo } from './playLogo';\n\nconst START_EVENT: Record<string, string | undefined> = {\n [MicroTrigger.Hover]: 'pointerenter',\n [MicroTrigger.Focus]: 'focusin',\n};\n\nconst STOP_EVENT: Record<string, string | undefined> = {\n [MicroTrigger.Hover]: 'pointerleave',\n [MicroTrigger.Focus]: 'focusout',\n};\n\nconst noop = (): void => undefined;\n\n/** Binds a micro-animation to hover, focus or loading; returns the unbind function. */\nexport function bindMicro(\n svg: Element,\n timeline: LogoTimeline,\n trigger: MicroTrigger | `${MicroTrigger}`,\n options: BindMicroOptions = {},\n): () => void {\n if (options.reducedMotion ?? prefersReducedMotion()) return noop;\n let playback: LogoPlayback | undefined;\n const stop = (): void => {\n playback?.cancel();\n playback = undefined;\n };\n if (trigger === MicroTrigger.Loading) {\n playback = playLogo(svg, timeline, { reducedMotion: false, iterations: Infinity });\n return stop;\n }\n const start = (): void => {\n stop();\n playback = playLogo(svg, timeline, { reducedMotion: false });\n };\n const startEvent = START_EVENT[trigger] ?? '';\n const stopEvent = STOP_EVENT[trigger] ?? '';\n svg.addEventListener(startEvent, start);\n svg.addEventListener(stopEvent, stop);\n return () => {\n svg.removeEventListener(startEvent, start);\n svg.removeEventListener(stopEvent, stop);\n stop();\n };\n}\n"]}
|