@pixerate/editor-react 0.72.0 → 0.74.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/dist/{chunk-YAFNCBDA.js → chunk-LWMUV5TC.js} +477 -377
- package/dist/chunk-LWMUV5TC.js.map +1 -0
- package/dist/image-editor/index.cjs +540 -440
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +12 -1
- package/dist/image-editor/index.d.ts +12 -1
- package/dist/image-editor/index.js +3 -1
- package/dist/index.cjs +969 -869
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/vector/index.cjs +910 -175
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +67 -3
- package/dist/vector/index.d.ts +67 -3
- package/dist/vector/index.js +915 -164
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-YAFNCBDA.js.map +0 -1
package/dist/vector/index.js
CHANGED
|
@@ -49,6 +49,7 @@ import {
|
|
|
49
49
|
VECTOR_OVERLAY_THEME as THEME2,
|
|
50
50
|
getVectorOverlayRenderData,
|
|
51
51
|
getVectorSceneRenderData,
|
|
52
|
+
getVectorSymbolEditBackground,
|
|
52
53
|
isVectorTextTarget,
|
|
53
54
|
toVectorKeyInput,
|
|
54
55
|
toVectorPointerInput,
|
|
@@ -72,11 +73,11 @@ function VectorWidthPointEditor({ controller, edit, accentColor = THEME.accent,
|
|
|
72
73
|
refocus();
|
|
73
74
|
controller.closeWidthPointEdit();
|
|
74
75
|
};
|
|
75
|
-
const
|
|
76
|
+
const num2 = (v) => v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
|
|
76
77
|
const apply = () => {
|
|
77
|
-
const t =
|
|
78
|
+
const t = num2(position);
|
|
78
79
|
refocus();
|
|
79
|
-
controller.commitWidthPointEdit({ t: t === void 0 ? void 0 : Math.min(Math.max(t, 0), 100) / 100, left:
|
|
80
|
+
controller.commitWidthPointEdit({ t: t === void 0 ? void 0 : Math.min(Math.max(t, 0), 100) / 100, left: num2(left), right: num2(right) });
|
|
80
81
|
};
|
|
81
82
|
const stop = (e) => e.stopPropagation();
|
|
82
83
|
return /* @__PURE__ */ jsxs(
|
|
@@ -192,9 +193,42 @@ function VectorFilterDefs({ filters }) {
|
|
|
192
193
|
return /* @__PURE__ */ jsx2("defs", { children: filters.map((f) => /* @__PURE__ */ jsx2("filter", { id: f.id, filterUnits: "userSpaceOnUse", x: f.x, y: f.y, width: f.width, height: f.height, colorInterpolationFilters: "sRGB", children: f.primitives.map(primitive) }, f.id)) });
|
|
193
194
|
}
|
|
194
195
|
var blendStyle = (blendMode) => blendMode ? { mixBlendMode: blendMode } : void 0;
|
|
196
|
+
function VectorTextElement({ node }) {
|
|
197
|
+
return /* @__PURE__ */ jsx2("g", { "data-vector-id": node.id, "data-vector-text": "", transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, filter: node.filter, style: blendStyle(node.blendMode), children: node.layers.map((layer, i) => /* @__PURE__ */ jsx2(
|
|
198
|
+
"text",
|
|
199
|
+
{
|
|
200
|
+
"data-vector-layer": layer.kind,
|
|
201
|
+
xmlSpace: "preserve",
|
|
202
|
+
fill: layer.fill,
|
|
203
|
+
stroke: layer.stroke,
|
|
204
|
+
strokeWidth: layer.stroke === "none" ? void 0 : layer.strokeWidth,
|
|
205
|
+
strokeLinejoin: layer.strokeLinejoin,
|
|
206
|
+
strokeDasharray: layer.strokeDasharray,
|
|
207
|
+
opacity: layer.opacity < 1 ? layer.opacity : void 0,
|
|
208
|
+
style: { whiteSpace: "pre", ...blendStyle(layer.blendMode) },
|
|
209
|
+
children: node.spans.map((sp, k) => /* @__PURE__ */ jsx2(
|
|
210
|
+
"tspan",
|
|
211
|
+
{
|
|
212
|
+
x: sp.x,
|
|
213
|
+
y: sp.y,
|
|
214
|
+
rotate: sp.rotate,
|
|
215
|
+
fontFamily: sp.fontFamily,
|
|
216
|
+
fontSize: sp.fontSize,
|
|
217
|
+
fontWeight: sp.fontWeight,
|
|
218
|
+
fontStyle: sp.fontStyle,
|
|
219
|
+
textDecoration: sp.textDecoration,
|
|
220
|
+
fill: layer.runFills && sp.fill ? sp.fill : void 0,
|
|
221
|
+
children: sp.text
|
|
222
|
+
},
|
|
223
|
+
k
|
|
224
|
+
))
|
|
225
|
+
},
|
|
226
|
+
i
|
|
227
|
+
)) });
|
|
228
|
+
}
|
|
195
229
|
function VectorSceneNodes({ nodes }) {
|
|
196
230
|
return /* @__PURE__ */ jsx2(Fragment, { children: nodes.map(
|
|
197
|
-
(node) => node.kind === "group" ? /* @__PURE__ */ jsx2(
|
|
231
|
+
(node) => node.kind === "text" ? /* @__PURE__ */ jsx2(VectorTextElement, { node }, node.id) : node.kind === "group" ? /* @__PURE__ */ jsx2(
|
|
198
232
|
"g",
|
|
199
233
|
{
|
|
200
234
|
"data-vector-id": node.id,
|
|
@@ -325,10 +359,13 @@ function VectorCanvas({
|
|
|
325
359
|
}) {
|
|
326
360
|
const state = useVectorEditorState(controller);
|
|
327
361
|
const svgRef = useRef3(null);
|
|
362
|
+
const textRef = useRef3(null);
|
|
363
|
+
const editingText = state.textEdit?.nodeId ?? null;
|
|
328
364
|
const fitted = useRef3(!fitOnMount);
|
|
329
365
|
const uid = useId().replace(/:/g, "");
|
|
330
366
|
const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });
|
|
331
367
|
const overlay = getVectorOverlayRenderData(state);
|
|
368
|
+
const host = getVectorSymbolEditBackground(state, { idPrefix: `ve${uid}-host-` });
|
|
332
369
|
useEffect2(() => {
|
|
333
370
|
const el = svgRef.current;
|
|
334
371
|
if (!el || fitted.current) return;
|
|
@@ -344,6 +381,11 @@ function VectorCanvas({
|
|
|
344
381
|
observer.observe(el);
|
|
345
382
|
return () => observer.disconnect();
|
|
346
383
|
}, [controller]);
|
|
384
|
+
useEffect2(() => {
|
|
385
|
+
if (!editingText) return;
|
|
386
|
+
const t = setTimeout(() => textRef.current?.focus({ preventScroll: true }), 0);
|
|
387
|
+
return () => clearTimeout(t);
|
|
388
|
+
}, [editingText]);
|
|
347
389
|
useEffect2(() => {
|
|
348
390
|
const el = svgRef.current;
|
|
349
391
|
if (!el) return;
|
|
@@ -429,7 +471,30 @@ function VectorCanvas({
|
|
|
429
471
|
stroke: "rgba(0,0,0,0.12)"
|
|
430
472
|
}
|
|
431
473
|
),
|
|
474
|
+
host && /* @__PURE__ */ jsxs2("g", { "data-vector-symbol-host": "", opacity: 0.3, pointerEvents: "none", transform: host.transform, children: [
|
|
475
|
+
/* @__PURE__ */ jsx2(VectorGradientDefs, { defs: host.defs }),
|
|
476
|
+
/* @__PURE__ */ jsx2(VectorFilterDefs, { filters: host.filters }),
|
|
477
|
+
/* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: host.nodes })
|
|
478
|
+
] }),
|
|
432
479
|
/* @__PURE__ */ jsx2("g", { "data-vector-scene": "", transform: scene.transform, children: /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: scene.nodes }) }),
|
|
480
|
+
overlay.frames.length > 0 && /* @__PURE__ */ jsx2("g", { "data-vector-frame-labels": "", children: overlay.frames.map((f) => /* @__PURE__ */ jsx2(
|
|
481
|
+
"text",
|
|
482
|
+
{
|
|
483
|
+
"data-vector-frame-label": f.id,
|
|
484
|
+
x: f.x,
|
|
485
|
+
y: f.y - 6,
|
|
486
|
+
fontSize: 11,
|
|
487
|
+
fontWeight: f.selected ? 600 : 400,
|
|
488
|
+
fill: f.selected ? accentColor : "#737373",
|
|
489
|
+
style: { fontFamily: "system-ui, sans-serif", cursor: "default", userSelect: "none" },
|
|
490
|
+
onPointerDown: (e) => {
|
|
491
|
+
e.stopPropagation();
|
|
492
|
+
controller.select([f.id]);
|
|
493
|
+
},
|
|
494
|
+
children: f.name
|
|
495
|
+
},
|
|
496
|
+
f.id
|
|
497
|
+
)) }),
|
|
433
498
|
/* @__PURE__ */ jsxs2("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
|
|
434
499
|
overlay.outlines.map((o) => /* @__PURE__ */ jsx2("path", { d: o.d, stroke: accentColor, strokeWidth: o.selected ? 1 : 1.5, opacity: o.selected ? 1 : 0.7 }, o.id)),
|
|
435
500
|
overlay.penPreview && /* @__PURE__ */ jsx2("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
|
|
@@ -571,6 +636,38 @@ function VectorCanvas({
|
|
|
571
636
|
}
|
|
572
637
|
),
|
|
573
638
|
overlay.cutPoint && /* @__PURE__ */ jsx2("circle", { "data-vector-cut-point": "", cx: overlay.cutPoint.x, cy: overlay.cutPoint.y, r: THEME2.handleRadius + 1, fill: THEME2.handleFill, stroke: accentColor }),
|
|
639
|
+
overlay.text && /* @__PURE__ */ jsxs2("g", { "data-vector-text-edit": overlay.text.nodeId, "data-editing": overlay.text.editing || void 0, children: [
|
|
640
|
+
/* @__PURE__ */ jsx2("path", { d: overlay.text.frame, stroke: accentColor, strokeDasharray: overlay.text.editing ? "3 2" : void 0, opacity: 0.8 }),
|
|
641
|
+
overlay.text.selection.map((d, i) => /* @__PURE__ */ jsx2("path", { "data-vector-text-selection": "", d, fill: accentColor, fillOpacity: 0.25, stroke: "none" }, i)),
|
|
642
|
+
overlay.text.caret && /* @__PURE__ */ jsx2("line", { "data-vector-text-caret": "", x1: overlay.text.caret.x1, y1: overlay.text.caret.y1, x2: overlay.text.caret.x2, y2: overlay.text.caret.y2, stroke: accentColor, strokeWidth: 1.5 }),
|
|
643
|
+
overlay.text.overflow && /* @__PURE__ */ jsxs2("g", { "data-vector-text-overflow": "", transform: `translate(${overlay.text.overflow.x} ${overlay.text.overflow.y})`, children: [
|
|
644
|
+
/* @__PURE__ */ jsx2("rect", { x: -5, y: -5, width: 10, height: 10, fill: THEME2.handleFill, stroke: "#dc2626" }),
|
|
645
|
+
/* @__PURE__ */ jsx2("path", { d: "M-3 0H3M0 -3V3", stroke: "#dc2626" })
|
|
646
|
+
] })
|
|
647
|
+
] }),
|
|
648
|
+
overlay.trackingPoints.map((t, i) => /* @__PURE__ */ jsx2("path", { "data-vector-tracking-point": "", d: `M${t.x - 5} ${t.y}H${t.x + 5}M${t.x} ${t.y - 5}V${t.y + 5}`, stroke: guideColor, strokeWidth: 1.5 }, `track:${i}`)),
|
|
649
|
+
overlay.typedEntry && /* @__PURE__ */ jsxs2("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
|
|
650
|
+
/* @__PURE__ */ jsx2("rect", { x: -3, y: -12, width: overlay.typedEntry.text.length * 7 + 8, height: 17, rx: 3, fill: THEME2.handleFill, stroke: accentColor }),
|
|
651
|
+
/* @__PURE__ */ jsx2("text", { x: 1, y: 1, fontSize: 11, fill: accentColor, style: { fontFamily: "ui-monospace, monospace" }, children: overlay.typedEntry.text })
|
|
652
|
+
] }),
|
|
653
|
+
overlay.connectorSites.map((s) => /* @__PURE__ */ jsx2(
|
|
654
|
+
"circle",
|
|
655
|
+
{
|
|
656
|
+
"data-vector-connection-site": s.side,
|
|
657
|
+
"data-active": s.active || void 0,
|
|
658
|
+
cx: s.x,
|
|
659
|
+
cy: s.y,
|
|
660
|
+
r: s.active ? 5 : 3.5,
|
|
661
|
+
fill: s.active ? accentColor : THEME2.handleFill,
|
|
662
|
+
stroke: accentColor
|
|
663
|
+
},
|
|
664
|
+
`site:${s.nodeId}:${s.side}`
|
|
665
|
+
)),
|
|
666
|
+
overlay.constraints.map((c) => /* @__PURE__ */ jsxs2("g", { "data-vector-constraint-glyph": c.kind, "data-satisfied": c.satisfied ? "true" : "false", children: [
|
|
667
|
+
c.dimension && /* @__PURE__ */ jsx2("line", { x1: c.dimension.x1, y1: c.dimension.y1, x2: c.dimension.x2, y2: c.dimension.y2, stroke: c.satisfied ? THEME2.guide : "#dc2626", strokeDasharray: "2 2" }),
|
|
668
|
+
/* @__PURE__ */ jsx2("text", { x: c.x, y: c.y, fontSize: 10, textAnchor: "middle", fill: c.satisfied ? accentColor : "#dc2626", stroke: "#ffffff", strokeWidth: 3, paintOrder: "stroke", style: { fontFamily: "system-ui, sans-serif" }, children: c.label })
|
|
669
|
+
] }, `constraint:${c.id}`)),
|
|
670
|
+
overlay.textBox && /* @__PURE__ */ jsx2("rect", { "data-vector-text-box": "", x: overlay.textBox.x, y: overlay.textBox.y, width: overlay.textBox.width, height: overlay.textBox.height, stroke: accentColor, strokeDasharray: "3 2" }),
|
|
574
671
|
overlay.marquee && /* @__PURE__ */ jsx2(
|
|
575
672
|
"rect",
|
|
576
673
|
{
|
|
@@ -603,6 +700,78 @@ function VectorCanvas({
|
|
|
603
700
|
)),
|
|
604
701
|
overlay.status && /* @__PURE__ */ jsx2("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
|
|
605
702
|
] }),
|
|
703
|
+
state.textEdit && overlay.text && // A hidden textarea at the caret takes typing and composed (IME) text.
|
|
704
|
+
/* @__PURE__ */ jsx2("foreignObject", { x: overlay.text.caret?.x1 ?? 0, y: overlay.text.caret?.y1 ?? 0, width: 1, height: 1, style: { overflow: "visible" }, children: /* @__PURE__ */ jsx2(
|
|
705
|
+
"textarea",
|
|
706
|
+
{
|
|
707
|
+
ref: textRef,
|
|
708
|
+
"data-testid": "vector-type-input",
|
|
709
|
+
"aria-label": "Text",
|
|
710
|
+
autoCapitalize: "off",
|
|
711
|
+
autoCorrect: "off",
|
|
712
|
+
spellCheck: false,
|
|
713
|
+
style: { position: "absolute", width: 1, height: 1, padding: 0, border: 0, opacity: 0, resize: "none", overflow: "hidden" },
|
|
714
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
715
|
+
onInput: (e) => {
|
|
716
|
+
const el = e.currentTarget;
|
|
717
|
+
if (e.nativeEvent.isComposing) return;
|
|
718
|
+
if (el.value) controller.textInput(el.value);
|
|
719
|
+
el.value = "";
|
|
720
|
+
},
|
|
721
|
+
onCompositionEnd: (e) => {
|
|
722
|
+
const el = e.currentTarget;
|
|
723
|
+
if (el.value) controller.textInput(el.value);
|
|
724
|
+
el.value = "";
|
|
725
|
+
},
|
|
726
|
+
onKeyDown: (e) => {
|
|
727
|
+
e.stopPropagation();
|
|
728
|
+
if (e.nativeEvent.isComposing) return;
|
|
729
|
+
if (e.key.length === 1 && !e.metaKey && !e.ctrlKey) return;
|
|
730
|
+
if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();
|
|
731
|
+
if (!controller.getState().textEdit) svgRef.current?.focus({ preventScroll: true });
|
|
732
|
+
},
|
|
733
|
+
onKeyUp: (e) => e.stopPropagation(),
|
|
734
|
+
onCopy: (e) => {
|
|
735
|
+
const text = controller.getSelectedText();
|
|
736
|
+
if (text) {
|
|
737
|
+
e.clipboardData.setData("text/plain", text);
|
|
738
|
+
e.preventDefault();
|
|
739
|
+
}
|
|
740
|
+
e.stopPropagation();
|
|
741
|
+
},
|
|
742
|
+
onCut: (e) => {
|
|
743
|
+
const text = controller.cutText();
|
|
744
|
+
if (text) {
|
|
745
|
+
e.clipboardData.setData("text/plain", text);
|
|
746
|
+
e.preventDefault();
|
|
747
|
+
}
|
|
748
|
+
e.stopPropagation();
|
|
749
|
+
},
|
|
750
|
+
onPaste: (e) => {
|
|
751
|
+
const text = e.clipboardData.getData("text/plain");
|
|
752
|
+
if (text) controller.textInput(text);
|
|
753
|
+
e.preventDefault();
|
|
754
|
+
e.stopPropagation();
|
|
755
|
+
}
|
|
756
|
+
}
|
|
757
|
+
) }),
|
|
758
|
+
state.symbolEdit && // Isolation mode: which symbol, and the way out (Escape with nothing selected works too).
|
|
759
|
+
/* @__PURE__ */ jsx2("foreignObject", { "data-vector-symbol-edit": state.symbolEdit.symbolId, x: 8, y: 8, width: 320, height: 32, style: { overflow: "visible" }, children: /* @__PURE__ */ jsxs2(
|
|
760
|
+
"div",
|
|
761
|
+
{
|
|
762
|
+
style: { display: "inline-flex", alignItems: "center", gap: 8, padding: "4px 8px", borderRadius: 6, background: "rgba(15,23,42,0.85)", color: "#fff", font: "12px system-ui, sans-serif" },
|
|
763
|
+
onPointerDown: (e) => e.stopPropagation(),
|
|
764
|
+
onDoubleClick: (e) => e.stopPropagation(),
|
|
765
|
+
onKeyDown: (e) => e.stopPropagation(),
|
|
766
|
+
children: [
|
|
767
|
+
/* @__PURE__ */ jsxs2("span", { children: [
|
|
768
|
+
"Symbol: ",
|
|
769
|
+
state.symbolEdit.name
|
|
770
|
+
] }),
|
|
771
|
+
/* @__PURE__ */ jsx2("button", { type: "button", "aria-label": "Exit symbol", style: { border: "1px solid rgba(255,255,255,0.4)", borderRadius: 4, padding: "0 6px", background: "transparent", color: "#fff", cursor: "pointer" }, onClick: () => controller.exitSymbolEdit(), children: "Done" })
|
|
772
|
+
]
|
|
773
|
+
}
|
|
774
|
+
) }),
|
|
606
775
|
edit && editAt && // Width Point Edit (double-click with the width tool), beside the point.
|
|
607
776
|
/* @__PURE__ */ jsx2("foreignObject", { "data-vector-width-point-edit": "", x: editAt.x + 12, y: editAt.y + 12, width: 180, height: 140, style: { overflow: "visible" }, children: /* @__PURE__ */ jsx2(VectorWidthPointEditor, { controller, edit, accentColor }, `${edit.nodeId}:${edit.index}:${edit.t}`) })
|
|
608
777
|
]
|
|
@@ -611,7 +780,7 @@ function VectorCanvas({
|
|
|
611
780
|
}
|
|
612
781
|
|
|
613
782
|
// src/vector/VectorToolbar.tsx
|
|
614
|
-
import
|
|
783
|
+
import React7 from "react";
|
|
615
784
|
import {
|
|
616
785
|
VECTOR_TOOLS,
|
|
617
786
|
VECTOR_TOOL_GLYPHS,
|
|
@@ -634,8 +803,10 @@ import {
|
|
|
634
803
|
isVectorMaskGroup,
|
|
635
804
|
widthProfilePreset,
|
|
636
805
|
widthProfilePresetId,
|
|
637
|
-
vectorPaintToHex as
|
|
638
|
-
vectorToolLabel
|
|
806
|
+
vectorPaintToHex as vectorPaintToHex4,
|
|
807
|
+
vectorToolLabel,
|
|
808
|
+
vectorPolarAngles,
|
|
809
|
+
vectorPolarIncrementOf
|
|
639
810
|
} from "@pixerate/editor/vector";
|
|
640
811
|
|
|
641
812
|
// src/vector/VectorEffectsPanel.tsx
|
|
@@ -1223,6 +1394,537 @@ function VectorMaskPanel({ controller, className }) {
|
|
|
1223
1394
|
] });
|
|
1224
1395
|
}
|
|
1225
1396
|
|
|
1397
|
+
// src/vector/VectorTextPanel.tsx
|
|
1398
|
+
import { useId as useId2 } from "react";
|
|
1399
|
+
import {
|
|
1400
|
+
VECTOR_FONT_WEIGHTS,
|
|
1401
|
+
VECTOR_TEXT_ALIGN_OPTIONS,
|
|
1402
|
+
applyVectorParagraphStyle,
|
|
1403
|
+
applyVectorTextStyle,
|
|
1404
|
+
getVectorTextPanel
|
|
1405
|
+
} from "@pixerate/editor/vector";
|
|
1406
|
+
import { jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1407
|
+
var buttonClass5 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
1408
|
+
var inputClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1409
|
+
var labelClass4 = "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
|
|
1410
|
+
var num = (v) => {
|
|
1411
|
+
const n = parseFloat(v);
|
|
1412
|
+
return Number.isFinite(n) ? n : null;
|
|
1413
|
+
};
|
|
1414
|
+
function VectorTextPanel({ controller, className }) {
|
|
1415
|
+
const state = useVectorEditorState(controller);
|
|
1416
|
+
const listId = `vector-fonts-${useId2().replace(/:/g, "")}`;
|
|
1417
|
+
const panel = getVectorTextPanel(state);
|
|
1418
|
+
if (!panel) return null;
|
|
1419
|
+
const f = panel.font;
|
|
1420
|
+
const style = (patch, merge) => applyVectorTextStyle(controller, patch, merge ? { merge } : {});
|
|
1421
|
+
const where = panel.target === "range" ? "Selected characters" : panel.target === "nodes" ? "Selected text" : "New text";
|
|
1422
|
+
return /* @__PURE__ */ jsxs7("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-text-panel": panel.target, "aria-label": "Text", children: [
|
|
1423
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1424
|
+
/* @__PURE__ */ jsx7("span", { className: labelClass4, children: "Text" }),
|
|
1425
|
+
/* @__PURE__ */ jsx7("span", { className: "text-[11px] text-muted-foreground", children: where })
|
|
1426
|
+
] }),
|
|
1427
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1428
|
+
/* @__PURE__ */ jsx7(
|
|
1429
|
+
"input",
|
|
1430
|
+
{
|
|
1431
|
+
"aria-label": "Font family",
|
|
1432
|
+
className: inputClass,
|
|
1433
|
+
list: listId,
|
|
1434
|
+
placeholder: "Mixed",
|
|
1435
|
+
defaultValue: f.fontFamily ?? "",
|
|
1436
|
+
onKeyDown: (e) => {
|
|
1437
|
+
if (e.key === "Enter") e.target.blur();
|
|
1438
|
+
},
|
|
1439
|
+
onBlur: (e) => {
|
|
1440
|
+
const v = e.target.value.trim();
|
|
1441
|
+
if (v && v !== f.fontFamily) style({ fontFamily: v });
|
|
1442
|
+
}
|
|
1443
|
+
},
|
|
1444
|
+
`family:${f.fontFamily}`
|
|
1445
|
+
),
|
|
1446
|
+
/* @__PURE__ */ jsx7("datalist", { id: listId, children: panel.families.map((fam) => /* @__PURE__ */ jsx7("option", { value: fam }, fam)) })
|
|
1447
|
+
] }),
|
|
1448
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1449
|
+
/* @__PURE__ */ jsxs7("select", { "aria-label": "Font weight", className: inputClass, value: f.fontWeight ?? "", onChange: (e) => style({ fontWeight: Number(e.target.value) }), children: [
|
|
1450
|
+
f.fontWeight === null && /* @__PURE__ */ jsx7("option", { value: "", children: "Mixed" }),
|
|
1451
|
+
VECTOR_FONT_WEIGHTS.map((w) => /* @__PURE__ */ jsx7("option", { value: w.value, children: w.label }, w.value)),
|
|
1452
|
+
f.fontWeight !== null && !VECTOR_FONT_WEIGHTS.some((w) => w.value === f.fontWeight) && /* @__PURE__ */ jsx7("option", { value: f.fontWeight, children: f.fontWeight })
|
|
1453
|
+
] }),
|
|
1454
|
+
/* @__PURE__ */ jsx7(
|
|
1455
|
+
"input",
|
|
1456
|
+
{
|
|
1457
|
+
type: "number",
|
|
1458
|
+
"aria-label": "Font size",
|
|
1459
|
+
title: "Font size",
|
|
1460
|
+
className: `${inputClass} w-14 flex-none`,
|
|
1461
|
+
min: 0.5,
|
|
1462
|
+
step: 1,
|
|
1463
|
+
placeholder: "\u2014",
|
|
1464
|
+
value: f.fontSize === null ? "" : Math.round(f.fontSize * 100) / 100,
|
|
1465
|
+
onChange: (e) => {
|
|
1466
|
+
const v = num(e.target.value);
|
|
1467
|
+
if (v !== null && v >= 0.5) style({ fontSize: v }, "text-size");
|
|
1468
|
+
},
|
|
1469
|
+
onBlur: () => controller.endMergedEdit()
|
|
1470
|
+
}
|
|
1471
|
+
)
|
|
1472
|
+
] }),
|
|
1473
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1474
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, "aria-label": "Bold", "aria-pressed": (f.fontWeight ?? 0) >= 600, onClick: () => style({ fontWeight: (f.fontWeight ?? 400) >= 600 ? 400 : 700 }), children: /* @__PURE__ */ jsx7("b", { children: "B" }) }),
|
|
1475
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, "aria-label": "Italic", "aria-pressed": f.italic === true, onClick: () => style({ italic: !f.italic }), children: /* @__PURE__ */ jsx7("i", { children: "I" }) }),
|
|
1476
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, "aria-label": "Underline", "aria-pressed": f.underline === true, onClick: () => style({ underline: !f.underline }), children: /* @__PURE__ */ jsx7("u", { children: "U" }) }),
|
|
1477
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, "aria-label": "Strike through", "aria-pressed": f.strikethrough === true, onClick: () => style({ strikethrough: !f.strikethrough }), children: /* @__PURE__ */ jsx7("s", { children: "S" }) })
|
|
1478
|
+
] }),
|
|
1479
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1", children: [
|
|
1480
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex flex-1 items-center gap-1 text-xs text-foreground", title: "Tracking (1/1000 em)", children: [
|
|
1481
|
+
/* @__PURE__ */ jsx7("span", { className: "text-[11px] text-muted-foreground", children: "VA" }),
|
|
1482
|
+
/* @__PURE__ */ jsx7(
|
|
1483
|
+
"input",
|
|
1484
|
+
{
|
|
1485
|
+
type: "number",
|
|
1486
|
+
"aria-label": "Tracking",
|
|
1487
|
+
className: inputClass,
|
|
1488
|
+
step: 10,
|
|
1489
|
+
placeholder: "\u2014",
|
|
1490
|
+
value: f.tracking ?? "",
|
|
1491
|
+
onChange: (e) => style({ tracking: num(e.target.value) ?? 0 }, "text-tracking"),
|
|
1492
|
+
onBlur: () => controller.endMergedEdit()
|
|
1493
|
+
}
|
|
1494
|
+
)
|
|
1495
|
+
] }),
|
|
1496
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex flex-1 items-center gap-1 text-xs text-foreground", title: "Leading (empty: auto)", children: [
|
|
1497
|
+
/* @__PURE__ */ jsx7("span", { className: "text-[11px] text-muted-foreground", children: "\u2195" }),
|
|
1498
|
+
/* @__PURE__ */ jsx7(
|
|
1499
|
+
"input",
|
|
1500
|
+
{
|
|
1501
|
+
type: "number",
|
|
1502
|
+
"aria-label": "Leading",
|
|
1503
|
+
className: inputClass,
|
|
1504
|
+
min: 0,
|
|
1505
|
+
step: 1,
|
|
1506
|
+
placeholder: "Auto",
|
|
1507
|
+
value: f.leading ?? "",
|
|
1508
|
+
onChange: (e) => style({ leading: num(e.target.value) }, "text-leading"),
|
|
1509
|
+
onBlur: () => controller.endMergedEdit()
|
|
1510
|
+
}
|
|
1511
|
+
)
|
|
1512
|
+
] })
|
|
1513
|
+
] }),
|
|
1514
|
+
/* @__PURE__ */ jsx7("div", { className: "flex flex-wrap items-center gap-1", role: "group", "aria-label": "Alignment", children: VECTOR_TEXT_ALIGN_OPTIONS.map((o) => /* @__PURE__ */ jsx7(
|
|
1515
|
+
"button",
|
|
1516
|
+
{
|
|
1517
|
+
type: "button",
|
|
1518
|
+
className: buttonClass5,
|
|
1519
|
+
"aria-label": `Align ${o.label}`,
|
|
1520
|
+
"aria-pressed": panel.align === o.value,
|
|
1521
|
+
onClick: () => applyVectorParagraphStyle(controller, { align: o.value }),
|
|
1522
|
+
children: o.label
|
|
1523
|
+
},
|
|
1524
|
+
o.value
|
|
1525
|
+
)) }),
|
|
1526
|
+
panel.target !== "defaults" && panel.ids.length === 1 && /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1527
|
+
/* @__PURE__ */ jsx7(
|
|
1528
|
+
"button",
|
|
1529
|
+
{
|
|
1530
|
+
type: "button",
|
|
1531
|
+
className: buttonClass5,
|
|
1532
|
+
"aria-pressed": panel.frame === "point",
|
|
1533
|
+
title: "Point text: no wrapping",
|
|
1534
|
+
onClick: () => controller.setTextFrame(panel.ids[0], { kind: "point" }),
|
|
1535
|
+
children: "Point"
|
|
1536
|
+
}
|
|
1537
|
+
),
|
|
1538
|
+
/* @__PURE__ */ jsx7(
|
|
1539
|
+
"button",
|
|
1540
|
+
{
|
|
1541
|
+
type: "button",
|
|
1542
|
+
className: buttonClass5,
|
|
1543
|
+
"aria-pressed": panel.frame === "area",
|
|
1544
|
+
title: "Area text: wraps in a box",
|
|
1545
|
+
onClick: () => controller.setTextFrame(panel.ids[0], { kind: "area" }),
|
|
1546
|
+
children: "Area"
|
|
1547
|
+
}
|
|
1548
|
+
)
|
|
1549
|
+
] }),
|
|
1550
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1551
|
+
/* @__PURE__ */ jsx7(
|
|
1552
|
+
"button",
|
|
1553
|
+
{
|
|
1554
|
+
type: "button",
|
|
1555
|
+
className: buttonClass5,
|
|
1556
|
+
"data-vector-text-action": "type-on-path",
|
|
1557
|
+
disabled: !panel.pathId,
|
|
1558
|
+
title: panel.pathId ? "Type on a Path: set text along the selected path" : "Select a path to set text along it",
|
|
1559
|
+
onClick: () => {
|
|
1560
|
+
const r = controller.typeOnPath(panel.pathId, { text: "Text on a path" });
|
|
1561
|
+
if (r.ok && r.id) controller.editText(r.id, { anchor: 0, focus: "Text on a path".length });
|
|
1562
|
+
},
|
|
1563
|
+
children: "Type on path"
|
|
1564
|
+
}
|
|
1565
|
+
),
|
|
1566
|
+
/* @__PURE__ */ jsx7(
|
|
1567
|
+
"button",
|
|
1568
|
+
{
|
|
1569
|
+
type: "button",
|
|
1570
|
+
className: buttonClass5,
|
|
1571
|
+
"data-vector-text-action": "create-outlines",
|
|
1572
|
+
disabled: panel.target !== "nodes" || !panel.outlines,
|
|
1573
|
+
title: panel.outlines ? "Create Outlines: turn the text into paths" : "Create Outlines needs a font with outlines (load one)",
|
|
1574
|
+
onClick: () => controller.createOutlines({ ids: panel.ids }),
|
|
1575
|
+
children: "Create outlines"
|
|
1576
|
+
}
|
|
1577
|
+
)
|
|
1578
|
+
] })
|
|
1579
|
+
] });
|
|
1580
|
+
}
|
|
1581
|
+
|
|
1582
|
+
// src/vector/VectorSymbolsPanel.tsx
|
|
1583
|
+
import { useId as useId3, useState as useState2 } from "react";
|
|
1584
|
+
import { getVectorSymbolsPanel, vectorPaintToHex } from "@pixerate/editor/vector";
|
|
1585
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1586
|
+
var buttonClass6 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
1587
|
+
var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1588
|
+
var labelClass5 = "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
|
|
1589
|
+
function VectorSymbolsPanel({ controller, className }) {
|
|
1590
|
+
const state = useVectorEditorState(controller);
|
|
1591
|
+
const uid = useId3().replace(/:/g, "");
|
|
1592
|
+
const panel = getVectorSymbolsPanel(state, { idPrefix: `vsym${uid}-` });
|
|
1593
|
+
const [error, setError] = useState2(null);
|
|
1594
|
+
const report = (r) => setError(r.ok ? null : r.message ?? null);
|
|
1595
|
+
const inst = panel.instance;
|
|
1596
|
+
return /* @__PURE__ */ jsxs8("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-symbols-panel": "", "aria-label": "Symbols", children: [
|
|
1597
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1", children: [
|
|
1598
|
+
/* @__PURE__ */ jsx8("span", { className: labelClass5, children: "Symbols" }),
|
|
1599
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, "data-vector-symbol-action": "make", disabled: !panel.canMake, title: "New Symbol from the selection (F8)", onClick: () => report(controller.makeSymbol()), children: "New" }),
|
|
1600
|
+
panel.editing && /* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, "data-vector-symbol-action": "exit", title: "Leave the symbol (Escape)", onClick: () => controller.exitSymbolEdit(), children: "Done" })
|
|
1601
|
+
] }),
|
|
1602
|
+
panel.symbols.length > 0 && /* @__PURE__ */ jsx8("ul", { className: "space-y-1", children: panel.symbols.map((s) => /* @__PURE__ */ jsxs8("li", { "data-vector-symbol": s.id, "data-editing": s.editing || void 0, className: "flex items-center gap-1", children: [
|
|
1603
|
+
/* @__PURE__ */ jsxs8("svg", { viewBox: s.viewBox, width: 28, height: 28, className: "shrink-0 rounded border border-border bg-background", "aria-hidden": "true", children: [
|
|
1604
|
+
/* @__PURE__ */ jsx8(VectorGradientDefs, { defs: s.scene.defs }),
|
|
1605
|
+
/* @__PURE__ */ jsx8(VectorFilterDefs, { filters: s.scene.filters }),
|
|
1606
|
+
/* @__PURE__ */ jsx8(VectorSceneNodes, { nodes: s.scene.nodes })
|
|
1607
|
+
] }),
|
|
1608
|
+
/* @__PURE__ */ jsxs8("span", { className: "min-w-0 flex-1 truncate text-xs text-foreground", title: `${s.name} \xB7 ${s.instances} instance${s.instances === 1 ? "" : "s"}`, children: [
|
|
1609
|
+
s.name,
|
|
1610
|
+
" ",
|
|
1611
|
+
/* @__PURE__ */ jsxs8("span", { className: "text-muted-foreground", children: [
|
|
1612
|
+
"\xB7 ",
|
|
1613
|
+
s.instances
|
|
1614
|
+
] })
|
|
1615
|
+
] }),
|
|
1616
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, "aria-label": `Place ${s.name}`, title: "Place an instance", onClick: () => report(controller.placeSymbol(s.id)), children: "+" }),
|
|
1617
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, "aria-label": `Edit ${s.name}`, "aria-pressed": s.editing, title: "Edit Symbol (isolation mode)", onClick: () => report(controller.editSymbol(s.id)), children: "\u270E" }),
|
|
1618
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, "aria-label": `Delete ${s.name}`, title: "Delete the symbol (its instances become groups)", onClick: () => report(controller.deleteSymbol(s.id, { detach: true })), children: "\xD7" })
|
|
1619
|
+
] }, s.id)) }),
|
|
1620
|
+
inst && /* @__PURE__ */ jsxs8("div", { className: "space-y-1", "data-vector-instance": inst.id, children: [
|
|
1621
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-1", children: [
|
|
1622
|
+
/* @__PURE__ */ jsx8("select", { "aria-label": "Instance symbol", className: selectClass5, value: inst.symbolId, onChange: (e) => report(controller.replaceSymbol(e.target.value, { ids: [inst.id] })), children: panel.symbols.map((s) => /* @__PURE__ */ jsx8("option", { value: s.id, children: s.name }, s.id)) }),
|
|
1623
|
+
/* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, title: "Break Link to Symbol: a plain group of its art", onClick: () => report(controller.detachInstances({ ids: [inst.id] })), children: "Detach" })
|
|
1624
|
+
] }),
|
|
1625
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", children: [
|
|
1626
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1627
|
+
/* @__PURE__ */ jsx8("input", { type: "checkbox", "aria-label": "Scale strokes", checked: inst.scaleStrokes, onChange: (e) => report(controller.setInstanceOptions({ scaleStrokes: e.target.checked }, { ids: [inst.id] })) }),
|
|
1628
|
+
"Scale strokes"
|
|
1629
|
+
] }),
|
|
1630
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Paint every fill of the art in this instance", children: [
|
|
1631
|
+
"Tint",
|
|
1632
|
+
/* @__PURE__ */ jsx8(
|
|
1633
|
+
"input",
|
|
1634
|
+
{
|
|
1635
|
+
type: "color",
|
|
1636
|
+
"aria-label": "Instance fill",
|
|
1637
|
+
className: "h-6 w-8 rounded border border-border",
|
|
1638
|
+
value: vectorPaintToHex(inst.fill ?? void 0) ?? "#000000",
|
|
1639
|
+
onChange: (e) => report(controller.setInstanceOptions({ fill: e.target.value }, { ids: [inst.id] }))
|
|
1640
|
+
}
|
|
1641
|
+
)
|
|
1642
|
+
] }),
|
|
1643
|
+
inst.fill !== null && /* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, "aria-label": "Clear tint", onClick: () => report(controller.setInstanceOptions({ fill: null }, { ids: [inst.id] })), children: "\xD7" })
|
|
1644
|
+
] })
|
|
1645
|
+
] }),
|
|
1646
|
+
error && /* @__PURE__ */ jsx8("p", { className: "text-[11px] text-destructive", children: error })
|
|
1647
|
+
] });
|
|
1648
|
+
}
|
|
1649
|
+
|
|
1650
|
+
// src/vector/VectorConstraintsPanel.tsx
|
|
1651
|
+
import { useState as useState3 } from "react";
|
|
1652
|
+
import { getVectorConstraintPanel } from "@pixerate/editor/vector";
|
|
1653
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1654
|
+
var buttonClass7 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
|
|
1655
|
+
var inputClass2 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1656
|
+
var labelClass6 = "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
|
|
1657
|
+
function VectorConstraintsPanel({ controller, className }) {
|
|
1658
|
+
const state = useVectorEditorState(controller);
|
|
1659
|
+
const panel = getVectorConstraintPanel(state);
|
|
1660
|
+
const [error, setError] = useState3(null);
|
|
1661
|
+
const [param, setParam] = useState3({ name: "", value: "" });
|
|
1662
|
+
const report = (r) => setError(r.ok ? null : r.message ?? null);
|
|
1663
|
+
const add = (a) => report(controller.addConstraint({ kind: a.kind, refs: a.refs }));
|
|
1664
|
+
if (!panel.actions.length && !panel.constraints.length && !state.document.constraints?.length && state.tool !== "direct") return null;
|
|
1665
|
+
return /* @__PURE__ */ jsxs9("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-constraints-panel": "", "data-dof-status": panel.dof.status, "aria-label": "Constraints", children: [
|
|
1666
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1", children: [
|
|
1667
|
+
/* @__PURE__ */ jsx9("span", { className: labelClass6, children: "Constraints" }),
|
|
1668
|
+
/* @__PURE__ */ jsx9("span", { className: "flex-1 truncate text-[11px] text-muted-foreground", "data-vector-dof": "", children: panel.dof.message }),
|
|
1669
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: buttonClass7, title: "Auto Constrain: add what the selection (or the drawing) nearly satisfies", onClick: () => report(controller.autoConstrain()), children: "Auto" })
|
|
1670
|
+
] }),
|
|
1671
|
+
panel.actions.length > 0 && /* @__PURE__ */ jsx9("div", { className: "flex flex-wrap gap-1", role: "group", "aria-label": "Add constraint", children: panel.actions.map((a) => /* @__PURE__ */ jsx9("button", { type: "button", className: buttonClass7, "data-vector-constraint-action": a.kind, title: `Add ${a.label}`, onClick: () => add(a), children: a.label }, a.kind)) }),
|
|
1672
|
+
panel.constraints.length > 0 && /* @__PURE__ */ jsx9("ul", { className: "space-y-1", children: panel.constraints.map((c) => /* @__PURE__ */ jsxs9("li", { "data-vector-constraint": c.id, "data-satisfied": c.satisfied && !c.conflict ? "true" : "false", className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1673
|
+
/* @__PURE__ */ jsxs9("span", { className: `min-w-0 flex-1 truncate ${c.conflict || !c.satisfied ? "text-destructive" : ""}`, children: [
|
|
1674
|
+
c.label,
|
|
1675
|
+
c.name ? ` ${c.name}` : ""
|
|
1676
|
+
] }),
|
|
1677
|
+
c.value !== void 0 && /* @__PURE__ */ jsx9(
|
|
1678
|
+
"input",
|
|
1679
|
+
{
|
|
1680
|
+
"aria-label": `Value of ${c.name ?? c.id}`,
|
|
1681
|
+
className: `${inputClass2} w-16 flex-none`,
|
|
1682
|
+
defaultValue: c.value,
|
|
1683
|
+
onKeyDown: (e) => {
|
|
1684
|
+
if (e.key === "Enter") e.target.blur();
|
|
1685
|
+
},
|
|
1686
|
+
onBlur: (e) => {
|
|
1687
|
+
if (e.target.value.trim() !== c.value) report(controller.setConstraintValue(c.id, e.target.value));
|
|
1688
|
+
}
|
|
1689
|
+
},
|
|
1690
|
+
`${c.id}:${c.value}`
|
|
1691
|
+
),
|
|
1692
|
+
/* @__PURE__ */ jsx9("button", { type: "button", className: buttonClass7, "aria-label": `Remove ${c.label} constraint`, onClick: () => report(controller.removeConstraints([c.id])), children: "\xD7" })
|
|
1693
|
+
] }, c.id)) }),
|
|
1694
|
+
/* @__PURE__ */ jsxs9("div", { className: "space-y-1", "aria-label": "Parameters", children: [
|
|
1695
|
+
panel.parameters.map((p) => /* @__PURE__ */ jsxs9("div", { className: "flex items-center gap-1 text-xs", children: [
|
|
1696
|
+
/* @__PURE__ */ jsx9("span", { className: "w-12 truncate font-mono", children: p.name }),
|
|
1697
|
+
/* @__PURE__ */ jsx9(
|
|
1698
|
+
"input",
|
|
1699
|
+
{
|
|
1700
|
+
"aria-label": `Parameter ${p.name}`,
|
|
1701
|
+
className: inputClass2,
|
|
1702
|
+
defaultValue: p.value,
|
|
1703
|
+
onKeyDown: (e) => {
|
|
1704
|
+
if (e.key === "Enter") e.target.blur();
|
|
1705
|
+
},
|
|
1706
|
+
onBlur: (e) => {
|
|
1707
|
+
if (e.target.value.trim() !== p.value) report(controller.setConstraintValue(p.name, e.target.value));
|
|
1708
|
+
}
|
|
1709
|
+
},
|
|
1710
|
+
`${p.name}:${p.value}`
|
|
1711
|
+
),
|
|
1712
|
+
/* @__PURE__ */ jsx9("span", { className: "w-10 text-right text-muted-foreground", children: p.result === null ? "!" : Math.round(p.result * 100) / 100 })
|
|
1713
|
+
] }, p.name)),
|
|
1714
|
+
/* @__PURE__ */ jsxs9(
|
|
1715
|
+
"form",
|
|
1716
|
+
{
|
|
1717
|
+
className: "flex items-center gap-1",
|
|
1718
|
+
onSubmit: (e) => {
|
|
1719
|
+
e.preventDefault();
|
|
1720
|
+
if (!param.name.trim()) return;
|
|
1721
|
+
const r = controller.setConstraintValue(param.name.trim(), param.value);
|
|
1722
|
+
report(r);
|
|
1723
|
+
if (r.ok) setParam({ name: "", value: "" });
|
|
1724
|
+
},
|
|
1725
|
+
children: [
|
|
1726
|
+
/* @__PURE__ */ jsx9("input", { "aria-label": "New parameter name", placeholder: "name", className: `${inputClass2} w-12 flex-none`, value: param.name, onChange: (e) => setParam({ ...param, name: e.target.value }) }),
|
|
1727
|
+
/* @__PURE__ */ jsx9("input", { "aria-label": "New parameter value", placeholder: "value", className: inputClass2, value: param.value, onChange: (e) => setParam({ ...param, value: e.target.value }) }),
|
|
1728
|
+
/* @__PURE__ */ jsx9("button", { type: "submit", className: buttonClass7, "aria-label": "Add parameter", children: "+" })
|
|
1729
|
+
]
|
|
1730
|
+
}
|
|
1731
|
+
)
|
|
1732
|
+
] }),
|
|
1733
|
+
error && /* @__PURE__ */ jsx9("p", { className: "text-[11px] text-destructive", children: error })
|
|
1734
|
+
] });
|
|
1735
|
+
}
|
|
1736
|
+
|
|
1737
|
+
// src/vector/VectorFramePanel.tsx
|
|
1738
|
+
import {
|
|
1739
|
+
VECTOR_HORIZONTAL_RESIZING,
|
|
1740
|
+
VECTOR_HORIZONTAL_RESIZING_LABELS,
|
|
1741
|
+
VECTOR_VERTICAL_RESIZING,
|
|
1742
|
+
VECTOR_VERTICAL_RESIZING_LABELS,
|
|
1743
|
+
getVectorFramePanel,
|
|
1744
|
+
vectorPaintToHex as vectorPaintToHex2
|
|
1745
|
+
} from "@pixerate/editor/vector";
|
|
1746
|
+
import { jsx as jsx10, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
1747
|
+
var buttonClass8 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
|
|
1748
|
+
var selectClass6 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1749
|
+
var numberClass4 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
1750
|
+
function VectorFramePanel({ controller, className }) {
|
|
1751
|
+
const state = useVectorEditorState(controller);
|
|
1752
|
+
const panel = getVectorFramePanel(state);
|
|
1753
|
+
const f = panel.frame;
|
|
1754
|
+
const ids = f ? [f.id] : void 0;
|
|
1755
|
+
const fillHex = f ? vectorPaintToHex2(f.frame.fill ?? null) : null;
|
|
1756
|
+
const number = (key, label) => /* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1757
|
+
/* @__PURE__ */ jsx10("span", { className: "w-4 text-muted-foreground", children: label }),
|
|
1758
|
+
/* @__PURE__ */ jsx10(
|
|
1759
|
+
"input",
|
|
1760
|
+
{
|
|
1761
|
+
type: "number",
|
|
1762
|
+
step: "1",
|
|
1763
|
+
min: key === "width" || key === "height" ? 1 : void 0,
|
|
1764
|
+
"aria-label": `Frame ${key}`,
|
|
1765
|
+
className: numberClass4,
|
|
1766
|
+
defaultValue: Math.round(f.frame[key] * 100) / 100,
|
|
1767
|
+
onKeyDown: (e) => {
|
|
1768
|
+
if (e.key === "Enter") e.currentTarget.blur();
|
|
1769
|
+
},
|
|
1770
|
+
onBlur: (e) => {
|
|
1771
|
+
const n = parseFloat(e.target.value);
|
|
1772
|
+
if (Number.isFinite(n) && n !== Math.round(f.frame[key] * 100) / 100 && (key === "x" || key === "y" || n > 0)) controller.setFrame({ [key]: n }, { ids });
|
|
1773
|
+
}
|
|
1774
|
+
},
|
|
1775
|
+
`${f.id}:${f.frame[key]}`
|
|
1776
|
+
)
|
|
1777
|
+
] });
|
|
1778
|
+
return /* @__PURE__ */ jsxs10("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-frame-panel": "", "aria-label": "Frame", children: [
|
|
1779
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1780
|
+
/* @__PURE__ */ jsx10("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Frame" }),
|
|
1781
|
+
/* @__PURE__ */ jsx10(
|
|
1782
|
+
"button",
|
|
1783
|
+
{
|
|
1784
|
+
type: "button",
|
|
1785
|
+
"data-vector-frame-action": "frame-selection",
|
|
1786
|
+
className: buttonClass8,
|
|
1787
|
+
title: "Frame Selection: put the selection in a frame fitted to it",
|
|
1788
|
+
"aria-label": "Frame Selection",
|
|
1789
|
+
disabled: !state.selection.length,
|
|
1790
|
+
onClick: () => controller.frameSelection(),
|
|
1791
|
+
children: "Frame selection"
|
|
1792
|
+
}
|
|
1793
|
+
)
|
|
1794
|
+
] }),
|
|
1795
|
+
f && /* @__PURE__ */ jsxs10("div", { className: "space-y-2", "data-vector-frame": f.id, children: [
|
|
1796
|
+
/* @__PURE__ */ jsxs10("div", { className: "grid grid-cols-2 gap-1", children: [
|
|
1797
|
+
number("x", "X"),
|
|
1798
|
+
number("y", "Y"),
|
|
1799
|
+
number("width", "W"),
|
|
1800
|
+
number("height", "H")
|
|
1801
|
+
] }),
|
|
1802
|
+
/* @__PURE__ */ jsxs10("div", { className: "flex items-center gap-2", children: [
|
|
1803
|
+
/* @__PURE__ */ jsxs10("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
1804
|
+
/* @__PURE__ */ jsx10(
|
|
1805
|
+
"input",
|
|
1806
|
+
{
|
|
1807
|
+
type: "checkbox",
|
|
1808
|
+
"aria-label": "Frame fill on",
|
|
1809
|
+
checked: f.frame.fill !== void 0 && f.frame.fill !== null,
|
|
1810
|
+
onChange: (e) => controller.setFrame({ fill: e.target.checked ? fillHex ?? "#ffffff" : null }, { ids })
|
|
1811
|
+
}
|
|
1812
|
+
),
|
|
1813
|
+
"Fill"
|
|
1814
|
+
] }),
|
|
1815
|
+
/* @__PURE__ */ jsx10(
|
|
1816
|
+
"input",
|
|
1817
|
+
{
|
|
1818
|
+
type: "color",
|
|
1819
|
+
"aria-label": "Frame fill colour",
|
|
1820
|
+
className: "h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40",
|
|
1821
|
+
disabled: f.frame.fill === void 0 || f.frame.fill === null,
|
|
1822
|
+
value: fillHex ?? "#ffffff",
|
|
1823
|
+
onChange: (e) => controller.setFrame({ fill: e.target.value }, { ids, merge: "frame-fill" })
|
|
1824
|
+
}
|
|
1825
|
+
),
|
|
1826
|
+
/* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Hide what lies outside the frame", children: [
|
|
1827
|
+
/* @__PURE__ */ jsx10("input", { type: "checkbox", "aria-label": "Clip content", checked: f.frame.clip !== false, onChange: (e) => controller.setFrame({ clip: e.target.checked }, { ids }) }),
|
|
1828
|
+
"Clip"
|
|
1829
|
+
] })
|
|
1830
|
+
] })
|
|
1831
|
+
] }),
|
|
1832
|
+
panel.resizing && /* @__PURE__ */ jsxs10("div", { className: "space-y-1", "data-vector-resizing": "", children: [
|
|
1833
|
+
/* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
1834
|
+
/* @__PURE__ */ jsx10("span", { className: "w-16", children: "Horizontal" }),
|
|
1835
|
+
/* @__PURE__ */ jsxs10(
|
|
1836
|
+
"select",
|
|
1837
|
+
{
|
|
1838
|
+
"aria-label": "Horizontal constraint",
|
|
1839
|
+
className: selectClass6,
|
|
1840
|
+
value: panel.resizing.horizontal,
|
|
1841
|
+
onChange: (e) => controller.setResizing({ horizontal: e.target.value }, { ids: panel.resizing.ids }),
|
|
1842
|
+
children: [
|
|
1843
|
+
panel.resizing.horizontal === "mixed" && /* @__PURE__ */ jsx10("option", { value: "mixed", disabled: true, children: "Mixed" }),
|
|
1844
|
+
VECTOR_HORIZONTAL_RESIZING.map((h) => /* @__PURE__ */ jsx10("option", { value: h, children: VECTOR_HORIZONTAL_RESIZING_LABELS[h] }, h))
|
|
1845
|
+
]
|
|
1846
|
+
}
|
|
1847
|
+
)
|
|
1848
|
+
] }),
|
|
1849
|
+
/* @__PURE__ */ jsxs10("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
1850
|
+
/* @__PURE__ */ jsx10("span", { className: "w-16", children: "Vertical" }),
|
|
1851
|
+
/* @__PURE__ */ jsxs10(
|
|
1852
|
+
"select",
|
|
1853
|
+
{
|
|
1854
|
+
"aria-label": "Vertical constraint",
|
|
1855
|
+
className: selectClass6,
|
|
1856
|
+
value: panel.resizing.vertical,
|
|
1857
|
+
onChange: (e) => controller.setResizing({ vertical: e.target.value }, { ids: panel.resizing.ids }),
|
|
1858
|
+
children: [
|
|
1859
|
+
panel.resizing.vertical === "mixed" && /* @__PURE__ */ jsx10("option", { value: "mixed", disabled: true, children: "Mixed" }),
|
|
1860
|
+
VECTOR_VERTICAL_RESIZING.map((v) => /* @__PURE__ */ jsx10("option", { value: v, children: VECTOR_VERTICAL_RESIZING_LABELS[v] }, v))
|
|
1861
|
+
]
|
|
1862
|
+
}
|
|
1863
|
+
)
|
|
1864
|
+
] })
|
|
1865
|
+
] })
|
|
1866
|
+
] });
|
|
1867
|
+
}
|
|
1868
|
+
|
|
1869
|
+
// src/vector/VectorConnectorPanel.tsx
|
|
1870
|
+
import {
|
|
1871
|
+
VECTOR_CONNECTOR_ROUTES,
|
|
1872
|
+
VECTOR_CONNECTOR_ROUTE_LABELS,
|
|
1873
|
+
VECTOR_CONNECTOR_SIDES,
|
|
1874
|
+
VECTOR_CONNECTOR_SIDE_LABELS,
|
|
1875
|
+
getVectorConnectorPanel
|
|
1876
|
+
} from "@pixerate/editor/vector";
|
|
1877
|
+
import { Fragment as Fragment2, jsx as jsx11, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
1878
|
+
var buttonClass9 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
|
|
1879
|
+
var selectClass7 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1880
|
+
var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
1881
|
+
function VectorConnectorPanel({ controller, className }) {
|
|
1882
|
+
const state = useVectorEditorState(controller);
|
|
1883
|
+
const panel = getVectorConnectorPanel(state);
|
|
1884
|
+
if (!panel) return null;
|
|
1885
|
+
const ids = [panel.id];
|
|
1886
|
+
const end = (key, label, e) => /* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1", "data-vector-connector-end": key, children: [
|
|
1887
|
+
/* @__PURE__ */ jsx11("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
1888
|
+
e ? /* @__PURE__ */ jsxs11(Fragment2, { children: [
|
|
1889
|
+
/* @__PURE__ */ jsx11(
|
|
1890
|
+
"select",
|
|
1891
|
+
{
|
|
1892
|
+
"aria-label": `${label} side`,
|
|
1893
|
+
className: selectClass7,
|
|
1894
|
+
value: e.side ?? "auto",
|
|
1895
|
+
onChange: (ev) => controller.setConnector({ [key]: { nodeId: e.nodeId, side: ev.target.value } }, { ids }),
|
|
1896
|
+
children: VECTOR_CONNECTOR_SIDES.map((s) => /* @__PURE__ */ jsx11("option", { value: s, children: VECTOR_CONNECTOR_SIDE_LABELS[s] }, s))
|
|
1897
|
+
}
|
|
1898
|
+
),
|
|
1899
|
+
/* @__PURE__ */ jsx11("button", { type: "button", className: buttonClass9, title: "Unglue: the end stays where it is", "aria-label": `Unglue ${label.toLowerCase()}`, onClick: () => controller.setConnector({ [key]: null }, { ids }), children: "Unglue" })
|
|
1900
|
+
] }) : /* @__PURE__ */ jsx11("span", { className: "text-[11px] text-muted-foreground", children: "Free" })
|
|
1901
|
+
] });
|
|
1902
|
+
return /* @__PURE__ */ jsxs11("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-connector-panel": panel.id, "aria-label": "Connector", children: [
|
|
1903
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex items-center gap-1", children: [
|
|
1904
|
+
/* @__PURE__ */ jsx11("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Connector" }),
|
|
1905
|
+
/* @__PURE__ */ jsx11("select", { "aria-label": "Connector route", className: selectClass7, value: panel.route, onChange: (e) => controller.setConnector({ route: e.target.value }, { ids }), children: VECTOR_CONNECTOR_ROUTES.map((r) => /* @__PURE__ */ jsx11("option", { value: r, children: VECTOR_CONNECTOR_ROUTE_LABELS[r] }, r)) }),
|
|
1906
|
+
panel.route === "elbow" && /* @__PURE__ */ jsx11(
|
|
1907
|
+
"input",
|
|
1908
|
+
{
|
|
1909
|
+
type: "number",
|
|
1910
|
+
min: "0",
|
|
1911
|
+
step: "1",
|
|
1912
|
+
"aria-label": "Elbow radius",
|
|
1913
|
+
title: "Corner radius of the bends",
|
|
1914
|
+
className: numberClass5,
|
|
1915
|
+
value: panel.radius,
|
|
1916
|
+
onChange: (e) => {
|
|
1917
|
+
const n = parseFloat(e.target.value);
|
|
1918
|
+
if (Number.isFinite(n) && n >= 0) controller.setConnector({ radius: n }, { ids });
|
|
1919
|
+
}
|
|
1920
|
+
}
|
|
1921
|
+
)
|
|
1922
|
+
] }),
|
|
1923
|
+
end("start", "Start", panel.start),
|
|
1924
|
+
end("end", "End", panel.end)
|
|
1925
|
+
] });
|
|
1926
|
+
}
|
|
1927
|
+
|
|
1226
1928
|
// src/vector/VectorGradientEditor.tsx
|
|
1227
1929
|
import {
|
|
1228
1930
|
VECTOR_GRADIENT_TYPES,
|
|
@@ -1234,12 +1936,12 @@ import {
|
|
|
1234
1936
|
VECTOR_GRADIENT_INTERPOLATION_LABELS,
|
|
1235
1937
|
VECTOR_STROKE_GRADIENT_MODE_LABELS,
|
|
1236
1938
|
getVectorGradientEditor,
|
|
1237
|
-
vectorPaintToHex
|
|
1939
|
+
vectorPaintToHex as vectorPaintToHex3
|
|
1238
1940
|
} from "@pixerate/editor/vector";
|
|
1239
|
-
import { Fragment as
|
|
1240
|
-
var
|
|
1241
|
-
var
|
|
1242
|
-
var
|
|
1941
|
+
import { Fragment as Fragment3, jsx as jsx12, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1942
|
+
var selectClass8 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1943
|
+
var numberClass6 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
1944
|
+
var buttonClass10 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
1243
1945
|
var percent = (v) => Math.round(v * 1e3) / 10;
|
|
1244
1946
|
function VectorGradientEditor({ controller, className }) {
|
|
1245
1947
|
const state = useVectorEditorState(controller);
|
|
@@ -1251,26 +1953,26 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1251
1953
|
const setType = (value) => {
|
|
1252
1954
|
if (value === "solid") {
|
|
1253
1955
|
const first = ed.stops[0];
|
|
1254
|
-
controller.setStyle({ [ed.target]:
|
|
1956
|
+
controller.setStyle({ [ed.target]: vectorPaintToHex3(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
|
|
1255
1957
|
} else controller.setGradient({ type: value });
|
|
1256
1958
|
};
|
|
1257
|
-
return /* @__PURE__ */
|
|
1258
|
-
/* @__PURE__ */
|
|
1259
|
-
/* @__PURE__ */
|
|
1260
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */
|
|
1261
|
-
ed.mixed && /* @__PURE__ */
|
|
1959
|
+
return /* @__PURE__ */ jsxs12("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
|
|
1960
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5", children: [
|
|
1961
|
+
/* @__PURE__ */ jsx12("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
|
|
1962
|
+
["fill", "stroke"].map((t) => /* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
|
|
1963
|
+
ed.mixed && /* @__PURE__ */ jsx12("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
1262
1964
|
] }),
|
|
1263
|
-
/* @__PURE__ */
|
|
1264
|
-
/* @__PURE__ */
|
|
1265
|
-
/* @__PURE__ */
|
|
1266
|
-
/* @__PURE__ */
|
|
1267
|
-
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */
|
|
1965
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex items-center gap-1.5", children: [
|
|
1966
|
+
/* @__PURE__ */ jsx12("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
|
|
1967
|
+
/* @__PURE__ */ jsxs12("select", { "aria-label": "Gradient type", className: selectClass8, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
|
|
1968
|
+
/* @__PURE__ */ jsx12("option", { value: "solid", children: "Solid" }),
|
|
1969
|
+
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx12("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
|
|
1268
1970
|
] }),
|
|
1269
|
-
/* @__PURE__ */
|
|
1971
|
+
/* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
|
|
1270
1972
|
] }),
|
|
1271
|
-
g && /* @__PURE__ */
|
|
1272
|
-
/* @__PURE__ */
|
|
1273
|
-
/* @__PURE__ */
|
|
1973
|
+
g && /* @__PURE__ */ jsxs12(Fragment3, { children: [
|
|
1974
|
+
/* @__PURE__ */ jsxs12("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
|
|
1975
|
+
/* @__PURE__ */ jsx12(
|
|
1274
1976
|
"div",
|
|
1275
1977
|
{
|
|
1276
1978
|
role: "button",
|
|
@@ -1284,7 +1986,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1284
1986
|
}
|
|
1285
1987
|
}
|
|
1286
1988
|
),
|
|
1287
|
-
ed.stops.map((s) => /* @__PURE__ */
|
|
1989
|
+
ed.stops.map((s) => /* @__PURE__ */ jsx12(
|
|
1288
1990
|
"button",
|
|
1289
1991
|
{
|
|
1290
1992
|
type: "button",
|
|
@@ -1298,21 +2000,21 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1298
2000
|
s.index
|
|
1299
2001
|
))
|
|
1300
2002
|
] }),
|
|
1301
|
-
sel && /* @__PURE__ */
|
|
1302
|
-
/* @__PURE__ */
|
|
2003
|
+
sel && /* @__PURE__ */ jsxs12("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
|
|
2004
|
+
/* @__PURE__ */ jsx12(
|
|
1303
2005
|
"input",
|
|
1304
2006
|
{
|
|
1305
2007
|
type: "color",
|
|
1306
2008
|
"aria-label": "Stop colour",
|
|
1307
2009
|
className: "h-7 w-9 cursor-pointer rounded border border-border bg-background",
|
|
1308
|
-
value:
|
|
2010
|
+
value: vectorPaintToHex3(sel.color) ?? "#000000",
|
|
1309
2011
|
onChange: (e) => controller.setGradientStop(sel.index, { color: e.target.value }, { merge: `gradient-stop-color-${sel.index}` }),
|
|
1310
2012
|
onBlur: end
|
|
1311
2013
|
}
|
|
1312
2014
|
),
|
|
1313
|
-
/* @__PURE__ */
|
|
2015
|
+
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
1314
2016
|
"Opacity",
|
|
1315
|
-
/* @__PURE__ */
|
|
2017
|
+
/* @__PURE__ */ jsx12(
|
|
1316
2018
|
"input",
|
|
1317
2019
|
{
|
|
1318
2020
|
type: "number",
|
|
@@ -1320,7 +2022,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1320
2022
|
max: 100,
|
|
1321
2023
|
step: 5,
|
|
1322
2024
|
"aria-label": "Stop opacity",
|
|
1323
|
-
className:
|
|
2025
|
+
className: numberClass6,
|
|
1324
2026
|
value: percent(sel.opacity ?? 1),
|
|
1325
2027
|
onChange: (e) => {
|
|
1326
2028
|
const n = parseFloat(e.target.value);
|
|
@@ -1329,9 +2031,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1329
2031
|
}
|
|
1330
2032
|
)
|
|
1331
2033
|
] }),
|
|
1332
|
-
/* @__PURE__ */
|
|
2034
|
+
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
1333
2035
|
"At",
|
|
1334
|
-
/* @__PURE__ */
|
|
2036
|
+
/* @__PURE__ */ jsx12(
|
|
1335
2037
|
"input",
|
|
1336
2038
|
{
|
|
1337
2039
|
type: "number",
|
|
@@ -1339,7 +2041,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1339
2041
|
max: 100,
|
|
1340
2042
|
step: 1,
|
|
1341
2043
|
"aria-label": "Stop position",
|
|
1342
|
-
className:
|
|
2044
|
+
className: numberClass6,
|
|
1343
2045
|
value: percent(sel.offset),
|
|
1344
2046
|
onChange: (e) => {
|
|
1345
2047
|
const n = parseFloat(e.target.value);
|
|
@@ -1348,9 +2050,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1348
2050
|
}
|
|
1349
2051
|
)
|
|
1350
2052
|
] }),
|
|
1351
|
-
!sel.last && /* @__PURE__ */
|
|
2053
|
+
!sel.last && /* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
1352
2054
|
"Midpoint",
|
|
1353
|
-
/* @__PURE__ */
|
|
2055
|
+
/* @__PURE__ */ jsx12(
|
|
1354
2056
|
"input",
|
|
1355
2057
|
{
|
|
1356
2058
|
type: "number",
|
|
@@ -1358,7 +2060,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1358
2060
|
max: 95,
|
|
1359
2061
|
step: 1,
|
|
1360
2062
|
"aria-label": "Stop midpoint",
|
|
1361
|
-
className:
|
|
2063
|
+
className: numberClass6,
|
|
1362
2064
|
value: percent(sel.midpoint ?? 0.5),
|
|
1363
2065
|
onChange: (e) => {
|
|
1364
2066
|
const n = parseFloat(e.target.value);
|
|
@@ -1367,15 +2069,15 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1367
2069
|
}
|
|
1368
2070
|
)
|
|
1369
2071
|
] }),
|
|
1370
|
-
/* @__PURE__ */
|
|
2072
|
+
/* @__PURE__ */ jsx12("button", { type: "button", className: buttonClass10, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
|
|
1371
2073
|
] }),
|
|
1372
|
-
/* @__PURE__ */
|
|
1373
|
-
/* @__PURE__ */
|
|
1374
|
-
/* @__PURE__ */
|
|
2074
|
+
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2075
|
+
/* @__PURE__ */ jsx12("span", { className: "w-16", children: "Colours" }),
|
|
2076
|
+
/* @__PURE__ */ jsx12("select", { "aria-label": "Gradient interpolation", className: selectClass8, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx12("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
|
|
1375
2077
|
] }),
|
|
1376
|
-
/* @__PURE__ */
|
|
1377
|
-
/* @__PURE__ */
|
|
1378
|
-
/* @__PURE__ */
|
|
2078
|
+
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2079
|
+
/* @__PURE__ */ jsx12("span", { className: "w-16", children: "Smooth" }),
|
|
2080
|
+
/* @__PURE__ */ jsx12(
|
|
1379
2081
|
"input",
|
|
1380
2082
|
{
|
|
1381
2083
|
type: "range",
|
|
@@ -1391,23 +2093,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1391
2093
|
}
|
|
1392
2094
|
)
|
|
1393
2095
|
] }),
|
|
1394
|
-
/* @__PURE__ */
|
|
1395
|
-
/* @__PURE__ */
|
|
1396
|
-
/* @__PURE__ */
|
|
2096
|
+
/* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2097
|
+
/* @__PURE__ */ jsx12("span", { className: "w-16", children: "Spread" }),
|
|
2098
|
+
/* @__PURE__ */ jsx12("select", { "aria-label": "Gradient spread", className: selectClass8, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx12("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
|
|
1397
2099
|
] }),
|
|
1398
|
-
ed.target === "stroke" && /* @__PURE__ */
|
|
1399
|
-
/* @__PURE__ */
|
|
1400
|
-
/* @__PURE__ */
|
|
2100
|
+
ed.target === "stroke" && /* @__PURE__ */ jsxs12("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2101
|
+
/* @__PURE__ */ jsx12("span", { className: "w-16", children: "On stroke" }),
|
|
2102
|
+
/* @__PURE__ */ jsx12("select", { "aria-label": "Gradient on stroke", className: selectClass8, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx12("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
|
|
1401
2103
|
] })
|
|
1402
2104
|
] })
|
|
1403
2105
|
] });
|
|
1404
2106
|
}
|
|
1405
2107
|
|
|
1406
2108
|
// src/vector/VectorToolbar.tsx
|
|
1407
|
-
import { Fragment as
|
|
2109
|
+
import { Fragment as Fragment4, jsx as jsx13, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1408
2110
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
1409
2111
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
1410
|
-
var
|
|
2112
|
+
var buttonClass11 = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
1411
2113
|
function VectorToolbar({
|
|
1412
2114
|
controller,
|
|
1413
2115
|
tools = VECTOR_TOOLS,
|
|
@@ -1425,16 +2127,16 @@ function VectorToolbar({
|
|
|
1425
2127
|
const editing = state.editing;
|
|
1426
2128
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
1427
2129
|
const maskSelected = state.selection.some((id) => isVectorMaskGroup(controller.getNode(id)));
|
|
1428
|
-
const [live, setLive] =
|
|
2130
|
+
const [live, setLive] = React7.useState(false);
|
|
1429
2131
|
const booleanDisabled = live ? count < 1 : count < 2;
|
|
1430
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */
|
|
1431
|
-
return /* @__PURE__ */
|
|
1432
|
-
/* @__PURE__ */
|
|
2132
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx13("button", { type: "button", className: buttonClass11, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
2133
|
+
return /* @__PURE__ */ jsxs13("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
2134
|
+
/* @__PURE__ */ jsx13("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx13(
|
|
1433
2135
|
"button",
|
|
1434
2136
|
{
|
|
1435
2137
|
type: "button",
|
|
1436
2138
|
"data-vector-tool": tool,
|
|
1437
|
-
className:
|
|
2139
|
+
className: buttonClass11,
|
|
1438
2140
|
"aria-pressed": state.tool === tool,
|
|
1439
2141
|
title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
1440
2142
|
"aria-label": vectorToolLabel(tool),
|
|
@@ -1443,30 +2145,50 @@ function VectorToolbar({
|
|
|
1443
2145
|
},
|
|
1444
2146
|
tool
|
|
1445
2147
|
)) }),
|
|
1446
|
-
showHistory && /* @__PURE__ */
|
|
2148
|
+
showHistory && /* @__PURE__ */ jsxs13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
1447
2149
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
1448
2150
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
1449
2151
|
] }),
|
|
1450
|
-
showSnap && /* @__PURE__ */
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
2152
|
+
showSnap && /* @__PURE__ */ jsxs13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
|
|
2153
|
+
/* @__PURE__ */ jsx13(
|
|
2154
|
+
"button",
|
|
2155
|
+
{
|
|
2156
|
+
type: "button",
|
|
2157
|
+
"data-vector-snap": "",
|
|
2158
|
+
className: buttonClass11,
|
|
2159
|
+
"aria-pressed": state.snap.objects,
|
|
2160
|
+
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
2161
|
+
"aria-label": VECTOR_SNAP_TITLE,
|
|
2162
|
+
onClick: () => controller.toggleSnapping(),
|
|
2163
|
+
children: "Snap"
|
|
2164
|
+
}
|
|
2165
|
+
),
|
|
2166
|
+
/* @__PURE__ */ jsxs13(
|
|
2167
|
+
"select",
|
|
2168
|
+
{
|
|
2169
|
+
"data-vector-polar": "",
|
|
2170
|
+
"aria-label": "Polar tracking increment",
|
|
2171
|
+
title: "Polar tracking: the angles drawn points snap to from where they leave",
|
|
2172
|
+
className: "h-8 rounded-md border border-border bg-background px-1 text-xs",
|
|
2173
|
+
value: String(vectorPolarIncrementOf(state.snap.angles) ?? ""),
|
|
2174
|
+
onChange: (e) => controller.setSnap({ angles: vectorPolarAngles(Number(e.target.value)) }),
|
|
2175
|
+
children: [
|
|
2176
|
+
vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */ jsx13("option", { value: "", children: "Custom" }),
|
|
2177
|
+
[90, 45, 30, 15].map((n) => /* @__PURE__ */ jsxs13("option", { value: n, children: [
|
|
2178
|
+
n,
|
|
2179
|
+
"\xB0"
|
|
2180
|
+
] }, n))
|
|
2181
|
+
]
|
|
2182
|
+
}
|
|
2183
|
+
)
|
|
2184
|
+
] }),
|
|
2185
|
+
showPathEdit && /* @__PURE__ */ jsxs13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
2186
|
+
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx13(
|
|
1465
2187
|
"button",
|
|
1466
2188
|
{
|
|
1467
2189
|
type: "button",
|
|
1468
2190
|
"data-vector-path-action": a.id,
|
|
1469
|
-
className:
|
|
2191
|
+
className: buttonClass11,
|
|
1470
2192
|
title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
|
|
1471
2193
|
"aria-label": a.title,
|
|
1472
2194
|
disabled: !a.enabled(count, points),
|
|
@@ -1475,12 +2197,12 @@ function VectorToolbar({
|
|
|
1475
2197
|
},
|
|
1476
2198
|
a.id
|
|
1477
2199
|
)),
|
|
1478
|
-
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */
|
|
2200
|
+
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx13(
|
|
1479
2201
|
"button",
|
|
1480
2202
|
{
|
|
1481
2203
|
type: "button",
|
|
1482
2204
|
"data-vector-edit": t.key,
|
|
1483
|
-
className:
|
|
2205
|
+
className: buttonClass11,
|
|
1484
2206
|
"aria-pressed": editing[t.key],
|
|
1485
2207
|
title: t.title,
|
|
1486
2208
|
"aria-label": t.title,
|
|
@@ -1489,9 +2211,9 @@ function VectorToolbar({
|
|
|
1489
2211
|
},
|
|
1490
2212
|
t.key
|
|
1491
2213
|
)),
|
|
1492
|
-
state.tool === "pencil" && /* @__PURE__ */
|
|
2214
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1493
2215
|
"Smoothing",
|
|
1494
|
-
/* @__PURE__ */
|
|
2216
|
+
/* @__PURE__ */ jsx13(
|
|
1495
2217
|
"input",
|
|
1496
2218
|
{
|
|
1497
2219
|
type: "range",
|
|
@@ -1505,9 +2227,9 @@ function VectorToolbar({
|
|
|
1505
2227
|
}
|
|
1506
2228
|
)
|
|
1507
2229
|
] }),
|
|
1508
|
-
state.tool === "eraser" && /* @__PURE__ */
|
|
2230
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1509
2231
|
"Size",
|
|
1510
|
-
/* @__PURE__ */
|
|
2232
|
+
/* @__PURE__ */ jsx13(
|
|
1511
2233
|
"input",
|
|
1512
2234
|
{
|
|
1513
2235
|
type: "number",
|
|
@@ -1523,12 +2245,12 @@ function VectorToolbar({
|
|
|
1523
2245
|
}
|
|
1524
2246
|
)
|
|
1525
2247
|
] }),
|
|
1526
|
-
state.tool === "paintBucket" && /* @__PURE__ */
|
|
2248
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx13(
|
|
1527
2249
|
"button",
|
|
1528
2250
|
{
|
|
1529
2251
|
type: "button",
|
|
1530
2252
|
"data-vector-fill-gap": "",
|
|
1531
|
-
className:
|
|
2253
|
+
className: buttonClass11,
|
|
1532
2254
|
"aria-pressed": editing.fillGap > 0,
|
|
1533
2255
|
title: "Close small gaps between path ends when filling",
|
|
1534
2256
|
"aria-label": "Close gaps",
|
|
@@ -1537,12 +2259,12 @@ function VectorToolbar({
|
|
|
1537
2259
|
}
|
|
1538
2260
|
)
|
|
1539
2261
|
] }),
|
|
1540
|
-
showDistort && /* @__PURE__ */
|
|
2262
|
+
showDistort && /* @__PURE__ */ jsx13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx13(
|
|
1541
2263
|
"button",
|
|
1542
2264
|
{
|
|
1543
2265
|
type: "button",
|
|
1544
2266
|
"data-vector-free-distort": "",
|
|
1545
|
-
className:
|
|
2267
|
+
className: buttonClass11,
|
|
1546
2268
|
"aria-pressed": state.freeDistort,
|
|
1547
2269
|
title: `Free Distort: drag a corner of the selection box to distort in perspective (Shift+Alt+${modKeyLabel()}: symmetric perspective; ${modKeyLabel()}-drag a side to skew)`,
|
|
1548
2270
|
"aria-label": "Free Distort",
|
|
@@ -1550,15 +2272,15 @@ function VectorToolbar({
|
|
|
1550
2272
|
children: "Distort"
|
|
1551
2273
|
}
|
|
1552
2274
|
) }),
|
|
1553
|
-
showBlend && /* @__PURE__ */
|
|
1554
|
-
showArrange && /* @__PURE__ */
|
|
1555
|
-
/* @__PURE__ */
|
|
1556
|
-
/* @__PURE__ */
|
|
2275
|
+
showBlend && /* @__PURE__ */ jsx13(VectorBlendControls, { controller }),
|
|
2276
|
+
showArrange && /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
2277
|
+
/* @__PURE__ */ jsxs13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
2278
|
+
/* @__PURE__ */ jsx13(
|
|
1557
2279
|
"button",
|
|
1558
2280
|
{
|
|
1559
2281
|
type: "button",
|
|
1560
2282
|
"data-vector-live-boolean": "",
|
|
1561
|
-
className:
|
|
2283
|
+
className: buttonClass11,
|
|
1562
2284
|
"aria-pressed": live,
|
|
1563
2285
|
title: "Live booleans: keep the shapes editable",
|
|
1564
2286
|
"aria-label": "Live booleans",
|
|
@@ -1575,12 +2297,12 @@ function VectorToolbar({
|
|
|
1575
2297
|
act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
|
|
1576
2298
|
act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
|
|
1577
2299
|
] }),
|
|
1578
|
-
/* @__PURE__ */
|
|
2300
|
+
/* @__PURE__ */ jsx13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx13(
|
|
1579
2301
|
"button",
|
|
1580
2302
|
{
|
|
1581
2303
|
type: "button",
|
|
1582
2304
|
"data-vector-pathfinder": a.id,
|
|
1583
|
-
className:
|
|
2305
|
+
className: buttonClass11,
|
|
1584
2306
|
title: a.title,
|
|
1585
2307
|
"aria-label": a.title,
|
|
1586
2308
|
disabled: count < a.min,
|
|
@@ -1589,7 +2311,7 @@ function VectorToolbar({
|
|
|
1589
2311
|
},
|
|
1590
2312
|
a.id
|
|
1591
2313
|
)) }),
|
|
1592
|
-
/* @__PURE__ */
|
|
2314
|
+
/* @__PURE__ */ jsxs13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
1593
2315
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
1594
2316
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
1595
2317
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -1607,19 +2329,19 @@ function VectorBlendControls({ controller }) {
|
|
|
1607
2329
|
const blend = getVectorBlendControls(state);
|
|
1608
2330
|
const o = blend.options;
|
|
1609
2331
|
const selected = blend.ids.length > 0;
|
|
1610
|
-
return /* @__PURE__ */
|
|
1611
|
-
/* @__PURE__ */
|
|
1612
|
-
/* @__PURE__ */
|
|
2332
|
+
return /* @__PURE__ */ jsxs13("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
|
|
2333
|
+
/* @__PURE__ */ jsx13("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass11, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
|
|
2334
|
+
/* @__PURE__ */ jsx13(
|
|
1613
2335
|
"select",
|
|
1614
2336
|
{
|
|
1615
2337
|
"aria-label": "Blend spacing",
|
|
1616
2338
|
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
1617
2339
|
value: o.spacing,
|
|
1618
2340
|
onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
|
|
1619
|
-
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */
|
|
2341
|
+
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx13("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
|
|
1620
2342
|
}
|
|
1621
2343
|
),
|
|
1622
|
-
o.spacing !== "smoothColor" && /* @__PURE__ */
|
|
2344
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ jsx13(
|
|
1623
2345
|
"input",
|
|
1624
2346
|
{
|
|
1625
2347
|
type: "number",
|
|
@@ -1635,12 +2357,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1635
2357
|
}
|
|
1636
2358
|
}
|
|
1637
2359
|
),
|
|
1638
|
-
/* @__PURE__ */
|
|
2360
|
+
/* @__PURE__ */ jsx13(
|
|
1639
2361
|
"button",
|
|
1640
2362
|
{
|
|
1641
2363
|
type: "button",
|
|
1642
2364
|
"data-vector-blend-action": "orient",
|
|
1643
|
-
className:
|
|
2365
|
+
className: buttonClass11,
|
|
1644
2366
|
"aria-pressed": o.orientToSpine,
|
|
1645
2367
|
title: "Orient steps to the spine",
|
|
1646
2368
|
"aria-label": "Orient to spine",
|
|
@@ -1648,12 +2370,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1648
2370
|
children: "\u293F"
|
|
1649
2371
|
}
|
|
1650
2372
|
),
|
|
1651
|
-
/* @__PURE__ */
|
|
2373
|
+
/* @__PURE__ */ jsx13(
|
|
1652
2374
|
"button",
|
|
1653
2375
|
{
|
|
1654
2376
|
type: "button",
|
|
1655
2377
|
"data-vector-blend-action": "spine",
|
|
1656
|
-
className:
|
|
2378
|
+
className: buttonClass11,
|
|
1657
2379
|
title: "Replace Spine (select a blend and a path)",
|
|
1658
2380
|
"aria-label": "Replace Spine",
|
|
1659
2381
|
disabled: !blend.canReplaceSpine,
|
|
@@ -1661,12 +2383,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1661
2383
|
children: "\u2307"
|
|
1662
2384
|
}
|
|
1663
2385
|
),
|
|
1664
|
-
/* @__PURE__ */
|
|
1665
|
-
/* @__PURE__ */
|
|
2386
|
+
/* @__PURE__ */ jsx13("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass11, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
|
|
2387
|
+
/* @__PURE__ */ jsx13("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass11, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
|
|
1666
2388
|
] });
|
|
1667
2389
|
}
|
|
1668
|
-
var
|
|
1669
|
-
var
|
|
2390
|
+
var selectClass9 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2391
|
+
var numberClass7 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
1670
2392
|
function VectorStylePanel({ controller, className }) {
|
|
1671
2393
|
const state = useVectorEditorState(controller);
|
|
1672
2394
|
const style = getVectorSelectionStyle(state);
|
|
@@ -1685,6 +2407,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1685
2407
|
widthProfile: widthProfilePresetId(d.strokeWidthProfile),
|
|
1686
2408
|
cornerRadius: d.cornerRadius,
|
|
1687
2409
|
cornerKind: d.cornerKind ?? "round",
|
|
2410
|
+
cornerSmoothing: 0,
|
|
1688
2411
|
corners: 1,
|
|
1689
2412
|
hasOpenPath: true
|
|
1690
2413
|
};
|
|
@@ -1697,17 +2420,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1697
2420
|
if (style) controller.setWidthProfile(id);
|
|
1698
2421
|
else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
|
|
1699
2422
|
};
|
|
1700
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */
|
|
2423
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx13("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
1701
2424
|
const arrowRow = (end, label) => {
|
|
1702
2425
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
1703
2426
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
1704
|
-
return /* @__PURE__ */
|
|
1705
|
-
/* @__PURE__ */
|
|
1706
|
-
/* @__PURE__ */
|
|
1707
|
-
/* @__PURE__ */
|
|
1708
|
-
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */
|
|
2427
|
+
return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2428
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
2429
|
+
/* @__PURE__ */ jsxs13("select", { "aria-label": `${label} arrowhead`, className: selectClass9, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
2430
|
+
/* @__PURE__ */ jsx13("option", { value: "", children: "None" }),
|
|
2431
|
+
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx13("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
1709
2432
|
] }),
|
|
1710
|
-
/* @__PURE__ */
|
|
2433
|
+
/* @__PURE__ */ jsx13(
|
|
1711
2434
|
"input",
|
|
1712
2435
|
{
|
|
1713
2436
|
type: "number",
|
|
@@ -1715,7 +2438,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1715
2438
|
step: 0.25,
|
|
1716
2439
|
"aria-label": `${label} arrowhead scale`,
|
|
1717
2440
|
title: "Scale (\xD7 stroke width)",
|
|
1718
|
-
className:
|
|
2441
|
+
className: numberClass7,
|
|
1719
2442
|
value: source[scaleKey],
|
|
1720
2443
|
onChange: (e) => {
|
|
1721
2444
|
const n = parseFloat(e.target.value);
|
|
@@ -1731,14 +2454,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1731
2454
|
};
|
|
1732
2455
|
const paintRow = (key, label) => {
|
|
1733
2456
|
const paint = source[key];
|
|
1734
|
-
const hex =
|
|
2457
|
+
const hex = vectorPaintToHex4(paint);
|
|
1735
2458
|
const on = paint !== null && paint !== "none";
|
|
1736
|
-
return /* @__PURE__ */
|
|
1737
|
-
/* @__PURE__ */
|
|
1738
|
-
/* @__PURE__ */
|
|
2459
|
+
return /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2460
|
+
/* @__PURE__ */ jsxs13("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
2461
|
+
/* @__PURE__ */ jsx13("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
1739
2462
|
label
|
|
1740
2463
|
] }),
|
|
1741
|
-
/* @__PURE__ */
|
|
2464
|
+
/* @__PURE__ */ jsx13(
|
|
1742
2465
|
"input",
|
|
1743
2466
|
{
|
|
1744
2467
|
type: "color",
|
|
@@ -1749,18 +2472,18 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1749
2472
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
1750
2473
|
}
|
|
1751
2474
|
),
|
|
1752
|
-
on && !hex && /* @__PURE__ */
|
|
1753
|
-
style?.mixed[key] && /* @__PURE__ */
|
|
2475
|
+
on && !hex && /* @__PURE__ */ jsx13("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
2476
|
+
style?.mixed[key] && /* @__PURE__ */ jsx13("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
1754
2477
|
] });
|
|
1755
2478
|
};
|
|
1756
|
-
return /* @__PURE__ */
|
|
1757
|
-
/* @__PURE__ */
|
|
2479
|
+
return /* @__PURE__ */ jsxs13("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
2480
|
+
/* @__PURE__ */ jsx13("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
1758
2481
|
paintRow("fill", "Fill"),
|
|
1759
2482
|
paintRow("stroke", "Stroke"),
|
|
1760
|
-
style && /* @__PURE__ */
|
|
1761
|
-
/* @__PURE__ */
|
|
1762
|
-
/* @__PURE__ */
|
|
1763
|
-
/* @__PURE__ */
|
|
2483
|
+
style && /* @__PURE__ */ jsx13(VectorGradientEditor, { controller }),
|
|
2484
|
+
/* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2485
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16", children: "Width" }),
|
|
2486
|
+
/* @__PURE__ */ jsx13(
|
|
1764
2487
|
"input",
|
|
1765
2488
|
{
|
|
1766
2489
|
type: "number",
|
|
@@ -1776,33 +2499,33 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1776
2499
|
}
|
|
1777
2500
|
)
|
|
1778
2501
|
] }),
|
|
1779
|
-
/* @__PURE__ */
|
|
1780
|
-
/* @__PURE__ */
|
|
1781
|
-
/* @__PURE__ */
|
|
2502
|
+
/* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
2503
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16", children: "Align" }),
|
|
2504
|
+
/* @__PURE__ */ jsx13("select", { "aria-label": "Stroke alignment", className: selectClass9, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx13("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
|
|
1782
2505
|
mixed("strokeAlign")
|
|
1783
2506
|
] }),
|
|
1784
|
-
/* @__PURE__ */
|
|
1785
|
-
/* @__PURE__ */
|
|
1786
|
-
/* @__PURE__ */
|
|
2507
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
2508
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16", children: "Profile" }),
|
|
2509
|
+
/* @__PURE__ */ jsxs13(
|
|
1787
2510
|
"select",
|
|
1788
2511
|
{
|
|
1789
2512
|
"aria-label": "Width profile",
|
|
1790
|
-
className:
|
|
2513
|
+
className: selectClass9,
|
|
1791
2514
|
value: source.widthProfile,
|
|
1792
2515
|
onChange: (e) => {
|
|
1793
2516
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
1794
2517
|
},
|
|
1795
2518
|
children: [
|
|
1796
|
-
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */
|
|
1797
|
-
source.widthProfile === "custom" && /* @__PURE__ */
|
|
2519
|
+
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx13("option", { value: p.id, children: p.label }, p.id)),
|
|
2520
|
+
source.widthProfile === "custom" && /* @__PURE__ */ jsx13("option", { value: "custom", disabled: true, children: "Custom" })
|
|
1798
2521
|
]
|
|
1799
2522
|
}
|
|
1800
2523
|
),
|
|
1801
|
-
/* @__PURE__ */
|
|
2524
|
+
/* @__PURE__ */ jsx13(
|
|
1802
2525
|
"button",
|
|
1803
2526
|
{
|
|
1804
2527
|
type: "button",
|
|
1805
|
-
className:
|
|
2528
|
+
className: buttonClass11,
|
|
1806
2529
|
title: "Reset to uniform width",
|
|
1807
2530
|
"aria-label": "Reset to uniform width",
|
|
1808
2531
|
disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
|
|
@@ -1812,27 +2535,27 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1812
2535
|
),
|
|
1813
2536
|
mixed("widthProfile")
|
|
1814
2537
|
] }),
|
|
1815
|
-
source.hasOpenPath && /* @__PURE__ */
|
|
2538
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs13(Fragment4, { children: [
|
|
1816
2539
|
arrowRow("start", "Start"),
|
|
1817
2540
|
arrowRow("end", "End"),
|
|
1818
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
1819
|
-
/* @__PURE__ */
|
|
1820
|
-
/* @__PURE__ */
|
|
1821
|
-
/* @__PURE__ */
|
|
1822
|
-
/* @__PURE__ */
|
|
2541
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
2542
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16", children: "Heads" }),
|
|
2543
|
+
/* @__PURE__ */ jsxs13("select", { "aria-label": "Arrowhead alignment", className: selectClass9, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
2544
|
+
/* @__PURE__ */ jsx13("option", { value: "extend", children: "Tip past end" }),
|
|
2545
|
+
/* @__PURE__ */ jsx13("option", { value: "tip", children: "Tip at end" })
|
|
1823
2546
|
] })
|
|
1824
2547
|
] })
|
|
1825
2548
|
] }),
|
|
1826
|
-
source.corners > 0 && /* @__PURE__ */
|
|
1827
|
-
/* @__PURE__ */
|
|
1828
|
-
/* @__PURE__ */
|
|
2549
|
+
source.corners > 0 && /* @__PURE__ */ jsxs13("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
2550
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
2551
|
+
/* @__PURE__ */ jsx13(
|
|
1829
2552
|
"input",
|
|
1830
2553
|
{
|
|
1831
2554
|
type: "number",
|
|
1832
2555
|
min: 0,
|
|
1833
2556
|
step: 1,
|
|
1834
2557
|
"aria-label": "Corner radius",
|
|
1835
|
-
className:
|
|
2558
|
+
className: numberClass7,
|
|
1836
2559
|
value: Math.round(source.cornerRadius * 100) / 100,
|
|
1837
2560
|
onChange: (e) => {
|
|
1838
2561
|
const n = parseFloat(e.target.value);
|
|
@@ -1840,12 +2563,29 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1840
2563
|
}
|
|
1841
2564
|
}
|
|
1842
2565
|
),
|
|
1843
|
-
/* @__PURE__ */
|
|
2566
|
+
/* @__PURE__ */ jsx13("select", { "aria-label": "Corner kind", className: selectClass9, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx13("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
1844
2567
|
mixed("cornerRadius")
|
|
1845
2568
|
] }),
|
|
1846
|
-
style && /* @__PURE__ */
|
|
1847
|
-
/* @__PURE__ */
|
|
1848
|
-
/* @__PURE__ */
|
|
2569
|
+
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
|
|
2570
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16", children: "Smoothing" }),
|
|
2571
|
+
/* @__PURE__ */ jsx13(
|
|
2572
|
+
"input",
|
|
2573
|
+
{
|
|
2574
|
+
type: "range",
|
|
2575
|
+
min: 0,
|
|
2576
|
+
max: 1,
|
|
2577
|
+
step: 0.05,
|
|
2578
|
+
"aria-label": "Corner smoothing",
|
|
2579
|
+
className: "h-4 flex-1 accent-primary",
|
|
2580
|
+
value: source.cornerSmoothing,
|
|
2581
|
+
onChange: (e) => controller.setCornerSmoothing(parseFloat(e.target.value), { merge: "corner-smoothing" }),
|
|
2582
|
+
onPointerUp: () => controller.endMergedEdit()
|
|
2583
|
+
}
|
|
2584
|
+
)
|
|
2585
|
+
] }),
|
|
2586
|
+
style && /* @__PURE__ */ jsxs13("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2587
|
+
/* @__PURE__ */ jsx13("span", { className: "w-16", children: "Opacity" }),
|
|
2588
|
+
/* @__PURE__ */ jsx13(
|
|
1849
2589
|
"input",
|
|
1850
2590
|
{
|
|
1851
2591
|
type: "range",
|
|
@@ -1859,31 +2599,39 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1859
2599
|
}
|
|
1860
2600
|
)
|
|
1861
2601
|
] }),
|
|
1862
|
-
|
|
1863
|
-
style && /* @__PURE__ */
|
|
1864
|
-
style && /* @__PURE__ */
|
|
1865
|
-
style && /* @__PURE__ */
|
|
2602
|
+
/* @__PURE__ */ jsx13(VectorTextPanel, { controller }),
|
|
2603
|
+
style && /* @__PURE__ */ jsx13(VectorAppearancePanel, { controller }),
|
|
2604
|
+
style && /* @__PURE__ */ jsx13(VectorEffectsPanel, { controller }),
|
|
2605
|
+
style && /* @__PURE__ */ jsx13(VectorEnvelopePanel, { controller }),
|
|
2606
|
+
style && /* @__PURE__ */ jsx13(VectorMaskPanel, { controller }),
|
|
2607
|
+
/* @__PURE__ */ jsx13(VectorFramePanel, { controller }),
|
|
2608
|
+
/* @__PURE__ */ jsx13(VectorConnectorPanel, { controller }),
|
|
2609
|
+
/* @__PURE__ */ jsx13(VectorConstraintsPanel, { controller }),
|
|
2610
|
+
/* @__PURE__ */ jsx13(VectorSymbolsPanel, { controller })
|
|
1866
2611
|
] });
|
|
1867
2612
|
}
|
|
1868
2613
|
|
|
1869
2614
|
// src/vector/VectorEditor.tsx
|
|
1870
|
-
import { jsx as
|
|
2615
|
+
import { jsx as jsx14, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1871
2616
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
1872
|
-
return /* @__PURE__ */
|
|
1873
|
-
/* @__PURE__ */
|
|
1874
|
-
/* @__PURE__ */
|
|
1875
|
-
/* @__PURE__ */
|
|
1876
|
-
showStylePanel && /* @__PURE__ */
|
|
2617
|
+
return /* @__PURE__ */ jsxs14("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
2618
|
+
/* @__PURE__ */ jsx14(VectorToolbar, { controller, tools }),
|
|
2619
|
+
/* @__PURE__ */ jsxs14("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
2620
|
+
/* @__PURE__ */ jsx14("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx14(VectorCanvas, { controller, ...canvasProps }) }),
|
|
2621
|
+
showStylePanel && /* @__PURE__ */ jsx14(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
|
|
1877
2622
|
] })
|
|
1878
2623
|
] });
|
|
1879
2624
|
}
|
|
1880
2625
|
export {
|
|
1881
2626
|
VectorAppearancePanel,
|
|
1882
2627
|
VectorCanvas,
|
|
2628
|
+
VectorConnectorPanel,
|
|
2629
|
+
VectorConstraintsPanel,
|
|
1883
2630
|
VectorEditor,
|
|
1884
2631
|
VectorEffectsPanel,
|
|
1885
2632
|
VectorEnvelopePanel,
|
|
1886
2633
|
VectorFilterDefs,
|
|
2634
|
+
VectorFramePanel,
|
|
1887
2635
|
VectorGradientAnnotator,
|
|
1888
2636
|
VectorGradientDefs,
|
|
1889
2637
|
VectorGradientEditor,
|
|
@@ -1891,6 +2639,9 @@ export {
|
|
|
1891
2639
|
VectorMaskedNodes,
|
|
1892
2640
|
VectorSceneNodes,
|
|
1893
2641
|
VectorStylePanel,
|
|
2642
|
+
VectorSymbolsPanel,
|
|
2643
|
+
VectorTextElement,
|
|
2644
|
+
VectorTextPanel,
|
|
1894
2645
|
VectorToolbar,
|
|
1895
2646
|
VectorWidthPointEditor,
|
|
1896
2647
|
useVectorCommands,
|