morphicons 1.4.0 → 1.4.2
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 +26 -4
- package/dist/MorphIcon.svelte +4 -3
- package/dist/dom.d.ts +16 -4
- package/dist/dom.js +22 -9
- package/dist/index.d.ts +2 -1
- package/dist/index.js +1 -1
- package/dist/react-native.d.ts +7 -2
- package/dist/react-native.js +12 -5
- package/dist/react.d.ts +6 -2
- package/dist/react.js +10 -4
- package/dist/{spring-BTcHiHR5.js → spring-CBynweFS.js} +7 -3
- package/dist/svelte-shared.d.ts +11 -6
- package/dist/svelte-shared.js +10 -6
- package/dist/vue.d.ts +17 -2
- package/dist/vue.js +17 -5
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -13,7 +13,14 @@
|
|
|
13
13
|
<a href="https://www.morphicons.com"><strong>morphicons.com</strong></a> — live playground
|
|
14
14
|
</p>
|
|
15
15
|
|
|
16
|
-
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://github.com/sponsors/guillermolg00">
|
|
18
|
+
<img src="https://img.shields.io/badge/%E2%98%95%20Buy%20me%20a%20coffee-EA4AAA" alt="Buy me a coffee — sponsor morphicons on GitHub Sponsors">
|
|
19
|
+
</a>
|
|
20
|
+
</p>
|
|
21
|
+
|
|
22
|
+
Universal morphing for stroke-based icons (Lucide, Tab
|
|
23
|
+
ler, Heroicons, Iconoir, or your own paths): **any icon morphs into any other** with spring physics. Rotations are never declared by hand — they emerge from the math (2D Procrustes + polar interpolation). Zero runtime dependencies.
|
|
17
24
|
|
|
18
25
|
```tsx
|
|
19
26
|
import { MorphIcon } from "morphicons/react";
|
|
@@ -58,7 +65,7 @@ ref.current?.morphTo(Check); // animates
|
|
|
58
65
|
ref.current?.set(X); // jumps without animating
|
|
59
66
|
```
|
|
60
67
|
|
|
61
|
-
Drop-in replacement for lucide-react: `size`, `strokeWidth`, `absoluteStrokeWidth`, `color`, `className` and the rest of the `<svg>` props pass straight through. Correct accessibility by default: `aria-hidden` unless you pass `label` (→ `role="img"` + `<title>`). Clean SSR: the server emits the exact static SVG (zero flash, zero layout shift); the runtime is born on hydration.
|
|
68
|
+
Drop-in replacement for lucide-react: `size`, `strokeWidth`, `absoluteStrokeWidth`, `color`, `className` and the rest of the `<svg>` props pass straight through. Correct accessibility by default: `aria-hidden` unless you pass `label` (→ `role="img"` + `<title>`). Clean SSR: the server emits the exact static SVG (zero flash, zero layout shift); the runtime is born on hydration. Morphs play regardless of the OS reduce-motion setting by default; opt into honoring it with `reducedMotion="user"` (see [Reduced motion](#reduced-motion-all-four-bindings)).
|
|
62
69
|
|
|
63
70
|
### Vue — same three modes
|
|
64
71
|
|
|
@@ -132,7 +139,7 @@ ref.current?.morphTo(Check); // animates
|
|
|
132
139
|
ref.current?.set(X); // jumps without animating
|
|
133
140
|
```
|
|
134
141
|
|
|
135
|
-
The DOM-free core beyond the browser: the dom driver is reused verbatim as the engine (React Native has a global `requestAnimationFrame`, and `PathEl` is structural), so the whole platform difference is a shim that forwards the per-frame `d` write to `Path.setNativeProps` of react-native-svg — outside the React render, exactly like the web mutation. Same surface as the React binding (`size`, `strokeWidth`, `absoluteStrokeWidth`, `color`, plus the native `Svg` props: `testID`, touch handlers…), same accessibility defaults (`aria-hidden` unless you pass `label` → `role="img"` + `aria-label`).
|
|
142
|
+
The DOM-free core beyond the browser: the dom driver is reused verbatim as the engine (React Native has a global `requestAnimationFrame`, and `PathEl` is structural), so the whole platform difference is a shim that forwards the per-frame `d` write to `Path.setNativeProps` of react-native-svg — outside the React render, exactly like the web mutation. Same surface as the React binding (`size`, `strokeWidth`, `absoluteStrokeWidth`, `color`, plus the native `Svg` props: `testID`, touch handlers…), same accessibility defaults (`aria-hidden` unless you pass `label` → `role="img"` + `aria-label`). With `reducedMotion="user"`, the OS setting comes from `AccessibilityInfo` (best-effort: the query is async; a `reduceMotionChanged` subscription keeps it exact from then on). Requires Metro with package `exports` resolution — default since React Native 0.79; on older versions enable `unstable_enablePackageExports`.
|
|
136
143
|
|
|
137
144
|
### Lifecycle contract (all four bindings)
|
|
138
145
|
|
|
@@ -142,6 +149,20 @@ The four components share one contract, pinned by mirrored client-mount tests:
|
|
|
142
149
|
- **Controlled wins.** While `from` AND `to` are both present, the pair owns the path: `icon` changes are ignored, no spring fires. Drop the pair and the current `icon` takes over (animated). Mixing the modes is not an error — the precedence is just explicit.
|
|
143
150
|
- **Clean re-entry.** Any exit from controlled mode (an imperative call or an icon takeover) invalidates the frozen pair, so returning to the same `from`/`to` re-bases on `from` and renders exactly like a clean mount at that `progress`.
|
|
144
151
|
|
|
152
|
+
### Reduced motion (all four bindings)
|
|
153
|
+
|
|
154
|
+
Icon morphs are small, short, communicative micro-transitions: the kind of motion the reduce-motion guidance considers generally acceptable, unlike parallax or full-screen movement. Auto-degrading them made the library look broken to every user with the OS setting on, so since 1.4.2 they play by default and the policy is an explicit prop (the same default Motion, formerly Framer Motion, ships):
|
|
155
|
+
|
|
156
|
+
```tsx
|
|
157
|
+
<MorphIcon icon={open ? X : Menu} reducedMotion="user" />
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
- `"never"` (default): morphs always animate; the OS setting is ignored.
|
|
161
|
+
- `"user"`: while the OS reduce-motion setting is on, every `morphTo` degrades to an instant swap (the pre-1.4.2 behavior). The web bindings read `prefers-reduced-motion`; React Native reads `AccessibilityInfo`, and only touches the accessibility bridge when some instance opts in.
|
|
162
|
+
- `"always"`: every `morphTo` jumps without animating (tests, screenshots).
|
|
163
|
+
|
|
164
|
+
The prop is live: changing it governs the next morph. The vanilla driver takes the same policy as an option, `createMorph(el, icon, { reducedMotion: "user" })`, and exposes it as a mutable `morph.reducedMotion`. Apps that must strictly follow the user's system preference should pass `reducedMotion="user"` across the board.
|
|
165
|
+
|
|
145
166
|
### Vanilla (no React)
|
|
146
167
|
|
|
147
168
|
```ts
|
|
@@ -152,6 +173,7 @@ m.morphTo(X, "snappy"); // preset or { stiffness, damping }
|
|
|
152
173
|
m.set(Check); // jump without animating (canonical d)
|
|
153
174
|
m.seek(X, 0.4); // morph frozen at t — scrubbing, gestures
|
|
154
175
|
m.progress = 0.4; // sugar: seek on the active target
|
|
176
|
+
m.reducedMotion = "user"; // live policy: "never" (default) | "user" | "always"
|
|
155
177
|
m.destroy();
|
|
156
178
|
```
|
|
157
179
|
|
|
@@ -312,7 +334,7 @@ c_k(t) = c_{A,k} + t·d_k + (σ*ᵗ·R(t·θ*) − I)·(c_{A,k} − g_A)
|
|
|
312
334
|
|
|
313
335
|
### 6. Serialization and canonical snap
|
|
314
336
|
|
|
315
|
-
In flight, each subpath is emitted as `M x y L x y …` (+ `Z` if the pair is closed↔closed; closed→open flies open, the loop opening at the cut chosen by the circular correspondence) with 2 decimals — invisible at 24px, and the frame's only allocation. On settle, the driver **snaps to the canonical `d`** of the target icon: exact fidelity at rest (real curves, not polylines), subpath count reset after duplications, and the DOM ends up identical to a static icon's. The jump is < 0.02px — imperceptible.
|
|
337
|
+
In flight, each subpath is emitted as `M x y L x y …` (+ `Z` if the pair is closed↔closed; closed→open flies open, the loop opening at the cut chosen by the circular correspondence) with 2 decimals — invisible at 24px, and the frame's only allocation. On settle, the driver **snaps to the canonical `d`** of the target icon: exact fidelity at rest (real curves, not polylines), subpath count reset after duplications, and the DOM ends up identical to a static icon's. The jump is < 0.02px — imperceptible. The canonical `d` itself is emitted with 4 decimals, and that is a correctness choice, not a size one: arc→cubic conversion goes through trig whose last ulp differs across JS engines, and SSR hydration compares the server's bytes against the browser's — quantized emission keeps them identical, where full precision leaked each engine's ulp into the markup (a Next hydration mismatch on icons with non-cardinal arcs).
|
|
316
338
|
|
|
317
339
|
### 7. The spring
|
|
318
340
|
|
package/dist/MorphIcon.svelte
CHANGED
|
@@ -25,6 +25,7 @@ let {
|
|
|
25
25
|
to,
|
|
26
26
|
progress,
|
|
27
27
|
spring,
|
|
28
|
+
reducedMotion,
|
|
28
29
|
size = 24,
|
|
29
30
|
color = "currentColor",
|
|
30
31
|
strokeWidth = 2,
|
|
@@ -40,17 +41,17 @@ const initialD = computeInitialD({ icon, from, to, progress });
|
|
|
40
41
|
let pathEl: SVGPathElement | undefined = $state();
|
|
41
42
|
// svelte-ignore state_referenced_locally — init-time values on purpose: they
|
|
42
43
|
// seed the controller's watch baselines so the first $effect run is a no-op.
|
|
43
|
-
const ctrl = createController({ icon, from, to, progress });
|
|
44
|
+
const ctrl = createController({ icon, from, to, progress, reducedMotion });
|
|
44
45
|
|
|
45
46
|
onMount(() => {
|
|
46
|
-
if (pathEl) ctrl.mount(pathEl, { icon, from, to, progress });
|
|
47
|
+
if (pathEl) ctrl.mount(pathEl, { icon, from, to, progress, reducedMotion });
|
|
47
48
|
return () => ctrl.destroy();
|
|
48
49
|
});
|
|
49
50
|
|
|
50
51
|
// Prop changes: one watcher owns the mode logic (controlled wins — change
|
|
51
52
|
// detection and precedence live in shared.ts).
|
|
52
53
|
$effect(() => {
|
|
53
|
-
const next = { icon, from, to, progress, spring };
|
|
54
|
+
const next = { icon, from, to, progress, spring, reducedMotion };
|
|
54
55
|
untrack(() => ctrl.watch(next));
|
|
55
56
|
});
|
|
56
57
|
|
package/dist/dom.d.ts
CHANGED
|
@@ -10,6 +10,15 @@ interface MorphOptions {
|
|
|
10
10
|
stiffness?: number;
|
|
11
11
|
damping?: number;
|
|
12
12
|
}
|
|
13
|
+
/** Reduced-motion policy. `"never"` (default): morphs always animate,
|
|
14
|
+
* ignoring the OS setting. `"user"`: while the OS reduce-motion setting is
|
|
15
|
+
* on, `morphTo` degrades to an instant `set`. `"always"`: every `morphTo`
|
|
16
|
+
* jumps (tests, screenshots). */
|
|
17
|
+
type ReducedMotionMode = "never" | "user" | "always";
|
|
18
|
+
interface CreateMorphOptions {
|
|
19
|
+
/** Reduced-motion policy; also live via the `reducedMotion` property. */
|
|
20
|
+
reducedMotion?: ReducedMotionMode;
|
|
21
|
+
}
|
|
13
22
|
interface Morph {
|
|
14
23
|
/** Animates toward the icon with spring physics. Interruptible: mid-flight
|
|
15
24
|
* it re-plans from the intermediate shape while preserving velocity. */
|
|
@@ -23,14 +32,17 @@ interface Morph {
|
|
|
23
32
|
/** Current progress (t of the last frame; 1 at rest). Assigning it is
|
|
24
33
|
* equivalent to seek(target, t) on the active target. */
|
|
25
34
|
progress: number;
|
|
35
|
+
/** Reduced-motion policy, live: assigning it applies to the next morphTo. */
|
|
36
|
+
reducedMotion: ReducedMotionMode;
|
|
26
37
|
/** Unregisters the instance; later calls are no-ops. */
|
|
27
38
|
destroy(): void;
|
|
28
39
|
}
|
|
29
40
|
/** Canonical `d` of an icon: the input string verbatim, or the real cubics
|
|
30
|
-
*
|
|
31
|
-
*
|
|
41
|
+
* quantized to 4 decimals (the at-rest snap; engine-stable bytes so SSR
|
|
42
|
+
* hydration matches, see fmtCanon in core/serialize). Exported because it
|
|
43
|
+
* is what a binding renders at SSR/rest before any runtime exists. */
|
|
32
44
|
declare function canonicalD(icon: IconInput): string;
|
|
33
45
|
/** Creates the morph instance over a `<path>` and paints the initial icon. */
|
|
34
|
-
declare function createMorph(el: PathEl, icon: IconInput): Morph;
|
|
46
|
+
declare function createMorph(el: PathEl, icon: IconInput, options?: CreateMorphOptions): Morph;
|
|
35
47
|
//#endregion
|
|
36
|
-
export { Morph, MorphOptions, PathEl, canonicalD, createMorph };
|
|
48
|
+
export { CreateMorphOptions, Morph, MorphOptions, PathEl, ReducedMotionMode, canonicalD, createMorph };
|
package/dist/dom.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as buildPlan, c as cubicsToPathD, f as interpPolar, l as serialize, n as Spring, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, c as cubicsToPathD, f as interpPolar, l as serialize, n as Spring, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-CBynweFS.js";
|
|
2
2
|
//#region src/dom/index.ts
|
|
3
3
|
const tickers = /* @__PURE__ */ new Set();
|
|
4
4
|
let rafId = 0;
|
|
@@ -41,8 +41,9 @@ function sampledOf(icon) {
|
|
|
41
41
|
return s;
|
|
42
42
|
}
|
|
43
43
|
/** Canonical `d` of an icon: the input string verbatim, or the real cubics
|
|
44
|
-
*
|
|
45
|
-
*
|
|
44
|
+
* quantized to 4 decimals (the at-rest snap; engine-stable bytes so SSR
|
|
45
|
+
* hydration matches, see fmtCanon in core/serialize). Exported because it
|
|
46
|
+
* is what a binding renders at SSR/rest before any runtime exists. */
|
|
46
47
|
function canonicalD(icon) {
|
|
47
48
|
if (typeof icon === "string") return icon;
|
|
48
49
|
let d = canon.get(icon);
|
|
@@ -75,8 +76,9 @@ function resolveSpring(s) {
|
|
|
75
76
|
};
|
|
76
77
|
}
|
|
77
78
|
/** Creates the morph instance over a `<path>` and paints the initial icon. */
|
|
78
|
-
function createMorph(el, icon) {
|
|
79
|
+
function createMorph(el, icon, options) {
|
|
79
80
|
const spring = new Spring();
|
|
81
|
+
let reducedMotion = options?.reducedMotion ?? "never";
|
|
80
82
|
let target = icon;
|
|
81
83
|
let rest = true;
|
|
82
84
|
let plan = null;
|
|
@@ -141,6 +143,13 @@ function createMorph(el, icon) {
|
|
|
141
143
|
target = icon;
|
|
142
144
|
settle();
|
|
143
145
|
};
|
|
146
|
+
/** True when the policy says this morphTo must jump instead of flying. */
|
|
147
|
+
const motionOff = () => {
|
|
148
|
+
if (reducedMotion === "always") return true;
|
|
149
|
+
if (reducedMotion !== "user") return false;
|
|
150
|
+
if (typeof matchMedia === "undefined") return false;
|
|
151
|
+
return matchMedia?.("(prefers-reduced-motion: reduce)").matches ?? false;
|
|
152
|
+
};
|
|
144
153
|
const seek = (icon, tt) => {
|
|
145
154
|
if (dead) return;
|
|
146
155
|
const reuse = !rest && plan !== null && icon === target;
|
|
@@ -153,11 +162,9 @@ function createMorph(el, icon) {
|
|
|
153
162
|
morphTo(icon, sp) {
|
|
154
163
|
if (dead) return;
|
|
155
164
|
if (icon === target && (rest || flying)) return;
|
|
156
|
-
if (
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
return;
|
|
160
|
-
}
|
|
165
|
+
if (motionOff()) {
|
|
166
|
+
setNow(icon);
|
|
167
|
+
return;
|
|
161
168
|
}
|
|
162
169
|
const { k, c } = resolveSpring(sp);
|
|
163
170
|
spring.config(k, c);
|
|
@@ -179,6 +186,12 @@ function createMorph(el, icon) {
|
|
|
179
186
|
set progress(v) {
|
|
180
187
|
if (!dead) seek(target, v);
|
|
181
188
|
},
|
|
189
|
+
get reducedMotion() {
|
|
190
|
+
return reducedMotion;
|
|
191
|
+
},
|
|
192
|
+
set reducedMotion(v) {
|
|
193
|
+
reducedMotion = v;
|
|
194
|
+
},
|
|
182
195
|
destroy() {
|
|
183
196
|
stop();
|
|
184
197
|
dead = true;
|
package/dist/index.d.ts
CHANGED
|
@@ -72,7 +72,8 @@ declare function resampleIcon(input: IconInput, N?: number): Sampled[];
|
|
|
72
72
|
/** Sampled subpaths → polyline `d` attribute. `closed[k]` appends Z to
|
|
73
73
|
* subpath k (closed loops in flight); without flags everything is open. */
|
|
74
74
|
declare function serialize(subs: readonly Float64Array[], closed?: readonly boolean[]): string;
|
|
75
|
-
/** Cubic subpaths → canonical `d
|
|
75
|
+
/** Cubic subpaths → canonical `d`, quantized to 4 decimals (engine-stable
|
|
76
|
+
* bytes; see fmtCanon). */
|
|
76
77
|
declare function cubicsToPathD(paths: readonly CubicPath[]): string;
|
|
77
78
|
//#endregion
|
|
78
79
|
export { type CubicPath, type IconInput, type IconNode, type IconNodeAttrs, type MorphPlan, type PlanItem, SPRING_PRESETS, type Sampled, Spring, type SpringPreset, type ViewBox, allocOutputs, buildPlan, cubicsToPathD, fitIcon, iconToCubics, interpLinear, interpPolar, resampleIcon, resamplePath, serialize };
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as buildPlan, c as cubicsToPathD, d as interpLinear, f as interpPolar, i as resamplePath, l as serialize, n as Spring, o as fitIcon, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, c as cubicsToPathD, d as interpLinear, f as interpPolar, i as resamplePath, l as serialize, n as Spring, o as fitIcon, r as resampleIcon, s as iconToCubics, t as SPRING_PRESETS, u as allocOutputs } from "./spring-CBynweFS.js";
|
|
2
2
|
export { SPRING_PRESETS, Spring, allocOutputs, buildPlan, cubicsToPathD, fitIcon, iconToCubics, interpLinear, interpPolar, resampleIcon, resamplePath, serialize };
|
package/dist/react-native.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as Sampled, c as SpringPreset, n as IconInput, r as IconNode } from "./types-JAF3s76Y.js";
|
|
2
|
-
import { Morph, MorphOptions, PathEl } from "./dom.js";
|
|
2
|
+
import { Morph, MorphOptions, PathEl, ReducedMotionMode } from "./dom.js";
|
|
3
3
|
import { SvgProps } from "react-native-svg";
|
|
4
4
|
//#region src/react-native/index.d.ts
|
|
5
5
|
/** Imperative surface exposed via ref. */
|
|
@@ -18,6 +18,11 @@ interface MorphIconProps extends Omit<SvgProps, "from" | "to"> {
|
|
|
18
18
|
progress?: number;
|
|
19
19
|
/** Physics for uncontrolled/imperative mode: preset or custom spring. */
|
|
20
20
|
spring?: SpringPreset | MorphOptions;
|
|
21
|
+
/** Reduced-motion policy: "never" (default) animates regardless of the OS
|
|
22
|
+
* setting, "user" honors AccessibilityInfo's reduce motion (morphs degrade
|
|
23
|
+
* to an instant swap while it is on; best-effort, the query is async),
|
|
24
|
+
* "always" always jumps. */
|
|
25
|
+
reducedMotion?: ReducedMotionMode;
|
|
21
26
|
size?: number | string;
|
|
22
27
|
color?: string;
|
|
23
28
|
strokeWidth?: number | string;
|
|
@@ -28,4 +33,4 @@ interface MorphIconProps extends Omit<SvgProps, "from" | "to"> {
|
|
|
28
33
|
}
|
|
29
34
|
declare const MorphIcon: import("react").ForwardRefExoticComponent<MorphIconProps & import("react").RefAttributes<MorphHandle>>;
|
|
30
35
|
//#endregion
|
|
31
|
-
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIcon, MorphIconProps, type MorphOptions, type PathEl, type Sampled, type SpringPreset };
|
|
36
|
+
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIcon, MorphIconProps, type MorphOptions, type PathEl, type ReducedMotionMode, type Sampled, type SpringPreset };
|
package/dist/react-native.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
|
|
2
2
|
import { canonicalD, createMorph } from "./dom.js";
|
|
3
3
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { jsx } from "react/jsx-runtime";
|
|
@@ -31,7 +31,7 @@ function frozenD(from, to, t) {
|
|
|
31
31
|
return serialize(out, plan.items.map((it) => it.closed));
|
|
32
32
|
}
|
|
33
33
|
const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
34
|
-
const { icon, from, to, progress, spring, size = 24, color = "currentColor", strokeWidth = 2, absoluteStrokeWidth, label, ...rest } = props;
|
|
34
|
+
const { icon, from, to, progress, spring, reducedMotion, size = 24, color = "currentColor", strokeWidth = 2, absoluteStrokeWidth, label, ...rest } = props;
|
|
35
35
|
const controlled = from !== void 0 && to !== void 0;
|
|
36
36
|
const initialIcon = icon ?? from ?? to;
|
|
37
37
|
const [initialD] = useState(() => {
|
|
@@ -42,6 +42,8 @@ const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
|
42
42
|
const morphRef = useRef(null);
|
|
43
43
|
const springRef = useRef(spring);
|
|
44
44
|
springRef.current = spring;
|
|
45
|
+
const rmRef = useRef(reducedMotion);
|
|
46
|
+
rmRef.current = reducedMotion;
|
|
45
47
|
const prevIcon = useRef(icon);
|
|
46
48
|
const prevControlled = useRef(controlled);
|
|
47
49
|
const dead = useRef(false);
|
|
@@ -52,9 +54,12 @@ const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
|
52
54
|
const el = useRef({ setAttribute: (name, value) => {
|
|
53
55
|
if (name === "d") pathRef.current?.setNativeProps({ d: value });
|
|
54
56
|
} });
|
|
55
|
-
/** morphTo that
|
|
57
|
+
/** morphTo that applies the reduced-motion policy ("user" reads the OS
|
|
58
|
+
* setting via AccessibilityInfo, best-effort). */
|
|
56
59
|
const fly = (m, i, s) => {
|
|
57
|
-
|
|
60
|
+
const mode = rmRef.current ?? "never";
|
|
61
|
+
if (mode === "user") initReducedMotion();
|
|
62
|
+
if (mode === "always" || mode === "user" && reduced) m.set(i);
|
|
58
63
|
else m.morphTo(i, s);
|
|
59
64
|
};
|
|
60
65
|
const flyRef = useRef(fly);
|
|
@@ -71,7 +76,6 @@ const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
|
71
76
|
}, []);
|
|
72
77
|
useIsoLayoutEffect(() => {
|
|
73
78
|
dead.current = false;
|
|
74
|
-
initReducedMotion();
|
|
75
79
|
if (pathRef.current && initialIcon !== void 0) {
|
|
76
80
|
const m = createMorph(el.current, controlled ? from : initialIcon);
|
|
77
81
|
morphRef.current = m;
|
|
@@ -94,6 +98,9 @@ const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
|
94
98
|
pair.current = null;
|
|
95
99
|
};
|
|
96
100
|
}, []);
|
|
101
|
+
useEffect(() => {
|
|
102
|
+
if (reducedMotion === "user") initReducedMotion();
|
|
103
|
+
}, [reducedMotion]);
|
|
97
104
|
useEffect(() => {
|
|
98
105
|
const left = prevControlled.current && !controlled;
|
|
99
106
|
prevControlled.current = controlled;
|
package/dist/react.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as Sampled, c as SpringPreset, n as IconInput, r as IconNode } from "./types-JAF3s76Y.js";
|
|
2
|
-
import { Morph, MorphOptions, PathEl } from "./dom.js";
|
|
2
|
+
import { Morph, MorphOptions, PathEl, ReducedMotionMode } from "./dom.js";
|
|
3
3
|
import { SVGProps } from "react";
|
|
4
4
|
//#region src/react/index.d.ts
|
|
5
5
|
/** Imperative surface exposed via ref. */
|
|
@@ -18,6 +18,10 @@ interface MorphIconProps extends Omit<SVGProps<SVGSVGElement>, "from" | "to" | "
|
|
|
18
18
|
progress?: number;
|
|
19
19
|
/** Physics for uncontrolled/imperative mode: preset or custom spring. */
|
|
20
20
|
spring?: SpringPreset | MorphOptions;
|
|
21
|
+
/** Reduced-motion policy: "never" (default) animates regardless of the OS
|
|
22
|
+
* setting, "user" honors prefers-reduced-motion (morphs degrade to an
|
|
23
|
+
* instant swap while it is on), "always" always jumps. */
|
|
24
|
+
reducedMotion?: ReducedMotionMode;
|
|
21
25
|
size?: number | string;
|
|
22
26
|
color?: string;
|
|
23
27
|
strokeWidth?: number | string;
|
|
@@ -28,4 +32,4 @@ interface MorphIconProps extends Omit<SVGProps<SVGSVGElement>, "from" | "to" | "
|
|
|
28
32
|
}
|
|
29
33
|
declare const MorphIcon: import("react").ForwardRefExoticComponent<MorphIconProps & import("react").RefAttributes<MorphHandle>>;
|
|
30
34
|
//#endregion
|
|
31
|
-
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIcon, MorphIconProps, type MorphOptions, type PathEl, type Sampled, type SpringPreset };
|
|
35
|
+
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIcon, MorphIconProps, type MorphOptions, type PathEl, type ReducedMotionMode, type Sampled, type SpringPreset };
|
package/dist/react.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
|
|
2
2
|
import { canonicalD, createMorph } from "./dom.js";
|
|
3
3
|
import { forwardRef, useCallback, useEffect, useImperativeHandle, useLayoutEffect, useRef, useState } from "react";
|
|
4
4
|
import { jsx, jsxs } from "react/jsx-runtime";
|
|
@@ -15,7 +15,7 @@ function frozenD(from, to, t) {
|
|
|
15
15
|
return serialize(out, plan.items.map((it) => it.closed));
|
|
16
16
|
}
|
|
17
17
|
const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
18
|
-
const { icon, from, to, progress, spring, size = 24, color = "currentColor", strokeWidth = 2, absoluteStrokeWidth, label, ...rest } = props;
|
|
18
|
+
const { icon, from, to, progress, spring, reducedMotion, size = 24, color = "currentColor", strokeWidth = 2, absoluteStrokeWidth, label, ...rest } = props;
|
|
19
19
|
const controlled = from !== void 0 && to !== void 0;
|
|
20
20
|
const initialIcon = icon ?? from ?? to;
|
|
21
21
|
const [initialD] = useState(() => {
|
|
@@ -26,6 +26,8 @@ const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
|
26
26
|
const morphRef = useRef(null);
|
|
27
27
|
const springRef = useRef(spring);
|
|
28
28
|
springRef.current = spring;
|
|
29
|
+
const rmRef = useRef(reducedMotion);
|
|
30
|
+
rmRef.current = reducedMotion;
|
|
29
31
|
const prevIcon = useRef(icon);
|
|
30
32
|
const prevControlled = useRef(controlled);
|
|
31
33
|
const dead = useRef(false);
|
|
@@ -39,14 +41,14 @@ const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
|
39
41
|
if (morphRef.current) return morphRef.current;
|
|
40
42
|
const el = pathRef.current;
|
|
41
43
|
if (dead.current || !el) return null;
|
|
42
|
-
morphRef.current = createMorph(el, birth);
|
|
44
|
+
morphRef.current = createMorph(el, birth, { reducedMotion: rmRef.current });
|
|
43
45
|
return morphRef.current;
|
|
44
46
|
}, []);
|
|
45
47
|
useIsoLayoutEffect(() => {
|
|
46
48
|
dead.current = false;
|
|
47
49
|
const el = pathRef.current;
|
|
48
50
|
if (el && initialIcon !== void 0) {
|
|
49
|
-
const m = createMorph(el, controlled ? from : initialIcon);
|
|
51
|
+
const m = createMorph(el, controlled ? from : initialIcon, { reducedMotion: rmRef.current });
|
|
50
52
|
morphRef.current = m;
|
|
51
53
|
if (controlled) {
|
|
52
54
|
pair.current = [from, to];
|
|
@@ -67,6 +69,10 @@ const MorphIcon = forwardRef(function MorphIcon(props, ref) {
|
|
|
67
69
|
pair.current = null;
|
|
68
70
|
};
|
|
69
71
|
}, []);
|
|
72
|
+
useEffect(() => {
|
|
73
|
+
const m = morphRef.current;
|
|
74
|
+
if (m) m.reducedMotion = reducedMotion ?? "never";
|
|
75
|
+
}, [reducedMotion]);
|
|
70
76
|
useEffect(() => {
|
|
71
77
|
const left = prevControlled.current && !controlled;
|
|
72
78
|
prevControlled.current = controlled;
|
|
@@ -305,12 +305,16 @@ function serialize(subs, closed) {
|
|
|
305
305
|
}
|
|
306
306
|
return d;
|
|
307
307
|
}
|
|
308
|
-
|
|
308
|
+
function fmtCanon(v) {
|
|
309
|
+
return String(Math.round(v * 1e4) / 1e4);
|
|
310
|
+
}
|
|
311
|
+
/** Cubic subpaths → canonical `d`, quantized to 4 decimals (engine-stable
|
|
312
|
+
* bytes; see fmtCanon). */
|
|
309
313
|
function cubicsToPathD(paths) {
|
|
310
314
|
let d = "";
|
|
311
315
|
for (const { pts, closed } of paths) {
|
|
312
|
-
d += `M${pts[0]} ${pts[1]}`;
|
|
313
|
-
for (let i = 2; i < pts.length; i += 6) d += `C${pts[i]} ${pts[i + 1]} ${pts[i + 2]} ${pts[i + 3]} ${pts[i + 4]} ${pts[i + 5]}`;
|
|
316
|
+
d += `M${fmtCanon(pts[0])} ${fmtCanon(pts[1])}`;
|
|
317
|
+
for (let i = 2; i < pts.length; i += 6) d += `C${fmtCanon(pts[i])} ${fmtCanon(pts[i + 1])} ${fmtCanon(pts[i + 2])} ${fmtCanon(pts[i + 3])} ${fmtCanon(pts[i + 4])} ${fmtCanon(pts[i + 5])}`;
|
|
314
318
|
if (closed) d += "Z";
|
|
315
319
|
}
|
|
316
320
|
return d;
|
package/dist/svelte-shared.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as Sampled, c as SpringPreset, n as IconInput, r as IconNode } from "./types-JAF3s76Y.js";
|
|
2
|
-
import { Morph, MorphOptions, PathEl } from "./dom.js";
|
|
2
|
+
import { Morph, MorphOptions, PathEl, ReducedMotionMode } from "./dom.js";
|
|
3
3
|
import { SVGAttributes } from "svelte/elements";
|
|
4
4
|
//#region src/svelte/shared.d.ts
|
|
5
5
|
/** Imperative surface exposed on the component instance (`bind:this`). */
|
|
@@ -22,6 +22,10 @@ interface MorphIconProps extends Omit<SVGAttributes<SVGSVGElement>, "from" | "to
|
|
|
22
22
|
progress?: number;
|
|
23
23
|
/** Physics for uncontrolled/imperative mode: preset or custom spring. */
|
|
24
24
|
spring?: SpringPreset | MorphOptions;
|
|
25
|
+
/** Reduced-motion policy: "never" (default) animates regardless of the OS
|
|
26
|
+
* setting, "user" honors prefers-reduced-motion (morphs degrade to an
|
|
27
|
+
* instant swap while it is on), "always" always jumps. */
|
|
28
|
+
reducedMotion?: ReducedMotionMode;
|
|
25
29
|
size?: number | string;
|
|
26
30
|
color?: string;
|
|
27
31
|
strokeWidth?: number | string;
|
|
@@ -31,7 +35,8 @@ interface MorphIconProps extends Omit<SVGAttributes<SVGSVGElement>, "from" | "to
|
|
|
31
35
|
label?: string;
|
|
32
36
|
}
|
|
33
37
|
type ModeProps = Pick<MorphIconProps, "icon" | "from" | "to" | "progress">;
|
|
34
|
-
type
|
|
38
|
+
type CtrlProps = ModeProps & Pick<MorphIconProps, "reducedMotion">;
|
|
39
|
+
type WatchProps = CtrlProps & {
|
|
35
40
|
spring?: SpringPreset | MorphOptions;
|
|
36
41
|
};
|
|
37
42
|
/** The initial d is constant for Svelte: computed once from the mount-time
|
|
@@ -54,15 +59,15 @@ declare function computeInitialD({ icon, from, to, progress }: ModeProps): strin
|
|
|
54
59
|
* path back to `icon`.
|
|
55
60
|
* - Every exit from controlled mode (imperative call or icon takeover)
|
|
56
61
|
* invalidates the frozen pair, so returning to it re-bases on `from`. */
|
|
57
|
-
declare function createController({ icon, from, to, progress }:
|
|
58
|
-
mount(mountEl: PathEl, { icon, from, to, progress }:
|
|
62
|
+
declare function createController({ icon, from, to, progress, reducedMotion }: CtrlProps): {
|
|
63
|
+
mount(mountEl: PathEl, { icon, from, to, progress, reducedMotion }: CtrlProps): void;
|
|
59
64
|
destroy(): void;
|
|
60
65
|
/** Prop watcher: ONE owner decides per run — the controlled pair while it
|
|
61
66
|
* is fully present, `icon` otherwise (mount doesn't fire thanks to the
|
|
62
67
|
* init-time baselines). */
|
|
63
|
-
watch({ icon, from, to, progress, spring }: WatchProps): void;
|
|
68
|
+
watch({ icon, from, to, progress, spring, reducedMotion }: WatchProps): void;
|
|
64
69
|
morphTo(icon: IconInput, spring?: SpringPreset | MorphOptions): void;
|
|
65
70
|
set(icon: IconInput): void;
|
|
66
71
|
};
|
|
67
72
|
//#endregion
|
|
68
|
-
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIconProps, type MorphOptions, type PathEl, type Sampled, type SpringPreset, computeInitialD, createController };
|
|
73
|
+
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIconProps, type MorphOptions, type PathEl, type ReducedMotionMode, type Sampled, type SpringPreset, computeInitialD, createController };
|
package/dist/svelte-shared.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
|
|
2
2
|
import { canonicalD, createMorph } from "./dom.js";
|
|
3
3
|
//#region src/svelte/shared.ts
|
|
4
4
|
/** Frozen shape of the from→to pair at t, using the pure core (SSR-safe).
|
|
@@ -35,10 +35,11 @@ function computeInitialD({ icon, from, to, progress }) {
|
|
|
35
35
|
* path back to `icon`.
|
|
36
36
|
* - Every exit from controlled mode (imperative call or icon takeover)
|
|
37
37
|
* invalidates the frozen pair, so returning to it re-bases on `from`. */
|
|
38
|
-
function createController({ icon, from, to, progress }) {
|
|
38
|
+
function createController({ icon, from, to, progress, reducedMotion }) {
|
|
39
39
|
let el = null;
|
|
40
40
|
let dead = false;
|
|
41
41
|
let morph = null;
|
|
42
|
+
let rm = reducedMotion ?? "never";
|
|
42
43
|
let based = false;
|
|
43
44
|
let pair = null;
|
|
44
45
|
let prevIcon = icon;
|
|
@@ -50,7 +51,7 @@ function createController({ icon, from, to, progress }) {
|
|
|
50
51
|
const ensure = (birth) => {
|
|
51
52
|
if (morph) return morph;
|
|
52
53
|
if (dead || !el) return null;
|
|
53
|
-
morph = createMorph(el, birth);
|
|
54
|
+
morph = createMorph(el, birth, { reducedMotion: rm });
|
|
54
55
|
return morph;
|
|
55
56
|
};
|
|
56
57
|
/** Controlled mode: freeze the pair at `progress` via seek (no spring). */
|
|
@@ -77,12 +78,13 @@ function createController({ icon, from, to, progress }) {
|
|
|
77
78
|
}
|
|
78
79
|
};
|
|
79
80
|
return {
|
|
80
|
-
mount(mountEl, { icon, from, to, progress }) {
|
|
81
|
+
mount(mountEl, { icon, from, to, progress, reducedMotion }) {
|
|
81
82
|
el = mountEl;
|
|
83
|
+
rm = reducedMotion ?? rm;
|
|
82
84
|
const controlled = from !== void 0 && to !== void 0;
|
|
83
85
|
const initialIcon = icon ?? from ?? to;
|
|
84
86
|
if (initialIcon === void 0) return;
|
|
85
|
-
const m = createMorph(el, controlled ? from : initialIcon);
|
|
87
|
+
const m = createMorph(el, controlled ? from : initialIcon, { reducedMotion: rm });
|
|
86
88
|
morph = m;
|
|
87
89
|
if (controlled) {
|
|
88
90
|
pair = [from, to];
|
|
@@ -106,7 +108,9 @@ function createController({ icon, from, to, progress }) {
|
|
|
106
108
|
/** Prop watcher: ONE owner decides per run — the controlled pair while it
|
|
107
109
|
* is fully present, `icon` otherwise (mount doesn't fire thanks to the
|
|
108
110
|
* init-time baselines). */
|
|
109
|
-
watch({ icon, from, to, progress, spring }) {
|
|
111
|
+
watch({ icon, from, to, progress, spring, reducedMotion }) {
|
|
112
|
+
rm = reducedMotion ?? "never";
|
|
113
|
+
if (morph) morph.reducedMotion = rm;
|
|
110
114
|
const controlled = from !== void 0 && to !== void 0;
|
|
111
115
|
const left = prevControlled && !controlled;
|
|
112
116
|
const iconChanged = icon !== prevIcon;
|
package/dist/vue.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { a as Sampled, c as SpringPreset, n as IconInput, r as IconNode } from "./types-JAF3s76Y.js";
|
|
2
|
-
import { Morph, MorphOptions, PathEl } from "./dom.js";
|
|
2
|
+
import { Morph, MorphOptions, PathEl, ReducedMotionMode } from "./dom.js";
|
|
3
3
|
import { PropType } from "vue";
|
|
4
4
|
//#region src/vue/index.d.ts
|
|
5
5
|
/** Imperative surface exposed on the component instance (template ref). */
|
|
@@ -33,6 +33,13 @@ declare const MorphIcon: import("vue").DefineComponent<import("vue").ExtractProp
|
|
|
33
33
|
type: PropType<SpringPreset | MorphOptions>;
|
|
34
34
|
default: undefined;
|
|
35
35
|
};
|
|
36
|
+
/** Reduced-motion policy: "never" (default) animates regardless of the OS
|
|
37
|
+
* setting, "user" honors prefers-reduced-motion (morphs degrade to an
|
|
38
|
+
* instant swap while it is on), "always" always jumps. */
|
|
39
|
+
reducedMotion: {
|
|
40
|
+
type: PropType<ReducedMotionMode>;
|
|
41
|
+
default: string;
|
|
42
|
+
};
|
|
36
43
|
size: {
|
|
37
44
|
type: (NumberConstructor | StringConstructor)[];
|
|
38
45
|
default: number;
|
|
@@ -83,6 +90,13 @@ declare const MorphIcon: import("vue").DefineComponent<import("vue").ExtractProp
|
|
|
83
90
|
type: PropType<SpringPreset | MorphOptions>;
|
|
84
91
|
default: undefined;
|
|
85
92
|
};
|
|
93
|
+
/** Reduced-motion policy: "never" (default) animates regardless of the OS
|
|
94
|
+
* setting, "user" honors prefers-reduced-motion (morphs degrade to an
|
|
95
|
+
* instant swap while it is on), "always" always jumps. */
|
|
96
|
+
reducedMotion: {
|
|
97
|
+
type: PropType<ReducedMotionMode>;
|
|
98
|
+
default: string;
|
|
99
|
+
};
|
|
86
100
|
size: {
|
|
87
101
|
type: (NumberConstructor | StringConstructor)[];
|
|
88
102
|
default: number;
|
|
@@ -111,6 +125,7 @@ declare const MorphIcon: import("vue").DefineComponent<import("vue").ExtractProp
|
|
|
111
125
|
to: IconInput;
|
|
112
126
|
progress: number;
|
|
113
127
|
spring: "bouncy" | "smooth" | "snappy" | MorphOptions;
|
|
128
|
+
reducedMotion: ReducedMotionMode;
|
|
114
129
|
size: string | number;
|
|
115
130
|
color: string;
|
|
116
131
|
strokeWidth: string | number;
|
|
@@ -118,4 +133,4 @@ declare const MorphIcon: import("vue").DefineComponent<import("vue").ExtractProp
|
|
|
118
133
|
label: string;
|
|
119
134
|
}, {}, {}, {}, string, import("vue").ComponentProvideOptions, true, {}, any>;
|
|
120
135
|
//#endregion
|
|
121
|
-
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIcon, type MorphOptions, type PathEl, type Sampled, type SpringPreset };
|
|
136
|
+
export { type IconInput, type IconNode, type Morph, MorphHandle, MorphIcon, type MorphOptions, type PathEl, type ReducedMotionMode, type Sampled, type SpringPreset };
|
package/dist/vue.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-
|
|
1
|
+
import { a as buildPlan, f as interpPolar, l as serialize, r as resampleIcon, u as allocOutputs } from "./spring-CBynweFS.js";
|
|
2
2
|
import { canonicalD, createMorph } from "./dom.js";
|
|
3
3
|
import { defineComponent, h, onBeforeUnmount, onMounted, shallowRef, watch } from "vue";
|
|
4
4
|
//#region src/vue/index.ts
|
|
@@ -40,6 +40,13 @@ const MorphIcon = defineComponent({
|
|
|
40
40
|
type: [String, Object],
|
|
41
41
|
default: void 0
|
|
42
42
|
},
|
|
43
|
+
/** Reduced-motion policy: "never" (default) animates regardless of the OS
|
|
44
|
+
* setting, "user" honors prefers-reduced-motion (morphs degrade to an
|
|
45
|
+
* instant swap while it is on), "always" always jumps. */
|
|
46
|
+
reducedMotion: {
|
|
47
|
+
type: String,
|
|
48
|
+
default: "never"
|
|
49
|
+
},
|
|
43
50
|
size: {
|
|
44
51
|
type: [Number, String],
|
|
45
52
|
default: 24
|
|
@@ -84,7 +91,7 @@ const MorphIcon = defineComponent({
|
|
|
84
91
|
if (morph) return morph;
|
|
85
92
|
const el = pathEl.value;
|
|
86
93
|
if (dead || !el) return null;
|
|
87
|
-
morph = createMorph(el, birth);
|
|
94
|
+
morph = createMorph(el, birth, { reducedMotion: props.reducedMotion });
|
|
88
95
|
return morph;
|
|
89
96
|
};
|
|
90
97
|
onMounted(() => {
|
|
@@ -93,7 +100,7 @@ const MorphIcon = defineComponent({
|
|
|
93
100
|
const controlled = from !== void 0 && to !== void 0;
|
|
94
101
|
const initialIcon = icon ?? from ?? to;
|
|
95
102
|
if (!el || initialIcon === void 0) return;
|
|
96
|
-
const m = createMorph(el, controlled ? from : initialIcon);
|
|
103
|
+
const m = createMorph(el, controlled ? from : initialIcon, { reducedMotion: props.reducedMotion });
|
|
97
104
|
morph = m;
|
|
98
105
|
if (controlled) {
|
|
99
106
|
pair = [from, to];
|
|
@@ -113,6 +120,9 @@ const MorphIcon = defineComponent({
|
|
|
113
120
|
based = false;
|
|
114
121
|
pair = null;
|
|
115
122
|
});
|
|
123
|
+
watch(() => props.reducedMotion, (v) => {
|
|
124
|
+
if (morph) morph.reducedMotion = v;
|
|
125
|
+
});
|
|
116
126
|
watch(() => [
|
|
117
127
|
props.icon,
|
|
118
128
|
props.from,
|
|
@@ -128,8 +138,10 @@ const MorphIcon = defineComponent({
|
|
|
128
138
|
if (icon === void 0 || !changed && !left) return;
|
|
129
139
|
pair = null;
|
|
130
140
|
based = false;
|
|
131
|
-
if (morph)
|
|
132
|
-
|
|
141
|
+
if (morph) {
|
|
142
|
+
morph.reducedMotion = props.reducedMotion;
|
|
143
|
+
morph.morphTo(icon, props.spring);
|
|
144
|
+
} else ensure(icon);
|
|
133
145
|
});
|
|
134
146
|
watch(() => [
|
|
135
147
|
props.from,
|
package/package.json
CHANGED