@humanspeak/svelte-motion 0.4.3 → 0.4.4

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/README.md CHANGED
@@ -214,6 +214,29 @@ Supported drag props:
214
214
 
215
215
  - String variant keys are resolved from `variants`.
216
216
  - Variant state inherits through context.
217
+ - A variant entry can be a `(custom) => keyframes` factory. The `custom` prop is forwarded — useful for staggered lists where each child needs its own offset or delay. Children without `custom` inherit the nearest motion ancestor's value.
218
+
219
+ ```svelte
220
+ <script lang="ts">
221
+ import { motion, type Variants } from '@humanspeak/svelte-motion'
222
+
223
+ const variants: Variants = {
224
+ hidden: { opacity: 0, x: -100 },
225
+ visible: (i) => ({
226
+ opacity: 1,
227
+ x: 0,
228
+ transition: { delay: (i as number) * 0.1 }
229
+ })
230
+ }
231
+ const items = ['Alpha', 'Beta', 'Gamma', 'Delta']
232
+ </script>
233
+
234
+ {#each items as item, i}
235
+ <motion.li custom={i} {variants} initial="hidden" animate="visible">
236
+ {item}
237
+ </motion.li>
238
+ {/each}
239
+ ```
217
240
 
218
241
  ## Layout animation
219
242
 
@@ -25,3 +25,23 @@ export declare const setInitialFalseContext: (value: boolean) => void;
25
25
  * @returns `true` if a parent set `initial={false}`, otherwise `false`.
26
26
  */
27
27
  export declare const getInitialFalseContext: () => boolean;
28
+ /**
29
+ * Provide a writable store carrying the current motion component's
30
+ * `custom` value so descendant motion components without their own
31
+ * `custom` prop can inherit it — and re-resolve their variants when the
32
+ * parent's `custom` changes.
33
+ *
34
+ * Mirrors framer-motion's variant-tree custom propagation. A store
35
+ * (rather than a snapshot) lets descendants react to changes the parent
36
+ * makes after mount.
37
+ *
38
+ * @param store Writable store holding the current component's effective `custom`.
39
+ */
40
+ export declare const setCustomContext: (store: Writable<unknown>) => void;
41
+ /**
42
+ * Read the nearest ancestor's `custom` store (if any).
43
+ *
44
+ * @returns The ancestor's writable store, or `undefined` when no motion
45
+ * ancestor has set one.
46
+ */
47
+ export declare const getCustomContext: () => Writable<unknown> | undefined;
@@ -1,6 +1,7 @@
1
1
  import { getContext, setContext } from 'svelte';
2
2
  const VARIANT_CONTEXT_KEY = Symbol('variant-context');
3
3
  const INITIAL_FALSE_CONTEXT_KEY = Symbol('initial-false-context');
4
+ const CUSTOM_CONTEXT_KEY = Symbol('custom-context');
4
5
  /**
5
6
  * Provide a writable store for the current variant key so children can
6
7
  * react to changes over time (true inheritance like Framer Motion).
@@ -35,3 +36,27 @@ export const setInitialFalseContext = (value) => {
35
36
  export const getInitialFalseContext = () => {
36
37
  return getContext(INITIAL_FALSE_CONTEXT_KEY) ?? false;
37
38
  };
39
+ /**
40
+ * Provide a writable store carrying the current motion component's
41
+ * `custom` value so descendant motion components without their own
42
+ * `custom` prop can inherit it — and re-resolve their variants when the
43
+ * parent's `custom` changes.
44
+ *
45
+ * Mirrors framer-motion's variant-tree custom propagation. A store
46
+ * (rather than a snapshot) lets descendants react to changes the parent
47
+ * makes after mount.
48
+ *
49
+ * @param store Writable store holding the current component's effective `custom`.
50
+ */
51
+ export const setCustomContext = (store) => {
52
+ setContext(CUSTOM_CONTEXT_KEY, store);
53
+ };
54
+ /**
55
+ * Read the nearest ancestor's `custom` store (if any).
56
+ *
57
+ * @returns The ancestor's writable store, or `undefined` when no motion
58
+ * ancestor has set one.
59
+ */
60
+ export const getCustomContext = () => {
61
+ return getContext(CUSTOM_CONTEXT_KEY);
62
+ };
@@ -53,7 +53,9 @@
53
53
  setVariantContext,
54
54
  getVariantContext,
55
55
  setInitialFalseContext,
56
- getInitialFalseContext
56
+ getInitialFalseContext,
57
+ setCustomContext,
58
+ getCustomContext
57
59
  } from '../components/variantContext.context'
58
60
  import { get, writable } from 'svelte/store'
59
61
  import {
@@ -75,6 +77,7 @@
75
77
  tag = 'div',
76
78
  key: keyProp,
77
79
  variants: variantsProp,
80
+ custom: customProp,
78
81
  initial: initialProp,
79
82
  animate: animateProp,
80
83
  exit: exitProp,
@@ -360,6 +363,32 @@
360
363
  // Provide context immediately during initialization so children can inherit
361
364
  setVariantContext(localVariantStore)
362
365
 
366
+ // Custom-value inheritance. Children with no `custom` prop adopt the
367
+ // nearest motion ancestor's value. Reactive via a writable store so a
368
+ // parent updating `custom` re-fires descendants' variant resolution.
369
+ const parentCustomStore = getCustomContext()
370
+ let inheritedCustom: unknown = undefined
371
+ if (parentCustomStore) {
372
+ parentCustomStore.subscribe((v) => (inheritedCustom = v))()
373
+ }
374
+ const initialCustomValue = customProp !== undefined ? customProp : inheritedCustom
375
+ const localCustomStore = writable<unknown>(initialCustomValue)
376
+ setCustomContext(localCustomStore)
377
+
378
+ let parentInheritedCustom = $state<unknown>(inheritedCustom)
379
+ $effect(() => {
380
+ if (!parentCustomStore) {
381
+ parentInheritedCustom = undefined
382
+ return
383
+ }
384
+ const unsubscribe = parentCustomStore.subscribe((v) => (parentInheritedCustom = v))
385
+ return () => unsubscribe()
386
+ })
387
+ const effectiveCustom = $derived(customProp !== undefined ? customProp : parentInheritedCustom)
388
+ $effect(() => {
389
+ localCustomStore.set(effectiveCustom)
390
+ })
391
+
363
392
  $effect(() => {
364
393
  if (!variantsProp) return localVariantStore.set(undefined)
365
394
  if (typeof animateProp === 'string') return localVariantStore.set(animateProp)
@@ -367,9 +396,13 @@
367
396
  localVariantStore.set(undefined)
368
397
  })
369
398
 
370
- const resolvedInitial = $derived(resolveInitial(effectiveInitialProp, variantsProp))
371
- const resolvedAnimate = $derived(resolveAnimate(effectiveAnimate, variantsProp))
372
- const resolvedExit = $derived(resolveExit(exitProp, variantsProp))
399
+ const resolvedInitial = $derived(
400
+ resolveInitial(effectiveInitialProp, variantsProp, effectiveCustom)
401
+ )
402
+ const resolvedAnimate = $derived(
403
+ resolveAnimate(effectiveAnimate, variantsProp, effectiveCustom)
404
+ )
405
+ const resolvedExit = $derived(resolveExit(exitProp, variantsProp, effectiveCustom))
373
406
 
374
407
  // Extract keyframes from resolved initial, handling initial={false}
375
408
  const initialKeyframes = $derived(
@@ -639,6 +672,12 @@
639
672
 
640
673
  // Track the last variant key we ran to avoid re-running on mount
641
674
  let lastRanVariantKey = $state<string | undefined>(undefined)
675
+ // Companion to `lastRanVariantKey`: the JSON-serialized resolved
676
+ // keyframes for that variant. Lets us detect when a function-form
677
+ // variant produces new keyframes (because `custom` changed) while
678
+ // the variant key stayed the same — otherwise the animate effect
679
+ // would short-circuit and the element would never re-animate.
680
+ let lastRanResolvedJson = $state<string | undefined>(undefined)
642
681
  let mountedWithInitialFalse = $state(false)
643
682
  // Track if the initial->animate transition has already been triggered by main effect
644
683
  let initialAnimationTriggered = $state(false)
@@ -916,8 +955,14 @@
916
955
  return
917
956
  }
918
957
  if (typeof animateProp === 'string') {
919
- if (lastRanVariantKey !== animateProp) {
958
+ // Compare BOTH the variant key and the resolved keyframes JSON.
959
+ // For static variants the JSON is constant per key; for
960
+ // function-form variants the JSON changes when `custom`
961
+ // changes, which we must treat as a new animation target.
962
+ const resolvedJson = resolvedAnimate ? JSON.stringify(resolvedAnimate) : undefined
963
+ if (lastRanVariantKey !== animateProp || lastRanResolvedJson !== resolvedJson) {
920
964
  lastRanVariantKey = animateProp
965
+ lastRanResolvedJson = resolvedJson
921
966
  runAnimation()
922
967
  }
923
968
  } else if (animateProp) {
@@ -956,8 +1001,10 @@
956
1001
  mountedWithInitialFalse = false
957
1002
  }
958
1003
  if (typeof currentAnimateKey === 'string') {
959
- if (lastRanVariantKey !== currentAnimateKey) {
1004
+ const resolvedJson = resolvedAnimate ? JSON.stringify(resolvedAnimate) : undefined
1005
+ if (lastRanVariantKey !== currentAnimateKey || lastRanResolvedJson !== resolvedJson) {
960
1006
  lastRanVariantKey = currentAnimateKey
1007
+ lastRanResolvedJson = resolvedJson
961
1008
  runAnimation()
962
1009
  }
963
1010
  } else {
@@ -989,6 +1036,9 @@
989
1036
  mountedWithInitialFalse = true
990
1037
  if (typeof currentAnimateKey === 'string') {
991
1038
  lastRanVariantKey = currentAnimateKey
1039
+ lastRanResolvedJson = resolvedAnimate
1040
+ ? JSON.stringify(resolvedAnimate)
1041
+ : undefined
992
1042
  }
993
1043
  dataPath = 5
994
1044
  isLoaded = 'ready'
@@ -1081,6 +1131,9 @@
1081
1131
  snapshot = transformSVGPathProperties(element!, snapshot)
1082
1132
  animate(element!, snapshot as DOMKeyframesDefinition, { duration: 0 })
1083
1133
  lastRanVariantKey = currentAnimateKey
1134
+ lastRanResolvedJson = resolvedAnimate
1135
+ ? JSON.stringify(resolvedAnimate)
1136
+ : undefined
1084
1137
  } else {
1085
1138
  runAnimation()
1086
1139
  }
package/dist/types.d.ts CHANGED
@@ -1,8 +1,32 @@
1
1
  import type { AnimationOptions, DOMKeyframesDefinition } from 'motion';
2
2
  import type { Snippet } from 'svelte';
3
+ /**
4
+ * A variant value: either a static keyframes object, or a factory function
5
+ * that receives the consumer-provided `custom` value and returns keyframes.
6
+ *
7
+ * Dynamic (function-form) variants let a single variants object emit
8
+ * per-instance keyframes — common for staggered lists where each child
9
+ * needs its own offset or delay.
10
+ *
11
+ * @example
12
+ * ```svelte
13
+ * <motion.div
14
+ * custom={index}
15
+ * variants={{
16
+ * visible: (i) => ({ opacity: 1, x: i * 50 }),
17
+ * hidden: { opacity: 0 }
18
+ * }}
19
+ * animate="visible"
20
+ * />
21
+ * ```
22
+ */
23
+ export type Variant = DOMKeyframesDefinition | ((custom: unknown) => DOMKeyframesDefinition) | undefined;
3
24
  /**
4
25
  * Variants define named animation states that can be referenced by string keys.
5
26
  *
27
+ * Each entry can be a static keyframes object or a `(custom) => keyframes`
28
+ * factory function (see {@link Variant}).
29
+ *
6
30
  * @example
7
31
  * ```svelte
8
32
  * <script>
@@ -15,7 +39,7 @@ import type { Snippet } from 'svelte';
15
39
  * <motion.div variants={variants} animate="open" />
16
40
  * ```
17
41
  */
18
- export type Variants = Record<string, DOMKeyframesDefinition | undefined>;
42
+ export type Variants = Record<string, Variant>;
19
43
  /**
20
44
  * Initial animation properties for a motion component.
21
45
  *
@@ -245,6 +269,12 @@ export type MotionProps = {
245
269
  key?: string;
246
270
  /** Variants define named animation states */
247
271
  variants?: Variants;
272
+ /**
273
+ * Value passed into function-form variants. Children without their own
274
+ * `custom` prop inherit this from the nearest motion ancestor — matching
275
+ * framer-motion's variant-tree custom propagation.
276
+ */
277
+ custom?: unknown;
248
278
  /** Initial state of the animation (object or variant key) */
249
279
  initial?: MotionInitial;
250
280
  /** Target state of the animation (object or variant key) */
@@ -3,80 +3,74 @@ import type { DOMKeyframesDefinition } from 'motion';
3
3
  /**
4
4
  * Resolves a variant key to its keyframes definition.
5
5
  *
6
- * Looks up a variant name in the variants object and returns the corresponding
7
- * keyframes. Returns `undefined` if the variants object or key is not provided.
6
+ * Looks up `key` in `variants`. When the entry is a function (dynamic
7
+ * variant), it's invoked with `custom` to produce keyframes matching
8
+ * framer-motion's per-instance variant pattern.
8
9
  *
9
10
  * @param variants - The variants object containing named animation states.
10
11
  * @param key - The variant key to look up.
11
- * @returns The keyframes definition for the variant, or undefined if not found.
12
+ * @param custom - Value forwarded to function-form variants. Pass-through
13
+ * `undefined` when no `custom` is in scope; the dynamic variant itself
14
+ * decides how to handle the absent input.
15
+ * @returns The keyframes definition for the variant, or `undefined` if the
16
+ * key is missing.
12
17
  *
13
18
  * @example
14
- * ```typescript
19
+ * ```ts
15
20
  * const variants = {
16
- * visible: { opacity: 1 },
17
- * hidden: { opacity: 0 }
21
+ * visible: (i: number) => ({ x: i * 50 }),
22
+ * hidden: { opacity: 0 }
18
23
  * }
19
- * resolveVariant(variants, 'visible') // { opacity: 1 }
20
- * resolveVariant(variants, 'foo') // undefined
21
- * resolveVariant(undefined, 'visible') // undefined
24
+ * resolveVariant(variants, 'visible', 3) // { x: 150 }
25
+ * resolveVariant(variants, 'hidden') // { opacity: 0 }
26
+ * resolveVariant(undefined, 'visible') // undefined
22
27
  * ```
23
28
  */
24
- export declare const resolveVariant: (variants: Variants | undefined, key: string | undefined) => DOMKeyframesDefinition | undefined;
29
+ export declare const resolveVariant: (variants: Variants | undefined, key: string | undefined, custom?: unknown) => DOMKeyframesDefinition | undefined;
25
30
  /**
26
31
  * Resolves the initial prop to keyframes, handling variant keys and `initial={false}`.
27
32
  *
28
- * When `initial` is a string, looks it up in the variants object. When `initial={false}`,
29
- * returns `false` to skip initial animation. Otherwise returns the keyframes directly.
33
+ * When `initial` is a string, looks it up in the variants object (invoking
34
+ * dynamic variants with `custom`). When `initial={false}`, returns `false` to
35
+ * skip the initial animation. Otherwise returns the keyframes directly.
30
36
  *
31
37
  * @param initial - The initial prop value (keyframes, variant key, false, or undefined).
32
38
  * @param variants - The variants object for resolving string keys.
39
+ * @param custom - Forwarded to function-form variants.
33
40
  * @returns Keyframes definition, `false` to skip animation, or undefined.
34
41
  *
35
42
  * @example
36
- * ```typescript
37
- * const variants = { hidden: { opacity: 0 } }
38
- * resolveInitial('hidden', variants) // { opacity: 0 }
39
- * resolveInitial({ x: 0 }, variants) // { x: 0 }
40
- * resolveInitial(false, variants) // false
41
- * resolveInitial(undefined, variants) // undefined
43
+ * ```ts
44
+ * const variants = { hidden: (i: number) => ({ x: -i * 100 }) }
45
+ * resolveInitial('hidden', variants, 2) // { x: -200 }
46
+ * resolveInitial({ x: 0 }, variants) // { x: 0 }
47
+ * resolveInitial(false, variants) // false
48
+ * resolveInitial(undefined, variants) // undefined
42
49
  * ```
43
50
  */
44
- export declare const resolveInitial: (initial: MotionInitial, variants: Variants | undefined) => DOMKeyframesDefinition | false | undefined;
51
+ export declare const resolveInitial: (initial: MotionInitial, variants: Variants | undefined, custom?: unknown) => DOMKeyframesDefinition | false | undefined;
45
52
  /**
46
53
  * Resolves the animate prop to keyframes, handling variant keys.
47
54
  *
48
- * When `animate` is a string, looks it up in the variants object.
49
- * Otherwise returns the keyframes directly.
55
+ * When `animate` is a string, looks it up in the variants object (invoking
56
+ * dynamic variants with `custom`). Otherwise returns the keyframes directly.
50
57
  *
51
58
  * @param animate - The animate prop value (keyframes, variant key, or undefined).
52
59
  * @param variants - The variants object for resolving string keys.
60
+ * @param custom - Forwarded to function-form variants.
53
61
  * @returns Keyframes definition or undefined.
54
- *
55
- * @example
56
- * ```typescript
57
- * const variants = { visible: { opacity: 1 } }
58
- * resolveAnimate('visible', variants) // { opacity: 1 }
59
- * resolveAnimate({ scale: 1.2 }, variants) // { scale: 1.2 }
60
- * resolveAnimate(undefined, variants) // undefined
61
- * ```
62
62
  */
63
- export declare const resolveAnimate: (animate: MotionAnimate, variants: Variants | undefined) => DOMKeyframesDefinition | undefined;
63
+ export declare const resolveAnimate: (animate: MotionAnimate, variants: Variants | undefined, custom?: unknown) => DOMKeyframesDefinition | undefined;
64
64
  /**
65
65
  * Resolves the exit prop to keyframes, handling variant keys.
66
66
  *
67
- * When `exit` is a string, looks it up in the variants object.
68
- * Otherwise returns the keyframes directly. Used by AnimatePresence for exit animations.
67
+ * When `exit` is a string, looks it up in the variants object (invoking
68
+ * dynamic variants with `custom`). Otherwise returns the keyframes directly.
69
+ * Used by AnimatePresence for exit animations.
69
70
  *
70
71
  * @param exit - The exit prop value (keyframes, variant key, or undefined).
71
72
  * @param variants - The variants object for resolving string keys.
73
+ * @param custom - Forwarded to function-form variants.
72
74
  * @returns Keyframes definition or undefined.
73
- *
74
- * @example
75
- * ```typescript
76
- * const variants = { hidden: { opacity: 0 } }
77
- * resolveExit('hidden', variants) // { opacity: 0 }
78
- * resolveExit({ y: -100 }, variants) // { y: -100 }
79
- * resolveExit(undefined, variants) // undefined
80
- * ```
81
75
  */
82
- export declare const resolveExit: (exit: MotionExit, variants: Variants | undefined) => DOMKeyframesDefinition | undefined;
76
+ export declare const resolveExit: (exit: MotionExit, variants: Variants | undefined, custom?: unknown) => DOMKeyframesDefinition | undefined;
@@ -1,104 +1,101 @@
1
1
  /**
2
2
  * Resolves a variant key to its keyframes definition.
3
3
  *
4
- * Looks up a variant name in the variants object and returns the corresponding
5
- * keyframes. Returns `undefined` if the variants object or key is not provided.
4
+ * Looks up `key` in `variants`. When the entry is a function (dynamic
5
+ * variant), it's invoked with `custom` to produce keyframes matching
6
+ * framer-motion's per-instance variant pattern.
6
7
  *
7
8
  * @param variants - The variants object containing named animation states.
8
9
  * @param key - The variant key to look up.
9
- * @returns The keyframes definition for the variant, or undefined if not found.
10
+ * @param custom - Value forwarded to function-form variants. Pass-through
11
+ * `undefined` when no `custom` is in scope; the dynamic variant itself
12
+ * decides how to handle the absent input.
13
+ * @returns The keyframes definition for the variant, or `undefined` if the
14
+ * key is missing.
10
15
  *
11
16
  * @example
12
- * ```typescript
17
+ * ```ts
13
18
  * const variants = {
14
- * visible: { opacity: 1 },
15
- * hidden: { opacity: 0 }
19
+ * visible: (i: number) => ({ x: i * 50 }),
20
+ * hidden: { opacity: 0 }
16
21
  * }
17
- * resolveVariant(variants, 'visible') // { opacity: 1 }
18
- * resolveVariant(variants, 'foo') // undefined
19
- * resolveVariant(undefined, 'visible') // undefined
22
+ * resolveVariant(variants, 'visible', 3) // { x: 150 }
23
+ * resolveVariant(variants, 'hidden') // { opacity: 0 }
24
+ * resolveVariant(undefined, 'visible') // undefined
20
25
  * ```
21
26
  */
22
- export const resolveVariant = (variants, key) => {
27
+ export const resolveVariant = (variants, key, custom) => {
23
28
  if (!variants || !key)
24
29
  return undefined;
25
- return variants[key];
30
+ const entry = variants[key];
31
+ if (typeof entry === 'function')
32
+ return entry(custom);
33
+ return entry;
26
34
  };
27
35
  /**
28
36
  * Resolves the initial prop to keyframes, handling variant keys and `initial={false}`.
29
37
  *
30
- * When `initial` is a string, looks it up in the variants object. When `initial={false}`,
31
- * returns `false` to skip initial animation. Otherwise returns the keyframes directly.
38
+ * When `initial` is a string, looks it up in the variants object (invoking
39
+ * dynamic variants with `custom`). When `initial={false}`, returns `false` to
40
+ * skip the initial animation. Otherwise returns the keyframes directly.
32
41
  *
33
42
  * @param initial - The initial prop value (keyframes, variant key, false, or undefined).
34
43
  * @param variants - The variants object for resolving string keys.
44
+ * @param custom - Forwarded to function-form variants.
35
45
  * @returns Keyframes definition, `false` to skip animation, or undefined.
36
46
  *
37
47
  * @example
38
- * ```typescript
39
- * const variants = { hidden: { opacity: 0 } }
40
- * resolveInitial('hidden', variants) // { opacity: 0 }
41
- * resolveInitial({ x: 0 }, variants) // { x: 0 }
42
- * resolveInitial(false, variants) // false
43
- * resolveInitial(undefined, variants) // undefined
48
+ * ```ts
49
+ * const variants = { hidden: (i: number) => ({ x: -i * 100 }) }
50
+ * resolveInitial('hidden', variants, 2) // { x: -200 }
51
+ * resolveInitial({ x: 0 }, variants) // { x: 0 }
52
+ * resolveInitial(false, variants) // false
53
+ * resolveInitial(undefined, variants) // undefined
44
54
  * ```
45
55
  */
46
- export const resolveInitial = (initial, variants) => {
56
+ export const resolveInitial = (initial, variants, custom) => {
47
57
  if (initial === false)
48
58
  return false;
49
59
  if (initial === undefined)
50
60
  return undefined;
51
61
  if (typeof initial === 'string')
52
- return resolveVariant(variants, initial);
62
+ return resolveVariant(variants, initial, custom);
53
63
  return initial;
54
64
  };
55
65
  /**
56
66
  * Resolves the animate prop to keyframes, handling variant keys.
57
67
  *
58
- * When `animate` is a string, looks it up in the variants object.
59
- * Otherwise returns the keyframes directly.
68
+ * When `animate` is a string, looks it up in the variants object (invoking
69
+ * dynamic variants with `custom`). Otherwise returns the keyframes directly.
60
70
  *
61
71
  * @param animate - The animate prop value (keyframes, variant key, or undefined).
62
72
  * @param variants - The variants object for resolving string keys.
73
+ * @param custom - Forwarded to function-form variants.
63
74
  * @returns Keyframes definition or undefined.
64
- *
65
- * @example
66
- * ```typescript
67
- * const variants = { visible: { opacity: 1 } }
68
- * resolveAnimate('visible', variants) // { opacity: 1 }
69
- * resolveAnimate({ scale: 1.2 }, variants) // { scale: 1.2 }
70
- * resolveAnimate(undefined, variants) // undefined
71
- * ```
72
75
  */
73
- export const resolveAnimate = (animate, variants) => {
76
+ export const resolveAnimate = (animate, variants, custom) => {
74
77
  if (animate === undefined)
75
78
  return undefined;
76
79
  if (typeof animate === 'string')
77
- return resolveVariant(variants, animate);
80
+ return resolveVariant(variants, animate, custom);
78
81
  return animate;
79
82
  };
80
83
  /**
81
84
  * Resolves the exit prop to keyframes, handling variant keys.
82
85
  *
83
- * When `exit` is a string, looks it up in the variants object.
84
- * Otherwise returns the keyframes directly. Used by AnimatePresence for exit animations.
86
+ * When `exit` is a string, looks it up in the variants object (invoking
87
+ * dynamic variants with `custom`). Otherwise returns the keyframes directly.
88
+ * Used by AnimatePresence for exit animations.
85
89
  *
86
90
  * @param exit - The exit prop value (keyframes, variant key, or undefined).
87
91
  * @param variants - The variants object for resolving string keys.
92
+ * @param custom - Forwarded to function-form variants.
88
93
  * @returns Keyframes definition or undefined.
89
- *
90
- * @example
91
- * ```typescript
92
- * const variants = { hidden: { opacity: 0 } }
93
- * resolveExit('hidden', variants) // { opacity: 0 }
94
- * resolveExit({ y: -100 }, variants) // { y: -100 }
95
- * resolveExit(undefined, variants) // undefined
96
- * ```
97
94
  */
98
- export const resolveExit = (exit, variants) => {
95
+ export const resolveExit = (exit, variants, custom) => {
99
96
  if (exit === undefined)
100
97
  return undefined;
101
98
  if (typeof exit === 'string')
102
- return resolveVariant(variants, exit);
99
+ return resolveVariant(variants, exit, custom);
103
100
  return exit;
104
101
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@humanspeak/svelte-motion",
3
- "version": "0.4.3",
3
+ "version": "0.4.4",
4
4
  "description": "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. The drop-in Framer Motion alternative for Svelte and SvelteKit.",
5
5
  "keywords": [
6
6
  "svelte",