@pixerate/editor-react 0.28.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 +257 -152
- 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 +237 -133
- 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,70 +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
|
-
overlay.builder.
|
|
312
|
-
|
|
399
|
+
overlay.builder.hatches.map(
|
|
400
|
+
(d, i) => d ? /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
401
|
+
"path",
|
|
402
|
+
{
|
|
403
|
+
"data-vector-builder-hatch": "",
|
|
404
|
+
d,
|
|
405
|
+
fill: "none",
|
|
406
|
+
stroke: overlay.builder.mode === "erase" ? import_vector3.VECTOR_OVERLAY_THEME.builderErase : accentColor,
|
|
407
|
+
strokeOpacity: import_vector3.VECTOR_OVERLAY_THEME.builderHatchOpacity,
|
|
408
|
+
strokeWidth: 1
|
|
409
|
+
},
|
|
410
|
+
`h${i}`
|
|
411
|
+
) : null
|
|
412
|
+
),
|
|
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 })
|
|
313
415
|
] }),
|
|
314
|
-
overlay.toolStroke && /* @__PURE__ */ (0,
|
|
416
|
+
overlay.toolStroke && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
315
417
|
"path",
|
|
316
418
|
{
|
|
317
419
|
"data-vector-tool-stroke": overlay.toolStroke.tool,
|
|
318
420
|
d: overlay.toolStroke.d,
|
|
319
|
-
stroke: overlay.toolStroke.tool === "eraser" ?
|
|
421
|
+
stroke: overlay.toolStroke.tool === "eraser" ? import_vector3.VECTOR_OVERLAY_THEME.eraserFill : accentColor,
|
|
320
422
|
strokeWidth: overlay.toolStroke.tool === "eraser" ? overlay.toolStroke.width : 1,
|
|
321
423
|
strokeLinecap: "round",
|
|
322
424
|
strokeLinejoin: "round",
|
|
323
425
|
strokeDasharray: overlay.toolStroke.tool === "knife" ? "5 3" : void 0
|
|
324
426
|
}
|
|
325
427
|
),
|
|
326
|
-
overlay.pencilString && /* @__PURE__ */ (0,
|
|
428
|
+
overlay.pencilString && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
327
429
|
"line",
|
|
328
430
|
{
|
|
329
431
|
"data-vector-pencil-string": "",
|
|
@@ -335,8 +437,8 @@ function VectorCanvas({
|
|
|
335
437
|
strokeDasharray: "2 2"
|
|
336
438
|
}
|
|
337
439
|
),
|
|
338
|
-
overlay.cutPoint && /* @__PURE__ */ (0,
|
|
339
|
-
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)(
|
|
340
442
|
"rect",
|
|
341
443
|
{
|
|
342
444
|
"data-vector-marquee": "",
|
|
@@ -344,13 +446,13 @@ function VectorCanvas({
|
|
|
344
446
|
y: overlay.marquee.y,
|
|
345
447
|
width: overlay.marquee.width,
|
|
346
448
|
height: overlay.marquee.height,
|
|
347
|
-
fill:
|
|
449
|
+
fill: import_vector3.VECTOR_OVERLAY_THEME.marqueeFill,
|
|
348
450
|
stroke: accentColor,
|
|
349
451
|
strokeDasharray: "3 2"
|
|
350
452
|
}
|
|
351
453
|
),
|
|
352
|
-
overlay.guides.lines.length > 0 && /* @__PURE__ */ (0,
|
|
353
|
-
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)(
|
|
354
456
|
"text",
|
|
355
457
|
{
|
|
356
458
|
"data-vector-guide-label": l.kind,
|
|
@@ -366,46 +468,48 @@ function VectorCanvas({
|
|
|
366
468
|
},
|
|
367
469
|
i
|
|
368
470
|
)),
|
|
369
|
-
overlay.status && /* @__PURE__ */ (0,
|
|
370
|
-
] })
|
|
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}`) })
|
|
371
475
|
]
|
|
372
476
|
}
|
|
373
477
|
);
|
|
374
478
|
}
|
|
375
479
|
|
|
376
480
|
// src/vector/VectorToolbar.tsx
|
|
377
|
-
var
|
|
378
|
-
var
|
|
481
|
+
var import_vector4 = require("@pixerate/editor/vector");
|
|
482
|
+
var import_jsx_runtime3 = require("react/jsx-runtime");
|
|
379
483
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
380
484
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
381
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";
|
|
382
|
-
function VectorToolbar({ controller, tools =
|
|
486
|
+
function VectorToolbar({ controller, tools = import_vector4.VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, showPathEdit = true, className }) {
|
|
383
487
|
const state = useVectorEditorState(controller);
|
|
384
488
|
const count = state.selection.length;
|
|
385
489
|
const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
|
|
386
490
|
const editing = state.editing;
|
|
387
491
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
388
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */ (0,
|
|
389
|
-
return /* @__PURE__ */ (0,
|
|
390
|
-
/* @__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)(
|
|
391
495
|
"button",
|
|
392
496
|
{
|
|
393
497
|
type: "button",
|
|
394
498
|
"data-vector-tool": tool,
|
|
395
499
|
className: buttonClass,
|
|
396
500
|
"aria-pressed": state.tool === tool,
|
|
397
|
-
title: `${(0,
|
|
398
|
-
"aria-label": (0,
|
|
501
|
+
title: `${(0, import_vector4.vectorToolLabel)(tool)} (${import_vector4.VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
502
|
+
"aria-label": (0, import_vector4.vectorToolLabel)(tool),
|
|
399
503
|
onClick: () => controller.setTool(tool),
|
|
400
|
-
children:
|
|
504
|
+
children: import_vector4.VECTOR_TOOL_GLYPHS[tool]
|
|
401
505
|
},
|
|
402
506
|
tool
|
|
403
507
|
)) }),
|
|
404
|
-
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: [
|
|
405
509
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
406
510
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
407
511
|
] }),
|
|
408
|
-
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)(
|
|
409
513
|
"button",
|
|
410
514
|
{
|
|
411
515
|
type: "button",
|
|
@@ -418,8 +522,8 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
418
522
|
children: "Snap"
|
|
419
523
|
}
|
|
420
524
|
) }),
|
|
421
|
-
showPathEdit && /* @__PURE__ */ (0,
|
|
422
|
-
|
|
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)(
|
|
423
527
|
"button",
|
|
424
528
|
{
|
|
425
529
|
type: "button",
|
|
@@ -433,7 +537,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
433
537
|
},
|
|
434
538
|
a.id
|
|
435
539
|
)),
|
|
436
|
-
|
|
540
|
+
import_vector4.VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
437
541
|
"button",
|
|
438
542
|
{
|
|
439
543
|
type: "button",
|
|
@@ -447,9 +551,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
447
551
|
},
|
|
448
552
|
t.key
|
|
449
553
|
)),
|
|
450
|
-
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: [
|
|
451
555
|
"Smoothing",
|
|
452
|
-
/* @__PURE__ */ (0,
|
|
556
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
453
557
|
"input",
|
|
454
558
|
{
|
|
455
559
|
type: "range",
|
|
@@ -463,9 +567,9 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
463
567
|
}
|
|
464
568
|
)
|
|
465
569
|
] }),
|
|
466
|
-
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: [
|
|
467
571
|
"Size",
|
|
468
|
-
/* @__PURE__ */ (0,
|
|
572
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
469
573
|
"input",
|
|
470
574
|
{
|
|
471
575
|
type: "number",
|
|
@@ -481,7 +585,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
481
585
|
}
|
|
482
586
|
)
|
|
483
587
|
] }),
|
|
484
|
-
state.tool === "paintBucket" && /* @__PURE__ */ (0,
|
|
588
|
+
state.tool === "paintBucket" && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
485
589
|
"button",
|
|
486
590
|
{
|
|
487
591
|
type: "button",
|
|
@@ -490,13 +594,13 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
490
594
|
"aria-pressed": editing.fillGap > 0,
|
|
491
595
|
title: "Close small gaps between path ends when filling",
|
|
492
596
|
"aria-label": "Close gaps",
|
|
493
|
-
onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 :
|
|
597
|
+
onClick: () => controller.setEditing({ fillGap: editing.fillGap > 0 ? 0 : import_vector4.VECTOR_FILL_GAP }),
|
|
494
598
|
children: "Gaps"
|
|
495
599
|
}
|
|
496
600
|
)
|
|
497
601
|
] }),
|
|
498
|
-
showArrange && /* @__PURE__ */ (0,
|
|
499
|
-
/* @__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: [
|
|
500
604
|
act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
|
|
501
605
|
act("\u2212", "Subtract", () => controller.booleanOp("subtract"), count < 2),
|
|
502
606
|
act("\u2229", "Intersect", () => controller.booleanOp("intersect"), count < 2),
|
|
@@ -504,7 +608,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
504
608
|
act("G", "Group", () => controller.group(), count < 1),
|
|
505
609
|
act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
|
|
506
610
|
] }),
|
|
507
|
-
/* @__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)(
|
|
508
612
|
"button",
|
|
509
613
|
{
|
|
510
614
|
type: "button",
|
|
@@ -518,7 +622,7 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
518
622
|
},
|
|
519
623
|
a.id
|
|
520
624
|
)) }),
|
|
521
|
-
/* @__PURE__ */ (0,
|
|
625
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
522
626
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
523
627
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
524
628
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -535,7 +639,7 @@ var selectClass = "h-7 min-w-0 flex-1 rounded border border-border bg-background
|
|
|
535
639
|
var numberClass = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
536
640
|
function VectorStylePanel({ controller, className }) {
|
|
537
641
|
const state = useVectorEditorState(controller);
|
|
538
|
-
const style = (0,
|
|
642
|
+
const style = (0, import_vector4.getVectorSelectionStyle)(state);
|
|
539
643
|
const d = state.defaults;
|
|
540
644
|
const source = style ?? {
|
|
541
645
|
fill: d.fill ?? null,
|
|
@@ -548,7 +652,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
548
652
|
arrowAlign: d.arrowAlign ?? "extend",
|
|
549
653
|
startArrowScale: d.startArrowScale ?? 1,
|
|
550
654
|
endArrowScale: d.endArrowScale ?? 1,
|
|
551
|
-
widthProfile: (0,
|
|
655
|
+
widthProfile: (0, import_vector4.widthProfilePresetId)(d.strokeWidthProfile),
|
|
552
656
|
cornerRadius: d.cornerRadius,
|
|
553
657
|
cornerKind: d.cornerKind ?? "round",
|
|
554
658
|
corners: 1,
|
|
@@ -561,19 +665,19 @@ function VectorStylePanel({ controller, className }) {
|
|
|
561
665
|
};
|
|
562
666
|
const setProfile = (id) => {
|
|
563
667
|
if (style) controller.setWidthProfile(id);
|
|
564
|
-
else controller.setDefaults({ strokeWidthProfile: (0,
|
|
668
|
+
else controller.setDefaults({ strokeWidthProfile: (0, import_vector4.widthProfilePreset)(id)?.length ? (0, import_vector4.widthProfilePreset)(id) : null });
|
|
565
669
|
};
|
|
566
|
-
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;
|
|
567
671
|
const arrowRow = (end, label) => {
|
|
568
672
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
569
673
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
570
|
-
return /* @__PURE__ */ (0,
|
|
571
|
-
/* @__PURE__ */ (0,
|
|
572
|
-
/* @__PURE__ */ (0,
|
|
573
|
-
/* @__PURE__ */ (0,
|
|
574
|
-
|
|
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))
|
|
575
679
|
] }),
|
|
576
|
-
/* @__PURE__ */ (0,
|
|
680
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
577
681
|
"input",
|
|
578
682
|
{
|
|
579
683
|
type: "number",
|
|
@@ -597,14 +701,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
597
701
|
};
|
|
598
702
|
const paintRow = (key, label) => {
|
|
599
703
|
const paint = source[key];
|
|
600
|
-
const hex = (0,
|
|
704
|
+
const hex = (0, import_vector4.vectorPaintToHex)(paint);
|
|
601
705
|
const on = paint !== null && paint !== "none";
|
|
602
|
-
return /* @__PURE__ */ (0,
|
|
603
|
-
/* @__PURE__ */ (0,
|
|
604
|
-
/* @__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 }) }),
|
|
605
709
|
label
|
|
606
710
|
] }),
|
|
607
|
-
/* @__PURE__ */ (0,
|
|
711
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
608
712
|
"input",
|
|
609
713
|
{
|
|
610
714
|
type: "color",
|
|
@@ -615,17 +719,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
615
719
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
616
720
|
}
|
|
617
721
|
),
|
|
618
|
-
on && !hex && /* @__PURE__ */ (0,
|
|
619
|
-
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" })
|
|
620
724
|
] });
|
|
621
725
|
};
|
|
622
|
-
return /* @__PURE__ */ (0,
|
|
623
|
-
/* @__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" }),
|
|
624
728
|
paintRow("fill", "Fill"),
|
|
625
729
|
paintRow("stroke", "Stroke"),
|
|
626
|
-
/* @__PURE__ */ (0,
|
|
627
|
-
/* @__PURE__ */ (0,
|
|
628
|
-
/* @__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)(
|
|
629
733
|
"input",
|
|
630
734
|
{
|
|
631
735
|
type: "number",
|
|
@@ -641,14 +745,14 @@ function VectorStylePanel({ controller, className }) {
|
|
|
641
745
|
}
|
|
642
746
|
)
|
|
643
747
|
] }),
|
|
644
|
-
/* @__PURE__ */ (0,
|
|
645
|
-
/* @__PURE__ */ (0,
|
|
646
|
-
/* @__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)) }),
|
|
647
751
|
mixed("strokeAlign")
|
|
648
752
|
] }),
|
|
649
|
-
/* @__PURE__ */ (0,
|
|
650
|
-
/* @__PURE__ */ (0,
|
|
651
|
-
/* @__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)(
|
|
652
756
|
"select",
|
|
653
757
|
{
|
|
654
758
|
"aria-label": "Width profile",
|
|
@@ -658,12 +762,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
658
762
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
659
763
|
},
|
|
660
764
|
children: [
|
|
661
|
-
|
|
662
|
-
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" })
|
|
663
767
|
]
|
|
664
768
|
}
|
|
665
769
|
),
|
|
666
|
-
/* @__PURE__ */ (0,
|
|
770
|
+
/* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
|
|
667
771
|
"button",
|
|
668
772
|
{
|
|
669
773
|
type: "button",
|
|
@@ -677,20 +781,20 @@ function VectorStylePanel({ controller, className }) {
|
|
|
677
781
|
),
|
|
678
782
|
mixed("widthProfile")
|
|
679
783
|
] }),
|
|
680
|
-
source.hasOpenPath && /* @__PURE__ */ (0,
|
|
784
|
+
source.hasOpenPath && /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(import_jsx_runtime3.Fragment, { children: [
|
|
681
785
|
arrowRow("start", "Start"),
|
|
682
786
|
arrowRow("end", "End"),
|
|
683
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */ (0,
|
|
684
|
-
/* @__PURE__ */ (0,
|
|
685
|
-
/* @__PURE__ */ (0,
|
|
686
|
-
/* @__PURE__ */ (0,
|
|
687
|
-
/* @__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" })
|
|
688
792
|
] })
|
|
689
793
|
] })
|
|
690
794
|
] }),
|
|
691
|
-
source.corners > 0 && /* @__PURE__ */ (0,
|
|
692
|
-
/* @__PURE__ */ (0,
|
|
693
|
-
/* @__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)(
|
|
694
798
|
"input",
|
|
695
799
|
{
|
|
696
800
|
type: "number",
|
|
@@ -705,12 +809,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
705
809
|
}
|
|
706
810
|
}
|
|
707
811
|
),
|
|
708
|
-
/* @__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)) }),
|
|
709
813
|
mixed("cornerRadius")
|
|
710
814
|
] }),
|
|
711
|
-
style && /* @__PURE__ */ (0,
|
|
712
|
-
/* @__PURE__ */ (0,
|
|
713
|
-
/* @__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)(
|
|
714
818
|
"input",
|
|
715
819
|
{
|
|
716
820
|
type: "range",
|
|
@@ -728,13 +832,13 @@ function VectorStylePanel({ controller, className }) {
|
|
|
728
832
|
}
|
|
729
833
|
|
|
730
834
|
// src/vector/VectorEditor.tsx
|
|
731
|
-
var
|
|
835
|
+
var import_jsx_runtime4 = require("react/jsx-runtime");
|
|
732
836
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
733
|
-
return /* @__PURE__ */ (0,
|
|
734
|
-
/* @__PURE__ */ (0,
|
|
735
|
-
/* @__PURE__ */ (0,
|
|
736
|
-
/* @__PURE__ */ (0,
|
|
737
|
-
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" })
|
|
738
842
|
] })
|
|
739
843
|
] });
|
|
740
844
|
}
|
|
@@ -746,6 +850,7 @@ function VectorEditor({ controller, tools, showStylePanel = true, height = 520,
|
|
|
746
850
|
VectorSceneNodes,
|
|
747
851
|
VectorStylePanel,
|
|
748
852
|
VectorToolbar,
|
|
853
|
+
VectorWidthPointEditor,
|
|
749
854
|
useVectorCommands,
|
|
750
855
|
useVectorEditor,
|
|
751
856
|
useVectorEditorController,
|