@pixerate/editor-react 0.68.1 → 0.69.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/paint-editor/index.cjs +25 -3
- package/dist/paint-editor/index.cjs.map +1 -1
- package/dist/paint-editor/index.d.cts +31 -2
- package/dist/paint-editor/index.d.ts +31 -2
- package/dist/paint-editor/index.js +26 -5
- package/dist/paint-editor/index.js.map +1 -1
- package/dist/vector/index.cjs +302 -152
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +23 -2
- package/dist/vector/index.d.ts +23 -2
- package/dist/vector/index.js +285 -136
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.js
CHANGED
|
@@ -187,7 +187,8 @@ function VectorGradientAnnotator({ annotator: g, accentColor = THEME2.accent })
|
|
|
187
187
|
}
|
|
188
188
|
function VectorFilterDefs({ filters }) {
|
|
189
189
|
if (!filters.length) return null;
|
|
190
|
-
const
|
|
190
|
+
const props = (attrs) => Object.fromEntries(Object.entries(attrs).map(([k, v]) => [k.replace(/-([a-z])/g, (_, c) => c.toUpperCase()), v]));
|
|
191
|
+
const primitive = (p, key) => React2.createElement(p.tag, { key, ...props(p.attrs) }, p.children?.map(primitive));
|
|
191
192
|
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
|
}
|
|
193
194
|
var blendStyle = (blendMode) => blendMode ? { mixBlendMode: blendMode } : void 0;
|
|
@@ -197,11 +198,12 @@ function VectorSceneNodes({ nodes }) {
|
|
|
197
198
|
"g",
|
|
198
199
|
{
|
|
199
200
|
"data-vector-id": node.id,
|
|
201
|
+
"data-vector-mask": node.mask?.type,
|
|
200
202
|
transform: node.transform,
|
|
201
203
|
opacity: node.opacity < 1 ? node.opacity : void 0,
|
|
202
204
|
filter: node.filter,
|
|
203
205
|
style: blendStyle(node.blendMode),
|
|
204
|
-
children: /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: node.children })
|
|
206
|
+
children: node.mask ? /* @__PURE__ */ jsx2(VectorMaskedNodes, { mask: node.mask, nodes: node.children }) : /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: node.children })
|
|
205
207
|
},
|
|
206
208
|
node.id
|
|
207
209
|
) : node.layers ? (
|
|
@@ -233,6 +235,13 @@ function VectorSceneNodes({ nodes }) {
|
|
|
233
235
|
)
|
|
234
236
|
) });
|
|
235
237
|
}
|
|
238
|
+
function VectorMaskedNodes({ mask, nodes }) {
|
|
239
|
+
const b = mask.bounds;
|
|
240
|
+
return /* @__PURE__ */ jsxs2(Fragment, { children: [
|
|
241
|
+
/* @__PURE__ */ jsx2("defs", { children: mask.clip ? /* @__PURE__ */ jsx2("clipPath", { id: mask.id, children: mask.clip.map((c, i) => /* @__PURE__ */ jsx2("path", { d: c.d, clipRule: c.clipRule }, i)) }) : b && /* @__PURE__ */ jsx2("mask", { id: mask.id, maskUnits: "userSpaceOnUse", x: b.x, y: b.y, width: b.width, height: b.height, style: mask.type === "alpha" ? { maskType: "alpha" } : void 0, children: mask.filter ? /* @__PURE__ */ jsx2("g", { filter: mask.filter, children: /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: mask.nodes ?? [] }) }) : /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes: mask.nodes ?? [] }) }) }),
|
|
242
|
+
/* @__PURE__ */ jsx2("g", { "data-vector-masked": "", clipPath: mask.clip ? `url(#${mask.id})` : void 0, mask: mask.clip ? void 0 : `url(#${mask.id})`, children: /* @__PURE__ */ jsx2(VectorSceneNodes, { nodes }) })
|
|
243
|
+
] });
|
|
244
|
+
}
|
|
236
245
|
function VectorPaintPath({
|
|
237
246
|
paint,
|
|
238
247
|
d,
|
|
@@ -460,6 +469,21 @@ function VectorCanvas({
|
|
|
460
469
|
/* @__PURE__ */ jsx2("circle", { cx: w.x, cy: w.y, r: THEME2.cornerWidgetRadius + (w.hovered ? 1 : 0), fill: THEME2.handleFill, stroke: accentColor }),
|
|
461
470
|
/* @__PURE__ */ jsx2("circle", { cx: w.x, cy: w.y, r: 1.5, fill: accentColor })
|
|
462
471
|
] }, `corner:${w.ref.nodeId}:${w.ref.subpath}:${w.ref.index}`)),
|
|
472
|
+
overlay.masks.map((m) => /* @__PURE__ */ jsx2(
|
|
473
|
+
"path",
|
|
474
|
+
{
|
|
475
|
+
"data-vector-mask-indicator": m.id,
|
|
476
|
+
"data-mask-type": m.type,
|
|
477
|
+
"data-inverted": m.invert || void 0,
|
|
478
|
+
d: m.d,
|
|
479
|
+
fill: "none",
|
|
480
|
+
stroke: THEME2.mask,
|
|
481
|
+
strokeWidth: m.editing ? 1.5 : 1,
|
|
482
|
+
strokeDasharray: m.editing ? void 0 : THEME2.maskDash,
|
|
483
|
+
opacity: m.enabled ? 1 : 0.5
|
|
484
|
+
},
|
|
485
|
+
`mask:${m.id}`
|
|
486
|
+
)),
|
|
463
487
|
overlay.blend.spines.map((sp) => /* @__PURE__ */ jsxs2("g", { "data-vector-blend-spine": sp.id, "data-replaced": sp.replaced || void 0, children: [
|
|
464
488
|
/* @__PURE__ */ jsx2("path", { d: sp.d, fill: "none", stroke: accentColor, strokeDasharray: "4 3" }),
|
|
465
489
|
sp.keys.map((k, i) => /* @__PURE__ */ jsx2("circle", { cx: k.x, cy: k.y, r: THEME2.handleRadius, fill: THEME2.handleFill, stroke: accentColor }, i))
|
|
@@ -607,6 +631,7 @@ import {
|
|
|
607
631
|
applyVectorBlendOptions,
|
|
608
632
|
getVectorBlendControls,
|
|
609
633
|
getVectorSelectionStyle,
|
|
634
|
+
isVectorMaskGroup,
|
|
610
635
|
widthProfilePreset,
|
|
611
636
|
widthProfilePresetId,
|
|
612
637
|
vectorPaintToHex as vectorPaintToHex2,
|
|
@@ -1080,6 +1105,124 @@ function VectorEnvelopePanel({ controller, className }) {
|
|
|
1080
1105
|
] });
|
|
1081
1106
|
}
|
|
1082
1107
|
|
|
1108
|
+
// src/vector/VectorMaskPanel.tsx
|
|
1109
|
+
import { VECTOR_MASK_TYPE_OPTIONS, getVectorMaskPanel } from "@pixerate/editor/vector";
|
|
1110
|
+
import { jsx as jsx6, jsxs as jsxs6 } from "react/jsx-runtime";
|
|
1111
|
+
var buttonClass4 = "inline-flex h-7 min-w-7 items-center justify-center rounded-md border border-border bg-background px-1.5 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
1112
|
+
var selectClass4 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1113
|
+
function VectorMaskPanel({ controller, className }) {
|
|
1114
|
+
const state = useVectorEditorState(controller);
|
|
1115
|
+
const panel = getVectorMaskPanel(state);
|
|
1116
|
+
if (!panel) return null;
|
|
1117
|
+
const g = panel.group;
|
|
1118
|
+
const ids = g ? [g.id] : void 0;
|
|
1119
|
+
return /* @__PURE__ */ jsxs6("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-mask-panel": "", "aria-label": "Mask", children: [
|
|
1120
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1121
|
+
/* @__PURE__ */ jsx6("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Mask" }),
|
|
1122
|
+
/* @__PURE__ */ jsx6(
|
|
1123
|
+
"button",
|
|
1124
|
+
{
|
|
1125
|
+
type: "button",
|
|
1126
|
+
"data-vector-mask-action": "clip",
|
|
1127
|
+
className: buttonClass4,
|
|
1128
|
+
title: panel.makeHint ?? "Make Clipping Mask: the topmost object clips the others",
|
|
1129
|
+
"aria-label": "Make Clipping Mask",
|
|
1130
|
+
disabled: !panel.canMake,
|
|
1131
|
+
onClick: () => controller.makeMask("clip"),
|
|
1132
|
+
children: "Clip"
|
|
1133
|
+
}
|
|
1134
|
+
),
|
|
1135
|
+
/* @__PURE__ */ jsx6(
|
|
1136
|
+
"button",
|
|
1137
|
+
{
|
|
1138
|
+
type: "button",
|
|
1139
|
+
"data-vector-mask-action": "opacity",
|
|
1140
|
+
className: buttonClass4,
|
|
1141
|
+
title: panel.makeHint ?? "Make Opacity Mask: the topmost object fades the others by its brightness",
|
|
1142
|
+
"aria-label": "Make Opacity Mask",
|
|
1143
|
+
disabled: !panel.canMake,
|
|
1144
|
+
onClick: () => controller.makeMask("luminance"),
|
|
1145
|
+
children: "Opacity"
|
|
1146
|
+
}
|
|
1147
|
+
),
|
|
1148
|
+
/* @__PURE__ */ jsx6(
|
|
1149
|
+
"button",
|
|
1150
|
+
{
|
|
1151
|
+
type: "button",
|
|
1152
|
+
"data-vector-mask-action": "release",
|
|
1153
|
+
className: buttonClass4,
|
|
1154
|
+
title: "Release: the mask goes back on top and draws as itself",
|
|
1155
|
+
"aria-label": "Release Mask",
|
|
1156
|
+
disabled: !g,
|
|
1157
|
+
onClick: () => controller.releaseMask({ ids }),
|
|
1158
|
+
children: "Release"
|
|
1159
|
+
}
|
|
1160
|
+
)
|
|
1161
|
+
] }),
|
|
1162
|
+
g && /* @__PURE__ */ jsxs6("div", { className: "space-y-2", "data-vector-mask-group": g.id, "data-mask-type": g.type, children: [
|
|
1163
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex items-center gap-1", children: [
|
|
1164
|
+
/* @__PURE__ */ jsx6(
|
|
1165
|
+
"select",
|
|
1166
|
+
{
|
|
1167
|
+
"aria-label": "Mask type",
|
|
1168
|
+
className: selectClass4,
|
|
1169
|
+
value: g.type,
|
|
1170
|
+
title: VECTOR_MASK_TYPE_OPTIONS.find((o) => o.value === g.type)?.title,
|
|
1171
|
+
onChange: (e) => controller.setMaskOptions({ type: e.target.value }, { ids }),
|
|
1172
|
+
children: VECTOR_MASK_TYPE_OPTIONS.map((o) => /* @__PURE__ */ jsx6("option", { value: o.value, title: o.title, children: o.label }, o.value))
|
|
1173
|
+
}
|
|
1174
|
+
),
|
|
1175
|
+
/* @__PURE__ */ jsxs6("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Show the contents where the mask hides them", children: [
|
|
1176
|
+
/* @__PURE__ */ jsx6("input", { type: "checkbox", "aria-label": "Invert mask", checked: g.invert, onChange: (e) => controller.setMaskOptions({ invert: e.target.checked }, { ids }) }),
|
|
1177
|
+
"Invert"
|
|
1178
|
+
] })
|
|
1179
|
+
] }),
|
|
1180
|
+
/* @__PURE__ */ jsxs6("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
1181
|
+
/* @__PURE__ */ jsx6(
|
|
1182
|
+
"button",
|
|
1183
|
+
{
|
|
1184
|
+
type: "button",
|
|
1185
|
+
"data-vector-mask-action": "edit-mask",
|
|
1186
|
+
className: buttonClass4,
|
|
1187
|
+
"aria-pressed": g.editing === "mask",
|
|
1188
|
+
title: "Edit Mask: select the mask (a path opens for anchor editing)",
|
|
1189
|
+
"aria-label": "Edit Mask",
|
|
1190
|
+
onClick: () => controller.editMask({ id: g.id }),
|
|
1191
|
+
children: "Edit mask"
|
|
1192
|
+
}
|
|
1193
|
+
),
|
|
1194
|
+
/* @__PURE__ */ jsx6(
|
|
1195
|
+
"button",
|
|
1196
|
+
{
|
|
1197
|
+
type: "button",
|
|
1198
|
+
"data-vector-mask-action": "edit-contents",
|
|
1199
|
+
className: buttonClass4,
|
|
1200
|
+
"aria-pressed": g.editing === "contents",
|
|
1201
|
+
title: "Edit Contents: select what the mask masks",
|
|
1202
|
+
"aria-label": "Edit Contents",
|
|
1203
|
+
disabled: !g.contents.length,
|
|
1204
|
+
onClick: () => controller.editMaskContents({ id: g.id }),
|
|
1205
|
+
children: "Contents"
|
|
1206
|
+
}
|
|
1207
|
+
),
|
|
1208
|
+
/* @__PURE__ */ jsx6(
|
|
1209
|
+
"button",
|
|
1210
|
+
{
|
|
1211
|
+
type: "button",
|
|
1212
|
+
"data-vector-mask-action": "toggle",
|
|
1213
|
+
className: buttonClass4,
|
|
1214
|
+
"aria-pressed": g.enabled,
|
|
1215
|
+
title: g.enabled ? "Turn the mask off (hide it): the contents draw whole" : "Turn the mask on",
|
|
1216
|
+
"aria-label": "Mask on",
|
|
1217
|
+
onClick: () => controller.setMaskOptions({ enabled: !g.enabled }, { ids }),
|
|
1218
|
+
children: g.enabled ? "On" : "Off"
|
|
1219
|
+
}
|
|
1220
|
+
)
|
|
1221
|
+
] })
|
|
1222
|
+
] })
|
|
1223
|
+
] });
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1083
1226
|
// src/vector/VectorGradientEditor.tsx
|
|
1084
1227
|
import {
|
|
1085
1228
|
VECTOR_GRADIENT_TYPES,
|
|
@@ -1093,10 +1236,10 @@ import {
|
|
|
1093
1236
|
getVectorGradientEditor,
|
|
1094
1237
|
vectorPaintToHex
|
|
1095
1238
|
} from "@pixerate/editor/vector";
|
|
1096
|
-
import { Fragment as Fragment2, jsx as
|
|
1097
|
-
var
|
|
1239
|
+
import { Fragment as Fragment2, jsx as jsx7, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
1240
|
+
var selectClass5 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1098
1241
|
var numberClass4 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
1099
|
-
var
|
|
1242
|
+
var buttonClass5 = "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";
|
|
1100
1243
|
var percent = (v) => Math.round(v * 1e3) / 10;
|
|
1101
1244
|
function VectorGradientEditor({ controller, className }) {
|
|
1102
1245
|
const state = useVectorEditorState(controller);
|
|
@@ -1111,23 +1254,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1111
1254
|
controller.setStyle({ [ed.target]: vectorPaintToHex(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
|
|
1112
1255
|
} else controller.setGradient({ type: value });
|
|
1113
1256
|
};
|
|
1114
|
-
return /* @__PURE__ */
|
|
1115
|
-
/* @__PURE__ */
|
|
1116
|
-
/* @__PURE__ */
|
|
1117
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */
|
|
1118
|
-
ed.mixed && /* @__PURE__ */
|
|
1257
|
+
return /* @__PURE__ */ jsxs7("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
|
|
1258
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1.5", children: [
|
|
1259
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
|
|
1260
|
+
["fill", "stroke"].map((t) => /* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
|
|
1261
|
+
ed.mixed && /* @__PURE__ */ jsx7("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
1119
1262
|
] }),
|
|
1120
|
-
/* @__PURE__ */
|
|
1121
|
-
/* @__PURE__ */
|
|
1122
|
-
/* @__PURE__ */
|
|
1123
|
-
/* @__PURE__ */
|
|
1124
|
-
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */
|
|
1263
|
+
/* @__PURE__ */ jsxs7("div", { className: "flex items-center gap-1.5", children: [
|
|
1264
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
|
|
1265
|
+
/* @__PURE__ */ jsxs7("select", { "aria-label": "Gradient type", className: selectClass5, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
|
|
1266
|
+
/* @__PURE__ */ jsx7("option", { value: "solid", children: "Solid" }),
|
|
1267
|
+
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx7("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
|
|
1125
1268
|
] }),
|
|
1126
|
-
/* @__PURE__ */
|
|
1269
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
|
|
1127
1270
|
] }),
|
|
1128
|
-
g && /* @__PURE__ */
|
|
1129
|
-
/* @__PURE__ */
|
|
1130
|
-
/* @__PURE__ */
|
|
1271
|
+
g && /* @__PURE__ */ jsxs7(Fragment2, { children: [
|
|
1272
|
+
/* @__PURE__ */ jsxs7("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
|
|
1273
|
+
/* @__PURE__ */ jsx7(
|
|
1131
1274
|
"div",
|
|
1132
1275
|
{
|
|
1133
1276
|
role: "button",
|
|
@@ -1141,7 +1284,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1141
1284
|
}
|
|
1142
1285
|
}
|
|
1143
1286
|
),
|
|
1144
|
-
ed.stops.map((s) => /* @__PURE__ */
|
|
1287
|
+
ed.stops.map((s) => /* @__PURE__ */ jsx7(
|
|
1145
1288
|
"button",
|
|
1146
1289
|
{
|
|
1147
1290
|
type: "button",
|
|
@@ -1155,8 +1298,8 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1155
1298
|
s.index
|
|
1156
1299
|
))
|
|
1157
1300
|
] }),
|
|
1158
|
-
sel && /* @__PURE__ */
|
|
1159
|
-
/* @__PURE__ */
|
|
1301
|
+
sel && /* @__PURE__ */ jsxs7("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
|
|
1302
|
+
/* @__PURE__ */ jsx7(
|
|
1160
1303
|
"input",
|
|
1161
1304
|
{
|
|
1162
1305
|
type: "color",
|
|
@@ -1167,9 +1310,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1167
1310
|
onBlur: end
|
|
1168
1311
|
}
|
|
1169
1312
|
),
|
|
1170
|
-
/* @__PURE__ */
|
|
1313
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
1171
1314
|
"Opacity",
|
|
1172
|
-
/* @__PURE__ */
|
|
1315
|
+
/* @__PURE__ */ jsx7(
|
|
1173
1316
|
"input",
|
|
1174
1317
|
{
|
|
1175
1318
|
type: "number",
|
|
@@ -1186,9 +1329,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1186
1329
|
}
|
|
1187
1330
|
)
|
|
1188
1331
|
] }),
|
|
1189
|
-
/* @__PURE__ */
|
|
1332
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
1190
1333
|
"At",
|
|
1191
|
-
/* @__PURE__ */
|
|
1334
|
+
/* @__PURE__ */ jsx7(
|
|
1192
1335
|
"input",
|
|
1193
1336
|
{
|
|
1194
1337
|
type: "number",
|
|
@@ -1205,9 +1348,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1205
1348
|
}
|
|
1206
1349
|
)
|
|
1207
1350
|
] }),
|
|
1208
|
-
!sel.last && /* @__PURE__ */
|
|
1351
|
+
!sel.last && /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
1209
1352
|
"Midpoint",
|
|
1210
|
-
/* @__PURE__ */
|
|
1353
|
+
/* @__PURE__ */ jsx7(
|
|
1211
1354
|
"input",
|
|
1212
1355
|
{
|
|
1213
1356
|
type: "number",
|
|
@@ -1224,15 +1367,15 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1224
1367
|
}
|
|
1225
1368
|
)
|
|
1226
1369
|
] }),
|
|
1227
|
-
/* @__PURE__ */
|
|
1370
|
+
/* @__PURE__ */ jsx7("button", { type: "button", className: buttonClass5, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
|
|
1228
1371
|
] }),
|
|
1229
|
-
/* @__PURE__ */
|
|
1230
|
-
/* @__PURE__ */
|
|
1231
|
-
/* @__PURE__ */
|
|
1372
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1373
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Colours" }),
|
|
1374
|
+
/* @__PURE__ */ jsx7("select", { "aria-label": "Gradient interpolation", className: selectClass5, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx7("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
|
|
1232
1375
|
] }),
|
|
1233
|
-
/* @__PURE__ */
|
|
1234
|
-
/* @__PURE__ */
|
|
1235
|
-
/* @__PURE__ */
|
|
1376
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1377
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Smooth" }),
|
|
1378
|
+
/* @__PURE__ */ jsx7(
|
|
1236
1379
|
"input",
|
|
1237
1380
|
{
|
|
1238
1381
|
type: "range",
|
|
@@ -1248,23 +1391,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
1248
1391
|
}
|
|
1249
1392
|
)
|
|
1250
1393
|
] }),
|
|
1251
|
-
/* @__PURE__ */
|
|
1252
|
-
/* @__PURE__ */
|
|
1253
|
-
/* @__PURE__ */
|
|
1394
|
+
/* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1395
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "Spread" }),
|
|
1396
|
+
/* @__PURE__ */ jsx7("select", { "aria-label": "Gradient spread", className: selectClass5, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx7("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
|
|
1254
1397
|
] }),
|
|
1255
|
-
ed.target === "stroke" && /* @__PURE__ */
|
|
1256
|
-
/* @__PURE__ */
|
|
1257
|
-
/* @__PURE__ */
|
|
1398
|
+
ed.target === "stroke" && /* @__PURE__ */ jsxs7("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
1399
|
+
/* @__PURE__ */ jsx7("span", { className: "w-16", children: "On stroke" }),
|
|
1400
|
+
/* @__PURE__ */ jsx7("select", { "aria-label": "Gradient on stroke", className: selectClass5, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx7("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
|
|
1258
1401
|
] })
|
|
1259
1402
|
] })
|
|
1260
1403
|
] });
|
|
1261
1404
|
}
|
|
1262
1405
|
|
|
1263
1406
|
// src/vector/VectorToolbar.tsx
|
|
1264
|
-
import { Fragment as Fragment3, jsx as
|
|
1407
|
+
import { Fragment as Fragment3, jsx as jsx8, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
1265
1408
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
1266
1409
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
1267
|
-
var
|
|
1410
|
+
var buttonClass6 = "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";
|
|
1268
1411
|
function VectorToolbar({
|
|
1269
1412
|
controller,
|
|
1270
1413
|
tools = VECTOR_TOOLS,
|
|
@@ -1281,16 +1424,17 @@ function VectorToolbar({
|
|
|
1281
1424
|
const points = state.tool === "direct" ? state.selectedAnchors.length : 0;
|
|
1282
1425
|
const editing = state.editing;
|
|
1283
1426
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
1427
|
+
const maskSelected = state.selection.some((id) => isVectorMaskGroup(controller.getNode(id)));
|
|
1284
1428
|
const [live, setLive] = React4.useState(false);
|
|
1285
1429
|
const booleanDisabled = live ? count < 1 : count < 2;
|
|
1286
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */
|
|
1287
|
-
return /* @__PURE__ */
|
|
1288
|
-
/* @__PURE__ */
|
|
1430
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx8("button", { type: "button", className: buttonClass6, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
1431
|
+
return /* @__PURE__ */ jsxs8("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
1432
|
+
/* @__PURE__ */ jsx8("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx8(
|
|
1289
1433
|
"button",
|
|
1290
1434
|
{
|
|
1291
1435
|
type: "button",
|
|
1292
1436
|
"data-vector-tool": tool,
|
|
1293
|
-
className:
|
|
1437
|
+
className: buttonClass6,
|
|
1294
1438
|
"aria-pressed": state.tool === tool,
|
|
1295
1439
|
title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
1296
1440
|
"aria-label": vectorToolLabel(tool),
|
|
@@ -1299,16 +1443,16 @@ function VectorToolbar({
|
|
|
1299
1443
|
},
|
|
1300
1444
|
tool
|
|
1301
1445
|
)) }),
|
|
1302
|
-
showHistory && /* @__PURE__ */
|
|
1446
|
+
showHistory && /* @__PURE__ */ jsxs8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
1303
1447
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
1304
1448
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
1305
1449
|
] }),
|
|
1306
|
-
showSnap && /* @__PURE__ */
|
|
1450
|
+
showSnap && /* @__PURE__ */ jsx8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ jsx8(
|
|
1307
1451
|
"button",
|
|
1308
1452
|
{
|
|
1309
1453
|
type: "button",
|
|
1310
1454
|
"data-vector-snap": "",
|
|
1311
|
-
className:
|
|
1455
|
+
className: buttonClass6,
|
|
1312
1456
|
"aria-pressed": state.snap.objects,
|
|
1313
1457
|
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
1314
1458
|
"aria-label": VECTOR_SNAP_TITLE,
|
|
@@ -1316,13 +1460,13 @@ function VectorToolbar({
|
|
|
1316
1460
|
children: "Snap"
|
|
1317
1461
|
}
|
|
1318
1462
|
) }),
|
|
1319
|
-
showPathEdit && /* @__PURE__ */
|
|
1320
|
-
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */
|
|
1463
|
+
showPathEdit && /* @__PURE__ */ jsxs8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
1464
|
+
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx8(
|
|
1321
1465
|
"button",
|
|
1322
1466
|
{
|
|
1323
1467
|
type: "button",
|
|
1324
1468
|
"data-vector-path-action": a.id,
|
|
1325
|
-
className:
|
|
1469
|
+
className: buttonClass6,
|
|
1326
1470
|
title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
|
|
1327
1471
|
"aria-label": a.title,
|
|
1328
1472
|
disabled: !a.enabled(count, points),
|
|
@@ -1331,12 +1475,12 @@ function VectorToolbar({
|
|
|
1331
1475
|
},
|
|
1332
1476
|
a.id
|
|
1333
1477
|
)),
|
|
1334
|
-
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */
|
|
1478
|
+
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx8(
|
|
1335
1479
|
"button",
|
|
1336
1480
|
{
|
|
1337
1481
|
type: "button",
|
|
1338
1482
|
"data-vector-edit": t.key,
|
|
1339
|
-
className:
|
|
1483
|
+
className: buttonClass6,
|
|
1340
1484
|
"aria-pressed": editing[t.key],
|
|
1341
1485
|
title: t.title,
|
|
1342
1486
|
"aria-label": t.title,
|
|
@@ -1345,9 +1489,9 @@ function VectorToolbar({
|
|
|
1345
1489
|
},
|
|
1346
1490
|
t.key
|
|
1347
1491
|
)),
|
|
1348
|
-
state.tool === "pencil" && /* @__PURE__ */
|
|
1492
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1349
1493
|
"Smoothing",
|
|
1350
|
-
/* @__PURE__ */
|
|
1494
|
+
/* @__PURE__ */ jsx8(
|
|
1351
1495
|
"input",
|
|
1352
1496
|
{
|
|
1353
1497
|
type: "range",
|
|
@@ -1361,9 +1505,9 @@ function VectorToolbar({
|
|
|
1361
1505
|
}
|
|
1362
1506
|
)
|
|
1363
1507
|
] }),
|
|
1364
|
-
state.tool === "eraser" && /* @__PURE__ */
|
|
1508
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
1365
1509
|
"Size",
|
|
1366
|
-
/* @__PURE__ */
|
|
1510
|
+
/* @__PURE__ */ jsx8(
|
|
1367
1511
|
"input",
|
|
1368
1512
|
{
|
|
1369
1513
|
type: "number",
|
|
@@ -1379,12 +1523,12 @@ function VectorToolbar({
|
|
|
1379
1523
|
}
|
|
1380
1524
|
)
|
|
1381
1525
|
] }),
|
|
1382
|
-
state.tool === "paintBucket" && /* @__PURE__ */
|
|
1526
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx8(
|
|
1383
1527
|
"button",
|
|
1384
1528
|
{
|
|
1385
1529
|
type: "button",
|
|
1386
1530
|
"data-vector-fill-gap": "",
|
|
1387
|
-
className:
|
|
1531
|
+
className: buttonClass6,
|
|
1388
1532
|
"aria-pressed": editing.fillGap > 0,
|
|
1389
1533
|
title: "Close small gaps between path ends when filling",
|
|
1390
1534
|
"aria-label": "Close gaps",
|
|
@@ -1393,12 +1537,12 @@ function VectorToolbar({
|
|
|
1393
1537
|
}
|
|
1394
1538
|
)
|
|
1395
1539
|
] }),
|
|
1396
|
-
showDistort && /* @__PURE__ */
|
|
1540
|
+
showDistort && /* @__PURE__ */ jsx8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx8(
|
|
1397
1541
|
"button",
|
|
1398
1542
|
{
|
|
1399
1543
|
type: "button",
|
|
1400
1544
|
"data-vector-free-distort": "",
|
|
1401
|
-
className:
|
|
1545
|
+
className: buttonClass6,
|
|
1402
1546
|
"aria-pressed": state.freeDistort,
|
|
1403
1547
|
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)`,
|
|
1404
1548
|
"aria-label": "Free Distort",
|
|
@@ -1406,15 +1550,15 @@ function VectorToolbar({
|
|
|
1406
1550
|
children: "Distort"
|
|
1407
1551
|
}
|
|
1408
1552
|
) }),
|
|
1409
|
-
showBlend && /* @__PURE__ */
|
|
1410
|
-
showArrange && /* @__PURE__ */
|
|
1411
|
-
/* @__PURE__ */
|
|
1412
|
-
/* @__PURE__ */
|
|
1553
|
+
showBlend && /* @__PURE__ */ jsx8(VectorBlendControls, { controller }),
|
|
1554
|
+
showArrange && /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1555
|
+
/* @__PURE__ */ jsxs8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
1556
|
+
/* @__PURE__ */ jsx8(
|
|
1413
1557
|
"button",
|
|
1414
1558
|
{
|
|
1415
1559
|
type: "button",
|
|
1416
1560
|
"data-vector-live-boolean": "",
|
|
1417
|
-
className:
|
|
1561
|
+
className: buttonClass6,
|
|
1418
1562
|
"aria-pressed": live,
|
|
1419
1563
|
title: "Live booleans: keep the shapes editable",
|
|
1420
1564
|
"aria-label": "Live booleans",
|
|
@@ -1427,14 +1571,16 @@ function VectorToolbar({
|
|
|
1427
1571
|
act("\u2229", "Intersect", () => controller.booleanOp("intersect", { live }), booleanDisabled),
|
|
1428
1572
|
act("\u22BB", "Exclude", () => controller.booleanOp("exclude", { live }), booleanDisabled),
|
|
1429
1573
|
act("G", "Group", () => controller.group(), count < 1),
|
|
1430
|
-
act("U", "Ungroup", () => controller.ungroup(), !groupSelected)
|
|
1574
|
+
act("U", "Ungroup", () => controller.ungroup(), !groupSelected),
|
|
1575
|
+
act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
|
|
1576
|
+
act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
|
|
1431
1577
|
] }),
|
|
1432
|
-
/* @__PURE__ */
|
|
1578
|
+
/* @__PURE__ */ jsx8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx8(
|
|
1433
1579
|
"button",
|
|
1434
1580
|
{
|
|
1435
1581
|
type: "button",
|
|
1436
1582
|
"data-vector-pathfinder": a.id,
|
|
1437
|
-
className:
|
|
1583
|
+
className: buttonClass6,
|
|
1438
1584
|
title: a.title,
|
|
1439
1585
|
"aria-label": a.title,
|
|
1440
1586
|
disabled: count < a.min,
|
|
@@ -1443,7 +1589,7 @@ function VectorToolbar({
|
|
|
1443
1589
|
},
|
|
1444
1590
|
a.id
|
|
1445
1591
|
)) }),
|
|
1446
|
-
/* @__PURE__ */
|
|
1592
|
+
/* @__PURE__ */ jsxs8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
1447
1593
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
1448
1594
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
1449
1595
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -1461,19 +1607,19 @@ function VectorBlendControls({ controller }) {
|
|
|
1461
1607
|
const blend = getVectorBlendControls(state);
|
|
1462
1608
|
const o = blend.options;
|
|
1463
1609
|
const selected = blend.ids.length > 0;
|
|
1464
|
-
return /* @__PURE__ */
|
|
1465
|
-
/* @__PURE__ */
|
|
1466
|
-
/* @__PURE__ */
|
|
1610
|
+
return /* @__PURE__ */ jsxs8("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
|
|
1611
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass6, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
|
|
1612
|
+
/* @__PURE__ */ jsx8(
|
|
1467
1613
|
"select",
|
|
1468
1614
|
{
|
|
1469
1615
|
"aria-label": "Blend spacing",
|
|
1470
1616
|
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
1471
1617
|
value: o.spacing,
|
|
1472
1618
|
onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
|
|
1473
|
-
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */
|
|
1619
|
+
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx8("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
|
|
1474
1620
|
}
|
|
1475
1621
|
),
|
|
1476
|
-
o.spacing !== "smoothColor" && /* @__PURE__ */
|
|
1622
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ jsx8(
|
|
1477
1623
|
"input",
|
|
1478
1624
|
{
|
|
1479
1625
|
type: "number",
|
|
@@ -1489,12 +1635,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1489
1635
|
}
|
|
1490
1636
|
}
|
|
1491
1637
|
),
|
|
1492
|
-
/* @__PURE__ */
|
|
1638
|
+
/* @__PURE__ */ jsx8(
|
|
1493
1639
|
"button",
|
|
1494
1640
|
{
|
|
1495
1641
|
type: "button",
|
|
1496
1642
|
"data-vector-blend-action": "orient",
|
|
1497
|
-
className:
|
|
1643
|
+
className: buttonClass6,
|
|
1498
1644
|
"aria-pressed": o.orientToSpine,
|
|
1499
1645
|
title: "Orient steps to the spine",
|
|
1500
1646
|
"aria-label": "Orient to spine",
|
|
@@ -1502,12 +1648,12 @@ function VectorBlendControls({ controller }) {
|
|
|
1502
1648
|
children: "\u293F"
|
|
1503
1649
|
}
|
|
1504
1650
|
),
|
|
1505
|
-
/* @__PURE__ */
|
|
1651
|
+
/* @__PURE__ */ jsx8(
|
|
1506
1652
|
"button",
|
|
1507
1653
|
{
|
|
1508
1654
|
type: "button",
|
|
1509
1655
|
"data-vector-blend-action": "spine",
|
|
1510
|
-
className:
|
|
1656
|
+
className: buttonClass6,
|
|
1511
1657
|
title: "Replace Spine (select a blend and a path)",
|
|
1512
1658
|
"aria-label": "Replace Spine",
|
|
1513
1659
|
disabled: !blend.canReplaceSpine,
|
|
@@ -1515,11 +1661,11 @@ function VectorBlendControls({ controller }) {
|
|
|
1515
1661
|
children: "\u2307"
|
|
1516
1662
|
}
|
|
1517
1663
|
),
|
|
1518
|
-
/* @__PURE__ */
|
|
1519
|
-
/* @__PURE__ */
|
|
1664
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass6, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
|
|
1665
|
+
/* @__PURE__ */ jsx8("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass6, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
|
|
1520
1666
|
] });
|
|
1521
1667
|
}
|
|
1522
|
-
var
|
|
1668
|
+
var selectClass6 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
1523
1669
|
var numberClass5 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
1524
1670
|
function VectorStylePanel({ controller, className }) {
|
|
1525
1671
|
const state = useVectorEditorState(controller);
|
|
@@ -1551,17 +1697,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1551
1697
|
if (style) controller.setWidthProfile(id);
|
|
1552
1698
|
else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
|
|
1553
1699
|
};
|
|
1554
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */
|
|
1700
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx8("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
1555
1701
|
const arrowRow = (end, label) => {
|
|
1556
1702
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
1557
1703
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
1558
|
-
return /* @__PURE__ */
|
|
1559
|
-
/* @__PURE__ */
|
|
1560
|
-
/* @__PURE__ */
|
|
1561
|
-
/* @__PURE__ */
|
|
1562
|
-
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */
|
|
1704
|
+
return /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
1705
|
+
/* @__PURE__ */ jsx8("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
1706
|
+
/* @__PURE__ */ jsxs8("select", { "aria-label": `${label} arrowhead`, className: selectClass6, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
1707
|
+
/* @__PURE__ */ jsx8("option", { value: "", children: "None" }),
|
|
1708
|
+
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx8("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
1563
1709
|
] }),
|
|
1564
|
-
/* @__PURE__ */
|
|
1710
|
+
/* @__PURE__ */ jsx8(
|
|
1565
1711
|
"input",
|
|
1566
1712
|
{
|
|
1567
1713
|
type: "number",
|
|
@@ -1587,12 +1733,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1587
1733
|
const paint = source[key];
|
|
1588
1734
|
const hex = vectorPaintToHex2(paint);
|
|
1589
1735
|
const on = paint !== null && paint !== "none";
|
|
1590
|
-
return /* @__PURE__ */
|
|
1591
|
-
/* @__PURE__ */
|
|
1592
|
-
/* @__PURE__ */
|
|
1736
|
+
return /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
1737
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
1738
|
+
/* @__PURE__ */ jsx8("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
1593
1739
|
label
|
|
1594
1740
|
] }),
|
|
1595
|
-
/* @__PURE__ */
|
|
1741
|
+
/* @__PURE__ */ jsx8(
|
|
1596
1742
|
"input",
|
|
1597
1743
|
{
|
|
1598
1744
|
type: "color",
|
|
@@ -1603,18 +1749,18 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1603
1749
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
1604
1750
|
}
|
|
1605
1751
|
),
|
|
1606
|
-
on && !hex && /* @__PURE__ */
|
|
1607
|
-
style?.mixed[key] && /* @__PURE__ */
|
|
1752
|
+
on && !hex && /* @__PURE__ */ jsx8("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
1753
|
+
style?.mixed[key] && /* @__PURE__ */ jsx8("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
1608
1754
|
] });
|
|
1609
1755
|
};
|
|
1610
|
-
return /* @__PURE__ */
|
|
1611
|
-
/* @__PURE__ */
|
|
1756
|
+
return /* @__PURE__ */ jsxs8("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
1757
|
+
/* @__PURE__ */ jsx8("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
1612
1758
|
paintRow("fill", "Fill"),
|
|
1613
1759
|
paintRow("stroke", "Stroke"),
|
|
1614
|
-
style && /* @__PURE__ */
|
|
1615
|
-
/* @__PURE__ */
|
|
1616
|
-
/* @__PURE__ */
|
|
1617
|
-
/* @__PURE__ */
|
|
1760
|
+
style && /* @__PURE__ */ jsx8(VectorGradientEditor, { controller }),
|
|
1761
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
1762
|
+
/* @__PURE__ */ jsx8("span", { className: "w-16", children: "Width" }),
|
|
1763
|
+
/* @__PURE__ */ jsx8(
|
|
1618
1764
|
"input",
|
|
1619
1765
|
{
|
|
1620
1766
|
type: "number",
|
|
@@ -1630,33 +1776,33 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1630
1776
|
}
|
|
1631
1777
|
)
|
|
1632
1778
|
] }),
|
|
1633
|
-
/* @__PURE__ */
|
|
1634
|
-
/* @__PURE__ */
|
|
1635
|
-
/* @__PURE__ */
|
|
1779
|
+
/* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
1780
|
+
/* @__PURE__ */ jsx8("span", { className: "w-16", children: "Align" }),
|
|
1781
|
+
/* @__PURE__ */ jsx8("select", { "aria-label": "Stroke alignment", className: selectClass6, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx8("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
|
|
1636
1782
|
mixed("strokeAlign")
|
|
1637
1783
|
] }),
|
|
1638
|
-
/* @__PURE__ */
|
|
1639
|
-
/* @__PURE__ */
|
|
1640
|
-
/* @__PURE__ */
|
|
1784
|
+
/* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
1785
|
+
/* @__PURE__ */ jsx8("span", { className: "w-16", children: "Profile" }),
|
|
1786
|
+
/* @__PURE__ */ jsxs8(
|
|
1641
1787
|
"select",
|
|
1642
1788
|
{
|
|
1643
1789
|
"aria-label": "Width profile",
|
|
1644
|
-
className:
|
|
1790
|
+
className: selectClass6,
|
|
1645
1791
|
value: source.widthProfile,
|
|
1646
1792
|
onChange: (e) => {
|
|
1647
1793
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
1648
1794
|
},
|
|
1649
1795
|
children: [
|
|
1650
|
-
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */
|
|
1651
|
-
source.widthProfile === "custom" && /* @__PURE__ */
|
|
1796
|
+
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx8("option", { value: p.id, children: p.label }, p.id)),
|
|
1797
|
+
source.widthProfile === "custom" && /* @__PURE__ */ jsx8("option", { value: "custom", disabled: true, children: "Custom" })
|
|
1652
1798
|
]
|
|
1653
1799
|
}
|
|
1654
1800
|
),
|
|
1655
|
-
/* @__PURE__ */
|
|
1801
|
+
/* @__PURE__ */ jsx8(
|
|
1656
1802
|
"button",
|
|
1657
1803
|
{
|
|
1658
1804
|
type: "button",
|
|
1659
|
-
className:
|
|
1805
|
+
className: buttonClass6,
|
|
1660
1806
|
title: "Reset to uniform width",
|
|
1661
1807
|
"aria-label": "Reset to uniform width",
|
|
1662
1808
|
disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
|
|
@@ -1666,20 +1812,20 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1666
1812
|
),
|
|
1667
1813
|
mixed("widthProfile")
|
|
1668
1814
|
] }),
|
|
1669
|
-
source.hasOpenPath && /* @__PURE__ */
|
|
1815
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs8(Fragment3, { children: [
|
|
1670
1816
|
arrowRow("start", "Start"),
|
|
1671
1817
|
arrowRow("end", "End"),
|
|
1672
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
1673
|
-
/* @__PURE__ */
|
|
1674
|
-
/* @__PURE__ */
|
|
1675
|
-
/* @__PURE__ */
|
|
1676
|
-
/* @__PURE__ */
|
|
1818
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
1819
|
+
/* @__PURE__ */ jsx8("span", { className: "w-16", children: "Heads" }),
|
|
1820
|
+
/* @__PURE__ */ jsxs8("select", { "aria-label": "Arrowhead alignment", className: selectClass6, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
1821
|
+
/* @__PURE__ */ jsx8("option", { value: "extend", children: "Tip past end" }),
|
|
1822
|
+
/* @__PURE__ */ jsx8("option", { value: "tip", children: "Tip at end" })
|
|
1677
1823
|
] })
|
|
1678
1824
|
] })
|
|
1679
1825
|
] }),
|
|
1680
|
-
source.corners > 0 && /* @__PURE__ */
|
|
1681
|
-
/* @__PURE__ */
|
|
1682
|
-
/* @__PURE__ */
|
|
1826
|
+
source.corners > 0 && /* @__PURE__ */ jsxs8("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
1827
|
+
/* @__PURE__ */ jsx8("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
1828
|
+
/* @__PURE__ */ jsx8(
|
|
1683
1829
|
"input",
|
|
1684
1830
|
{
|
|
1685
1831
|
type: "number",
|
|
@@ -1694,12 +1840,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1694
1840
|
}
|
|
1695
1841
|
}
|
|
1696
1842
|
),
|
|
1697
|
-
/* @__PURE__ */
|
|
1843
|
+
/* @__PURE__ */ jsx8("select", { "aria-label": "Corner kind", className: selectClass6, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx8("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
1698
1844
|
mixed("cornerRadius")
|
|
1699
1845
|
] }),
|
|
1700
|
-
style && /* @__PURE__ */
|
|
1701
|
-
/* @__PURE__ */
|
|
1702
|
-
/* @__PURE__ */
|
|
1846
|
+
style && /* @__PURE__ */ jsxs8("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
1847
|
+
/* @__PURE__ */ jsx8("span", { className: "w-16", children: "Opacity" }),
|
|
1848
|
+
/* @__PURE__ */ jsx8(
|
|
1703
1849
|
"input",
|
|
1704
1850
|
{
|
|
1705
1851
|
type: "range",
|
|
@@ -1713,20 +1859,21 @@ function VectorStylePanel({ controller, className }) {
|
|
|
1713
1859
|
}
|
|
1714
1860
|
)
|
|
1715
1861
|
] }),
|
|
1716
|
-
style && /* @__PURE__ */
|
|
1717
|
-
style && /* @__PURE__ */
|
|
1718
|
-
style && /* @__PURE__ */
|
|
1862
|
+
style && /* @__PURE__ */ jsx8(VectorAppearancePanel, { controller }),
|
|
1863
|
+
style && /* @__PURE__ */ jsx8(VectorEffectsPanel, { controller }),
|
|
1864
|
+
style && /* @__PURE__ */ jsx8(VectorEnvelopePanel, { controller }),
|
|
1865
|
+
style && /* @__PURE__ */ jsx8(VectorMaskPanel, { controller })
|
|
1719
1866
|
] });
|
|
1720
1867
|
}
|
|
1721
1868
|
|
|
1722
1869
|
// src/vector/VectorEditor.tsx
|
|
1723
|
-
import { jsx as
|
|
1870
|
+
import { jsx as jsx9, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
1724
1871
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }) {
|
|
1725
|
-
return /* @__PURE__ */
|
|
1726
|
-
/* @__PURE__ */
|
|
1727
|
-
/* @__PURE__ */
|
|
1728
|
-
/* @__PURE__ */
|
|
1729
|
-
showStylePanel && /* @__PURE__ */
|
|
1872
|
+
return /* @__PURE__ */ jsxs9("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
1873
|
+
/* @__PURE__ */ jsx9(VectorToolbar, { controller, tools }),
|
|
1874
|
+
/* @__PURE__ */ jsxs9("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
1875
|
+
/* @__PURE__ */ jsx9("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx9(VectorCanvas, { controller, ...canvasProps }) }),
|
|
1876
|
+
showStylePanel && /* @__PURE__ */ jsx9(VectorStylePanel, { controller, className: "w-full shrink-0 rounded-md border border-border p-3 md:w-56" })
|
|
1730
1877
|
] })
|
|
1731
1878
|
] });
|
|
1732
1879
|
}
|
|
@@ -1740,6 +1887,8 @@ export {
|
|
|
1740
1887
|
VectorGradientAnnotator,
|
|
1741
1888
|
VectorGradientDefs,
|
|
1742
1889
|
VectorGradientEditor,
|
|
1890
|
+
VectorMaskPanel,
|
|
1891
|
+
VectorMaskedNodes,
|
|
1743
1892
|
VectorSceneNodes,
|
|
1744
1893
|
VectorStylePanel,
|
|
1745
1894
|
VectorToolbar,
|