@grida/svg-editor 1.0.0-alpha.4 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/README.md +365 -384
  2. package/dist/dom-BEjG2bSw.d.mts +320 -0
  3. package/dist/dom-DYD1BHTo.js +6050 -0
  4. package/dist/dom-Y2QR7QSi.d.ts +318 -0
  5. package/dist/dom-tM3Dr1EK.mjs +5971 -0
  6. package/dist/dom.d.mts +3 -2
  7. package/dist/dom.d.ts +3 -2
  8. package/dist/dom.js +13 -1
  9. package/dist/dom.mjs +2 -2
  10. package/dist/editor-CWNtt1vu.mjs +2998 -0
  11. package/dist/editor-CZgwg8BK.d.mts +2537 -0
  12. package/dist/editor-CesShk9o.js +3004 -0
  13. package/dist/editor-kFTYSb_8.d.ts +2537 -0
  14. package/dist/index.d.mts +2 -2
  15. package/dist/index.d.ts +2 -2
  16. package/dist/index.js +5 -2
  17. package/dist/index.mjs +3 -2
  18. package/dist/model-D0iEDcg3.mjs +5451 -0
  19. package/dist/model-tywvTGZv.js +5640 -0
  20. package/dist/presets.d.mts +17 -2
  21. package/dist/presets.d.ts +17 -2
  22. package/dist/presets.js +20 -15
  23. package/dist/presets.mjs +19 -15
  24. package/dist/react.d.mts +88 -10
  25. package/dist/react.d.ts +88 -10
  26. package/dist/react.js +169 -14
  27. package/dist/react.mjs +159 -16
  28. package/package.json +35 -9
  29. package/dist/dom-CmOu0HvI.mjs +0 -1623
  30. package/dist/dom-Cn-RtjRL.d.ts +0 -48
  31. package/dist/dom-CoVZzFqy.js +0 -1672
  32. package/dist/dom-DJnZhtOd.d.mts +0 -48
  33. package/dist/editor-CjK56cgb.mjs +0 -1823
  34. package/dist/editor-D2l_CDr0.d.ts +0 -818
  35. package/dist/editor-D2zZAyny.js +0 -1835
  36. package/dist/editor-Uu6dZX4y.d.mts +0 -818
  37. package/dist/index-CHiXYO9-.d.ts +0 -1
  38. package/dist/index-ThDLM4Am.d.mts +0 -1
  39. package/dist/paint-Cfiw4g_J.mjs +0 -477
  40. package/dist/paint-dDV-Trt9.js +0 -531
  41. /package/dist/{chunk-CfYAbeIz.mjs → chunk-D7D4PA-g.mjs} +0 -0
@@ -1 +0,0 @@
1
- export { };
@@ -1 +0,0 @@
1
- export { };
@@ -1,477 +0,0 @@
1
- import { SVGPathData, SVGPathDataTransformer } from "svg-pathdata";
2
- //#region src/util/dom.ts
3
- /**
4
- * `true` when the document's active element is a text-input-like control
5
- * (input / textarea / contentEditable). Used by keymap + gesture defaults
6
- * to avoid hijacking keystrokes while the user is typing.
7
- */
8
- function is_text_input_focused() {
9
- if (typeof document === "undefined") return false;
10
- const el = document.activeElement;
11
- if (!el) return false;
12
- const tag = el.tagName;
13
- if (tag === "INPUT" || tag === "TEXTAREA") return true;
14
- if (el.isContentEditable) return true;
15
- return false;
16
- }
17
- //#endregion
18
- //#region src/core/intents.ts
19
- function num(doc, id, name, fallback = 0) {
20
- const v = doc.get_attr(id, name);
21
- if (v === null || v === "") return fallback;
22
- const n = parseFloat(v);
23
- return Number.isFinite(n) ? n : fallback;
24
- }
25
- function capture_translate_baseline(doc, id) {
26
- const tag = doc.tag_of(id);
27
- const own_transform = doc.get_attr(id, "transform");
28
- if (own_transform !== null || tag === "g") return {
29
- type: "viaTransform",
30
- transform: own_transform
31
- };
32
- switch (tag) {
33
- case "rect": return {
34
- type: "rect",
35
- x: num(doc, id, "x"),
36
- y: num(doc, id, "y")
37
- };
38
- case "circle": return {
39
- type: "circle",
40
- cx: num(doc, id, "cx"),
41
- cy: num(doc, id, "cy")
42
- };
43
- case "ellipse": return {
44
- type: "ellipse",
45
- cx: num(doc, id, "cx"),
46
- cy: num(doc, id, "cy")
47
- };
48
- case "line": return {
49
- type: "line",
50
- x1: num(doc, id, "x1"),
51
- y1: num(doc, id, "y1"),
52
- x2: num(doc, id, "x2"),
53
- y2: num(doc, id, "y2")
54
- };
55
- case "polyline": return {
56
- type: "polyline",
57
- points: doc.get_attr(id, "points") ?? ""
58
- };
59
- case "polygon": return {
60
- type: "polygon",
61
- points: doc.get_attr(id, "points") ?? ""
62
- };
63
- case "path": return {
64
- type: "path",
65
- d: doc.get_attr(id, "d") ?? ""
66
- };
67
- case "text": return {
68
- type: "text",
69
- x: num(doc, id, "x"),
70
- y: num(doc, id, "y")
71
- };
72
- case "tspan": return {
73
- type: "tspan",
74
- x: num(doc, id, "x"),
75
- y: num(doc, id, "y")
76
- };
77
- case "image": return {
78
- type: "image",
79
- x: num(doc, id, "x"),
80
- y: num(doc, id, "y")
81
- };
82
- case "use": return {
83
- type: "use",
84
- x: num(doc, id, "x"),
85
- y: num(doc, id, "y")
86
- };
87
- default: return { type: "unsupported" };
88
- }
89
- }
90
- function shift_points_string(points, dx, dy) {
91
- const nums = points.split(/[\s,]+/).filter(Boolean).map(Number);
92
- const out = [];
93
- for (let i = 0; i + 1 < nums.length; i += 2) out.push(`${nums[i] + dx},${nums[i + 1] + dy}`);
94
- return out.join(" ");
95
- }
96
- function compose_leading_translate(existing, dx, dy) {
97
- if (dx === 0 && dy === 0) return existing ? existing : null;
98
- if (!existing) return `translate(${dx} ${dy})`;
99
- const N = "[+-]?(?:\\d+\\.?\\d*|\\.\\d+)(?:[eE][+-]?\\d+)?";
100
- const re = new RegExp(`^\\s*translate\\(\\s*(${N})(?:\\s*,\\s*|\\s+)(${N})\\s*\\)\\s*(.*)$`);
101
- const m = existing.match(re);
102
- if (m) {
103
- const tx = parseFloat(m[1]) + dx;
104
- const ty = parseFloat(m[2]) + dy;
105
- const rest = m[3].trim();
106
- return rest ? `translate(${tx} ${ty}) ${rest}` : `translate(${tx} ${ty})`;
107
- }
108
- return `translate(${dx} ${dy}) ${existing}`;
109
- }
110
- function shift_path_d(d, dx, dy) {
111
- if (dx === 0 && dy === 0) return d;
112
- try {
113
- return new SVGPathData(d).transform(SVGPathDataTransformer.TRANSLATE(dx, dy)).encode();
114
- } catch {
115
- return d;
116
- }
117
- }
118
- function apply_translate(doc, id, baseline, dx, dy) {
119
- switch (baseline.type) {
120
- case "viaTransform":
121
- doc.set_attr(id, "transform", compose_leading_translate(baseline.transform ?? "", dx, dy));
122
- return;
123
- case "rect":
124
- case "image":
125
- case "use":
126
- case "text":
127
- case "tspan":
128
- doc.set_attr(id, "x", String(baseline.x + dx));
129
- doc.set_attr(id, "y", String(baseline.y + dy));
130
- return;
131
- case "circle":
132
- case "ellipse":
133
- doc.set_attr(id, "cx", String(baseline.cx + dx));
134
- doc.set_attr(id, "cy", String(baseline.cy + dy));
135
- return;
136
- case "line":
137
- doc.set_attr(id, "x1", String(baseline.x1 + dx));
138
- doc.set_attr(id, "y1", String(baseline.y1 + dy));
139
- doc.set_attr(id, "x2", String(baseline.x2 + dx));
140
- doc.set_attr(id, "y2", String(baseline.y2 + dy));
141
- return;
142
- case "polyline":
143
- case "polygon":
144
- doc.set_attr(id, "points", shift_points_string(baseline.points, dx, dy));
145
- return;
146
- case "path":
147
- doc.set_attr(id, "d", shift_path_d(baseline.d, dx, dy));
148
- return;
149
- case "unsupported": return;
150
- }
151
- }
152
- function is_resizable(tag) {
153
- switch (tag) {
154
- case "rect":
155
- case "image":
156
- case "use":
157
- case "circle":
158
- case "ellipse":
159
- case "line":
160
- case "polyline":
161
- case "polygon":
162
- case "path":
163
- case "text": return true;
164
- default: return false;
165
- }
166
- }
167
- function capture_resize_baseline(doc, id, bbox) {
168
- const tag = doc.tag_of(id);
169
- let attrs;
170
- switch (tag) {
171
- case "rect":
172
- attrs = {
173
- kind: "rect",
174
- x: num(doc, id, "x"),
175
- y: num(doc, id, "y"),
176
- w: num(doc, id, "width", bbox.width),
177
- h: num(doc, id, "height", bbox.height)
178
- };
179
- break;
180
- case "image":
181
- attrs = {
182
- kind: "image",
183
- x: num(doc, id, "x"),
184
- y: num(doc, id, "y"),
185
- w: num(doc, id, "width", bbox.width),
186
- h: num(doc, id, "height", bbox.height)
187
- };
188
- break;
189
- case "use":
190
- attrs = {
191
- kind: "use",
192
- x: num(doc, id, "x"),
193
- y: num(doc, id, "y"),
194
- w: num(doc, id, "width", bbox.width),
195
- h: num(doc, id, "height", bbox.height)
196
- };
197
- break;
198
- case "circle":
199
- attrs = {
200
- kind: "circle",
201
- cx: num(doc, id, "cx"),
202
- cy: num(doc, id, "cy"),
203
- r: num(doc, id, "r")
204
- };
205
- break;
206
- case "ellipse":
207
- attrs = {
208
- kind: "ellipse",
209
- cx: num(doc, id, "cx"),
210
- cy: num(doc, id, "cy"),
211
- rx: num(doc, id, "rx"),
212
- ry: num(doc, id, "ry")
213
- };
214
- break;
215
- case "line":
216
- attrs = {
217
- kind: "line",
218
- x1: num(doc, id, "x1"),
219
- y1: num(doc, id, "y1"),
220
- x2: num(doc, id, "x2"),
221
- y2: num(doc, id, "y2")
222
- };
223
- break;
224
- case "polyline":
225
- attrs = {
226
- kind: "polyline",
227
- points: doc.get_attr(id, "points") ?? ""
228
- };
229
- break;
230
- case "polygon":
231
- attrs = {
232
- kind: "polygon",
233
- points: doc.get_attr(id, "points") ?? ""
234
- };
235
- break;
236
- case "path":
237
- attrs = {
238
- kind: "path",
239
- d: doc.get_attr(id, "d") ?? ""
240
- };
241
- break;
242
- case "text":
243
- attrs = {
244
- kind: "text",
245
- x: num(doc, id, "x"),
246
- y: num(doc, id, "y"),
247
- fontSize: parseFloat(doc.get_attr(id, "font-size") ?? "16") || 16
248
- };
249
- break;
250
- default: attrs = { kind: "unsupported" };
251
- }
252
- return {
253
- bbox,
254
- attrs
255
- };
256
- }
257
- function compute_resize_factors(baseline, dir, dx, dy, shift) {
258
- const b = baseline.bbox;
259
- let anchorX = 0;
260
- let anchorY = 0;
261
- let baseHX = 0;
262
- let baseHY = 0;
263
- let affectsX = true;
264
- let affectsY = true;
265
- switch (dir) {
266
- case "nw":
267
- anchorX = b.x + b.width;
268
- anchorY = b.y + b.height;
269
- baseHX = b.x;
270
- baseHY = b.y;
271
- break;
272
- case "n":
273
- anchorX = b.x + b.width / 2;
274
- anchorY = b.y + b.height;
275
- baseHX = b.x + b.width / 2;
276
- baseHY = b.y;
277
- affectsX = false;
278
- break;
279
- case "ne":
280
- anchorX = b.x;
281
- anchorY = b.y + b.height;
282
- baseHX = b.x + b.width;
283
- baseHY = b.y;
284
- break;
285
- case "e":
286
- anchorX = b.x;
287
- anchorY = b.y + b.height / 2;
288
- baseHX = b.x + b.width;
289
- baseHY = b.y + b.height / 2;
290
- affectsY = false;
291
- break;
292
- case "se":
293
- anchorX = b.x;
294
- anchorY = b.y;
295
- baseHX = b.x + b.width;
296
- baseHY = b.y + b.height;
297
- break;
298
- case "s":
299
- anchorX = b.x + b.width / 2;
300
- anchorY = b.y;
301
- baseHX = b.x + b.width / 2;
302
- baseHY = b.y + b.height;
303
- affectsX = false;
304
- break;
305
- case "sw":
306
- anchorX = b.x + b.width;
307
- anchorY = b.y;
308
- baseHX = b.x;
309
- baseHY = b.y + b.height;
310
- break;
311
- case "w":
312
- anchorX = b.x + b.width;
313
- anchorY = b.y + b.height / 2;
314
- baseHX = b.x;
315
- baseHY = b.y + b.height / 2;
316
- affectsY = false;
317
- break;
318
- }
319
- const newHX = baseHX + (affectsX ? dx : 0);
320
- const newHY = baseHY + (affectsY ? dy : 0);
321
- const denomX = baseHX - anchorX;
322
- const denomY = baseHY - anchorY;
323
- let sx = affectsX && denomX !== 0 ? (newHX - anchorX) / denomX : 1;
324
- let sy = affectsY && denomY !== 0 ? (newHY - anchorY) / denomY : 1;
325
- if (shift && affectsX && affectsY) {
326
- const mag = Math.max(Math.abs(sx), Math.abs(sy));
327
- sx = sx >= 0 ? mag : -mag;
328
- sy = sy >= 0 ? mag : -mag;
329
- }
330
- sx = Math.max(.001, sx);
331
- sy = Math.max(.001, sy);
332
- return {
333
- sx,
334
- sy,
335
- origin: {
336
- x: anchorX,
337
- y: anchorY
338
- }
339
- };
340
- }
341
- function scale_points_string(points, origin, sx, sy) {
342
- const nums = points.split(/[\s,]+/).filter(Boolean).map(Number);
343
- const out = [];
344
- for (let i = 0; i + 1 < nums.length; i += 2) {
345
- const x = origin.x + (nums[i] - origin.x) * sx;
346
- const y = origin.y + (nums[i + 1] - origin.y) * sy;
347
- out.push(`${x},${y}`);
348
- }
349
- return out.join(" ");
350
- }
351
- function scale_path_d(d, origin, sx, sy) {
352
- try {
353
- const e = origin.x * (1 - sx);
354
- const f = origin.y * (1 - sy);
355
- return new SVGPathData(d).transform(SVGPathDataTransformer.MATRIX(sx, 0, 0, sy, e, f)).encode();
356
- } catch {
357
- return d;
358
- }
359
- }
360
- function apply_resize(doc, id, baseline, sx, sy, origin) {
361
- const a = baseline.attrs;
362
- switch (a.kind) {
363
- case "rect":
364
- case "image":
365
- case "use":
366
- doc.set_attr(id, "x", String(origin.x + (a.x - origin.x) * sx));
367
- doc.set_attr(id, "y", String(origin.y + (a.y - origin.y) * sy));
368
- doc.set_attr(id, "width", String(Math.max(.001, a.w * sx)));
369
- doc.set_attr(id, "height", String(Math.max(.001, a.h * sy)));
370
- return;
371
- case "circle": {
372
- const s = Math.min(sx, sy);
373
- doc.set_attr(id, "cx", String(origin.x + (a.cx - origin.x) * s));
374
- doc.set_attr(id, "cy", String(origin.y + (a.cy - origin.y) * s));
375
- doc.set_attr(id, "r", String(Math.max(.001, a.r * s)));
376
- return;
377
- }
378
- case "ellipse":
379
- doc.set_attr(id, "cx", String(origin.x + (a.cx - origin.x) * sx));
380
- doc.set_attr(id, "cy", String(origin.y + (a.cy - origin.y) * sy));
381
- doc.set_attr(id, "rx", String(Math.max(.001, a.rx * sx)));
382
- doc.set_attr(id, "ry", String(Math.max(.001, a.ry * sy)));
383
- return;
384
- case "line":
385
- doc.set_attr(id, "x1", String(origin.x + (a.x1 - origin.x) * sx));
386
- doc.set_attr(id, "y1", String(origin.y + (a.y1 - origin.y) * sy));
387
- doc.set_attr(id, "x2", String(origin.x + (a.x2 - origin.x) * sx));
388
- doc.set_attr(id, "y2", String(origin.y + (a.y2 - origin.y) * sy));
389
- return;
390
- case "polyline":
391
- case "polygon":
392
- doc.set_attr(id, "points", scale_points_string(a.points, origin, sx, sy));
393
- return;
394
- case "path":
395
- doc.set_attr(id, "d", scale_path_d(a.d, origin, sx, sy));
396
- return;
397
- case "text": {
398
- if (!(sx !== 1 && sy !== 1)) return;
399
- const s = Math.min(sx, sy);
400
- doc.set_attr(id, "x", String(origin.x + (a.x - origin.x) * s));
401
- doc.set_attr(id, "y", String(origin.y + (a.y - origin.y) * s));
402
- doc.set_attr(id, "font-size", String(Math.max(1, a.fontSize * s)));
403
- return;
404
- }
405
- case "unsupported": return;
406
- }
407
- }
408
- //#endregion
409
- //#region src/core/paint.ts
410
- /**
411
- * Parse a *computed* paint string into the discriminated union. Returns null
412
- * for `inherit` / `var()` / empty. Returns an invalid-computed-value record
413
- * for syntactic errors (rare; we're permissive).
414
- */
415
- function parse_paint(declared) {
416
- if (declared === null || declared === "") return null;
417
- const trimmed = declared.trim();
418
- if (trimmed === "") return null;
419
- if (trimmed === "inherit" || trimmed === "initial" || trimmed === "unset" || trimmed === "revert" || trimmed === "revert-layer") return null;
420
- if (/^var\s*\(/i.test(trimmed)) return {
421
- error: "invalid_at_computed_value_time",
422
- reason: "var() substitution requires a cascade engine (not implemented)"
423
- };
424
- if (trimmed === "none") return { kind: "none" };
425
- if (trimmed === "context-fill" || trimmed === "contextFill") return { kind: "context_fill" };
426
- if (trimmed === "context-stroke" || trimmed === "contextStroke") return { kind: "context_stroke" };
427
- const url_match = trimmed.match(/^url\(\s*(["']?)#([^)"']+)\1\s*\)\s*(.*)$/i);
428
- if (url_match) {
429
- const id = url_match[2];
430
- const rest = url_match[3].trim();
431
- let fallback;
432
- if (rest !== "") {
433
- const f = parse_paint(rest);
434
- if (f && f.kind === "none") fallback = { kind: "none" };
435
- else if (f && f.kind === "color") fallback = {
436
- kind: "color",
437
- value: f.value
438
- };
439
- }
440
- return fallback ? {
441
- kind: "ref",
442
- id,
443
- fallback
444
- } : {
445
- kind: "ref",
446
- id
447
- };
448
- }
449
- if (/^currentcolor$/i.test(trimmed)) return {
450
- kind: "color",
451
- value: { kind: "current_color" }
452
- };
453
- return {
454
- kind: "color",
455
- value: {
456
- kind: "rgb",
457
- value: trimmed
458
- }
459
- };
460
- }
461
- /** Serialize a Paint back to an SVG attribute / inline-style value. */
462
- function serialize_paint(paint) {
463
- switch (paint.kind) {
464
- case "none": return "none";
465
- case "context_fill": return "context-fill";
466
- case "context_stroke": return "context-stroke";
467
- case "color": return paint.value.kind === "current_color" ? "currentColor" : paint.value.value;
468
- case "ref":
469
- if (paint.fallback) {
470
- const f = paint.fallback.kind === "none" ? "none" : paint.fallback.value.kind === "current_color" ? "currentColor" : paint.fallback.value.value;
471
- return `url(#${paint.id}) ${f}`;
472
- }
473
- return `url(#${paint.id})`;
474
- }
475
- }
476
- //#endregion
477
- export { capture_resize_baseline as a, is_resizable as c, apply_translate as i, is_text_input_focused as l, serialize_paint as n, capture_translate_baseline as o, apply_resize as r, compute_resize_factors as s, parse_paint as t };