@pixerate/editor-react 0.65.0 → 0.67.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-OSBDCFF4.js → chunk-LCTQWNTL.js} +67 -3
- package/dist/{chunk-OSBDCFF4.js.map → chunk-LCTQWNTL.js.map} +1 -1
- package/dist/image-editor/index.cjs +65 -1
- package/dist/image-editor/index.cjs.map +1 -1
- package/dist/image-editor/index.d.cts +4 -3
- package/dist/image-editor/index.d.ts +4 -3
- package/dist/image-editor/index.js +1 -1
- package/dist/index.cjs +65 -1
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +1 -1
- package/dist/vector/index.cjs +482 -210
- 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 +468 -193
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.js
CHANGED
|
@@ -44,7 +44,7 @@ function useVectorCommands(registry, controller, options = {}) {
|
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
// src/vector/VectorCanvas.tsx
|
|
47
|
-
import { useEffect as useEffect2, useId, useRef as useRef3 } from "react";
|
|
47
|
+
import React2, { useEffect as useEffect2, useId, useRef as useRef3 } from "react";
|
|
48
48
|
import {
|
|
49
49
|
VECTOR_OVERLAY_THEME as THEME2,
|
|
50
50
|
getVectorOverlayRenderData,
|
|
@@ -185,37 +185,95 @@ function VectorGradientAnnotator({ annotator: g, accentColor = THEME2.accent })
|
|
|
185
185
|
] }, s.index))
|
|
186
186
|
] });
|
|
187
187
|
}
|
|
188
|
+
function VectorFilterDefs({ filters }) {
|
|
189
|
+
if (!filters.length) return null;
|
|
190
|
+
const primitive = (p, key) => React2.createElement(p.tag, { key, ...p.attrs }, p.children?.map(primitive));
|
|
191
|
+
return /* @__PURE__ */ jsx2("defs", { children: filters.map((f) => /* @__PURE__ */ jsx2("filter", { id: f.id, filterUnits: "userSpaceOnUse", x: f.x, y: f.y, width: f.width, height: f.height, colorInterpolationFilters: "sRGB", children: f.primitives.map(primitive) }, f.id)) });
|
|
192
|
+
}
|
|
193
|
+
var blendStyle = (blendMode) => blendMode ? { mixBlendMode: blendMode } : void 0;
|
|
188
194
|
function VectorSceneNodes({ nodes }) {
|
|
189
195
|
return /* @__PURE__ */ jsx2(Fragment, { children: nodes.map(
|
|
190
|
-
(node) => node.kind === "group" ? /* @__PURE__ */ jsx2(
|
|
191
|
-
"
|
|
196
|
+
(node) => node.kind === "group" ? /* @__PURE__ */ jsx2(
|
|
197
|
+
"g",
|
|
192
198
|
{
|
|
193
199
|
"data-vector-id": node.id,
|
|
194
200
|
transform: node.transform,
|
|
201
|
+
opacity: node.opacity < 1 ? node.opacity : void 0,
|
|
202
|
+
filter: node.filter,
|
|
203
|
+
style: blendStyle(node.blendMode),
|
|
204
|
+
children: /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: node.children })
|
|
205
|
+
},
|
|
206
|
+
node.id
|
|
207
|
+
) : node.layers ? (
|
|
208
|
+
// Fill and stroke stacks: each layer in order, bottom to top.
|
|
209
|
+
/* @__PURE__ */ jsx2(
|
|
210
|
+
"g",
|
|
211
|
+
{
|
|
212
|
+
"data-vector-id": node.id,
|
|
213
|
+
transform: node.transform,
|
|
214
|
+
opacity: node.opacity < 1 ? node.opacity : void 0,
|
|
215
|
+
filter: node.filter,
|
|
216
|
+
style: blendStyle(node.blendMode),
|
|
217
|
+
children: node.layers.map((layer) => /* @__PURE__ */ jsx2(VectorPaintPath, { paint: layer, d: node.d, fillRule: node.fillRule, layer: `${layer.kind}-${layer.index}` }, `${layer.kind}-${layer.index}`))
|
|
218
|
+
},
|
|
219
|
+
node.id
|
|
220
|
+
)
|
|
221
|
+
) : /* @__PURE__ */ jsx2(
|
|
222
|
+
VectorPaintPath,
|
|
223
|
+
{
|
|
224
|
+
paint: node,
|
|
195
225
|
d: node.d,
|
|
196
|
-
fill: node.fill,
|
|
197
226
|
fillRule: node.fillRule,
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
strokeDasharray: node.strokeDasharray,
|
|
203
|
-
opacity: node.opacity < 1 ? node.opacity : void 0
|
|
227
|
+
id: node.id,
|
|
228
|
+
transform: node.transform,
|
|
229
|
+
filter: node.filter,
|
|
230
|
+
blendMode: node.blendMode
|
|
204
231
|
},
|
|
205
232
|
node.id
|
|
206
233
|
)
|
|
207
234
|
) });
|
|
208
235
|
}
|
|
209
|
-
function
|
|
210
|
-
|
|
236
|
+
function VectorPaintPath({
|
|
237
|
+
paint,
|
|
238
|
+
d,
|
|
239
|
+
fillRule,
|
|
240
|
+
id,
|
|
241
|
+
transform,
|
|
242
|
+
filter,
|
|
243
|
+
blendMode,
|
|
244
|
+
layer
|
|
245
|
+
}) {
|
|
246
|
+
const opacity = paint.opacity < 1 ? paint.opacity : void 0;
|
|
247
|
+
const style = blendStyle(blendMode ?? (layer ? paint.blendMode : void 0));
|
|
248
|
+
const sp = paint.strokeParts;
|
|
249
|
+
if (!sp)
|
|
250
|
+
return /* @__PURE__ */ jsx2(
|
|
251
|
+
"path",
|
|
252
|
+
{
|
|
253
|
+
"data-vector-id": id,
|
|
254
|
+
"data-vector-layer": layer,
|
|
255
|
+
transform,
|
|
256
|
+
d,
|
|
257
|
+
fill: paint.fill,
|
|
258
|
+
fillRule,
|
|
259
|
+
stroke: paint.stroke,
|
|
260
|
+
strokeWidth: paint.stroke === "none" ? void 0 : paint.strokeWidth,
|
|
261
|
+
strokeLinecap: paint.strokeLinecap,
|
|
262
|
+
strokeLinejoin: paint.strokeLinejoin,
|
|
263
|
+
strokeDasharray: paint.strokeDasharray,
|
|
264
|
+
opacity,
|
|
265
|
+
filter,
|
|
266
|
+
style
|
|
267
|
+
}
|
|
268
|
+
);
|
|
211
269
|
const clip = sp.clip;
|
|
212
270
|
const b = clip?.bounds;
|
|
213
|
-
return /* @__PURE__ */ jsxs2("g", { "data-vector-id":
|
|
214
|
-
clip && b && /* @__PURE__ */ jsx2("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ jsx2("clipPath", { id: clip.id, children: /* @__PURE__ */ jsx2("path", { d
|
|
271
|
+
return /* @__PURE__ */ jsxs2("g", { "data-vector-id": id, "data-vector-layer": layer, "data-vector-stroke-align": sp.align, transform, opacity, filter, style, children: [
|
|
272
|
+
clip && b && /* @__PURE__ */ jsx2("defs", { children: clip.kind === "clip" ? /* @__PURE__ */ jsx2("clipPath", { id: clip.id, children: /* @__PURE__ */ jsx2("path", { d, clipRule: fillRule }) }) : /* @__PURE__ */ jsxs2("mask", { id: clip.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, children: [
|
|
215
273
|
/* @__PURE__ */ jsx2("rect", { x: b.x, y: b.y, width: b.width, height: b.height, fill: "#fff" }),
|
|
216
|
-
/* @__PURE__ */ jsx2("path", { d
|
|
274
|
+
/* @__PURE__ */ jsx2("path", { d, fill: "#000", fillRule })
|
|
217
275
|
] }) }),
|
|
218
|
-
/* @__PURE__ */ jsx2("path", { "data-vector-part": "fill", d
|
|
276
|
+
/* @__PURE__ */ jsx2("path", { "data-vector-part": "fill", d, fill: paint.fill, fillRule, stroke: "none" }),
|
|
219
277
|
sp.outline !== void 0 ? (
|
|
220
278
|
// A variable-width stroke: its outline, filled.
|
|
221
279
|
/* @__PURE__ */ jsx2(
|
|
@@ -223,7 +281,7 @@ function VectorPartedPath({ node }) {
|
|
|
223
281
|
{
|
|
224
282
|
"data-vector-part": "stroke-outline",
|
|
225
283
|
d: sp.outline,
|
|
226
|
-
fill:
|
|
284
|
+
fill: paint.stroke,
|
|
227
285
|
clipPath: clip?.kind === "clip" ? `url(#${clip.id})` : void 0,
|
|
228
286
|
mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
|
|
229
287
|
}
|
|
@@ -234,16 +292,16 @@ function VectorPartedPath({ node }) {
|
|
|
234
292
|
"data-vector-part": "stroke",
|
|
235
293
|
d: sp.d,
|
|
236
294
|
fill: "none",
|
|
237
|
-
stroke:
|
|
295
|
+
stroke: paint.stroke,
|
|
238
296
|
strokeWidth: sp.width,
|
|
239
|
-
strokeLinecap:
|
|
240
|
-
strokeLinejoin:
|
|
241
|
-
strokeDasharray:
|
|
297
|
+
strokeLinecap: paint.strokeLinecap,
|
|
298
|
+
strokeLinejoin: paint.strokeLinejoin,
|
|
299
|
+
strokeDasharray: paint.strokeDasharray,
|
|
242
300
|
clipPath: clip?.kind === "clip" ? `url(#${clip.id})` : void 0,
|
|
243
301
|
mask: clip?.kind === "mask" ? `url(#${clip.id})` : void 0
|
|
244
302
|
}
|
|
245
303
|
),
|
|
246
|
-
sp.heads.map((head, i) => /* @__PURE__ */ jsx2("path", { "data-vector-part": "head", d: head, fill:
|
|
304
|
+
sp.heads.map((head, i) => /* @__PURE__ */ jsx2("path", { "data-vector-part": "head", d: head, fill: paint.stroke }, i))
|
|
247
305
|
] });
|
|
248
306
|
}
|
|
249
307
|
function VectorCanvas({
|
|
@@ -349,6 +407,7 @@ function VectorCanvas({
|
|
|
349
407
|
/* @__PURE__ */ jsx2("rect", { x: 8, y: 8, width: 8, height: 8, fill: "#e5e7eb" })
|
|
350
408
|
] }) }),
|
|
351
409
|
/* @__PURE__ */ jsx2(VectorGradientDefs, { defs: scene.defs }),
|
|
410
|
+
/* @__PURE__ */ jsx2(VectorFilterDefs, { filters: scene.filters }),
|
|
352
411
|
/* @__PURE__ */ jsx2(
|
|
353
412
|
"rect",
|
|
354
413
|
{
|
|
@@ -541,7 +600,7 @@ import {
|
|
|
541
600
|
VECTOR_ARROWHEAD_LABELS,
|
|
542
601
|
VECTOR_CORNER_KINDS,
|
|
543
602
|
VECTOR_CORNER_KIND_LABELS,
|
|
544
|
-
VECTOR_STROKE_ALIGN_LABELS,
|
|
603
|
+
VECTOR_STROKE_ALIGN_LABELS as VECTOR_STROKE_ALIGN_LABELS2,
|
|
545
604
|
VECTOR_WIDTH_PRESETS,
|
|
546
605
|
VECTOR_BLEND_SPACINGS,
|
|
547
606
|
VECTOR_BLEND_SPACING_LABELS,
|
|
@@ -697,14 +756,227 @@ function VectorEffectsPanel({ controller, className }) {
|
|
|
697
756
|
] });
|
|
698
757
|
}
|
|
699
758
|
|
|
700
|
-
// src/vector/
|
|
701
|
-
import
|
|
702
|
-
|
|
759
|
+
// src/vector/VectorAppearancePanel.tsx
|
|
760
|
+
import {
|
|
761
|
+
VECTOR_BLEND_MODE_MENU,
|
|
762
|
+
VECTOR_FILTER_MENU,
|
|
763
|
+
VECTOR_STROKE_ALIGN_LABELS,
|
|
764
|
+
getVectorAppearancePanel
|
|
765
|
+
} from "@pixerate/editor/vector";
|
|
703
766
|
import { jsx as jsx4, jsxs as jsxs4 } from "react/jsx-runtime";
|
|
704
767
|
var buttonClass2 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
|
|
705
768
|
var numberClass2 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
706
769
|
var selectClass2 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
770
|
+
var headingClass = "text-[11px] font-medium uppercase tracking-wide text-muted-foreground";
|
|
707
771
|
var labelClass2 = "w-full truncate text-[11px] text-muted-foreground";
|
|
772
|
+
function VectorAppearancePanel({ controller, className }) {
|
|
773
|
+
const state = useVectorEditorState(controller);
|
|
774
|
+
const panel = getVectorAppearancePanel(state);
|
|
775
|
+
if (!panel) return null;
|
|
776
|
+
const end = () => controller.endMergedEdit();
|
|
777
|
+
const ids = panel.pathIds;
|
|
778
|
+
const update = (l, patch, merge = false) => controller.updatePaintLayer(l.kind, l.index, patch, { ids, merge });
|
|
779
|
+
const layerRow = (l, count) => {
|
|
780
|
+
const name = `${l.kind === "fill" ? "Fill" : "Stroke"} ${l.index + 1}`;
|
|
781
|
+
if (l.unknownKind)
|
|
782
|
+
return /* @__PURE__ */ jsxs4("div", { className: "text-[11px] text-muted-foreground", "data-vector-layer": `${l.kind}-${l.index}`, children: [
|
|
783
|
+
name,
|
|
784
|
+
": ",
|
|
785
|
+
l.unknownKind,
|
|
786
|
+
" (not drawn by this version)"
|
|
787
|
+
] }, `${l.kind}-${l.index}`);
|
|
788
|
+
return /* @__PURE__ */ jsxs4("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-layer": `${l.kind}-${l.index}`, "data-vector-layer-editing": l.editing ? "" : void 0, children: [
|
|
789
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
|
|
790
|
+
/* @__PURE__ */ jsx4("input", { type: "checkbox", "aria-label": `${name} visible`, title: "Show", checked: l.visible, onChange: (e) => update(l, { visible: e.target.checked }) }),
|
|
791
|
+
l.gradient ? /* @__PURE__ */ jsx4(
|
|
792
|
+
"button",
|
|
793
|
+
{
|
|
794
|
+
type: "button",
|
|
795
|
+
"aria-label": `${name} gradient`,
|
|
796
|
+
title: "Edit this gradient",
|
|
797
|
+
className: "h-7 w-10 rounded border border-border",
|
|
798
|
+
style: { background: l.swatch },
|
|
799
|
+
onClick: () => controller.setGradientTarget(l.kind, l.index)
|
|
800
|
+
}
|
|
801
|
+
) : /* @__PURE__ */ jsx4(
|
|
802
|
+
"input",
|
|
803
|
+
{
|
|
804
|
+
type: "color",
|
|
805
|
+
"aria-label": `${name} colour`,
|
|
806
|
+
className: "h-7 w-10 cursor-pointer rounded border border-border bg-background",
|
|
807
|
+
value: l.color ?? "#000000",
|
|
808
|
+
onChange: (e) => update(l, { paint: e.target.value }, true),
|
|
809
|
+
onBlur: end
|
|
810
|
+
}
|
|
811
|
+
),
|
|
812
|
+
/* @__PURE__ */ jsx4(
|
|
813
|
+
"input",
|
|
814
|
+
{
|
|
815
|
+
type: "number",
|
|
816
|
+
min: 0,
|
|
817
|
+
max: 100,
|
|
818
|
+
step: 5,
|
|
819
|
+
"aria-label": `${name} opacity`,
|
|
820
|
+
title: "Opacity %",
|
|
821
|
+
className: numberClass2,
|
|
822
|
+
value: Math.round(l.opacity * 100),
|
|
823
|
+
onChange: (e) => {
|
|
824
|
+
const n = parseFloat(e.target.value);
|
|
825
|
+
if (Number.isFinite(n)) update(l, { opacity: Math.min(100, Math.max(0, n)) / 100 }, true);
|
|
826
|
+
},
|
|
827
|
+
onBlur: end
|
|
828
|
+
}
|
|
829
|
+
),
|
|
830
|
+
/* @__PURE__ */ jsx4("select", { "aria-label": `${name} blend mode`, className: selectClass2, value: l.blendMode, onChange: (e) => update(l, { blendMode: e.target.value }), children: VECTOR_BLEND_MODE_MENU.map((m) => /* @__PURE__ */ jsx4("option", { value: m.mode, children: m.label }, m.mode)) })
|
|
831
|
+
] }),
|
|
832
|
+
l.kind === "stroke" && /* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
|
|
833
|
+
/* @__PURE__ */ jsx4(
|
|
834
|
+
"input",
|
|
835
|
+
{
|
|
836
|
+
type: "number",
|
|
837
|
+
min: 0,
|
|
838
|
+
step: 0.5,
|
|
839
|
+
"aria-label": `${name} width`,
|
|
840
|
+
title: "Width",
|
|
841
|
+
className: numberClass2,
|
|
842
|
+
value: l.strokeWidth ?? 1,
|
|
843
|
+
onChange: (e) => {
|
|
844
|
+
const n = parseFloat(e.target.value);
|
|
845
|
+
if (Number.isFinite(n) && n >= 0) update(l, { strokeWidth: n }, true);
|
|
846
|
+
},
|
|
847
|
+
onBlur: end
|
|
848
|
+
}
|
|
849
|
+
),
|
|
850
|
+
/* @__PURE__ */ jsx4("select", { "aria-label": `${name} alignment`, className: selectClass2, value: l.strokeAlign ?? "center", onChange: (e) => update(l, { strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS).map((a) => /* @__PURE__ */ jsx4("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS[a] }, a)) })
|
|
851
|
+
] }),
|
|
852
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
|
|
853
|
+
/* @__PURE__ */ jsx4("span", { className: "flex-1" }),
|
|
854
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Edit gradient", "aria-label": `Edit ${name} gradient`, "aria-pressed": l.editing, onClick: () => controller.setGradientTarget(l.kind, l.index), children: "\u270E" }),
|
|
855
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Move up", "aria-label": `Move ${name} up`, disabled: l.index === count - 1, onClick: () => controller.movePaintLayer(l.kind, l.index, l.index + 1, { ids }), children: "\u2191" }),
|
|
856
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Move down", "aria-label": `Move ${name} down`, disabled: l.index === 0, onClick: () => controller.movePaintLayer(l.kind, l.index, l.index - 1, { ids }), children: "\u2193" }),
|
|
857
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Remove", "aria-label": `Remove ${name}`, onClick: () => controller.removePaintLayer(l.kind, l.index, { ids }), children: "\xD7" })
|
|
858
|
+
] })
|
|
859
|
+
] }, `${l.kind}-${l.index}`);
|
|
860
|
+
};
|
|
861
|
+
const stack = (kind, layers, mixed) => /* @__PURE__ */ jsxs4("div", { className: "space-y-1", "data-vector-stack": kind, children: [
|
|
862
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
863
|
+
/* @__PURE__ */ jsx4("span", { className: `flex-1 ${headingClass}`, children: kind === "fill" ? "Fills" : "Strokes" }),
|
|
864
|
+
mixed && /* @__PURE__ */ jsx4("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }),
|
|
865
|
+
/* @__PURE__ */ jsx4(
|
|
866
|
+
"button",
|
|
867
|
+
{
|
|
868
|
+
type: "button",
|
|
869
|
+
className: buttonClass2,
|
|
870
|
+
title: kind === "fill" ? "Add fill" : "Add stroke",
|
|
871
|
+
"aria-label": kind === "fill" ? "Add fill" : "Add stroke",
|
|
872
|
+
disabled: !ids.length,
|
|
873
|
+
onClick: () => controller.addPaintLayer(kind, {}, { ids }),
|
|
874
|
+
children: "+"
|
|
875
|
+
}
|
|
876
|
+
)
|
|
877
|
+
] }),
|
|
878
|
+
[...layers].reverse().map((l) => layerRow(l, layers.length))
|
|
879
|
+
] });
|
|
880
|
+
const filterNode = panel.filterNodeId;
|
|
881
|
+
const setFilter = (f, key, value, merge) => {
|
|
882
|
+
if (filterNode) controller.updateFilter(filterNode, f.id, { params: { [key]: value } }, { merge });
|
|
883
|
+
};
|
|
884
|
+
const filterParam = (f, spec) => {
|
|
885
|
+
const label = `${f.label} ${spec.label}`;
|
|
886
|
+
if (spec.kind === "color")
|
|
887
|
+
return /* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-filter-param": spec.key, children: [
|
|
888
|
+
/* @__PURE__ */ jsx4("span", { className: "w-14 truncate text-[11px] text-muted-foreground", children: spec.label }),
|
|
889
|
+
/* @__PURE__ */ jsx4(
|
|
890
|
+
"input",
|
|
891
|
+
{
|
|
892
|
+
type: "color",
|
|
893
|
+
"aria-label": label,
|
|
894
|
+
className: "h-7 w-10 cursor-pointer rounded border border-border bg-background",
|
|
895
|
+
value: /^#[0-9a-f]{6}$/i.test(String(f.params[spec.key])) ? String(f.params[spec.key]) : "#000000",
|
|
896
|
+
onChange: (e) => setFilter(f, spec.key, e.target.value, true),
|
|
897
|
+
onBlur: end
|
|
898
|
+
}
|
|
899
|
+
)
|
|
900
|
+
] }, spec.key);
|
|
901
|
+
if (spec.kind !== "number") return null;
|
|
902
|
+
const value = Number(f.params[spec.key] ?? 0);
|
|
903
|
+
const onChange = (e) => {
|
|
904
|
+
const n = parseFloat(e.target.value);
|
|
905
|
+
if (Number.isFinite(n)) setFilter(f, spec.key, n, true);
|
|
906
|
+
};
|
|
907
|
+
return /* @__PURE__ */ jsxs4("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-filter-param": spec.key, children: [
|
|
908
|
+
/* @__PURE__ */ jsx4("span", { className: labelClass2, children: spec.label }),
|
|
909
|
+
spec.slider && /* @__PURE__ */ jsx4(
|
|
910
|
+
"input",
|
|
911
|
+
{
|
|
912
|
+
type: "range",
|
|
913
|
+
min: spec.min,
|
|
914
|
+
max: spec.max,
|
|
915
|
+
step: spec.step,
|
|
916
|
+
"aria-label": `${label} slider`,
|
|
917
|
+
className: "h-4 min-w-0 flex-1 accent-primary",
|
|
918
|
+
value,
|
|
919
|
+
onChange,
|
|
920
|
+
onPointerUp: end,
|
|
921
|
+
onKeyUp: end,
|
|
922
|
+
onBlur: end
|
|
923
|
+
}
|
|
924
|
+
),
|
|
925
|
+
/* @__PURE__ */ jsx4("input", { type: "number", min: spec.min, max: spec.max, step: spec.step, "aria-label": label, className: numberClass2, value: Math.round(value * 1e3) / 1e3, onChange, onBlur: end })
|
|
926
|
+
] }, spec.key);
|
|
927
|
+
};
|
|
928
|
+
return /* @__PURE__ */ jsxs4("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-appearance": "", "aria-label": "Appearance", children: [
|
|
929
|
+
ids.length > 0 && stack("stroke", panel.strokes, panel.mixed.strokes),
|
|
930
|
+
ids.length > 0 && stack("fill", panel.fills, panel.mixed.fills),
|
|
931
|
+
/* @__PURE__ */ jsxs4("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "blendMode", children: [
|
|
932
|
+
/* @__PURE__ */ jsx4("span", { className: "w-16", children: "Blend" }),
|
|
933
|
+
/* @__PURE__ */ jsx4("select", { "aria-label": "Blend mode", className: selectClass2, value: panel.blendMode, onChange: (e) => controller.setBlendMode(e.target.value, { ids: panel.nodeIds }), children: VECTOR_BLEND_MODE_MENU.map((m) => /* @__PURE__ */ jsx4("option", { value: m.mode, children: m.label }, m.mode)) }),
|
|
934
|
+
panel.mixed.blendMode && /* @__PURE__ */ jsx4("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
935
|
+
] }),
|
|
936
|
+
/* @__PURE__ */ jsxs4("div", { className: "space-y-2", "data-vector-filters": "", children: [
|
|
937
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-2", children: [
|
|
938
|
+
/* @__PURE__ */ jsx4("span", { className: `w-16 ${headingClass}`, children: "Filters" }),
|
|
939
|
+
/* @__PURE__ */ jsxs4(
|
|
940
|
+
"select",
|
|
941
|
+
{
|
|
942
|
+
"aria-label": "Add filter",
|
|
943
|
+
className: selectClass2,
|
|
944
|
+
value: "",
|
|
945
|
+
onChange: (e) => {
|
|
946
|
+
if (e.target.value) controller.addFilter(e.target.value, { ids: panel.nodeIds });
|
|
947
|
+
},
|
|
948
|
+
children: [
|
|
949
|
+
/* @__PURE__ */ jsx4("option", { value: "", children: "Add filter\u2026" }),
|
|
950
|
+
VECTOR_FILTER_MENU.map((m) => /* @__PURE__ */ jsx4("option", { value: m.type, children: m.label }, m.type))
|
|
951
|
+
]
|
|
952
|
+
}
|
|
953
|
+
)
|
|
954
|
+
] }),
|
|
955
|
+
!filterNode && /* @__PURE__ */ jsx4("p", { className: "text-[11px] text-muted-foreground", children: "Select one object to edit its filters." }),
|
|
956
|
+
filterNode && panel.filters.map((f, i) => /* @__PURE__ */ jsxs4("div", { className: "space-y-1 rounded border border-border p-1.5", "data-vector-filter": f.id, "data-vector-filter-type": f.type, children: [
|
|
957
|
+
/* @__PURE__ */ jsxs4("div", { className: "flex items-center gap-1", children: [
|
|
958
|
+
/* @__PURE__ */ jsxs4("label", { className: "flex min-w-0 flex-1 items-center gap-1.5 text-xs font-medium text-foreground", children: [
|
|
959
|
+
/* @__PURE__ */ jsx4("input", { type: "checkbox", "aria-label": `${f.label} on`, checked: f.enabled, onChange: (e) => controller.updateFilter(filterNode, f.id, { enabled: e.target.checked }) }),
|
|
960
|
+
/* @__PURE__ */ jsx4("span", { className: "truncate", children: f.label })
|
|
961
|
+
] }),
|
|
962
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Move up", "aria-label": `Move ${f.label} up`, disabled: i === 0, onClick: () => controller.moveFilter(filterNode, f.id, i - 1), children: "\u2191" }),
|
|
963
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Move down", "aria-label": `Move ${f.label} down`, disabled: i === panel.filters.length - 1, onClick: () => controller.moveFilter(filterNode, f.id, i + 1), children: "\u2193" }),
|
|
964
|
+
/* @__PURE__ */ jsx4("button", { type: "button", className: buttonClass2, title: "Remove", "aria-label": `Remove ${f.label}`, onClick: () => controller.removeFilter(filterNode, f.id), children: "\xD7" })
|
|
965
|
+
] }),
|
|
966
|
+
f.specs.map((spec) => filterParam(f, spec))
|
|
967
|
+
] }, f.id))
|
|
968
|
+
] })
|
|
969
|
+
] });
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// src/vector/VectorEnvelopePanel.tsx
|
|
973
|
+
import React3 from "react";
|
|
974
|
+
import { VECTOR_ENVELOPE_STYLE_OPTIONS, getVectorEnvelopePanel } from "@pixerate/editor/vector";
|
|
975
|
+
import { jsx as jsx5, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
976
|
+
var buttonClass3 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40";
|
|
977
|
+
var numberClass3 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
978
|
+
var selectClass3 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
979
|
+
var labelClass3 = "w-full truncate text-[11px] text-muted-foreground";
|
|
708
980
|
var SLIDERS = [
|
|
709
981
|
{ key: "bend", label: "Bend %" },
|
|
710
982
|
{ key: "horizontal", label: "Horizontal distortion %" },
|
|
@@ -727,16 +999,16 @@ function VectorEnvelopePanel({ controller, className }) {
|
|
|
727
999
|
if (env) set({ [key]: n });
|
|
728
1000
|
else setSize({ ...size, [key]: n });
|
|
729
1001
|
};
|
|
730
|
-
return /* @__PURE__ */
|
|
731
|
-
/* @__PURE__ */
|
|
732
|
-
/* @__PURE__ */
|
|
733
|
-
/* @__PURE__ */
|
|
734
|
-
/* @__PURE__ */
|
|
1002
|
+
return /* @__PURE__ */ jsxs5("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-envelope": "", "aria-label": "Envelope", children: [
|
|
1003
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1004
|
+
/* @__PURE__ */ jsx5("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Envelope" }),
|
|
1005
|
+
/* @__PURE__ */ jsx5("button", { type: "button", "data-vector-envelope-action": "warp", className: buttonClass3, title: "Make with Warp", "aria-label": "Make with Warp", disabled: !panel.canMake || !!env, onClick: () => set({ style: "arc", bend: 50 }), children: "Warp" }),
|
|
1006
|
+
/* @__PURE__ */ jsx5(
|
|
735
1007
|
"button",
|
|
736
1008
|
{
|
|
737
1009
|
type: "button",
|
|
738
1010
|
"data-vector-envelope-action": "mesh",
|
|
739
|
-
className:
|
|
1011
|
+
className: buttonClass3,
|
|
740
1012
|
title: "Make with Mesh",
|
|
741
1013
|
"aria-label": "Make with Mesh",
|
|
742
1014
|
disabled: !panel.canMake || !!env,
|
|
@@ -744,12 +1016,12 @@ function VectorEnvelopePanel({ controller, className }) {
|
|
|
744
1016
|
children: "Mesh"
|
|
745
1017
|
}
|
|
746
1018
|
),
|
|
747
|
-
/* @__PURE__ */
|
|
1019
|
+
/* @__PURE__ */ jsx5(
|
|
748
1020
|
"button",
|
|
749
1021
|
{
|
|
750
1022
|
type: "button",
|
|
751
1023
|
"data-vector-envelope-action": "top",
|
|
752
|
-
className:
|
|
1024
|
+
className: buttonClass3,
|
|
753
1025
|
title: "Make with Top Object",
|
|
754
1026
|
"aria-label": "Make with Top Object",
|
|
755
1027
|
disabled: !panel.canMakeWithTopObject,
|
|
@@ -757,12 +1029,12 @@ function VectorEnvelopePanel({ controller, className }) {
|
|
|
757
1029
|
children: "Top"
|
|
758
1030
|
}
|
|
759
1031
|
),
|
|
760
|
-
/* @__PURE__ */
|
|
761
|
-
/* @__PURE__ */
|
|
1032
|
+
/* @__PURE__ */ jsx5("button", { type: "button", "data-vector-envelope-action": "release", className: buttonClass3, title: "Release Envelope", "aria-label": "Release Envelope", disabled: !panel.canRelease, onClick: () => controller.releaseEnvelope(), children: "Release" }),
|
|
1033
|
+
/* @__PURE__ */ jsx5("button", { type: "button", "data-vector-envelope-action": "expand", className: buttonClass3, title: "Expand Envelope", "aria-label": "Expand Envelope", disabled: !panel.canRelease, onClick: () => controller.expandAppearance(), children: "Expand" })
|
|
762
1034
|
] }),
|
|
763
|
-
env && /* @__PURE__ */
|
|
764
|
-
/* @__PURE__ */
|
|
765
|
-
/* @__PURE__ */
|
|
1035
|
+
env && /* @__PURE__ */ jsxs5("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": "style", children: [
|
|
1036
|
+
/* @__PURE__ */ jsx5("span", { className: labelClass3, children: "Warp" }),
|
|
1037
|
+
/* @__PURE__ */ jsx5("select", { "aria-label": "Envelope warp", className: selectClass3, value: env.params.style, onChange: (e) => set({ style: e.target.value }), children: VECTOR_ENVELOPE_STYLE_OPTIONS.map((o) => /* @__PURE__ */ jsx5("option", { value: o.value, children: o.label }, o.value)) })
|
|
766
1038
|
] }),
|
|
767
1039
|
env && env.params.style !== "mesh" && SLIDERS.map((s) => {
|
|
768
1040
|
const value = env.params[s.key];
|
|
@@ -770,9 +1042,9 @@ function VectorEnvelopePanel({ controller, className }) {
|
|
|
770
1042
|
const n = parseFloat(ev.target.value);
|
|
771
1043
|
if (Number.isFinite(n)) set({ [s.key]: n }, true);
|
|
772
1044
|
};
|
|
773
|
-
return /* @__PURE__ */
|
|
774
|
-
/* @__PURE__ */
|
|
775
|
-
/* @__PURE__ */
|
|
1045
|
+
return /* @__PURE__ */ jsxs5("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": s.key, children: [
|
|
1046
|
+
/* @__PURE__ */ jsx5("span", { className: labelClass3, children: s.label }),
|
|
1047
|
+
/* @__PURE__ */ jsx5(
|
|
776
1048
|
"input",
|
|
777
1049
|
{
|
|
778
1050
|
type: "range",
|
|
@@ -788,23 +1060,23 @@ function VectorEnvelopePanel({ controller, className }) {
|
|
|
788
1060
|
onBlur: end
|
|
789
1061
|
}
|
|
790
1062
|
),
|
|
791
|
-
/* @__PURE__ */
|
|
1063
|
+
/* @__PURE__ */ jsx5("input", { type: "number", min: -100, max: 100, step: 1, "aria-label": `Envelope ${s.label}`, className: numberClass3, value, onChange, onBlur: end })
|
|
792
1064
|
] }, s.key);
|
|
793
1065
|
}),
|
|
794
|
-
env && env.params.style !== "mesh" && /* @__PURE__ */
|
|
795
|
-
/* @__PURE__ */
|
|
796
|
-
/* @__PURE__ */
|
|
797
|
-
/* @__PURE__ */
|
|
798
|
-
/* @__PURE__ */
|
|
1066
|
+
env && env.params.style !== "mesh" && /* @__PURE__ */ jsxs5("label", { className: "flex flex-wrap items-center gap-x-2 gap-y-0.5 text-xs text-foreground", "data-vector-envelope-param": "orientation", children: [
|
|
1067
|
+
/* @__PURE__ */ jsx5("span", { className: labelClass3, children: "Orientation" }),
|
|
1068
|
+
/* @__PURE__ */ jsxs5("select", { "aria-label": "Envelope orientation", className: selectClass3, value: env.params.orientation, onChange: (e) => set({ orientation: e.target.value }), children: [
|
|
1069
|
+
/* @__PURE__ */ jsx5("option", { value: "horizontal", children: "Horizontal" }),
|
|
1070
|
+
/* @__PURE__ */ jsx5("option", { value: "vertical", children: "Vertical" })
|
|
799
1071
|
] })
|
|
800
1072
|
] }),
|
|
801
|
-
/* @__PURE__ */
|
|
802
|
-
/* @__PURE__ */
|
|
803
|
-
/* @__PURE__ */
|
|
804
|
-
/* @__PURE__ */
|
|
1073
|
+
/* @__PURE__ */ jsxs5("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-envelope-param": "mesh", children: [
|
|
1074
|
+
/* @__PURE__ */ jsx5("span", { className: "min-w-0 flex-1 truncate text-[11px] text-muted-foreground", children: "Mesh rows \xD7 columns" }),
|
|
1075
|
+
/* @__PURE__ */ jsx5("input", { type: "number", min: 1, max: 16, step: 1, "aria-label": "Envelope mesh rows", className: numberClass3, value: rows, onChange: (e) => setCount("rows", e.target.value) }),
|
|
1076
|
+
/* @__PURE__ */ jsx5("input", { type: "number", min: 1, max: 16, step: 1, "aria-label": "Envelope mesh columns", className: numberClass3, value: columns, onChange: (e) => setCount("columns", e.target.value) })
|
|
805
1077
|
] }),
|
|
806
|
-
panel.count > 1 && !panel.canMakeWithTopObject && /* @__PURE__ */
|
|
807
|
-
env && /* @__PURE__ */
|
|
1078
|
+
panel.count > 1 && !panel.canMakeWithTopObject && /* @__PURE__ */ jsx5("p", { className: "text-[11px] text-muted-foreground", children: "Select one object to edit its envelope." }),
|
|
1079
|
+
env && /* @__PURE__ */ jsx5("p", { className: "text-[11px] text-muted-foreground", children: "Edit the mesh with the Envelope tool (U)." })
|
|
808
1080
|
] });
|
|
809
1081
|
}
|
|
810
1082
|
|
|
@@ -821,10 +1093,10 @@ import {
|
|
|
821
1093
|
getVectorGradientEditor,
|
|
822
1094
|
vectorPaintToHex
|
|
823
1095
|
} from "@pixerate/editor/vector";
|
|
824
|
-
import { Fragment as Fragment2, jsx as
|
|
825
|
-
var
|
|
826
|
-
var
|
|
827
|
-
var
|
|
1096
|
+
import { Fragment as Fragment2, jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1097
|
+
var selectClass4 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1098
|
+
var numberClass4 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
1099
|
+
var buttonClass4 = "inline-flex h-7 min-w-7 items-center justify-center rounded border border-border bg-background px-1.5 text-xs text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
828
1100
|
var percent = (v) => Math.round(v * 1e3) / 10;
|
|
829
1101
|
function VectorGradientEditor({ controller, className }) {
|
|
830
1102
|
const state = useVectorEditorState(controller);
|
|
@@ -839,23 +1111,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
839
1111
|
controller.setStyle({ [ed.target]: vectorPaintToHex(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
|
|
840
1112
|
} else controller.setGradient({ type: value });
|
|
841
1113
|
};
|
|
842
|
-
return /* @__PURE__ */
|
|
843
|
-
/* @__PURE__ */
|
|
844
|
-
/* @__PURE__ */
|
|
845
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */
|
|
846
|
-
ed.mixed && /* @__PURE__ */
|
|
1114
|
+
return /* @__PURE__ */ jsxs6("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
|
|
1115
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
|
|
1116
|
+
/* @__PURE__ */ jsx6("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
|
|
1117
|
+
["fill", "stroke"].map((t) => /* @__PURE__ */ jsx6("button", { type: "button", className: buttonClass4, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
|
|
1118
|
+
ed.mixed && /* @__PURE__ */ jsx6("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
847
1119
|
] }),
|
|
848
|
-
/* @__PURE__ */
|
|
849
|
-
/* @__PURE__ */
|
|
850
|
-
/* @__PURE__ */
|
|
851
|
-
/* @__PURE__ */
|
|
852
|
-
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */
|
|
1120
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1.5", children: [
|
|
1121
|
+
/* @__PURE__ */ jsx6("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
|
|
1122
|
+
/* @__PURE__ */ jsxs6("select", { "aria-label": "Gradient type", className: selectClass4, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
|
|
1123
|
+
/* @__PURE__ */ jsx6("option", { value: "solid", children: "Solid" }),
|
|
1124
|
+
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx6("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
|
|
853
1125
|
] }),
|
|
854
|
-
/* @__PURE__ */
|
|
1126
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: buttonClass4, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
|
|
855
1127
|
] }),
|
|
856
|
-
g && /* @__PURE__ */
|
|
857
|
-
/* @__PURE__ */
|
|
858
|
-
/* @__PURE__ */
|
|
1128
|
+
g && /* @__PURE__ */ jsxs6(Fragment2, { children: [
|
|
1129
|
+
/* @__PURE__ */ jsxs6("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
|
|
1130
|
+
/* @__PURE__ */ jsx6(
|
|
859
1131
|
"div",
|
|
860
1132
|
{
|
|
861
1133
|
role: "button",
|
|
@@ -869,7 +1141,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
869
1141
|
}
|
|
870
1142
|
}
|
|
871
1143
|
),
|
|
872
|
-
ed.stops.map((s) => /* @__PURE__ */
|
|
1144
|
+
ed.stops.map((s) => /* @__PURE__ */ jsx6(
|
|
873
1145
|
"button",
|
|
874
1146
|
{
|
|
875
1147
|
type: "button",
|
|
@@ -883,8 +1155,8 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
883
1155
|
s.index
|
|
884
1156
|
))
|
|
885
1157
|
] }),
|
|
886
|
-
sel && /* @__PURE__ */
|
|
887
|
-
/* @__PURE__ */
|
|
1158
|
+
sel && /* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
|
|
1159
|
+
/* @__PURE__ */ jsx6(
|
|
888
1160
|
"input",
|
|
889
1161
|
{
|
|
890
1162
|
type: "color",
|
|
@@ -895,9 +1167,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
895
1167
|
onBlur: end
|
|
896
1168
|
}
|
|
897
1169
|
),
|
|
898
|
-
/* @__PURE__ */
|
|
1170
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
899
1171
|
"Opacity",
|
|
900
|
-
/* @__PURE__ */
|
|
1172
|
+
/* @__PURE__ */ jsx6(
|
|
901
1173
|
"input",
|
|
902
1174
|
{
|
|
903
1175
|
type: "number",
|
|
@@ -905,7 +1177,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
905
1177
|
max: 100,
|
|
906
1178
|
step: 5,
|
|
907
1179
|
"aria-label": "Stop opacity",
|
|
908
|
-
className:
|
|
1180
|
+
className: numberClass4,
|
|
909
1181
|
value: percent(sel.opacity ?? 1),
|
|
910
1182
|
onChange: (e) => {
|
|
911
1183
|
const n = parseFloat(e.target.value);
|
|
@@ -914,9 +1186,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
914
1186
|
}
|
|
915
1187
|
)
|
|
916
1188
|
] }),
|
|
917
|
-
/* @__PURE__ */
|
|
1189
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
918
1190
|
"At",
|
|
919
|
-
/* @__PURE__ */
|
|
1191
|
+
/* @__PURE__ */ jsx6(
|
|
920
1192
|
"input",
|
|
921
1193
|
{
|
|
922
1194
|
type: "number",
|
|
@@ -924,7 +1196,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
924
1196
|
max: 100,
|
|
925
1197
|
step: 1,
|
|
926
1198
|
"aria-label": "Stop position",
|
|
927
|
-
className:
|
|
1199
|
+
className: numberClass4,
|
|
928
1200
|
value: percent(sel.offset),
|
|
929
1201
|
onChange: (e) => {
|
|
930
1202
|
const n = parseFloat(e.target.value);
|
|
@@ -933,9 +1205,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
933
1205
|
}
|
|
934
1206
|
)
|
|
935
1207
|
] }),
|
|
936
|
-
!sel.last && /* @__PURE__ */
|
|
1208
|
+
!sel.last && /* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
937
1209
|
"Midpoint",
|
|
938
|
-
/* @__PURE__ */
|
|
1210
|
+
/* @__PURE__ */ jsx6(
|
|
939
1211
|
"input",
|
|
940
1212
|
{
|
|
941
1213
|
type: "number",
|
|
@@ -943,7 +1215,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
943
1215
|
max: 95,
|
|
944
1216
|
step: 1,
|
|
945
1217
|
"aria-label": "Stop midpoint",
|
|
946
|
-
className:
|
|
1218
|
+
className: numberClass4,
|
|
947
1219
|
value: percent(sel.midpoint ?? 0.5),
|
|
948
1220
|
onChange: (e) => {
|
|
949
1221
|
const n = parseFloat(e.target.value);
|
|
@@ -952,15 +1224,15 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
952
1224
|
}
|
|
953
1225
|
)
|
|
954
1226
|
] }),
|
|
955
|
-
/* @__PURE__ */
|
|
1227
|
+
/* @__PURE__ */ jsx6("button", { type: "button", className: buttonClass4, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
|
|
956
1228
|
] }),
|
|
957
|
-
/* @__PURE__ */
|
|
958
|
-
/* @__PURE__ */
|
|
959
|
-
/* @__PURE__ */
|
|
1229
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1230
|
+
/* @__PURE__ */ jsx6("span", { className: "w-16", children: "Colours" }),
|
|
1231
|
+
/* @__PURE__ */ jsx6("select", { "aria-label": "Gradient interpolation", className: selectClass4, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx6("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
|
|
960
1232
|
] }),
|
|
961
|
-
/* @__PURE__ */
|
|
962
|
-
/* @__PURE__ */
|
|
963
|
-
/* @__PURE__ */
|
|
1233
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1234
|
+
/* @__PURE__ */ jsx6("span", { className: "w-16", children: "Smooth" }),
|
|
1235
|
+
/* @__PURE__ */ jsx6(
|
|
964
1236
|
"input",
|
|
965
1237
|
{
|
|
966
1238
|
type: "range",
|
|
@@ -976,23 +1248,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
976
1248
|
}
|
|
977
1249
|
)
|
|
978
1250
|
] }),
|
|
979
|
-
/* @__PURE__ */
|
|
980
|
-
/* @__PURE__ */
|
|
981
|
-
/* @__PURE__ */
|
|
1251
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1252
|
+
/* @__PURE__ */ jsx6("span", { className: "w-16", children: "Spread" }),
|
|
1253
|
+
/* @__PURE__ */ jsx6("select", { "aria-label": "Gradient spread", className: selectClass4, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx6("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
|
|
982
1254
|
] }),
|
|
983
|
-
ed.target === "stroke" && /* @__PURE__ */
|
|
984
|
-
/* @__PURE__ */
|
|
985
|
-
/* @__PURE__ */
|
|
1255
|
+
ed.target === "stroke" && /* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1256
|
+
/* @__PURE__ */ jsx6("span", { className: "w-16", children: "On stroke" }),
|
|
1257
|
+
/* @__PURE__ */ jsx6("select", { "aria-label": "Gradient on stroke", className: selectClass4, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx6("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
|
|
986
1258
|
] })
|
|
987
1259
|
] })
|
|
988
1260
|
] });
|
|
989
1261
|
}
|
|
990
1262
|
|
|
991
1263
|
// src/vector/VectorToolbar.tsx
|
|
992
|
-
import { Fragment as Fragment3, jsx as
|
|
1264
|
+
import { Fragment as Fragment3, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
993
1265
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
994
1266
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
995
|
-
var
|
|
1267
|
+
var buttonClass5 = "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";
|
|
996
1268
|
function VectorToolbar({
|
|
997
1269
|
controller,
|
|
998
1270
|
tools = VECTOR_TOOLS,
|
|
@@ -1011,14 +1283,14 @@ function VectorToolbar({
|
|
|
1011
1283
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
1012
1284
|
const [live, setLive] = React4.useState(false);
|
|
1013
1285
|
const booleanDisabled = live ? count < 1 : count < 2;
|
|
1014
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */
|
|
1015
|
-
return /* @__PURE__ */
|
|
1016
|
-
/* @__PURE__ */
|
|
1286
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
1287
|
+
return /* @__PURE__ */ jsxs7("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
1288
|
+
/* @__PURE__ */ jsx7("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx7(
|
|
1017
1289
|
"button",
|
|
1018
1290
|
{
|
|
1019
1291
|
type: "button",
|
|
1020
1292
|
"data-vector-tool": tool,
|
|
1021
|
-
className:
|
|
1293
|
+
className: buttonClass5,
|
|
1022
1294
|
"aria-pressed": state.tool === tool,
|
|
1023
1295
|
title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
1024
1296
|
"aria-label": vectorToolLabel(tool),
|
|
@@ -1027,16 +1299,16 @@ function VectorToolbar({
|
|
|
1027
1299
|
},
|
|
1028
1300
|
tool
|
|
1029
1301
|
)) }),
|
|
1030
|
-
showHistory && /* @__PURE__ */
|
|
1302
|
+
showHistory && /* @__PURE__ */ jsxs7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
1031
1303
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
1032
1304
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
1033
1305
|
] }),
|
|
1034
|
-
showSnap && /* @__PURE__ */
|
|
1306
|
+
showSnap && /* @__PURE__ */ jsx7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ jsx7(
|
|
1035
1307
|
"button",
|
|
1036
1308
|
{
|
|
1037
1309
|
type: "button",
|
|
1038
1310
|
"data-vector-snap": "",
|
|
1039
|
-
className:
|
|
1311
|
+
className: buttonClass5,
|
|
1040
1312
|
"aria-pressed": state.snap.objects,
|
|
1041
1313
|
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
1042
1314
|
"aria-label": VECTOR_SNAP_TITLE,
|
|
@@ -1044,13 +1316,13 @@ function VectorToolbar({
|
|
|
1044
1316
|
children: "Snap"
|
|
1045
1317
|
}
|
|
1046
1318
|
) }),
|
|
1047
|
-
showPathEdit && /* @__PURE__ */
|
|
1048
|
-
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */
|
|
1319
|
+
showPathEdit && /* @__PURE__ */ jsxs7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
1320
|
+
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx7(
|
|
1049
1321
|
"button",
|
|
1050
1322
|
{
|
|
1051
1323
|
type: "button",
|
|
1052
1324
|
"data-vector-path-action": a.id,
|
|
1053
|
-
className:
|
|
1325
|
+
className: buttonClass5,
|
|
1054
1326
|
title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
|
|
1055
1327
|
"aria-label": a.title,
|
|
1056
1328
|
disabled: !a.enabled(count, points),
|
|
@@ -1059,12 +1331,12 @@ function VectorToolbar({
|
|
|
1059
1331
|
},
|
|
1060
1332
|
a.id
|
|
1061
1333
|
)),
|
|
1062
|
-
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */
|
|
1334
|
+
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx7(
|
|
1063
1335
|
"button",
|
|
1064
1336
|
{
|
|
1065
1337
|
type: "button",
|
|
1066
1338
|
"data-vector-edit": t.key,
|
|
1067
|
-
className:
|
|
1339
|
+
className: buttonClass5,
|
|
1068
1340
|
"aria-pressed": editing[t.key],
|
|
1069
1341
|
title: t.title,
|
|
1070
1342
|
"aria-label": t.title,
|
|
@@ -1073,9 +1345,9 @@ function VectorToolbar({
|
|
|
1073
1345
|
},
|
|
1074
1346
|
t.key
|
|
1075
1347
|
)),
|
|
1076
|
-
state.tool === "pencil" && /* @__PURE__ */
|
|
1348
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1077
1349
|
"Smoothing",
|
|
1078
|
-
/* @__PURE__ */
|
|
1350
|
+
/* @__PURE__ */ jsx7(
|
|
1079
1351
|
"input",
|
|
1080
1352
|
{
|
|
1081
1353
|
type: "range",
|
|
@@ -1089,9 +1361,9 @@ function VectorToolbar({
|
|
|
1089
1361
|
}
|
|
1090
1362
|
)
|
|
1091
1363
|
] }),
|
|
1092
|
-
state.tool === "eraser" && /* @__PURE__ */
|
|
1364
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1093
1365
|
"Size",
|
|
1094
|
-
/* @__PURE__ */
|
|
1366
|
+
/* @__PURE__ */ jsx7(
|
|
1095
1367
|
"input",
|
|
1096
1368
|
{
|
|
1097
1369
|
type: "number",
|
|
@@ -1107,12 +1379,12 @@ function VectorToolbar({
|
|
|
1107
1379
|
}
|
|
1108
1380
|
)
|
|
1109
1381
|
] }),
|
|
1110
|
-
state.tool === "paintBucket" && /* @__PURE__ */
|
|
1382
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx7(
|
|
1111
1383
|
"button",
|
|
1112
1384
|
{
|
|
1113
1385
|
type: "button",
|
|
1114
1386
|
"data-vector-fill-gap": "",
|
|
1115
|
-
className:
|
|
1387
|
+
className: buttonClass5,
|
|
1116
1388
|
"aria-pressed": editing.fillGap > 0,
|
|
1117
1389
|
title: "Close small gaps between path ends when filling",
|
|
1118
1390
|
"aria-label": "Close gaps",
|
|
@@ -1121,12 +1393,12 @@ function VectorToolbar({
|
|
|
1121
1393
|
}
|
|
1122
1394
|
)
|
|
1123
1395
|
] }),
|
|
1124
|
-
showDistort && /* @__PURE__ */
|
|
1396
|
+
showDistort && /* @__PURE__ */ jsx7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx7(
|
|
1125
1397
|
"button",
|
|
1126
1398
|
{
|
|
1127
1399
|
type: "button",
|
|
1128
1400
|
"data-vector-free-distort": "",
|
|
1129
|
-
className:
|
|
1401
|
+
className: buttonClass5,
|
|
1130
1402
|
"aria-pressed": state.freeDistort,
|
|
1131
1403
|
title: `Free Distort: drag a corner of the selection box to distort in perspective (Shift+Alt+${modKeyLabel()}: symmetric perspective; ${modKeyLabel()}-drag a side to skew)`,
|
|
1132
1404
|
"aria-label": "Free Distort",
|
|
@@ -1134,15 +1406,15 @@ function VectorToolbar({
|
|
|
1134
1406
|
children: "Distort"
|
|
1135
1407
|
}
|
|
1136
1408
|
) }),
|
|
1137
|
-
showBlend && /* @__PURE__ */
|
|
1138
|
-
showArrange && /* @__PURE__ */
|
|
1139
|
-
/* @__PURE__ */
|
|
1140
|
-
/* @__PURE__ */
|
|
1409
|
+
showBlend && /* @__PURE__ */ jsx7(VectorBlendControls, { controller }),
|
|
1410
|
+
showArrange && /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1411
|
+
/* @__PURE__ */ jsxs7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
1412
|
+
/* @__PURE__ */ jsx7(
|
|
1141
1413
|
"button",
|
|
1142
1414
|
{
|
|
1143
1415
|
type: "button",
|
|
1144
1416
|
"data-vector-live-boolean": "",
|
|
1145
|
-
className:
|
|
1417
|
+
className: buttonClass5,
|
|
1146
1418
|
"aria-pressed": live,
|
|
1147
1419
|
title: "Live booleans: keep the shapes editable",
|
|
1148
1420
|
"aria-label": "Live booleans",
|
|
@@ -1157,12 +1429,12 @@ function VectorToolbar({
|
|
|
1157
1429
|
act("G", "Group", () => controller.group(), count < 1),
|
|
1158
1430
|
act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
|
|
1159
1431
|
] }),
|
|
1160
|
-
/* @__PURE__ */
|
|
1432
|
+
/* @__PURE__ */ jsx7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx7(
|
|
1161
1433
|
"button",
|
|
1162
1434
|
{
|
|
1163
1435
|
type: "button",
|
|
1164
1436
|
"data-vector-pathfinder": a.id,
|
|
1165
|
-
className:
|
|
1437
|
+
className: buttonClass5,
|
|
1166
1438
|
title: a.title,
|
|
1167
1439
|
"aria-label": a.title,
|
|
1168
1440
|
disabled: count < a.min,
|
|
@@ -1171,7 +1443,7 @@ function VectorToolbar({
|
|
|
1171
1443
|
},
|
|
1172
1444
|
a.id
|
|
1173
1445
|
)) }),
|
|
1174
|
-
/* @__PURE__ */
|
|
1446
|
+
/* @__PURE__ */ jsxs7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
1175
1447
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
1176
1448
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
1177
1449
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -1189,19 +1461,19 @@ function VectorBlendControls({ controller }) {
|
|
|
1189
1461
|
const blend = getVectorBlendControls(state);
|
|
1190
1462
|
const o = blend.options;
|
|
1191
1463
|
const selected = blend.ids.length > 0;
|
|
1192
|
-
return /* @__PURE__ */
|
|
1193
|
-
/* @__PURE__ */
|
|
1194
|
-
/* @__PURE__ */
|
|
1464
|
+
return /* @__PURE__ */ jsxs7("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
|
|
1465
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass5, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
|
|
1466
|
+
/* @__PURE__ */ jsx7(
|
|
1195
1467
|
"select",
|
|
1196
1468
|
{
|
|
1197
1469
|
"aria-label": "Blend spacing",
|
|
1198
1470
|
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
1199
1471
|
value: o.spacing,
|
|
1200
1472
|
onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
|
|
1201
|
-
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */
|
|
1473
|
+
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx7("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
|
|
1202
1474
|
}
|
|
1203
1475
|
),
|
|
1204
|
-
o.spacing !== "smoothColor" && /* @__PURE__ */
|
|
1476
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ jsx7(
|
|
1205
1477
|
"input",
|
|
1206
1478
|
{
|
|
1207
1479
|
type: "number",
|
|
@@ -1217,12 +1489,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1217
1489
|
}
|
|
1218
1490
|
}
|
|
1219
1491
|
),
|
|
1220
|
-
/* @__PURE__ */
|
|
1492
|
+
/* @__PURE__ */ jsx7(
|
|
1221
1493
|
"button",
|
|
1222
1494
|
{
|
|
1223
1495
|
type: "button",
|
|
1224
1496
|
"data-vector-blend-action": "orient",
|
|
1225
|
-
className:
|
|
1497
|
+
className: buttonClass5,
|
|
1226
1498
|
"aria-pressed": o.orientToSpine,
|
|
1227
1499
|
title: "Orient steps to the spine",
|
|
1228
1500
|
"aria-label": "Orient to spine",
|
|
@@ -1230,12 +1502,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1230
1502
|
children: "\u293F"
|
|
1231
1503
|
}
|
|
1232
1504
|
),
|
|
1233
|
-
/* @__PURE__ */
|
|
1505
|
+
/* @__PURE__ */ jsx7(
|
|
1234
1506
|
"button",
|
|
1235
1507
|
{
|
|
1236
1508
|
type: "button",
|
|
1237
1509
|
"data-vector-blend-action": "spine",
|
|
1238
|
-
className:
|
|
1510
|
+
className: buttonClass5,
|
|
1239
1511
|
title: "Replace Spine (select a blend and a path)",
|
|
1240
1512
|
"aria-label": "Replace Spine",
|
|
1241
1513
|
disabled: !blend.canReplaceSpine,
|
|
@@ -1243,12 +1515,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1243
1515
|
children: "\u2307"
|
|
1244
1516
|
}
|
|
1245
1517
|
),
|
|
1246
|
-
/* @__PURE__ */
|
|
1247
|
-
/* @__PURE__ */
|
|
1518
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass5, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
|
|
1519
|
+
/* @__PURE__ */ jsx7("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass5, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
|
|
1248
1520
|
] });
|
|
1249
1521
|
}
|
|
1250
|
-
var
|
|
1251
|
-
var
|
|
1522
|
+
var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1523
|
+
var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
1252
1524
|
function VectorStylePanel({ controller, className }) {
|
|
1253
1525
|
const state = useVectorEditorState(controller);
|
|
1254
1526
|
const style = getVectorSelectionStyle(state);
|
|
@@ -1279,17 +1551,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1279
1551
|
if (style) controller.setWidthProfile(id);
|
|
1280
1552
|
else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
|
|
1281
1553
|
};
|
|
1282
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */
|
|
1554
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx7("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
1283
1555
|
const arrowRow = (end, label) => {
|
|
1284
1556
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
1285
1557
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
1286
|
-
return /* @__PURE__ */
|
|
1287
|
-
/* @__PURE__ */
|
|
1288
|
-
/* @__PURE__ */
|
|
1289
|
-
/* @__PURE__ */
|
|
1290
|
-
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */
|
|
1558
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
1559
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
1560
|
+
/* @__PURE__ */ jsxs7("select", { "aria-label": `${label} arrowhead`, className: selectClass5, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
1561
|
+
/* @__PURE__ */ jsx7("option", { value: "", children: "None" }),
|
|
1562
|
+
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx7("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
1291
1563
|
] }),
|
|
1292
|
-
/* @__PURE__ */
|
|
1564
|
+
/* @__PURE__ */ jsx7(
|
|
1293
1565
|
"input",
|
|
1294
1566
|
{
|
|
1295
1567
|
type: "number",
|
|
@@ -1297,7 +1569,7 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1297
1569
|
step: 0.25,
|
|
1298
1570
|
"aria-label": `${label} arrowhead scale`,
|
|
1299
1571
|
title: "Scale (\xD7 stroke width)",
|
|
1300
|
-
className:
|
|
1572
|
+
className: numberClass5,
|
|
1301
1573
|
value: source[scaleKey],
|
|
1302
1574
|
onChange: (e) => {
|
|
1303
1575
|
const n = parseFloat(e.target.value);
|
|
@@ -1315,12 +1587,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1315
1587
|
const paint = source[key];
|
|
1316
1588
|
const hex = vectorPaintToHex2(paint);
|
|
1317
1589
|
const on = paint !== null && paint !== "none";
|
|
1318
|
-
return /* @__PURE__ */
|
|
1319
|
-
/* @__PURE__ */
|
|
1320
|
-
/* @__PURE__ */
|
|
1590
|
+
return /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
1591
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
1592
|
+
/* @__PURE__ */ jsx7("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
1321
1593
|
label
|
|
1322
1594
|
] }),
|
|
1323
|
-
/* @__PURE__ */
|
|
1595
|
+
/* @__PURE__ */ jsx7(
|
|
1324
1596
|
"input",
|
|
1325
1597
|
{
|
|
1326
1598
|
type: "color",
|
|
@@ -1331,18 +1603,18 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1331
1603
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
1332
1604
|
}
|
|
1333
1605
|
),
|
|
1334
|
-
on && !hex && /* @__PURE__ */
|
|
1335
|
-
style?.mixed[key] && /* @__PURE__ */
|
|
1606
|
+
on && !hex && /* @__PURE__ */ jsx7("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
1607
|
+
style?.mixed[key] && /* @__PURE__ */ jsx7("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
1336
1608
|
] });
|
|
1337
1609
|
};
|
|
1338
|
-
return /* @__PURE__ */
|
|
1339
|
-
/* @__PURE__ */
|
|
1610
|
+
return /* @__PURE__ */ jsxs7("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
1611
|
+
/* @__PURE__ */ jsx7("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
1340
1612
|
paintRow("fill", "Fill"),
|
|
1341
1613
|
paintRow("stroke", "Stroke"),
|
|
1342
|
-
style && /* @__PURE__ */
|
|
1343
|
-
/* @__PURE__ */
|
|
1344
|
-
/* @__PURE__ */
|
|
1345
|
-
/* @__PURE__ */
|
|
1614
|
+
style && /* @__PURE__ */ jsx7(VectorGradientEditor, { controller }),
|
|
1615
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
1616
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Width" }),
|
|
1617
|
+
/* @__PURE__ */ jsx7(
|
|
1346
1618
|
"input",
|
|
1347
1619
|
{
|
|
1348
1620
|
type: "number",
|
|
@@ -1358,33 +1630,33 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1358
1630
|
}
|
|
1359
1631
|
)
|
|
1360
1632
|
] }),
|
|
1361
|
-
/* @__PURE__ */
|
|
1362
|
-
/* @__PURE__ */
|
|
1363
|
-
/* @__PURE__ */
|
|
1633
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
1634
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Align" }),
|
|
1635
|
+
/* @__PURE__ */ jsx7("select", { "aria-label": "Stroke alignment", className: selectClass5, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx7("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
|
|
1364
1636
|
mixed("strokeAlign")
|
|
1365
1637
|
] }),
|
|
1366
|
-
/* @__PURE__ */
|
|
1367
|
-
/* @__PURE__ */
|
|
1368
|
-
/* @__PURE__ */
|
|
1638
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
1639
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Profile" }),
|
|
1640
|
+
/* @__PURE__ */ jsxs7(
|
|
1369
1641
|
"select",
|
|
1370
1642
|
{
|
|
1371
1643
|
"aria-label": "Width profile",
|
|
1372
|
-
className:
|
|
1644
|
+
className: selectClass5,
|
|
1373
1645
|
value: source.widthProfile,
|
|
1374
1646
|
onChange: (e) => {
|
|
1375
1647
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
1376
1648
|
},
|
|
1377
1649
|
children: [
|
|
1378
|
-
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */
|
|
1379
|
-
source.widthProfile === "custom" && /* @__PURE__ */
|
|
1650
|
+
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx7("option", { value: p.id, children: p.label }, p.id)),
|
|
1651
|
+
source.widthProfile === "custom" && /* @__PURE__ */ jsx7("option", { value: "custom", disabled: true, children: "Custom" })
|
|
1380
1652
|
]
|
|
1381
1653
|
}
|
|
1382
1654
|
),
|
|
1383
|
-
/* @__PURE__ */
|
|
1655
|
+
/* @__PURE__ */ jsx7(
|
|
1384
1656
|
"button",
|
|
1385
1657
|
{
|
|
1386
1658
|
type: "button",
|
|
1387
|
-
className:
|
|
1659
|
+
className: buttonClass5,
|
|
1388
1660
|
title: "Reset to uniform width",
|
|
1389
1661
|
"aria-label": "Reset to uniform width",
|
|
1390
1662
|
disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
|
|
@@ -1394,27 +1666,27 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1394
1666
|
),
|
|
1395
1667
|
mixed("widthProfile")
|
|
1396
1668
|
] }),
|
|
1397
|
-
source.hasOpenPath && /* @__PURE__ */
|
|
1669
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs7(Fragment3, { children: [
|
|
1398
1670
|
arrowRow("start", "Start"),
|
|
1399
1671
|
arrowRow("end", "End"),
|
|
1400
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
1401
|
-
/* @__PURE__ */
|
|
1402
|
-
/* @__PURE__ */
|
|
1403
|
-
/* @__PURE__ */
|
|
1404
|
-
/* @__PURE__ */
|
|
1672
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
1673
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Heads" }),
|
|
1674
|
+
/* @__PURE__ */ jsxs7("select", { "aria-label": "Arrowhead alignment", className: selectClass5, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
1675
|
+
/* @__PURE__ */ jsx7("option", { value: "extend", children: "Tip past end" }),
|
|
1676
|
+
/* @__PURE__ */ jsx7("option", { value: "tip", children: "Tip at end" })
|
|
1405
1677
|
] })
|
|
1406
1678
|
] })
|
|
1407
1679
|
] }),
|
|
1408
|
-
source.corners > 0 && /* @__PURE__ */
|
|
1409
|
-
/* @__PURE__ */
|
|
1410
|
-
/* @__PURE__ */
|
|
1680
|
+
source.corners > 0 && /* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
1681
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
1682
|
+
/* @__PURE__ */ jsx7(
|
|
1411
1683
|
"input",
|
|
1412
1684
|
{
|
|
1413
1685
|
type: "number",
|
|
1414
1686
|
min: 0,
|
|
1415
1687
|
step: 1,
|
|
1416
1688
|
"aria-label": "Corner radius",
|
|
1417
|
-
className:
|
|
1689
|
+
className: numberClass5,
|
|
1418
1690
|
value: Math.round(source.cornerRadius * 100) / 100,
|
|
1419
1691
|
onChange: (e) => {
|
|
1420
1692
|
const n = parseFloat(e.target.value);
|
|
@@ -1422,12 +1694,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1422
1694
|
}
|
|
1423
1695
|
}
|
|
1424
1696
|
),
|
|
1425
|
-
/* @__PURE__ */
|
|
1697
|
+
/* @__PURE__ */ jsx7("select", { "aria-label": "Corner kind", className: selectClass5, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx7("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
1426
1698
|
mixed("cornerRadius")
|
|
1427
1699
|
] }),
|
|
1428
|
-
style && /* @__PURE__ */
|
|
1429
|
-
/* @__PURE__ */
|
|
1430
|
-
/* @__PURE__ */
|
|
1700
|
+
style && /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
1701
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Opacity" }),
|
|
1702
|
+
/* @__PURE__ */ jsx7(
|
|
1431
1703
|
"input",
|
|
1432
1704
|
{
|
|
1433
1705
|
type: "range",
|
|
@@ -1441,27 +1713,30 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1441
1713
|
}
|
|
1442
1714
|
)
|
|
1443
1715
|
] }),
|
|
1444
|
-
style && /* @__PURE__ */
|
|
1445
|
-
style && /* @__PURE__ */
|
|
1716
|
+
style && /* @__PURE__ */ jsx7(VectorAppearancePanel, { controller }),
|
|
1717
|
+
style && /* @__PURE__ */ jsx7(VectorEffectsPanel, { controller }),
|
|
1718
|
+
style && /* @__PURE__ */ jsx7(VectorEnvelopePanel, { controller })
|
|
1446
1719
|
] });
|
|
1447
1720
|
}
|
|
1448
1721
|
|
|
1449
1722
|
// src/vector/VectorEditor.tsx
|
|
1450
|
-
import { jsx as
|
|
1723
|
+
import { jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1451
1724
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
1452
|
-
return /* @__PURE__ */
|
|
1453
|
-
/* @__PURE__ */
|
|
1454
|
-
/* @__PURE__ */
|
|
1455
|
-
/* @__PURE__ */
|
|
1456
|
-
showStylePanel && /* @__PURE__ */
|
|
1725
|
+
return /* @__PURE__ */ jsxs8("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
1726
|
+
/* @__PURE__ */ jsx8(VectorToolbar, { controller, tools }),
|
|
1727
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
1728
|
+
/* @__PURE__ */ jsx8("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx8(VectorCanvas, { controller, ...canvasProps }) }),
|
|
1729
|
+
showStylePanel && /* @__PURE__ */ jsx8(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
|
|
1457
1730
|
] })
|
|
1458
1731
|
] });
|
|
1459
1732
|
}
|
|
1460
1733
|
export {
|
|
1734
|
+
VectorAppearancePanel,
|
|
1461
1735
|
VectorCanvas,
|
|
1462
1736
|
VectorEditor,
|
|
1463
1737
|
VectorEffectsPanel,
|
|
1464
1738
|
VectorEnvelopePanel,
|
|
1739
|
+
VectorFilterDefs,
|
|
1465
1740
|
VectorGradientAnnotator,
|
|
1466
1741
|
VectorGradientDefs,
|
|
1467
1742
|
VectorGradientEditor,
|