solidjs-motion 0.1.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.
Files changed (49) hide show
  1. package/CHANGELOG.md +117 -0
  2. package/LICENSE +21 -0
  3. package/README.md +140 -0
  4. package/dist/index.d.ts +2 -0
  5. package/dist/index.js +2627 -0
  6. package/dist/index.js.map +1 -0
  7. package/dist/src/default-values.d.ts +6 -0
  8. package/dist/src/index.d.ts +16 -0
  9. package/dist/src/motion-config.d.ts +14 -0
  10. package/dist/src/motion-proxy.d.ts +103 -0
  11. package/dist/src/presence-context.d.ts +4 -0
  12. package/dist/src/presence.d.ts +95 -0
  13. package/dist/src/primitives/createDrag.d.ts +16 -0
  14. package/dist/src/primitives/createDragControls.d.ts +30 -0
  15. package/dist/src/primitives/createGestures.d.ts +8 -0
  16. package/dist/src/primitives/createInView.d.ts +51 -0
  17. package/dist/src/primitives/createMotion.d.ts +82 -0
  18. package/dist/src/primitives/createPan.d.ts +83 -0
  19. package/dist/src/primitives/createScroll.d.ts +40 -0
  20. package/dist/src/primitives/gesture-state.d.ts +108 -0
  21. package/dist/src/primitives/motion-value.d.ts +111 -0
  22. package/dist/src/primitives/value-registry.d.ts +34 -0
  23. package/dist/src/reduced-motion.d.ts +29 -0
  24. package/dist/src/style.d.ts +79 -0
  25. package/dist/src/types.d.ts +374 -0
  26. package/dist/src/use-motion.d.ts +35 -0
  27. package/dist/src/variants.d.ts +64 -0
  28. package/package.json +78 -0
  29. package/src/default-values.ts +52 -0
  30. package/src/index.ts +60 -0
  31. package/src/motion-config.tsx +37 -0
  32. package/src/motion-proxy.tsx +377 -0
  33. package/src/presence-context.ts +32 -0
  34. package/src/presence.tsx +466 -0
  35. package/src/primitives/createDrag.ts +670 -0
  36. package/src/primitives/createDragControls.ts +101 -0
  37. package/src/primitives/createGestures.ts +145 -0
  38. package/src/primitives/createInView.ts +124 -0
  39. package/src/primitives/createMotion.ts +638 -0
  40. package/src/primitives/createPan.ts +338 -0
  41. package/src/primitives/createScroll.ts +101 -0
  42. package/src/primitives/gesture-state.ts +772 -0
  43. package/src/primitives/motion-value.ts +328 -0
  44. package/src/primitives/value-registry.ts +114 -0
  45. package/src/reduced-motion.ts +51 -0
  46. package/src/style.ts +266 -0
  47. package/src/types.ts +538 -0
  48. package/src/use-motion.tsx +412 -0
  49. package/src/variants.ts +134 -0
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Look up the canonical fallback value for a property key. Returns the table
3
+ * value if known, else `null` — which motion's `animate()` interprets as
4
+ * "read from computed style at animation start."
5
+ */
6
+ export declare function getMotionDefault(key: string): number | null;
@@ -0,0 +1,16 @@
1
+ export { animate, inView, isMotionValue, motionValue, scroll, spring } from 'motion';
2
+ export { MotionConfig, MotionConfigContext, useMotionConfig } from './motion-config';
3
+ export { MOTION_OPT_KEYS, type Motion, motion } from './motion-proxy';
4
+ export { Presence, type PresenceProps, type UseAnimatePresenceOptions, type UseAnimatePresenceResult, useAnimatePresence, } from './presence';
5
+ export { PresenceContext, usePresenceContext } from './presence-context';
6
+ export { createDragControls } from './primitives/createDragControls';
7
+ export { type CreateInViewOptions, type CreateInViewResult, createInView, } from './primitives/createInView';
8
+ export { createMotion } from './primitives/createMotion';
9
+ export { type CreatePanOptions, type CreatePanResult, createPan, type PanAxisPair, } from './primitives/createPan';
10
+ export { type CreateScrollOptions, type CreateScrollResult, createScroll, } from './primitives/createScroll';
11
+ export { createMotionValue, createMotionValueEvent, createSpring, createTemplate, createTime, createTransform, createVelocity, toSignal, } from './primitives/motion-value';
12
+ export { createReducedMotion, shouldReduceMotion } from './reduced-motion';
13
+ export { targetToStyle } from './style';
14
+ export type * from './types';
15
+ export { useMotion } from './use-motion';
16
+ export { effectiveLabels, resolveVariant, useVariantContext, VariantContext } from './variants';
@@ -0,0 +1,14 @@
1
+ import { Context, JSX } from 'solid-js';
2
+ import { MotionConfigContextValue, MotionConfigProps } from './types';
3
+ export declare const MotionConfigContext: Context<MotionConfigContextValue>;
4
+ export declare function useMotionConfig(): MotionConfigContextValue;
5
+ /**
6
+ * Provider that flows reduced-motion mode, default transition, and CSP nonce
7
+ * to every descendant motion element.
8
+ *
9
+ * @example
10
+ * <MotionConfig reducedMotion="user" transition={{ duration: 0.4 }}>
11
+ * <App />
12
+ * </MotionConfig>
13
+ */
14
+ export declare function MotionConfig(props: MotionConfigProps): JSX.Element;
@@ -0,0 +1,103 @@
1
+ import { Component, JSX } from 'solid-js';
2
+ import { MotionOptions, MotionStyle } from './types';
3
+ /**
4
+ * The exhaustive list of keys that `motion.X` (and `motion.create`,
5
+ * landing next) route to `useMotion` rather than the underlying element.
6
+ * Anything not in this array falls through to `rest` and is spread onto
7
+ * the DOM element via Solid's reactive spread — keeping things like
8
+ * `class`, `onClick`, dynamic style, etc. reactive end-to-end.
9
+ *
10
+ * The `satisfies` clause ensures every entry IS a valid `MotionOptions`
11
+ * key (typos fail to compile). The `_ensureExhaustive` constant below
12
+ * asserts the converse — every `MotionOptions` key appears in this
13
+ * array — so adding a new option to types.ts without registering here
14
+ * fails to compile with the missing key surfaced in the error.
15
+ */
16
+ /**
17
+ * Union of every `MotionOptions` key the proxy splits off from element
18
+ * attributes. Hardcoded as a literal union — NOT derived from
19
+ * `typeof MOTION_OPT_KEYS[number]` — so JSR's "slow types" rule can
20
+ * resolve the public type without inferring it from a const.
21
+ *
22
+ * Two compile-time exhaustiveness directions guarantee consistency
23
+ * between this type and the runtime `MOTION_OPT_KEYS` array:
24
+ *
25
+ * 1. `_MissingMotionOptKeys` (below) verifies the union covers every
26
+ * key in `keyof MotionOptions`.
27
+ * 2. The `satisfies` clause on `MOTION_OPT_KEYS` verifies every array
28
+ * entry IS a `MotionOptKey` (typo-proof at the runtime layer).
29
+ *
30
+ * If `MotionOptions` grows a new key, both `_MissingMotionOptKeys`
31
+ * AND `splitProps` at runtime break — the type check surfaces the
32
+ * specific missing key by name.
33
+ */
34
+ export type MotionOptKey = "initial" | "animate" | "exit" | "hover" | "press" | "focus" | "inView" | "inViewOptions" | "drag" | "dragConstraints" | "dragElastic" | "dragMomentum" | "dragTransition" | "dragSnapToOrigin" | "dragControls" | "whileDrag" | "panThreshold" | "variants" | "custom" | "transition" | "onAnimationStart" | "onAnimationComplete" | "onAnimationCancel" | "onUpdate" | "onHoverStart" | "onHoverEnd" | "onPressStart" | "onPress" | "onPressCancel" | "onFocus" | "onBlur" | "onPanStart" | "onPan" | "onPanEnd" | "onViewportEnter" | "onViewportLeave" | "onDragStart" | "onDrag" | "onDragEnd" | "onDragTransitionEnd";
35
+ /**
36
+ * Frozen list of `MotionOptKey`s — fed to `splitProps` at every
37
+ * tag-component render to separate motion options from element
38
+ * attributes. The `satisfies` clause checks every entry against
39
+ * `MotionOptKey` at compile time so typos / drift between the union
40
+ * and the array surface as errors.
41
+ */
42
+ export declare const MOTION_OPT_KEYS: readonly MotionOptKey[];
43
+ /**
44
+ * The shape of the `motion` proxy: every HTML/SVG intrinsic element name
45
+ * maps to a typed `Component` whose props are that element's native
46
+ * attribute set intersected with {@link MotionOptions}.
47
+ *
48
+ * The intersected `{ create: ... }` member adds the HOC entry point. The
49
+ * intersection's explicit `create` field wins over the mapped type's
50
+ * lookup (and there's no HTML/SVG tag named `create`), so `motion.create`
51
+ * is unambiguously typed as the HOC.
52
+ */
53
+ export type Motion = {
54
+ [Tag in keyof JSX.IntrinsicElements]: Component<Omit<JSX.IntrinsicElements[Tag], "style"> & {
55
+ style?: MotionStyle;
56
+ } & MotionOptions>;
57
+ } & {
58
+ /**
59
+ * Wrap a custom Component with motion's behavior. The wrapped Component
60
+ * must forward props (specifically `ref` and `style`) to a single DOM
61
+ * element root — either by spreading `{...props}` on its root or by
62
+ * explicitly setting `ref={props.ref}` and `style={props.style}`. Solid
63
+ * doesn't have `forwardRef`; the contract is enforced by convention and
64
+ * a dev-mode runtime warning if motion's ref never reaches the DOM.
65
+ *
66
+ * @example
67
+ * ```tsx
68
+ * function MyCard(props) {
69
+ * return <div {...props}>{props.children}</div>
70
+ * }
71
+ * const Animated = motion.create(MyCard)
72
+ * <Animated animate={{ x: 100 }} hover={{ scale: 1.05 }} class="card" />
73
+ * ```
74
+ */
75
+ create: <P extends Record<string, any>>(Component: Component<P>) => Component<P & MotionOptions>;
76
+ };
77
+ /**
78
+ * `motion` — the indexable proxy. Every property access returns a cached
79
+ * motion-aware component for the given HTML/SVG tag. The reserved
80
+ * `motion.create` key returns the HOC entry point.
81
+ *
82
+ * @example HTML element
83
+ * ```tsx
84
+ * <motion.div animate={{ x: 100 }} hover={{ scale: 1.05 }}>
85
+ * draggable card
86
+ * </motion.div>
87
+ * ```
88
+ *
89
+ * @example SVG element (handled transparently via <Dynamic>)
90
+ * ```tsx
91
+ * <motion.path d="M0 0 L100 100" animate={{ pathLength: 1 }} />
92
+ * ```
93
+ *
94
+ * @example Wrapping a custom Component via the HOC
95
+ * ```tsx
96
+ * const Animated = motion.create(MyCard)
97
+ * <Animated animate={{ scale: 1.05 }} class="my-card" />
98
+ * ```
99
+ *
100
+ * Non-string keys (Symbols, well-known properties) return `undefined` so
101
+ * debugging tools and `typeof` checks see a sane shape.
102
+ */
103
+ export declare const motion: Motion;
@@ -0,0 +1,4 @@
1
+ import { Context } from 'solid-js';
2
+ import { PresenceContextValue } from './types';
3
+ export declare const PresenceContext: Context<PresenceContextValue>;
4
+ export declare function usePresenceContext(): PresenceContextValue;
@@ -0,0 +1,95 @@
1
+ import { Component, JSX } from 'solid-js';
2
+ export type PresenceProps = {
3
+ /**
4
+ * Exit/enter coordination.
5
+ * - `"sync"` (default) — exit and enter overlap (transition-group's
6
+ * `parallel`). Best for list animations and independent items.
7
+ * - `"wait"` — old child fully exits before the new one enters
8
+ * (transition-group's `out-in`). Single-child only; ignored (with a
9
+ * dev-mode warning) when wrapping a list.
10
+ */
11
+ mode?: "sync" | "wait";
12
+ /**
13
+ * Animate children on first mount. Defaults to `true` (matches motion-
14
+ * react). Set `false` to suppress the entry animation for the very first
15
+ * child(ren); subsequent mounts mid-life still animate.
16
+ */
17
+ initial?: boolean;
18
+ /**
19
+ * List-path only — controls what transition-group does with an exiting
20
+ * element while its `exit` animation is playing. Forwarded directly to
21
+ * `@solid-primitives/transition-group`'s `createListTransition`.
22
+ *
23
+ * - `"move-to-end"` (default) — the exiting element is appended to the
24
+ * end of the rendered array so its DOM position changes during exit.
25
+ * Fine for grids/cascades; surprising for vertically-stacked toasts
26
+ * because surviving siblings JUMP up while the dismissed item is
27
+ * still fading out below them.
28
+ * - `"keep-index"` — the exiting element stays at its original index
29
+ * until exit completes. Surviving siblings don't reflow until the
30
+ * slot is released. Best default for notification stacks.
31
+ * - `"remove"` — no exit transition; the element is gone from the
32
+ * rendered array immediately. Useful when the exit is purely visual
33
+ * on the child (e.g., it self-animates via opacity transitions
34
+ * instead of `exit`).
35
+ */
36
+ exitMethod?: "move-to-end" | "keep-index" | "remove";
37
+ children: JSX.Element;
38
+ };
39
+ /**
40
+ * Wraps a conditional or iterated JSX subtree and runs the descendants'
41
+ * `exit` targets before they unmount. Matches motion-react's
42
+ * `<AnimatePresence>` shape but with Solid's `<Show>` / `<For>` /
43
+ * `<Index>` patterns instead of conditional children.
44
+ *
45
+ * Nested motion children are first-class: when an ancestor unmounts,
46
+ * Presence walks the subtree from each resolved child and fires every
47
+ * registered `runExit` it finds in parallel — including motion children
48
+ * nested inside plain wrappers, or descendants whose `exit` label was
49
+ * cascaded down via `m.Provider`. Each motion descendant animates with
50
+ * its own variant/target; transition-group only releases the DOM once
51
+ * the combined `Promise.all` settles. Mirrors motion-react's behavior
52
+ * where a `<motion.div exit={...}>` inside an `<AnimatePresence>` boundary
53
+ * animates correctly regardless of depth.
54
+ *
55
+ * @example Single (conditional unmount)
56
+ * <Presence>
57
+ * <Show when={open()}>
58
+ * <motion.div initial={{ opacity: 0 }} animate={{ opacity: 1 }} exit={{ opacity: 0 }}>
59
+ * saved
60
+ * </motion.div>
61
+ * </Show>
62
+ * </Presence>
63
+ *
64
+ * @example List (items entering and exiting independently)
65
+ * <Presence>
66
+ * <For each={items()}>
67
+ * {(item) => (
68
+ * <motion.li exit={{ opacity: 0, x: 20 }}>{item.text}</motion.li>
69
+ * )}
70
+ * </For>
71
+ * </Presence>
72
+ */
73
+ export declare const Presence: Component<PresenceProps>;
74
+ export type UseAnimatePresenceOptions = {
75
+ /** Same semantics as `<Presence initial>`. Defaults to `true`. */
76
+ initial?: boolean;
77
+ };
78
+ export type UseAnimatePresenceResult = {
79
+ /**
80
+ * Provider component to wrap your conditional rendering. Every motion
81
+ * descendant inside this Provider registers with the hook's internal
82
+ * registry and is reachable via `exit()`.
83
+ */
84
+ Provider: Component<{
85
+ children: JSX.Element;
86
+ }>;
87
+ /**
88
+ * Trigger exit on every motion child currently registered with this
89
+ * hook. Resolves when all exit animations have settled. Calling `exit()`
90
+ * does NOT unmount anything — the caller is responsible for flipping
91
+ * their mount signal once the promise resolves.
92
+ */
93
+ exit: () => Promise<void>;
94
+ };
95
+ export declare function useAnimatePresence(options?: UseAnimatePresenceOptions): UseAnimatePresenceResult;
@@ -0,0 +1,16 @@
1
+ import { MotionOptions } from '../types';
2
+ import { SetActive } from './gesture-state';
3
+ /**
4
+ * Bind pointer-driven drag to an element. Layers on top of createPan for the
5
+ * pointer session; adds transform writes, body styles, pointer capture, and
6
+ * state-machine activation.
7
+ *
8
+ * Drag is enabled when `opts.drag` is truthy (`true`, `"x"`, or `"y"`).
9
+ * createDrag always wires the pointer session — the enable check is per-
10
+ * gesture-start, so toggling `opts.drag` on/off doesn't churn listeners.
11
+ *
12
+ * Phase 2 Commit 6 — Stage 2 scope: VE bootstrap, translation, axis lock,
13
+ * body/pointer styles, callbacks, cleanup. Constraints + elastic resistance
14
+ * land in Stage 3; momentum + dragSnapToOrigin in Stage 4.
15
+ */
16
+ export declare function createDrag(el: HTMLElement, getOpts: () => MotionOptions, setActive: SetActive): void;
@@ -0,0 +1,30 @@
1
+ import { DragControls } from '../types';
2
+ /**
3
+ * Symbol used to attach the registration method on the controls object.
4
+ * `createDrag` imports this symbol to find/register its handler. Userland
5
+ * code never touches it — exported only for library-internal use.
6
+ */
7
+ export declare const DRAG_CONTROLS_REGISTER: unique symbol;
8
+ /**
9
+ * Create a controls instance for imperatively starting a drag on a motion
10
+ * element from a different element (e.g., a drag-handle button).
11
+ *
12
+ * Pattern (Q9):
13
+ *
14
+ * @example
15
+ * function Card() {
16
+ * const controls = createDragControls()
17
+ * const m = useMotion({ drag: "y", dragControls: controls })
18
+ * return (
19
+ * <div {...m()}>
20
+ * <button onPointerDown={(e) => controls.start(e)}>handle</button>
21
+ * Card body
22
+ * </div>
23
+ * )
24
+ * }
25
+ *
26
+ * The handle's pointerdown fires `controls.start(event)`. createDrag is
27
+ * registered with the controls and translates the call into a pan-session
28
+ * synthesized from the handle's event, bypassing the threshold gate.
29
+ */
30
+ export declare function createDragControls(): DragControls;
@@ -0,0 +1,8 @@
1
+ import { MotionElement, MotionOptions } from '../types';
2
+ import { SetActive } from './gesture-state';
3
+ /**
4
+ * Bind pointer-event-driven gestures (hover, press) to the motion element.
5
+ * Toggles the state machine's `whileHover` / `whilePress` flags and forwards
6
+ * events to the user's `MotionCallbacks`.
7
+ */
8
+ export declare function createGestures(el: MotionElement, getOpts: () => MotionOptions, setActive: SetActive): void;
@@ -0,0 +1,51 @@
1
+ import { Accessor } from 'solid-js';
2
+ import { ViewportOptions } from '../types';
3
+ export type CreateInViewOptions = ViewportOptions & {
4
+ /**
5
+ * Fires with the raw {@link IntersectionObserverEntry} on every
6
+ * visibility transition (enter AND leave). Convenience hook for callers
7
+ * who prefer event-driven access to the entry — the entry is also
8
+ * available reactively via the returned `view.entry()` accessor.
9
+ */
10
+ onChange?: (entry: IntersectionObserverEntry) => void;
11
+ };
12
+ /** Returned by {@link createInView}. Two Solid Accessors — call them to track. */
13
+ export type CreateInViewResult = {
14
+ /** Solid Accessor; `true` while the element intersects the viewport per the configured threshold. */
15
+ isInView: Accessor<boolean>;
16
+ /** Solid Accessor; the most recent {@link IntersectionObserverEntry}, or `null` before any. */
17
+ entry: Accessor<IntersectionObserverEntry | null>;
18
+ };
19
+ /**
20
+ * Observe an element via {@link IntersectionObserver} and expose its
21
+ * in-view state as a pair of Solid Accessors.
22
+ *
23
+ * Pass a ref-style accessor that returns the element. The observer
24
+ * attaches once the accessor returns a non-null element and re-attaches
25
+ * if it changes. The observer is disconnected on owner disposal.
26
+ *
27
+ * Options can be a static object or a function form (matching `useMotion`
28
+ * and `createPan`'s convention). The function form is tracked inside the
29
+ * effect — option changes (e.g., switching `root`) re-attach the observer.
30
+ *
31
+ * @example Static options
32
+ * const [el, setEl] = createSignal<HTMLElement>()
33
+ * const view = createInView(el, { once: true })
34
+ * createEffect(() => {
35
+ * if (view.isInView()) console.log("now in view")
36
+ * })
37
+ *
38
+ * @example Function-form options (reactive)
39
+ * const [root, setRoot] = createSignal<HTMLElement>()
40
+ * const view = createInView(el, () => ({ root, margin: "100px" }))
41
+ *
42
+ * @example Reading the raw entry reactively
43
+ * const view = createInView(el)
44
+ * createEffect(() => {
45
+ * const e = view.entry()
46
+ * if (e) console.log("ratio:", e.intersectionRatio)
47
+ * })
48
+ *
49
+ * <div ref={setEl}>watch me</div>
50
+ */
51
+ export declare function createInView(ref: () => Element | null | undefined, options?: CreateInViewOptions | (() => CreateInViewOptions)): CreateInViewResult;
@@ -0,0 +1,82 @@
1
+ import { MotionValue } from 'motion';
2
+ import { AnimateValue, MotionElement, MotionOptions, Target, Transition, VariantContextValue, VariantLabels, Variants } from '../types';
3
+ import { ActiveStoreTuple } from './gesture-state';
4
+ /**
5
+ * Detect whether an animate-value is a variant name (string or string[]) vs.
6
+ * an explicit target object. Returns the labels or undefined.
7
+ */
8
+ export declare function asVariantLabels(value: AnimateValue | undefined): VariantLabels | undefined;
9
+ /**
10
+ * Resolve a per-state animate value into a {@link Target}. Implements the
11
+ * Q4 sub-2 priority table:
12
+ *
13
+ * - explicit Target object → use as-is (parent context ignored)
14
+ * - variant name → look up in own variants only (no cascade)
15
+ * - undefined → fall back to parent context's variant name, then look up in
16
+ * own variants
17
+ */
18
+ export declare function resolveTarget(ownValue: AnimateValue | undefined, ownVariants: Variants | undefined, parentLabel: VariantLabels | undefined, custom: unknown): Target | null;
19
+ /**
20
+ * Merge transition specs in priority order: MotionConfig default <
21
+ * user's `transition` < per-target `transition`. When reduced motion is
22
+ * active, returns `{ duration: 0 }` and drops everything else (Q11 sub-4).
23
+ */
24
+ export declare function mergeTransition(configDefault: Transition | undefined, ownTransition: Transition | undefined, perTargetTransition: Transition | undefined, reduced: boolean): Transition;
25
+ /**
26
+ * Apply a static target to an element's inline style before paint. Used on
27
+ * mount when no SSR style was emitted. The ref callback fires before the
28
+ * browser yields, so this avoids a frame of flicker.
29
+ */
30
+ declare function applyStaticStyle(el: MotionElement, target: Target): void;
31
+ export type CreateMotionConfig = {
32
+ /**
33
+ * When true, `createMotion` skips applying the initial style — the server
34
+ * already emitted it inline, and re-applying on hydration would shift the
35
+ * paint. Detected via the `data-motion-hydrated` marker in useMotion.
36
+ */
37
+ initialAppliedBySSR?: boolean;
38
+ /**
39
+ * Q4 — useMotion lifts the gesture-state active store one level up so its
40
+ * `myVariantCtx` can read the same flags it propagates to descendants.
41
+ * When omitted (standalone createMotion use), the state machine creates
42
+ * its own internal store.
43
+ */
44
+ activeStore?: ActiveStoreTuple;
45
+ /**
46
+ * Q4 follow-up — useMotion passes a shadowed parent context here so the
47
+ * controlling-variants check is applied uniformly. When omitted, createMotion
48
+ * falls back to `useVariantContext()` directly (the standalone path).
49
+ */
50
+ parentContext?: VariantContextValue;
51
+ /**
52
+ * MV-in-style Stage 2 — useMotion scrapes `MotionValue`-valued keys out of
53
+ * the user's `style` prop and passes them here. createMotion registers
54
+ * each as an external (user-owned) entry in the value registry and
55
+ * subscribes a writer that re-composes `el.style` from the registry
56
+ * snapshot on every change.
57
+ */
58
+ styleMotionValues?: Map<string, MotionValue<unknown>>;
59
+ /**
60
+ * MV-in-style Stage 4 — static transform-shortcut entries in the user's
61
+ * `style` (e.g., `style={{ x: 10, scale: mv }}`'s `x: 10`). useMotion scrapes
62
+ * these alongside MVs so createMotion can seed transient registry entries
63
+ * for them. Without this, the writer would drop the static keys on every
64
+ * recompose since they wouldn't appear in the registry.
65
+ *
66
+ * Map values are the resolved leaf (number or string) — no MVs, no
67
+ * keyframe arrays, no accessor functions. useMotion runs the
68
+ * `snapshotValue` reduction before passing them in.
69
+ */
70
+ styleStaticTransforms?: Map<string, number | string>;
71
+ };
72
+ /**
73
+ * The imperative primitive: bind an element to a reactive motion-options
74
+ * source. Caller is responsible for keeping the element alive (refs in a
75
+ * component, drag controls, etc.).
76
+ *
77
+ * Phase 1 scope: animate + initial + transition + lifecycle hooks +
78
+ * reduced-motion override + presence registration. Phase 2 layers gesture
79
+ * states (hover/press/focus/inView) and drag on top.
80
+ */
81
+ export declare function createMotion(el: MotionElement, getOpts: () => MotionOptions, config?: CreateMotionConfig): void;
82
+ export { applyStaticStyle };
@@ -0,0 +1,83 @@
1
+ import { Accessor } from 'solid-js';
2
+ import { MotionValueAccessor, PanInfo } from '../types';
3
+ export type CreatePanOptions = {
4
+ /** Fires once after pointer movement crosses the threshold. */
5
+ onPanStart?: (event: PointerEvent, info: PanInfo) => void;
6
+ /** Fires on every pointermove after onPanStart, until pointerup/cancel. */
7
+ onPan?: (event: PointerEvent, info: PanInfo) => void;
8
+ /**
9
+ * Fires on pointerup OR pointercancel after onPanStart has fired.
10
+ * If the pointer was released before the threshold was crossed, onPanEnd
11
+ * is NOT fired (no pan ever happened).
12
+ */
13
+ onPanEnd?: (event: PointerEvent, info: PanInfo) => void;
14
+ /**
15
+ * Minimum cumulative offset (in px) before onPanStart fires. Distinguishes
16
+ * pan from click. Default: 3px (motion's default).
17
+ */
18
+ threshold?: number;
19
+ };
20
+ /** Per-axis pair of {@link MotionValueAccessor}s — `pan.point`, `pan.delta`, etc. */
21
+ export type PanAxisPair = {
22
+ x: MotionValueAccessor<number>;
23
+ y: MotionValueAccessor<number>;
24
+ };
25
+ /**
26
+ * Returned by {@link createPan}. `isPanning` is a plain Accessor (booleans
27
+ * aren't animate-able). The four numeric pairs are MotionValueAccessors,
28
+ * each composable with `animate()`, `createTransform`, and `useMotion`.
29
+ */
30
+ export type CreatePanResult = {
31
+ isPanning: Accessor<boolean>;
32
+ point: PanAxisPair;
33
+ delta: PanAxisPair;
34
+ offset: PanAxisPair;
35
+ velocity: PanAxisPair;
36
+ };
37
+ /**
38
+ * Observe pointer-driven pan gestures on an element.
39
+ *
40
+ * Returns `{ isPanning, point, delta, offset, velocity }`:
41
+ *
42
+ * - `pan.isPanning()` — Solid Accessor; `true` between onPanStart and onPanEnd.
43
+ * - `pan.point.x`, `pan.point.y` — current pointer position in client coords.
44
+ * Each is a {@link MotionValueAccessor}: call `pan.point.x()` for a tracked
45
+ * read, `pan.point.x.get()` for an untracked snapshot, and pass it directly
46
+ * to `animate()`, `createTransform`, or `useMotion` targets.
47
+ * - `pan.delta.x/y` — delta since last pointermove.
48
+ * - `pan.offset.x/y` — cumulative offset since the current pointerdown.
49
+ * - `pan.velocity.x/y` — sliding-window velocity in px/sec.
50
+ *
51
+ * Fields update from `pointerdown` forward (including pre-threshold moves)
52
+ * — gate reads on `pan.isPanning()` if you only care about real pans.
53
+ *
54
+ * The `options` argument accepts either a static object or a function form
55
+ * (matching `useMotion`'s convention). The function form is read INSIDE
56
+ * each pointer-event handler, so reactive option changes apply on the next
57
+ * relevant event without re-attaching listeners.
58
+ *
59
+ * @example Static options
60
+ * const pan = createPan(el, {
61
+ * onPanStart: (e, info) => console.log("start", info.point),
62
+ * threshold: 3,
63
+ * })
64
+ *
65
+ * @example Reactive options (function form — signals tracked)
66
+ * const [threshold, setThreshold] = createSignal(3)
67
+ * const pan = createPan(el, () => ({
68
+ * threshold: threshold(),
69
+ * onPanStart: (e, info) => console.log(info),
70
+ * }))
71
+ *
72
+ * @example Composing pan.point.x with createTransform
73
+ * const pan = createPan(el)
74
+ * const rotation = createTransform(pan.point.x, [0, 300], [0, 90])
75
+ * <div ref={setEl} style={{ transform: `rotate(${rotation()}deg)` }} />
76
+ *
77
+ * @example Reading reactively in JSX
78
+ * const pan = createPan(el)
79
+ * <Show when={pan.isPanning()}>
80
+ * Position: {pan.point.x()}, {pan.point.y()}
81
+ * </Show>
82
+ */
83
+ export declare function createPan(ref: () => HTMLElement | null | undefined, options?: CreatePanOptions | (() => CreatePanOptions)): CreatePanResult;
@@ -0,0 +1,40 @@
1
+ import { MotionValueAccessor } from '../types';
2
+ type ScrollOffset = any[];
3
+ export type CreateScrollOptions = {
4
+ /** Accessor returning the scroll container element. Defaults to window. */
5
+ container?: () => Element | null;
6
+ /** Accessor returning the scroll target. Defaults to the container itself. */
7
+ target?: () => Element | null;
8
+ /** Primary scroll axis (both axes are still populated regardless). */
9
+ axis?: "x" | "y";
10
+ /** Intersection offsets controlling when progress reaches 0/1. */
11
+ offset?: ScrollOffset;
12
+ };
13
+ export type CreateScrollResult = {
14
+ /** Current scroll-x position in px. Callable: `scrollX()` for reactive read. */
15
+ scrollX: MotionValueAccessor<number>;
16
+ /** Current scroll-y position in px. Callable: `scrollY()` for reactive read. */
17
+ scrollY: MotionValueAccessor<number>;
18
+ /** Normalized scroll-x progress in `[0, 1]` (or `[0, n]` for multi-offset). */
19
+ scrollXProgress: MotionValueAccessor<number>;
20
+ /** Normalized scroll-y progress in `[0, 1]`. */
21
+ scrollYProgress: MotionValueAccessor<number>;
22
+ };
23
+ /**
24
+ * Bind four {@link MotionValueAccessor}s to a scroll source. Mirrors
25
+ * motion/react's `useScroll`; defaults to the window when no container is
26
+ * supplied. Each returned value is callable as a Solid Accessor AND has the
27
+ * full MotionValue surface, so it composes with `useMotion`'s target,
28
+ * `animate()`, `createTransform`, and direct JSX reactivity.
29
+ *
30
+ * @example
31
+ * const { scrollY, scrollYProgress } = createScroll()
32
+ * const opacity = createTransform(scrollYProgress, [0, 1], [1, 0])
33
+ *
34
+ * @example
35
+ * const [el, setEl] = createSignal<HTMLElement>()
36
+ * const { scrollY } = createScroll({ container: el })
37
+ * <div ref={setEl} style={{ overflow: "auto" }}>...</div>
38
+ */
39
+ export declare function createScroll(options?: CreateScrollOptions): CreateScrollResult;
40
+ export {};