morphicons 1.7.0 → 2.0.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/README.md +61 -428
- package/dist/MorphIcon.astro +4 -2
- package/dist/MorphIcon.svelte +19 -8
- package/dist/adapters.d.ts +12 -11
- package/dist/adapters.js +223 -23
- package/dist/{controller-CXZuwJ_M.js → controller--7SSBggk.js} +45 -11
- package/dist/{controller-Dkruvf3Y.d.ts → controller-DKm8s2ni.d.ts} +15 -6
- package/dist/dom.d.ts +2 -2
- package/dist/dom.js +292 -31
- package/dist/element.d.ts +7 -6
- package/dist/element.js +18 -5
- package/dist/index-DDNCPQ4Z.d.ts +55 -0
- package/dist/{index-DXp5b6BU.d.ts → index-DpvKR75N.d.ts} +26 -3
- package/dist/index.d.ts +12 -47
- package/dist/index.js +2 -1
- package/dist/plan-DhaDhEVq.d.ts +76 -0
- package/dist/react-native.d.ts +7 -6
- package/dist/react-native.js +65 -11
- package/dist/react.d.ts +7 -6
- package/dist/react.js +32 -7
- package/dist/region-BjECuWZR.js +60 -0
- package/dist/region.d.ts +2 -0
- package/dist/region.js +819 -0
- package/dist/{spring-CFHloqPP.js → resample-CXlaZ5ZQ.js} +56 -142
- package/dist/spring-B_P92nf6.js +95 -0
- package/dist/spring-BgZHrI2W.d.ts +33 -0
- package/dist/svelte-shared.d.ts +6 -5
- package/dist/svelte-shared.js +1 -2
- package/dist/vue.d.ts +6 -5
- package/dist/vue.js +31 -7
- package/package.json +62 -36
- package/dist/types-JAF3s76Y.d.ts +0 -60
package/dist/MorphIcon.svelte
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
import { onMount, untrack } from "svelte";
|
|
13
13
|
import {
|
|
14
14
|
computeInitialD,
|
|
15
|
+
computeInitialPaint,
|
|
15
16
|
createController,
|
|
16
17
|
type IconInput,
|
|
17
18
|
type MorphIconProps,
|
|
@@ -37,21 +38,34 @@ let {
|
|
|
37
38
|
// svelte-ignore state_referenced_locally — mount-time value on purpose: the
|
|
38
39
|
// initial d must stay a constant so Svelte never rewrites the attribute.
|
|
39
40
|
const initialD = computeInitialD({ icon, from, to, progress });
|
|
41
|
+
const initialPaint = computeInitialPaint({ icon, from, to, progress });
|
|
40
42
|
|
|
41
43
|
let pathEl: SVGPathElement | undefined = $state();
|
|
44
|
+
const sw = $derived(
|
|
45
|
+
absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
|
|
46
|
+
);
|
|
47
|
+
|
|
42
48
|
// svelte-ignore state_referenced_locally — init-time values on purpose: they
|
|
43
49
|
// seed the controller's watch baselines so the first $effect run is a no-op.
|
|
44
|
-
const ctrl = createController({
|
|
50
|
+
const ctrl = createController({
|
|
51
|
+
icon,
|
|
52
|
+
from,
|
|
53
|
+
to,
|
|
54
|
+
progress,
|
|
55
|
+
reducedMotion,
|
|
56
|
+
strokeWidth: sw,
|
|
57
|
+
});
|
|
45
58
|
|
|
46
59
|
onMount(() => {
|
|
47
|
-
if (pathEl)
|
|
60
|
+
if (pathEl)
|
|
61
|
+
ctrl.mount(pathEl, { icon, from, to, progress, reducedMotion, strokeWidth: sw });
|
|
48
62
|
return () => ctrl.destroy();
|
|
49
63
|
});
|
|
50
64
|
|
|
51
65
|
// Prop changes: one watcher owns the mode logic (controlled wins — change
|
|
52
66
|
// detection and precedence live in shared.ts).
|
|
53
67
|
$effect(() => {
|
|
54
|
-
const next = { icon, from, to, progress, spring, reducedMotion };
|
|
68
|
+
const next = { icon, from, to, progress, spring, reducedMotion, strokeWidth: sw };
|
|
55
69
|
untrack(() => ctrl.watch(next));
|
|
56
70
|
});
|
|
57
71
|
|
|
@@ -62,10 +76,6 @@ export function morphTo(i: IconInput, s?: SpringPreset | MorphOptions): void {
|
|
|
62
76
|
export function set(i: IconInput): void {
|
|
63
77
|
ctrl.set(i);
|
|
64
78
|
}
|
|
65
|
-
|
|
66
|
-
const sw = $derived(
|
|
67
|
-
absoluteStrokeWidth ? (Number(strokeWidth) * 24) / Number(size) : strokeWidth,
|
|
68
|
-
);
|
|
69
79
|
</script>
|
|
70
80
|
|
|
71
81
|
<svg
|
|
@@ -75,6 +85,7 @@ const sw = $derived(
|
|
|
75
85
|
viewBox="0 0 24 24"
|
|
76
86
|
fill="none"
|
|
77
87
|
stroke={color}
|
|
88
|
+
{color}
|
|
78
89
|
stroke-width={sw}
|
|
79
90
|
stroke-linecap="round"
|
|
80
91
|
stroke-linejoin="round"
|
|
@@ -83,5 +94,5 @@ const sw = $derived(
|
|
|
83
94
|
{...rest}
|
|
84
95
|
>
|
|
85
96
|
{#if label}<title>{label}</title>{/if}
|
|
86
|
-
<path bind:this={pathEl} d={initialD} />
|
|
97
|
+
<path bind:this={pathEl} d={initialD} {...initialPaint} />
|
|
87
98
|
</svg>
|
package/dist/adapters.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { i as PathEl } from "./index-
|
|
1
|
+
import { a as IconInput } from "./plan-DhaDhEVq.js";
|
|
2
|
+
import { i as PathEl } from "./index-DpvKR75N.js";
|
|
3
3
|
//#region src/adapters/canvas.d.ts
|
|
4
4
|
/** The 2D context surface the target drives — structural, so a real
|
|
5
5
|
* CanvasRenderingContext2D, an OffscreenCanvasRenderingContext2D or a test
|
|
@@ -42,7 +42,7 @@ interface CanvasTargetOptions {
|
|
|
42
42
|
* the morph's geometry onto the canvas, so
|
|
43
43
|
* `createMorph(canvasTarget(canvas), icon)` animates pixels instead of an
|
|
44
44
|
* inline `<svg><path>`. */
|
|
45
|
-
declare function canvasTarget(surface: CanvasSurface | Canvas2DContext, opts?: CanvasTargetOptions): PathEl;
|
|
45
|
+
export declare function canvasTarget(surface: CanvasSurface | Canvas2DContext, opts?: CanvasTargetOptions): PathEl;
|
|
46
46
|
//#endregion
|
|
47
47
|
//#region src/adapters/mask.d.ts
|
|
48
48
|
/** The subset of an element's inline style the mask target writes — structural
|
|
@@ -85,15 +85,16 @@ interface MaskPathEl extends PathEl {
|
|
|
85
85
|
* the back buffer and re-points `mask-image` at it: the property VALUE flips
|
|
86
86
|
* (`url(#a)` ↔ `url(#b)`) each frame, which forces reference re-resolution
|
|
87
87
|
* in every engine. Chromium/Gecko pay nothing extra for it. */
|
|
88
|
-
declare function maskTarget(el: MaskEl, opts?: MaskTargetOptions): MaskPathEl;
|
|
88
|
+
export declare function maskTarget(el: MaskEl, opts?: MaskTargetOptions): MaskPathEl;
|
|
89
89
|
//#endregion
|
|
90
90
|
//#region src/adapters/svg.d.ts
|
|
91
91
|
/** SVG markup → an IconInput the pipeline can lower. Strips non-rendered
|
|
92
|
-
* containers, rejects what can't
|
|
93
|
-
*
|
|
94
|
-
*
|
|
95
|
-
*
|
|
96
|
-
*
|
|
97
|
-
|
|
92
|
+
* containers, rejects what it can't read (an unsupported element, empty
|
|
93
|
+
* geometry) and re-grids onto 24 via the viewBox when one is present.
|
|
94
|
+
* Stroke icons return a `d` (when a viewBox forced a fit) or an IconNode,
|
|
95
|
+
* exactly as before; fill-drawn icons return an IconNode whose elements
|
|
96
|
+
* carry their resolved paint (fill/stroke/stroke-width/fill-rule), which
|
|
97
|
+
* is what makes the morph fly as a region. */
|
|
98
|
+
export declare function svgToIcon(markup: string): IconInput;
|
|
98
99
|
//#endregion
|
|
99
|
-
export {
|
|
100
|
+
export type { Canvas2DContext, CanvasSurface, CanvasTargetOptions, MaskEl, MaskPathEl, MaskStyle, MaskTargetOptions };
|
package/dist/adapters.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { t as fitIcon } from "./normalize-CYnN3Npw.js";
|
|
1
|
+
import { n as iconToCubics, r as cubicsToPathD, t as fitIcon } from "./normalize-CYnN3Npw.js";
|
|
2
2
|
//#region src/adapters/canvas.ts
|
|
3
3
|
/** Adapts a canvas (or its 2D context) into a `PathEl` whose writes stroke
|
|
4
4
|
* the morph's geometry onto the canvas, so
|
|
@@ -118,7 +118,7 @@ function maskTarget(el, opts = {}) {
|
|
|
118
118
|
//#endregion
|
|
119
119
|
//#region src/adapters/svg.ts
|
|
120
120
|
const SVG_CONTAINER = /<(defs|mask|clipPath|symbol)\b[^>]*>[\s\S]*?<\/\1>/gi;
|
|
121
|
-
const
|
|
121
|
+
const SVG_TAG = /<(\/?)([a-zA-Z][\w:-]*)((?:[^>"']|"[^"]*"|'[^']*')*?)(\/?)>/g;
|
|
122
122
|
const SVG_ATTR = /([\w:-]+)\s*=\s*"([^"]*)"|([\w:-]+)\s*=\s*'([^']*)'/g;
|
|
123
123
|
const SVG_VIEWBOX = /<svg\b[^>]*\bviewBox\s*=\s*["']([^"']+)["']/i;
|
|
124
124
|
const SVG_GEOMETRY = /* @__PURE__ */ new Set([
|
|
@@ -130,9 +130,12 @@ const SVG_GEOMETRY = /* @__PURE__ */ new Set([
|
|
|
130
130
|
"polyline",
|
|
131
131
|
"polygon"
|
|
132
132
|
]);
|
|
133
|
-
const
|
|
133
|
+
const SVG_GROUP = /* @__PURE__ */ new Set([
|
|
134
134
|
"svg",
|
|
135
135
|
"g",
|
|
136
|
+
"a"
|
|
137
|
+
]);
|
|
138
|
+
const SVG_SKIP = /* @__PURE__ */ new Set([
|
|
136
139
|
"title",
|
|
137
140
|
"desc",
|
|
138
141
|
"defs",
|
|
@@ -140,6 +143,16 @@ const SVG_SKIP = /* @__PURE__ */ new Set([
|
|
|
140
143
|
"clippath",
|
|
141
144
|
"symbol"
|
|
142
145
|
]);
|
|
146
|
+
const PAINT = [
|
|
147
|
+
"fill",
|
|
148
|
+
"stroke",
|
|
149
|
+
"stroke-width",
|
|
150
|
+
"fill-rule",
|
|
151
|
+
"opacity",
|
|
152
|
+
"fill-opacity",
|
|
153
|
+
"visibility",
|
|
154
|
+
"display"
|
|
155
|
+
];
|
|
143
156
|
function readSvgAttrs(raw) {
|
|
144
157
|
const attrs = {};
|
|
145
158
|
SVG_ATTR.lastIndex = 0;
|
|
@@ -150,32 +163,219 @@ function readSvgAttrs(raw) {
|
|
|
150
163
|
}
|
|
151
164
|
return attrs;
|
|
152
165
|
}
|
|
166
|
+
function paintOf(parent, attrs) {
|
|
167
|
+
const out = { ...parent };
|
|
168
|
+
for (const k of PAINT) if (attrs[k] !== void 0) out[k] = String(attrs[k]);
|
|
169
|
+
for (const decl of String(attrs.style ?? "").split(";")) {
|
|
170
|
+
const i = decl.indexOf(":");
|
|
171
|
+
const k = decl.slice(0, i).trim().toLowerCase();
|
|
172
|
+
if (i > 0 && PAINT.includes(k)) out[k] = decl.slice(i + 1).trim();
|
|
173
|
+
}
|
|
174
|
+
return out;
|
|
175
|
+
}
|
|
176
|
+
const mul = (m, n) => [
|
|
177
|
+
m[0] * n[0] + m[2] * n[1],
|
|
178
|
+
m[1] * n[0] + m[3] * n[1],
|
|
179
|
+
m[0] * n[2] + m[2] * n[3],
|
|
180
|
+
m[1] * n[2] + m[3] * n[3],
|
|
181
|
+
m[0] * n[4] + m[2] * n[5] + m[4],
|
|
182
|
+
m[1] * n[4] + m[3] * n[5] + m[5]
|
|
183
|
+
];
|
|
184
|
+
function parseTransform(v) {
|
|
185
|
+
const s = String(v ?? "").trim();
|
|
186
|
+
if (!s) return null;
|
|
187
|
+
let m = [
|
|
188
|
+
1,
|
|
189
|
+
0,
|
|
190
|
+
0,
|
|
191
|
+
1,
|
|
192
|
+
0,
|
|
193
|
+
0
|
|
194
|
+
];
|
|
195
|
+
for (const [, fn, args] of s.matchAll(/(\w+)\s*\(([^)]*)\)/g)) {
|
|
196
|
+
const a = args.trim().split(/[\s,]+/).map(Number);
|
|
197
|
+
const rad = (a[0] ?? 0) * Math.PI / 180;
|
|
198
|
+
let t;
|
|
199
|
+
switch (fn) {
|
|
200
|
+
case "matrix":
|
|
201
|
+
t = [
|
|
202
|
+
a[0],
|
|
203
|
+
a[1],
|
|
204
|
+
a[2],
|
|
205
|
+
a[3],
|
|
206
|
+
a[4],
|
|
207
|
+
a[5]
|
|
208
|
+
];
|
|
209
|
+
break;
|
|
210
|
+
case "translate":
|
|
211
|
+
t = [
|
|
212
|
+
1,
|
|
213
|
+
0,
|
|
214
|
+
0,
|
|
215
|
+
1,
|
|
216
|
+
a[0],
|
|
217
|
+
a[1] ?? 0
|
|
218
|
+
];
|
|
219
|
+
break;
|
|
220
|
+
case "scale":
|
|
221
|
+
t = [
|
|
222
|
+
a[0],
|
|
223
|
+
0,
|
|
224
|
+
0,
|
|
225
|
+
a[1] ?? a[0],
|
|
226
|
+
0,
|
|
227
|
+
0
|
|
228
|
+
];
|
|
229
|
+
break;
|
|
230
|
+
case "rotate": {
|
|
231
|
+
const c = Math.cos(rad);
|
|
232
|
+
const n = Math.sin(rad);
|
|
233
|
+
const [cx = 0, cy = 0] = [a[1], a[2]];
|
|
234
|
+
t = [
|
|
235
|
+
c,
|
|
236
|
+
n,
|
|
237
|
+
-n,
|
|
238
|
+
c,
|
|
239
|
+
cx - c * cx + n * cy,
|
|
240
|
+
cy - n * cx - c * cy
|
|
241
|
+
];
|
|
242
|
+
break;
|
|
243
|
+
}
|
|
244
|
+
case "skewX":
|
|
245
|
+
t = [
|
|
246
|
+
1,
|
|
247
|
+
0,
|
|
248
|
+
Math.tan(rad),
|
|
249
|
+
1,
|
|
250
|
+
0,
|
|
251
|
+
0
|
|
252
|
+
];
|
|
253
|
+
break;
|
|
254
|
+
case "skewY":
|
|
255
|
+
t = [
|
|
256
|
+
1,
|
|
257
|
+
Math.tan(rad),
|
|
258
|
+
0,
|
|
259
|
+
1,
|
|
260
|
+
0,
|
|
261
|
+
0
|
|
262
|
+
];
|
|
263
|
+
break;
|
|
264
|
+
default: throw new Error(`morphicons: unsupported transform "${fn}"`);
|
|
265
|
+
}
|
|
266
|
+
if (t.some((x) => !Number.isFinite(x))) throw new Error(`morphicons: invalid transform "${s}"`);
|
|
267
|
+
m = mul(m, t);
|
|
268
|
+
}
|
|
269
|
+
return m;
|
|
270
|
+
}
|
|
271
|
+
function bake(tag, attrs, m) {
|
|
272
|
+
const paths = iconToCubics([[tag, attrs]]);
|
|
273
|
+
for (const { pts } of paths) for (let i = 0; i < pts.length; i += 2) {
|
|
274
|
+
const x = pts[i];
|
|
275
|
+
const y = pts[i + 1];
|
|
276
|
+
pts[i] = m[0] * x + m[2] * y + m[4];
|
|
277
|
+
pts[i + 1] = m[1] * x + m[3] * y + m[5];
|
|
278
|
+
}
|
|
279
|
+
return cubicsToPathD(paths);
|
|
280
|
+
}
|
|
281
|
+
function stripComments(markup) {
|
|
282
|
+
let out = markup;
|
|
283
|
+
let prev;
|
|
284
|
+
do {
|
|
285
|
+
prev = out;
|
|
286
|
+
out = out.replace(/<!--[\s\S]*?-->/g, "");
|
|
287
|
+
} while (out !== prev);
|
|
288
|
+
const open = out.indexOf("<!--");
|
|
289
|
+
return open === -1 ? out : out.slice(0, open);
|
|
290
|
+
}
|
|
291
|
+
const painted = (v) => v !== void 0 && v.trim().toLowerCase() !== "none";
|
|
153
292
|
/** SVG markup → an IconInput the pipeline can lower. Strips non-rendered
|
|
154
|
-
* containers, rejects what can't
|
|
155
|
-
*
|
|
156
|
-
*
|
|
157
|
-
*
|
|
158
|
-
*
|
|
293
|
+
* containers, rejects what it can't read (an unsupported element, empty
|
|
294
|
+
* geometry) and re-grids onto 24 via the viewBox when one is present.
|
|
295
|
+
* Stroke icons return a `d` (when a viewBox forced a fit) or an IconNode,
|
|
296
|
+
* exactly as before; fill-drawn icons return an IconNode whose elements
|
|
297
|
+
* carry their resolved paint (fill/stroke/stroke-width/fill-rule), which
|
|
298
|
+
* is what makes the morph fly as a region. */
|
|
159
299
|
function svgToIcon(markup) {
|
|
160
300
|
if (markup.trimStart().charCodeAt(0) !== 60) throw new Error("morphicons: svgToIcon expects SVG markup — a d string or IconNode is already an IconInput");
|
|
161
|
-
const
|
|
162
|
-
|
|
163
|
-
const
|
|
164
|
-
|
|
165
|
-
const
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
301
|
+
const clean = stripComments(markup);
|
|
302
|
+
if (/<style\b/i.test(clean)) throw new Error("morphicons: unsupported SVG element <style>");
|
|
303
|
+
const body = clean.replace(SVG_CONTAINER, "");
|
|
304
|
+
const found = [];
|
|
305
|
+
const stack = [{
|
|
306
|
+
paint: {},
|
|
307
|
+
m: null
|
|
308
|
+
}];
|
|
309
|
+
SVG_TAG.lastIndex = 0;
|
|
310
|
+
let mt;
|
|
311
|
+
while ((mt = SVG_TAG.exec(body)) !== null) {
|
|
312
|
+
const tag = mt[2].toLowerCase();
|
|
313
|
+
const top = stack[stack.length - 1];
|
|
314
|
+
if (mt[1]) {
|
|
315
|
+
if (SVG_GROUP.has(tag) && stack.length > 1) stack.pop();
|
|
316
|
+
continue;
|
|
317
|
+
}
|
|
170
318
|
if (SVG_SKIP.has(tag)) continue;
|
|
319
|
+
const attrs = readSvgAttrs(mt[3]);
|
|
320
|
+
const own = parseTransform(attrs.transform);
|
|
321
|
+
const m = own ? top.m ? mul(top.m, own) : own : top.m;
|
|
322
|
+
const paint = paintOf(top.paint, attrs);
|
|
323
|
+
if (top.paint.display === "none") paint.display = "none";
|
|
324
|
+
if (SVG_GROUP.has(tag)) {
|
|
325
|
+
if (!mt[4]) stack.push({
|
|
326
|
+
paint,
|
|
327
|
+
m
|
|
328
|
+
});
|
|
329
|
+
continue;
|
|
330
|
+
}
|
|
171
331
|
if (!SVG_GEOMETRY.has(tag)) throw new Error(`morphicons: unsupported SVG element <${tag}>`);
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
332
|
+
if (paint.display === "none" || /^(hidden|collapse)$/.test(paint.visibility ?? "")) continue;
|
|
333
|
+
found.push({
|
|
334
|
+
tag,
|
|
335
|
+
attrs,
|
|
336
|
+
paint,
|
|
337
|
+
m
|
|
338
|
+
});
|
|
339
|
+
}
|
|
340
|
+
if (found.length === 0) throw new Error("morphicons: no morphable geometry in SVG");
|
|
341
|
+
const fills = found.map(({ paint }) => paint.fill !== void 0 ? painted(paint.fill) : !painted(paint.stroke));
|
|
342
|
+
const vb = SVG_VIEWBOX.exec(clean);
|
|
343
|
+
if (!fills.some(Boolean)) {
|
|
344
|
+
const node = found.map(({ tag, attrs, m }) => {
|
|
345
|
+
if (!m) return [tag, attrs];
|
|
346
|
+
const { transform: _, ...rest } = attrs;
|
|
347
|
+
return ["path", {
|
|
348
|
+
...rest,
|
|
349
|
+
d: bake(tag, attrs, m)
|
|
350
|
+
}];
|
|
351
|
+
});
|
|
352
|
+
return vb ? fitIcon(node, vb[1]) : node;
|
|
175
353
|
}
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
354
|
+
let scale = 1;
|
|
355
|
+
if (vb) {
|
|
356
|
+
const [, , w, h] = vb[1].trim().split(/[\s,]+/).map(Number);
|
|
357
|
+
scale = Math.min(24 / w, 24 / h);
|
|
358
|
+
}
|
|
359
|
+
return found.map(({ tag, attrs, paint, m }, i) => {
|
|
360
|
+
const d = m ? bake(tag, attrs, m) : tag === "path" ? String(attrs.d ?? "") : bake(tag, attrs, [
|
|
361
|
+
1,
|
|
362
|
+
0,
|
|
363
|
+
0,
|
|
364
|
+
1,
|
|
365
|
+
0,
|
|
366
|
+
0
|
|
367
|
+
]);
|
|
368
|
+
const out = {
|
|
369
|
+
d: vb ? fitIcon(d, vb[1]) : d,
|
|
370
|
+
fill: fills[i] ? "currentColor" : "none",
|
|
371
|
+
stroke: painted(paint.stroke) ? "currentColor" : "none"
|
|
372
|
+
};
|
|
373
|
+
if (painted(paint.stroke)) out["stroke-width"] = String(Number.parseFloat(paint["stroke-width"] ?? "1") * scale);
|
|
374
|
+
if (paint["fill-rule"]) out["fill-rule"] = paint["fill-rule"];
|
|
375
|
+
if (paint.opacity) out.opacity = paint.opacity;
|
|
376
|
+
if (paint["fill-opacity"]) out["fill-opacity"] = paint["fill-opacity"];
|
|
377
|
+
return ["path", out];
|
|
378
|
+
});
|
|
179
379
|
}
|
|
180
380
|
//#endregion
|
|
181
381
|
export { canvasTarget, maskTarget, svgToIcon };
|
|
@@ -1,12 +1,23 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as interpPolar, r as allocOutputs } from "./spring-B_P92nf6.js";
|
|
2
2
|
import { i as serialize } from "./normalize-CYnN3Npw.js";
|
|
3
|
-
import {
|
|
3
|
+
import { a as buildPlan, t as resampleIcon } from "./resample-CXlaZ5ZQ.js";
|
|
4
|
+
import { a as paintOf } from "./region-BjECuWZR.js";
|
|
5
|
+
import { canonicalD, createMorph, restAttrs } from "./dom.js";
|
|
4
6
|
//#region src/dom/controller.ts
|
|
7
|
+
/** The endpoint a frozen controlled pair renders at rest at `t` (SSR
|
|
8
|
+
* included), or null mid-way for a stroke pair, interpolated with the pure
|
|
9
|
+
* core. A pair with a fill endpoint scrubs as a region morph, which only the
|
|
10
|
+
* lazy chunk draws: mid-way it renders `from` until the client's first seek. */
|
|
11
|
+
function frozenEnd(from, to, t) {
|
|
12
|
+
if (t >= 1) return to;
|
|
13
|
+
if (t <= 0 || paintOf(from) === "fill" || paintOf(to) === "fill") return from;
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
5
16
|
/** Frozen shape of the from→to pair at t, using the pure core (SSR-safe).
|
|
6
17
|
* At exact endpoints returns the canonical `d` (real curves, not polyline). */
|
|
7
18
|
function frozenD(from, to, t) {
|
|
8
|
-
|
|
9
|
-
if (
|
|
19
|
+
const end = frozenEnd(from, to, t);
|
|
20
|
+
if (end !== null) return canonicalD(end);
|
|
10
21
|
const plan = buildPlan(resampleIcon(from), resampleIcon(to));
|
|
11
22
|
const out = allocOutputs(plan);
|
|
12
23
|
interpPolar(plan, t, out);
|
|
@@ -21,6 +32,17 @@ function computeInitialD({ icon, from, to, progress }) {
|
|
|
21
32
|
const first = icon ?? from ?? to;
|
|
22
33
|
return first !== void 0 ? canonicalD(first) : "";
|
|
23
34
|
}
|
|
35
|
+
/** Paint attributes of the initial `<path>` (SSR included), a constant like
|
|
36
|
+
* the initial d: a fill icon is filled before any script runs. A frozen
|
|
37
|
+
* stroke pair paints as the endpoint it is nearer to; one with a fill
|
|
38
|
+
* endpoint rests on `from` mid-way, like its d (frozenEnd). */
|
|
39
|
+
function computeInitialPaint({ icon, from, to, progress }) {
|
|
40
|
+
if (from !== void 0 && to !== void 0) {
|
|
41
|
+
const t = progress ?? 0;
|
|
42
|
+
return restAttrs(frozenEnd(from, to, t) ?? (t < .5 ? from : to));
|
|
43
|
+
}
|
|
44
|
+
return restAttrs(icon ?? from ?? to);
|
|
45
|
+
}
|
|
24
46
|
/** Per-instance driver state — the exact logic of the React/Vue bindings
|
|
25
47
|
* (mount, mode watch, controlled seek with re-basing, imperative). Takes the
|
|
26
48
|
* init-time props: they seed the watch baselines so a watcher that fires on
|
|
@@ -37,11 +59,12 @@ function computeInitialD({ icon, from, to, progress }) {
|
|
|
37
59
|
* path back to `icon`.
|
|
38
60
|
* - Every exit from controlled mode (imperative call or icon takeover)
|
|
39
61
|
* invalidates the frozen pair, so returning to it re-bases on `from`. */
|
|
40
|
-
function createController({ icon, from, to, progress, reducedMotion }) {
|
|
62
|
+
function createController({ icon, from, to, progress, reducedMotion, strokeWidth }) {
|
|
41
63
|
let el = null;
|
|
42
64
|
let dead = false;
|
|
43
65
|
let morph = null;
|
|
44
66
|
let rm = reducedMotion ?? "never";
|
|
67
|
+
let sw = Number(strokeWidth ?? 2);
|
|
45
68
|
let based = false;
|
|
46
69
|
let pair = null;
|
|
47
70
|
let prevIcon = icon;
|
|
@@ -53,7 +76,10 @@ function createController({ icon, from, to, progress, reducedMotion }) {
|
|
|
53
76
|
const ensure = (birth) => {
|
|
54
77
|
if (morph) return morph;
|
|
55
78
|
if (dead || !el) return null;
|
|
56
|
-
morph = createMorph(el, birth, {
|
|
79
|
+
morph = createMorph(el, birth, {
|
|
80
|
+
reducedMotion: rm,
|
|
81
|
+
strokeWidth: sw
|
|
82
|
+
});
|
|
57
83
|
return morph;
|
|
58
84
|
};
|
|
59
85
|
/** Controlled mode: freeze the pair at `progress` via seek (no spring). */
|
|
@@ -80,13 +106,17 @@ function createController({ icon, from, to, progress, reducedMotion }) {
|
|
|
80
106
|
}
|
|
81
107
|
};
|
|
82
108
|
return {
|
|
83
|
-
mount(mountEl, { icon, from, to, progress, reducedMotion }) {
|
|
109
|
+
mount(mountEl, { icon, from, to, progress, reducedMotion, strokeWidth }) {
|
|
84
110
|
el = mountEl;
|
|
85
111
|
rm = reducedMotion ?? rm;
|
|
112
|
+
sw = Number(strokeWidth ?? sw);
|
|
86
113
|
const controlled = from !== void 0 && to !== void 0;
|
|
87
114
|
const initialIcon = icon ?? from ?? to;
|
|
88
115
|
if (initialIcon === void 0) return;
|
|
89
|
-
const m = createMorph(el, controlled ? from : initialIcon, {
|
|
116
|
+
const m = createMorph(el, controlled ? from : initialIcon, {
|
|
117
|
+
reducedMotion: rm,
|
|
118
|
+
strokeWidth: sw
|
|
119
|
+
});
|
|
90
120
|
morph = m;
|
|
91
121
|
if (controlled) {
|
|
92
122
|
pair = [from, to];
|
|
@@ -110,9 +140,13 @@ function createController({ icon, from, to, progress, reducedMotion }) {
|
|
|
110
140
|
/** Prop watcher: ONE owner decides per run — the controlled pair while it
|
|
111
141
|
* is fully present, `icon` otherwise (mount doesn't fire thanks to the
|
|
112
142
|
* init-time baselines). */
|
|
113
|
-
watch({ icon, from, to, progress, spring, reducedMotion }) {
|
|
143
|
+
watch({ icon, from, to, progress, spring, reducedMotion, strokeWidth }) {
|
|
114
144
|
rm = reducedMotion ?? "never";
|
|
115
|
-
|
|
145
|
+
sw = Number(strokeWidth ?? 2);
|
|
146
|
+
if (morph) {
|
|
147
|
+
morph.reducedMotion = rm;
|
|
148
|
+
morph.strokeWidth = sw;
|
|
149
|
+
}
|
|
116
150
|
const controlled = from !== void 0 && to !== void 0;
|
|
117
151
|
const left = prevControlled && !controlled;
|
|
118
152
|
const iconChanged = icon !== prevIcon;
|
|
@@ -149,4 +183,4 @@ function createController({ icon, from, to, progress, reducedMotion }) {
|
|
|
149
183
|
};
|
|
150
184
|
}
|
|
151
185
|
//#endregion
|
|
152
|
-
export {
|
|
186
|
+
export { frozenEnd as i, computeInitialPaint as n, createController as r, computeInitialD as t };
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { a as IconInput } from "./plan-DhaDhEVq.js";
|
|
2
|
+
import { r as SpringPreset } from "./spring-BgZHrI2W.js";
|
|
3
|
+
import { a as ReducedMotionMode, i as PathEl, r as MorphOptions } from "./index-DpvKR75N.js";
|
|
3
4
|
//#region src/dom/controller.d.ts
|
|
4
5
|
/** Imperative surface exposed by every binding (ref / bind:this / element). */
|
|
5
6
|
interface MorphHandle {
|
|
@@ -15,6 +16,9 @@ interface MorphModeProps {
|
|
|
15
16
|
}
|
|
16
17
|
interface MorphCtrlProps extends MorphModeProps {
|
|
17
18
|
reducedMotion?: ReducedMotionMode;
|
|
19
|
+
/** The stroke width on the 24 grid (absoluteStrokeWidth already resolved):
|
|
20
|
+
* what a stroke icon is outlined at when it morphs with a fill icon. */
|
|
21
|
+
strokeWidth?: number | string;
|
|
18
22
|
}
|
|
19
23
|
interface MorphWatchProps extends MorphCtrlProps {
|
|
20
24
|
spring?: SpringPreset | MorphOptions;
|
|
@@ -24,6 +28,11 @@ interface MorphWatchProps extends MorphCtrlProps {
|
|
|
24
28
|
* without mismatch) and from then on only the driver mutates it outside the
|
|
25
29
|
* template. */
|
|
26
30
|
declare function computeInitialD({ icon, from, to, progress }: MorphModeProps): string;
|
|
31
|
+
/** Paint attributes of the initial `<path>` (SSR included), a constant like
|
|
32
|
+
* the initial d: a fill icon is filled before any script runs. A frozen
|
|
33
|
+
* stroke pair paints as the endpoint it is nearer to; one with a fill
|
|
34
|
+
* endpoint rests on `from` mid-way, like its d (frozenEnd). */
|
|
35
|
+
declare function computeInitialPaint({ icon, from, to, progress }: MorphModeProps): Record<string, string> | undefined;
|
|
27
36
|
/** Per-instance driver state — the exact logic of the React/Vue bindings
|
|
28
37
|
* (mount, mode watch, controlled seek with re-basing, imperative). Takes the
|
|
29
38
|
* init-time props: they seed the watch baselines so a watcher that fires on
|
|
@@ -40,15 +49,15 @@ declare function computeInitialD({ icon, from, to, progress }: MorphModeProps):
|
|
|
40
49
|
* path back to `icon`.
|
|
41
50
|
* - Every exit from controlled mode (imperative call or icon takeover)
|
|
42
51
|
* invalidates the frozen pair, so returning to it re-bases on `from`. */
|
|
43
|
-
declare function createController({ icon, from, to, progress, reducedMotion }: MorphCtrlProps): {
|
|
44
|
-
mount(mountEl: PathEl, { icon, from, to, progress, reducedMotion }: MorphCtrlProps): void;
|
|
52
|
+
declare function createController({ icon, from, to, progress, reducedMotion, strokeWidth }: MorphCtrlProps): {
|
|
53
|
+
mount(mountEl: PathEl, { icon, from, to, progress, reducedMotion, strokeWidth }: MorphCtrlProps): void;
|
|
45
54
|
destroy(): void;
|
|
46
55
|
/** Prop watcher: ONE owner decides per run — the controlled pair while it
|
|
47
56
|
* is fully present, `icon` otherwise (mount doesn't fire thanks to the
|
|
48
57
|
* init-time baselines). */
|
|
49
|
-
watch({ icon, from, to, progress, spring, reducedMotion }: MorphWatchProps): void;
|
|
58
|
+
watch({ icon, from, to, progress, spring, reducedMotion, strokeWidth }: MorphWatchProps): void;
|
|
50
59
|
morphTo(icon: IconInput, spring?: SpringPreset | MorphOptions): void;
|
|
51
60
|
set(icon: IconInput): void;
|
|
52
61
|
};
|
|
53
62
|
//#endregion
|
|
54
|
-
export { computeInitialD as n,
|
|
63
|
+
export { createController as i, computeInitialD as n, computeInitialPaint as r, MorphHandle as t };
|
package/dist/dom.d.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as ReducedMotionMode, i as PathEl, n as Morph, o as canonicalD, r as MorphOptions, s as createMorph, t as CreateMorphOptions } from "./index-
|
|
2
|
-
export { CreateMorphOptions, Morph, MorphOptions, PathEl, ReducedMotionMode, canonicalD, createMorph };
|
|
1
|
+
import { a as ReducedMotionMode, c as preloadRegion, i as PathEl, l as restAttrs, n as Morph, o as canonicalD, r as MorphOptions, s as createMorph, t as CreateMorphOptions, u as setRegionLoader } from "./index-DpvKR75N.js";
|
|
2
|
+
export { CreateMorphOptions, Morph, MorphOptions, PathEl, ReducedMotionMode, canonicalD, createMorph, preloadRegion, restAttrs, setRegionLoader };
|