@pixerate/editor-react 0.23.0 → 0.24.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
CHANGED
|
@@ -88,10 +88,11 @@ function VectorGradientDefs({ defs }) {
|
|
|
88
88
|
}
|
|
89
89
|
function VectorSceneNodes({ nodes }) {
|
|
90
90
|
return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_jsx_runtime.Fragment, { children: nodes.map(
|
|
91
|
-
(node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { "data-vector-id": node.id, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VectorSceneNodes, { nodes: node.children }) }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
91
|
+
(node) => node.kind === "group" ? /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(VectorSceneNodes, { nodes: node.children }) }, node.id) : /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
92
92
|
"path",
|
|
93
93
|
{
|
|
94
94
|
"data-vector-id": node.id,
|
|
95
|
+
transform: node.transform,
|
|
95
96
|
d: node.d,
|
|
96
97
|
fill: node.fill,
|
|
97
98
|
fillRule: node.fillRule,
|
|
@@ -111,6 +112,7 @@ function VectorCanvas({
|
|
|
111
112
|
fitOnMount = true,
|
|
112
113
|
checkerboard = true,
|
|
113
114
|
accentColor = import_vector2.VECTOR_OVERLAY_THEME.accent,
|
|
115
|
+
guideColor = import_vector2.VECTOR_OVERLAY_THEME.guide,
|
|
114
116
|
className,
|
|
115
117
|
style,
|
|
116
118
|
"aria-label": ariaLabel = "Vector canvas"
|
|
@@ -222,7 +224,7 @@ function VectorCanvas({
|
|
|
222
224
|
/* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
|
|
223
225
|
overlay.outlines.map((o) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: o.d, stroke: accentColor, strokeWidth: o.selected ? 1 : 1.5, opacity: o.selected ? 1 : 0.7 }, o.id)),
|
|
224
226
|
overlay.penPreview && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
|
|
225
|
-
box && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-selection": "", children: [
|
|
227
|
+
box && /* @__PURE__ */ (0, import_jsx_runtime.jsxs)("g", { "data-vector-selection": "", transform: box.transform || void 0, children: [
|
|
226
228
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("rect", { x: box.x, y: box.y, width: box.width, height: box.height, stroke: accentColor }),
|
|
227
229
|
/* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { x1: box.x + box.width / 2, y1: box.y, x2: box.x + box.width / 2, y2: box.y - 24, stroke: accentColor }),
|
|
228
230
|
box.handles.map(
|
|
@@ -253,6 +255,23 @@ function VectorCanvas({
|
|
|
253
255
|
strokeDasharray: "3 2"
|
|
254
256
|
}
|
|
255
257
|
),
|
|
258
|
+
overlay.guides.lines.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("g", { "data-vector-guides": "", stroke: guideColor, children: overlay.guides.lines.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)("line", { "data-vector-guide": l.kind, x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2, strokeDasharray: l.kind === "construction" ? "4 3" : void 0 }, i)) }),
|
|
259
|
+
overlay.guides.labels.map((l, i) => /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
|
|
260
|
+
"text",
|
|
261
|
+
{
|
|
262
|
+
"data-vector-guide-label": l.kind,
|
|
263
|
+
x: l.x + 6,
|
|
264
|
+
y: l.y - 6,
|
|
265
|
+
fontSize: 10,
|
|
266
|
+
fill: guideColor,
|
|
267
|
+
stroke: "#ffffff",
|
|
268
|
+
strokeWidth: 3,
|
|
269
|
+
paintOrder: "stroke",
|
|
270
|
+
style: { fontFamily: "system-ui, sans-serif" },
|
|
271
|
+
children: l.text
|
|
272
|
+
},
|
|
273
|
+
i
|
|
274
|
+
)),
|
|
256
275
|
overlay.status && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
|
|
257
276
|
] })
|
|
258
277
|
]
|
|
@@ -263,8 +282,10 @@ function VectorCanvas({
|
|
|
263
282
|
// src/vector/VectorToolbar.tsx
|
|
264
283
|
var import_vector3 = require("@pixerate/editor/vector");
|
|
265
284
|
var import_jsx_runtime2 = require("react/jsx-runtime");
|
|
285
|
+
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
286
|
+
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
266
287
|
var buttonClass = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
267
|
-
function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHistory = true, showArrange = true, className }) {
|
|
288
|
+
function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }) {
|
|
268
289
|
const state = useVectorEditorState(controller);
|
|
269
290
|
const count = state.selection.length;
|
|
270
291
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
@@ -288,6 +309,19 @@ function VectorToolbar({ controller, tools = import_vector3.VECTOR_TOOLS, showHi
|
|
|
288
309
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
289
310
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
290
311
|
] }),
|
|
312
|
+
showSnap && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
|
|
313
|
+
"button",
|
|
314
|
+
{
|
|
315
|
+
type: "button",
|
|
316
|
+
"data-vector-snap": "",
|
|
317
|
+
className: buttonClass,
|
|
318
|
+
"aria-pressed": state.snap.objects,
|
|
319
|
+
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
320
|
+
"aria-label": VECTOR_SNAP_TITLE,
|
|
321
|
+
onClick: () => controller.toggleSnapping(),
|
|
322
|
+
children: "Snap"
|
|
323
|
+
}
|
|
324
|
+
) }),
|
|
291
325
|
showArrange && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(import_jsx_runtime2.Fragment, { children: [
|
|
292
326
|
/* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
293
327
|
act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vector/index.ts","../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["export * from './useVectorEditor';\nexport * from './VectorCanvas';\nexport * from './VectorToolbar';\nexport * from './VectorEditor';\n","import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\">\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_vector","THEME","text","import_vector","import_jsx_runtime","import_jsx_runtime"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vector/index.ts","../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["export * from './useVectorEditor';\nexport * from './VectorCanvas';\nexport * from './VectorToolbar';\nexport * from './VectorEditor';\n","import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n /** Smart guide colour (default magenta). */\n guideColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} transform={node.transform} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n transform={node.transform}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n guideColor = THEME.guide,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\" transform={box.transform || undefined}>\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.guides.lines.length > 0 && (\n <g data-vector-guides=\"\" stroke={guideColor}>\n {overlay.guides.lines.map((l, i) => (\n <line key={i} data-vector-guide={l.kind} x1={l.x1} y1={l.y1} x2={l.x2} y2={l.y2} strokeDasharray={l.kind === 'construction' ? '4 3' : undefined} />\n ))}\n </g>\n )}\n {overlay.guides.labels.map((l, i) => (\n <text\n key={i}\n data-vector-guide-label={l.kind}\n x={l.x + 6}\n y={l.y - 6}\n fontSize={10}\n fill={guideColor}\n stroke=\"#ffffff\"\n strokeWidth={3}\n paintOrder=\"stroke\"\n style={{ fontFamily: 'system-ui, sans-serif' }}\n >\n {l.text}\n </text>\n ))}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst VECTOR_SNAP_TITLE = 'Snap to objects';\nconst modKeyLabel = () => (typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform ?? '') ? '⌘' : 'Ctrl');\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n /** The snapping toggle (default `true`). */\n showSnap?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showSnap && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Snapping\">\n <button\n type=\"button\"\n data-vector-snap=\"\"\n className={buttonClass}\n aria-pressed={state.snap.objects}\n title={`${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`}\n aria-label={VECTOR_SNAP_TITLE}\n onClick={() => controller.toggleSnapping()}\n >\n Snap\n </button>\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["import_react","import_vector","THEME","text","import_vector","import_jsx_runtime","import_jsx_runtime"]}
|
package/dist/vector/index.d.cts
CHANGED
|
@@ -37,6 +37,8 @@ interface VectorCanvasProps {
|
|
|
37
37
|
checkerboard?: boolean;
|
|
38
38
|
/** Overlay accent colour (default sky blue). */
|
|
39
39
|
accentColor?: string;
|
|
40
|
+
/** Smart guide colour (default magenta). */
|
|
41
|
+
guideColor?: string;
|
|
40
42
|
className?: string;
|
|
41
43
|
style?: React__default.CSSProperties;
|
|
42
44
|
/** Accessible label (default "Vector canvas"). */
|
|
@@ -54,7 +56,7 @@ declare function VectorSceneNodes({ nodes }: {
|
|
|
54
56
|
* An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay
|
|
55
57
|
* and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.
|
|
56
58
|
*/
|
|
57
|
-
declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
|
|
59
|
+
declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, guideColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
|
|
58
60
|
|
|
59
61
|
interface VectorToolbarProps {
|
|
60
62
|
controller: VectorEditorController;
|
|
@@ -64,10 +66,12 @@ interface VectorToolbarProps {
|
|
|
64
66
|
showHistory?: boolean;
|
|
65
67
|
/** Boolean, group, arrange and align buttons (default `true`). */
|
|
66
68
|
showArrange?: boolean;
|
|
69
|
+
/** The snapping toggle (default `true`). */
|
|
70
|
+
showSnap?: boolean;
|
|
67
71
|
className?: string;
|
|
68
72
|
}
|
|
69
|
-
/** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */
|
|
70
|
-
declare function VectorToolbar({ controller, tools, showHistory, showArrange, className }: VectorToolbarProps): React__default.JSX.Element;
|
|
73
|
+
/** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */
|
|
74
|
+
declare function VectorToolbar({ controller, tools, showHistory, showArrange, showSnap, className }: VectorToolbarProps): React__default.JSX.Element;
|
|
71
75
|
interface VectorStylePanelProps {
|
|
72
76
|
controller: VectorEditorController;
|
|
73
77
|
className?: string;
|
package/dist/vector/index.d.ts
CHANGED
|
@@ -37,6 +37,8 @@ interface VectorCanvasProps {
|
|
|
37
37
|
checkerboard?: boolean;
|
|
38
38
|
/** Overlay accent colour (default sky blue). */
|
|
39
39
|
accentColor?: string;
|
|
40
|
+
/** Smart guide colour (default magenta). */
|
|
41
|
+
guideColor?: string;
|
|
40
42
|
className?: string;
|
|
41
43
|
style?: React__default.CSSProperties;
|
|
42
44
|
/** Accessible label (default "Vector canvas"). */
|
|
@@ -54,7 +56,7 @@ declare function VectorSceneNodes({ nodes }: {
|
|
|
54
56
|
* An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay
|
|
55
57
|
* and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.
|
|
56
58
|
*/
|
|
57
|
-
declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
|
|
59
|
+
declare function VectorCanvas({ controller, fitOnMount, checkerboard, accentColor, guideColor, className, style, 'aria-label': ariaLabel, }: VectorCanvasProps): React__default.JSX.Element;
|
|
58
60
|
|
|
59
61
|
interface VectorToolbarProps {
|
|
60
62
|
controller: VectorEditorController;
|
|
@@ -64,10 +66,12 @@ interface VectorToolbarProps {
|
|
|
64
66
|
showHistory?: boolean;
|
|
65
67
|
/** Boolean, group, arrange and align buttons (default `true`). */
|
|
66
68
|
showArrange?: boolean;
|
|
69
|
+
/** The snapping toggle (default `true`). */
|
|
70
|
+
showSnap?: boolean;
|
|
67
71
|
className?: string;
|
|
68
72
|
}
|
|
69
|
-
/** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */
|
|
70
|
-
declare function VectorToolbar({ controller, tools, showHistory, showArrange, className }: VectorToolbarProps): React__default.JSX.Element;
|
|
73
|
+
/** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */
|
|
74
|
+
declare function VectorToolbar({ controller, tools, showHistory, showArrange, showSnap, className }: VectorToolbarProps): React__default.JSX.Element;
|
|
71
75
|
interface VectorStylePanelProps {
|
|
72
76
|
controller: VectorEditorController;
|
|
73
77
|
className?: string;
|
package/dist/vector/index.js
CHANGED
|
@@ -63,10 +63,11 @@ function VectorGradientDefs({ defs }) {
|
|
|
63
63
|
}
|
|
64
64
|
function VectorSceneNodes({ nodes }) {
|
|
65
65
|
return /* @__PURE__ */ jsx(Fragment, { children: nodes.map(
|
|
66
|
-
(node) => node.kind === "group" ? /* @__PURE__ */ jsx("g", { "data-vector-id": node.id, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ jsx(VectorSceneNodes, { nodes: node.children }) }, node.id) : /* @__PURE__ */ jsx(
|
|
66
|
+
(node) => node.kind === "group" ? /* @__PURE__ */ jsx("g", { "data-vector-id": node.id, transform: node.transform, opacity: node.opacity < 1 ? node.opacity : void 0, children: /* @__PURE__ */ jsx(VectorSceneNodes, { nodes: node.children }) }, node.id) : /* @__PURE__ */ jsx(
|
|
67
67
|
"path",
|
|
68
68
|
{
|
|
69
69
|
"data-vector-id": node.id,
|
|
70
|
+
transform: node.transform,
|
|
70
71
|
d: node.d,
|
|
71
72
|
fill: node.fill,
|
|
72
73
|
fillRule: node.fillRule,
|
|
@@ -86,6 +87,7 @@ function VectorCanvas({
|
|
|
86
87
|
fitOnMount = true,
|
|
87
88
|
checkerboard = true,
|
|
88
89
|
accentColor = THEME.accent,
|
|
90
|
+
guideColor = THEME.guide,
|
|
89
91
|
className,
|
|
90
92
|
style,
|
|
91
93
|
"aria-label": ariaLabel = "Vector canvas"
|
|
@@ -197,7 +199,7 @@ function VectorCanvas({
|
|
|
197
199
|
/* @__PURE__ */ jsxs("g", { "data-vector-overlay": "", pointerEvents: "none", fill: "none", children: [
|
|
198
200
|
overlay.outlines.map((o) => /* @__PURE__ */ jsx("path", { d: o.d, stroke: accentColor, strokeWidth: o.selected ? 1 : 1.5, opacity: o.selected ? 1 : 0.7 }, o.id)),
|
|
199
201
|
overlay.penPreview && /* @__PURE__ */ jsx("path", { d: overlay.penPreview, stroke: accentColor, strokeDasharray: "4 3" }),
|
|
200
|
-
box && /* @__PURE__ */ jsxs("g", { "data-vector-selection": "", children: [
|
|
202
|
+
box && /* @__PURE__ */ jsxs("g", { "data-vector-selection": "", transform: box.transform || void 0, children: [
|
|
201
203
|
/* @__PURE__ */ jsx("rect", { x: box.x, y: box.y, width: box.width, height: box.height, stroke: accentColor }),
|
|
202
204
|
/* @__PURE__ */ jsx("line", { x1: box.x + box.width / 2, y1: box.y, x2: box.x + box.width / 2, y2: box.y - 24, stroke: accentColor }),
|
|
203
205
|
box.handles.map(
|
|
@@ -228,6 +230,23 @@ function VectorCanvas({
|
|
|
228
230
|
strokeDasharray: "3 2"
|
|
229
231
|
}
|
|
230
232
|
),
|
|
233
|
+
overlay.guides.lines.length > 0 && /* @__PURE__ */ jsx("g", { "data-vector-guides": "", stroke: guideColor, children: overlay.guides.lines.map((l, i) => /* @__PURE__ */ jsx("line", { "data-vector-guide": l.kind, x1: l.x1, y1: l.y1, x2: l.x2, y2: l.y2, strokeDasharray: l.kind === "construction" ? "4 3" : void 0 }, i)) }),
|
|
234
|
+
overlay.guides.labels.map((l, i) => /* @__PURE__ */ jsx(
|
|
235
|
+
"text",
|
|
236
|
+
{
|
|
237
|
+
"data-vector-guide-label": l.kind,
|
|
238
|
+
x: l.x + 6,
|
|
239
|
+
y: l.y - 6,
|
|
240
|
+
fontSize: 10,
|
|
241
|
+
fill: guideColor,
|
|
242
|
+
stroke: "#ffffff",
|
|
243
|
+
strokeWidth: 3,
|
|
244
|
+
paintOrder: "stroke",
|
|
245
|
+
style: { fontFamily: "system-ui, sans-serif" },
|
|
246
|
+
children: l.text
|
|
247
|
+
},
|
|
248
|
+
i
|
|
249
|
+
)),
|
|
231
250
|
overlay.status && /* @__PURE__ */ jsx("text", { "data-vector-status": "", x: 8, y: "100%", dy: -8, fontSize: 11, fill: accentColor, style: { fontFamily: "system-ui, sans-serif" }, children: overlay.status })
|
|
232
251
|
] })
|
|
233
252
|
]
|
|
@@ -245,8 +264,10 @@ import {
|
|
|
245
264
|
vectorToolLabel
|
|
246
265
|
} from "@pixerate/editor/vector";
|
|
247
266
|
import { Fragment as Fragment2, jsx as jsx2, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
267
|
+
var VECTOR_SNAP_TITLE = "Snap to objects";
|
|
268
|
+
var modKeyLabel = () => typeof navigator !== "undefined" && /Mac|iPhone|iPad/.test(navigator.platform ?? "") ? "\u2318" : "Ctrl";
|
|
248
269
|
var buttonClass = "inline-flex h-8 min-w-8 items-center justify-center rounded-md border border-border bg-background px-2 text-xs font-medium text-foreground hover:bg-muted disabled:cursor-not-allowed disabled:opacity-40 aria-pressed:bg-primary aria-pressed:text-primary-foreground";
|
|
249
|
-
function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, className }) {
|
|
270
|
+
function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }) {
|
|
250
271
|
const state = useVectorEditorState(controller);
|
|
251
272
|
const count = state.selection.length;
|
|
252
273
|
const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === "group");
|
|
@@ -270,6 +291,19 @@ function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, s
|
|
|
270
291
|
act("\u21B6", state.canUndo ? `Undo ${state.undoLabel}` : "Undo", () => controller.undo(), !state.canUndo),
|
|
271
292
|
act("\u21B7", state.canRedo ? `Redo ${state.redoLabel}` : "Redo", () => controller.redo(), !state.canRedo)
|
|
272
293
|
] }),
|
|
294
|
+
showSnap && /* @__PURE__ */ jsx2("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Snapping", children: /* @__PURE__ */ jsx2(
|
|
295
|
+
"button",
|
|
296
|
+
{
|
|
297
|
+
type: "button",
|
|
298
|
+
"data-vector-snap": "",
|
|
299
|
+
className: buttonClass,
|
|
300
|
+
"aria-pressed": state.snap.objects,
|
|
301
|
+
title: `${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`,
|
|
302
|
+
"aria-label": VECTOR_SNAP_TITLE,
|
|
303
|
+
onClick: () => controller.toggleSnapping(),
|
|
304
|
+
children: "Snap"
|
|
305
|
+
}
|
|
306
|
+
) }),
|
|
273
307
|
showArrange && /* @__PURE__ */ jsxs2(Fragment2, { children: [
|
|
274
308
|
/* @__PURE__ */ jsxs2("div", { className: "ml-1 flex items-center gap-1", role: "group", "aria-label": "Combine", children: [
|
|
275
309
|
act("\u222A", "Union", () => controller.booleanOp("union"), count < 2),
|
package/dist/vector/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\">\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect","text","Fragment","jsx","jsxs","jsx","jsxs"]}
|
|
1
|
+
{"version":3,"sources":["../../src/vector/useVectorEditor.ts","../../src/vector/VectorCanvas.tsx","../../src/vector/VectorToolbar.tsx","../../src/vector/VectorEditor.tsx"],"sourcesContent":["import { useEffect, useMemo, useRef, useSyncExternalStore } from 'react';\nimport {\n VectorEditorController,\n createVectorCommands,\n type VectorCommandOptions,\n type VectorEditorOptions,\n type VectorEditorState,\n type VectorTool,\n} from '@pixerate/editor/vector';\n\n/** A `VectorEditorController` created once for the component's lifetime (destroyed on unmount). */\nexport function useVectorEditorController(options: VectorEditorOptions = {}): VectorEditorController {\n const ref = useRef<VectorEditorController | null>(null);\n if (!ref.current) ref.current = new VectorEditorController(options);\n const controller = ref.current;\n useEffect(() => () => controller.destroy(), [controller]);\n return controller;\n}\n\n/** The controller's state (re-renders on every change). */\nexport function useVectorEditorState(controller: VectorEditorController): VectorEditorState {\n return useSyncExternalStore(controller.subscribe, controller.getState, controller.getState);\n}\n\nexport interface UseVectorEditorResult {\n controller: VectorEditorController;\n state: VectorEditorState;\n setTool: (tool: VectorTool) => void;\n undo: () => void;\n redo: () => void;\n deleteSelection: () => void;\n toSvg: () => string;\n}\n\n/**\n * Subscribes to a vector editor controller and exposes its state with bound actions. Pass\n * options instead of a controller to create one for the component's lifetime.\n */\nexport function useVectorEditor(controllerOrOptions: VectorEditorController | VectorEditorOptions = {}): UseVectorEditorResult {\n const own = useRef<VectorEditorController | null>(null);\n let controller: VectorEditorController;\n if (controllerOrOptions instanceof VectorEditorController) controller = controllerOrOptions;\n else {\n if (!own.current) own.current = new VectorEditorController(controllerOrOptions);\n controller = own.current;\n }\n const state = useVectorEditorState(controller);\n const actions = useMemo(\n () => ({\n setTool: (tool: VectorTool) => controller.setTool(tool),\n undo: () => void controller.undo(),\n redo: () => void controller.redo(),\n deleteSelection: () => void controller.deleteSelection(),\n toSvg: () => controller.toSvg(),\n }),\n [controller],\n );\n return { controller, state, ...actions };\n}\n\n/** Anything with `register(specs) → unregister` (a `CommandRegistry`). */\nexport interface VectorCommandTarget {\n register(specs: any): () => void;\n}\n\n/**\n * Registers the controller's palette / agent commands (`vector.addShape`, `vector.boolean`, …)\n * in `registry` while mounted, so `createCommandToolProvider` exposes them to agents.\n */\nexport function useVectorCommands(\n registry: VectorCommandTarget | null | undefined,\n controller: VectorEditorController,\n options: VectorCommandOptions = {},\n): void {\n const { prefix, group, scope } = options;\n useEffect(() => {\n if (!registry) return;\n return registry.register(createVectorCommands(controller, { prefix, group, scope }));\n }, [registry, controller, prefix, group, scope]);\n}\n","import React, { useEffect, useId, useRef } from 'react';\nimport {\n VECTOR_OVERLAY_THEME as THEME,\n getVectorOverlayRenderData,\n getVectorSceneRenderData,\n isVectorTextTarget,\n toVectorKeyInput,\n toVectorPointerInput,\n type VectorEditorController,\n type VectorRenderNode,\n type SvgGradientDef,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nexport interface VectorCanvasProps {\n controller: VectorEditorController;\n /** Fit the artboard into the canvas when it first gets a size (default `true`). */\n fitOnMount?: boolean;\n /** Draw a checkerboard behind a transparent artboard (default `true`). */\n checkerboard?: boolean;\n /** Overlay accent colour (default sky blue). */\n accentColor?: string;\n /** Smart guide colour (default magenta). */\n guideColor?: string;\n className?: string;\n style?: React.CSSProperties;\n /** Accessible label (default \"Vector canvas\"). */\n 'aria-label'?: string;\n}\n\n/** Renders `<linearGradient>` / `<radialGradient>` descriptors. */\nexport function VectorGradientDefs({ defs }: { defs: readonly SvgGradientDef[] }) {\n if (!defs.length) return null;\n return (\n <defs>\n {defs.map((def) => {\n const stops = def.stops.map((s, i) => <stop key={i} offset={s.offset} stopColor={s.stopColor} stopOpacity={s.stopOpacity} />);\n return def.type === 'radial' ? (\n <radialGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </radialGradient>\n ) : (\n <linearGradient key={def.id} id={def.id} {...(def.attrs as any)}>\n {stops}\n </linearGradient>\n );\n })}\n </defs>\n );\n}\n\n/** Renders scene nodes (paths and groups) from `getVectorSceneRenderData`. */\nexport function VectorSceneNodes({ nodes }: { nodes: readonly VectorRenderNode[] }) {\n return (\n <>\n {nodes.map((node) =>\n node.kind === 'group' ? (\n <g key={node.id} data-vector-id={node.id} transform={node.transform} opacity={node.opacity < 1 ? node.opacity : undefined}>\n <VectorSceneNodes nodes={node.children} />\n </g>\n ) : (\n <path\n key={node.id}\n data-vector-id={node.id}\n transform={node.transform}\n d={node.d}\n fill={node.fill}\n fillRule={node.fillRule}\n stroke={node.stroke}\n strokeWidth={node.stroke === 'none' ? undefined : node.strokeWidth}\n strokeLinecap={node.strokeLinecap as any}\n strokeLinejoin={node.strokeLinejoin as any}\n strokeDasharray={node.strokeDasharray}\n opacity={node.opacity < 1 ? node.opacity : undefined}\n />\n ),\n )}\n </>\n );\n}\n\n/**\n * An SVG canvas for a `VectorEditorController`: draws the document and the editing overlay\n * and forwards pointer, wheel, keyboard and clipboard events. Focus it to use shortcuts.\n */\nexport function VectorCanvas({\n controller,\n fitOnMount = true,\n checkerboard = true,\n accentColor = THEME.accent,\n guideColor = THEME.guide,\n className,\n style,\n 'aria-label': ariaLabel = 'Vector canvas',\n}: VectorCanvasProps) {\n const state = useVectorEditorState(controller);\n const svgRef = useRef<SVGSVGElement | null>(null);\n const fitted = useRef(!fitOnMount);\n const uid = useId().replace(/:/g, '');\n const scene = getVectorSceneRenderData(state, { idPrefix: `ve${uid}-` });\n const overlay = getVectorOverlayRenderData(state);\n\n useEffect(() => {\n const el = svgRef.current;\n if (!el || fitted.current) return;\n const fit = () => {\n const rect = el.getBoundingClientRect();\n if (fitted.current || rect.width <= 0 || rect.height <= 0) return;\n fitted.current = true;\n controller.zoomToFit({ width: rect.width, height: rect.height });\n };\n fit();\n if (fitted.current || typeof ResizeObserver === 'undefined') return;\n const observer = new ResizeObserver(fit);\n observer.observe(el);\n return () => observer.disconnect();\n }, [controller]);\n\n // Wheel needs a non-passive listener to stop page scroll / browser zoom.\n useEffect(() => {\n const el = svgRef.current;\n if (!el) return;\n const onWheel = (e: WheelEvent) => {\n e.preventDefault();\n controller.wheel(local(e), { x: e.deltaX, y: e.deltaY }, toVectorPointerInput(e));\n };\n el.addEventListener('wheel', onWheel, { passive: false });\n return () => el.removeEventListener('wheel', onWheel);\n }, [controller]);\n\n const local = (e: { clientX: number; clientY: number }) => {\n const rect = svgRef.current?.getBoundingClientRect();\n return { x: e.clientX - (rect?.left ?? 0), y: e.clientY - (rect?.top ?? 0) };\n };\n\n const clipboard = (kind: 'copy' | 'cut' | 'paste') => (event: React.ClipboardEvent) => {\n const native = (event && 'nativeEvent' in event ? event.nativeEvent : event) as ClipboardEvent;\n const data = native?.clipboardData;\n if (kind === 'paste') {\n const text = data?.getData('text/plain') ?? '';\n const pasted = text ? controller.paste(text) : controller.paste();\n if (pasted.length) event.preventDefault?.();\n return;\n }\n const text = kind === 'cut' ? controller.cut() : controller.copy();\n if (text && data) {\n data.setData('text/plain', text);\n event.preventDefault?.();\n }\n };\n\n const hs = THEME.transformHandleSize;\n const as = THEME.anchorSize;\n const box = overlay.selectionBox;\n\n return (\n <svg\n ref={svgRef}\n role=\"application\"\n aria-label={ariaLabel}\n tabIndex={0}\n data-tool={state.tool}\n className={className}\n style={{ display: 'block', width: '100%', height: '100%', outline: 'none', touchAction: 'none', userSelect: 'none', cursor: state.cursor, ...style }}\n onPointerDown={(e) => {\n svgRef.current?.focus({ preventScroll: true });\n (e.currentTarget as Element).setPointerCapture?.(e.pointerId);\n controller.pointerDown(local(e), toVectorPointerInput(e));\n }}\n onPointerMove={(e) => controller.pointerMove(local(e), toVectorPointerInput(e))}\n onPointerUp={(e) => {\n (e.currentTarget as Element).releasePointerCapture?.(e.pointerId);\n controller.pointerUp(local(e), toVectorPointerInput(e));\n }}\n onPointerCancel={() => controller.cancelDrag()}\n onPointerLeave={() => controller.pointerLeave()}\n onDoubleClick={(e) => controller.doubleClick(local(e))}\n onKeyDown={(e) => {\n if (isVectorTextTarget(e.target)) return;\n if (controller.keyDown(toVectorKeyInput(e))) e.preventDefault();\n }}\n onKeyUp={(e) => controller.keyUp(toVectorKeyInput(e))}\n onCopy={clipboard('copy')}\n onCut={clipboard('cut')}\n onPaste={clipboard('paste')}\n >\n {checkerboard && (\n <defs>\n <pattern id={`ve${uid}-checker`} width={16} height={16} patternUnits=\"userSpaceOnUse\">\n <rect width={16} height={16} fill=\"#ffffff\" />\n <rect width={8} height={8} fill=\"#e5e7eb\" />\n <rect x={8} y={8} width={8} height={8} fill=\"#e5e7eb\" />\n </pattern>\n </defs>\n )}\n <VectorGradientDefs defs={scene.defs} />\n <rect\n data-vector-artboard=\"\"\n x={overlay.artboard.x}\n y={overlay.artboard.y}\n width={overlay.artboard.width}\n height={overlay.artboard.height}\n fill={scene.background ?? (checkerboard ? `url(#ve${uid}-checker)` : '#ffffff')}\n stroke=\"rgba(0,0,0,0.12)\"\n />\n <g data-vector-scene=\"\" transform={scene.transform}>\n <VectorSceneNodes nodes={scene.nodes} />\n </g>\n <g data-vector-overlay=\"\" pointerEvents=\"none\" fill=\"none\">\n {overlay.outlines.map((o) => (\n <path key={o.id} d={o.d} stroke={accentColor} strokeWidth={o.selected ? 1 : 1.5} opacity={o.selected ? 1 : 0.7} />\n ))}\n {overlay.penPreview && <path d={overlay.penPreview} stroke={accentColor} strokeDasharray=\"4 3\" />}\n {box && (\n <g data-vector-selection=\"\" transform={box.transform || undefined}>\n <rect x={box.x} y={box.y} width={box.width} height={box.height} stroke={accentColor} />\n <line x1={box.x + box.width / 2} y1={box.y} x2={box.x + box.width / 2} y2={box.y - 24} stroke={accentColor} />\n {box.handles.map((h) =>\n h.handle === 'rotate' ? (\n <circle key={h.handle} data-handle={h.handle} cx={h.x} cy={h.y} r={hs / 2 + 1} fill={THEME.handleFill} stroke={accentColor} />\n ) : (\n <rect key={h.handle} data-handle={h.handle} x={h.x - hs / 2} y={h.y - hs / 2} width={hs} height={hs} fill={THEME.handleFill} stroke={accentColor} />\n ),\n )}\n </g>\n )}\n {overlay.handles.map((h) => (\n <g key={`${h.ref.nodeId}:${h.ref.subpath}:${h.ref.index}:${h.ref.handle}`} data-vector-handle={h.ref.handle}>\n <line x1={h.anchorX} y1={h.anchorY} x2={h.x} y2={h.y} stroke={accentColor} />\n <circle cx={h.x} cy={h.y} r={THEME.handleRadius + (h.hovered ? 1 : 0)} fill={THEME.handleFill} stroke={accentColor} />\n </g>\n ))}\n {overlay.anchors.map((a) => {\n const size = as + (a.hovered ? 2 : 0);\n const fill = a.selected ? accentColor : THEME.handleFill;\n const key = `${a.ref.nodeId}:${a.ref.subpath}:${a.ref.index}`;\n return a.type === 'corner' ? (\n <rect key={key} data-vector-anchor={a.ref.index} x={a.x - size / 2} y={a.y - size / 2} width={size} height={size} fill={fill} stroke={accentColor} />\n ) : (\n <circle key={key} data-vector-anchor={a.ref.index} cx={a.x} cy={a.y} r={size / 2} fill={fill} stroke={accentColor} />\n );\n })}\n {state.tool === 'pen' && overlay.anchors.some((a) => a.closes && a.hovered) && (\n <circle cx={overlay.anchors.find((a) => a.closes)!.x} cy={overlay.anchors.find((a) => a.closes)!.y} r={as} stroke={accentColor} />\n )}\n {overlay.marquee && (\n <rect\n data-vector-marquee=\"\"\n x={overlay.marquee.x}\n y={overlay.marquee.y}\n width={overlay.marquee.width}\n height={overlay.marquee.height}\n fill={THEME.marqueeFill}\n stroke={accentColor}\n strokeDasharray=\"3 2\"\n />\n )}\n {overlay.guides.lines.length > 0 && (\n <g data-vector-guides=\"\" stroke={guideColor}>\n {overlay.guides.lines.map((l, i) => (\n <line key={i} data-vector-guide={l.kind} x1={l.x1} y1={l.y1} x2={l.x2} y2={l.y2} strokeDasharray={l.kind === 'construction' ? '4 3' : undefined} />\n ))}\n </g>\n )}\n {overlay.guides.labels.map((l, i) => (\n <text\n key={i}\n data-vector-guide-label={l.kind}\n x={l.x + 6}\n y={l.y - 6}\n fontSize={10}\n fill={guideColor}\n stroke=\"#ffffff\"\n strokeWidth={3}\n paintOrder=\"stroke\"\n style={{ fontFamily: 'system-ui, sans-serif' }}\n >\n {l.text}\n </text>\n ))}\n {overlay.status && (\n <text data-vector-status=\"\" x={8} y=\"100%\" dy={-8} fontSize={11} fill={accentColor} style={{ fontFamily: 'system-ui, sans-serif' }}>\n {overlay.status}\n </text>\n )}\n </g>\n </svg>\n );\n}\n","import React from 'react';\nimport {\n VECTOR_TOOLS,\n VECTOR_TOOL_GLYPHS,\n VECTOR_TOOL_SHORTCUTS,\n getVectorSelectionStyle,\n vectorPaintToHex,\n vectorToolLabel,\n type VectorEditorController,\n type VectorTool,\n} from '@pixerate/editor/vector';\nimport { useVectorEditorState } from './useVectorEditor';\n\nconst VECTOR_SNAP_TITLE = 'Snap to objects';\nconst modKeyLabel = () => (typeof navigator !== 'undefined' && /Mac|iPhone|iPad/.test(navigator.platform ?? '') ? '⌘' : 'Ctrl');\n\nconst buttonClass =\n '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';\n\nexport interface VectorToolbarProps {\n controller: VectorEditorController;\n /** Tools to show (default: all). */\n tools?: readonly VectorTool[];\n /** Undo / redo buttons (default `true`). */\n showHistory?: boolean;\n /** Boolean, group, arrange and align buttons (default `true`). */\n showArrange?: boolean;\n /** The snapping toggle (default `true`). */\n showSnap?: boolean;\n className?: string;\n}\n\n/** Tool picker plus undo, snapping, boolean operations, grouping, stacking and alignment. */\nexport function VectorToolbar({ controller, tools = VECTOR_TOOLS, showHistory = true, showArrange = true, showSnap = true, className }: VectorToolbarProps) {\n const state = useVectorEditorState(controller);\n const count = state.selection.length;\n const groupSelected = state.selection.some((id) => controller.getNode(id)?.type === 'group');\n const act = (label: string, title: string, run: () => unknown, disabled = false) => (\n <button key={label} type=\"button\" className={buttonClass} title={title} aria-label={title} disabled={disabled} onClick={() => void run()}>\n {label}\n </button>\n );\n return (\n <div role=\"toolbar\" aria-label=\"Vector tools\" className={`flex flex-wrap items-center gap-1 ${className ?? ''}`}>\n <div className=\"flex items-center gap-1\" role=\"group\" aria-label=\"Tools\">\n {tools.map((tool) => (\n <button\n key={tool}\n type=\"button\"\n data-vector-tool={tool}\n className={buttonClass}\n aria-pressed={state.tool === tool}\n title={`${vectorToolLabel(tool)} (${VECTOR_TOOL_SHORTCUTS[tool]})`}\n aria-label={vectorToolLabel(tool)}\n onClick={() => controller.setTool(tool)}\n >\n {VECTOR_TOOL_GLYPHS[tool]}\n </button>\n ))}\n </div>\n {showHistory && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"History\">\n {act('↶', state.canUndo ? `Undo ${state.undoLabel}` : 'Undo', () => controller.undo(), !state.canUndo)}\n {act('↷', state.canRedo ? `Redo ${state.redoLabel}` : 'Redo', () => controller.redo(), !state.canRedo)}\n </div>\n )}\n {showSnap && (\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Snapping\">\n <button\n type=\"button\"\n data-vector-snap=\"\"\n className={buttonClass}\n aria-pressed={state.snap.objects}\n title={`${VECTOR_SNAP_TITLE} (hold ${modKeyLabel()} to bypass)`}\n aria-label={VECTOR_SNAP_TITLE}\n onClick={() => controller.toggleSnapping()}\n >\n Snap\n </button>\n </div>\n )}\n {showArrange && (\n <>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Combine\">\n {act('∪', 'Union', () => controller.booleanOp('union'), count < 2)}\n {act('−', 'Subtract', () => controller.booleanOp('subtract'), count < 2)}\n {act('∩', 'Intersect', () => controller.booleanOp('intersect'), count < 2)}\n {act('⊻', 'Exclude', () => controller.booleanOp('exclude'), count < 2)}\n {act('G', 'Group', () => controller.group(), count < 1)}\n {act('U', 'Ungroup', () => controller.ungroup(), !groupSelected)}\n </div>\n <div className=\"ml-1 flex items-center gap-1\" role=\"group\" aria-label=\"Arrange\">\n {act('⇈', 'Bring to Front', () => controller.arrange('front'), count < 1)}\n {act('⇊', 'Send to Back', () => controller.arrange('back'), count < 1)}\n {act('⇤', 'Align Left', () => controller.align('left'), count < 1)}\n {act('↔', 'Align Center', () => controller.align('center'), count < 1)}\n {act('⇥', 'Align Right', () => controller.align('right'), count < 1)}\n {act('⤒', 'Align Top', () => controller.align('top'), count < 1)}\n {act('↕', 'Align Middle', () => controller.align('middle'), count < 1)}\n {act('⤓', 'Align Bottom', () => controller.align('bottom'), count < 1)}\n </div>\n </>\n )}\n </div>\n );\n}\n\nexport interface VectorStylePanelProps {\n controller: VectorEditorController;\n className?: string;\n}\n\n/**\n * Fill, stroke, stroke width and opacity of the selection; with nothing selected it edits\n * the defaults for new shapes.\n */\nexport function VectorStylePanel({ controller, className }: VectorStylePanelProps) {\n const state = useVectorEditorState(controller);\n const style = getVectorSelectionStyle(state);\n const source = style ?? { fill: state.defaults.fill ?? null, stroke: state.defaults.stroke ?? null, strokeWidth: state.defaults.strokeWidth ?? 1, opacity: 1 };\n const apply = (patch: Record<string, unknown>) => {\n if (style) controller.setStyle(patch);\n else controller.setDefaults(patch);\n };\n const paintRow = (key: 'fill' | 'stroke', label: string) => {\n const paint = source[key];\n const hex = vectorPaintToHex(paint);\n const on = paint !== null && paint !== 'none';\n return (\n <div className=\"flex items-center gap-2\" data-vector-style={key}>\n <label className=\"flex w-16 items-center gap-1.5 text-xs text-foreground\">\n <input type=\"checkbox\" checked={on} aria-label={`${label} on`} onChange={(e) => apply({ [key]: e.target.checked ? (hex ?? '#000000') : null })} />\n {label}\n </label>\n <input\n type=\"color\"\n aria-label={`${label} colour`}\n className=\"h-7 w-10 cursor-pointer rounded border border-border bg-background disabled:opacity-40\"\n disabled={!on}\n value={hex ?? '#000000'}\n onChange={(e) => apply({ [key]: e.target.value })}\n />\n {on && !hex && <span className=\"truncate text-[11px] text-muted-foreground\">{typeof paint === 'string' ? paint : 'Gradient'}</span>}\n {style?.mixed[key] && <span className=\"text-[11px] text-muted-foreground\">Mixed</span>}\n </div>\n );\n };\n return (\n <div className={`space-y-2 ${className ?? ''}`} aria-label={style ? 'Selection style' : 'New shape style'}>\n <p className=\"text-[11px] font-medium uppercase tracking-wide text-muted-foreground\">{style ? 'Selection' : 'New shapes'}</p>\n {paintRow('fill', 'Fill')}\n {paintRow('stroke', 'Stroke')}\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Width</span>\n <input\n type=\"number\"\n min={0}\n step={0.5}\n aria-label=\"Stroke width\"\n className=\"h-7 w-20 rounded border border-border bg-background px-1.5 text-xs\"\n value={source.strokeWidth}\n onChange={(e) => {\n const n = parseFloat(e.target.value);\n if (Number.isFinite(n) && n >= 0) apply({ strokeWidth: n });\n }}\n />\n </label>\n {style && (\n <label className=\"flex items-center gap-2 text-xs text-foreground\">\n <span className=\"w-16\">Opacity</span>\n <input\n type=\"range\"\n min={0}\n max={1}\n step={0.05}\n aria-label=\"Opacity\"\n className=\"h-4 flex-1 accent-primary\"\n value={source.opacity}\n onChange={(e) => apply({ opacity: parseFloat(e.target.value) })}\n />\n </label>\n )}\n </div>\n );\n}\n","import React from 'react';\nimport type { VectorEditorController, VectorTool } from '@pixerate/editor/vector';\nimport { VectorCanvas, type VectorCanvasProps } from './VectorCanvas';\nimport { VectorStylePanel, VectorToolbar } from './VectorToolbar';\n\nexport interface VectorEditorProps {\n controller: VectorEditorController;\n /** Tools in the toolbar (default: all). */\n tools?: readonly VectorTool[];\n /** The style sidebar (default `true`). */\n showStylePanel?: boolean;\n /** Canvas height (default 520px). */\n height?: number | string;\n canvasProps?: Omit<VectorCanvasProps, 'controller'>;\n className?: string;\n}\n\n/** Toolbar, canvas and style panel for a `VectorEditorController`. */\nexport function VectorEditor({ controller, tools, showStylePanel = true, height = 520, canvasProps, className }: VectorEditorProps) {\n return (\n <div className={`flex flex-col gap-2 ${className ?? ''}`} data-vector-editor=\"\">\n <VectorToolbar controller={controller} tools={tools} />\n <div className=\"flex min-h-0 flex-col gap-2 md:flex-row\">\n <div className=\"relative min-h-[320px] flex-1 overflow-hidden rounded-md border border-border bg-muted/40\" style={{ height }}>\n <VectorCanvas controller={controller} {...canvasProps} />\n </div>\n {showStylePanel && <VectorStylePanel controller={controller} className=\"w-full shrink-0 rounded-md border border-border p-3 md:w-56\" />}\n </div>\n </div>\n );\n}\n"],"mappings":"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","names":["useEffect","useRef","useRef","useEffect","text","Fragment","jsx","jsxs","jsx","jsxs"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@pixerate/editor-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.24.0",
|
|
4
4
|
"description": "React bindings and prompt/rich-text components for @pixerate/editor",
|
|
5
5
|
"author": "Pixerate",
|
|
6
6
|
"license": "MIT",
|
|
@@ -107,7 +107,7 @@
|
|
|
107
107
|
},
|
|
108
108
|
"peerDependencies": {
|
|
109
109
|
"@dagrejs/dagre": "^1.1.8",
|
|
110
|
-
"@pixerate/editor": ">=0.
|
|
110
|
+
"@pixerate/editor": ">=0.34.0 <1.0.0-0",
|
|
111
111
|
"@tiptap/core": "^2.11.5",
|
|
112
112
|
"@tiptap/pm": "^2.11.5",
|
|
113
113
|
"@tiptap/react": "^2.11.5",
|
|
@@ -134,7 +134,7 @@
|
|
|
134
134
|
"tsup": "^8.4.0",
|
|
135
135
|
"typescript": "^5.7.3",
|
|
136
136
|
"vitest": "^4.1.11",
|
|
137
|
-
"@pixerate/editor": "0.
|
|
137
|
+
"@pixerate/editor": "0.34.0"
|
|
138
138
|
},
|
|
139
139
|
"scripts": {
|
|
140
140
|
"build": "tsup",
|