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/style.ts
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
import { isMotionValue, type MotionValue } from "motion"
|
|
2
|
+
import type { JSX } from "solid-js"
|
|
3
|
+
import type { Target } from "./types"
|
|
4
|
+
|
|
5
|
+
// ---------------------------------------------------------------------------
|
|
6
|
+
// Tables (Q5 locked decisions)
|
|
7
|
+
// ---------------------------------------------------------------------------
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Set of CSS shortcut keys motion treats as transform components. Re-used by
|
|
11
|
+
* `createMotion`'s Stage 3 animate bridge to decide whether an animate-target
|
|
12
|
+
* key should be routed through the value registry (composed via the writer's
|
|
13
|
+
* `el.style.transform =`) or sent down the existing WAA path.
|
|
14
|
+
*/
|
|
15
|
+
export const TRANSFORM_KEYS = /* @__PURE__ */ new Set([
|
|
16
|
+
"x",
|
|
17
|
+
"y",
|
|
18
|
+
"z",
|
|
19
|
+
"scale",
|
|
20
|
+
"scaleX",
|
|
21
|
+
"scaleY",
|
|
22
|
+
"scaleZ",
|
|
23
|
+
"rotate",
|
|
24
|
+
"rotateX",
|
|
25
|
+
"rotateY",
|
|
26
|
+
"rotateZ",
|
|
27
|
+
"skew",
|
|
28
|
+
"skewX",
|
|
29
|
+
"skewY",
|
|
30
|
+
"transformPerspective",
|
|
31
|
+
])
|
|
32
|
+
|
|
33
|
+
/** Order matters — motion composes transforms in this sequence (Q5 sub-1). */
|
|
34
|
+
const TRANSFORM_ORDER = [
|
|
35
|
+
"x",
|
|
36
|
+
"y",
|
|
37
|
+
"z",
|
|
38
|
+
"scale",
|
|
39
|
+
"scaleX",
|
|
40
|
+
"scaleY",
|
|
41
|
+
"scaleZ",
|
|
42
|
+
"rotate",
|
|
43
|
+
"rotateX",
|
|
44
|
+
"rotateY",
|
|
45
|
+
"rotateZ",
|
|
46
|
+
"skew",
|
|
47
|
+
"skewX",
|
|
48
|
+
"skewY",
|
|
49
|
+
"transformPerspective",
|
|
50
|
+
] as const
|
|
51
|
+
|
|
52
|
+
const PX_PROPERTIES = /* @__PURE__ */ new Set([
|
|
53
|
+
"width",
|
|
54
|
+
"minWidth",
|
|
55
|
+
"maxWidth",
|
|
56
|
+
"height",
|
|
57
|
+
"minHeight",
|
|
58
|
+
"maxHeight",
|
|
59
|
+
"top",
|
|
60
|
+
"right",
|
|
61
|
+
"bottom",
|
|
62
|
+
"left",
|
|
63
|
+
"padding",
|
|
64
|
+
"paddingTop",
|
|
65
|
+
"paddingRight",
|
|
66
|
+
"paddingBottom",
|
|
67
|
+
"paddingLeft",
|
|
68
|
+
"paddingInline",
|
|
69
|
+
"paddingBlock",
|
|
70
|
+
"paddingInlineStart",
|
|
71
|
+
"paddingInlineEnd",
|
|
72
|
+
"paddingBlockStart",
|
|
73
|
+
"paddingBlockEnd",
|
|
74
|
+
"margin",
|
|
75
|
+
"marginTop",
|
|
76
|
+
"marginRight",
|
|
77
|
+
"marginBottom",
|
|
78
|
+
"marginLeft",
|
|
79
|
+
"marginInline",
|
|
80
|
+
"marginBlock",
|
|
81
|
+
"marginInlineStart",
|
|
82
|
+
"marginInlineEnd",
|
|
83
|
+
"marginBlockStart",
|
|
84
|
+
"marginBlockEnd",
|
|
85
|
+
"borderWidth",
|
|
86
|
+
"borderTopWidth",
|
|
87
|
+
"borderRightWidth",
|
|
88
|
+
"borderBottomWidth",
|
|
89
|
+
"borderLeftWidth",
|
|
90
|
+
"borderRadius",
|
|
91
|
+
"borderTopLeftRadius",
|
|
92
|
+
"borderTopRightRadius",
|
|
93
|
+
"borderBottomLeftRadius",
|
|
94
|
+
"borderBottomRightRadius",
|
|
95
|
+
"gap",
|
|
96
|
+
"rowGap",
|
|
97
|
+
"columnGap",
|
|
98
|
+
"fontSize",
|
|
99
|
+
"outlineWidth",
|
|
100
|
+
"outlineOffset",
|
|
101
|
+
])
|
|
102
|
+
|
|
103
|
+
// ---------------------------------------------------------------------------
|
|
104
|
+
// Snapshot — unwrap MotionValue / Accessor / keyframe-array to a leaf value.
|
|
105
|
+
// Returns `undefined` when the leaf is null or undefined; callers omit those.
|
|
106
|
+
// ---------------------------------------------------------------------------
|
|
107
|
+
|
|
108
|
+
type Leaf = string | number
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Reduce a target-value (which may be raw, a MotionValue, an Accessor, or a
|
|
112
|
+
* keyframe array) to a concrete leaf value the writer can apply to the DOM
|
|
113
|
+
* or use to initialize a transient MotionValue. The cascade follows motion's
|
|
114
|
+
* own semantics:
|
|
115
|
+
*
|
|
116
|
+
* - `null` / `undefined` → `undefined` (caller drops the key)
|
|
117
|
+
* - keyframe array → first frame (consistent with motion-vanilla's
|
|
118
|
+
* initial-style snapshot)
|
|
119
|
+
* - `MotionValue` → its current `.get()`
|
|
120
|
+
* - `Accessor` (a bare zero-arg function) → its invocation result
|
|
121
|
+
* - primitive (string / number) → returned as-is
|
|
122
|
+
*
|
|
123
|
+
* Exported for the MV-in-style Stage 4 work: createMotion uses it to
|
|
124
|
+
* snapshot initial-target entries when registering them into the value
|
|
125
|
+
* registry as transient MVs.
|
|
126
|
+
*/
|
|
127
|
+
export function snapshotValue(value: unknown): Leaf | undefined {
|
|
128
|
+
if (value === null || value === undefined) return undefined
|
|
129
|
+
if (Array.isArray(value)) return snapshotValue(value[0])
|
|
130
|
+
if (isMotionValue(value)) return snapshotValue((value as MotionValue<Leaf>).get())
|
|
131
|
+
if (typeof value === "function") return snapshotValue((value as () => unknown)())
|
|
132
|
+
if (typeof value === "string" || typeof value === "number") return value
|
|
133
|
+
// Anything else (objects, booleans) isn't a renderable CSS value — skip it.
|
|
134
|
+
return undefined
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// ---------------------------------------------------------------------------
|
|
138
|
+
// Transform composition — produce a single `transform: ...` string from the
|
|
139
|
+
// shorthand keys present in the target.
|
|
140
|
+
// ---------------------------------------------------------------------------
|
|
141
|
+
|
|
142
|
+
/**
|
|
143
|
+
* Per-key transform formatter functions. Pre-built once at module load and
|
|
144
|
+
* shared across all elements. Used by `createMotion`'s specialized writer
|
|
145
|
+
* to avoid evaluating the transform-key switch on every single-key write —
|
|
146
|
+
* at Sierpinski-scale fan-out (thousands of writes per frame) the switch's
|
|
147
|
+
* 15 case-comparisons add up to non-trivial CPU.
|
|
148
|
+
*
|
|
149
|
+
* Pre-pick the formatter with `pickTransformFormatter(key)` ONCE at writer-
|
|
150
|
+
* compile time, then the per-call cost in the hot path is just `formatter(v)`.
|
|
151
|
+
*/
|
|
152
|
+
type TransformFormatter = (value: Leaf) => string
|
|
153
|
+
|
|
154
|
+
const TRANSFORM_FORMATTERS: Readonly<Record<string, TransformFormatter>> = {
|
|
155
|
+
// Translate: number → "px", string passes through verbatim.
|
|
156
|
+
x: (v) => `translateX(${typeof v === "string" ? v : `${v}px`})`,
|
|
157
|
+
y: (v) => `translateY(${typeof v === "string" ? v : `${v}px`})`,
|
|
158
|
+
z: (v) => `translateZ(${typeof v === "string" ? v : `${v}px`})`,
|
|
159
|
+
// Scale: dimensionless. Skip the type check entirely.
|
|
160
|
+
scale: (v) => `scale(${v})`,
|
|
161
|
+
scaleX: (v) => `scaleX(${v})`,
|
|
162
|
+
scaleY: (v) => `scaleY(${v})`,
|
|
163
|
+
scaleZ: (v) => `scaleZ(${v})`,
|
|
164
|
+
// Rotate / skew: number → "deg", string passes through.
|
|
165
|
+
rotate: (v) => `rotate(${typeof v === "string" ? v : `${v}deg`})`,
|
|
166
|
+
rotateX: (v) => `rotateX(${typeof v === "string" ? v : `${v}deg`})`,
|
|
167
|
+
rotateY: (v) => `rotateY(${typeof v === "string" ? v : `${v}deg`})`,
|
|
168
|
+
rotateZ: (v) => `rotateZ(${typeof v === "string" ? v : `${v}deg`})`,
|
|
169
|
+
skew: (v) => `skew(${typeof v === "string" ? v : `${v}deg`})`,
|
|
170
|
+
skewX: (v) => `skewX(${typeof v === "string" ? v : `${v}deg`})`,
|
|
171
|
+
skewY: (v) => `skewY(${typeof v === "string" ? v : `${v}deg`})`,
|
|
172
|
+
transformPerspective: (v) => `perspective(${typeof v === "string" ? v : `${v}px`})`,
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Look up the formatter for a transform-shortcut key. Returns `undefined`
|
|
177
|
+
* for non-transform keys; callers should check `TRANSFORM_KEYS.has(key)`
|
|
178
|
+
* before assuming a formatter exists.
|
|
179
|
+
*/
|
|
180
|
+
export function pickTransformFormatter(key: string): TransformFormatter | undefined {
|
|
181
|
+
return TRANSFORM_FORMATTERS[key]
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Format a motion transform-shortcut key + value as the corresponding CSS
|
|
186
|
+
* transform function string (e.g. `transformFunctionFor("scale", 1.05)`
|
|
187
|
+
* → `"scale(1.05)"`). One-shot variant — for hot paths, use
|
|
188
|
+
* `pickTransformFormatter(key)` once at compile time and reuse.
|
|
189
|
+
*/
|
|
190
|
+
export function transformFunctionFor(key: string, value: Leaf): string {
|
|
191
|
+
return TRANSFORM_FORMATTERS[key]?.(value) ?? ""
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
// ---------------------------------------------------------------------------
|
|
195
|
+
// Property formatting — apply unit table for non-transform CSS properties.
|
|
196
|
+
// ---------------------------------------------------------------------------
|
|
197
|
+
|
|
198
|
+
/**
|
|
199
|
+
* Format a non-transform CSS property's value (e.g. `formatProperty("width", 100)`
|
|
200
|
+
* → `"100px"`, `formatProperty("opacity", 0.5)` → `0.5`). Applies motion's
|
|
201
|
+
* default-unit table (PX for dimensional CSS, dimensionless otherwise);
|
|
202
|
+
* leaves CSS variables alone. Exported for `createMotion`'s writer fast path.
|
|
203
|
+
*/
|
|
204
|
+
export function formatProperty(key: string, value: Leaf): string | number {
|
|
205
|
+
if (typeof value === "string") return value
|
|
206
|
+
// CSS variables: stringify the number, never auto-unit (Q5 sub-4).
|
|
207
|
+
if (key.startsWith("--")) return String(value)
|
|
208
|
+
if (PX_PROPERTIES.has(key)) return `${value}px`
|
|
209
|
+
// Dimensionless or unknown — emit the bare number.
|
|
210
|
+
return value
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// ---------------------------------------------------------------------------
|
|
214
|
+
// targetToStyle — the SSR/hydration linchpin. Pure, deterministic, no DOM
|
|
215
|
+
// reads, no time-dependent values, no input mutation. Same inputs → same
|
|
216
|
+
// outputs on server and client.
|
|
217
|
+
// ---------------------------------------------------------------------------
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Convert a {@link Target} to a Solid {@link JSX.CSSProperties} object.
|
|
221
|
+
*
|
|
222
|
+
* - Composes transform shorthand (`x`, `y`, `scale`, `rotate`, etc.) into a
|
|
223
|
+
* single `transform: "..."` string in motion's canonical order.
|
|
224
|
+
* - Applies the default-unit table (px for dimensional CSS, deg for rotate/
|
|
225
|
+
* skew, dimensionless for scale/opacity/etc.).
|
|
226
|
+
* - For keyframe arrays, uses the first frame; a leading `null`/`undefined`
|
|
227
|
+
* keyframe omits the property entirely.
|
|
228
|
+
* - MotionValues and Solid Accessors are snapshotted at call time. Callers
|
|
229
|
+
* wrap in `untrack` if they don't want the read to subscribe.
|
|
230
|
+
* - Skips the `transition` key (animation config, not style).
|
|
231
|
+
* - CSS variables (`--foo`) emit raw values, no unit guess.
|
|
232
|
+
*
|
|
233
|
+
* @example
|
|
234
|
+
* targetToStyle({ x: 100, scale: 0.9, opacity: 0.5 })
|
|
235
|
+
* // { transform: "translateX(100px) scale(0.9)", opacity: 0.5 }
|
|
236
|
+
*/
|
|
237
|
+
export function targetToStyle(target: Target): JSX.CSSProperties {
|
|
238
|
+
const out: Record<string, string | number> = {}
|
|
239
|
+
const transforms: Record<string, Leaf> = {}
|
|
240
|
+
|
|
241
|
+
for (const key in target) {
|
|
242
|
+
if (key === "transition") continue
|
|
243
|
+
const raw = target[key as keyof Target]
|
|
244
|
+
const snapshot = snapshotValue(raw)
|
|
245
|
+
if (snapshot === undefined) continue
|
|
246
|
+
|
|
247
|
+
if (TRANSFORM_KEYS.has(key)) {
|
|
248
|
+
transforms[key] = snapshot
|
|
249
|
+
} else {
|
|
250
|
+
out[key] = formatProperty(key, snapshot)
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Compose transform string in motion's canonical order.
|
|
255
|
+
const parts: string[] = []
|
|
256
|
+
for (const key of TRANSFORM_ORDER) {
|
|
257
|
+
if (key in transforms) {
|
|
258
|
+
parts.push(transformFunctionFor(key, transforms[key] as Leaf))
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
if (parts.length > 0) {
|
|
262
|
+
out.transform = parts.join(" ")
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
return out as JSX.CSSProperties
|
|
266
|
+
}
|