@pixerate/editor-react 0.83.0 → 0.84.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/vector/index.cjs +371 -318
- package/dist/vector/index.cjs.map +1 -1
- package/dist/vector/index.d.cts +12 -1
- package/dist/vector/index.d.ts +12 -1
- package/dist/vector/index.js +239 -187
- package/dist/vector/index.js.map +1 -1
- package/package.json +3 -3
package/dist/vector/index.js
CHANGED
|
@@ -649,6 +649,11 @@ function VectorCanvas({
|
|
|
649
649
|
sh.lines.map((d, i) => /* @__PURE__ */ jsx2("path", { d, stroke: accentColor, strokeWidth: 1, opacity: 0.7 }, i)),
|
|
650
650
|
sh.points.map((p) => /* @__PURE__ */ jsx2("circle", { "data-vector-shading-point": p.index, "data-selected": p.selected || void 0, cx: p.x, cy: p.y, r: p.selected ? 6 : 4.5, fill: p.color, stroke: p.selected ? accentColor : "#ffffff", strokeWidth: p.selected ? 2 : 1.5 }, p.index))
|
|
651
651
|
] }, `shading:${sh.nodeId}`)),
|
|
652
|
+
overlay.perspective && /* @__PURE__ */ jsxs2("g", { "data-vector-perspective-grid": "", fill: "none", pointerEvents: "none", children: [
|
|
653
|
+
overlay.perspective.planes.map((pl) => /* @__PURE__ */ jsx2("path", { "data-vector-perspective-plane": pl.plane, "data-active": pl.active || void 0, d: pl.d, stroke: pl.active ? accentColor : guideColor, strokeWidth: pl.active ? 1 : 0.75, opacity: pl.active ? 0.55 : 0.25 }, pl.plane)),
|
|
654
|
+
overlay.perspective.vanishingPoints.map((v, i) => /* @__PURE__ */ jsx2("circle", { "data-vector-vanishing-point": i, cx: v.x, cy: v.y, r: 4, stroke: accentColor, strokeWidth: 1.5 }, i)),
|
|
655
|
+
/* @__PURE__ */ jsx2("circle", { "data-vector-perspective-origin": "", cx: overlay.perspective.origin.x, cy: overlay.perspective.origin.y, r: 3, fill: accentColor })
|
|
656
|
+
] }),
|
|
652
657
|
overlay.symmetry.map((sy) => /* @__PURE__ */ jsx2("g", { "data-vector-symmetry-axes": sy.nodeId, stroke: accentColor, strokeDasharray: sy.drawing ? void 0 : "6 4", opacity: sy.drawing ? 0.9 : 0.6, children: sy.lines.map((l, i) => /* @__PURE__ */ jsx2("line", { x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2 }, i)) }, `sym:${sy.nodeId}`)),
|
|
653
658
|
overlay.trackingPoints.map((t, i) => /* @__PURE__ */ jsx2("path", { "data-vector-tracking-point": "", d: `M${t.x - 5} ${t.y}H${t.x + 5}M${t.x} ${t.y - 5}V${t.y + 5}`, stroke: guideColor, strokeWidth: 1.5 }, `track:${i}`)),
|
|
654
659
|
overlay.typedEntry && /* @__PURE__ */ jsxs2("g", { "data-vector-typed-entry": "", transform: `translate(${overlay.typedEntry.x + 14} ${overlay.typedEntry.y + 18})`, children: [
|
|
@@ -785,7 +790,7 @@ function VectorCanvas({
|
|
|
785
790
|
}
|
|
786
791
|
|
|
787
792
|
// src/vector/VectorToolbar.tsx
|
|
788
|
-
import
|
|
793
|
+
import React10 from "react";
|
|
789
794
|
import {
|
|
790
795
|
VECTOR_TOOLS,
|
|
791
796
|
VECTOR_TOOL_GLYPHS,
|
|
@@ -2091,10 +2096,55 @@ function VectorSymmetryPanel({ controller, className }) {
|
|
|
2091
2096
|
] });
|
|
2092
2097
|
}
|
|
2093
2098
|
|
|
2094
|
-
// src/vector/
|
|
2095
|
-
import
|
|
2099
|
+
// src/vector/VectorPerspectivePanel.tsx
|
|
2100
|
+
import React9 from "react";
|
|
2101
|
+
import { vectorPerspectivePlanes, vectorPerspectivePreset } from "@pixerate/editor/vector";
|
|
2096
2102
|
import { jsx as jsx15, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
2103
|
+
var buttonClass13 = "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";
|
|
2097
2104
|
var selectClass10 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2105
|
+
var PLANE_LABELS = { floor: "Floor", left: "Left wall", right: "Right wall", side: "Side wall", front: "Front" };
|
|
2106
|
+
function VectorPerspectivePanel({ controller, className }) {
|
|
2107
|
+
const state = useVectorEditorState(controller);
|
|
2108
|
+
const grid = state.document.perspective ?? null;
|
|
2109
|
+
const planes = grid ? vectorPerspectivePlanes(grid) : [];
|
|
2110
|
+
const [attachTo, setAttachTo] = React9.useState("");
|
|
2111
|
+
const target = (state.perspectivePlane ?? attachTo) || planes[0];
|
|
2112
|
+
const add = (n) => controller.setPerspective(vectorPerspectivePreset(n, state.document.width, state.document.height));
|
|
2113
|
+
return /* @__PURE__ */ jsxs15("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-perspective-panel": "", "aria-label": "Perspective", children: [
|
|
2114
|
+
/* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
2115
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Perspective" }),
|
|
2116
|
+
[1, 2, 3].map((n) => /* @__PURE__ */ jsxs15("button", { type: "button", className: buttonClass13, "aria-pressed": grid?.vanishingPoints.length === n, "data-vector-perspective-preset": n, title: `A ${n}-point perspective grid for the page`, onClick: () => add(n), children: [
|
|
2117
|
+
n,
|
|
2118
|
+
"-pt"
|
|
2119
|
+
] }, n))
|
|
2120
|
+
] }),
|
|
2121
|
+
grid && /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
2122
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16" }),
|
|
2123
|
+
/* @__PURE__ */ jsxs15("select", { "aria-label": "Draw on plane", title: "New rectangles, ellipses, polygons and stars are drawn on this plane", className: selectClass10, value: state.perspectivePlane ?? "", onChange: (e) => controller.setPerspectivePlane(e.target.value || null), children: [
|
|
2124
|
+
/* @__PURE__ */ jsx15("option", { value: "", children: "Draw flat" }),
|
|
2125
|
+
planes.map((p) => /* @__PURE__ */ jsxs15("option", { value: p, children: [
|
|
2126
|
+
"Draw on ",
|
|
2127
|
+
PLANE_LABELS[p].toLowerCase()
|
|
2128
|
+
] }, p))
|
|
2129
|
+
] })
|
|
2130
|
+
] }),
|
|
2131
|
+
grid && /* @__PURE__ */ jsxs15("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
2132
|
+
/* @__PURE__ */ jsx15("span", { className: "w-16" }),
|
|
2133
|
+
!state.perspectivePlane && /* @__PURE__ */ jsx15("select", { "aria-label": "Attach to plane", className: "h-7 w-24 rounded border border-border bg-background px-1 text-xs", value: attachTo || planes[0], onChange: (e) => setAttachTo(e.target.value), children: planes.map((p) => /* @__PURE__ */ jsx15("option", { value: p, children: PLANE_LABELS[p] }, p)) }),
|
|
2134
|
+
/* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, disabled: !state.selection.length, title: "Lay the selection onto the plane (read flat from the grid's origin)", onClick: () => controller.attachToPerspective(target), children: "Attach" }),
|
|
2135
|
+
/* @__PURE__ */ jsxs15("label", { className: "flex items-center gap-1 text-xs text-foreground", title: "Show the grid on the canvas", children: [
|
|
2136
|
+
/* @__PURE__ */ jsx15("input", { type: "checkbox", "aria-label": "Show perspective grid", checked: grid.visible !== false, onChange: (e) => controller.setPerspective({ ...grid, visible: e.target.checked }) }),
|
|
2137
|
+
"Show"
|
|
2138
|
+
] }),
|
|
2139
|
+
/* @__PURE__ */ jsx15("button", { type: "button", className: buttonClass13, title: "Remove the grid (art on it stays)", onClick: () => controller.setPerspective(null), children: "Remove" })
|
|
2140
|
+
] })
|
|
2141
|
+
] });
|
|
2142
|
+
}
|
|
2143
|
+
|
|
2144
|
+
// src/vector/VectorLensPanel.tsx
|
|
2145
|
+
import { VECTOR_LENS_LABELS, VECTOR_LENS_SPECS, VECTOR_LENS_TYPES } from "@pixerate/editor/vector";
|
|
2146
|
+
import { jsx as jsx16, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
2147
|
+
var selectClass11 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2098
2148
|
var numberClass9 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
2099
2149
|
function VectorLensPanel({ controller, className }) {
|
|
2100
2150
|
const state = useVectorEditorState(controller);
|
|
@@ -2103,26 +2153,26 @@ function VectorLensPanel({ controller, className }) {
|
|
|
2103
2153
|
const lens = path.lens ?? null;
|
|
2104
2154
|
const spec = lens ? VECTOR_LENS_SPECS[lens.type] : null;
|
|
2105
2155
|
const ids = [path.id];
|
|
2106
|
-
return /* @__PURE__ */
|
|
2107
|
-
/* @__PURE__ */
|
|
2108
|
-
/* @__PURE__ */
|
|
2109
|
-
/* @__PURE__ */
|
|
2156
|
+
return /* @__PURE__ */ jsxs16("div", { className: `space-y-2 ${className ?? ""}`, "data-vector-lens-panel": "", "aria-label": "Lens", children: [
|
|
2157
|
+
/* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
2158
|
+
/* @__PURE__ */ jsx16("span", { className: "w-16 text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: "Lens" }),
|
|
2159
|
+
/* @__PURE__ */ jsxs16(
|
|
2110
2160
|
"select",
|
|
2111
2161
|
{
|
|
2112
2162
|
"aria-label": "Lens type",
|
|
2113
|
-
className:
|
|
2163
|
+
className: selectClass11,
|
|
2114
2164
|
value: lens?.type ?? "",
|
|
2115
2165
|
onChange: (e) => e.target.value ? controller.setLens({ type: e.target.value }, { ids }) : controller.setLens(null, { ids }),
|
|
2116
2166
|
children: [
|
|
2117
|
-
/* @__PURE__ */
|
|
2118
|
-
VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */
|
|
2167
|
+
/* @__PURE__ */ jsx16("option", { value: "", children: "None" }),
|
|
2168
|
+
VECTOR_LENS_TYPES.map((t) => /* @__PURE__ */ jsx16("option", { value: t, children: VECTOR_LENS_LABELS[t] }, t))
|
|
2119
2169
|
]
|
|
2120
2170
|
}
|
|
2121
2171
|
)
|
|
2122
2172
|
] }),
|
|
2123
|
-
lens && spec && /* @__PURE__ */
|
|
2124
|
-
/* @__PURE__ */
|
|
2125
|
-
/* @__PURE__ */
|
|
2173
|
+
lens && spec && /* @__PURE__ */ jsxs16("div", { className: "flex flex-wrap items-center gap-1", children: [
|
|
2174
|
+
/* @__PURE__ */ jsx16("span", { className: "w-16" }),
|
|
2175
|
+
/* @__PURE__ */ jsx16(
|
|
2126
2176
|
"input",
|
|
2127
2177
|
{
|
|
2128
2178
|
type: "number",
|
|
@@ -2139,7 +2189,7 @@ function VectorLensPanel({ controller, className }) {
|
|
|
2139
2189
|
}
|
|
2140
2190
|
}
|
|
2141
2191
|
),
|
|
2142
|
-
spec.color !== null && /* @__PURE__ */
|
|
2192
|
+
spec.color !== null && /* @__PURE__ */ jsx16("input", { type: "color", "aria-label": "Lens colour", title: "Lens colour", className: "h-7 w-9 rounded border border-border bg-background", value: lens.color ?? spec.color, onChange: (e) => controller.setLens({ color: e.target.value }, { ids, merge: `lens-color-${path.id}` }) })
|
|
2143
2193
|
] })
|
|
2144
2194
|
] });
|
|
2145
2195
|
}
|
|
@@ -2157,10 +2207,10 @@ import {
|
|
|
2157
2207
|
getVectorGradientEditor,
|
|
2158
2208
|
vectorPaintToHex as vectorPaintToHex5
|
|
2159
2209
|
} from "@pixerate/editor/vector";
|
|
2160
|
-
import { Fragment as Fragment5, jsx as
|
|
2161
|
-
var
|
|
2210
|
+
import { Fragment as Fragment5, jsx as jsx17, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
2211
|
+
var selectClass12 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2162
2212
|
var numberClass10 = "h-7 w-14 rounded border border-border bg-background px-1.5 text-xs";
|
|
2163
|
-
var
|
|
2213
|
+
var buttonClass14 = "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";
|
|
2164
2214
|
var percent = (v) => Math.round(v * 1e3) / 10;
|
|
2165
2215
|
function VectorGradientEditor({ controller, className }) {
|
|
2166
2216
|
const state = useVectorEditorState(controller);
|
|
@@ -2175,23 +2225,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2175
2225
|
controller.setStyle({ [ed.target]: vectorPaintToHex5(first?.color) ?? first?.color ?? "#000000" }, void 0, { label: "Solid Colour" });
|
|
2176
2226
|
} else controller.setGradient({ type: value });
|
|
2177
2227
|
};
|
|
2178
|
-
return /* @__PURE__ */
|
|
2179
|
-
/* @__PURE__ */
|
|
2180
|
-
/* @__PURE__ */
|
|
2181
|
-
["fill", "stroke"].map((t) => /* @__PURE__ */
|
|
2182
|
-
ed.mixed && /* @__PURE__ */
|
|
2228
|
+
return /* @__PURE__ */ jsxs17("div", { className: `space-y-1.5 ${className ?? ""}`, "data-vector-gradient-editor": ed.target, "aria-label": "Gradient", children: [
|
|
2229
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-1.5", children: [
|
|
2230
|
+
/* @__PURE__ */ jsx17("span", { className: "w-16 text-xs text-foreground", children: "Gradient" }),
|
|
2231
|
+
["fill", "stroke"].map((t) => /* @__PURE__ */ jsx17("button", { type: "button", className: buttonClass14, "aria-pressed": ed.target === t, onClick: () => controller.setGradientTarget(t), children: t === "fill" ? "Fill" : "Stroke" }, t)),
|
|
2232
|
+
ed.mixed && /* @__PURE__ */ jsx17("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
2183
2233
|
] }),
|
|
2184
|
-
/* @__PURE__ */
|
|
2185
|
-
/* @__PURE__ */
|
|
2186
|
-
/* @__PURE__ */
|
|
2187
|
-
/* @__PURE__ */
|
|
2188
|
-
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */
|
|
2234
|
+
/* @__PURE__ */ jsxs17("div", { className: "flex items-center gap-1.5", children: [
|
|
2235
|
+
/* @__PURE__ */ jsx17("span", { className: "w-16 text-xs text-foreground", children: "Type" }),
|
|
2236
|
+
/* @__PURE__ */ jsxs17("select", { "aria-label": "Gradient type", className: selectClass12, value: ed.type ?? "solid", onChange: (e) => setType(e.target.value), children: [
|
|
2237
|
+
/* @__PURE__ */ jsx17("option", { value: "solid", children: "Solid" }),
|
|
2238
|
+
VECTOR_GRADIENT_TYPES.map((t) => /* @__PURE__ */ jsx17("option", { value: t, children: VECTOR_GRADIENT_TYPE_LABELS[t] }, t))
|
|
2189
2239
|
] }),
|
|
2190
|
-
/* @__PURE__ */
|
|
2240
|
+
/* @__PURE__ */ jsx17("button", { type: "button", className: buttonClass14, "aria-label": "Reverse gradient", title: "Reverse gradient", disabled: !g, onClick: () => controller.reverseGradient(), children: "\u21C4" })
|
|
2191
2241
|
] }),
|
|
2192
|
-
g && /* @__PURE__ */
|
|
2193
|
-
/* @__PURE__ */
|
|
2194
|
-
/* @__PURE__ */
|
|
2242
|
+
g && /* @__PURE__ */ jsxs17(Fragment5, { children: [
|
|
2243
|
+
/* @__PURE__ */ jsxs17("div", { className: "relative h-8", "data-vector-gradient-ramp": "", children: [
|
|
2244
|
+
/* @__PURE__ */ jsx17(
|
|
2195
2245
|
"div",
|
|
2196
2246
|
{
|
|
2197
2247
|
role: "button",
|
|
@@ -2205,7 +2255,7 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2205
2255
|
}
|
|
2206
2256
|
}
|
|
2207
2257
|
),
|
|
2208
|
-
ed.stops.map((s) => /* @__PURE__ */
|
|
2258
|
+
ed.stops.map((s) => /* @__PURE__ */ jsx17(
|
|
2209
2259
|
"button",
|
|
2210
2260
|
{
|
|
2211
2261
|
type: "button",
|
|
@@ -2219,8 +2269,8 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2219
2269
|
s.index
|
|
2220
2270
|
))
|
|
2221
2271
|
] }),
|
|
2222
|
-
sel && /* @__PURE__ */
|
|
2223
|
-
/* @__PURE__ */
|
|
2272
|
+
sel && /* @__PURE__ */ jsxs17("div", { className: "flex flex-wrap items-center gap-1.5", "data-vector-gradient-selected-stop": sel.index, children: [
|
|
2273
|
+
/* @__PURE__ */ jsx17(
|
|
2224
2274
|
"input",
|
|
2225
2275
|
{
|
|
2226
2276
|
type: "color",
|
|
@@ -2231,9 +2281,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2231
2281
|
onBlur: end
|
|
2232
2282
|
}
|
|
2233
2283
|
),
|
|
2234
|
-
/* @__PURE__ */
|
|
2284
|
+
/* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2235
2285
|
"Opacity",
|
|
2236
|
-
/* @__PURE__ */
|
|
2286
|
+
/* @__PURE__ */ jsx17(
|
|
2237
2287
|
"input",
|
|
2238
2288
|
{
|
|
2239
2289
|
type: "number",
|
|
@@ -2250,9 +2300,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2250
2300
|
}
|
|
2251
2301
|
)
|
|
2252
2302
|
] }),
|
|
2253
|
-
/* @__PURE__ */
|
|
2303
|
+
/* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2254
2304
|
"At",
|
|
2255
|
-
/* @__PURE__ */
|
|
2305
|
+
/* @__PURE__ */ jsx17(
|
|
2256
2306
|
"input",
|
|
2257
2307
|
{
|
|
2258
2308
|
type: "number",
|
|
@@ -2269,9 +2319,9 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2269
2319
|
}
|
|
2270
2320
|
)
|
|
2271
2321
|
] }),
|
|
2272
|
-
!sel.last && /* @__PURE__ */
|
|
2322
|
+
!sel.last && /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1 text-[11px] text-muted-foreground", children: [
|
|
2273
2323
|
"Midpoint",
|
|
2274
|
-
/* @__PURE__ */
|
|
2324
|
+
/* @__PURE__ */ jsx17(
|
|
2275
2325
|
"input",
|
|
2276
2326
|
{
|
|
2277
2327
|
type: "number",
|
|
@@ -2288,15 +2338,15 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2288
2338
|
}
|
|
2289
2339
|
)
|
|
2290
2340
|
] }),
|
|
2291
|
-
/* @__PURE__ */
|
|
2341
|
+
/* @__PURE__ */ jsx17("button", { type: "button", className: buttonClass14, "aria-label": "Delete stop", title: "Delete stop", disabled: ed.stops.length <= 2, onClick: () => controller.removeGradientStop(sel.index), children: "\u2715" })
|
|
2292
2342
|
] }),
|
|
2293
|
-
/* @__PURE__ */
|
|
2294
|
-
/* @__PURE__ */
|
|
2295
|
-
/* @__PURE__ */
|
|
2343
|
+
/* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2344
|
+
/* @__PURE__ */ jsx17("span", { className: "w-16", children: "Colours" }),
|
|
2345
|
+
/* @__PURE__ */ jsx17("select", { "aria-label": "Gradient interpolation", className: selectClass12, value: ed.interpolation, onChange: (e) => controller.setGradient({ interpolation: e.target.value }), children: VECTOR_GRADIENT_INTERPOLATIONS.map((i) => /* @__PURE__ */ jsx17("option", { value: i, children: VECTOR_GRADIENT_INTERPOLATION_LABELS[i] }, i)) })
|
|
2296
2346
|
] }),
|
|
2297
|
-
/* @__PURE__ */
|
|
2298
|
-
/* @__PURE__ */
|
|
2299
|
-
/* @__PURE__ */
|
|
2347
|
+
/* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2348
|
+
/* @__PURE__ */ jsx17("span", { className: "w-16", children: "Smooth" }),
|
|
2349
|
+
/* @__PURE__ */ jsx17(
|
|
2300
2350
|
"input",
|
|
2301
2351
|
{
|
|
2302
2352
|
type: "range",
|
|
@@ -2312,23 +2362,23 @@ function VectorGradientEditor({ controller, className }) {
|
|
|
2312
2362
|
}
|
|
2313
2363
|
)
|
|
2314
2364
|
] }),
|
|
2315
|
-
/* @__PURE__ */
|
|
2316
|
-
/* @__PURE__ */
|
|
2317
|
-
/* @__PURE__ */
|
|
2365
|
+
/* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2366
|
+
/* @__PURE__ */ jsx17("span", { className: "w-16", children: "Spread" }),
|
|
2367
|
+
/* @__PURE__ */ jsx17("select", { "aria-label": "Gradient spread", className: selectClass12, value: ed.spread, onChange: (e) => controller.setGradient({ spread: e.target.value }), children: VECTOR_GRADIENT_SPREADS.map((sp) => /* @__PURE__ */ jsx17("option", { value: sp, children: VECTOR_GRADIENT_SPREAD_LABELS[sp] }, sp)) })
|
|
2318
2368
|
] }),
|
|
2319
|
-
ed.target === "stroke" && /* @__PURE__ */
|
|
2320
|
-
/* @__PURE__ */
|
|
2321
|
-
/* @__PURE__ */
|
|
2369
|
+
ed.target === "stroke" && /* @__PURE__ */ jsxs17("label", { className: "flex items-center gap-1.5 text-xs text-foreground", children: [
|
|
2370
|
+
/* @__PURE__ */ jsx17("span", { className: "w-16", children: "On stroke" }),
|
|
2371
|
+
/* @__PURE__ */ jsx17("select", { "aria-label": "Gradient on stroke", className: selectClass12, value: ed.strokeMode, onChange: (e) => controller.setGradient({ strokeMode: e.target.value }), children: VECTOR_STROKE_GRADIENT_MODES.map((m) => /* @__PURE__ */ jsx17("option", { value: m, children: VECTOR_STROKE_GRADIENT_MODE_LABELS[m] }, m)) })
|
|
2322
2372
|
] })
|
|
2323
2373
|
] })
|
|
2324
2374
|
] });
|
|
2325
2375
|
}
|
|
2326
2376
|
|
|
2327
2377
|
// src/vector/VectorToolbar.tsx
|
|
2328
|
-
import { Fragment as Fragment6, jsx as
|
|
2378
|
+
import { Fragment as Fragment6, jsx as jsx18, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
2329
2379
|
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
2330
2380
|
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
2331
|
-
var
|
|
2381
|
+
var buttonClass15 = "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";
|
|
2332
2382
|
function VectorToolbar({
|
|
2333
2383
|
controller,
|
|
2334
2384
|
tools = VECTOR_TOOLS,
|
|
@@ -2346,16 +2396,16 @@ function VectorToolbar({
|
|
|
2346
2396
|
const editing = state.editing;
|
|
2347
2397
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
2348
2398
|
const maskSelected = state.selection.some((id) => isVectorMaskGroup(controller.getNode(id)));
|
|
2349
|
-
const [live, setLive] =
|
|
2399
|
+
const [live, setLive] = React10.useState(false);
|
|
2350
2400
|
const booleanDisabled = live ? count < 1 : count < 2;
|
|
2351
|
-
const act = (label, title, run, disabled = false) => /* @__PURE__ */
|
|
2352
|
-
return /* @__PURE__ */
|
|
2353
|
-
/* @__PURE__ */
|
|
2401
|
+
const act = (label, title, run, disabled = false) => /* @__PURE__ */ jsx18("button", { type: "button", className: buttonClass15, title, "aria-label": title, disabled, onClick: () => void run(), children: label }, label);
|
|
2402
|
+
return /* @__PURE__ */ jsxs18("div", { role: "toolbar", "aria-label": "Vector tools", className: `flex flex-wrap items-center gap-1 ${className ?? ""}`, children: [
|
|
2403
|
+
/* @__PURE__ */ jsx18("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Tools", children: tools.map((tool) => /* @__PURE__ */ jsx18(
|
|
2354
2404
|
"button",
|
|
2355
2405
|
{
|
|
2356
2406
|
type: "button",
|
|
2357
2407
|
"data-vector-tool": tool,
|
|
2358
|
-
className:
|
|
2408
|
+
className: buttonClass15,
|
|
2359
2409
|
"aria-pressed": state.tool === tool,
|
|
2360
2410
|
title: `${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`,
|
|
2361
2411
|
"aria-label": vectorToolLabel(tool),
|
|
@@ -2364,17 +2414,17 @@ function VectorToolbar({
|
|
|
2364
2414
|
},
|
|
2365
2415
|
tool
|
|
2366
2416
|
)) }),
|
|
2367
|
-
showHistory && /* @__PURE__ */
|
|
2417
|
+
showHistory && /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "History", children: [
|
|
2368
2418
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
2369
2419
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
2370
2420
|
] }),
|
|
2371
|
-
showSnap && /* @__PURE__ */
|
|
2372
|
-
/* @__PURE__ */
|
|
2421
|
+
showSnap && /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: [
|
|
2422
|
+
/* @__PURE__ */ jsx18(
|
|
2373
2423
|
"button",
|
|
2374
2424
|
{
|
|
2375
2425
|
type: "button",
|
|
2376
2426
|
"data-vector-snap": "",
|
|
2377
|
-
className:
|
|
2427
|
+
className: buttonClass15,
|
|
2378
2428
|
"aria-pressed": state.snap.objects,
|
|
2379
2429
|
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
2380
2430
|
"aria-label": VECTOR_SNAP_TITLE,
|
|
@@ -2382,7 +2432,7 @@ function VectorToolbar({
|
|
|
2382
2432
|
children: "Snap"
|
|
2383
2433
|
}
|
|
2384
2434
|
),
|
|
2385
|
-
/* @__PURE__ */
|
|
2435
|
+
/* @__PURE__ */ jsxs18(
|
|
2386
2436
|
"select",
|
|
2387
2437
|
{
|
|
2388
2438
|
"data-vector-polar": "",
|
|
@@ -2392,8 +2442,8 @@ function VectorToolbar({
|
|
|
2392
2442
|
value: String(vectorPolarIncrementOf(state.snap.angles) ?? ""),
|
|
2393
2443
|
onChange: (e) => controller.setSnap({ angles: vectorPolarAngles(Number(e.target.value)) }),
|
|
2394
2444
|
children: [
|
|
2395
|
-
vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */
|
|
2396
|
-
[90, 45, 30, 15].map((n) => /* @__PURE__ */
|
|
2445
|
+
vectorPolarIncrementOf(state.snap.angles) === null && /* @__PURE__ */ jsx18("option", { value: "", children: "Custom" }),
|
|
2446
|
+
[90, 45, 30, 15].map((n) => /* @__PURE__ */ jsxs18("option", { value: n, children: [
|
|
2397
2447
|
n,
|
|
2398
2448
|
"\xB0"
|
|
2399
2449
|
] }, n))
|
|
@@ -2401,13 +2451,13 @@ function VectorToolbar({
|
|
|
2401
2451
|
}
|
|
2402
2452
|
)
|
|
2403
2453
|
] }),
|
|
2404
|
-
showPathEdit && /* @__PURE__ */
|
|
2405
|
-
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */
|
|
2454
|
+
showPathEdit && /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Path editing", children: [
|
|
2455
|
+
VECTOR_PATH_ACTIONS.map((a) => /* @__PURE__ */ jsx18(
|
|
2406
2456
|
"button",
|
|
2407
2457
|
{
|
|
2408
2458
|
type: "button",
|
|
2409
2459
|
"data-vector-path-action": a.id,
|
|
2410
|
-
className:
|
|
2460
|
+
className: buttonClass15,
|
|
2411
2461
|
title: a.shortcut ? `${a.title} (${modKeyLabel()}+${a.shortcut})` : a.title,
|
|
2412
2462
|
"aria-label": a.title,
|
|
2413
2463
|
disabled: !a.enabled(count, points),
|
|
@@ -2416,12 +2466,12 @@ function VectorToolbar({
|
|
|
2416
2466
|
},
|
|
2417
2467
|
a.id
|
|
2418
2468
|
)),
|
|
2419
|
-
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */
|
|
2469
|
+
VECTOR_EDIT_TOGGLES.map((t) => /* @__PURE__ */ jsx18(
|
|
2420
2470
|
"button",
|
|
2421
2471
|
{
|
|
2422
2472
|
type: "button",
|
|
2423
2473
|
"data-vector-edit": t.key,
|
|
2424
|
-
className:
|
|
2474
|
+
className: buttonClass15,
|
|
2425
2475
|
"aria-pressed": editing[t.key],
|
|
2426
2476
|
title: t.title,
|
|
2427
2477
|
"aria-label": t.title,
|
|
@@ -2430,9 +2480,9 @@ function VectorToolbar({
|
|
|
2430
2480
|
},
|
|
2431
2481
|
t.key
|
|
2432
2482
|
)),
|
|
2433
|
-
state.tool === "pencil" && /* @__PURE__ */
|
|
2483
|
+
state.tool === "pencil" && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2434
2484
|
"Smoothing",
|
|
2435
|
-
/* @__PURE__ */
|
|
2485
|
+
/* @__PURE__ */ jsx18(
|
|
2436
2486
|
"input",
|
|
2437
2487
|
{
|
|
2438
2488
|
type: "range",
|
|
@@ -2446,9 +2496,9 @@ function VectorToolbar({
|
|
|
2446
2496
|
}
|
|
2447
2497
|
)
|
|
2448
2498
|
] }),
|
|
2449
|
-
state.tool === "eraser" && /* @__PURE__ */
|
|
2499
|
+
state.tool === "eraser" && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-1 text-xs text-foreground", children: [
|
|
2450
2500
|
"Size",
|
|
2451
|
-
/* @__PURE__ */
|
|
2501
|
+
/* @__PURE__ */ jsx18(
|
|
2452
2502
|
"input",
|
|
2453
2503
|
{
|
|
2454
2504
|
type: "number",
|
|
@@ -2464,12 +2514,12 @@ function VectorToolbar({
|
|
|
2464
2514
|
}
|
|
2465
2515
|
)
|
|
2466
2516
|
] }),
|
|
2467
|
-
state.tool === "paintBucket" && /* @__PURE__ */
|
|
2517
|
+
state.tool === "paintBucket" && /* @__PURE__ */ jsx18(
|
|
2468
2518
|
"button",
|
|
2469
2519
|
{
|
|
2470
2520
|
type: "button",
|
|
2471
2521
|
"data-vector-fill-gap": "",
|
|
2472
|
-
className:
|
|
2522
|
+
className: buttonClass15,
|
|
2473
2523
|
"aria-pressed": editing.fillGap > 0,
|
|
2474
2524
|
title: "Close small gaps between path ends when filling",
|
|
2475
2525
|
"aria-label": "Close gaps",
|
|
@@ -2478,12 +2528,12 @@ function VectorToolbar({
|
|
|
2478
2528
|
}
|
|
2479
2529
|
)
|
|
2480
2530
|
] }),
|
|
2481
|
-
showDistort && /* @__PURE__ */
|
|
2531
|
+
showDistort && /* @__PURE__ */ jsx18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Distort", children: /* @__PURE__ */ jsx18(
|
|
2482
2532
|
"button",
|
|
2483
2533
|
{
|
|
2484
2534
|
type: "button",
|
|
2485
2535
|
"data-vector-free-distort": "",
|
|
2486
|
-
className:
|
|
2536
|
+
className: buttonClass15,
|
|
2487
2537
|
"aria-pressed": state.freeDistort,
|
|
2488
2538
|
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)`,
|
|
2489
2539
|
"aria-label": "Free Distort",
|
|
@@ -2491,15 +2541,15 @@ function VectorToolbar({
|
|
|
2491
2541
|
children: "Distort"
|
|
2492
2542
|
}
|
|
2493
2543
|
) }),
|
|
2494
|
-
showBlend && /* @__PURE__ */
|
|
2495
|
-
showArrange && /* @__PURE__ */
|
|
2496
|
-
/* @__PURE__ */
|
|
2497
|
-
/* @__PURE__ */
|
|
2544
|
+
showBlend && /* @__PURE__ */ jsx18(VectorBlendControls, { controller }),
|
|
2545
|
+
showArrange && /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2546
|
+
/* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
2547
|
+
/* @__PURE__ */ jsx18(
|
|
2498
2548
|
"button",
|
|
2499
2549
|
{
|
|
2500
2550
|
type: "button",
|
|
2501
2551
|
"data-vector-live-boolean": "",
|
|
2502
|
-
className:
|
|
2552
|
+
className: buttonClass15,
|
|
2503
2553
|
"aria-pressed": live,
|
|
2504
2554
|
title: "Live booleans: keep the shapes editable",
|
|
2505
2555
|
"aria-label": "Live booleans",
|
|
@@ -2516,12 +2566,12 @@ function VectorToolbar({
|
|
|
2516
2566
|
act("\u25D0", `Make Clipping Mask (${modKeyLabel()}+7)`, () => controller.makeMask("clip"), count < 2),
|
|
2517
2567
|
act("\u25D1", `Release Mask (${modKeyLabel()}+Alt+7)`, () => controller.releaseMask(), !maskSelected)
|
|
2518
2568
|
] }),
|
|
2519
|
-
/* @__PURE__ */
|
|
2569
|
+
/* @__PURE__ */ jsx18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Pathfinder", children: VECTOR_PATHFINDER_ACTIONS.map((a) => /* @__PURE__ */ jsx18(
|
|
2520
2570
|
"button",
|
|
2521
2571
|
{
|
|
2522
2572
|
type: "button",
|
|
2523
2573
|
"data-vector-pathfinder": a.id,
|
|
2524
|
-
className:
|
|
2574
|
+
className: buttonClass15,
|
|
2525
2575
|
title: a.title,
|
|
2526
2576
|
"aria-label": a.title,
|
|
2527
2577
|
disabled: count < a.min,
|
|
@@ -2530,7 +2580,7 @@ function VectorToolbar({
|
|
|
2530
2580
|
},
|
|
2531
2581
|
a.id
|
|
2532
2582
|
)) }),
|
|
2533
|
-
/* @__PURE__ */
|
|
2583
|
+
/* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Arrange", children: [
|
|
2534
2584
|
act("\u21C8", "Bring to Front", () => controller.arrange("front"), count < 1),
|
|
2535
2585
|
act("\u21CA", "Send to Back", () => controller.arrange("back"), count < 1),
|
|
2536
2586
|
act("\u21E4", "Align Left", () => controller.align("left"), count < 1),
|
|
@@ -2548,19 +2598,19 @@ function VectorBlendControls({ controller }) {
|
|
|
2548
2598
|
const blend = getVectorBlendControls(state);
|
|
2549
2599
|
const o = blend.options;
|
|
2550
2600
|
const selected = blend.ids.length > 0;
|
|
2551
|
-
return /* @__PURE__ */
|
|
2552
|
-
/* @__PURE__ */
|
|
2553
|
-
/* @__PURE__ */
|
|
2601
|
+
return /* @__PURE__ */ jsxs18("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Blend", "data-vector-blend-controls": "", children: [
|
|
2602
|
+
/* @__PURE__ */ jsx18("button", { type: "button", "data-vector-blend-action": "make", className: buttonClass15, title: "Make Blend", "aria-label": "Make Blend", disabled: !blend.canBlend, onClick: () => controller.blend(), children: "\u29D3" }),
|
|
2603
|
+
/* @__PURE__ */ jsx18(
|
|
2554
2604
|
"select",
|
|
2555
2605
|
{
|
|
2556
2606
|
"aria-label": "Blend spacing",
|
|
2557
2607
|
className: "h-7 rounded border border-border bg-background px-1 text-xs",
|
|
2558
2608
|
value: o.spacing,
|
|
2559
2609
|
onChange: (e) => applyVectorBlendOptions(controller, { spacing: e.target.value }),
|
|
2560
|
-
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */
|
|
2610
|
+
children: VECTOR_BLEND_SPACINGS.map((sp) => /* @__PURE__ */ jsx18("option", { value: sp, children: VECTOR_BLEND_SPACING_LABELS[sp] }, sp))
|
|
2561
2611
|
}
|
|
2562
2612
|
),
|
|
2563
|
-
o.spacing !== "smoothColor" && /* @__PURE__ */
|
|
2613
|
+
o.spacing !== "smoothColor" && /* @__PURE__ */ jsx18(
|
|
2564
2614
|
"input",
|
|
2565
2615
|
{
|
|
2566
2616
|
type: "number",
|
|
@@ -2576,12 +2626,12 @@ function VectorBlendControls({ controller }) {
|
|
|
2576
2626
|
}
|
|
2577
2627
|
}
|
|
2578
2628
|
),
|
|
2579
|
-
/* @__PURE__ */
|
|
2629
|
+
/* @__PURE__ */ jsx18(
|
|
2580
2630
|
"button",
|
|
2581
2631
|
{
|
|
2582
2632
|
type: "button",
|
|
2583
2633
|
"data-vector-blend-action": "orient",
|
|
2584
|
-
className:
|
|
2634
|
+
className: buttonClass15,
|
|
2585
2635
|
"aria-pressed": o.orientToSpine,
|
|
2586
2636
|
title: "Orient steps to the spine",
|
|
2587
2637
|
"aria-label": "Orient to spine",
|
|
@@ -2589,12 +2639,12 @@ function VectorBlendControls({ controller }) {
|
|
|
2589
2639
|
children: "\u293F"
|
|
2590
2640
|
}
|
|
2591
2641
|
),
|
|
2592
|
-
/* @__PURE__ */
|
|
2642
|
+
/* @__PURE__ */ jsx18(
|
|
2593
2643
|
"button",
|
|
2594
2644
|
{
|
|
2595
2645
|
type: "button",
|
|
2596
2646
|
"data-vector-blend-action": "spine",
|
|
2597
|
-
className:
|
|
2647
|
+
className: buttonClass15,
|
|
2598
2648
|
title: "Replace Spine (select a blend and a path)",
|
|
2599
2649
|
"aria-label": "Replace Spine",
|
|
2600
2650
|
disabled: !blend.canReplaceSpine,
|
|
@@ -2602,11 +2652,11 @@ function VectorBlendControls({ controller }) {
|
|
|
2602
2652
|
children: "\u2307"
|
|
2603
2653
|
}
|
|
2604
2654
|
),
|
|
2605
|
-
/* @__PURE__ */
|
|
2606
|
-
/* @__PURE__ */
|
|
2655
|
+
/* @__PURE__ */ jsx18("button", { type: "button", "data-vector-blend-action": "expand", className: buttonClass15, title: "Expand Blend", "aria-label": "Expand Blend", disabled: !selected, onClick: () => controller.expandBlend(), children: "\u29C9" }),
|
|
2656
|
+
/* @__PURE__ */ jsx18("button", { type: "button", "data-vector-blend-action": "release", className: buttonClass15, title: "Release Blend", "aria-label": "Release Blend", disabled: !selected, onClick: () => controller.releaseBlend(), children: "\u29C4" })
|
|
2607
2657
|
] });
|
|
2608
2658
|
}
|
|
2609
|
-
var
|
|
2659
|
+
var selectClass13 = "h-7 min-w-0 flex-1 rounded border border-border bg-background px-1 text-xs";
|
|
2610
2660
|
var numberClass11 = "h-7 w-16 rounded border border-border bg-background px-1.5 text-xs";
|
|
2611
2661
|
function VectorStylePanel({ controller, className }) {
|
|
2612
2662
|
const state = useVectorEditorState(controller);
|
|
@@ -2639,17 +2689,17 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2639
2689
|
if (style) controller.setWidthProfile(id);
|
|
2640
2690
|
else controller.setDefaults({ strokeWidthProfile: widthProfilePreset(id)?.length ? widthProfilePreset(id) : null });
|
|
2641
2691
|
};
|
|
2642
|
-
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */
|
|
2692
|
+
const mixed = (key) => style?.mixed[key] ? /* @__PURE__ */ jsx18("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" }) : null;
|
|
2643
2693
|
const arrowRow = (end, label) => {
|
|
2644
2694
|
const key = end === "start" ? "startArrow" : "endArrow";
|
|
2645
2695
|
const scaleKey = end === "start" ? "startArrowScale" : "endArrowScale";
|
|
2646
|
-
return /* @__PURE__ */
|
|
2647
|
-
/* @__PURE__ */
|
|
2648
|
-
/* @__PURE__ */
|
|
2649
|
-
/* @__PURE__ */
|
|
2650
|
-
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */
|
|
2696
|
+
return /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2697
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16 text-xs text-foreground", children: label }),
|
|
2698
|
+
/* @__PURE__ */ jsxs18("select", { "aria-label": `${label} arrowhead`, className: selectClass13, value: source[key] ?? "", onChange: (e) => apply({ [key]: e.target.value || null }), children: [
|
|
2699
|
+
/* @__PURE__ */ jsx18("option", { value: "", children: "None" }),
|
|
2700
|
+
VECTOR_ARROWHEADS.map((h) => /* @__PURE__ */ jsx18("option", { value: h, children: VECTOR_ARROWHEAD_LABELS[h] }, h))
|
|
2651
2701
|
] }),
|
|
2652
|
-
/* @__PURE__ */
|
|
2702
|
+
/* @__PURE__ */ jsx18(
|
|
2653
2703
|
"input",
|
|
2654
2704
|
{
|
|
2655
2705
|
type: "number",
|
|
@@ -2675,12 +2725,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2675
2725
|
const paint = source[key];
|
|
2676
2726
|
const hex = vectorPaintToHex6(paint);
|
|
2677
2727
|
const on = paint !== null && paint !== "none";
|
|
2678
|
-
return /* @__PURE__ */
|
|
2679
|
-
/* @__PURE__ */
|
|
2680
|
-
/* @__PURE__ */
|
|
2728
|
+
return /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", "data-vector-style": key, children: [
|
|
2729
|
+
/* @__PURE__ */ jsxs18("label", { className: "flex w-16 items-center gap-1.5 text-xs text-foreground", children: [
|
|
2730
|
+
/* @__PURE__ */ jsx18("input", { type: "checkbox", checked: on, "aria-label": `${label} on`, onChange: (e) => apply({ [key]: e.target.checked ? hex ?? "#000000" : null }) }),
|
|
2681
2731
|
label
|
|
2682
2732
|
] }),
|
|
2683
|
-
/* @__PURE__ */
|
|
2733
|
+
/* @__PURE__ */ jsx18(
|
|
2684
2734
|
"input",
|
|
2685
2735
|
{
|
|
2686
2736
|
type: "color",
|
|
@@ -2691,18 +2741,18 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2691
2741
|
onChange: (e) => apply({ [key]: e.target.value })
|
|
2692
2742
|
}
|
|
2693
2743
|
),
|
|
2694
|
-
on && !hex && /* @__PURE__ */
|
|
2695
|
-
style?.mixed[key] && /* @__PURE__ */
|
|
2744
|
+
on && !hex && /* @__PURE__ */ jsx18("span", { className: "truncate text-[11px] text-muted-foreground", children: typeof paint === "string" ? paint : "Gradient" }),
|
|
2745
|
+
style?.mixed[key] && /* @__PURE__ */ jsx18("span", { className: "text-[11px] text-muted-foreground", children: "Mixed" })
|
|
2696
2746
|
] });
|
|
2697
2747
|
};
|
|
2698
|
-
return /* @__PURE__ */
|
|
2699
|
-
/* @__PURE__ */
|
|
2748
|
+
return /* @__PURE__ */ jsxs18("div", { className: `space-y-2 ${className ?? ""}`, "aria-label": style ? "Selection style" : "New shape style", children: [
|
|
2749
|
+
/* @__PURE__ */ jsx18("p", { className: "text-[11px] font-medium uppercase tracking-wide text-muted-foreground", children: style ? "Selection" : "New shapes" }),
|
|
2700
2750
|
paintRow("fill", "Fill"),
|
|
2701
2751
|
paintRow("stroke", "Stroke"),
|
|
2702
|
-
style && /* @__PURE__ */
|
|
2703
|
-
/* @__PURE__ */
|
|
2704
|
-
/* @__PURE__ */
|
|
2705
|
-
/* @__PURE__ */
|
|
2752
|
+
style && /* @__PURE__ */ jsx18(VectorGradientEditor, { controller }),
|
|
2753
|
+
/* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2754
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16", children: "Width" }),
|
|
2755
|
+
/* @__PURE__ */ jsx18(
|
|
2706
2756
|
"input",
|
|
2707
2757
|
{
|
|
2708
2758
|
type: "number",
|
|
@@ -2718,33 +2768,33 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2718
2768
|
}
|
|
2719
2769
|
)
|
|
2720
2770
|
] }),
|
|
2721
|
-
/* @__PURE__ */
|
|
2722
|
-
/* @__PURE__ */
|
|
2723
|
-
/* @__PURE__ */
|
|
2771
|
+
/* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "strokeAlign", children: [
|
|
2772
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16", children: "Align" }),
|
|
2773
|
+
/* @__PURE__ */ jsx18("select", { "aria-label": "Stroke alignment", className: selectClass13, value: source.strokeAlign, onChange: (e) => apply({ strokeAlign: e.target.value }), children: Object.keys(VECTOR_STROKE_ALIGN_LABELS2).map((a) => /* @__PURE__ */ jsx18("option", { value: a, children: VECTOR_STROKE_ALIGN_LABELS2[a] }, a)) }),
|
|
2724
2774
|
mixed("strokeAlign")
|
|
2725
2775
|
] }),
|
|
2726
|
-
/* @__PURE__ */
|
|
2727
|
-
/* @__PURE__ */
|
|
2728
|
-
/* @__PURE__ */
|
|
2776
|
+
/* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "widthProfile", children: [
|
|
2777
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16", children: "Profile" }),
|
|
2778
|
+
/* @__PURE__ */ jsxs18(
|
|
2729
2779
|
"select",
|
|
2730
2780
|
{
|
|
2731
2781
|
"aria-label": "Width profile",
|
|
2732
|
-
className:
|
|
2782
|
+
className: selectClass13,
|
|
2733
2783
|
value: source.widthProfile,
|
|
2734
2784
|
onChange: (e) => {
|
|
2735
2785
|
if (e.target.value !== "custom") setProfile(e.target.value);
|
|
2736
2786
|
},
|
|
2737
2787
|
children: [
|
|
2738
|
-
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */
|
|
2739
|
-
source.widthProfile === "custom" && /* @__PURE__ */
|
|
2788
|
+
VECTOR_WIDTH_PRESETS.map((p) => /* @__PURE__ */ jsx18("option", { value: p.id, children: p.label }, p.id)),
|
|
2789
|
+
source.widthProfile === "custom" && /* @__PURE__ */ jsx18("option", { value: "custom", disabled: true, children: "Custom" })
|
|
2740
2790
|
]
|
|
2741
2791
|
}
|
|
2742
2792
|
),
|
|
2743
|
-
/* @__PURE__ */
|
|
2793
|
+
/* @__PURE__ */ jsx18(
|
|
2744
2794
|
"button",
|
|
2745
2795
|
{
|
|
2746
2796
|
type: "button",
|
|
2747
|
-
className:
|
|
2797
|
+
className: buttonClass15,
|
|
2748
2798
|
title: "Reset to uniform width",
|
|
2749
2799
|
"aria-label": "Reset to uniform width",
|
|
2750
2800
|
disabled: source.widthProfile === "uniform" && !style?.mixed.widthProfile,
|
|
@@ -2754,20 +2804,20 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2754
2804
|
),
|
|
2755
2805
|
mixed("widthProfile")
|
|
2756
2806
|
] }),
|
|
2757
|
-
source.hasOpenPath && /* @__PURE__ */
|
|
2807
|
+
source.hasOpenPath && /* @__PURE__ */ jsxs18(Fragment6, { children: [
|
|
2758
2808
|
arrowRow("start", "Start"),
|
|
2759
2809
|
arrowRow("end", "End"),
|
|
2760
|
-
(source.startArrow || source.endArrow) && /* @__PURE__ */
|
|
2761
|
-
/* @__PURE__ */
|
|
2762
|
-
/* @__PURE__ */
|
|
2763
|
-
/* @__PURE__ */
|
|
2764
|
-
/* @__PURE__ */
|
|
2810
|
+
(source.startArrow || source.endArrow) && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "arrowAlign", children: [
|
|
2811
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16", children: "Heads" }),
|
|
2812
|
+
/* @__PURE__ */ jsxs18("select", { "aria-label": "Arrowhead alignment", className: selectClass13, value: source.arrowAlign, onChange: (e) => apply({ arrowAlign: e.target.value }), children: [
|
|
2813
|
+
/* @__PURE__ */ jsx18("option", { value: "extend", children: "Tip past end" }),
|
|
2814
|
+
/* @__PURE__ */ jsx18("option", { value: "tip", children: "Tip at end" })
|
|
2765
2815
|
] })
|
|
2766
2816
|
] })
|
|
2767
2817
|
] }),
|
|
2768
|
-
source.corners > 0 && /* @__PURE__ */
|
|
2769
|
-
/* @__PURE__ */
|
|
2770
|
-
/* @__PURE__ */
|
|
2818
|
+
source.corners > 0 && /* @__PURE__ */ jsxs18("div", { className: "flex items-center gap-2", "data-vector-style": "corners", children: [
|
|
2819
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16 text-xs text-foreground", children: "Corners" }),
|
|
2820
|
+
/* @__PURE__ */ jsx18(
|
|
2771
2821
|
"input",
|
|
2772
2822
|
{
|
|
2773
2823
|
type: "number",
|
|
@@ -2782,12 +2832,12 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2782
2832
|
}
|
|
2783
2833
|
}
|
|
2784
2834
|
),
|
|
2785
|
-
/* @__PURE__ */
|
|
2835
|
+
/* @__PURE__ */ jsx18("select", { "aria-label": "Corner kind", className: selectClass13, value: source.cornerKind, onChange: (e) => setCorners({ kind: e.target.value }), children: VECTOR_CORNER_KINDS.map((k) => /* @__PURE__ */ jsx18("option", { value: k, children: VECTOR_CORNER_KIND_LABELS[k] }, k)) }),
|
|
2786
2836
|
mixed("cornerRadius")
|
|
2787
2837
|
] }),
|
|
2788
|
-
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */
|
|
2789
|
-
/* @__PURE__ */
|
|
2790
|
-
/* @__PURE__ */
|
|
2838
|
+
style && source.corners > 0 && source.cornerKind === "round" && source.cornerRadius > 0 && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", "data-vector-style": "corner-smoothing", title: "Corner smoothing: squircle corners that ease into the curve (0.6 is iOS-like)", children: [
|
|
2839
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16", children: "Smoothing" }),
|
|
2840
|
+
/* @__PURE__ */ jsx18(
|
|
2791
2841
|
"input",
|
|
2792
2842
|
{
|
|
2793
2843
|
type: "range",
|
|
@@ -2802,9 +2852,9 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2802
2852
|
}
|
|
2803
2853
|
)
|
|
2804
2854
|
] }),
|
|
2805
|
-
style && /* @__PURE__ */
|
|
2806
|
-
/* @__PURE__ */
|
|
2807
|
-
/* @__PURE__ */
|
|
2855
|
+
style && /* @__PURE__ */ jsxs18("label", { className: "flex items-center gap-2 text-xs text-foreground", children: [
|
|
2856
|
+
/* @__PURE__ */ jsx18("span", { className: "w-16", children: "Opacity" }),
|
|
2857
|
+
/* @__PURE__ */ jsx18(
|
|
2808
2858
|
"input",
|
|
2809
2859
|
{
|
|
2810
2860
|
type: "range",
|
|
@@ -2818,25 +2868,26 @@ function VectorStylePanel({ controller, className }) {
|
|
|
2818
2868
|
}
|
|
2819
2869
|
)
|
|
2820
2870
|
] }),
|
|
2821
|
-
/* @__PURE__ */
|
|
2822
|
-
style && /* @__PURE__ */
|
|
2823
|
-
style && /* @__PURE__ */
|
|
2824
|
-
style && /* @__PURE__ */
|
|
2825
|
-
style && /* @__PURE__ */
|
|
2826
|
-
/* @__PURE__ */
|
|
2827
|
-
/* @__PURE__ */
|
|
2828
|
-
/* @__PURE__ */
|
|
2829
|
-
/* @__PURE__ */
|
|
2830
|
-
/* @__PURE__ */
|
|
2831
|
-
/* @__PURE__ */
|
|
2832
|
-
/* @__PURE__ */
|
|
2833
|
-
/* @__PURE__ */
|
|
2871
|
+
/* @__PURE__ */ jsx18(VectorTextPanel, { controller }),
|
|
2872
|
+
style && /* @__PURE__ */ jsx18(VectorAppearancePanel, { controller }),
|
|
2873
|
+
style && /* @__PURE__ */ jsx18(VectorEffectsPanel, { controller }),
|
|
2874
|
+
style && /* @__PURE__ */ jsx18(VectorEnvelopePanel, { controller }),
|
|
2875
|
+
style && /* @__PURE__ */ jsx18(VectorMaskPanel, { controller }),
|
|
2876
|
+
/* @__PURE__ */ jsx18(VectorFramePanel, { controller }),
|
|
2877
|
+
/* @__PURE__ */ jsx18(VectorConnectorPanel, { controller }),
|
|
2878
|
+
/* @__PURE__ */ jsx18(VectorShadingPanel, { controller }),
|
|
2879
|
+
/* @__PURE__ */ jsx18(VectorSymmetryPanel, { controller }),
|
|
2880
|
+
/* @__PURE__ */ jsx18(VectorPerspectivePanel, { controller }),
|
|
2881
|
+
/* @__PURE__ */ jsx18(VectorLensPanel, { controller }),
|
|
2882
|
+
/* @__PURE__ */ jsx18(VectorTracePanel, { controller }),
|
|
2883
|
+
/* @__PURE__ */ jsx18(VectorConstraintsPanel, { controller }),
|
|
2884
|
+
/* @__PURE__ */ jsx18(VectorSymbolsPanel, { controller })
|
|
2834
2885
|
] });
|
|
2835
2886
|
}
|
|
2836
2887
|
|
|
2837
2888
|
// src/vector/VectorProviderEffectsPanel.tsx
|
|
2838
2889
|
import { useState as useState7, useSyncExternalStore as useSyncExternalStore2 } from "react";
|
|
2839
|
-
import { Fragment as Fragment7, jsx as
|
|
2890
|
+
import { Fragment as Fragment7, jsx as jsx19, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
2840
2891
|
var btn = "rounded border border-border px-1.5 py-0.5 text-[11px] disabled:opacity-40";
|
|
2841
2892
|
function Controls({ controller, group }) {
|
|
2842
2893
|
const provider = controller.effectsProvider;
|
|
@@ -2844,11 +2895,11 @@ function Controls({ controller, group }) {
|
|
|
2844
2895
|
const { description, error } = useEffectDescription(provider, tag.effect);
|
|
2845
2896
|
const [message, setMessage] = useState7(null);
|
|
2846
2897
|
const run = (work) => void work.then(() => setMessage(null), (e) => setMessage(e instanceof Error ? e.message : String(e)));
|
|
2847
|
-
if (!provider) return /* @__PURE__ */
|
|
2848
|
-
if (error) return /* @__PURE__ */
|
|
2849
|
-
if (!description) return /* @__PURE__ */
|
|
2850
|
-
return /* @__PURE__ */
|
|
2851
|
-
/* @__PURE__ */
|
|
2898
|
+
if (!provider) return /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider: the shapes are kept as they are." });
|
|
2899
|
+
if (error) return /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-amber-500", children: error });
|
|
2900
|
+
if (!description) return /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-muted-foreground", children: "Loading\u2026" });
|
|
2901
|
+
return /* @__PURE__ */ jsxs19(Fragment7, { children: [
|
|
2902
|
+
/* @__PURE__ */ jsx19(
|
|
2852
2903
|
EffectParameters,
|
|
2853
2904
|
{
|
|
2854
2905
|
description,
|
|
@@ -2858,7 +2909,7 @@ function Controls({ controller, group }) {
|
|
|
2858
2909
|
onPreset: (v) => run(controller.updateVectorEffect(group.id, v))
|
|
2859
2910
|
}
|
|
2860
2911
|
),
|
|
2861
|
-
message && /* @__PURE__ */
|
|
2912
|
+
message && /* @__PURE__ */ jsx19("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
|
|
2862
2913
|
] });
|
|
2863
2914
|
}
|
|
2864
2915
|
function VectorProviderEffectsPanel({ controller, className = "" }) {
|
|
@@ -2877,41 +2928,41 @@ function VectorProviderEffectsPanel({ controller, className = "" }) {
|
|
|
2877
2928
|
setMessage(null);
|
|
2878
2929
|
Promise.resolve().then(work).catch((e) => setMessage(e instanceof Error ? e.message : String(e)));
|
|
2879
2930
|
};
|
|
2880
|
-
return /* @__PURE__ */
|
|
2881
|
-
/* @__PURE__ */
|
|
2882
|
-
/* @__PURE__ */
|
|
2883
|
-
/* @__PURE__ */
|
|
2931
|
+
return /* @__PURE__ */ jsxs19("section", { className: `flex flex-col gap-2 text-xs ${className}`, "aria-label": "Vector effects", "data-vector-provider-effects": "", children: [
|
|
2932
|
+
/* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1", children: [
|
|
2933
|
+
/* @__PURE__ */ jsx19("span", { className: "flex-1 font-semibold", children: "Effects" }),
|
|
2934
|
+
/* @__PURE__ */ jsx19("button", { type: "button", className: btn, disabled: !provider, "aria-pressed": browse, onClick: () => setBrowse(!browse), children: "Add effect\u2026" })
|
|
2884
2935
|
] }),
|
|
2885
|
-
!provider && /* @__PURE__ */
|
|
2886
|
-
browse && /* @__PURE__ */
|
|
2887
|
-
selected.map((g) => /* @__PURE__ */
|
|
2888
|
-
/* @__PURE__ */
|
|
2889
|
-
/* @__PURE__ */
|
|
2890
|
-
/* @__PURE__ */
|
|
2936
|
+
!provider && /* @__PURE__ */ jsx19("p", { className: "text-[11px] text-muted-foreground", children: "No effects provider is registered. Effect groups are kept as shapes." }),
|
|
2937
|
+
browse && /* @__PURE__ */ jsx19(EffectsBrowser, { provider, kinds: ["vector"], title: "Vector effects", onPick: (e) => (setBrowse(false), run(() => controller.addVectorEffect(e.id))) }),
|
|
2938
|
+
selected.map((g) => /* @__PURE__ */ jsxs19("div", { className: "space-y-1 rounded border border-border p-1", "data-vector-effect-group": g.id, children: [
|
|
2939
|
+
/* @__PURE__ */ jsxs19("div", { className: "flex items-center gap-1", children: [
|
|
2940
|
+
/* @__PURE__ */ jsx19("span", { className: "min-w-0 flex-1 truncate font-medium", children: g.providerEffect.name ?? g.providerEffect.effect }),
|
|
2941
|
+
/* @__PURE__ */ jsx19("button", { type: "button", className: btn, title: "Keep the shapes as a plain group", onClick: () => run(() => controller.detachVectorEffect(g.id)), children: "Detach" })
|
|
2891
2942
|
] }),
|
|
2892
|
-
/* @__PURE__ */
|
|
2943
|
+
/* @__PURE__ */ jsx19(Controls, { controller, group: g })
|
|
2893
2944
|
] }, g.id)),
|
|
2894
|
-
groups.length > 0 && !selected.length && /* @__PURE__ */
|
|
2945
|
+
groups.length > 0 && !selected.length && /* @__PURE__ */ jsxs19("p", { className: "text-[11px] text-muted-foreground", children: [
|
|
2895
2946
|
"Select an effect group (",
|
|
2896
2947
|
groups.length,
|
|
2897
2948
|
" in the drawing) to change it."
|
|
2898
2949
|
] }),
|
|
2899
|
-
message && /* @__PURE__ */
|
|
2950
|
+
message && /* @__PURE__ */ jsx19("p", { role: "alert", className: "text-[11px] text-destructive", children: message })
|
|
2900
2951
|
] });
|
|
2901
2952
|
}
|
|
2902
2953
|
|
|
2903
2954
|
// src/vector/VectorEditor.tsx
|
|
2904
|
-
import { jsx as
|
|
2955
|
+
import { jsx as jsx20, jsxs as jsxs20 } from "react/jsx-runtime";
|
|
2905
2956
|
function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, effects, className }) {
|
|
2906
2957
|
useEffects(controller, effects);
|
|
2907
2958
|
const effectsShown = !!effects || controller.vectorEffectGroups().length > 0;
|
|
2908
|
-
return /* @__PURE__ */
|
|
2909
|
-
/* @__PURE__ */
|
|
2910
|
-
/* @__PURE__ */
|
|
2911
|
-
/* @__PURE__ */
|
|
2912
|
-
showStylePanel && /* @__PURE__ */
|
|
2913
|
-
/* @__PURE__ */
|
|
2914
|
-
effectsShown && /* @__PURE__ */
|
|
2959
|
+
return /* @__PURE__ */ jsxs20("div", { className: `flex flex-col gap-2 ${className ?? ""}`, "data-vector-editor": "", children: [
|
|
2960
|
+
/* @__PURE__ */ jsx20(VectorToolbar, { controller, tools }),
|
|
2961
|
+
/* @__PURE__ */ jsxs20("div", { className: "flex min-h-0 flex-col gap-2 md:flex-row", children: [
|
|
2962
|
+
/* @__PURE__ */ jsx20("div", { className: "relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40", style: { height }, children: /* @__PURE__ */ jsx20(VectorCanvas, { controller, ...canvasProps }) }),
|
|
2963
|
+
showStylePanel && /* @__PURE__ */ jsxs20("div", { className: "flex w-full shrink-0 flex-col gap-2 md:w-56", children: [
|
|
2964
|
+
/* @__PURE__ */ jsx20(VectorStylePanel, { controller, className: "rounded-md border border-border p-3" }),
|
|
2965
|
+
effectsShown && /* @__PURE__ */ jsx20(VectorProviderEffectsPanel, { controller, className: "rounded-md border border-border p-3" })
|
|
2915
2966
|
] })
|
|
2916
2967
|
] })
|
|
2917
2968
|
] });
|
|
@@ -2932,6 +2983,7 @@ export {
|
|
|
2932
2983
|
VectorLensPanel,
|
|
2933
2984
|
VectorMaskPanel,
|
|
2934
2985
|
VectorMaskedNodes,
|
|
2986
|
+
VectorPerspectivePanel,
|
|
2935
2987
|
VectorProviderEffectsPanel,
|
|
2936
2988
|
VectorSceneNodes,
|
|
2937
2989
|
VectorShadingPanel,
|