@pixerate/editor-react 0.29.0 → 0.31.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/vector/index.cjs +340 -155
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +25 -3
- package/dist/vector/index.d.ts +25 -3
- package/dist/vector/index.js +325 -137
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.js
CHANGED
|
@@ -44,9 +44,9 @@ function useVectorCommands(registry, controller, options = {}) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// src/vector/VectorCanvas.tsx
|
|
47
|
-
import { useEffect as useEffect2, useId, useRef as
|
|
47
|
+
import { useEffect as useEffect2, useId, useRef as useRef3 } from "react";
|
|
48
48
|
import {
|
|
49
|
-
VECTOR_OVERLAY_THEME as
|
|
49
|
+
VECTOR_OVERLAY_THEME as THEME2,
|
|
50
50
|
getVectorOverlayRenderData,
|
|
51
51
|
getVectorSceneRenderData,
|
|
52
52
|
isVectorTextTarget,
|
|
@@ -54,17 +54,102 @@ import {
|
|
|
54
54
|
toVectorPointerInput,
|
|
55
55
|
vectorDiamondPath
|
|
56
56
|
} from "@pixerate/editor/vector";
|
|
57
|
-
|
|
57
|
+
|
|
58
|
+
// src/vector/VectorWidthPointEditor.tsx
|
|
59
|
+
import { useRef as useRef2, useState } from "react";
|
|
60
|
+
import { VECTOR_OVERLAY_THEME as THEME } from "@pixerate/editor/vector";
|
|
61
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
62
|
+
var round = (v, digits) => String(Math.round(v * 10 ** digits) / 10 ** digits);
|
|
63
|
+
var fieldStyle = { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 };
|
|
64
|
+
var inputStyle = { width: 72, font: "inherit", padding: "1px 4px" };
|
|
65
|
+
function VectorWidthPointEditor({ controller, edit, accentColor = THEME.accent, className, style }) {
|
|
66
|
+
const [position, setPosition] = useState(() => round(edit.t * 100, 2));
|
|
67
|
+
const [left, setLeft] = useState(() => round(edit.left, 3));
|
|
68
|
+
const [right, setRight] = useState(() => round(edit.right, 3));
|
|
69
|
+
const formRef = useRef2(null);
|
|
70
|
+
const refocus = () => formRef.current?.closest("svg")?.focus?.({ preventScroll: true });
|
|
71
|
+
const close = () => {
|
|
72
|
+
refocus();
|
|
73
|
+
controller.closeWidthPointEdit();
|
|
74
|
+
};
|
|
75
|
+
const num = (v) => v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
|
|
76
|
+
const apply = () => {
|
|
77
|
+
const t = num(position);
|
|
78
|
+
refocus();
|
|
79
|
+
controller.commitWidthPointEdit({ t: t === void 0 ? void 0 : Math.min(Math.max(t, 0), 100) / 100, left: num(left), right: num(right) });
|
|
80
|
+
};
|
|
81
|
+
const stop = (e) => e.stopPropagation();
|
|
82
|
+
return /* @__PURE__ */ jsxs(
|
|
83
|
+
"form",
|
|
84
|
+
{
|
|
85
|
+
ref: formRef,
|
|
86
|
+
"data-vector-width-point-editor": "",
|
|
87
|
+
"aria-label": "Width point",
|
|
88
|
+
className,
|
|
89
|
+
style: {
|
|
90
|
+
display: "grid",
|
|
91
|
+
gap: 4,
|
|
92
|
+
padding: 8,
|
|
93
|
+
background: "#ffffff",
|
|
94
|
+
border: "1px solid rgba(0,0,0,0.15)",
|
|
95
|
+
borderRadius: 6,
|
|
96
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
|
97
|
+
font: "12px system-ui, sans-serif",
|
|
98
|
+
color: "#18181b",
|
|
99
|
+
...style
|
|
100
|
+
},
|
|
101
|
+
onPointerDown: stop,
|
|
102
|
+
onPointerMove: stop,
|
|
103
|
+
onPointerUp: stop,
|
|
104
|
+
onDoubleClick: stop,
|
|
105
|
+
onCopy: stop,
|
|
106
|
+
onCut: stop,
|
|
107
|
+
onPaste: stop,
|
|
108
|
+
onKeyDown: (e) => {
|
|
109
|
+
e.stopPropagation();
|
|
110
|
+
if (e.key === "Escape") {
|
|
111
|
+
e.preventDefault();
|
|
112
|
+
close();
|
|
113
|
+
}
|
|
114
|
+
},
|
|
115
|
+
onSubmit: (e) => {
|
|
116
|
+
e.preventDefault();
|
|
117
|
+
apply();
|
|
118
|
+
},
|
|
119
|
+
children: [
|
|
120
|
+
/* @__PURE__ */ jsxs("label", { style: fieldStyle, children: [
|
|
121
|
+
"Position %",
|
|
122
|
+
/* @__PURE__ */ jsx("input", { "aria-label": "Width point position", type: "number", min: 0, max: 100, step: "any", value: position, onChange: (e) => setPosition(e.target.value), style: inputStyle, autoFocus: true })
|
|
123
|
+
] }),
|
|
124
|
+
/* @__PURE__ */ jsxs("label", { style: fieldStyle, children: [
|
|
125
|
+
"Left",
|
|
126
|
+
/* @__PURE__ */ jsx("input", { "aria-label": "Width point left side", type: "number", min: 0, step: "any", value: left, onChange: (e) => setLeft(e.target.value), style: inputStyle })
|
|
127
|
+
] }),
|
|
128
|
+
/* @__PURE__ */ jsxs("label", { style: fieldStyle, children: [
|
|
129
|
+
"Right",
|
|
130
|
+
/* @__PURE__ */ jsx("input", { "aria-label": "Width point right side", type: "number", min: 0, step: "any", value: right, onChange: (e) => setRight(e.target.value), style: inputStyle })
|
|
131
|
+
] }),
|
|
132
|
+
/* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "flex-end", gap: 4, marginTop: 2 }, children: [
|
|
133
|
+
/* @__PURE__ */ jsx("button", { type: "button", onClick: close, children: "Cancel" }),
|
|
134
|
+
/* @__PURE__ */ jsx("button", { type: "submit", style: { background: accentColor, color: "#ffffff", border: "none", borderRadius: 4, padding: "2px 8px" }, children: edit.index === null ? "Add" : "Apply" })
|
|
135
|
+
] })
|
|
136
|
+
]
|
|
137
|
+
}
|
|
138
|
+
);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
// src/vector/VectorCanvas.tsx
|
|
142
|
+
import { Fragment, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
58
143
|
function VectorGradientDefs({ defs }) {
|
|
59
144
|
if (!defs.length) return null;
|
|
60
|
-
return /* @__PURE__ */
|
|
61
|
-
const stops = def.stops.map((s, i) => /* @__PURE__ */
|
|
62
|
-
return def.type === "radial" ? /* @__PURE__ */
|
|
145
|
+
return /* @__PURE__ */ jsx2("defs", { children: defs.map((def) => {
|
|
146
|
+
const stops = def.stops.map((s, i) => /* @__PURE__ */ jsx2("stop", { offset: s.offset, stopColor: s.stopColor, stopOpacity: s.stopOpacity }, i));
|
|
147
|
+
return def.type === "radial" ? /* @__PURE__ */ jsx2("radialGradient", { id: def.id, ...def.attrs, children: stops }, def.id) : /* @__PURE__ */ jsx2("linearGradient", { id: def.id, ...def.attrs, children: stops }, def.id);
|
|
63
148
|
}) });
|
|
64
149
|
}
|
|
65
150
|
function VectorSceneNodes({ nodes }) {
|
|
66
|
-
return /* @__PURE__ */
|
|
67
|
-
(node) => node.kind === "group" ? /* @__PURE__ */
|
|
151
|
+
return /* @__PURE__ */ jsx2(Fragment, { children: nodes.map(
|
|
152
|
+
(node) => node.kind === "group" ? /* @__PURE__ */ jsx2("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: node.children }) }, node.id) : node.strokeParts ? /* @__PURE__ */ jsx2(VectorPartedPath, { node }, node.id) : /* @__PURE__ */ jsx2(
|
|
68
153
|
"path",
|
|
69
154
|
{
|
|
70
155
|
"data-vector-id": node.id,
|
|
@@ -87,15 +172,15 @@ function VectorPartedPath({ node }) {
|
|
|
87
172
|
const sp = node.strokeParts;
|
|
88
173
|
const clip = sp.clip;
|
|
89
174
|
const b = clip?.bounds;
|
|
90
|
-
return /* @__PURE__ */
|
|
91
|
-
clip && b && /* @__PURE__ */
|
|
92
|
-
/* @__PURE__ */
|
|
93
|
-
/* @__PURE__ */
|
|
175
|
+
return /* @__PURE__ */ jsxs2("g", { "data-vector-id": node.id, "data-vector-stroke-align": sp.align, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: [
|
|
176
|
+
clip && b && /* @__PURE__ */ jsx2("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ jsx2("clipPath", { id: clip.id, children: /* @__PURE__ */ jsx2("path", { d: node.d, clipRule: node.fillRule }) }) : /* @__PURE__ */ jsxs2("mask", { id: clip.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, children: [
|
|
177
|
+
/* @__PURE__ */ jsx2("rect", { x: b.x, y: b.y, width: b.width, height: b.height, fill: "#fff" }),
|
|
178
|
+
/* @__PURE__ */ jsx2("path", { d: node.d, fill: "#000", fillRule: node.fillRule })
|
|
94
179
|
] }) }),
|
|
95
|
-
/* @__PURE__ */
|
|
180
|
+
/* @__PURE__ */ jsx2("path", { "data-vector-part": "fill", d: node.d, fill: node.fill, fillRule: node.fillRule, stroke: "none" }),
|
|
96
181
|
sp.outline !== void 0 ? (
|
|
97
182
|
// A variable-width stroke: its outline, filled.
|
|
98
|
-
/* @__PURE__ */
|
|
183
|
+
/* @__PURE__ */ jsx2(
|
|
99
184
|
"path",
|
|
100
185
|
{
|
|
101
186
|
"data-vector-part": "stroke-outline",
|
|
@@ -105,7 +190,7 @@ function VectorPartedPath({ node }) {
|
|
|
105
190
|
mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
|
|
106
191
|
}
|
|
107
192
|
)
|
|
108
|
-
) : /* @__PURE__ */
|
|
193
|
+
) : /* @__PURE__ */ jsx2(
|
|
109
194
|
"path",
|
|
110
195
|
{
|
|
111
196
|
"data-vector-part": "stroke",
|
|
@@ -120,22 +205,22 @@ function VectorPartedPath({ node }) {
|
|
|
120
205
|
mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
|
|
121
206
|
}
|
|
122
207
|
),
|
|
123
|
-
sp.heads.map((head, i) => /* @__PURE__ */
|
|
208
|
+
sp.heads.map((head, i) => /* @__PURE__ */ jsx2("path", { "data-vector-part": "head", d: head, fill: node.stroke }, i))
|
|
124
209
|
] });
|
|
125
210
|
}
|
|
126
211
|
function VectorCanvas({
|
|
127
212
|
controller,
|
|
128
213
|
fitOnMount = true,
|
|
129
214
|
checkerboard = true,
|
|
130
|
-
accentColor =
|
|
131
|
-
guideColor =
|
|
215
|
+
accentColor = THEME2.accent,
|
|
216
|
+
guideColor = THEME2.guide,
|
|
132
217
|
className,
|
|
133
218
|
style,
|
|
134
219
|
"aria-label": ariaLabel = "Vector canvas"
|
|
135
220
|
}) {
|
|
136
221
|
const state = useVectorEditorState(controller);
|
|
137
|
-
const svgRef =
|
|
138
|
-
const fitted =
|
|
222
|
+
const svgRef = useRef3(null);
|
|
223
|
+
const fitted = useRef3(!fitOnMount);
|
|
139
224
|
const uid = useId().replace(/:/g, "");
|
|
140
225
|
const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });
|
|
141
226
|
const overlay = getVectorOverlayRenderData(state);
|
|
@@ -183,10 +268,12 @@ function VectorCanvas({
|
|
|
183
268
|
event.preventDefault?.();
|
|
184
269
|
}
|
|
185
270
|
};
|
|
186
|
-
const hs =
|
|
187
|
-
const as =
|
|
271
|
+
const hs = THEME2.transformHandleSize;
|
|
272
|
+
const as = THEME2.anchorSize;
|
|
188
273
|
const box = overlay.selectionBox;
|
|
189
|
-
|
|
274
|
+
const edit = state.widthPointEdit;
|
|
275
|
+
const editAt = edit && { x: edit.point.x * state.viewport.zoom + state.viewport.x, y: edit.point.y * state.viewport.zoom + state.viewport.y };
|
|
276
|
+
return /* @__PURE__ */ jsxs2(
|
|
190
277
|
"svg",
|
|
191
278
|
{
|
|
192
279
|
ref: svgRef,
|
|
@@ -218,13 +305,13 @@ function VectorCanvas({
|
|
|
218
305
|
onCut: clipboard("cut"),
|
|
219
306
|
onPaste: clipboard("paste"),
|
|
220
307
|
children: [
|
|
221
|
-
checkerboard && /* @__PURE__ */
|
|
222
|
-
/* @__PURE__ */
|
|
223
|
-
/* @__PURE__ */
|
|
224
|
-
/* @__PURE__ */
|
|
308
|
+
checkerboard && /* @__PURE__ */ jsx2("defs", { children: /* @__PURE__ */ jsxs2("pattern", { id: `ve${uid}-checker`, width: 16, height: 16, patternUnits: "userSpaceOnUse", children: [
|
|
309
|
+
/* @__PURE__ */ jsx2("rect", { width: 16, height: 16, fill: "#ffffff" }),
|
|
310
|
+
/* @__PURE__ */ jsx2("rect", { width: 8, height: 8, fill: "#e5e7eb" }),
|
|
311
|
+
/* @__PURE__ */ jsx2("rect", { x: 8, y: 8, width: 8, height: 8, fill: "#e5e7eb" })
|
|
225
312
|
] }) }),
|
|
226
|
-
/* @__PURE__ */
|
|
227
|
-
/* @__PURE__ */
|
|
313
|
+
/* @__PURE__ */ jsx2(VectorGradientDefs, { defs: scene.defs }),
|
|
314
|
+
/* @__PURE__ */ jsx2(
|
|
228
315
|
"rect",
|
|
229
316
|
{
|
|
230
317
|
"data-vector-artboard": "",
|
|
@@ -236,84 +323,105 @@ function VectorCanvas({
|
|
|
236
323
|
stroke: "rgba(0,0,0,0.12)"
|
|
237
324
|
}
|
|
238
325
|
),
|
|
239
|
-
/* @__PURE__ */
|
|
240
|
-
/* @__PURE__ */
|
|
241
|
-
overlay.outlines.map((o) => /* @__PURE__ */
|
|
242
|
-
overlay.penPreview && /* @__PURE__ */
|
|
243
|
-
box && /* @__PURE__ */
|
|
244
|
-
/* @__PURE__ */
|
|
245
|
-
/* @__PURE__ */
|
|
326
|
+
/* @__PURE__ */ jsx2("g", { "data-vector-scene": "", transform: scene.transform, children: /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: scene.nodes }) }),
|
|
327
|
+
/* @__PURE__ */ jsxs2("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
|
|
328
|
+
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)),
|
|
329
|
+
overlay.penPreview && /* @__PURE__ */ jsx2("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
|
|
330
|
+
box && /* @__PURE__ */ jsxs2("g", { "data-vector-selection": "", transform: box.transform || void 0, children: [
|
|
331
|
+
/* @__PURE__ */ jsx2("rect", { x: box.x, y: box.y, width: box.width, height: box.height, stroke: accentColor }),
|
|
332
|
+
/* @__PURE__ */ jsx2("line", { x1: box.x + box.width / 2, y1: box.y, x2: box.x + box.width / 2, y2: box.y - 24, stroke: accentColor }),
|
|
246
333
|
box.handles.map(
|
|
247
|
-
(h) => h.handle === "rotate" ? /* @__PURE__ */
|
|
334
|
+
(h) => h.handle === "rotate" ? /* @__PURE__ */ jsx2("circle", { "data-handle": h.handle, cx: h.x, cy: h.y, r: hs / 2 + 1, fill: THEME2.handleFill, stroke: accentColor }, h.handle) : /* @__PURE__ */ jsx2("rect", { "data-handle": h.handle, x: h.x - hs / 2, y: h.y - hs / 2, width: hs, height: hs, fill: THEME2.handleFill, stroke: accentColor }, h.handle)
|
|
248
335
|
)
|
|
249
336
|
] }),
|
|
250
|
-
overlay.handles.map((h) => /* @__PURE__ */
|
|
251
|
-
/* @__PURE__ */
|
|
252
|
-
/* @__PURE__ */
|
|
337
|
+
overlay.handles.map((h) => /* @__PURE__ */ jsxs2("g", { "data-vector-handle": h.ref.handle, children: [
|
|
338
|
+
/* @__PURE__ */ jsx2("line", { x1: h.anchorX, y1: h.anchorY, x2: h.x, y2: h.y, stroke: accentColor }),
|
|
339
|
+
/* @__PURE__ */ jsx2("circle", { cx: h.x, cy: h.y, r: THEME2.handleRadius + (h.hovered ? 1 : 0), fill: THEME2.handleFill, stroke: accentColor })
|
|
253
340
|
] }, `${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`)),
|
|
254
341
|
overlay.anchors.map((a) => {
|
|
255
342
|
const size = as + (a.hovered ? 2 : 0);
|
|
256
|
-
const fill = a.selected ? accentColor :
|
|
343
|
+
const fill = a.selected ? accentColor : THEME2.handleFill;
|
|
257
344
|
const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;
|
|
258
|
-
return a.type === "corner" ? /* @__PURE__ */
|
|
345
|
+
return a.type === "corner" ? /* @__PURE__ */ jsx2("rect", { "data-vector-anchor": a.ref.index, x: a.x - size / 2, y: a.y - size / 2, width: size, height: size, fill, stroke: accentColor }, key) : /* @__PURE__ */ jsx2("circle", { "data-vector-anchor": a.ref.index, cx: a.x, cy: a.y, r: size / 2, fill, stroke: accentColor }, key);
|
|
259
346
|
}),
|
|
260
|
-
overlay.cornerWidgets.map((w) => /* @__PURE__ */
|
|
261
|
-
/* @__PURE__ */
|
|
262
|
-
/* @__PURE__ */
|
|
347
|
+
overlay.cornerWidgets.map((w) => /* @__PURE__ */ jsxs2("g", { "data-vector-corner-widget": w.ref.index, "data-corner-kind": w.kind, children: [
|
|
348
|
+
/* @__PURE__ */ jsx2("circle", { cx: w.x, cy: w.y, r: THEME2.cornerWidgetRadius + (w.hovered ? 1 : 0), fill: THEME2.handleFill, stroke: accentColor }),
|
|
349
|
+
/* @__PURE__ */ jsx2("circle", { cx: w.x, cy: w.y, r: 1.5, fill: accentColor })
|
|
263
350
|
] }, `corner:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
|
|
264
|
-
overlay.
|
|
265
|
-
/* @__PURE__ */
|
|
266
|
-
/* @__PURE__ */
|
|
351
|
+
overlay.blend.spines.map((sp) => /* @__PURE__ */ jsxs2("g", { "data-vector-blend-spine": sp.id, "data-replaced": sp.replaced || void 0, children: [
|
|
352
|
+
/* @__PURE__ */ jsx2("path", { d: sp.d, fill: "none", stroke: accentColor, strokeDasharray: "4 3" }),
|
|
353
|
+
sp.keys.map((k, i) => /* @__PURE__ */ jsx2("circle", { cx: k.x, cy: k.y, r: THEME2.handleRadius, fill: THEME2.handleFill, stroke: accentColor }, i))
|
|
354
|
+
] }, `blend:${sp.id}`)),
|
|
355
|
+
overlay.blend.start && /* @__PURE__ */ jsxs2("g", { "data-vector-blend-start": "", children: [
|
|
356
|
+
/* @__PURE__ */ jsx2("path", { d: overlay.blend.start.d, fill: "none", stroke: accentColor, strokeWidth: 2 }),
|
|
357
|
+
overlay.blend.start.anchor && /* @__PURE__ */ jsx2("circle", { cx: overlay.blend.start.anchor.x, cy: overlay.blend.start.anchor.y, r: THEME2.handleRadius + 1, fill: accentColor })
|
|
267
358
|
] }),
|
|
268
|
-
overlay.
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
359
|
+
overlay.blend.anchors.map((a, i) => /* @__PURE__ */ jsx2(
|
|
360
|
+
"rect",
|
|
361
|
+
{
|
|
362
|
+
"data-vector-blend-anchor": i,
|
|
363
|
+
x: a.x - as / 2,
|
|
364
|
+
y: a.y - as / 2,
|
|
365
|
+
width: as,
|
|
366
|
+
height: as,
|
|
367
|
+
fill: a.hovered ? accentColor : THEME2.handleFill,
|
|
368
|
+
stroke: accentColor
|
|
369
|
+
},
|
|
370
|
+
`blend-anchor:${i}`
|
|
371
|
+
)),
|
|
372
|
+
overlay.widthSpot && /* @__PURE__ */ jsxs2("g", { "data-vector-width-spot": "", children: [
|
|
373
|
+
/* @__PURE__ */ jsx2("line", { x1: overlay.widthSpot.left.x, y1: overlay.widthSpot.left.y, x2: overlay.widthSpot.right.x, y2: overlay.widthSpot.right.y, stroke: accentColor, strokeDasharray: "3 2" }),
|
|
374
|
+
/* @__PURE__ */ jsx2("path", { d: vectorDiamondPath(overlay.widthSpot.x, overlay.widthSpot.y), fill: "none", stroke: accentColor })
|
|
375
|
+
] }),
|
|
376
|
+
overlay.widthPoints.map((w) => /* @__PURE__ */ jsxs2("g", { "data-vector-width-point": w.ref.index, "data-selected": w.selected || void 0, children: [
|
|
377
|
+
/* @__PURE__ */ jsx2("line", { x1: w.left.x, y1: w.left.y, x2: w.right.x, y2: w.right.y, stroke: accentColor }),
|
|
378
|
+
/* @__PURE__ */ jsx2("path", { d: vectorDiamondPath(w.x, w.y), fill: w.selected || w.active ? accentColor : THEME2.handleFill, stroke: accentColor, strokeWidth: w.hovered === "center" ? 2 : 1 }),
|
|
379
|
+
["left", "right"].map((side) => /* @__PURE__ */ jsx2("circle", { "data-vector-width-side": side, cx: w[side].x, cy: w[side].y, r: THEME2.handleRadius + (w.hovered === side ? 1 : 0), fill: THEME2.handleFill, stroke: accentColor }, side))
|
|
272
380
|
] }, `width:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
|
|
273
|
-
state.tool === "pen" && overlay.anchors.some((a) => a.closes && a.hovered) && /* @__PURE__ */
|
|
274
|
-
overlay.builder && /* @__PURE__ */
|
|
275
|
-
overlay.builder.faces.map((d, i) => /* @__PURE__ */
|
|
381
|
+
state.tool === "pen" && overlay.anchors.some((a) => a.closes && a.hovered) && /* @__PURE__ */ jsx2("circle", { cx: overlay.anchors.find((a) => a.closes).x, cy: overlay.anchors.find((a) => a.closes).y, r: as, stroke: accentColor }),
|
|
382
|
+
overlay.builder && /* @__PURE__ */ jsxs2("g", { "data-vector-builder": overlay.builder.mode, children: [
|
|
383
|
+
overlay.builder.faces.map((d, i) => /* @__PURE__ */ jsx2(
|
|
276
384
|
"path",
|
|
277
385
|
{
|
|
278
386
|
"data-vector-builder-face": "",
|
|
279
387
|
d,
|
|
280
|
-
fill: overlay.builder.mode === "erase" ?
|
|
388
|
+
fill: overlay.builder.mode === "erase" ? THEME2.builderEraseFill : THEME2.builderFill,
|
|
281
389
|
fillRule: "nonzero",
|
|
282
|
-
stroke: overlay.builder.mode === "erase" ?
|
|
390
|
+
stroke: overlay.builder.mode === "erase" ? THEME2.builderErase : accentColor,
|
|
283
391
|
strokeWidth: 1
|
|
284
392
|
},
|
|
285
393
|
`f${i}`
|
|
286
394
|
)),
|
|
287
395
|
overlay.builder.hatches.map(
|
|
288
|
-
(d, i) => d ? /* @__PURE__ */
|
|
396
|
+
(d, i) => d ? /* @__PURE__ */ jsx2(
|
|
289
397
|
"path",
|
|
290
398
|
{
|
|
291
399
|
"data-vector-builder-hatch": "",
|
|
292
400
|
d,
|
|
293
401
|
fill: "none",
|
|
294
|
-
stroke: overlay.builder.mode === "erase" ?
|
|
295
|
-
strokeOpacity:
|
|
402
|
+
stroke: overlay.builder.mode === "erase" ? THEME2.builderErase : accentColor,
|
|
403
|
+
strokeOpacity: THEME2.builderHatchOpacity,
|
|
296
404
|
strokeWidth: 1
|
|
297
405
|
},
|
|
298
406
|
`h${i}`
|
|
299
407
|
) : null
|
|
300
408
|
),
|
|
301
|
-
overlay.builder.lines.map((d, i) => /* @__PURE__ */
|
|
302
|
-
overlay.builder.trail && /* @__PURE__ */
|
|
409
|
+
overlay.builder.lines.map((d, i) => /* @__PURE__ */ jsx2("path", { "data-vector-builder-line": "", d, fill: "none", stroke: THEME2.builderErase, strokeWidth: 3, strokeLinecap: "round" }, `l${i}`)),
|
|
410
|
+
overlay.builder.trail && /* @__PURE__ */ jsx2("path", { "data-vector-builder-trail": "", d: overlay.builder.trail, fill: "none", stroke: overlay.builder.mode === "erase" ? THEME2.builderErase : accentColor, strokeWidth: 1 })
|
|
303
411
|
] }),
|
|
304
|
-
overlay.toolStroke && /* @__PURE__ */
|
|
412
|
+
overlay.toolStroke && /* @__PURE__ */ jsx2(
|
|
305
413
|
"path",
|
|
306
414
|
{
|
|
307
415
|
"data-vector-tool-stroke": overlay.toolStroke.tool,
|
|
308
416
|
d: overlay.toolStroke.d,
|
|
309
|
-
stroke: overlay.toolStroke.tool === "eraser" ?
|
|
417
|
+
stroke: overlay.toolStroke.tool === "eraser" ? THEME2.eraserFill : accentColor,
|
|
310
418
|
strokeWidth: overlay.toolStroke.tool === "eraser" ? overlay.toolStroke.width : 1,
|
|
311
419
|
strokeLinecap: "round",
|
|
312
420
|
strokeLinejoin: "round",
|
|
313
421
|
strokeDasharray: overlay.toolStroke.tool === "knife" ? "5 3" : void 0
|
|
314
422
|
}
|
|
315
423
|
),
|
|
316
|
-
overlay.pencilString && /* @__PURE__ */
|
|
424
|
+
overlay.pencilString && /* @__PURE__ */ jsx2(
|
|
317
425
|
"line",
|
|
318
426
|
{
|
|
319
427
|
"data-vector-pencil-string": "",
|
|
@@ -325,8 +433,8 @@ function VectorCanvas({
|
|
|
325
433
|
strokeDasharray: "2 2"
|
|
326
434
|
}
|
|
327
435
|
),
|
|
328
|
-
overlay.cutPoint && /* @__PURE__ */
|
|
329
|
-
overlay.marquee && /* @__PURE__ */
|
|
436
|
+
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 }),
|
|
437
|
+
overlay.marquee && /* @__PURE__ */ jsx2(
|
|
330
438
|
"rect",
|
|
331
439
|
{
|
|
332
440
|
"data-vector-marquee": "",
|
|
@@ -334,13 +442,13 @@ function VectorCanvas({
|
|
|
334
442
|
y: overlay.marquee.y,
|
|
335
443
|
width: overlay.marquee.width,
|
|
336
444
|
height: overlay.marquee.height,
|
|
337
|
-
fill:
|
|
445
|
+
fill: THEME2.marqueeFill,
|
|
338
446
|
stroke: accentColor,
|
|
339
447
|
strokeDasharray: "3 2"
|
|
340
448
|
}
|
|
341
449
|
),
|
|
342
|
-
overlay.guides.lines.length > 0 && /* @__PURE__ */
|
|
343
|
-
overlay.guides.labels.map((l, i) => /* @__PURE__ */
|
|
450
|
+
overlay.guides.lines.length > 0 && /* @__PURE__ */ jsx2("g", { "data-vector-guides": "", stroke: guideColor, children: overlay.guides.lines.map((l, i) => /* @__PURE__ */ jsx2("line", { "data-vector-guide": l.kind, x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2, strokeDasharray: l.kind === "construction" ? "4 3" : void 0 }, i)) }),
|
|
451
|
+
overlay.guides.labels.map((l, i) => /* @__PURE__ */ jsx2(
|
|
344
452
|
"text",
|
|
345
453
|
{
|
|
346
454
|
"data-vector-guide-label": l.kind,
|
|
@@ -356,8 +464,10 @@ function VectorCanvas({
|
|
|
356
464
|
},
|
|
357
465
|
i
|
|
358
466
|
)),
|
|
359
|
-
overlay.status && /* @__PURE__ */
|
|
360
|
-
] })
|
|
467
|
+
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 })
|
|
468
|
+
] }),
|
|
469
|
+
edit && editAt && // Width Point Edit (double-click with the width tool), beside the point.
|
|
470
|
+
/* @__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}`) })
|
|
361
471
|
]
|
|
362
472
|
}
|
|
363
473
|
);
|
|
@@ -378,25 +488,38 @@ import {
|
|
|
378
488
|
VECTOR_CORNER_KIND_LABELS,
|
|
379
489
|
VECTOR_STROKE_ALIGN_LABELS,
|
|
380
490
|
VECTOR_WIDTH_PRESETS,
|
|
491
|
+
VECTOR_BLEND_SPACINGS,
|
|
492
|
+
VECTOR_BLEND_SPACING_LABELS,
|
|
493
|
+
applyVectorBlendOptions,
|
|
494
|
+
getVectorBlendControls,
|
|
381
495
|
getVectorSelectionStyle,
|
|
382
496
|
widthProfilePreset,
|
|
383
497
|
widthProfilePresetId,
|
|
384
498
|
vectorPaintToHex,
|
|
385
499
|
vectorToolLabel
|
|
386
500
|
} from "@pixerate/editor/vector";
|
|
387
|
-
import { Fragment as Fragment2, jsx as
|
|
501
|
+
import { Fragment as Fragment2, jsx as jsx3, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
388
502
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
389
503
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
390
504
|
var buttonClass = "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";
|
|
391
|
-
function VectorToolbar({
|
|
505
|
+
function VectorToolbar({
|
|
506
|
+
controller,
|
|
507
|
+
tools = VECTOR_TOOLS,
|
|
508
|
+
showHistory = true,
|
|
509
|
+
showArrange = true,
|
|
510
|
+
showSnap = true,
|
|
511
|
+
showPathEdit = true,
|
|
512
|
+
showBlend = true,
|
|
513
|
+
className
|
|
514
|
+
}) {
|
|
392
515
|
const state = useVectorEditorState(controller);
|
|
393
516
|
const count = state.selection.length;
|
|
394
517
|
const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
|
|
395
518
|
const editing = state.editing;
|
|
396
519
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
397
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */
|
|
398
|
-
return /* @__PURE__ */
|
|
399
|
-
/* @__PURE__ */
|
|
520
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx3("button", { type: "button", className: buttonClass, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
521
|
+
return /* @__PURE__ */ jsxs3("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
522
|
+
/* @__PURE__ */ jsx3("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx3(
|
|
400
523
|
"button",
|
|
401
524
|
{
|
|
402
525
|
type: "button",
|
|
@@ -410,11 +533,11 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
410
533
|
},
|
|
411
534
|
tool
|
|
412
535
|
)) }),
|
|
413
|
-
showHistory && /* @__PURE__ */
|
|
536
|
+
showHistory && /* @__PURE__ */ jsxs3("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
414
537
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
415
538
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
416
539
|
] }),
|
|
417
|
-
showSnap && /* @__PURE__ */
|
|
540
|
+
showSnap && /* @__PURE__ */ jsx3("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ jsx3(
|
|
418
541
|
"button",
|
|
419
542
|
{
|
|
420
543
|
type: "button",
|
|
@@ -427,8 +550,8 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
427
550
|
children: "Snap"
|
|
428
551
|
}
|
|
429
552
|
) }),
|
|
430
|
-
showPathEdit && /* @__PURE__ */
|
|
431
|
-
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */
|
|
553
|
+
showPathEdit && /* @__PURE__ */ jsxs3("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
554
|
+
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx3(
|
|
432
555
|
"button",
|
|
433
556
|
{
|
|
434
557
|
type: "button",
|
|
@@ -442,7 +565,7 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
442
565
|
},
|
|
443
566
|
a.id
|
|
444
567
|
)),
|
|
445
|
-
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */
|
|
568
|
+
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx3(
|
|
446
569
|
"button",
|
|
447
570
|
{
|
|
448
571
|
type: "button",
|
|
@@ -456,9 +579,9 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
456
579
|
},
|
|
457
580
|
t.key
|
|
458
581
|
)),
|
|
459
|
-
state.tool === "pencil" && /* @__PURE__ */
|
|
582
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
460
583
|
"Smoothing",
|
|
461
|
-
/* @__PURE__ */
|
|
584
|
+
/* @__PURE__ */ jsx3(
|
|
462
585
|
"input",
|
|
463
586
|
{
|
|
464
587
|
type: "range",
|
|
@@ -472,9 +595,9 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
472
595
|
}
|
|
473
596
|
)
|
|
474
597
|
] }),
|
|
475
|
-
state.tool === "eraser" && /* @__PURE__ */
|
|
598
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
476
599
|
"Size",
|
|
477
|
-
/* @__PURE__ */
|
|
600
|
+
/* @__PURE__ */ jsx3(
|
|
478
601
|
"input",
|
|
479
602
|
{
|
|
480
603
|
type: "number",
|
|
@@ -490,7 +613,7 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
490
613
|
}
|
|
491
614
|
)
|
|
492
615
|
] }),
|
|
493
|
-
state.tool === "paintBucket" && /* @__PURE__ */
|
|
616
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx3(
|
|
494
617
|
"button",
|
|
495
618
|
{
|
|
496
619
|
type: "button",
|
|
@@ -504,8 +627,9 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
504
627
|
}
|
|
505
628
|
)
|
|
506
629
|
] }),
|
|
507
|
-
|
|
508
|
-
|
|
630
|
+
showBlend && /* @__PURE__ */ jsx3(VectorBlendControls, { controller }),
|
|
631
|
+
showArrange && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
632
|
+
/* @__PURE__ */ jsxs3("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
509
633
|
act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
|
|
510
634
|
act("\u2212", "Subtract", () => controller.booleanOp("subtract"), count < 2),
|
|
511
635
|
act("\u2229", "Intersect", () => controller.booleanOp("intersect"), count < 2),
|
|
@@ -513,7 +637,7 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
513
637
|
act("G", "Group", () => controller.group(), count < 1),
|
|
514
638
|
act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
|
|
515
639
|
] }),
|
|
516
|
-
/* @__PURE__ */
|
|
640
|
+
/* @__PURE__ */ jsx3("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx3(
|
|
517
641
|
"button",
|
|
518
642
|
{
|
|
519
643
|
type: "button",
|
|
@@ -527,7 +651,7 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
527
651
|
},
|
|
528
652
|
a.id
|
|
529
653
|
)) }),
|
|
530
|
-
/* @__PURE__ */
|
|
654
|
+
/* @__PURE__ */ jsxs3("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
531
655
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
532
656
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
533
657
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -540,6 +664,69 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
540
664
|
] })
|
|
541
665
|
] });
|
|
542
666
|
}
|
|
667
|
+
function VectorBlendControls({ controller }) {
|
|
668
|
+
const state = useVectorEditorState(controller);
|
|
669
|
+
const blend = getVectorBlendControls(state);
|
|
670
|
+
const o = blend.options;
|
|
671
|
+
const selected = blend.ids.length > 0;
|
|
672
|
+
return /* @__PURE__ */ jsxs3("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
|
|
673
|
+
/* @__PURE__ */ jsx3("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
|
|
674
|
+
/* @__PURE__ */ jsx3(
|
|
675
|
+
"select",
|
|
676
|
+
{
|
|
677
|
+
"aria-label": "Blend spacing",
|
|
678
|
+
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
679
|
+
value: o.spacing,
|
|
680
|
+
onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
|
|
681
|
+
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx3("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
|
|
682
|
+
}
|
|
683
|
+
),
|
|
684
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ jsx3(
|
|
685
|
+
"input",
|
|
686
|
+
{
|
|
687
|
+
type: "number",
|
|
688
|
+
min: o.spacing === "steps" ? 1 : 0.5,
|
|
689
|
+
step: 1,
|
|
690
|
+
"aria-label": o.spacing === "steps" ? "Blend steps" : "Blend distance",
|
|
691
|
+
className: "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs",
|
|
692
|
+
value: o.spacing === "steps" ? o.steps : o.distance,
|
|
693
|
+
onChange: (e) => {
|
|
694
|
+
const n = parseFloat(e.target.value);
|
|
695
|
+
if (!Number.isFinite(n) || n <= 0) return;
|
|
696
|
+
applyVectorBlendOptions(controller, o.spacing === "steps" ? { steps: n } : { distance: n });
|
|
697
|
+
}
|
|
698
|
+
}
|
|
699
|
+
),
|
|
700
|
+
/* @__PURE__ */ jsx3(
|
|
701
|
+
"button",
|
|
702
|
+
{
|
|
703
|
+
type: "button",
|
|
704
|
+
"data-vector-blend-action": "orient",
|
|
705
|
+
className: buttonClass,
|
|
706
|
+
"aria-pressed": o.orientToSpine,
|
|
707
|
+
title: "Orient steps to the spine",
|
|
708
|
+
"aria-label": "Orient to spine",
|
|
709
|
+
onClick: () => applyVectorBlendOptions(controller, { orientToSpine: !o.orientToSpine }),
|
|
710
|
+
children: "\u293F"
|
|
711
|
+
}
|
|
712
|
+
),
|
|
713
|
+
/* @__PURE__ */ jsx3(
|
|
714
|
+
"button",
|
|
715
|
+
{
|
|
716
|
+
type: "button",
|
|
717
|
+
"data-vector-blend-action": "spine",
|
|
718
|
+
className: buttonClass,
|
|
719
|
+
title: "Replace Spine (select a blend and a path)",
|
|
720
|
+
"aria-label": "Replace Spine",
|
|
721
|
+
disabled: !blend.canReplaceSpine,
|
|
722
|
+
onClick: () => controller.replaceBlendSpine(),
|
|
723
|
+
children: "\u2307"
|
|
724
|
+
}
|
|
725
|
+
),
|
|
726
|
+
/* @__PURE__ */ jsx3("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
|
|
727
|
+
/* @__PURE__ */ jsx3("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
|
|
728
|
+
] });
|
|
729
|
+
}
|
|
543
730
|
var selectClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
544
731
|
var numberClass = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
545
732
|
function VectorStylePanel({ controller, className }) {
|
|
@@ -572,17 +759,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
572
759
|
if (style) controller.setWidthProfile(id);
|
|
573
760
|
else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
|
|
574
761
|
};
|
|
575
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */
|
|
762
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx3("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
576
763
|
const arrowRow = (end, label) => {
|
|
577
764
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
578
765
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
579
|
-
return /* @__PURE__ */
|
|
580
|
-
/* @__PURE__ */
|
|
581
|
-
/* @__PURE__ */
|
|
582
|
-
/* @__PURE__ */
|
|
583
|
-
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */
|
|
766
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
767
|
+
/* @__PURE__ */ jsx3("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
768
|
+
/* @__PURE__ */ jsxs3("select", { "aria-label": `${label} arrowhead`, className: selectClass, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
769
|
+
/* @__PURE__ */ jsx3("option", { value: "", children: "None" }),
|
|
770
|
+
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx3("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
584
771
|
] }),
|
|
585
|
-
/* @__PURE__ */
|
|
772
|
+
/* @__PURE__ */ jsx3(
|
|
586
773
|
"input",
|
|
587
774
|
{
|
|
588
775
|
type: "number",
|
|
@@ -608,12 +795,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
608
795
|
const paint = source[key];
|
|
609
796
|
const hex = vectorPaintToHex(paint);
|
|
610
797
|
const on = paint !== null && paint !== "none";
|
|
611
|
-
return /* @__PURE__ */
|
|
612
|
-
/* @__PURE__ */
|
|
613
|
-
/* @__PURE__ */
|
|
798
|
+
return /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
799
|
+
/* @__PURE__ */ jsxs3("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
800
|
+
/* @__PURE__ */ jsx3("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
614
801
|
label
|
|
615
802
|
] }),
|
|
616
|
-
/* @__PURE__ */
|
|
803
|
+
/* @__PURE__ */ jsx3(
|
|
617
804
|
"input",
|
|
618
805
|
{
|
|
619
806
|
type: "color",
|
|
@@ -624,17 +811,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
624
811
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
625
812
|
}
|
|
626
813
|
),
|
|
627
|
-
on && !hex && /* @__PURE__ */
|
|
628
|
-
style?.mixed[key] && /* @__PURE__ */
|
|
814
|
+
on && !hex && /* @__PURE__ */ jsx3("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
815
|
+
style?.mixed[key] && /* @__PURE__ */ jsx3("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
629
816
|
] });
|
|
630
817
|
};
|
|
631
|
-
return /* @__PURE__ */
|
|
632
|
-
/* @__PURE__ */
|
|
818
|
+
return /* @__PURE__ */ jsxs3("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
819
|
+
/* @__PURE__ */ jsx3("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
633
820
|
paintRow("fill", "Fill"),
|
|
634
821
|
paintRow("stroke", "Stroke"),
|
|
635
|
-
/* @__PURE__ */
|
|
636
|
-
/* @__PURE__ */
|
|
637
|
-
/* @__PURE__ */
|
|
822
|
+
/* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
823
|
+
/* @__PURE__ */ jsx3("span", { className: "w-16", children: "Width" }),
|
|
824
|
+
/* @__PURE__ */ jsx3(
|
|
638
825
|
"input",
|
|
639
826
|
{
|
|
640
827
|
type: "number",
|
|
@@ -650,14 +837,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
650
837
|
}
|
|
651
838
|
)
|
|
652
839
|
] }),
|
|
653
|
-
/* @__PURE__ */
|
|
654
|
-
/* @__PURE__ */
|
|
655
|
-
/* @__PURE__ */
|
|
840
|
+
/* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
841
|
+
/* @__PURE__ */ jsx3("span", { className: "w-16", children: "Align" }),
|
|
842
|
+
/* @__PURE__ */ jsx3("select", { "aria-label": "Stroke alignment", className: selectClass, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ jsx3("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
|
|
656
843
|
mixed("strokeAlign")
|
|
657
844
|
] }),
|
|
658
|
-
/* @__PURE__ */
|
|
659
|
-
/* @__PURE__ */
|
|
660
|
-
/* @__PURE__ */
|
|
845
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
846
|
+
/* @__PURE__ */ jsx3("span", { className: "w-16", children: "Profile" }),
|
|
847
|
+
/* @__PURE__ */ jsxs3(
|
|
661
848
|
"select",
|
|
662
849
|
{
|
|
663
850
|
"aria-label": "Width profile",
|
|
@@ -667,12 +854,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
667
854
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
668
855
|
},
|
|
669
856
|
children: [
|
|
670
|
-
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */
|
|
671
|
-
source.widthProfile === "custom" && /* @__PURE__ */
|
|
857
|
+
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx3("option", { value: p.id, children: p.label }, p.id)),
|
|
858
|
+
source.widthProfile === "custom" && /* @__PURE__ */ jsx3("option", { value: "custom", disabled: true, children: "Custom" })
|
|
672
859
|
]
|
|
673
860
|
}
|
|
674
861
|
),
|
|
675
|
-
/* @__PURE__ */
|
|
862
|
+
/* @__PURE__ */ jsx3(
|
|
676
863
|
"button",
|
|
677
864
|
{
|
|
678
865
|
type: "button",
|
|
@@ -686,20 +873,20 @@ function VectorStylePanel({ controller, className }) {
|
|
|
686
873
|
),
|
|
687
874
|
mixed("widthProfile")
|
|
688
875
|
] }),
|
|
689
|
-
source.hasOpenPath && /* @__PURE__ */
|
|
876
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs3(Fragment2, { children: [
|
|
690
877
|
arrowRow("start", "Start"),
|
|
691
878
|
arrowRow("end", "End"),
|
|
692
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
693
|
-
/* @__PURE__ */
|
|
694
|
-
/* @__PURE__ */
|
|
695
|
-
/* @__PURE__ */
|
|
696
|
-
/* @__PURE__ */
|
|
879
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
880
|
+
/* @__PURE__ */ jsx3("span", { className: "w-16", children: "Heads" }),
|
|
881
|
+
/* @__PURE__ */ jsxs3("select", { "aria-label": "Arrowhead alignment", className: selectClass, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
882
|
+
/* @__PURE__ */ jsx3("option", { value: "extend", children: "Tip past end" }),
|
|
883
|
+
/* @__PURE__ */ jsx3("option", { value: "tip", children: "Tip at end" })
|
|
697
884
|
] })
|
|
698
885
|
] })
|
|
699
886
|
] }),
|
|
700
|
-
source.corners > 0 && /* @__PURE__ */
|
|
701
|
-
/* @__PURE__ */
|
|
702
|
-
/* @__PURE__ */
|
|
887
|
+
source.corners > 0 && /* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
888
|
+
/* @__PURE__ */ jsx3("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
889
|
+
/* @__PURE__ */ jsx3(
|
|
703
890
|
"input",
|
|
704
891
|
{
|
|
705
892
|
type: "number",
|
|
@@ -714,12 +901,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
714
901
|
}
|
|
715
902
|
}
|
|
716
903
|
),
|
|
717
|
-
/* @__PURE__ */
|
|
904
|
+
/* @__PURE__ */ jsx3("select", { "aria-label": "Corner kind", className: selectClass, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx3("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
718
905
|
mixed("cornerRadius")
|
|
719
906
|
] }),
|
|
720
|
-
style && /* @__PURE__ */
|
|
721
|
-
/* @__PURE__ */
|
|
722
|
-
/* @__PURE__ */
|
|
907
|
+
style && /* @__PURE__ */ jsxs3("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
908
|
+
/* @__PURE__ */ jsx3("span", { className: "w-16", children: "Opacity" }),
|
|
909
|
+
/* @__PURE__ */ jsx3(
|
|
723
910
|
"input",
|
|
724
911
|
{
|
|
725
912
|
type: "range",
|
|
@@ -737,13 +924,13 @@ function VectorStylePanel({ controller, className }) {
|
|
|
737
924
|
}
|
|
738
925
|
|
|
739
926
|
// src/vector/VectorEditor.tsx
|
|
740
|
-
import { jsx as
|
|
927
|
+
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
741
928
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
742
|
-
return /* @__PURE__ */
|
|
743
|
-
/* @__PURE__ */
|
|
744
|
-
/* @__PURE__ */
|
|
745
|
-
/* @__PURE__ */
|
|
746
|
-
showStylePanel && /* @__PURE__ */
|
|
929
|
+
return /* @__PURE__ */ jsxs4("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
930
|
+
/* @__PURE__ */ jsx4(VectorToolbar, { controller, tools }),
|
|
931
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
932
|
+
/* @__PURE__ */ jsx4("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx4(VectorCanvas, { controller, ...canvasProps }) }),
|
|
933
|
+
showStylePanel && /* @__PURE__ */ jsx4(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
|
|
747
934
|
] })
|
|
748
935
|
] });
|
|
749
936
|
}
|
|
@@ -754,6 +941,7 @@ export {
|
|
|
754
941
|
VectorSceneNodes,
|
|
755
942
|
VectorStylePanel,
|
|
756
943
|
VectorToolbar,
|
|
944
|
+
VectorWidthPointEditor,
|
|
757
945
|
useVectorCommands,
|
|
758
946
|
useVectorEditor,
|
|
759
947
|
useVectorEditorController,
|