@pixerate/editor 0.26.1 → 0.28.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 +32 -3
- package/dist/agent/index.cjs +56 -6
- package/dist/agent/index.cjs.map +1 -1
- package/dist/agent/index.d.cts +6 -4
- package/dist/agent/index.d.ts +6 -4
- package/dist/agent/index.js +51 -1
- package/dist/agent/index.js.map +1 -1
- package/dist/animation/index.cjs +62 -0
- package/dist/animation/index.cjs.map +1 -0
- package/dist/animation/index.d.cts +296 -0
- package/dist/animation/index.d.ts +296 -0
- package/dist/animation/index.js +62 -0
- package/dist/animation/index.js.map +1 -0
- package/dist/{builtins-Chj6zv_O.d.ts → builtins-haN82vjT.d.ts} +33 -3
- package/dist/{builtins-CdPcLwwF.d.cts → builtins-nMXh9sT3.d.cts} +33 -3
- package/dist/canvas/index.cjs +535 -737
- package/dist/canvas/index.cjs.map +1 -1
- package/dist/canvas/index.d.cts +74 -291
- package/dist/canvas/index.d.ts +74 -291
- package/dist/canvas/index.js +417 -619
- package/dist/canvas/index.js.map +1 -1
- package/dist/{chunk-X7HXL2SB.js → chunk-3RPOW4CS.js} +342 -16
- package/dist/chunk-3RPOW4CS.js.map +1 -0
- package/dist/{chunk-UBCFIABU.js → chunk-5H2OPMXZ.js} +183 -6
- package/dist/chunk-5H2OPMXZ.js.map +1 -0
- package/dist/chunk-A6PHCAGX.cjs +87 -0
- package/dist/chunk-A6PHCAGX.cjs.map +1 -0
- package/dist/{chunk-HRMEDT2L.js → chunk-D6AVOTSD.js} +6 -85
- package/dist/chunk-D6AVOTSD.js.map +1 -0
- package/dist/chunk-HXDMZQTD.cjs +497 -0
- package/dist/chunk-HXDMZQTD.cjs.map +1 -0
- package/dist/chunk-IB3ZHQAX.js +497 -0
- package/dist/chunk-IB3ZHQAX.js.map +1 -0
- package/dist/{chunk-4BVP64JJ.cjs → chunk-K2UYKJ6M.cjs} +24 -103
- package/dist/chunk-K2UYKJ6M.cjs.map +1 -0
- package/dist/{chunk-HDYMLS3X.cjs → chunk-MC2LQN5S.cjs} +196 -19
- package/dist/chunk-MC2LQN5S.cjs.map +1 -0
- package/dist/{chunk-XQFHLT53.js → chunk-MXZJ4PKN.js} +24 -10
- package/dist/chunk-MXZJ4PKN.js.map +1 -0
- package/dist/{chunk-LN2POBHT.cjs → chunk-OYEFRO3D.cjs} +28 -14
- package/dist/chunk-OYEFRO3D.cjs.map +1 -0
- package/dist/chunk-PWJARCI4.js +637 -0
- package/dist/chunk-PWJARCI4.js.map +1 -0
- package/dist/chunk-TGD52FEZ.js +87 -0
- package/dist/chunk-TGD52FEZ.js.map +1 -0
- package/dist/chunk-TTYCN6ZH.cjs +637 -0
- package/dist/chunk-TTYCN6ZH.cjs.map +1 -0
- package/dist/{chunk-NQF32I6Z.cjs → chunk-U7VGDSNY.cjs} +463 -137
- package/dist/chunk-U7VGDSNY.cjs.map +1 -0
- package/dist/commands/index.cjs +30 -410
- package/dist/commands/index.cjs.map +1 -1
- package/dist/commands/index.d.cts +1 -1
- package/dist/commands/index.d.ts +1 -1
- package/dist/commands/index.js +17 -397
- package/dist/commands/index.js.map +1 -1
- package/dist/{controller-CxXGOEmy.d.ts → controller-w6eRTjF8.d.cts} +67 -2
- package/dist/{controller-CxXGOEmy.d.cts → controller-w6eRTjF8.d.ts} +67 -2
- package/dist/extensions/index.cjs +25 -7
- package/dist/extensions/index.cjs.map +1 -1
- package/dist/extensions/index.d.cts +1 -1
- package/dist/extensions/index.d.ts +1 -1
- package/dist/extensions/index.js +32 -14
- package/dist/generation/index.cjs +3 -3
- package/dist/generation/index.cjs.map +1 -1
- package/dist/generation/index.js +3 -3
- package/dist/{index-jcJwmBYt.d.cts → index-C6gaQRH0.d.cts} +53 -4
- package/dist/{index-B273Zllk.d.ts → index-DXAv_R08.d.ts} +53 -4
- package/dist/index.cjs +57 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +7 -5
- package/dist/index.d.ts +7 -5
- package/dist/index.js +132 -94
- package/dist/index.js.map +1 -1
- package/dist/spreadsheet/index.cjs +23 -3
- package/dist/spreadsheet/index.cjs.map +1 -1
- package/dist/spreadsheet/index.d.cts +76 -3
- package/dist/spreadsheet/index.d.ts +76 -3
- package/dist/spreadsheet/index.js +28 -8
- package/dist/tokens/index.cjs +4 -2
- package/dist/tokens/index.cjs.map +1 -1
- package/dist/tokens/index.js +6 -4
- package/package.json +6 -1
- package/dist/chunk-4BVP64JJ.cjs.map +0 -1
- package/dist/chunk-HDYMLS3X.cjs.map +0 -1
- package/dist/chunk-HRMEDT2L.js.map +0 -1
- package/dist/chunk-LN2POBHT.cjs.map +0 -1
- package/dist/chunk-NQF32I6Z.cjs.map +0 -1
- package/dist/chunk-UBCFIABU.js.map +0 -1
- package/dist/chunk-X7HXL2SB.js.map +0 -1
- package/dist/chunk-XQFHLT53.js.map +0 -1
package/dist/canvas/index.js
CHANGED
|
@@ -1,17 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DEFAULT_ARRANGE_SHORTCUTS,
|
|
3
|
-
matchArrangeShortcut
|
|
4
|
-
} from "../chunk-7DWMJCAT.js";
|
|
5
|
-
import {
|
|
6
|
-
TokenError,
|
|
7
|
-
getTokenCollection,
|
|
8
|
-
getTokenVariable,
|
|
9
|
-
hexToRgba,
|
|
10
|
-
isTokenInScope,
|
|
11
|
-
normalizeTokenColor,
|
|
12
|
-
resolveToken,
|
|
13
|
-
rgbaToHex
|
|
14
|
-
} from "../chunk-HRMEDT2L.js";
|
|
15
1
|
import {
|
|
16
2
|
DEFAULT_NODE_HEIGHT,
|
|
17
3
|
DEFAULT_NODE_WIDTH,
|
|
@@ -27,79 +13,50 @@ import {
|
|
|
27
13
|
overlaps,
|
|
28
14
|
placeBeside
|
|
29
15
|
} from "../chunk-VHZCXHS4.js";
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
/** Advances time by `ms`, resolving due sleep timers in order, followed by queued frames. */
|
|
75
|
-
async advance(ms) {
|
|
76
|
-
const end = this.time + Math.max(0, ms);
|
|
77
|
-
await settle();
|
|
78
|
-
while (true) {
|
|
79
|
-
let next;
|
|
80
|
-
for (const s of this.sleepers) {
|
|
81
|
-
if (s.at <= end && (!next || s.at < next.at || s.at === next.at && s.seq < next.seq)) {
|
|
82
|
-
next = s;
|
|
83
|
-
}
|
|
84
|
-
}
|
|
85
|
-
if (!next) break;
|
|
86
|
-
this.sleepers.splice(this.sleepers.indexOf(next), 1);
|
|
87
|
-
this.time = Math.max(this.time, next.at);
|
|
88
|
-
next.resolve();
|
|
89
|
-
await settle();
|
|
90
|
-
}
|
|
91
|
-
this.time = end;
|
|
92
|
-
const dueFrames = this.frames.splice(0, this.frames.length);
|
|
93
|
-
for (const resolveFrame of dueFrames) {
|
|
94
|
-
resolveFrame();
|
|
95
|
-
}
|
|
96
|
-
await settle();
|
|
97
|
-
}
|
|
98
|
-
/** Convenience method to advance by 1 frame at a given fps (default: 60fps = 16.67ms). */
|
|
99
|
-
async tick(fps = 60) {
|
|
100
|
-
await this.advance(Math.round(1e3 / fps));
|
|
101
|
-
}
|
|
102
|
-
};
|
|
16
|
+
import {
|
|
17
|
+
DEFAULT_ARRANGE_SHORTCUTS,
|
|
18
|
+
matchArrangeShortcut
|
|
19
|
+
} from "../chunk-7DWMJCAT.js";
|
|
20
|
+
import {
|
|
21
|
+
TokenError,
|
|
22
|
+
getTokenCollection,
|
|
23
|
+
getTokenVariable,
|
|
24
|
+
isTokenInScope,
|
|
25
|
+
resolveToken
|
|
26
|
+
} from "../chunk-D6AVOTSD.js";
|
|
27
|
+
import {
|
|
28
|
+
ANIMATION_EASING_LABELS,
|
|
29
|
+
ANIMATION_EASING_PRESETS,
|
|
30
|
+
ANIMATION_TIME_EPSILON,
|
|
31
|
+
AnimationPlayer,
|
|
32
|
+
CUBIC_BEZIER_PRESETS,
|
|
33
|
+
ManualClock,
|
|
34
|
+
createAnimationPlayer,
|
|
35
|
+
cubicBezierAt,
|
|
36
|
+
easingFromInfluence,
|
|
37
|
+
easingLabel,
|
|
38
|
+
easingToCss,
|
|
39
|
+
evaluateEasing,
|
|
40
|
+
evaluateTrack,
|
|
41
|
+
formatOklab,
|
|
42
|
+
interpolateAnimationValue,
|
|
43
|
+
isAnimationEasing,
|
|
44
|
+
isAnimationKeyframe,
|
|
45
|
+
mixColorsOklab,
|
|
46
|
+
normalizeKeyframes,
|
|
47
|
+
oklabToRgb,
|
|
48
|
+
pageHidden,
|
|
49
|
+
parseCssEasing,
|
|
50
|
+
parseOklab,
|
|
51
|
+
realClock,
|
|
52
|
+
resolveEasing,
|
|
53
|
+
rgbToOklab,
|
|
54
|
+
springAt,
|
|
55
|
+
wrapAnimationTime
|
|
56
|
+
} from "../chunk-PWJARCI4.js";
|
|
57
|
+
import {
|
|
58
|
+
normalizeTokenColor
|
|
59
|
+
} from "../chunk-TGD52FEZ.js";
|
|
103
60
|
|
|
104
61
|
// src/canvas/trajectory.ts
|
|
105
62
|
var defaultEasing = (t) => --t * t * t + 1;
|
|
@@ -2277,7 +2234,7 @@ function fitSingleFrom(pts, u, t0, t1) {
|
|
|
2277
2234
|
let best = generate(pts, u, t0, t1);
|
|
2278
2235
|
let [bestErr, bestSplit] = maxError(pts, best, u);
|
|
2279
2236
|
const params = u.slice();
|
|
2280
|
-
for (let
|
|
2237
|
+
for (let round2 = 0; round2 < MAX_REPARAM; round2++) {
|
|
2281
2238
|
for (let i = 0; i < params.length; i++) params[i] = newton(best, pts[i], params[i]);
|
|
2282
2239
|
const c = generate(pts, params, t0, t1);
|
|
2283
2240
|
const [e, s] = maxError(pts, c, params);
|
|
@@ -7843,244 +7800,8 @@ function pruneCanvasTokenModes(nodes, state) {
|
|
|
7843
7800
|
return changed ? out : nodes;
|
|
7844
7801
|
}
|
|
7845
7802
|
|
|
7846
|
-
// src/canvas/animation/easing.ts
|
|
7847
|
-
var ANIMATION_EASING_PRESETS = [
|
|
7848
|
-
"linear",
|
|
7849
|
-
"ease",
|
|
7850
|
-
"ease-in",
|
|
7851
|
-
"ease-out",
|
|
7852
|
-
"ease-in-out",
|
|
7853
|
-
"ease-in-back",
|
|
7854
|
-
"ease-out-back",
|
|
7855
|
-
"spring",
|
|
7856
|
-
"hold"
|
|
7857
|
-
];
|
|
7858
|
-
var ANIMATION_EASING_LABELS = {
|
|
7859
|
-
linear: "Linear",
|
|
7860
|
-
hold: "Hold",
|
|
7861
|
-
ease: "Ease",
|
|
7862
|
-
"ease-in": "Ease in",
|
|
7863
|
-
"ease-out": "Ease out",
|
|
7864
|
-
"ease-in-out": "Ease in-out",
|
|
7865
|
-
"ease-in-back": "Back in",
|
|
7866
|
-
"ease-out-back": "Back out",
|
|
7867
|
-
spring: "Spring"
|
|
7868
|
-
};
|
|
7869
|
-
var CUBIC_BEZIER_PRESETS = {
|
|
7870
|
-
linear: [0, 0, 1, 1],
|
|
7871
|
-
ease: [0.25, 0.1, 0.25, 1],
|
|
7872
|
-
"ease-in": [0.42, 0, 1, 1],
|
|
7873
|
-
"ease-out": [0, 0, 0.58, 1],
|
|
7874
|
-
"ease-in-out": [0.42, 0, 0.58, 1],
|
|
7875
|
-
"ease-in-back": [0.36, 0, 0.66, -0.56],
|
|
7876
|
-
"ease-out-back": [0.34, 1.56, 0.64, 1]
|
|
7877
|
-
};
|
|
7878
|
-
var DEFAULT_SPRING = { stiffness: 170, damping: 26, mass: 1, velocity: 0 };
|
|
7879
|
-
var clamp = (v, lo, hi) => Math.min(hi, Math.max(lo, v));
|
|
7880
|
-
var finite2 = (v, fallback) => typeof v === "number" && Number.isFinite(v) ? v : fallback;
|
|
7881
|
-
function cubicBezierAt(x1, y1, x2, y2, x) {
|
|
7882
|
-
if (x <= 0) return 0;
|
|
7883
|
-
if (x >= 1) return 1;
|
|
7884
|
-
x1 = clamp(x1, 0, 1);
|
|
7885
|
-
x2 = clamp(x2, 0, 1);
|
|
7886
|
-
const bx = (s2) => 3 * (1 - s2) * (1 - s2) * s2 * x1 + 3 * (1 - s2) * s2 * s2 * x2 + s2 * s2 * s2;
|
|
7887
|
-
const by = (s2) => 3 * (1 - s2) * (1 - s2) * s2 * y1 + 3 * (1 - s2) * s2 * s2 * y2 + s2 * s2 * s2;
|
|
7888
|
-
const dbx = (s2) => 3 * (1 - s2) * (1 - s2) * x1 + 6 * (1 - s2) * s2 * (x2 - x1) + 3 * s2 * s2 * (1 - x2);
|
|
7889
|
-
let s = x;
|
|
7890
|
-
for (let i = 0; i < 8; i++) {
|
|
7891
|
-
const d = dbx(s);
|
|
7892
|
-
if (Math.abs(d) < 1e-9) break;
|
|
7893
|
-
const next = s - (bx(s) - x) / d;
|
|
7894
|
-
if (!(next >= 0 && next <= 1)) break;
|
|
7895
|
-
s = next;
|
|
7896
|
-
}
|
|
7897
|
-
if (Math.abs(bx(s) - x) > 1e-7) {
|
|
7898
|
-
let lo = 0;
|
|
7899
|
-
let hi = 1;
|
|
7900
|
-
for (let i = 0; i < 60; i++) {
|
|
7901
|
-
s = (lo + hi) / 2;
|
|
7902
|
-
if (bx(s) < x) lo = s;
|
|
7903
|
-
else hi = s;
|
|
7904
|
-
}
|
|
7905
|
-
}
|
|
7906
|
-
return by(s);
|
|
7907
|
-
}
|
|
7908
|
-
function springAt(spring, seconds) {
|
|
7909
|
-
const stiffness = Math.max(1e-6, finite2(spring.stiffness, DEFAULT_SPRING.stiffness));
|
|
7910
|
-
const damping = Math.max(0, finite2(spring.damping, DEFAULT_SPRING.damping));
|
|
7911
|
-
const mass = Math.max(1e-6, finite2(spring.mass, DEFAULT_SPRING.mass));
|
|
7912
|
-
const v0 = -finite2(spring.velocity, DEFAULT_SPRING.velocity);
|
|
7913
|
-
const t = Math.max(0, seconds);
|
|
7914
|
-
const w0 = Math.sqrt(stiffness / mass);
|
|
7915
|
-
const zeta = damping / (2 * Math.sqrt(stiffness * mass));
|
|
7916
|
-
let x;
|
|
7917
|
-
if (zeta < 1 - 1e-6) {
|
|
7918
|
-
const wd = w0 * Math.sqrt(1 - zeta * zeta);
|
|
7919
|
-
x = Math.exp(-zeta * w0 * t) * (Math.cos(wd * t) + (zeta * w0 + v0) / wd * Math.sin(wd * t));
|
|
7920
|
-
} else if (zeta <= 1 + 1e-6) {
|
|
7921
|
-
x = Math.exp(-w0 * t) * (1 + (w0 + v0) * t);
|
|
7922
|
-
} else {
|
|
7923
|
-
const r = Math.sqrt(zeta * zeta - 1);
|
|
7924
|
-
const s1 = -w0 * (zeta - r);
|
|
7925
|
-
const s2 = -w0 * (zeta + r);
|
|
7926
|
-
const c2 = (v0 - s1) / (s2 - s1);
|
|
7927
|
-
const c1 = 1 - c2;
|
|
7928
|
-
x = c1 * Math.exp(s1 * t) + c2 * Math.exp(s2 * t);
|
|
7929
|
-
}
|
|
7930
|
-
return 1 - x;
|
|
7931
|
-
}
|
|
7932
|
-
function resolveEasing(easing) {
|
|
7933
|
-
if (easing === "hold") return "hold";
|
|
7934
|
-
if (easing === "spring") return { type: "spring", ...DEFAULT_SPRING };
|
|
7935
|
-
if (typeof easing === "string") {
|
|
7936
|
-
const preset = CUBIC_BEZIER_PRESETS[easing];
|
|
7937
|
-
if (preset) return { type: "cubic-bezier", x1: preset[0], y1: preset[1], x2: preset[2], y2: preset[3] };
|
|
7938
|
-
const parsed = parseCssEasing(easing);
|
|
7939
|
-
return parsed ? resolveEasing(parsed) : { type: "cubic-bezier", x1: 0, y1: 0, x2: 1, y2: 1 };
|
|
7940
|
-
}
|
|
7941
|
-
if (easing && typeof easing === "object") {
|
|
7942
|
-
if (easing.type === "spring") {
|
|
7943
|
-
return {
|
|
7944
|
-
type: "spring",
|
|
7945
|
-
stiffness: finite2(easing.stiffness, DEFAULT_SPRING.stiffness),
|
|
7946
|
-
damping: finite2(easing.damping, DEFAULT_SPRING.damping),
|
|
7947
|
-
mass: finite2(easing.mass, DEFAULT_SPRING.mass),
|
|
7948
|
-
velocity: finite2(easing.velocity, DEFAULT_SPRING.velocity)
|
|
7949
|
-
};
|
|
7950
|
-
}
|
|
7951
|
-
if (easing.type === "cubic-bezier") {
|
|
7952
|
-
return {
|
|
7953
|
-
type: "cubic-bezier",
|
|
7954
|
-
x1: clamp(finite2(easing.x1, 0), 0, 1),
|
|
7955
|
-
y1: finite2(easing.y1, 0),
|
|
7956
|
-
x2: clamp(finite2(easing.x2, 1), 0, 1),
|
|
7957
|
-
y2: finite2(easing.y2, 1)
|
|
7958
|
-
};
|
|
7959
|
-
}
|
|
7960
|
-
}
|
|
7961
|
-
return { type: "cubic-bezier", x1: 0, y1: 0, x2: 1, y2: 1 };
|
|
7962
|
-
}
|
|
7963
|
-
function evaluateEasing(easing, u, durationMs = 1e3) {
|
|
7964
|
-
if (!(u > 0)) return 0;
|
|
7965
|
-
if (u >= 1) return 1;
|
|
7966
|
-
const resolved = resolveEasing(easing);
|
|
7967
|
-
if (resolved === "hold") return 0;
|
|
7968
|
-
if (resolved.type === "spring") return springAt(resolved, u * Math.max(0, durationMs) / 1e3);
|
|
7969
|
-
const { x1, y1, x2, y2 } = resolved;
|
|
7970
|
-
if (x1 === y1 && x2 === y2) return u;
|
|
7971
|
-
return cubicBezierAt(x1, y1, x2, y2, u);
|
|
7972
|
-
}
|
|
7973
|
-
function easingFromInfluence(outInfluence = 1 / 3, inInfluence = 1 / 3) {
|
|
7974
|
-
return {
|
|
7975
|
-
type: "cubic-bezier",
|
|
7976
|
-
x1: clamp(outInfluence, 0.01, 1),
|
|
7977
|
-
y1: 0,
|
|
7978
|
-
x2: 1 - clamp(inInfluence, 0.01, 1),
|
|
7979
|
-
y2: 1
|
|
7980
|
-
};
|
|
7981
|
-
}
|
|
7982
|
-
var NUMBER = String.raw`\s*(-?(?:\d+\.?\d*|\.\d+)(?:e[-+]?\d+)?)\s*`;
|
|
7983
|
-
var CUBIC_RE = new RegExp(`^cubic-bezier\\(${NUMBER},${NUMBER},${NUMBER},${NUMBER}\\)$`, "i");
|
|
7984
|
-
function parseCssEasing(text) {
|
|
7985
|
-
const value = text.trim().toLowerCase();
|
|
7986
|
-
if (value.length > 200) return null;
|
|
7987
|
-
if (ANIMATION_EASING_PRESETS.includes(value)) return value;
|
|
7988
|
-
if (value === "step-end" || /^steps\(\s*1\s*(,\s*(end|jump-end)\s*)?\)$/.test(value)) return "hold";
|
|
7989
|
-
const m = CUBIC_RE.exec(value);
|
|
7990
|
-
if (!m) return null;
|
|
7991
|
-
const [x1, y1, x2, y2] = m.slice(1).map(Number);
|
|
7992
|
-
if (![x1, y1, x2, y2].every(Number.isFinite) || x1 < 0 || x1 > 1 || x2 < 0 || x2 > 1) return null;
|
|
7993
|
-
return { type: "cubic-bezier", x1, y1, x2, y2 };
|
|
7994
|
-
}
|
|
7995
|
-
function isAnimationEasing(value) {
|
|
7996
|
-
if (typeof value === "string") return parseCssEasing(value) !== null;
|
|
7997
|
-
if (typeof value !== "object" || value === null) return false;
|
|
7998
|
-
const e = value;
|
|
7999
|
-
if (e.type === "spring") return true;
|
|
8000
|
-
return e.type === "cubic-bezier" && [e.x1, e.y1, e.x2, e.y2].every((v) => typeof v === "number" && Number.isFinite(v));
|
|
8001
|
-
}
|
|
8002
|
-
var round = (v, digits = 4) => {
|
|
8003
|
-
const p = 10 ** digits;
|
|
8004
|
-
return Math.round(v * p) / p;
|
|
8005
|
-
};
|
|
8006
|
-
function easingToCss(easing, durationMs = 1e3, samples = 24) {
|
|
8007
|
-
const resolved = resolveEasing(easing);
|
|
8008
|
-
if (resolved === "hold") return "steps(1, end)";
|
|
8009
|
-
if (resolved.type === "spring") {
|
|
8010
|
-
const n = Math.max(2, Math.floor(samples));
|
|
8011
|
-
const points = [];
|
|
8012
|
-
for (let i = 0; i <= n; i++) points.push(String(round(i === n ? 1 : springAt(resolved, i / n * durationMs / 1e3))));
|
|
8013
|
-
return `linear(${points.join(", ")})`;
|
|
8014
|
-
}
|
|
8015
|
-
const { x1, y1, x2, y2 } = resolved;
|
|
8016
|
-
for (const [name, p] of Object.entries(CUBIC_BEZIER_PRESETS)) {
|
|
8017
|
-
if (["linear", "ease", "ease-in", "ease-out", "ease-in-out"].includes(name) && p[0] === x1 && p[1] === y1 && p[2] === x2 && p[3] === y2) {
|
|
8018
|
-
return name;
|
|
8019
|
-
}
|
|
8020
|
-
}
|
|
8021
|
-
return `cubic-bezier(${round(x1)}, ${round(y1)}, ${round(x2)}, ${round(y2)})`;
|
|
8022
|
-
}
|
|
8023
|
-
function easingLabel(easing) {
|
|
8024
|
-
if (easing === void 0 || easing === null) return ANIMATION_EASING_LABELS.linear;
|
|
8025
|
-
if (typeof easing === "string") return ANIMATION_EASING_LABELS[easing] ?? easing;
|
|
8026
|
-
return easing.type === "spring" ? "Spring" : "Custom";
|
|
8027
|
-
}
|
|
8028
|
-
|
|
8029
|
-
// src/canvas/animation/color.ts
|
|
8030
|
-
var toLinear = (c) => c <= 0.04045 ? c / 12.92 : ((c + 0.055) / 1.055) ** 2.4;
|
|
8031
|
-
var toGamma = (c) => c <= 31308e-7 ? 12.92 * c : 1.055 * Math.sign(c) * Math.abs(c) ** (1 / 2.4) - 0.055;
|
|
8032
|
-
function rgbToOklab(r, g, b, alpha = 1) {
|
|
8033
|
-
const lr = toLinear(r);
|
|
8034
|
-
const lg = toLinear(g);
|
|
8035
|
-
const lb = toLinear(b);
|
|
8036
|
-
const l = Math.cbrt(0.4122214708 * lr + 0.5363325363 * lg + 0.0514459929 * lb);
|
|
8037
|
-
const m = Math.cbrt(0.2119034982 * lr + 0.6806995451 * lg + 0.1073969566 * lb);
|
|
8038
|
-
const s = Math.cbrt(0.0883024619 * lr + 0.2817188376 * lg + 0.6299787005 * lb);
|
|
8039
|
-
return {
|
|
8040
|
-
l: 0.2104542553 * l + 0.793617785 * m - 0.0040720468 * s,
|
|
8041
|
-
a: 1.9779984951 * l - 2.428592205 * m + 0.4505937099 * s,
|
|
8042
|
-
b: 0.0259040371 * l + 0.7827717662 * m - 0.808675766 * s,
|
|
8043
|
-
alpha
|
|
8044
|
-
};
|
|
8045
|
-
}
|
|
8046
|
-
function oklabToRgb(color) {
|
|
8047
|
-
const l_ = color.l + 0.3963377774 * color.a + 0.2158037573 * color.b;
|
|
8048
|
-
const m_ = color.l - 0.1055613458 * color.a - 0.0638541728 * color.b;
|
|
8049
|
-
const s_ = color.l - 0.0894841775 * color.a - 1.291485548 * color.b;
|
|
8050
|
-
const l = l_ ** 3;
|
|
8051
|
-
const m = m_ ** 3;
|
|
8052
|
-
const s = s_ ** 3;
|
|
8053
|
-
const clamp01 = (v) => Math.min(1, Math.max(0, v));
|
|
8054
|
-
return {
|
|
8055
|
-
r: clamp01(toGamma(4.0767416621 * l - 3.3077115913 * m + 0.2309699292 * s)),
|
|
8056
|
-
g: clamp01(toGamma(-1.2684380046 * l + 2.6097574011 * m - 0.3413193965 * s)),
|
|
8057
|
-
b: clamp01(toGamma(-0.0041960863 * l - 0.7034186147 * m + 1.707614701 * s)),
|
|
8058
|
-
a: clamp01(color.alpha)
|
|
8059
|
-
};
|
|
8060
|
-
}
|
|
8061
|
-
function parseOklab(input) {
|
|
8062
|
-
const hex = normalizeTokenColor(input);
|
|
8063
|
-
if (hex === null) return null;
|
|
8064
|
-
const c = hexToRgba(hex);
|
|
8065
|
-
return rgbToOklab(c.r, c.g, c.b, c.a);
|
|
8066
|
-
}
|
|
8067
|
-
function formatOklab(color) {
|
|
8068
|
-
const { r, g, b, a } = oklabToRgb(color);
|
|
8069
|
-
return rgbaToHex(r, g, b, a);
|
|
8070
|
-
}
|
|
8071
|
-
function mixColorsOklab(from, to, t) {
|
|
8072
|
-
const a = parseOklab(from);
|
|
8073
|
-
const b = parseOklab(to);
|
|
8074
|
-
if (!a || !b) return null;
|
|
8075
|
-
const alpha = a.alpha + (b.alpha - a.alpha) * t;
|
|
8076
|
-
const premul = (k) => a[k] * a.alpha + (b[k] * b.alpha - a[k] * a.alpha) * t;
|
|
8077
|
-
if (alpha <= 1e-9) return formatOklab({ l: a.l + (b.l - a.l) * t, a: 0, b: 0, alpha: 0 });
|
|
8078
|
-
return formatOklab({ l: premul("l") / alpha, a: premul("a") / alpha, b: premul("b") / alpha, alpha });
|
|
8079
|
-
}
|
|
8080
|
-
|
|
8081
7803
|
// src/canvas/animation/model.ts
|
|
8082
7804
|
var ANIMATION_DATA_KEY = "animation";
|
|
8083
|
-
var ANIMATION_TIME_EPSILON = 1e-6;
|
|
8084
7805
|
var num = (value) => {
|
|
8085
7806
|
if (typeof value === "number") return Number.isFinite(value) ? value : void 0;
|
|
8086
7807
|
if (typeof value === "string") {
|
|
@@ -8221,37 +7942,6 @@ function clampAnimationValue(property, value) {
|
|
|
8221
7942
|
if (property.max !== void 0) v = Math.min(property.max, v);
|
|
8222
7943
|
return v;
|
|
8223
7944
|
}
|
|
8224
|
-
function isValue(value) {
|
|
8225
|
-
return typeof value === "number" && Number.isFinite(value) || typeof value === "string";
|
|
8226
|
-
}
|
|
8227
|
-
function isAnimationKeyframe(value) {
|
|
8228
|
-
if (typeof value !== "object" || value === null) return false;
|
|
8229
|
-
const k = value;
|
|
8230
|
-
return typeof k.time === "number" && Number.isFinite(k.time) && isValue(k.value) && (k.easing === void 0 || isAnimationEasing(k.easing));
|
|
8231
|
-
}
|
|
8232
|
-
var sortedCache = /* @__PURE__ */ new WeakMap();
|
|
8233
|
-
function normalizeKeyframes(keyframes) {
|
|
8234
|
-
if (!Array.isArray(keyframes)) return [];
|
|
8235
|
-
const cached = sortedCache.get(keyframes);
|
|
8236
|
-
if (cached && cached.items.length === keyframes.length && cached.items.every((k, i) => k === keyframes[i])) return cached.out;
|
|
8237
|
-
const indexed = keyframes.map((k, i) => ({ k, i })).filter(({ k }) => isAnimationKeyframe(k)).sort((a, b) => a.k.time - b.k.time || a.i - b.i);
|
|
8238
|
-
const out = [];
|
|
8239
|
-
let lastIndex = -1;
|
|
8240
|
-
for (const { k, i } of indexed) {
|
|
8241
|
-
const prev = out[out.length - 1];
|
|
8242
|
-
if (prev && Math.abs(k.time - prev.time) <= ANIMATION_TIME_EPSILON) {
|
|
8243
|
-
if (i > lastIndex) {
|
|
8244
|
-
out[out.length - 1] = k;
|
|
8245
|
-
lastIndex = i;
|
|
8246
|
-
}
|
|
8247
|
-
continue;
|
|
8248
|
-
}
|
|
8249
|
-
out.push(k);
|
|
8250
|
-
lastIndex = i;
|
|
8251
|
-
}
|
|
8252
|
-
sortedCache.set(keyframes, { items: [...keyframes], out });
|
|
8253
|
-
return out;
|
|
8254
|
-
}
|
|
8255
7945
|
function getNodeAnimation(node) {
|
|
8256
7946
|
const raw = node?.data?.[ANIMATION_DATA_KEY];
|
|
8257
7947
|
if (typeof raw !== "object" || raw === null || typeof raw.tracks !== "object" || raw.tracks === null) return null;
|
|
@@ -8289,45 +7979,6 @@ function withNodeAnimation(node, animation) {
|
|
|
8289
7979
|
|
|
8290
7980
|
// src/canvas/animation/evaluate.ts
|
|
8291
7981
|
var nodesOf = (doc) => Array.isArray(doc) ? doc : doc.nodes;
|
|
8292
|
-
function interpolateAnimationValue(from, to, t, type = "number") {
|
|
8293
|
-
if (typeof from === "number" && typeof to === "number") return from + (to - from) * t;
|
|
8294
|
-
if (t <= 0) return from;
|
|
8295
|
-
if (t >= 1) return to;
|
|
8296
|
-
if (type === "color" || typeof from === "string" && typeof to === "string") {
|
|
8297
|
-
const mixed = mixColorsOklab(from, to, t);
|
|
8298
|
-
if (mixed !== null) return mixed;
|
|
8299
|
-
}
|
|
8300
|
-
return from;
|
|
8301
|
-
}
|
|
8302
|
-
function segmentIndex(keys, time) {
|
|
8303
|
-
let lo = 0;
|
|
8304
|
-
let hi = keys.length - 1;
|
|
8305
|
-
let found = -1;
|
|
8306
|
-
while (lo <= hi) {
|
|
8307
|
-
const mid = lo + hi >> 1;
|
|
8308
|
-
if (keys[mid].time <= time) {
|
|
8309
|
-
found = mid;
|
|
8310
|
-
lo = mid + 1;
|
|
8311
|
-
} else hi = mid - 1;
|
|
8312
|
-
}
|
|
8313
|
-
return found;
|
|
8314
|
-
}
|
|
8315
|
-
function evaluateTrack(track, time, type = "number") {
|
|
8316
|
-
const keyframes = Array.isArray(track) ? track : track?.keyframes;
|
|
8317
|
-
const keys = normalizeKeyframes(keyframes);
|
|
8318
|
-
if (keys.length === 0) return void 0;
|
|
8319
|
-
if (!(time > keys[0].time)) return keys[0].value;
|
|
8320
|
-
const last = keys[keys.length - 1];
|
|
8321
|
-
if (time >= last.time) return last.value;
|
|
8322
|
-
const i = segmentIndex(keys, time);
|
|
8323
|
-
const a = keys[i];
|
|
8324
|
-
const b = keys[i + 1];
|
|
8325
|
-
if (time === a.time) return a.value;
|
|
8326
|
-
const span = b.time - a.time;
|
|
8327
|
-
const u = (time - a.time) / span;
|
|
8328
|
-
if (a.easing === "hold") return a.value;
|
|
8329
|
-
return interpolateAnimationValue(a.value, b.value, evaluateEasing(a.easing, u, span), type);
|
|
8330
|
-
}
|
|
8331
7982
|
function getAnimationDuration(doc) {
|
|
8332
7983
|
let end = 0;
|
|
8333
7984
|
for (const node of nodesOf(doc)) {
|
|
@@ -8341,17 +7992,6 @@ function getAnimationDuration(doc) {
|
|
|
8341
7992
|
}
|
|
8342
7993
|
return end;
|
|
8343
7994
|
}
|
|
8344
|
-
function wrapAnimationTime(time, duration, loop = "none") {
|
|
8345
|
-
if (!Number.isFinite(time)) return 0;
|
|
8346
|
-
if (!(duration > 0)) return 0;
|
|
8347
|
-
if (loop === "loop") return (time % duration + duration) % duration;
|
|
8348
|
-
if (loop === "ping-pong") {
|
|
8349
|
-
const period = duration * 2;
|
|
8350
|
-
const m = (time % period + period) % period;
|
|
8351
|
-
return m <= duration ? m : period - m;
|
|
8352
|
-
}
|
|
8353
|
-
return Math.min(duration, Math.max(0, time));
|
|
8354
|
-
}
|
|
8355
7995
|
function evaluateAnimation(doc, time, options = {}) {
|
|
8356
7996
|
const only = options.ids ? new Set(options.ids) : null;
|
|
8357
7997
|
const out = {};
|
|
@@ -8873,212 +8513,6 @@ function animateNode(nodes, nodeId, options) {
|
|
|
8873
8513
|
return { nodes: draft.commit(), refs };
|
|
8874
8514
|
}
|
|
8875
8515
|
|
|
8876
|
-
// src/canvas/animation/player.ts
|
|
8877
|
-
var AnimationPlayer = class {
|
|
8878
|
-
clock;
|
|
8879
|
-
durationSource;
|
|
8880
|
-
onTime;
|
|
8881
|
-
onEnd;
|
|
8882
|
-
time;
|
|
8883
|
-
playing = false;
|
|
8884
|
-
scrubbing = false;
|
|
8885
|
-
resumeAfterScrub = false;
|
|
8886
|
-
loop;
|
|
8887
|
-
rate;
|
|
8888
|
-
/** Unwrapped time at `anchorAt` (keeps the ping-pong direction across frames). */
|
|
8889
|
-
anchorRaw = 0;
|
|
8890
|
-
anchorAt = 0;
|
|
8891
|
-
generation = 0;
|
|
8892
|
-
cached;
|
|
8893
|
-
listeners = /* @__PURE__ */ new Set();
|
|
8894
|
-
constructor(options = {}) {
|
|
8895
|
-
this.clock = options.clock ?? realClock;
|
|
8896
|
-
this.durationSource = options.duration ?? 1e3;
|
|
8897
|
-
this.loop = options.loop ?? "none";
|
|
8898
|
-
this.rate = Number.isFinite(options.playbackRate) && options.playbackRate !== 0 ? options.playbackRate : 1;
|
|
8899
|
-
this.onTime = options.onTime;
|
|
8900
|
-
this.onEnd = options.onEnd;
|
|
8901
|
-
this.time = this.clamp(options.time ?? 0);
|
|
8902
|
-
this.cached = this.snapshot();
|
|
8903
|
-
}
|
|
8904
|
-
/** Current timeline length (ms). */
|
|
8905
|
-
get duration() {
|
|
8906
|
-
const d = typeof this.durationSource === "function" ? this.durationSource() : this.durationSource;
|
|
8907
|
-
return Number.isFinite(d) && d > 0 ? d : 0;
|
|
8908
|
-
}
|
|
8909
|
-
get currentTime() {
|
|
8910
|
-
return this.time;
|
|
8911
|
-
}
|
|
8912
|
-
get isPlaying() {
|
|
8913
|
-
return this.playing;
|
|
8914
|
-
}
|
|
8915
|
-
getState() {
|
|
8916
|
-
return this.cached;
|
|
8917
|
-
}
|
|
8918
|
-
subscribe(listener) {
|
|
8919
|
-
this.listeners.add(listener);
|
|
8920
|
-
return () => {
|
|
8921
|
-
this.listeners.delete(listener);
|
|
8922
|
-
};
|
|
8923
|
-
}
|
|
8924
|
-
/** Replaces the callbacks (hooks keep them current without recreating the player). */
|
|
8925
|
-
setCallbacks(callbacks) {
|
|
8926
|
-
this.onTime = callbacks.onTime;
|
|
8927
|
-
this.onEnd = callbacks.onEnd;
|
|
8928
|
-
}
|
|
8929
|
-
setDuration(duration) {
|
|
8930
|
-
this.durationSource = duration;
|
|
8931
|
-
this.time = this.clamp(this.time);
|
|
8932
|
-
this.emit(false);
|
|
8933
|
-
}
|
|
8934
|
-
setLoop(loop) {
|
|
8935
|
-
if (loop === this.loop) return;
|
|
8936
|
-
this.reanchor();
|
|
8937
|
-
this.loop = loop;
|
|
8938
|
-
this.emit(false);
|
|
8939
|
-
}
|
|
8940
|
-
setPlaybackRate(rate) {
|
|
8941
|
-
if (!Number.isFinite(rate) || rate === 0 || rate === this.rate) return;
|
|
8942
|
-
this.reanchor();
|
|
8943
|
-
this.rate = rate;
|
|
8944
|
-
this.emit(false);
|
|
8945
|
-
}
|
|
8946
|
-
/** Starts playing from the playhead (from the start again when a one-shot play had ended). */
|
|
8947
|
-
play() {
|
|
8948
|
-
if (this.playing) return;
|
|
8949
|
-
const d = this.duration;
|
|
8950
|
-
if (this.loop === "none" && d > 0) {
|
|
8951
|
-
if (this.rate > 0 && this.time >= d) this.time = 0;
|
|
8952
|
-
if (this.rate < 0 && this.time <= 0) this.time = d;
|
|
8953
|
-
}
|
|
8954
|
-
this.playing = true;
|
|
8955
|
-
this.scrubbing = false;
|
|
8956
|
-
this.anchorRaw = this.time;
|
|
8957
|
-
this.anchorAt = this.clock.now();
|
|
8958
|
-
this.emit(false);
|
|
8959
|
-
this.schedule();
|
|
8960
|
-
}
|
|
8961
|
-
pause() {
|
|
8962
|
-
if (!this.playing) return;
|
|
8963
|
-
this.tick(false);
|
|
8964
|
-
this.playing = false;
|
|
8965
|
-
this.generation++;
|
|
8966
|
-
this.emit(true);
|
|
8967
|
-
}
|
|
8968
|
-
toggle() {
|
|
8969
|
-
if (this.playing) this.pause();
|
|
8970
|
-
else this.play();
|
|
8971
|
-
}
|
|
8972
|
-
/** Pauses and returns to the start. */
|
|
8973
|
-
stop() {
|
|
8974
|
-
this.playing = false;
|
|
8975
|
-
this.generation++;
|
|
8976
|
-
this.seek(0);
|
|
8977
|
-
}
|
|
8978
|
-
/** Moves the playhead (clamped to the timeline); playback continues from there. */
|
|
8979
|
-
seek(time) {
|
|
8980
|
-
if (!Number.isFinite(time)) return;
|
|
8981
|
-
this.time = this.clamp(time);
|
|
8982
|
-
this.anchorRaw = this.time;
|
|
8983
|
-
this.anchorAt = this.clock.now();
|
|
8984
|
-
this.emit(true);
|
|
8985
|
-
}
|
|
8986
|
-
/** Seeks while dragging the playhead: playback pauses until `endScrub`. */
|
|
8987
|
-
scrub(time) {
|
|
8988
|
-
if (!this.scrubbing) {
|
|
8989
|
-
this.resumeAfterScrub = this.playing;
|
|
8990
|
-
if (this.playing) {
|
|
8991
|
-
this.playing = false;
|
|
8992
|
-
this.generation++;
|
|
8993
|
-
}
|
|
8994
|
-
this.scrubbing = true;
|
|
8995
|
-
}
|
|
8996
|
-
this.seek(time);
|
|
8997
|
-
}
|
|
8998
|
-
/** Ends a scrub, resuming playback if it was playing when the scrub began. */
|
|
8999
|
-
endScrub() {
|
|
9000
|
-
if (!this.scrubbing) return;
|
|
9001
|
-
this.scrubbing = false;
|
|
9002
|
-
const resume = this.resumeAfterScrub;
|
|
9003
|
-
this.resumeAfterScrub = false;
|
|
9004
|
-
if (resume) this.play();
|
|
9005
|
-
else this.emit(false);
|
|
9006
|
-
}
|
|
9007
|
-
/** Stops scheduling frames and drops listeners. */
|
|
9008
|
-
destroy() {
|
|
9009
|
-
this.playing = false;
|
|
9010
|
-
this.generation++;
|
|
9011
|
-
this.listeners.clear();
|
|
9012
|
-
}
|
|
9013
|
-
clamp(time) {
|
|
9014
|
-
return Math.min(this.duration, Math.max(0, Number.isFinite(time) ? time : 0));
|
|
9015
|
-
}
|
|
9016
|
-
reanchor() {
|
|
9017
|
-
if (!this.playing) return;
|
|
9018
|
-
this.tick(false);
|
|
9019
|
-
this.anchorRaw = this.rawNow();
|
|
9020
|
-
this.anchorAt = this.clock.now();
|
|
9021
|
-
}
|
|
9022
|
-
rawNow() {
|
|
9023
|
-
return this.anchorRaw + (this.clock.now() - this.anchorAt) * this.rate;
|
|
9024
|
-
}
|
|
9025
|
-
schedule() {
|
|
9026
|
-
const generation = this.generation;
|
|
9027
|
-
this.clock.frame().then(() => {
|
|
9028
|
-
if (generation !== this.generation || !this.playing) return;
|
|
9029
|
-
this.tick(true);
|
|
9030
|
-
if (this.playing) this.schedule();
|
|
9031
|
-
});
|
|
9032
|
-
}
|
|
9033
|
-
/** Advances the playhead to the clock's time; `notify` emits the change. */
|
|
9034
|
-
tick(notify) {
|
|
9035
|
-
if (!this.playing) return;
|
|
9036
|
-
const d = this.duration;
|
|
9037
|
-
const raw = this.rawNow();
|
|
9038
|
-
let ended = false;
|
|
9039
|
-
if (this.loop === "none" || d <= 0) {
|
|
9040
|
-
if (this.rate > 0 && raw >= d) {
|
|
9041
|
-
this.time = d;
|
|
9042
|
-
ended = true;
|
|
9043
|
-
} else if (this.rate < 0 && raw <= 0) {
|
|
9044
|
-
this.time = 0;
|
|
9045
|
-
ended = true;
|
|
9046
|
-
} else this.time = this.clamp(raw);
|
|
9047
|
-
} else {
|
|
9048
|
-
this.time = wrapAnimationTime(raw, d, this.loop);
|
|
9049
|
-
}
|
|
9050
|
-
if (ended) {
|
|
9051
|
-
this.playing = false;
|
|
9052
|
-
this.generation++;
|
|
9053
|
-
}
|
|
9054
|
-
if (notify || ended) this.emit(true);
|
|
9055
|
-
if (ended) this.onEnd?.();
|
|
9056
|
-
}
|
|
9057
|
-
snapshot() {
|
|
9058
|
-
return {
|
|
9059
|
-
time: this.time,
|
|
9060
|
-
playing: this.playing,
|
|
9061
|
-
scrubbing: this.scrubbing,
|
|
9062
|
-
loop: this.loop,
|
|
9063
|
-
playbackRate: this.rate,
|
|
9064
|
-
duration: this.duration
|
|
9065
|
-
};
|
|
9066
|
-
}
|
|
9067
|
-
emit(timeChanged) {
|
|
9068
|
-
const next = this.snapshot();
|
|
9069
|
-
const prev = this.cached;
|
|
9070
|
-
if (prev.time === next.time && prev.playing === next.playing && prev.scrubbing === next.scrubbing && prev.loop === next.loop && prev.playbackRate === next.playbackRate && prev.duration === next.duration) {
|
|
9071
|
-
return;
|
|
9072
|
-
}
|
|
9073
|
-
this.cached = next;
|
|
9074
|
-
if (timeChanged && prev.time !== next.time) this.onTime?.(next.time);
|
|
9075
|
-
for (const listener of this.listeners) listener(next);
|
|
9076
|
-
}
|
|
9077
|
-
};
|
|
9078
|
-
function createAnimationPlayer(options) {
|
|
9079
|
-
return new AnimationPlayer(options);
|
|
9080
|
-
}
|
|
9081
|
-
|
|
9082
8516
|
// src/canvas/animation/export.ts
|
|
9083
8517
|
function sampleAnimationFrames(doc, options = {}) {
|
|
9084
8518
|
const fps = options.fps ?? 30;
|
|
@@ -9109,22 +8543,22 @@ var CSS_ANIMATION_PROPERTIES = {
|
|
|
9109
8543
|
width: { css: "width", camel: "width" },
|
|
9110
8544
|
height: { css: "height", camel: "height" }
|
|
9111
8545
|
};
|
|
9112
|
-
var
|
|
8546
|
+
var round = (v) => Math.round(v * 1e3) / 1e3;
|
|
9113
8547
|
function cssValue(source, value, node, partner, partnerSource) {
|
|
9114
8548
|
if (source === "x" || source === "y") {
|
|
9115
8549
|
const dx = source === "x" ? value - node.position.x : partner !== void 0 ? partner - node.position.x : 0;
|
|
9116
8550
|
const dy = source === "y" ? value - node.position.y : partner !== void 0 && partnerSource === "y" ? partner - node.position.y : 0;
|
|
9117
|
-
return `${
|
|
8551
|
+
return `${round(dx)}px ${round(dy)}px`;
|
|
9118
8552
|
}
|
|
9119
8553
|
if (typeof value === "string") return value;
|
|
9120
8554
|
switch (source) {
|
|
9121
8555
|
case "rotation":
|
|
9122
|
-
return `${
|
|
8556
|
+
return `${round(value)}deg`;
|
|
9123
8557
|
case "scale":
|
|
9124
8558
|
case "opacity":
|
|
9125
|
-
return String(
|
|
8559
|
+
return String(round(value));
|
|
9126
8560
|
default:
|
|
9127
|
-
return `${
|
|
8561
|
+
return `${round(value)}px`;
|
|
9128
8562
|
}
|
|
9129
8563
|
}
|
|
9130
8564
|
var sameTimes = (a, b) => a.length === b.length && a.every((k, i) => Math.abs(k.time - b[i].time) < 1e-6 && JSON.stringify(k.easing ?? "linear") === JSON.stringify(b[i].easing ?? "linear"));
|
|
@@ -9184,7 +8618,7 @@ function getCssPropertyAnimations(nodes, nodeId, options = {}) {
|
|
|
9184
8618
|
if (sources.includes("motionPath")) {
|
|
9185
8619
|
const position = (t) => {
|
|
9186
8620
|
const moved = ANIMATABLE_PROPERTIES.find((p) => p.key === "motionPath").write(node, evalAt("motionPath", t));
|
|
9187
|
-
return `${
|
|
8621
|
+
return `${round(moved.position.x - node.position.x)}px ${round(moved.position.y - node.position.y)}px`;
|
|
9188
8622
|
};
|
|
9189
8623
|
out.push({ property: css, camel: camel2, sources, stops: sampledStops(duration, fps, allTimes, position), exact: false });
|
|
9190
8624
|
continue;
|
|
@@ -9219,7 +8653,7 @@ function timing(options, duration) {
|
|
|
9219
8653
|
const rate = options.playbackRate && Number.isFinite(options.playbackRate) && options.playbackRate !== 0 ? options.playbackRate : 1;
|
|
9220
8654
|
const loop = options.loop ?? "none";
|
|
9221
8655
|
return {
|
|
9222
|
-
duration:
|
|
8656
|
+
duration: round(duration / Math.abs(rate)),
|
|
9223
8657
|
iterations: loop === "none" ? 1 : Infinity,
|
|
9224
8658
|
direction: loop === "ping-pong" ? rate < 0 ? "alternate-reverse" : "alternate" : rate < 0 ? "reverse" : "normal"
|
|
9225
8659
|
};
|
|
@@ -9236,7 +8670,7 @@ function exportCssAnimation(nodes, nodeId, options = {}) {
|
|
|
9236
8670
|
for (const prop of props) {
|
|
9237
8671
|
const name = `${base}-${cssIdent(prop.property)}`;
|
|
9238
8672
|
const lines = prop.stops.map((s) => {
|
|
9239
|
-
const pct = `${
|
|
8673
|
+
const pct = `${round(s.offset * 100)}%`;
|
|
9240
8674
|
const easing = s.easing ? ` animation-timing-function: ${s.easing};` : "";
|
|
9241
8675
|
return ` ${pct} { ${prop.property}: ${s.value};${easing} }`;
|
|
9242
8676
|
});
|
|
@@ -11382,6 +10816,289 @@ function restoreCanvasMainComponent(nodes, id, options = {}) {
|
|
|
11382
10816
|
for (const nn of nested) after = rebaseInstance(after, nn, options.tokens);
|
|
11383
10817
|
return done(nodes, after, [compId], options);
|
|
11384
10818
|
}
|
|
10819
|
+
var VARIANT_GAP = 20;
|
|
10820
|
+
var variantKey = (values) => formatVariantName(values);
|
|
10821
|
+
function variantNameProblem(kind, s) {
|
|
10822
|
+
const label = kind === "property" ? "Variant property names" : "Variant values";
|
|
10823
|
+
if (!s) return `${label} must not be empty.`;
|
|
10824
|
+
if (/[,=]/.test(s)) return `${label} can't contain "," or "=".`;
|
|
10825
|
+
return void 0;
|
|
10826
|
+
}
|
|
10827
|
+
function cleanVariantPairs(pairs) {
|
|
10828
|
+
const out = {};
|
|
10829
|
+
for (const [rawKey, rawValue] of Object.entries(pairs)) {
|
|
10830
|
+
if (typeof rawValue !== "string") return `"${rawKey}" takes a text value.`;
|
|
10831
|
+
const k = rawKey.trim();
|
|
10832
|
+
const v = rawValue.trim();
|
|
10833
|
+
const problem = variantNameProblem("property", k) ?? variantNameProblem("value", v);
|
|
10834
|
+
if (problem) return problem;
|
|
10835
|
+
out[k] = v;
|
|
10836
|
+
}
|
|
10837
|
+
return out;
|
|
10838
|
+
}
|
|
10839
|
+
function variantOrder(ids, values) {
|
|
10840
|
+
const order = [];
|
|
10841
|
+
for (const id of ids) for (const k of Object.keys(values.get(id) ?? {})) if (!order.includes(k)) order.push(k);
|
|
10842
|
+
return order;
|
|
10843
|
+
}
|
|
10844
|
+
function withVariantValues(node, values, order) {
|
|
10845
|
+
const name = formatVariantName(values, order);
|
|
10846
|
+
const data = { ...dataOf(node), [COMPONENT_DATA_KEY]: { ...getComponentData(node) ?? {}, variant: values }, label: name };
|
|
10847
|
+
if (typeof data.name === "string" && data.name.includes("=")) data.name = name;
|
|
10848
|
+
return same(dataOf(node), data) ? node : withData(node, data);
|
|
10849
|
+
}
|
|
10850
|
+
function closestVariant(list, wanted) {
|
|
10851
|
+
const entries = Object.entries(wanted);
|
|
10852
|
+
let best;
|
|
10853
|
+
const better = (a, b) => {
|
|
10854
|
+
for (let i = 0; i < a.length; i++) if (a[i] !== b[i]) return a[i] > b[i];
|
|
10855
|
+
return false;
|
|
10856
|
+
};
|
|
10857
|
+
for (const v of list) {
|
|
10858
|
+
const values = getVariantValues(v);
|
|
10859
|
+
const matches2 = entries.map(([k, val]) => values[k] === val ? 1 : 0);
|
|
10860
|
+
const score = [matches2.reduce((a, b) => a + b, 0), ...matches2];
|
|
10861
|
+
if (!best || better(score, best.score)) best = { score, id: v.id };
|
|
10862
|
+
}
|
|
10863
|
+
return best?.id;
|
|
10864
|
+
}
|
|
10865
|
+
function variantEditIn(g, setId) {
|
|
10866
|
+
const set = g.get(setId);
|
|
10867
|
+
if (!isCanvasComponentSet(set)) return { code: "not-found", reason: "Choose a component set." };
|
|
10868
|
+
if (owningInstanceIn(g, setId)) return { code: "inside-instance", reason: "A component set inside an instance can't be edited: edit its main." };
|
|
10869
|
+
const variants = variantsIn(g, setId);
|
|
10870
|
+
return { set, variants, values: new Map(variants.map((v) => [v.id, getVariantValues(v)])) };
|
|
10871
|
+
}
|
|
10872
|
+
function addMissingProperties(edit, pairs, defaultValue) {
|
|
10873
|
+
const known = /* @__PURE__ */ new Set();
|
|
10874
|
+
for (const v of edit.values.values()) for (const k of Object.keys(v)) known.add(k);
|
|
10875
|
+
const added = Object.keys(pairs).filter((k) => !known.has(k));
|
|
10876
|
+
if (added.length === 0) return void 0;
|
|
10877
|
+
const problem = variantNameProblem("value", defaultValue);
|
|
10878
|
+
if (problem) return problem;
|
|
10879
|
+
const defs = dataOf(edit.set)[COMPONENT_SET_DATA_KEY]?.properties;
|
|
10880
|
+
const clash = isRecord(defs) ? added.find((k) => k in defs) : void 0;
|
|
10881
|
+
if (clash) return `"${clash}" is a component property.`;
|
|
10882
|
+
for (const [id, v] of edit.values) {
|
|
10883
|
+
const next = { ...v };
|
|
10884
|
+
for (const k of added) next[k] = defaultValue;
|
|
10885
|
+
edit.values.set(id, next);
|
|
10886
|
+
}
|
|
10887
|
+
return void 0;
|
|
10888
|
+
}
|
|
10889
|
+
function duplicateAmong(values, ids) {
|
|
10890
|
+
for (const id of ids) {
|
|
10891
|
+
const key = variantKey(values.get(id));
|
|
10892
|
+
for (const [other, v] of values) {
|
|
10893
|
+
if (other !== id && variantKey(v) === key) return formatVariantName(values.get(id), variantOrder([...values.keys()], values));
|
|
10894
|
+
}
|
|
10895
|
+
}
|
|
10896
|
+
return void 0;
|
|
10897
|
+
}
|
|
10898
|
+
function writeVariantEdit(edit, extra = []) {
|
|
10899
|
+
const order = variantOrder([...edit.variants.map((v) => v.id), ...extra], edit.values);
|
|
10900
|
+
const updates = /* @__PURE__ */ new Map();
|
|
10901
|
+
for (const v of edit.variants) {
|
|
10902
|
+
const next = withVariantValues(v, edit.values.get(v.id), order);
|
|
10903
|
+
if (next !== v) updates.set(v.id, next);
|
|
10904
|
+
}
|
|
10905
|
+
return updates;
|
|
10906
|
+
}
|
|
10907
|
+
function nextVariantValuesIn(g, setId, fromId) {
|
|
10908
|
+
const variants = variantsIn(g, setId);
|
|
10909
|
+
const base = getVariantValues(g.get(fromId));
|
|
10910
|
+
const first = variantOptionsIn(g, setId)[0];
|
|
10911
|
+
const prop = first?.name ?? "Property 1";
|
|
10912
|
+
const used = new Set(variants.map((v) => variantKey(getVariantValues(v))));
|
|
10913
|
+
for (let k = variants.length + 1; ; k++) {
|
|
10914
|
+
const values = { ...base, [prop]: `Variant${k}` };
|
|
10915
|
+
if (!first?.values.includes(`Variant${k}`) && !used.has(variantKey(values))) return values;
|
|
10916
|
+
}
|
|
10917
|
+
}
|
|
10918
|
+
function getNextVariantValues(nodes, setId, from) {
|
|
10919
|
+
const g = new Graph(nodes);
|
|
10920
|
+
return nextVariantValuesIn(g, setId, from ?? defaultVariantIn(g, setId));
|
|
10921
|
+
}
|
|
10922
|
+
function relayoutSet(nodes, setId, options) {
|
|
10923
|
+
if (options.layout === false || !getAutoLayout(nodes.find((n) => n.id === setId))) return nodes;
|
|
10924
|
+
return applyNodeUpdates(nodes, layoutUpMany(nodes, [setId], typeof options.layout === "object" ? options.layout : {}));
|
|
10925
|
+
}
|
|
10926
|
+
function addCanvasVariant(nodes, setId, options = {}) {
|
|
10927
|
+
const g = new Graph(nodes);
|
|
10928
|
+
const edit = variantEditIn(g, setId);
|
|
10929
|
+
if (!("set" in edit)) return fail(nodes, edit.code, edit.reason);
|
|
10930
|
+
if (edit.variants.length === 0) return fail(nodes, "invalid", "The set has no variant to duplicate.");
|
|
10931
|
+
const fromId = options.from ?? defaultVariantIn(g, setId);
|
|
10932
|
+
const source = edit.variants.find((v) => v.id === fromId);
|
|
10933
|
+
if (!source) return fail(nodes, "not-found", `"${fromId}" is not a variant of this set.`);
|
|
10934
|
+
const wanted = cleanVariantPairs(options.variant ?? nextVariantValuesIn(g, setId, fromId));
|
|
10935
|
+
if (typeof wanted === "string") return fail(nodes, "invalid", wanted);
|
|
10936
|
+
const problem = addMissingProperties(edit, wanted, (options.defaultValue ?? "Default").trim());
|
|
10937
|
+
if (problem) return fail(nodes, "invalid", problem);
|
|
10938
|
+
const id = options.id ?? uniqueId(`${setId}-variant`, g.byId);
|
|
10939
|
+
if (g.get(id)) return fail(nodes, "invalid", `Id "${id}" is taken.`);
|
|
10940
|
+
edit.values.set(id, { ...edit.values.get(fromId), ...wanted });
|
|
10941
|
+
const dup = duplicateAmong(edit.values, [id]);
|
|
10942
|
+
if (dup) return fail(nodes, "invalid", `A variant with ${dup} exists already.`);
|
|
10943
|
+
const autoLayout = !!getAutoLayout(edit.set);
|
|
10944
|
+
const { width, height } = getNodeSize(source);
|
|
10945
|
+
let position = options.position ? { ...options.position } : { ...source.position };
|
|
10946
|
+
if (!options.position && !autoLayout) {
|
|
10947
|
+
const rects = edit.variants.map((v) => ({ ...v.position, ...getNodeSize(v) }));
|
|
10948
|
+
position = { x: source.position.x + width + VARIANT_GAP, y: source.position.y };
|
|
10949
|
+
for (let k = 0; k <= rects.length; k++) {
|
|
10950
|
+
const hit = rects.find((r) => position.x < r.x + r.width && r.x < position.x + width && position.y < r.y + r.height && r.y < position.y + height);
|
|
10951
|
+
if (!hit) break;
|
|
10952
|
+
position = { x: hit.x + hit.width + VARIANT_GAP, y: position.y };
|
|
10953
|
+
}
|
|
10954
|
+
}
|
|
10955
|
+
const taken = new Set(g.byId.keys());
|
|
10956
|
+
taken.add(id);
|
|
10957
|
+
const map = /* @__PURE__ */ new Map([[fromId, id]]);
|
|
10958
|
+
const sub3 = g.subtree(fromId);
|
|
10959
|
+
for (const d of sub3) {
|
|
10960
|
+
if (d === fromId) continue;
|
|
10961
|
+
const nid = uniqueId(`${id}-${d}`, taken);
|
|
10962
|
+
taken.add(nid);
|
|
10963
|
+
map.set(d, nid);
|
|
10964
|
+
}
|
|
10965
|
+
const order = variantOrder([...edit.variants.map((v) => v.id), id], edit.values);
|
|
10966
|
+
const subSet = new Set(sub3);
|
|
10967
|
+
const copies = nodes.filter((n) => subSet.has(n.id)).map((n) => {
|
|
10968
|
+
const next = { ...n, id: map.get(n.id) };
|
|
10969
|
+
if (n.parentId !== void 0 && n.id !== fromId) next.parentId = map.get(n.parentId) ?? n.parentId;
|
|
10970
|
+
for (const k of RUNTIME_KEYS) delete next[k];
|
|
10971
|
+
if (n.id !== fromId) return next;
|
|
10972
|
+
next.position = position;
|
|
10973
|
+
return withVariantValues(next, edit.values.get(id), order);
|
|
10974
|
+
});
|
|
10975
|
+
const updates = writeVariantEdit(edit, [id]);
|
|
10976
|
+
const inSet = new Set(g.subtree(setId));
|
|
10977
|
+
let last = -1;
|
|
10978
|
+
nodes.forEach((n, i) => {
|
|
10979
|
+
if (inSet.has(n.id)) last = i;
|
|
10980
|
+
});
|
|
10981
|
+
let after = [];
|
|
10982
|
+
nodes.forEach((n, i) => {
|
|
10983
|
+
after.push(updates.get(n.id) ?? n);
|
|
10984
|
+
if (i === last) after.push(...copies);
|
|
10985
|
+
});
|
|
10986
|
+
if (autoLayout && !options.position) after = relayoutSet(after, setId, options);
|
|
10987
|
+
else {
|
|
10988
|
+
const size = getNodeSize(edit.set);
|
|
10989
|
+
const w = Math.max(size.width, position.x + width + VARIANT_GAP);
|
|
10990
|
+
const h = Math.max(size.height, position.y + height + VARIANT_GAP);
|
|
10991
|
+
if (w !== size.width || h !== size.height) after = applyNodeUpdates(after, [{ id: setId, width: w, height: h }]);
|
|
10992
|
+
}
|
|
10993
|
+
return done(nodes, after, [id], options);
|
|
10994
|
+
}
|
|
10995
|
+
function removeCanvasVariant(nodes, variantId, options = {}) {
|
|
10996
|
+
const g = new Graph(nodes);
|
|
10997
|
+
const set = variantSetIn(g, variantId);
|
|
10998
|
+
if (!set) return fail(nodes, "not-found", "Choose a variant of a component set.");
|
|
10999
|
+
if (owningInstanceIn(g, set.id)) return fail(nodes, "inside-instance", "A component set inside an instance can't be edited: edit its main.");
|
|
11000
|
+
const others = variantsIn(g, set.id).filter((v) => v.id !== variantId);
|
|
11001
|
+
if (others.length === 0) return fail(nodes, "invalid", "A component set needs at least one variant: delete the set instead.");
|
|
11002
|
+
const replacement = closestVariant(others, getVariantValues(g.get(variantId)));
|
|
11003
|
+
const swap = (v) => v === variantId ? replacement : v;
|
|
11004
|
+
const repointed = nodes.map((n) => {
|
|
11005
|
+
const data = dataOf(n);
|
|
11006
|
+
let next;
|
|
11007
|
+
const inst = getInstanceData(n);
|
|
11008
|
+
if (inst) {
|
|
11009
|
+
const moved = { ...inst, componentId: swap(inst.componentId) };
|
|
11010
|
+
if (inst.properties) moved.properties = Object.fromEntries(Object.entries(inst.properties).map(([k, v]) => [k, swap(v)]));
|
|
11011
|
+
if (!same(moved, inst)) next = { ...data, [INSTANCE_DATA_KEY]: moved };
|
|
11012
|
+
}
|
|
11013
|
+
for (const key of [COMPONENT_DATA_KEY, COMPONENT_SET_DATA_KEY]) {
|
|
11014
|
+
const owner = data[key];
|
|
11015
|
+
if (!isRecord(owner) || !isRecord(owner.properties)) continue;
|
|
11016
|
+
const defs = {};
|
|
11017
|
+
for (const [k, def] of Object.entries(owner.properties)) {
|
|
11018
|
+
defs[k] = def.type === "instanceSwap" ? { ...def, default: swap(def.default), ...def.preferred ? { preferred: [...new Set(def.preferred.map(swap))] } : {} } : def;
|
|
11019
|
+
}
|
|
11020
|
+
if (!same(defs, owner.properties)) next = { ...next ?? data, [key]: { ...owner, properties: defs } };
|
|
11021
|
+
}
|
|
11022
|
+
return next ? withData(n, next) : n;
|
|
11023
|
+
});
|
|
11024
|
+
const mid = syncCanvasComponents(nodes, repointed, options);
|
|
11025
|
+
const gone = new Set(new Graph(mid).subtree(variantId));
|
|
11026
|
+
const after = relayoutSet(
|
|
11027
|
+
mid.filter((n) => !gone.has(n.id)),
|
|
11028
|
+
set.id,
|
|
11029
|
+
options
|
|
11030
|
+
);
|
|
11031
|
+
return { ok: true, nodes: syncCanvasComponents(mid, after, options), ids: [replacement] };
|
|
11032
|
+
}
|
|
11033
|
+
function setCanvasVariantProperties(nodes, variantId, values, options = {}) {
|
|
11034
|
+
const g = new Graph(nodes);
|
|
11035
|
+
const set = variantSetIn(g, variantId);
|
|
11036
|
+
if (!set) return fail(nodes, "not-found", "Choose a variant of a component set.");
|
|
11037
|
+
const edit = variantEditIn(g, set.id);
|
|
11038
|
+
if (!("set" in edit)) return fail(nodes, edit.code, edit.reason);
|
|
11039
|
+
const wanted = cleanVariantPairs(values);
|
|
11040
|
+
if (typeof wanted === "string") return fail(nodes, "invalid", wanted);
|
|
11041
|
+
const problem = addMissingProperties(edit, wanted, (options.defaultValue ?? "Default").trim());
|
|
11042
|
+
if (problem) return fail(nodes, "invalid", problem);
|
|
11043
|
+
edit.values.set(variantId, { ...edit.values.get(variantId), ...wanted });
|
|
11044
|
+
const dup = duplicateAmong(edit.values, [variantId]);
|
|
11045
|
+
if (dup) return fail(nodes, "invalid", `A variant with ${dup} exists already.`);
|
|
11046
|
+
const updates = writeVariantEdit(edit);
|
|
11047
|
+
if (updates.size === 0) return { ok: true, nodes, ids: [variantId] };
|
|
11048
|
+
return done(nodes, nodes.map((n) => updates.get(n.id) ?? n), [variantId], options);
|
|
11049
|
+
}
|
|
11050
|
+
function updateInstanceValues(g, setId, updates, fn) {
|
|
11051
|
+
const ids = new Set(variantsIn(g, setId).map((v) => v.id));
|
|
11052
|
+
for (const n of g.nodes) {
|
|
11053
|
+
const inst = getInstanceData(n);
|
|
11054
|
+
if (!inst?.properties || !ids.has(inst.componentId)) continue;
|
|
11055
|
+
const props = fn({ ...inst.properties });
|
|
11056
|
+
if (same(props, inst.properties)) continue;
|
|
11057
|
+
const cur = updates.get(n.id) ?? n;
|
|
11058
|
+
updates.set(n.id, withData(cur, { ...dataOf(cur), [INSTANCE_DATA_KEY]: { ...inst, properties: props } }));
|
|
11059
|
+
}
|
|
11060
|
+
}
|
|
11061
|
+
function renameCanvasVariantProperty(nodes, setId, name, newName, options = {}) {
|
|
11062
|
+
const g = new Graph(nodes);
|
|
11063
|
+
const edit = variantEditIn(g, setId);
|
|
11064
|
+
if (!("set" in edit)) return fail(nodes, edit.code, edit.reason);
|
|
11065
|
+
const known = variantOptionsIn(g, setId);
|
|
11066
|
+
if (!known.some((o) => o.name === name)) return fail(nodes, "unknown-property", `Unknown variant property "${name}".`);
|
|
11067
|
+
const to = newName.trim();
|
|
11068
|
+
const problem = variantNameProblem("property", to);
|
|
11069
|
+
if (problem) return fail(nodes, "invalid", problem);
|
|
11070
|
+
if (to === name) return { ok: true, nodes, ids: [setId] };
|
|
11071
|
+
if (known.some((o) => o.name === to)) return fail(nodes, "invalid", `"${to}" exists already.`);
|
|
11072
|
+
if (to in propertyDefsIn(g, edit.variants[0].id)) return fail(nodes, "invalid", `"${to}" is a component property.`);
|
|
11073
|
+
const rename = (v) => Object.fromEntries(Object.entries(v).map(([k, val]) => [k === name ? to : k, val]));
|
|
11074
|
+
for (const [id, v] of edit.values) edit.values.set(id, rename(v));
|
|
11075
|
+
const updates = writeVariantEdit(edit);
|
|
11076
|
+
updateInstanceValues(g, setId, updates, (props) => name in props ? rename(props) : props);
|
|
11077
|
+
return done(nodes, nodes.map((n) => updates.get(n.id) ?? n), [setId], options);
|
|
11078
|
+
}
|
|
11079
|
+
function renameCanvasVariantValue(nodes, setId, property, value, newValue, options = {}) {
|
|
11080
|
+
const g = new Graph(nodes);
|
|
11081
|
+
const edit = variantEditIn(g, setId);
|
|
11082
|
+
if (!("set" in edit)) return fail(nodes, edit.code, edit.reason);
|
|
11083
|
+
const option = variantOptionsIn(g, setId).find((o) => o.name === property);
|
|
11084
|
+
if (!option) return fail(nodes, "unknown-property", `Unknown variant property "${property}".`);
|
|
11085
|
+
if (!option.values.includes(value)) return fail(nodes, "invalid", `"${property}" has no value "${value}".`);
|
|
11086
|
+
const to = newValue.trim();
|
|
11087
|
+
const problem = variantNameProblem("value", to);
|
|
11088
|
+
if (problem) return fail(nodes, "invalid", problem);
|
|
11089
|
+
if (to === value) return { ok: true, nodes, ids: [setId] };
|
|
11090
|
+
const touched = [];
|
|
11091
|
+
for (const [id, v] of edit.values) {
|
|
11092
|
+
if (v[property] !== value) continue;
|
|
11093
|
+
edit.values.set(id, { ...v, [property]: to });
|
|
11094
|
+
touched.push(id);
|
|
11095
|
+
}
|
|
11096
|
+
const dup = duplicateAmong(edit.values, touched);
|
|
11097
|
+
if (dup) return fail(nodes, "invalid", `A variant with ${dup} exists already.`);
|
|
11098
|
+
const updates = writeVariantEdit(edit);
|
|
11099
|
+
updateInstanceValues(g, setId, updates, (props) => props[property] === value ? { ...props, [property]: to } : props);
|
|
11100
|
+
return done(nodes, nodes.map((n) => updates.get(n.id) ?? n), [setId], options);
|
|
11101
|
+
}
|
|
11385
11102
|
|
|
11386
11103
|
// src/canvas/componentsController.ts
|
|
11387
11104
|
function makePatch(from, to) {
|
|
@@ -11449,6 +11166,24 @@ function createCanvasComponentsController(options) {
|
|
|
11449
11166
|
}
|
|
11450
11167
|
const reason = (result) => result.ok ? true : result.reason;
|
|
11451
11168
|
const nodes = () => options.getNodes();
|
|
11169
|
+
const refused = (why) => ({ ok: false, nodes: nodes(), code: "not-found", reason: why });
|
|
11170
|
+
const setOf = (id) => {
|
|
11171
|
+
const at = target(id);
|
|
11172
|
+
const node = at ? nodes().find((n) => n.id === at) : void 0;
|
|
11173
|
+
if (!node) return void 0;
|
|
11174
|
+
return isCanvasComponentSet(node) ? node.id : getVariantSet(nodes(), node.id);
|
|
11175
|
+
};
|
|
11176
|
+
const variantOf = (id) => {
|
|
11177
|
+
const at = target(id);
|
|
11178
|
+
return at && getVariantSet(nodes(), at) ? at : void 0;
|
|
11179
|
+
};
|
|
11180
|
+
const NO_SET = "Select a component set or one of its variants.";
|
|
11181
|
+
const NO_VARIANT = "Select a variant of a component set.";
|
|
11182
|
+
const addOptions = (setId, addOptions_) => {
|
|
11183
|
+
if (addOptions_.from !== void 0 || setId !== void 0) return addOptions_;
|
|
11184
|
+
const v = variantOf();
|
|
11185
|
+
return v ? { ...addOptions_, from: v } : addOptions_;
|
|
11186
|
+
};
|
|
11452
11187
|
const controller = {
|
|
11453
11188
|
sync,
|
|
11454
11189
|
/** Rebuilds every instance from its main (e.g. after loading), not recorded in history. */
|
|
@@ -11563,6 +11298,63 @@ function createCanvasComponentsController(options) {
|
|
|
11563
11298
|
(n) => setCanvasComponentProperty(n, componentId, name, def, { ...syncOptions(), ...propertyOptions })
|
|
11564
11299
|
);
|
|
11565
11300
|
},
|
|
11301
|
+
/**
|
|
11302
|
+
* Adds a variant to a set (default: the selection's set) by duplicating `from` (default:
|
|
11303
|
+
* the selected variant, else the default variant) with the given `variant` values.
|
|
11304
|
+
*/
|
|
11305
|
+
addVariant(setId, variantOptions = {}) {
|
|
11306
|
+
const set2 = setOf(setId);
|
|
11307
|
+
if (!set2) return refused(NO_SET);
|
|
11308
|
+
const opts = addOptions(setId, variantOptions);
|
|
11309
|
+
return run("Add Variant", (n) => addCanvasVariant(n, set2, { ...syncOptions(), ...opts }));
|
|
11310
|
+
},
|
|
11311
|
+
canAddVariant(setId, variantOptions = {}) {
|
|
11312
|
+
const set2 = setOf(setId);
|
|
11313
|
+
return set2 ? reason(addCanvasVariant(nodes(), set2, { ...addOptions(setId, variantOptions), layout: false })) : NO_SET;
|
|
11314
|
+
},
|
|
11315
|
+
/** Removes a variant (default: the selected one); its instances switch to the closest variant. */
|
|
11316
|
+
removeVariant(variantId) {
|
|
11317
|
+
const v = variantOf(variantId);
|
|
11318
|
+
if (!v) return refused(NO_VARIANT);
|
|
11319
|
+
return run("Remove Variant", (n) => removeCanvasVariant(n, v, syncOptions()));
|
|
11320
|
+
},
|
|
11321
|
+
canRemoveVariant(variantId) {
|
|
11322
|
+
const v = variantOf(variantId);
|
|
11323
|
+
return v ? reason(removeCanvasVariant(nodes(), v, { layout: false })) : NO_VARIANT;
|
|
11324
|
+
},
|
|
11325
|
+
/** Edits a variant's property values (default: the selected variant). */
|
|
11326
|
+
setVariantProperties(values, variantId, variantOptions = {}) {
|
|
11327
|
+
const v = variantOf(variantId);
|
|
11328
|
+
if (!v) return refused(NO_VARIANT);
|
|
11329
|
+
return run("Edit Variant", (n) => setCanvasVariantProperties(n, v, values, { ...syncOptions(), ...variantOptions }));
|
|
11330
|
+
},
|
|
11331
|
+
canSetVariantProperties(values, variantId) {
|
|
11332
|
+
const v = variantOf(variantId);
|
|
11333
|
+
if (!v) return NO_VARIANT;
|
|
11334
|
+
return values ? reason(setCanvasVariantProperties(nodes(), v, values, { layout: false })) : true;
|
|
11335
|
+
},
|
|
11336
|
+
/** Renames a variant property across a set (default: the selection's set). */
|
|
11337
|
+
renameVariantProperty(name, newName, setId) {
|
|
11338
|
+
const set2 = setOf(setId);
|
|
11339
|
+
if (!set2) return refused(NO_SET);
|
|
11340
|
+
return run("Rename Variant Property", (n) => renameCanvasVariantProperty(n, set2, name, newName, syncOptions()));
|
|
11341
|
+
},
|
|
11342
|
+
canRenameVariantProperty(name, newName, setId) {
|
|
11343
|
+
const set2 = setOf(setId);
|
|
11344
|
+
if (!set2) return NO_SET;
|
|
11345
|
+
return name !== void 0 && newName !== void 0 ? reason(renameCanvasVariantProperty(nodes(), set2, name, newName, { layout: false })) : true;
|
|
11346
|
+
},
|
|
11347
|
+
/** Renames a value of a variant property across a set (default: the selection's set). */
|
|
11348
|
+
renameVariantValue(property, value, newValue, setId) {
|
|
11349
|
+
const set2 = setOf(setId);
|
|
11350
|
+
if (!set2) return refused(NO_SET);
|
|
11351
|
+
return run("Rename Variant Value", (n) => renameCanvasVariantValue(n, set2, property, value, newValue, syncOptions()));
|
|
11352
|
+
},
|
|
11353
|
+
canRenameVariantValue(property, value, newValue, setId) {
|
|
11354
|
+
const set2 = setOf(setId);
|
|
11355
|
+
if (!set2) return NO_SET;
|
|
11356
|
+
return property !== void 0 && value !== void 0 && newValue !== void 0 ? reason(renameCanvasVariantValue(nodes(), set2, property, value, newValue, { layout: false })) : true;
|
|
11357
|
+
},
|
|
11566
11358
|
/** Binds (or with `null` unbinds) a main layer's field to a component property. */
|
|
11567
11359
|
bindComponentProperty(layerId, field, name) {
|
|
11568
11360
|
return run("Bind Component Property", (n) => bindCanvasComponentProperty(n, layerId, field, name, syncOptions()));
|
|
@@ -11644,6 +11436,7 @@ export {
|
|
|
11644
11436
|
StreamingLayoutManager,
|
|
11645
11437
|
TOKEN_BINDINGS_DATA_KEY,
|
|
11646
11438
|
TOKEN_MODES_DATA_KEY,
|
|
11439
|
+
addCanvasVariant,
|
|
11647
11440
|
addKeyframesAtTime,
|
|
11648
11441
|
alignNodes,
|
|
11649
11442
|
animateNode,
|
|
@@ -11767,6 +11560,7 @@ export {
|
|
|
11767
11560
|
getLayoutParent,
|
|
11768
11561
|
getLayoutSizing,
|
|
11769
11562
|
getLayoutedNodes,
|
|
11563
|
+
getNextVariantValues,
|
|
11770
11564
|
getNodeAnimation,
|
|
11771
11565
|
getNodeOverrides,
|
|
11772
11566
|
getNodeRect,
|
|
@@ -11880,7 +11674,10 @@ export {
|
|
|
11880
11674
|
recordAnimationChanges,
|
|
11881
11675
|
relayoutUpdates,
|
|
11882
11676
|
remapPastedNodes,
|
|
11677
|
+
removeCanvasVariant,
|
|
11883
11678
|
removeTrack,
|
|
11679
|
+
renameCanvasVariantProperty,
|
|
11680
|
+
renameCanvasVariantValue,
|
|
11884
11681
|
reorderNodes,
|
|
11885
11682
|
repositionNodesTo,
|
|
11886
11683
|
resetCanvasInstanceOverrides,
|
|
@@ -11908,6 +11705,7 @@ export {
|
|
|
11908
11705
|
setCanvasComponentProperty,
|
|
11909
11706
|
setCanvasInstanceProperties,
|
|
11910
11707
|
setCanvasTokenMode,
|
|
11708
|
+
setCanvasVariantProperties,
|
|
11911
11709
|
setKeyframe,
|
|
11912
11710
|
setKeyframeEasing,
|
|
11913
11711
|
setMotionPath,
|