UNPKG

@humanspeak/svelte-motion

Version:

Framer Motion for Svelte 5. Declarative motion.<tag> components with AnimatePresence exit animations, gestures (hover, tap, drag, focus, in-view), variants, FLIP layout animations, shared-layout transitions, spring physics, and scroll-linked motion values

45 lines (44 loc) 2.01 kB
import { wrap } from 'motion'; export function useCycle(...args) { const getItems = args.length === 1 && typeof args[0] === 'function' ? args[0] : () => args; if (getItems().length === 0) { throw new Error('useCycle requires at least one item'); } let index = $state(0); return { get current() { const items = getItems(); // Reactive-getter form: if the consumer's source emptied // mid-cycle the public type can no longer be honored. Throw // loudly so the bug surfaces immediately rather than leaking // `undefined` through a `T`-typed read. if (items.length === 0) { throw new Error('useCycle items getter returned an empty list'); } // Clamp on read so out-of-range indexes (from `cycle(-5)` or // `cycle(99)`, or items shrinking under us in the getter form) // resolve to the nearest valid edge instead of `undefined`. const safeIndex = index < 0 ? 0 : index >= items.length ? items.length - 1 : index; return items[safeIndex]; }, cycle: (next) => { const items = getItems(); if (items.length === 0) return; // Reject non-finite / non-integer indexes up-front: `NaN` slips // past the read-time clamp (NaN comparisons return false for // both `< 0` and `>= length`) and would silently make `.current` // resolve to `undefined`, breaking the `T` contract. Throw // loudly to surface the consumer bug at write-time. if (typeof next === 'number' && !Number.isInteger(next)) { throw new Error('useCycle index must be a finite integer'); } const target = typeof next === 'number' ? next : wrap(0, items.length, index + 1); if (target === index) return; index = target; } }; }