@humanspeak/svelte-motion 0.5.2 → 0.5.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.
@@ -0,0 +1,70 @@
1
+ <script lang="ts">
2
+ import { setLazyMotionContext } from './lazyMotion.context'
3
+ import { domMin } from '../features/domMin'
4
+ import {
5
+ isLazyFeatureBundle,
6
+ normalizeLazyFeatureBundle,
7
+ type FeatureBundle,
8
+ type LazyFeatureBundle
9
+ } from '../features'
10
+ import { untrack, type Snippet } from 'svelte'
11
+
12
+ /**
13
+ * Props accepted by the LazyMotion component.
14
+ */
15
+ type Props = {
16
+ /** Child content rendered inside the active LazyMotion context. */
17
+ children?: Snippet
18
+ /** Eager or async feature bundle used by descendant `m.*` components. */
19
+ features: FeatureBundle | LazyFeatureBundle
20
+ /** Enables strict LazyMotion usage checks. Defaults to false. */
21
+ strict?: boolean
22
+ }
23
+
24
+ let { children, features, strict = false }: Props = $props()
25
+
26
+ let loadedFeatures = $state<FeatureBundle>(
27
+ untrack(() => (isLazyFeatureBundle(features) ? domMin : features))
28
+ )
29
+ let isLoaded = $state(untrack(() => !isLazyFeatureBundle(features)))
30
+
31
+ setLazyMotionContext({
32
+ getFeatures: () => loadedFeatures,
33
+ getIsLoaded: () => isLoaded,
34
+ get strict() {
35
+ return strict
36
+ }
37
+ })
38
+
39
+ let loadId = 0
40
+
41
+ $effect(() => {
42
+ const currentLoadId = ++loadId
43
+
44
+ if (!isLazyFeatureBundle(features)) {
45
+ loadedFeatures = features
46
+ isLoaded = true
47
+ return
48
+ }
49
+
50
+ loadedFeatures = domMin
51
+ isLoaded = false
52
+ features()
53
+ .then((bundle) => {
54
+ if (currentLoadId !== loadId) return
55
+ loadedFeatures = normalizeLazyFeatureBundle(bundle)
56
+ isLoaded = true
57
+ })
58
+ .catch(() => {
59
+ if (currentLoadId !== loadId) return
60
+ loadedFeatures = domMin
61
+ isLoaded = false
62
+ })
63
+
64
+ return () => {
65
+ loadId += 1
66
+ }
67
+ })
68
+ </script>
69
+
70
+ {@render children?.()}
@@ -0,0 +1,16 @@
1
+ import { type FeatureBundle, type LazyFeatureBundle } from '../features';
2
+ import { type Snippet } from 'svelte';
3
+ /**
4
+ * Props accepted by the LazyMotion component.
5
+ */
6
+ type Props = {
7
+ /** Child content rendered inside the active LazyMotion context. */
8
+ children?: Snippet;
9
+ /** Eager or async feature bundle used by descendant `m.*` components. */
10
+ features: FeatureBundle | LazyFeatureBundle;
11
+ /** Enables strict LazyMotion usage checks. Defaults to false. */
12
+ strict?: boolean;
13
+ };
14
+ declare const LazyMotion: import("svelte").Component<Props, {}, "">;
15
+ type LazyMotion = ReturnType<typeof LazyMotion>;
16
+ export default LazyMotion;
@@ -0,0 +1,25 @@
1
+ import type { FeatureBundle } from '../features';
2
+ /**
3
+ * Context value published by `<LazyMotion>` for descendant motion elements.
4
+ */
5
+ export type LazyMotionContext = {
6
+ /** Returns the currently active feature bundle. */
7
+ getFeatures: () => FeatureBundle;
8
+ /** Returns whether an async bundle has finished loading. */
9
+ getIsLoaded: () => boolean;
10
+ /** Enables Framer Motion-style strict lazy usage checks. */
11
+ strict: boolean;
12
+ };
13
+ /**
14
+ * Reads the nearest LazyMotion context.
15
+ *
16
+ * @returns The active LazyMotion context, or undefined outside LazyMotion.
17
+ */
18
+ export declare const getLazyMotionContext: () => LazyMotionContext | undefined;
19
+ /**
20
+ * Publishes a LazyMotion context for descendant motion elements.
21
+ *
22
+ * @param context - LazyMotion context to publish.
23
+ * @returns The same context value returned by Svelte's setContext.
24
+ */
25
+ export declare const setLazyMotionContext: (context: LazyMotionContext) => LazyMotionContext;
@@ -0,0 +1,19 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ const key = Symbol('lazyMotion');
3
+ /**
4
+ * Reads the nearest LazyMotion context.
5
+ *
6
+ * @returns The active LazyMotion context, or undefined outside LazyMotion.
7
+ */
8
+ export const getLazyMotionContext = () => {
9
+ return getContext(key);
10
+ };
11
+ /**
12
+ * Publishes a LazyMotion context for descendant motion elements.
13
+ *
14
+ * @param context - LazyMotion context to publish.
15
+ * @returns The same context value returned by Svelte's setContext.
16
+ */
17
+ export const setLazyMotionContext = (context) => {
18
+ return setContext(key, context);
19
+ };
@@ -0,0 +1,22 @@
1
+ import type { ProjectionNode } from '../utils/projection';
2
+ /**
3
+ * Publish a `ProjectionNode` as the projection parent for descendant
4
+ * motion components.
5
+ *
6
+ * @param node The current component's ProjectionNode, or `null` to
7
+ * explicitly clear (rarely needed — Svelte context auto-clears on
8
+ * component unmount).
9
+ */
10
+ export declare const setProjectionParent: (node: ProjectionNode | null) => void;
11
+ /**
12
+ * Read the ancestor `ProjectionNode` published by the nearest motion
13
+ * ancestor. Returns `null` when this component is at the root of the
14
+ * projection tree (or when no motion ancestor exists).
15
+ *
16
+ * Must be called BEFORE the current component publishes its own node
17
+ * via `setProjectionParent` — see the order-of-operations note in this
18
+ * file's header.
19
+ *
20
+ * @returns The parent ProjectionNode, or `null`.
21
+ */
22
+ export declare const getProjectionParent: () => ProjectionNode | null;
@@ -0,0 +1,56 @@
1
+ import { getContext, setContext } from 'svelte';
2
+ /**
3
+ * Svelte context plumbing for the projection tree.
4
+ *
5
+ * Every `motion.*` element creates a `ProjectionNode` at setup time and
6
+ * publishes it via `setProjectionParent(node)` so descendant motion
7
+ * elements can pick it up via `getProjectionParent()` and wire themselves
8
+ * as `node.parent` + register in `node.parent.children`. The tree shape
9
+ * mirrors the Svelte component tree exactly because Svelte's
10
+ * `setContext` propagates down through descendants in component-init
11
+ * order.
12
+ *
13
+ * This is the closest analog we have to framer-motion's depth-sorted
14
+ * FlatTree (`projection/node/create-projection-node.ts`), but without
15
+ * the global `root` registry — parent/child pointers are sufficient
16
+ * for the workflows this PR enables (drag↔swap origin compensation,
17
+ * ancestor-zeroing measure). A global root would only be needed once we
18
+ * port shared-element `layoutId` morphing onto projection nodes; the
19
+ * current `layoutId.ts` registry continues to handle that case
20
+ * independently.
21
+ *
22
+ * Order of operations inside a single `motion.*` component (CRITICAL):
23
+ * 1. Read parent via `getProjectionParent()` BEFORE creating own node.
24
+ * 2. Construct own node, set `node.parent = parent`.
25
+ * 3. Publish own node via `setProjectionParent(node)`.
26
+ * If steps 1 and 3 are reversed, the component sees ITS OWN node as
27
+ * the parent (because `setContext` shadows from the call site down)
28
+ * and the tree collapses to a chain of self-references. Same trap
29
+ * `layoutScroll.context.ts` documents — don't repeat it.
30
+ */
31
+ const PROJECTION_CONTEXT_KEY = Symbol('svelte-motion:projection-parent');
32
+ /**
33
+ * Publish a `ProjectionNode` as the projection parent for descendant
34
+ * motion components.
35
+ *
36
+ * @param node The current component's ProjectionNode, or `null` to
37
+ * explicitly clear (rarely needed — Svelte context auto-clears on
38
+ * component unmount).
39
+ */
40
+ export const setProjectionParent = (node) => {
41
+ setContext(PROJECTION_CONTEXT_KEY, node);
42
+ };
43
+ /**
44
+ * Read the ancestor `ProjectionNode` published by the nearest motion
45
+ * ancestor. Returns `null` when this component is at the root of the
46
+ * projection tree (or when no motion ancestor exists).
47
+ *
48
+ * Must be called BEFORE the current component publishes its own node
49
+ * via `setProjectionParent` — see the order-of-operations note in this
50
+ * file's header.
51
+ *
52
+ * @returns The parent ProjectionNode, or `null`.
53
+ */
54
+ export const getProjectionParent = () => {
55
+ return getContext(PROJECTION_CONTEXT_KEY) ?? null;
56
+ };
@@ -0,0 +1,6 @@
1
+ import type { FeatureBundle } from './index';
2
+ /**
3
+ * DOM animation feature bundle: animations plus hover, tap, focus, pan,
4
+ * and in-view gestures.
5
+ */
6
+ export declare const domAnimation: FeatureBundle;
@@ -0,0 +1,8 @@
1
+ /**
2
+ * DOM animation feature bundle: animations plus hover, tap, focus, pan,
3
+ * and in-view gestures.
4
+ */
5
+ export const domAnimation = {
6
+ animations: true,
7
+ gestures: true
8
+ };
@@ -0,0 +1,5 @@
1
+ import type { FeatureBundle } from './index';
2
+ /**
3
+ * Full DOM feature bundle: animations, gestures, drag, and layout features.
4
+ */
5
+ export declare const domMax: FeatureBundle;
@@ -0,0 +1,9 @@
1
+ import { domAnimation } from './domAnimation';
2
+ /**
3
+ * Full DOM feature bundle: animations, gestures, drag, and layout features.
4
+ */
5
+ export const domMax = {
6
+ ...domAnimation,
7
+ drag: true,
8
+ layout: true
9
+ };
@@ -0,0 +1,5 @@
1
+ import type { FeatureBundle } from './index';
2
+ /**
3
+ * Minimal DOM feature bundle: mount/update animations only.
4
+ */
5
+ export declare const domMin: FeatureBundle;
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Minimal DOM feature bundle: mount/update animations only.
3
+ */
4
+ export const domMin = {
5
+ animations: true
6
+ };
@@ -0,0 +1,39 @@
1
+ /**
2
+ * Runtime capabilities made available to motion components inside a
3
+ * `<LazyMotion>` subtree.
4
+ */
5
+ export type FeatureBundle = {
6
+ /** Enables initial/animate/exit animation behavior. */
7
+ animations: true;
8
+ /** Enables hover, tap, focus, pan, and in-view gesture behavior. */
9
+ gestures?: true;
10
+ /** Enables drag gesture behavior. */
11
+ drag?: true;
12
+ /** Enables layout and shared-layout animation behavior. */
13
+ layout?: true;
14
+ };
15
+ /**
16
+ * Function form accepted by `<LazyMotion features>`.
17
+ *
18
+ * The function resolves to a feature bundle directly or to a module-like
19
+ * object with the bundle as its default export.
20
+ */
21
+ export type LazyFeatureBundle = () => Promise<FeatureBundle | {
22
+ default: FeatureBundle;
23
+ }>;
24
+ /**
25
+ * Returns whether a LazyMotion `features` value is an async loader.
26
+ *
27
+ * @param features - Feature bundle or loader passed to `<LazyMotion>`.
28
+ * @returns True when the features value should be invoked asynchronously.
29
+ */
30
+ export declare const isLazyFeatureBundle: (features: FeatureBundle | LazyFeatureBundle) => features is LazyFeatureBundle;
31
+ /**
32
+ * Normalizes an asynchronously loaded feature bundle.
33
+ *
34
+ * @param loaded - Resolved bundle or default-export module wrapper.
35
+ * @returns The concrete feature bundle.
36
+ */
37
+ export declare const normalizeLazyFeatureBundle: (loaded: FeatureBundle | {
38
+ default: FeatureBundle;
39
+ }) => FeatureBundle;
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Returns whether a LazyMotion `features` value is an async loader.
3
+ *
4
+ * @param features - Feature bundle or loader passed to `<LazyMotion>`.
5
+ * @returns True when the features value should be invoked asynchronously.
6
+ */
7
+ export const isLazyFeatureBundle = (features) => typeof features === 'function';
8
+ /**
9
+ * Normalizes an asynchronously loaded feature bundle.
10
+ *
11
+ * @param loaded - Resolved bundle or default-export module wrapper.
12
+ * @returns The concrete feature bundle.
13
+ */
14
+ export const normalizeLazyFeatureBundle = (loaded) => {
15
+ if ('default' in loaded)
16
+ return loaded.default;
17
+ return loaded;
18
+ };