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.
- package/CHANGELOG.md +117 -0
- package/LICENSE +21 -0
- package/README.md +140 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2627 -0
- package/dist/index.js.map +1 -0
- package/dist/src/default-values.d.ts +6 -0
- package/dist/src/index.d.ts +16 -0
- package/dist/src/motion-config.d.ts +14 -0
- package/dist/src/motion-proxy.d.ts +103 -0
- package/dist/src/presence-context.d.ts +4 -0
- package/dist/src/presence.d.ts +95 -0
- package/dist/src/primitives/createDrag.d.ts +16 -0
- package/dist/src/primitives/createDragControls.d.ts +30 -0
- package/dist/src/primitives/createGestures.d.ts +8 -0
- package/dist/src/primitives/createInView.d.ts +51 -0
- package/dist/src/primitives/createMotion.d.ts +82 -0
- package/dist/src/primitives/createPan.d.ts +83 -0
- package/dist/src/primitives/createScroll.d.ts +40 -0
- package/dist/src/primitives/gesture-state.d.ts +108 -0
- package/dist/src/primitives/motion-value.d.ts +111 -0
- package/dist/src/primitives/value-registry.d.ts +34 -0
- package/dist/src/reduced-motion.d.ts +29 -0
- package/dist/src/style.d.ts +79 -0
- package/dist/src/types.d.ts +374 -0
- package/dist/src/use-motion.d.ts +35 -0
- package/dist/src/variants.d.ts +64 -0
- package/package.json +78 -0
- package/src/default-values.ts +52 -0
- package/src/index.ts +60 -0
- package/src/motion-config.tsx +37 -0
- package/src/motion-proxy.tsx +377 -0
- package/src/presence-context.ts +32 -0
- package/src/presence.tsx +466 -0
- package/src/primitives/createDrag.ts +670 -0
- package/src/primitives/createDragControls.ts +101 -0
- package/src/primitives/createGestures.ts +145 -0
- package/src/primitives/createInView.ts +124 -0
- package/src/primitives/createMotion.ts +638 -0
- package/src/primitives/createPan.ts +338 -0
- package/src/primitives/createScroll.ts +101 -0
- package/src/primitives/gesture-state.ts +772 -0
- package/src/primitives/motion-value.ts +328 -0
- package/src/primitives/value-registry.ts +114 -0
- package/src/reduced-motion.ts +51 -0
- package/src/style.ts +266 -0
- package/src/types.ts +538 -0
- package/src/use-motion.tsx +412 -0
- package/src/variants.ts +134 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,538 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AnimationPlaybackControls,
|
|
3
|
+
MotionValue,
|
|
4
|
+
PanInfo,
|
|
5
|
+
PressGestureInfo,
|
|
6
|
+
ResolvedValues,
|
|
7
|
+
SpringOptions,
|
|
8
|
+
Transition,
|
|
9
|
+
} from "motion"
|
|
10
|
+
import type { InertiaOptions } from "motion-dom"
|
|
11
|
+
import type { Accessor, Component, JSX } from "solid-js"
|
|
12
|
+
|
|
13
|
+
// ---------------------------------------------------------------------------
|
|
14
|
+
// Re-exports from motion. These are the upstream types we adopt as-is so the
|
|
15
|
+
// migration story stays clean and our wrappers don't drift from the engine.
|
|
16
|
+
// ---------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
export type {
|
|
19
|
+
AnimationPlaybackControls,
|
|
20
|
+
MotionValue,
|
|
21
|
+
PanInfo,
|
|
22
|
+
ResolvedValues,
|
|
23
|
+
SpringOptions,
|
|
24
|
+
Transition,
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Callable {@link MotionValue} — has every MotionValue method (`.get`, `.set`,
|
|
29
|
+
* `.jump`, `.on`, `.getVelocity`, ...) AND can be invoked as a Solid-tracked
|
|
30
|
+
* Accessor. Returned by `createMotionValue`, `createTransform`,
|
|
31
|
+
* `createSpring`, `createTime`, `createVelocity`, and `createTemplate`.
|
|
32
|
+
*
|
|
33
|
+
* - `mv()` — Solid-tracked read (use in JSX, `createEffect`, `createMemo`)
|
|
34
|
+
* - `mv.get()` — sync untracked read (motion engine's API; matches motion/react)
|
|
35
|
+
* - `mv.set(v)` / `mv.jump(v)` — imperative writes (also trigger the Solid
|
|
36
|
+
* signal via an internal `mv.on("change", ...)` bridge)
|
|
37
|
+
*
|
|
38
|
+
* Passes motion's `isMotionValue` (duck-typed on `.getVelocity`), so the same
|
|
39
|
+
* value can be used as a target in `useMotion({ animate: { x: mv } })` or as
|
|
40
|
+
* the target of `animate(mv, 100)`.
|
|
41
|
+
*/
|
|
42
|
+
export type MotionValueAccessor<T> = MotionValue<T> & (() => T)
|
|
43
|
+
|
|
44
|
+
/** Per-press info delivered to onPressStart / onPress / onPressCancel. */
|
|
45
|
+
export type PressInfo = PressGestureInfo
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Options for {@link DragControls.start}.
|
|
49
|
+
*
|
|
50
|
+
* Only `snapToCursor` is exposed for v0.1 (Q9b — anything else can be added
|
|
51
|
+
* non-breakingly later).
|
|
52
|
+
*/
|
|
53
|
+
export type DragControlsStartOptions = {
|
|
54
|
+
/**
|
|
55
|
+
* When true, center the dragged element under the pointer on drag-start.
|
|
56
|
+
* Useful for "drag handle hand-off" patterns where the handle and the
|
|
57
|
+
* dragged element are distinct — the user clicks the handle and the
|
|
58
|
+
* dragged element jumps to follow the pointer.
|
|
59
|
+
*/
|
|
60
|
+
snapToCursor?: boolean
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Imperative drag controls returned from `createDragControls()`. Defined
|
|
65
|
+
* locally because motion's public `motion` export doesn't surface its
|
|
66
|
+
* DragControls class type.
|
|
67
|
+
*
|
|
68
|
+
* Usage (Q9): one controls instance binds to one motion element via
|
|
69
|
+
* `dragControls: controls` in MotionOptions. A separate UI element (e.g., a
|
|
70
|
+
* drag-handle button) captures the pointer event and forwards it via
|
|
71
|
+
* `controls.start(event)`, decoupling the drag-listener element from the
|
|
72
|
+
* element that actually moves.
|
|
73
|
+
*/
|
|
74
|
+
export type DragControls = {
|
|
75
|
+
/**
|
|
76
|
+
* Begin a drag from an externally-captured pointer event. Bypasses
|
|
77
|
+
* createDrag's threshold gate — drag starts immediately at the event's
|
|
78
|
+
* coordinates (the user explicitly invoked, so no "did they really
|
|
79
|
+
* mean it" hysteresis is needed).
|
|
80
|
+
*
|
|
81
|
+
* No-op when no motion element is currently registered with this
|
|
82
|
+
* controls instance.
|
|
83
|
+
*/
|
|
84
|
+
start: (event: PointerEvent, options?: DragControlsStartOptions) => void
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// ---------------------------------------------------------------------------
|
|
88
|
+
// Value types — a property's value can be a literal, a MotionValue, or a Solid
|
|
89
|
+
// Accessor. Motion's engine subscribes to MotionValues natively; Accessors are
|
|
90
|
+
// snapshotted at effect time.
|
|
91
|
+
// ---------------------------------------------------------------------------
|
|
92
|
+
|
|
93
|
+
type Numeric = number | MotionValue<number> | Accessor<number>
|
|
94
|
+
type Stringish = string | MotionValue<string> | Accessor<string>
|
|
95
|
+
type AnyValue = Numeric | Stringish
|
|
96
|
+
|
|
97
|
+
/** A property value or an array of values (keyframe sequence). */
|
|
98
|
+
export type Keyframes<T> = T | T[]
|
|
99
|
+
|
|
100
|
+
// ---------------------------------------------------------------------------
|
|
101
|
+
// Target — strict transform shorthand + index signature for arbitrary CSS.
|
|
102
|
+
// Plain values, MotionValues, and Accessors all accepted. A `transition` key
|
|
103
|
+
// can also be present for per-target transition overrides.
|
|
104
|
+
// ---------------------------------------------------------------------------
|
|
105
|
+
|
|
106
|
+
export type Target = {
|
|
107
|
+
// Translate shorthand (px when number)
|
|
108
|
+
x?: Keyframes<Numeric | Stringish>
|
|
109
|
+
y?: Keyframes<Numeric | Stringish>
|
|
110
|
+
z?: Keyframes<Numeric | Stringish>
|
|
111
|
+
|
|
112
|
+
// Scale (dimensionless)
|
|
113
|
+
scale?: Keyframes<Numeric>
|
|
114
|
+
scaleX?: Keyframes<Numeric>
|
|
115
|
+
scaleY?: Keyframes<Numeric>
|
|
116
|
+
scaleZ?: Keyframes<Numeric>
|
|
117
|
+
|
|
118
|
+
// Rotate (deg when number)
|
|
119
|
+
rotate?: Keyframes<Numeric | Stringish>
|
|
120
|
+
rotateX?: Keyframes<Numeric | Stringish>
|
|
121
|
+
rotateY?: Keyframes<Numeric | Stringish>
|
|
122
|
+
rotateZ?: Keyframes<Numeric | Stringish>
|
|
123
|
+
|
|
124
|
+
// Skew (deg when number)
|
|
125
|
+
skew?: Keyframes<Numeric | Stringish>
|
|
126
|
+
skewX?: Keyframes<Numeric | Stringish>
|
|
127
|
+
skewY?: Keyframes<Numeric | Stringish>
|
|
128
|
+
|
|
129
|
+
// Transform-related but not shorthand
|
|
130
|
+
transformPerspective?: Keyframes<Numeric>
|
|
131
|
+
transformOrigin?: Stringish
|
|
132
|
+
|
|
133
|
+
// Common CSS with narrowed types
|
|
134
|
+
opacity?: Keyframes<Numeric>
|
|
135
|
+
zIndex?: Keyframes<Numeric>
|
|
136
|
+
|
|
137
|
+
// Per-target transition override (Q3 sub-3)
|
|
138
|
+
transition?: Transition
|
|
139
|
+
|
|
140
|
+
// Catch-all for arbitrary CSS (incl. CSS variables like "--foo").
|
|
141
|
+
// Index signature must accept every named property above, plus Transition.
|
|
142
|
+
[key: string]: Keyframes<AnyValue> | Transition | undefined
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
// ---------------------------------------------------------------------------
|
|
146
|
+
// Variants
|
|
147
|
+
// ---------------------------------------------------------------------------
|
|
148
|
+
|
|
149
|
+
/** A single variant — a target shape, or a function producing one from `custom`. */
|
|
150
|
+
export type Variant = Target | ((custom: unknown) => Target)
|
|
151
|
+
|
|
152
|
+
/** A named map of variants. */
|
|
153
|
+
export type Variants = Record<string, Variant>
|
|
154
|
+
|
|
155
|
+
/** What can be passed where a variant name is expected (string, array of strings). */
|
|
156
|
+
export type VariantLabels = string | string[]
|
|
157
|
+
|
|
158
|
+
/** What can drive an animation: a target object, a variant name, or an array of names. */
|
|
159
|
+
export type AnimateValue = Target | VariantLabels
|
|
160
|
+
|
|
161
|
+
// ---------------------------------------------------------------------------
|
|
162
|
+
// Viewport / IntersectionObserver options for inView gesture and createInView.
|
|
163
|
+
// ---------------------------------------------------------------------------
|
|
164
|
+
|
|
165
|
+
export type ViewportOptions = {
|
|
166
|
+
/** Stop observing after first entry (default false). */
|
|
167
|
+
once?: boolean
|
|
168
|
+
/** rootMargin string passed to IntersectionObserver. */
|
|
169
|
+
margin?: string
|
|
170
|
+
/**
|
|
171
|
+
* Threshold(s) at which IntersectionObserver fires its callback.
|
|
172
|
+
*
|
|
173
|
+
* - `"some"` (default) — fires the moment any pixel intersects.
|
|
174
|
+
* - `"all"` — fires only when fully visible.
|
|
175
|
+
* - `number` 0–1 — single threshold; fires at that intersection ratio.
|
|
176
|
+
* - `number[]` — an array of thresholds; fires at each crossing. Use this
|
|
177
|
+
* for continuous `intersectionRatio` updates (e.g., for scroll-linked
|
|
178
|
+
* fades). With a single threshold the observer is silent between
|
|
179
|
+
* crossings, so `entry.intersectionRatio` stays stale.
|
|
180
|
+
*/
|
|
181
|
+
amount?: "some" | "all" | number | number[]
|
|
182
|
+
/** Solid-style accessor returning the root element; defaults to viewport. */
|
|
183
|
+
root?: () => Element | null
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
// ---------------------------------------------------------------------------
|
|
187
|
+
// Drag options
|
|
188
|
+
// ---------------------------------------------------------------------------
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Drag bounds (Q8). Three shapes:
|
|
192
|
+
*
|
|
193
|
+
* - **Numeric** (`{ top, left, right, bottom }`): absolute MV-value bounds.
|
|
194
|
+
* Missing keys are unbounded on that side.
|
|
195
|
+
* - **HTMLElement**: container the dragged element must stay inside. Bounds
|
|
196
|
+
* are computed at drag-start from the container's bounding rect.
|
|
197
|
+
* - **`() => HTMLElement | null`**: Solid-style accessor for a reactive
|
|
198
|
+
* container ref. Called at drag-start.
|
|
199
|
+
*/
|
|
200
|
+
export type DragConstraints =
|
|
201
|
+
| { top?: number; left?: number; right?: number; bottom?: number }
|
|
202
|
+
| HTMLElement
|
|
203
|
+
| (() => HTMLElement | null)
|
|
204
|
+
|
|
205
|
+
export type DragOptions = {
|
|
206
|
+
drag?: boolean | "x" | "y"
|
|
207
|
+
dragConstraints?: DragConstraints
|
|
208
|
+
/** Elastic resistance past constraints (0–1, default 0.5). */
|
|
209
|
+
dragElastic?: number
|
|
210
|
+
/** Carry momentum after release (default true). */
|
|
211
|
+
dragMomentum?: boolean
|
|
212
|
+
/**
|
|
213
|
+
* Options for the momentum / snap-back animation. Inertia by default
|
|
214
|
+
* (Q15d preset). Shallow-merges over the defaults — user fields override
|
|
215
|
+
* any single setting (bounceStiffness, timeConstant, etc.).
|
|
216
|
+
*/
|
|
217
|
+
dragTransition?: Partial<InertiaOptions>
|
|
218
|
+
/** Snap back to origin on release (default false). */
|
|
219
|
+
dragSnapToOrigin?: boolean
|
|
220
|
+
/** Imperatively-triggered drag (from createDragControls). */
|
|
221
|
+
dragControls?: DragControls
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// ---------------------------------------------------------------------------
|
|
225
|
+
// Lifecycle callbacks. Every hook is typed in Phase 1; only the animation-
|
|
226
|
+
// lifecycle ones (start/complete/cancel/update) are wired in Phase 1. Gesture
|
|
227
|
+
// and drag hooks fire when those features land in Phase 2.
|
|
228
|
+
// ---------------------------------------------------------------------------
|
|
229
|
+
|
|
230
|
+
export type MotionCallbacks = {
|
|
231
|
+
// Animation lifecycle (Phase 1)
|
|
232
|
+
onAnimationStart?: () => void
|
|
233
|
+
onAnimationComplete?: (definition: AnimateValue) => void
|
|
234
|
+
onAnimationCancel?: () => void
|
|
235
|
+
onUpdate?: (latest: ResolvedValues) => void
|
|
236
|
+
|
|
237
|
+
// Gesture lifecycle (Phase 2)
|
|
238
|
+
onHoverStart?: (e: PointerEvent) => void
|
|
239
|
+
onHoverEnd?: (e: PointerEvent) => void
|
|
240
|
+
/** Press began. `info.success` isn't meaningful yet — see {@link onPress} / {@link onPressCancel}. */
|
|
241
|
+
onPressStart?: (e: PointerEvent) => void
|
|
242
|
+
/** Press completed with the pointer still over the element. */
|
|
243
|
+
onPress?: (e: PointerEvent, info: PressInfo) => void
|
|
244
|
+
/** Press cancelled — pointer left the element before pointer-up. */
|
|
245
|
+
onPressCancel?: (e: PointerEvent, info: PressInfo) => void
|
|
246
|
+
onFocus?: (e: FocusEvent) => void
|
|
247
|
+
onBlur?: (e: FocusEvent) => void
|
|
248
|
+
onPanStart?: (e: PointerEvent, info: PanInfo) => void
|
|
249
|
+
onPan?: (e: PointerEvent, info: PanInfo) => void
|
|
250
|
+
onPanEnd?: (e: PointerEvent, info: PanInfo) => void
|
|
251
|
+
onViewportEnter?: (entry: IntersectionObserverEntry) => void
|
|
252
|
+
onViewportLeave?: (entry: IntersectionObserverEntry) => void
|
|
253
|
+
|
|
254
|
+
// Drag lifecycle (Phase 2)
|
|
255
|
+
onDragStart?: (e: PointerEvent, info: PanInfo) => void
|
|
256
|
+
onDrag?: (e: PointerEvent, info: PanInfo) => void
|
|
257
|
+
onDragEnd?: (e: PointerEvent, info: PanInfo) => void
|
|
258
|
+
onDragTransitionEnd?: () => void
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// ---------------------------------------------------------------------------
|
|
262
|
+
// MotionOptions — the full options bag accepted by useMotion / createMotion /
|
|
263
|
+
// <motion.div> / motion(). Includes animation targets, gesture targets,
|
|
264
|
+
// drag config, variants, callbacks, custom prop.
|
|
265
|
+
// ---------------------------------------------------------------------------
|
|
266
|
+
|
|
267
|
+
export type MotionOptions = MotionCallbacks &
|
|
268
|
+
DragOptions & {
|
|
269
|
+
/** Starting state. `false` means "don't apply an initial style". */
|
|
270
|
+
initial?: AnimateValue | false
|
|
271
|
+
/** Target the element animates to. */
|
|
272
|
+
animate?: AnimateValue
|
|
273
|
+
/** Target applied when component unmounts inside <Presence>. */
|
|
274
|
+
exit?: AnimateValue
|
|
275
|
+
|
|
276
|
+
/** Hover gesture target. */
|
|
277
|
+
hover?: AnimateValue
|
|
278
|
+
/** Press gesture target. */
|
|
279
|
+
press?: AnimateValue
|
|
280
|
+
/** Focus gesture target. */
|
|
281
|
+
focus?: AnimateValue
|
|
282
|
+
/** Viewport gesture target. */
|
|
283
|
+
inView?: AnimateValue
|
|
284
|
+
/** Viewport observer config for the inView gesture. */
|
|
285
|
+
inViewOptions?: ViewportOptions
|
|
286
|
+
|
|
287
|
+
/**
|
|
288
|
+
* Visual-state target applied while a drag gesture is active. Like
|
|
289
|
+
* other gesture targets, this can be a Target object or a variant
|
|
290
|
+
* label. Common idiom: `whileDrag: { scale: 1.05 }` for a lift-while-
|
|
291
|
+
* dragging effect — composes with drag's translation via the shared
|
|
292
|
+
* VisualElement (Q5/C-lean).
|
|
293
|
+
*/
|
|
294
|
+
whileDrag?: AnimateValue
|
|
295
|
+
|
|
296
|
+
/**
|
|
297
|
+
* Minimum cumulative pointer movement (in px) before pan/drag start fires.
|
|
298
|
+
* Distinguishes a pan from a click. Q11a default: 3px (matches motion).
|
|
299
|
+
*/
|
|
300
|
+
panThreshold?: number
|
|
301
|
+
|
|
302
|
+
/** Default transition applied to every property in animate/gesture targets. */
|
|
303
|
+
transition?: Transition
|
|
304
|
+
|
|
305
|
+
/** Named variant map; resolved by variant name in animate/gesture targets. */
|
|
306
|
+
variants?: Variants
|
|
307
|
+
|
|
308
|
+
/** Value passed to function variants. */
|
|
309
|
+
custom?: unknown
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// ---------------------------------------------------------------------------
|
|
313
|
+
// useMotion return type — a getter function that merges user props with
|
|
314
|
+
// motion's props (style, ref, hydration marker), plus a Provider component
|
|
315
|
+
// for opt-in variant context propagation to descendants.
|
|
316
|
+
// ---------------------------------------------------------------------------
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* The element types `useMotion` can attach to. HTMLElement covers the bulk;
|
|
320
|
+
* SVGElement is included so `motion.path`, `motion.circle`, etc. (Phase 4)
|
|
321
|
+
* thread through without a `ref` type-cast. `createMotion` only uses methods
|
|
322
|
+
* available on both (style, addEventListener, getBoundingClientRect) so the
|
|
323
|
+
* union is honored at runtime. Drag stays HTMLElement-only via an
|
|
324
|
+
* `instanceof` check in createMotion's body — motion-dom's VisualElement
|
|
325
|
+
* layer is HTML-specific.
|
|
326
|
+
*/
|
|
327
|
+
export type MotionElement = HTMLElement | SVGElement
|
|
328
|
+
|
|
329
|
+
// ---------------------------------------------------------------------------
|
|
330
|
+
// MotionStyle — the `style` prop shape accepted by useMotion + motion.X.
|
|
331
|
+
//
|
|
332
|
+
// Extends `JSX.CSSProperties` with two additions:
|
|
333
|
+
//
|
|
334
|
+
// 1. Motion's transform-shortcut keys (`x`, `y`, `scale`, `rotate`, ...).
|
|
335
|
+
// These aren't valid CSS property names — motion composes them into
|
|
336
|
+
// the `transform` CSS property at runtime via the registry-writer.
|
|
337
|
+
//
|
|
338
|
+
// 2. MotionValue variants of every value. `style={{ opacity: opacityMV,
|
|
339
|
+
// scale: scaleMV }}` typechecks because every key (CSS or transform
|
|
340
|
+
// shortcut) accepts either its native value type OR a MotionValue.
|
|
341
|
+
//
|
|
342
|
+
// The runtime contract lives in useMotion's `captureStyleEntries`:
|
|
343
|
+
// MotionValues land in the registry and the writer composes them on
|
|
344
|
+
// every change; static transform shortcuts seed transient registry
|
|
345
|
+
// entries; plain CSS keys pass through to Solid's style binding.
|
|
346
|
+
// ---------------------------------------------------------------------------
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* Motion's transform-shortcut keys and their value types. Each key may be
|
|
350
|
+
* a value OR a `MotionValue` holding that value.
|
|
351
|
+
*
|
|
352
|
+
* Value units (when expressed as a `number`):
|
|
353
|
+
* - `x` / `y` / `z` / `transformPerspective` → px
|
|
354
|
+
* - `scale*` → dimensionless multiplier
|
|
355
|
+
* - `rotate*` / `skew*` → deg
|
|
356
|
+
*
|
|
357
|
+
* Strings with explicit units pass through verbatim (e.g.
|
|
358
|
+
* `x: "50%"`, `rotate: "0.5turn"`).
|
|
359
|
+
*
|
|
360
|
+
* Variance note: motion's `MotionValue<T>` is invariant in `T` (it has both
|
|
361
|
+
* `get(): T` and `set(v: T)`), which means a user's `MotionValue<number>`
|
|
362
|
+
* cannot widen to `MotionValue<number | string>`. We use `MotionValue<any>`
|
|
363
|
+
* across the shortcut value types so a literal `createMotionValue(1)` is
|
|
364
|
+
* assignable as `x` / `y` / etc. without forcing the caller to type the MV
|
|
365
|
+
* with the full union. Type-safety on the value side is mostly recovered by
|
|
366
|
+
* the runtime: `formatProperty` and `transformFunctionFor` handle the
|
|
367
|
+
* number/string distinction.
|
|
368
|
+
*/
|
|
369
|
+
// biome-ignore lint/suspicious/noExplicitAny: see variance note above.
|
|
370
|
+
type AnyMotionValue = MotionValue<any>
|
|
371
|
+
|
|
372
|
+
export type MotionTransformShortcuts = {
|
|
373
|
+
x?: number | string | AnyMotionValue
|
|
374
|
+
y?: number | string | AnyMotionValue
|
|
375
|
+
z?: number | string | AnyMotionValue
|
|
376
|
+
scale?: number | string | AnyMotionValue
|
|
377
|
+
scaleX?: number | string | AnyMotionValue
|
|
378
|
+
scaleY?: number | string | AnyMotionValue
|
|
379
|
+
scaleZ?: number | string | AnyMotionValue
|
|
380
|
+
rotate?: number | string | AnyMotionValue
|
|
381
|
+
rotateX?: number | string | AnyMotionValue
|
|
382
|
+
rotateY?: number | string | AnyMotionValue
|
|
383
|
+
rotateZ?: number | string | AnyMotionValue
|
|
384
|
+
skew?: number | string | AnyMotionValue
|
|
385
|
+
skewX?: number | string | AnyMotionValue
|
|
386
|
+
skewY?: number | string | AnyMotionValue
|
|
387
|
+
transformPerspective?: number | string | AnyMotionValue
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
/**
|
|
391
|
+
* Widen each value of `T` to also accept any `MotionValue`. Pragmatic shape:
|
|
392
|
+
* motion's `MotionValue<T>` is invariant in T, so a strict per-key
|
|
393
|
+
* `MotionValue<NonNullable<T[K]>>` would reject e.g. `MotionValue<number>` for
|
|
394
|
+
* a `width: string | number` key. `MotionValue<any>` is the necessary
|
|
395
|
+
* escape hatch — the runtime always normalizes via `mv.get()` and
|
|
396
|
+
* `formatProperty`.
|
|
397
|
+
*/
|
|
398
|
+
type WithMotionValues<T> = {
|
|
399
|
+
[K in keyof T]?: T[K] | AnyMotionValue
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* The `style` prop shape for motion-aware elements: every native CSS
|
|
404
|
+
* property (with values optionally widened to a `MotionValue`), plus motion's
|
|
405
|
+
* transform-shortcut keys (with the same widening).
|
|
406
|
+
*
|
|
407
|
+
* Some CSS individual-transform properties (`scale`, `rotate`) collide with
|
|
408
|
+
* motion shortcut keys of the same name. We strip them from the CSS side
|
|
409
|
+
* before intersecting — motion's semantics win, the legacy CSS individual-
|
|
410
|
+
* transform path is a corner case users almost never reach for.
|
|
411
|
+
*
|
|
412
|
+
* @example
|
|
413
|
+
* const scale = createMotionValue(1)
|
|
414
|
+
* const m = useMotion({})
|
|
415
|
+
* <div {...m({ style: { scale, opacity: 0.5, color: "red" } })} />
|
|
416
|
+
*/
|
|
417
|
+
export type MotionStyle = MotionTransformShortcuts &
|
|
418
|
+
WithMotionValues<Omit<JSX.CSSProperties, keyof MotionTransformShortcuts>>
|
|
419
|
+
|
|
420
|
+
// Override `ref` + `style` from `JSX.HTMLAttributes` rather than intersecting
|
|
421
|
+
// (the latter would produce a `string | MotionStyle` mess for `style` because
|
|
422
|
+
// the base allows a raw CSS string and TypeScript can't narrow the union
|
|
423
|
+
// through the consumer code paths in useMotion).
|
|
424
|
+
export type ElementProps = Omit<JSX.HTMLAttributes<MotionElement>, "ref" | "style"> & {
|
|
425
|
+
ref?: ((el: MotionElement) => void) | MotionElement | undefined
|
|
426
|
+
style?: MotionStyle
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
export type MotionMergedProps<P extends ElementProps> = Omit<P, "ref" | "style"> & {
|
|
430
|
+
// Output style is the INTERSECTION of MotionStyle and JSX.CSSProperties.
|
|
431
|
+
// This is the only shape that works in both directions:
|
|
432
|
+
//
|
|
433
|
+
// • Spread onto a raw JSX element: `<div {...m({})} />` — element's
|
|
434
|
+
// `style` expects `string | CSSProperties | undefined`. Intersection's
|
|
435
|
+
// scale/rotate collapse to `number | string` (MV variants drop out
|
|
436
|
+
// against the CSS Scale/Rotation types), assignable to CSSProperties.
|
|
437
|
+
//
|
|
438
|
+
// • Chained back as input: `fade(slide({ class: "card" }))` — outer
|
|
439
|
+
// useMotion's `m()` expects `ElementProps` with `style: MotionStyle`.
|
|
440
|
+
// The intersection is also assignable to MotionStyle (narrower).
|
|
441
|
+
//
|
|
442
|
+
// Runtime emits MV-stripped values via `stripStyleEntriesOwnedByRegistry`,
|
|
443
|
+
// so the actually-rendered prop carries only the intersection-narrow
|
|
444
|
+
// values — type and runtime agree at this point.
|
|
445
|
+
style: MotionStyle & JSX.CSSProperties
|
|
446
|
+
ref: (el: MotionElement) => void
|
|
447
|
+
"data-motion-hydrated"?: ""
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
export type MotionGetProps = <P extends ElementProps>(userProps?: P) => MotionMergedProps<P>
|
|
451
|
+
|
|
452
|
+
export type UseMotionResult = MotionGetProps & {
|
|
453
|
+
/** Opt-in: wrap descendant motion elements to receive this element's variant context. */
|
|
454
|
+
Provider: Component<{ children: JSX.Element }>
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
// ---------------------------------------------------------------------------
|
|
458
|
+
// Contexts
|
|
459
|
+
// ---------------------------------------------------------------------------
|
|
460
|
+
|
|
461
|
+
/** Propagates the variant state from a parent motion element to descendants. */
|
|
462
|
+
export type VariantContextValue = {
|
|
463
|
+
variants?: Accessor<Variants | undefined>
|
|
464
|
+
/** Propagated only when the parent's `initial` is a variant name (not `false` or an explicit Target). */
|
|
465
|
+
initial?: Accessor<VariantLabels | undefined>
|
|
466
|
+
animate?: Accessor<VariantLabels | undefined>
|
|
467
|
+
hover?: Accessor<VariantLabels | undefined>
|
|
468
|
+
press?: Accessor<VariantLabels | undefined>
|
|
469
|
+
focus?: Accessor<VariantLabels | undefined>
|
|
470
|
+
inView?: Accessor<VariantLabels | undefined>
|
|
471
|
+
exit?: Accessor<VariantLabels | undefined>
|
|
472
|
+
custom?: Accessor<unknown>
|
|
473
|
+
transition?: Accessor<Transition | undefined>
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/**
|
|
477
|
+
* Wired with a no-op default; the real implementation is provided by
|
|
478
|
+
* `<Presence>` and `useAnimatePresence()` in Phase 3.
|
|
479
|
+
*
|
|
480
|
+
* Inverted shape vs. motion-react's `AnimatePresenceProps`: the child
|
|
481
|
+
* registers a `runExit` callable that knows how to animate ITSELF out (closes
|
|
482
|
+
* over its own state machine). Presence just coordinates timing — it doesn't
|
|
483
|
+
* resolve targets or merge transitions. See ADR 0003.
|
|
484
|
+
*
|
|
485
|
+
* - `register(el, runExit)` — called from `createMotion` when `opts.exit` is
|
|
486
|
+
* set. `runExit` flips the state machine's `exit` flag and awaits the
|
|
487
|
+
* resulting animate's completion.
|
|
488
|
+
* - `unregister(el)` — Presence (or the hook's `exit()`) prunes after exit.
|
|
489
|
+
* createMotion deliberately omits self-unregister; Solid disposes the
|
|
490
|
+
* child owner well before transition-group's onExit runs.
|
|
491
|
+
* - `beforeUnmount(el)` — Presence (or the hook's `exit()`) dispatches to the
|
|
492
|
+
* registered `runExit`. Returns a resolved promise if no `runExit` is
|
|
493
|
+
* registered, so non-exit children pass through cleanly.
|
|
494
|
+
* - `registerEnter` / `beforeMount` — symmetric to the exit pair. createMotion
|
|
495
|
+
* defers its first-mount animate when it's inside a real Presence (the
|
|
496
|
+
* no-op default has no `registerEnter`) because the element may be off-DOM
|
|
497
|
+
* at the moment the gesture-state-machine first iterates (e.g., the new
|
|
498
|
+
* child during a `mode: "wait"` swap is created BEFORE the old child's
|
|
499
|
+
* exit completes). Running motion's `animate()` on a disconnected element
|
|
500
|
+
* completes off-DOM and the final `commitStyles` silently no-ops, leaving
|
|
501
|
+
* the element painted at its `initial` target when it finally enters the
|
|
502
|
+
* DOM. Presence's `onEnter` (switch) / `onChange.added` (list) calls
|
|
503
|
+
* `beforeMount(el)` once the element is actually connected; the child's
|
|
504
|
+
* registered `runEnter` flips its readiness signal and the state machine
|
|
505
|
+
* dispatches the first animate against a live element.
|
|
506
|
+
* - `initial` — when an enclosing `<Presence initial={false}>` (or the hook
|
|
507
|
+
* with `initial: false`) is active, descendants suppress their first-mount
|
|
508
|
+
* animation. Accessor-shaped so the implementation can flip post-mount.
|
|
509
|
+
*/
|
|
510
|
+
export type PresenceContextValue = {
|
|
511
|
+
register: (el: MotionElement, runExit: () => Promise<void>) => void
|
|
512
|
+
unregister: (el: MotionElement) => void
|
|
513
|
+
beforeUnmount: (el: MotionElement) => Promise<void>
|
|
514
|
+
registerEnter?: (el: MotionElement, runEnter: () => void) => void
|
|
515
|
+
beforeMount?: (el: MotionElement) => void
|
|
516
|
+
initial?: Accessor<boolean>
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
/** <MotionConfig> provides defaults that flow to every descendant motion element. */
|
|
520
|
+
export type MotionConfigContextValue = {
|
|
521
|
+
/** "always" forces snap; "never" ignores system pref; "user" respects prefers-reduced-motion. */
|
|
522
|
+
reducedMotion: Accessor<"always" | "never" | "user">
|
|
523
|
+
/** Default transition merged with descendant transitions (descendant wins on conflict). */
|
|
524
|
+
transition: Accessor<Transition | undefined>
|
|
525
|
+
/** CSP nonce applied to any inline style emissions (advanced). */
|
|
526
|
+
nonce: Accessor<string | undefined>
|
|
527
|
+
}
|
|
528
|
+
|
|
529
|
+
// ---------------------------------------------------------------------------
|
|
530
|
+
// MotionConfig component props
|
|
531
|
+
// ---------------------------------------------------------------------------
|
|
532
|
+
|
|
533
|
+
export type MotionConfigProps = {
|
|
534
|
+
reducedMotion?: "always" | "never" | "user"
|
|
535
|
+
transition?: Transition
|
|
536
|
+
nonce?: string
|
|
537
|
+
children: JSX.Element
|
|
538
|
+
}
|