@pixerate/editor-react 0.29.0 → 0.30.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 +246 -155
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +19 -2
- package/dist/vector/index.d.ts +19 -2
- package/dist/vector/index.js +226 -136
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.cjs
CHANGED
|
@@ -26,6 +26,7 @@ __export(vector_exports, {
|
|
|
26
26
|
VectorSceneNodes: () => VectorSceneNodes,
|
|
27
27
|
VectorStylePanel: () => VectorStylePanel,
|
|
28
28
|
VectorToolbar: () => VectorToolbar,
|
|
29
|
+
VectorWidthPointEditor: () => VectorWidthPointEditor,
|
|
29
30
|
useVectorCommands: () => useVectorCommands,
|
|
30
31
|
useVectorEditor: () => useVectorEditor,
|
|
31
32
|
useVectorEditorController: () => useVectorEditorController,
|
|
@@ -76,19 +77,104 @@ function useVectorCommands(registry, controller, options = {}) {
|
|
|
76
77
|
}
|
|
77
78
|
|
|
78
79
|
// src/vector/VectorCanvas.tsx
|
|
80
|
+
var import_react3 = require("react");
|
|
81
|
+
var import_vector3 = require("@pixerate/editor/vector");
|
|
82
|
+
|
|
83
|
+
// src/vector/VectorWidthPointEditor.tsx
|
|
79
84
|
var import_react2 = require("react");
|
|
80
85
|
var import_vector2 = require("@pixerate/editor/vector");
|
|
81
86
|
var import_jsx_runtime = require("react/jsx-runtime");
|
|
87
|
+
var round = (v, digits) => String(Math.round(v * 10 ** digits) / 10 ** digits);
|
|
88
|
+
var fieldStyle = { display: "flex", alignItems: "center", justifyContent: "space-between", gap: 6 };
|
|
89
|
+
var inputStyle = { width: 72, font: "inherit", padding: "1px 4px" };
|
|
90
|
+
function VectorWidthPointEditor({ controller, edit, accentColor = import_vector2.VECTOR_OVERLAY_THEME.accent, className, style }) {
|
|
91
|
+
const [position, setPosition] = (0, import_react2.useState)(() => round(edit.t * 100, 2));
|
|
92
|
+
const [left, setLeft] = (0, import_react2.useState)(() => round(edit.left, 3));
|
|
93
|
+
const [right, setRight] = (0, import_react2.useState)(() => round(edit.right, 3));
|
|
94
|
+
const formRef = (0, import_react2.useRef)(null);
|
|
95
|
+
const refocus = () => formRef.current?.closest("svg")?.focus?.({ preventScroll: true });
|
|
96
|
+
const close = () => {
|
|
97
|
+
refocus();
|
|
98
|
+
controller.closeWidthPointEdit();
|
|
99
|
+
};
|
|
100
|
+
const num = (v) => v.trim() !== "" && Number.isFinite(Number(v)) ? Number(v) : void 0;
|
|
101
|
+
const apply = () => {
|
|
102
|
+
const t = num(position);
|
|
103
|
+
refocus();
|
|
104
|
+
controller.commitWidthPointEdit({ t: t === void 0 ? void 0 : Math.min(Math.max(t, 0), 100) / 100, left: num(left), right: num(right) });
|
|
105
|
+
};
|
|
106
|
+
const stop = (e) => e.stopPropagation();
|
|
107
|
+
return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
|
|
108
|
+
"form",
|
|
109
|
+
{
|
|
110
|
+
ref: formRef,
|
|
111
|
+
"data-vector-width-point-editor": "",
|
|
112
|
+
"aria-label": "Width point",
|
|
113
|
+
className,
|
|
114
|
+
style: {
|
|
115
|
+
display: "grid",
|
|
116
|
+
gap: 4,
|
|
117
|
+
padding: 8,
|
|
118
|
+
background: "#ffffff",
|
|
119
|
+
border: "1px solid rgba(0,0,0,0.15)",
|
|
120
|
+
borderRadius: 6,
|
|
121
|
+
boxShadow: "0 2px 8px rgba(0,0,0,0.15)",
|
|
122
|
+
font: "12px system-ui, sans-serif",
|
|
123
|
+
color: "#18181b",
|
|
124
|
+
...style
|
|
125
|
+
},
|
|
126
|
+
onPointerDown: stop,
|
|
127
|
+
onPointerMove: stop,
|
|
128
|
+
onPointerUp: stop,
|
|
129
|
+
onDoubleClick: stop,
|
|
130
|
+
onCopy: stop,
|
|
131
|
+
onCut: stop,
|
|
132
|
+
onPaste: stop,
|
|
133
|
+
onKeyDown: (e) => {
|
|
134
|
+
e.stopPropagation();
|
|
135
|
+
if (e.key === "Escape") {
|
|
136
|
+
e.preventDefault();
|
|
137
|
+
close();
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
onSubmit: (e) => {
|
|
141
|
+
e.preventDefault();
|
|
142
|
+
apply();
|
|
143
|
+
},
|
|
144
|
+
children: [
|
|
145
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldStyle, children: [
|
|
146
|
+
"Position %",
|
|
147
|
+
/* @__PURE__ */ (0, import_jsx_runtime.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 })
|
|
148
|
+
] }),
|
|
149
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldStyle, children: [
|
|
150
|
+
"Left",
|
|
151
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { "aria-label": "Width point left side", type: "number", min: 0, step: "any", value: left, onChange: (e) => setLeft(e.target.value), style: inputStyle })
|
|
152
|
+
] }),
|
|
153
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("label", { style: fieldStyle, children: [
|
|
154
|
+
"Right",
|
|
155
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("input", { "aria-label": "Width point right side", type: "number", min: 0, step: "any", value: right, onChange: (e) => setRight(e.target.value), style: inputStyle })
|
|
156
|
+
] }),
|
|
157
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("div", { style: { display: "flex", justifyContent: "flex-end", gap: 4, marginTop: 2 }, children: [
|
|
158
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "button", onClick: close, children: "Cancel" }),
|
|
159
|
+
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("button", { type: "submit", style: { background: accentColor, color: "#ffffff", border: "none", borderRadius: 4, padding: "2px 8px" }, children: edit.index === null ? "Add" : "Apply" })
|
|
160
|
+
] })
|
|
161
|
+
]
|
|
162
|
+
}
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
// src/vector/VectorCanvas.tsx
|
|
167
|
+
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
82
168
|
function VectorGradientDefs({ defs }) {
|
|
83
169
|
if (!defs.length) return null;
|
|
84
|
-
return /* @__PURE__ */ (0,
|
|
85
|
-
const stops = def.stops.map((s, i) => /* @__PURE__ */ (0,
|
|
86
|
-
return def.type === "radial" ? /* @__PURE__ */ (0,
|
|
170
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: defs.map((def) => {
|
|
171
|
+
const stops = def.stops.map((s, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("stop", { offset: s.offset, stopColor: s.stopColor, stopOpacity: s.stopOpacity }, i));
|
|
172
|
+
return def.type === "radial" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("radialGradient", { id: def.id, ...def.attrs, children: stops }, def.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("linearGradient", { id: def.id, ...def.attrs, children: stops }, def.id);
|
|
87
173
|
}) });
|
|
88
174
|
}
|
|
89
175
|
function VectorSceneNodes({ nodes }) {
|
|
90
|
-
return /* @__PURE__ */ (0,
|
|
91
|
-
(node) => node.kind === "group" ? /* @__PURE__ */ (0,
|
|
176
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_jsx_runtime2.Fragment, { children: nodes.map(
|
|
177
|
+
(node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: node.children }) }, node.id) : node.strokeParts ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorPartedPath, { node }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
92
178
|
"path",
|
|
93
179
|
{
|
|
94
180
|
"data-vector-id": node.id,
|
|
@@ -111,15 +197,15 @@ function VectorPartedPath({ node }) {
|
|
|
111
197
|
const sp = node.strokeParts;
|
|
112
198
|
const clip = sp.clip;
|
|
113
199
|
const b = clip?.bounds;
|
|
114
|
-
return /* @__PURE__ */ (0,
|
|
115
|
-
clip && b && /* @__PURE__ */ (0,
|
|
116
|
-
/* @__PURE__ */ (0,
|
|
117
|
-
/* @__PURE__ */ (0,
|
|
200
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-id": node.id, "data-vector-stroke-align": sp.align, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: [
|
|
201
|
+
clip && b && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("clipPath", { id: clip.id, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: node.d, clipRule: node.fillRule }) }) : /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("mask", { id: clip.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, children: [
|
|
202
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: b.x, y: b.y, width: b.width, height: b.height, fill: "#fff" }),
|
|
203
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: node.d, fill: "#000", fillRule: node.fillRule })
|
|
118
204
|
] }) }),
|
|
119
|
-
/* @__PURE__ */ (0,
|
|
205
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "fill", d: node.d, fill: node.fill, fillRule: node.fillRule, stroke: "none" }),
|
|
120
206
|
sp.outline !== void 0 ? (
|
|
121
207
|
// A variable-width stroke: its outline, filled.
|
|
122
|
-
/* @__PURE__ */ (0,
|
|
208
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
123
209
|
"path",
|
|
124
210
|
{
|
|
125
211
|
"data-vector-part": "stroke-outline",
|
|
@@ -129,7 +215,7 @@ function VectorPartedPath({ node }) {
|
|
|
129
215
|
mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
|
|
130
216
|
}
|
|
131
217
|
)
|
|
132
|
-
) : /* @__PURE__ */ (0,
|
|
218
|
+
) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
133
219
|
"path",
|
|
134
220
|
{
|
|
135
221
|
"data-vector-part": "stroke",
|
|
@@ -144,26 +230,26 @@ function VectorPartedPath({ node }) {
|
|
|
144
230
|
mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
|
|
145
231
|
}
|
|
146
232
|
),
|
|
147
|
-
sp.heads.map((head, i) => /* @__PURE__ */ (0,
|
|
233
|
+
sp.heads.map((head, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-part": "head", d: head, fill: node.stroke }, i))
|
|
148
234
|
] });
|
|
149
235
|
}
|
|
150
236
|
function VectorCanvas({
|
|
151
237
|
controller,
|
|
152
238
|
fitOnMount = true,
|
|
153
239
|
checkerboard = true,
|
|
154
|
-
accentColor =
|
|
155
|
-
guideColor =
|
|
240
|
+
accentColor = import_vector3.VECTOR_OVERLAY_THEME.accent,
|
|
241
|
+
guideColor = import_vector3.VECTOR_OVERLAY_THEME.guide,
|
|
156
242
|
className,
|
|
157
243
|
style,
|
|
158
244
|
"aria-label": ariaLabel = "Vector canvas"
|
|
159
245
|
}) {
|
|
160
246
|
const state = useVectorEditorState(controller);
|
|
161
|
-
const svgRef = (0,
|
|
162
|
-
const fitted = (0,
|
|
163
|
-
const uid = (0,
|
|
164
|
-
const scene = (0,
|
|
165
|
-
const overlay = (0,
|
|
166
|
-
(0,
|
|
247
|
+
const svgRef = (0, import_react3.useRef)(null);
|
|
248
|
+
const fitted = (0, import_react3.useRef)(!fitOnMount);
|
|
249
|
+
const uid = (0, import_react3.useId)().replace(/:/g, "");
|
|
250
|
+
const scene = (0, import_vector3.getVectorSceneRenderData)(state, { idPrefix: `ve${uid}-` });
|
|
251
|
+
const overlay = (0, import_vector3.getVectorOverlayRenderData)(state);
|
|
252
|
+
(0, import_react3.useEffect)(() => {
|
|
167
253
|
const el = svgRef.current;
|
|
168
254
|
if (!el || fitted.current) return;
|
|
169
255
|
const fit = () => {
|
|
@@ -178,12 +264,12 @@ function VectorCanvas({
|
|
|
178
264
|
observer.observe(el);
|
|
179
265
|
return () => observer.disconnect();
|
|
180
266
|
}, [controller]);
|
|
181
|
-
(0,
|
|
267
|
+
(0, import_react3.useEffect)(() => {
|
|
182
268
|
const el = svgRef.current;
|
|
183
269
|
if (!el) return;
|
|
184
270
|
const onWheel = (e) => {
|
|
185
271
|
e.preventDefault();
|
|
186
|
-
controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, (0,
|
|
272
|
+
controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, (0, import_vector3.toVectorPointerInput)(e));
|
|
187
273
|
};
|
|
188
274
|
el.addEventListener("wheel", onWheel, { passive: false });
|
|
189
275
|
return () => el.removeEventListener("wheel", onWheel);
|
|
@@ -207,10 +293,12 @@ function VectorCanvas({
|
|
|
207
293
|
event.preventDefault?.();
|
|
208
294
|
}
|
|
209
295
|
};
|
|
210
|
-
const hs =
|
|
211
|
-
const as =
|
|
296
|
+
const hs = import_vector3.VECTOR_OVERLAY_THEME.transformHandleSize;
|
|
297
|
+
const as = import_vector3.VECTOR_OVERLAY_THEME.anchorSize;
|
|
212
298
|
const box = overlay.selectionBox;
|
|
213
|
-
|
|
299
|
+
const edit = state.widthPointEdit;
|
|
300
|
+
const editAt = edit && { x: edit.point.x * state.viewport.zoom + state.viewport.x, y: edit.point.y * state.viewport.zoom + state.viewport.y };
|
|
301
|
+
return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
|
|
214
302
|
"svg",
|
|
215
303
|
{
|
|
216
304
|
ref: svgRef,
|
|
@@ -223,32 +311,32 @@ function VectorCanvas({
|
|
|
223
311
|
onPointerDown: (e) => {
|
|
224
312
|
svgRef.current?.focus({ preventScroll: true });
|
|
225
313
|
e.currentTarget.setPointerCapture?.(e.pointerId);
|
|
226
|
-
controller.pointerDown(local(e), (0,
|
|
314
|
+
controller.pointerDown(local(e), (0, import_vector3.toVectorPointerInput)(e));
|
|
227
315
|
},
|
|
228
|
-
onPointerMove: (e) => controller.pointerMove(local(e), (0,
|
|
316
|
+
onPointerMove: (e) => controller.pointerMove(local(e), (0, import_vector3.toVectorPointerInput)(e)),
|
|
229
317
|
onPointerUp: (e) => {
|
|
230
318
|
e.currentTarget.releasePointerCapture?.(e.pointerId);
|
|
231
|
-
controller.pointerUp(local(e), (0,
|
|
319
|
+
controller.pointerUp(local(e), (0, import_vector3.toVectorPointerInput)(e));
|
|
232
320
|
},
|
|
233
321
|
onPointerCancel: () => controller.cancelDrag(),
|
|
234
322
|
onPointerLeave: () => controller.pointerLeave(),
|
|
235
323
|
onDoubleClick: (e) => controller.doubleClick(local(e)),
|
|
236
324
|
onKeyDown: (e) => {
|
|
237
|
-
if ((0,
|
|
238
|
-
if (controller.keyDown((0,
|
|
325
|
+
if ((0, import_vector3.isVectorTextTarget)(e.target)) return;
|
|
326
|
+
if (controller.keyDown((0, import_vector3.toVectorKeyInput)(e))) e.preventDefault();
|
|
239
327
|
},
|
|
240
|
-
onKeyUp: (e) => controller.keyUp((0,
|
|
328
|
+
onKeyUp: (e) => controller.keyUp((0, import_vector3.toVectorKeyInput)(e)),
|
|
241
329
|
onCopy: clipboard("copy"),
|
|
242
330
|
onCut: clipboard("cut"),
|
|
243
331
|
onPaste: clipboard("paste"),
|
|
244
332
|
children: [
|
|
245
|
-
checkerboard && /* @__PURE__ */ (0,
|
|
246
|
-
/* @__PURE__ */ (0,
|
|
247
|
-
/* @__PURE__ */ (0,
|
|
248
|
-
/* @__PURE__ */ (0,
|
|
333
|
+
checkerboard && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("defs", { children: /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("pattern", { id: `ve${uid}-checker`, width: 16, height: 16, patternUnits: "userSpaceOnUse", children: [
|
|
334
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: 16, height: 16, fill: "#ffffff" }),
|
|
335
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { width: 8, height: 8, fill: "#e5e7eb" }),
|
|
336
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: 8, y: 8, width: 8, height: 8, fill: "#e5e7eb" })
|
|
249
337
|
] }) }),
|
|
250
|
-
/* @__PURE__ */ (0,
|
|
251
|
-
/* @__PURE__ */ (0,
|
|
338
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorGradientDefs, { defs: scene.defs }),
|
|
339
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
252
340
|
"rect",
|
|
253
341
|
{
|
|
254
342
|
"data-vector-artboard": "",
|
|
@@ -260,84 +348,84 @@ function VectorCanvas({
|
|
|
260
348
|
stroke: "rgba(0,0,0,0.12)"
|
|
261
349
|
}
|
|
262
350
|
),
|
|
263
|
-
/* @__PURE__ */ (0,
|
|
264
|
-
/* @__PURE__ */ (0,
|
|
265
|
-
overlay.outlines.map((o) => /* @__PURE__ */ (0,
|
|
266
|
-
overlay.penPreview && /* @__PURE__ */ (0,
|
|
267
|
-
box && /* @__PURE__ */ (0,
|
|
268
|
-
/* @__PURE__ */ (0,
|
|
269
|
-
/* @__PURE__ */ (0,
|
|
351
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-scene": "", transform: scene.transform, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorSceneNodes, { nodes: scene.nodes }) }),
|
|
352
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
|
|
353
|
+
overlay.outlines.map((o) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: o.d, stroke: accentColor, strokeWidth: o.selected ? 1 : 1.5, opacity: o.selected ? 1 : 0.7 }, o.id)),
|
|
354
|
+
overlay.penPreview && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
|
|
355
|
+
box && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-selection": "", transform: box.transform || void 0, children: [
|
|
356
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { x: box.x, y: box.y, width: box.width, height: box.height, stroke: accentColor }),
|
|
357
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: box.x + box.width / 2, y1: box.y, x2: box.x + box.width / 2, y2: box.y - 24, stroke: accentColor }),
|
|
270
358
|
box.handles.map(
|
|
271
|
-
(h) => h.handle === "rotate" ? /* @__PURE__ */ (0,
|
|
359
|
+
(h) => h.handle === "rotate" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-handle": h.handle, cx: h.x, cy: h.y, r: hs / 2 + 1, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, h.handle) : /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("rect", { "data-handle": h.handle, x: h.x - hs / 2, y: h.y - hs / 2, width: hs, height: hs, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, h.handle)
|
|
272
360
|
)
|
|
273
361
|
] }),
|
|
274
|
-
overlay.handles.map((h) => /* @__PURE__ */ (0,
|
|
275
|
-
/* @__PURE__ */ (0,
|
|
276
|
-
/* @__PURE__ */ (0,
|
|
362
|
+
overlay.handles.map((h) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-handle": h.ref.handle, children: [
|
|
363
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: h.anchorX, y1: h.anchorY, x2: h.x, y2: h.y, stroke: accentColor }),
|
|
364
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: h.x, cy: h.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + (h.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor })
|
|
277
365
|
] }, `${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`)),
|
|
278
366
|
overlay.anchors.map((a) => {
|
|
279
367
|
const size = as + (a.hovered ? 2 : 0);
|
|
280
|
-
const fill = a.selected ? accentColor :
|
|
368
|
+
const fill = a.selected ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill;
|
|
281
369
|
const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;
|
|
282
|
-
return a.type === "corner" ? /* @__PURE__ */ (0,
|
|
370
|
+
return a.type === "corner" ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-anchor": a.ref.index, cx: a.x, cy: a.y, r: size / 2, fill, stroke: accentColor }, key);
|
|
283
371
|
}),
|
|
284
|
-
overlay.cornerWidgets.map((w) => /* @__PURE__ */ (0,
|
|
285
|
-
/* @__PURE__ */ (0,
|
|
286
|
-
/* @__PURE__ */ (0,
|
|
372
|
+
overlay.cornerWidgets.map((w) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-corner-widget": w.ref.index, "data-corner-kind": w.kind, children: [
|
|
373
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: w.x, cy: w.y, r: import_vector3.VECTOR_OVERLAY_THEME.cornerWidgetRadius + (w.hovered ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
|
|
374
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: w.x, cy: w.y, r: 1.5, fill: accentColor })
|
|
287
375
|
] }, `corner:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
|
|
288
|
-
overlay.widthSpot && /* @__PURE__ */ (0,
|
|
289
|
-
/* @__PURE__ */ (0,
|
|
290
|
-
/* @__PURE__ */ (0,
|
|
376
|
+
overlay.widthSpot && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-width-spot": "", children: [
|
|
377
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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" }),
|
|
378
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: (0, import_vector3.vectorDiamondPath)(overlay.widthSpot.x, overlay.widthSpot.y), fill: "none", stroke: accentColor })
|
|
291
379
|
] }),
|
|
292
|
-
overlay.widthPoints.map((w) => /* @__PURE__ */ (0,
|
|
293
|
-
/* @__PURE__ */ (0,
|
|
294
|
-
/* @__PURE__ */ (0,
|
|
295
|
-
["left", "right"].map((side) => /* @__PURE__ */ (0,
|
|
380
|
+
overlay.widthPoints.map((w) => /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-width-point": w.ref.index, "data-selected": w.selected || void 0, children: [
|
|
381
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("line", { x1: w.left.x, y1: w.left.y, x2: w.right.x, y2: w.right.y, stroke: accentColor }),
|
|
382
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { d: (0, import_vector3.vectorDiamondPath)(w.x, w.y), fill: w.selected || w.active ? accentColor : import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor, strokeWidth: w.hovered === "center" ? 2 : 1 }),
|
|
383
|
+
["left", "right"].map((side) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-width-side": side, cx: w[side].x, cy: w[side].y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + (w.hovered === side ? 1 : 0), fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }, side))
|
|
296
384
|
] }, `width:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
|
|
297
|
-
state.tool === "pen" && overlay.anchors.some((a) => a.closes && a.hovered) && /* @__PURE__ */ (0,
|
|
298
|
-
overlay.builder && /* @__PURE__ */ (0,
|
|
299
|
-
overlay.builder.faces.map((d, i) => /* @__PURE__ */ (0,
|
|
385
|
+
state.tool === "pen" && overlay.anchors.some((a) => a.closes && a.hovered) && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { cx: overlay.anchors.find((a) => a.closes).x, cy: overlay.anchors.find((a) => a.closes).y, r: as, stroke: accentColor }),
|
|
386
|
+
overlay.builder && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("g", { "data-vector-builder": overlay.builder.mode, children: [
|
|
387
|
+
overlay.builder.faces.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
300
388
|
"path",
|
|
301
389
|
{
|
|
302
390
|
"data-vector-builder-face": "",
|
|
303
391
|
d,
|
|
304
|
-
fill: overlay.builder.mode === "erase" ?
|
|
392
|
+
fill: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderEraseFill : import_vector3.VECTOR_OVERLAY_THEME.builderFill,
|
|
305
393
|
fillRule: "nonzero",
|
|
306
|
-
stroke: overlay.builder.mode === "erase" ?
|
|
394
|
+
stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor,
|
|
307
395
|
strokeWidth: 1
|
|
308
396
|
},
|
|
309
397
|
`f${i}`
|
|
310
398
|
)),
|
|
311
399
|
overlay.builder.hatches.map(
|
|
312
|
-
(d, i) => d ? /* @__PURE__ */ (0,
|
|
400
|
+
(d, i) => d ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
313
401
|
"path",
|
|
314
402
|
{
|
|
315
403
|
"data-vector-builder-hatch": "",
|
|
316
404
|
d,
|
|
317
405
|
fill: "none",
|
|
318
|
-
stroke: overlay.builder.mode === "erase" ?
|
|
319
|
-
strokeOpacity:
|
|
406
|
+
stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor,
|
|
407
|
+
strokeOpacity: import_vector3.VECTOR_OVERLAY_THEME.builderHatchOpacity,
|
|
320
408
|
strokeWidth: 1
|
|
321
409
|
},
|
|
322
410
|
`h${i}`
|
|
323
411
|
) : null
|
|
324
412
|
),
|
|
325
|
-
overlay.builder.lines.map((d, i) => /* @__PURE__ */ (0,
|
|
326
|
-
overlay.builder.trail && /* @__PURE__ */ (0,
|
|
413
|
+
overlay.builder.lines.map((d, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-builder-line": "", d, fill: "none", stroke: import_vector3.VECTOR_OVERLAY_THEME.builderErase, strokeWidth: 3, strokeLinecap: "round" }, `l${i}`)),
|
|
414
|
+
overlay.builder.trail && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("path", { "data-vector-builder-trail": "", d: overlay.builder.trail, fill: "none", stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor, strokeWidth: 1 })
|
|
327
415
|
] }),
|
|
328
|
-
overlay.toolStroke && /* @__PURE__ */ (0,
|
|
416
|
+
overlay.toolStroke && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
329
417
|
"path",
|
|
330
418
|
{
|
|
331
419
|
"data-vector-tool-stroke": overlay.toolStroke.tool,
|
|
332
420
|
d: overlay.toolStroke.d,
|
|
333
|
-
stroke: overlay.toolStroke.tool === "eraser" ?
|
|
421
|
+
stroke: overlay.toolStroke.tool === "eraser" ? import_vector3.VECTOR_OVERLAY_THEME.eraserFill : accentColor,
|
|
334
422
|
strokeWidth: overlay.toolStroke.tool === "eraser" ? overlay.toolStroke.width : 1,
|
|
335
423
|
strokeLinecap: "round",
|
|
336
424
|
strokeLinejoin: "round",
|
|
337
425
|
strokeDasharray: overlay.toolStroke.tool === "knife" ? "5 3" : void 0
|
|
338
426
|
}
|
|
339
427
|
),
|
|
340
|
-
overlay.pencilString && /* @__PURE__ */ (0,
|
|
428
|
+
overlay.pencilString && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
341
429
|
"line",
|
|
342
430
|
{
|
|
343
431
|
"data-vector-pencil-string": "",
|
|
@@ -349,8 +437,8 @@ function VectorCanvas({
|
|
|
349
437
|
strokeDasharray: "2 2"
|
|
350
438
|
}
|
|
351
439
|
),
|
|
352
|
-
overlay.cutPoint && /* @__PURE__ */ (0,
|
|
353
|
-
overlay.marquee && /* @__PURE__ */ (0,
|
|
440
|
+
overlay.cutPoint && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("circle", { "data-vector-cut-point": "", cx: overlay.cutPoint.x, cy: overlay.cutPoint.y, r: import_vector3.VECTOR_OVERLAY_THEME.handleRadius + 1, fill: import_vector3.VECTOR_OVERLAY_THEME.handleFill, stroke: accentColor }),
|
|
441
|
+
overlay.marquee && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
354
442
|
"rect",
|
|
355
443
|
{
|
|
356
444
|
"data-vector-marquee": "",
|
|
@@ -358,13 +446,13 @@ function VectorCanvas({
|
|
|
358
446
|
y: overlay.marquee.y,
|
|
359
447
|
width: overlay.marquee.width,
|
|
360
448
|
height: overlay.marquee.height,
|
|
361
|
-
fill:
|
|
449
|
+
fill: import_vector3.VECTOR_OVERLAY_THEME.marqueeFill,
|
|
362
450
|
stroke: accentColor,
|
|
363
451
|
strokeDasharray: "3 2"
|
|
364
452
|
}
|
|
365
453
|
),
|
|
366
|
-
overlay.guides.lines.length > 0 && /* @__PURE__ */ (0,
|
|
367
|
-
overlay.guides.labels.map((l, i) => /* @__PURE__ */ (0,
|
|
454
|
+
overlay.guides.lines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("g", { "data-vector-guides": "", stroke: guideColor, children: overlay.guides.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("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)) }),
|
|
455
|
+
overlay.guides.labels.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
368
456
|
"text",
|
|
369
457
|
{
|
|
370
458
|
"data-vector-guide-label": l.kind,
|
|
@@ -380,46 +468,48 @@ function VectorCanvas({
|
|
|
380
468
|
},
|
|
381
469
|
i
|
|
382
470
|
)),
|
|
383
|
-
overlay.status && /* @__PURE__ */ (0,
|
|
384
|
-
] })
|
|
471
|
+
overlay.status && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
|
|
472
|
+
] }),
|
|
473
|
+
edit && editAt && // Width Point Edit (double-click with the width tool), beside the point.
|
|
474
|
+
/* @__PURE__ */ (0, import_jsx_runtime2.jsx)("foreignObject", { "data-vector-width-point-edit": "", x: editAt.x + 12, y: editAt.y + 12, width: 180, height: 140, style: { overflow: "visible" }, children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(VectorWidthPointEditor, { controller, edit, accentColor }, `${edit.nodeId}:${edit.index}:${edit.t}`) })
|
|
385
475
|
]
|
|
386
476
|
}
|
|
387
477
|
);
|
|
388
478
|
}
|
|
389
479
|
|
|
390
480
|
// src/vector/VectorToolbar.tsx
|
|
391
|
-
var
|
|
392
|
-
var
|
|
481
|
+
var import_vector4 = require("@pixerate/editor/vector");
|
|
482
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
393
483
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
394
484
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
395
485
|
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";
|
|
396
|
-
function VectorToolbar({ controller, tools =
|
|
486
|
+
function VectorToolbar({ controller, tools = import_vector4.VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, showPathEdit = true, className }) {
|
|
397
487
|
const state = useVectorEditorState(controller);
|
|
398
488
|
const count = state.selection.length;
|
|
399
489
|
const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
|
|
400
490
|
const editing = state.editing;
|
|
401
491
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
402
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0,
|
|
403
|
-
return /* @__PURE__ */ (0,
|
|
404
|
-
/* @__PURE__ */ (0,
|
|
492
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("button", { type: "button", className: buttonClass, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
493
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
494
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
405
495
|
"button",
|
|
406
496
|
{
|
|
407
497
|
type: "button",
|
|
408
498
|
"data-vector-tool": tool,
|
|
409
499
|
className: buttonClass,
|
|
410
500
|
"aria-pressed": state.tool === tool,
|
|
411
|
-
title: `${(0,
|
|
412
|
-
"aria-label": (0,
|
|
501
|
+
title: `${(0, import_vector4.vectorToolLabel)(tool)} (${import_vector4.VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
502
|
+
"aria-label": (0, import_vector4.vectorToolLabel)(tool),
|
|
413
503
|
onClick: () => controller.setTool(tool),
|
|
414
|
-
children:
|
|
504
|
+
children: import_vector4.VECTOR_TOOL_GLYPHS[tool]
|
|
415
505
|
},
|
|
416
506
|
tool
|
|
417
507
|
)) }),
|
|
418
|
-
showHistory && /* @__PURE__ */ (0,
|
|
508
|
+
showHistory && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
419
509
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
420
510
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
421
511
|
] }),
|
|
422
|
-
showSnap && /* @__PURE__ */ (0,
|
|
512
|
+
showSnap && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
423
513
|
"button",
|
|
424
514
|
{
|
|
425
515
|
type: "button",
|
|
@@ -432,8 +522,8 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
432
522
|
children: "Snap"
|
|
433
523
|
}
|
|
434
524
|
) }),
|
|
435
|
-
showPathEdit && /* @__PURE__ */ (0,
|
|
436
|
-
|
|
525
|
+
showPathEdit && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
526
|
+
import_vector4.VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
437
527
|
"button",
|
|
438
528
|
{
|
|
439
529
|
type: "button",
|
|
@@ -447,7 +537,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
447
537
|
},
|
|
448
538
|
a.id
|
|
449
539
|
)),
|
|
450
|
-
|
|
540
|
+
import_vector4.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
451
541
|
"button",
|
|
452
542
|
{
|
|
453
543
|
type: "button",
|
|
@@ -461,9 +551,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
461
551
|
},
|
|
462
552
|
t.key
|
|
463
553
|
)),
|
|
464
|
-
state.tool === "pencil" && /* @__PURE__ */ (0,
|
|
554
|
+
state.tool === "pencil" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
465
555
|
"Smoothing",
|
|
466
|
-
/* @__PURE__ */ (0,
|
|
556
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
467
557
|
"input",
|
|
468
558
|
{
|
|
469
559
|
type: "range",
|
|
@@ -477,9 +567,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
477
567
|
}
|
|
478
568
|
)
|
|
479
569
|
] }),
|
|
480
|
-
state.tool === "eraser" && /* @__PURE__ */ (0,
|
|
570
|
+
state.tool === "eraser" && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
481
571
|
"Size",
|
|
482
|
-
/* @__PURE__ */ (0,
|
|
572
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
483
573
|
"input",
|
|
484
574
|
{
|
|
485
575
|
type: "number",
|
|
@@ -495,7 +585,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
495
585
|
}
|
|
496
586
|
)
|
|
497
587
|
] }),
|
|
498
|
-
state.tool === "paintBucket" && /* @__PURE__ */ (0,
|
|
588
|
+
state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
499
589
|
"button",
|
|
500
590
|
{
|
|
501
591
|
type: "button",
|
|
@@ -504,13 +594,13 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
504
594
|
"aria-pressed": editing.fillGap > 0,
|
|
505
595
|
title: "Close small gaps between path ends when filling",
|
|
506
596
|
"aria-label": "Close gaps",
|
|
507
|
-
onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 :
|
|
597
|
+
onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector4.VECTOR_FILL_GAP }),
|
|
508
598
|
children: "Gaps"
|
|
509
599
|
}
|
|
510
600
|
)
|
|
511
601
|
] }),
|
|
512
|
-
showArrange && /* @__PURE__ */ (0,
|
|
513
|
-
/* @__PURE__ */ (0,
|
|
602
|
+
showArrange && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
603
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
514
604
|
act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
|
|
515
605
|
act("\u2212", "Subtract", () => controller.booleanOp("subtract"), count < 2),
|
|
516
606
|
act("\u2229", "Intersect", () => controller.booleanOp("intersect"), count < 2),
|
|
@@ -518,7 +608,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
518
608
|
act("G", "Group", () => controller.group(), count < 1),
|
|
519
609
|
act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
|
|
520
610
|
] }),
|
|
521
|
-
/* @__PURE__ */ (0,
|
|
611
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: import_vector4.VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
522
612
|
"button",
|
|
523
613
|
{
|
|
524
614
|
type: "button",
|
|
@@ -532,7 +622,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
532
622
|
},
|
|
533
623
|
a.id
|
|
534
624
|
)) }),
|
|
535
|
-
/* @__PURE__ */ (0,
|
|
625
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
536
626
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
537
627
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
538
628
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -549,7 +639,7 @@ var selectClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background
|
|
|
549
639
|
var numberClass = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
550
640
|
function VectorStylePanel({ controller, className }) {
|
|
551
641
|
const state = useVectorEditorState(controller);
|
|
552
|
-
const style = (0,
|
|
642
|
+
const style = (0, import_vector4.getVectorSelectionStyle)(state);
|
|
553
643
|
const d = state.defaults;
|
|
554
644
|
const source = style ?? {
|
|
555
645
|
fill: d.fill ?? null,
|
|
@@ -562,7 +652,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
562
652
|
arrowAlign: d.arrowAlign ?? "extend",
|
|
563
653
|
startArrowScale: d.startArrowScale ?? 1,
|
|
564
654
|
endArrowScale: d.endArrowScale ?? 1,
|
|
565
|
-
widthProfile: (0,
|
|
655
|
+
widthProfile: (0, import_vector4.widthProfilePresetId)(d.strokeWidthProfile),
|
|
566
656
|
cornerRadius: d.cornerRadius,
|
|
567
657
|
cornerKind: d.cornerKind ?? "round",
|
|
568
658
|
corners: 1,
|
|
@@ -575,19 +665,19 @@ function VectorStylePanel({ controller, className }) {
|
|
|
575
665
|
};
|
|
576
666
|
const setProfile = (id) => {
|
|
577
667
|
if (style) controller.setWidthProfile(id);
|
|
578
|
-
else controller.setDefaults({ strokeWidthProfile: (0,
|
|
668
|
+
else controller.setDefaults({ strokeWidthProfile: (0, import_vector4.widthProfilePreset)(id)?.length ? (0, import_vector4.widthProfilePreset)(id) : null });
|
|
579
669
|
};
|
|
580
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0,
|
|
670
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
581
671
|
const arrowRow = (end, label) => {
|
|
582
672
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
583
673
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
584
|
-
return /* @__PURE__ */ (0,
|
|
585
|
-
/* @__PURE__ */ (0,
|
|
586
|
-
/* @__PURE__ */ (0,
|
|
587
|
-
/* @__PURE__ */ (0,
|
|
588
|
-
|
|
674
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
675
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
676
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { "aria-label": `${label} arrowhead`, className: selectClass, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
677
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "", children: "None" }),
|
|
678
|
+
import_vector4.VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: h, children: import_vector4.VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
589
679
|
] }),
|
|
590
|
-
/* @__PURE__ */ (0,
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
591
681
|
"input",
|
|
592
682
|
{
|
|
593
683
|
type: "number",
|
|
@@ -611,14 +701,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
611
701
|
};
|
|
612
702
|
const paintRow = (key, label) => {
|
|
613
703
|
const paint = source[key];
|
|
614
|
-
const hex = (0,
|
|
704
|
+
const hex = (0, import_vector4.vectorPaintToHex)(paint);
|
|
615
705
|
const on = paint !== null && paint !== "none";
|
|
616
|
-
return /* @__PURE__ */ (0,
|
|
617
|
-
/* @__PURE__ */ (0,
|
|
618
|
-
/* @__PURE__ */ (0,
|
|
706
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
707
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
708
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
619
709
|
label
|
|
620
710
|
] }),
|
|
621
|
-
/* @__PURE__ */ (0,
|
|
711
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
622
712
|
"input",
|
|
623
713
|
{
|
|
624
714
|
type: "color",
|
|
@@ -629,17 +719,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
629
719
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
630
720
|
}
|
|
631
721
|
),
|
|
632
|
-
on && !hex && /* @__PURE__ */ (0,
|
|
633
|
-
style?.mixed[key] && /* @__PURE__ */ (0,
|
|
722
|
+
on && !hex && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
723
|
+
style?.mixed[key] && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
634
724
|
] });
|
|
635
725
|
};
|
|
636
|
-
return /* @__PURE__ */ (0,
|
|
637
|
-
/* @__PURE__ */ (0,
|
|
726
|
+
return /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
727
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
638
728
|
paintRow("fill", "Fill"),
|
|
639
729
|
paintRow("stroke", "Stroke"),
|
|
640
|
-
/* @__PURE__ */ (0,
|
|
641
|
-
/* @__PURE__ */ (0,
|
|
642
|
-
/* @__PURE__ */ (0,
|
|
730
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
731
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Width" }),
|
|
732
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
643
733
|
"input",
|
|
644
734
|
{
|
|
645
735
|
type: "number",
|
|
@@ -655,14 +745,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
655
745
|
}
|
|
656
746
|
)
|
|
657
747
|
] }),
|
|
658
|
-
/* @__PURE__ */ (0,
|
|
659
|
-
/* @__PURE__ */ (0,
|
|
660
|
-
/* @__PURE__ */ (0,
|
|
748
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
749
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Align" }),
|
|
750
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { "aria-label": "Stroke alignment", className: selectClass, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(import_vector4.VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: a, children: import_vector4.VECTOR_STROKE_ALIGN_LABELS[a] }, a)) }),
|
|
661
751
|
mixed("strokeAlign")
|
|
662
752
|
] }),
|
|
663
|
-
/* @__PURE__ */ (0,
|
|
664
|
-
/* @__PURE__ */ (0,
|
|
665
|
-
/* @__PURE__ */ (0,
|
|
753
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
754
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Profile" }),
|
|
755
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
|
|
666
756
|
"select",
|
|
667
757
|
{
|
|
668
758
|
"aria-label": "Width profile",
|
|
@@ -672,12 +762,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
672
762
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
673
763
|
},
|
|
674
764
|
children: [
|
|
675
|
-
|
|
676
|
-
source.widthProfile === "custom" && /* @__PURE__ */ (0,
|
|
765
|
+
import_vector4.VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: p.id, children: p.label }, p.id)),
|
|
766
|
+
source.widthProfile === "custom" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "custom", disabled: true, children: "Custom" })
|
|
677
767
|
]
|
|
678
768
|
}
|
|
679
769
|
),
|
|
680
|
-
/* @__PURE__ */ (0,
|
|
770
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
681
771
|
"button",
|
|
682
772
|
{
|
|
683
773
|
type: "button",
|
|
@@ -691,20 +781,20 @@ function VectorStylePanel({ controller, className }) {
|
|
|
691
781
|
),
|
|
692
782
|
mixed("widthProfile")
|
|
693
783
|
] }),
|
|
694
|
-
source.hasOpenPath && /* @__PURE__ */ (0,
|
|
784
|
+
source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
695
785
|
arrowRow("start", "Start"),
|
|
696
786
|
arrowRow("end", "End"),
|
|
697
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */ (0,
|
|
698
|
-
/* @__PURE__ */ (0,
|
|
699
|
-
/* @__PURE__ */ (0,
|
|
700
|
-
/* @__PURE__ */ (0,
|
|
701
|
-
/* @__PURE__ */ (0,
|
|
787
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
788
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Heads" }),
|
|
789
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("select", { "aria-label": "Arrowhead alignment", className: selectClass, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
790
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "extend", children: "Tip past end" }),
|
|
791
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: "tip", children: "Tip at end" })
|
|
702
792
|
] })
|
|
703
793
|
] })
|
|
704
794
|
] }),
|
|
705
|
-
source.corners > 0 && /* @__PURE__ */ (0,
|
|
706
|
-
/* @__PURE__ */ (0,
|
|
707
|
-
/* @__PURE__ */ (0,
|
|
795
|
+
source.corners > 0 && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
796
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
797
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
708
798
|
"input",
|
|
709
799
|
{
|
|
710
800
|
type: "number",
|
|
@@ -719,12 +809,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
719
809
|
}
|
|
720
810
|
}
|
|
721
811
|
),
|
|
722
|
-
/* @__PURE__ */ (0,
|
|
812
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("select", { "aria-label": "Corner kind", className: selectClass, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: import_vector4.VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("option", { value: k, children: import_vector4.VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
723
813
|
mixed("cornerRadius")
|
|
724
814
|
] }),
|
|
725
|
-
style && /* @__PURE__ */ (0,
|
|
726
|
-
/* @__PURE__ */ (0,
|
|
727
|
-
/* @__PURE__ */ (0,
|
|
815
|
+
style && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
816
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: "w-16", children: "Opacity" }),
|
|
817
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
728
818
|
"input",
|
|
729
819
|
{
|
|
730
820
|
type: "range",
|
|
@@ -742,13 +832,13 @@ function VectorStylePanel({ controller, className }) {
|
|
|
742
832
|
}
|
|
743
833
|
|
|
744
834
|
// src/vector/VectorEditor.tsx
|
|
745
|
-
var
|
|
835
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
746
836
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
747
|
-
return /* @__PURE__ */ (0,
|
|
748
|
-
/* @__PURE__ */ (0,
|
|
749
|
-
/* @__PURE__ */ (0,
|
|
750
|
-
/* @__PURE__ */ (0,
|
|
751
|
-
showStylePanel && /* @__PURE__ */ (0,
|
|
837
|
+
return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
838
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VectorToolbar, { controller, tools }),
|
|
839
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
840
|
+
/* @__PURE__ */ (0, import_jsx_runtime4.jsx)("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VectorCanvas, { controller, ...canvasProps }) }),
|
|
841
|
+
showStylePanel && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
|
|
752
842
|
] })
|
|
753
843
|
] });
|
|
754
844
|
}
|
|
@@ -760,6 +850,7 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
|
|
|
760
850
|
VectorSceneNodes,
|
|
761
851
|
VectorStylePanel,
|
|
762
852
|
VectorToolbar,
|
|
853
|
+
VectorWidthPointEditor,
|
|
763
854
|
useVectorCommands,
|
|
764
855
|
useVectorEditor,
|
|
765
856
|
useVectorEditorController,
|