canvu-react 0.4.64 → 0.4.66

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.
@@ -118,6 +118,175 @@ type VectorToolDefinition = {
118
118
  ariaLabel?: string;
119
119
  };
120
120
 
121
+ type ReadOnlyItemClickCandidateDetail = {
122
+ /** The item Canvu hit-tested under the read-only click. */
123
+ item: VectorSceneItem;
124
+ /** World-space pointer location at press time. */
125
+ worldX: number;
126
+ worldY: number;
127
+ /** Viewport/client pointer location at press time. */
128
+ clientX: number;
129
+ clientY: number;
130
+ pointerType: string;
131
+ shiftKey: boolean;
132
+ altKey: boolean;
133
+ metaKey: boolean;
134
+ ctrlKey: boolean;
135
+ /** Current viewport items. */
136
+ items: readonly VectorSceneItem[];
137
+ /** Selection snapshot before read-only activation handles the click. */
138
+ selectedIds: readonly string[];
139
+ };
140
+ /**
141
+ * Controls which items may receive select-mode click activation while
142
+ * `VectorViewport` is read-only (`interactive={false}`).
143
+ *
144
+ * `"custom"` keeps the default high-level behavior: custom placement/tool
145
+ * items with `onSelectModeItemClick` remain clickable, while regular items do
146
+ * not become selectable unless the app opts in. Use `"all"` or a predicate for
147
+ * read-only viewers that still need item selection or item-specific side effects.
148
+ */
149
+ type ReadOnlyItemClickScope = "custom" | "all" | ((detail: ReadOnlyItemClickCandidateDetail) => boolean);
150
+
151
+ /**
152
+ * Stable ids for Canvu's built-in shape context-menu items.
153
+ *
154
+ * Use these with `ShapeContextMenu.itemContributions`, `VectorViewport.contextMenuItems`,
155
+ * or plugin `contextMenuItems` when replacing a default item without relying on magic strings.
156
+ */
157
+ declare const SHAPE_CONTEXT_MENU_ITEM_IDS: {
158
+ readonly cut: "cut";
159
+ readonly copy: "copy";
160
+ readonly duplicate: "duplicate";
161
+ readonly reorderDivider: "reorder-divider";
162
+ readonly bringToFront: "bring-to-front";
163
+ readonly bringForward: "bring-forward";
164
+ readonly sendBackward: "send-backward";
165
+ readonly sendToBack: "send-to-back";
166
+ readonly lockDivider: "lock-divider";
167
+ readonly toggleLock: "toggle-lock";
168
+ readonly delete: "delete";
169
+ };
170
+ /**
171
+ * Runtime data and built-in commands exposed to shape context-menu item renderers.
172
+ *
173
+ * Use this for small menu item customizations and plugin actions. The built-in action
174
+ * functions already close the menu after running, matching Canvu's default menu behavior.
175
+ * For full viewport-level replacement, use {@link VectorViewportProps.contextMenu}.
176
+ */
177
+ type ShapeContextMenuRenderContext = {
178
+ /** Viewport client coordinates where the menu opened. */
179
+ position: {
180
+ x: number;
181
+ y: number;
182
+ };
183
+ /** Selected item ids targeted by this menu. */
184
+ selectedIds: readonly string[];
185
+ /** Selected scene items targeted by this menu. */
186
+ selectedItems: readonly VectorSceneItem[];
187
+ /** True when every selected item is locked. */
188
+ allSelectedLocked: boolean;
189
+ /** True when at least one selected item is locked. */
190
+ anySelectedLocked: boolean;
191
+ /** Close the floating context menu without running an action. */
192
+ close: () => void;
193
+ /** Cut the selected items to Canvu's internal clipboard, then close the menu. */
194
+ cut: () => void;
195
+ /** Copy the selected items to Canvu's internal clipboard, then close the menu. */
196
+ copy: () => void;
197
+ /** Duplicate selected items, then close the menu. */
198
+ duplicate: () => void;
199
+ /** Toggle lock state for selected items, then close the menu. */
200
+ toggleLock: () => void;
201
+ /** Move selected items above all siblings, then close the menu. */
202
+ bringToFront: () => void;
203
+ /** Move selected items forward one layer, then close the menu. */
204
+ bringForward: () => void;
205
+ /** Move selected items backward one layer, then close the menu. */
206
+ sendBackward: () => void;
207
+ /** Move selected items behind all siblings, then close the menu. */
208
+ sendToBack: () => void;
209
+ /** Delete selected items, then close the menu. */
210
+ delete: () => void;
211
+ /** Alias for {@link ShapeContextMenuRenderContext.delete}. */
212
+ deleteSelection: () => void;
213
+ };
214
+ /**
215
+ * One renderable context-menu row, divider, or custom block.
216
+ *
217
+ * Items compose by `id`: passing an item with a built-in id such as
218
+ * `SHAPE_CONTEXT_MENU_ITEM_IDS.copy` replaces that item in place, while new ids append.
219
+ *
220
+ * @example
221
+ * ```tsx
222
+ * const replaceCopy = {
223
+ * id: SHAPE_CONTEXT_MENU_ITEM_IDS.copy,
224
+ * render: (ctx) => <button onClick={ctx.copy}>Copy shape ids</button>,
225
+ * };
226
+ * ```
227
+ */
228
+ type ShapeContextMenuItem = {
229
+ /** Stable id used for merge-by-id replacement and ordering. */
230
+ id: string;
231
+ /** Render the item using the current selection and built-in menu actions. */
232
+ render: (ctx: ShapeContextMenuRenderContext) => ReactNode;
233
+ };
234
+ /**
235
+ * Props for {@link ShapeContextMenu}.
236
+ *
237
+ * Use `itemContributions` for small add-or-replace customizations, `items` when you
238
+ * want to own the exact base list, and `children` when you want custom contents inside
239
+ * the floating menu shell. App code usually customizes the menu through
240
+ * {@link VectorViewportProps.contextMenuItems} instead of rendering this component directly.
241
+ */
242
+ type ShapeContextMenuProps = {
243
+ /** Viewport client coordinates (from `clientX` / `clientY`). */
244
+ x: number;
245
+ y: number;
246
+ /** Selected item ids targeted by this menu. */
247
+ selectedIds?: readonly string[];
248
+ /** Selected scene items targeted by this menu. */
249
+ selectedItems?: readonly VectorSceneItem[];
250
+ /** When true, all selected items are locked and the built-in action is "unlock". */
251
+ allSelectedLocked?: boolean;
252
+ onClose: () => void;
253
+ onToggleLock: () => void;
254
+ onCut: () => void;
255
+ onCopy: () => void;
256
+ onBringToFront: () => void;
257
+ onBringForward: () => void;
258
+ onSendBackward: () => void;
259
+ onSendToBack: () => void;
260
+ onDuplicate: () => void;
261
+ onDelete: () => void;
262
+ /**
263
+ * Exact base menu item list. Omit to use Canvu's built-in shape actions.
264
+ *
265
+ * This replaces the default base list; use `itemContributions` for add-or-replace
266
+ * customization without duplicating defaults.
267
+ */
268
+ items?: readonly ShapeContextMenuItem[];
269
+ /**
270
+ * Menu item additions or replacements merged by `id`.
271
+ *
272
+ * Matching ids replace the base item in place, new ids append, and later contributions win.
273
+ * Ignored when `children` are provided because custom children render exact markup.
274
+ */
275
+ itemContributions?: readonly ShapeContextMenuItem[];
276
+ /**
277
+ * Full custom menu contents. JSX nodes are rendered exactly; a render function receives
278
+ * the same selection/action context as menu items. Default items are not rendered.
279
+ */
280
+ children?: ReactNode | ((ctx: ShapeContextMenuRenderContext) => ReactNode);
281
+ };
282
+ /**
283
+ * Floating menu for shape actions (reorder, clipboard, lock, delete).
284
+ *
285
+ * This is a web-only React component. It renders in a portal, closes on Escape or
286
+ * outside click, and composes menu rows by `id` unless exact `children` are provided.
287
+ */
288
+ declare function ShapeContextMenu({ x, y, selectedIds, selectedItems, allSelectedLocked, onClose, onToggleLock, onCut, onCopy, onBringToFront, onBringForward, onSendBackward, onSendToBack, onDuplicate, onDelete, items, itemContributions, children, }: ShapeContextMenuProps): react.ReactPortal | null;
289
+
121
290
  type ActiveToolStyle = StrokeStyle & {
122
291
  toolKind: "draw" | "marker" | "text" | "rect" | "ellipse" | "architectural-cloud" | "line" | "arrow";
123
292
  label?: string;
@@ -179,6 +348,47 @@ type SelectModeItemClickDetail = {
179
348
  /** Set the viewport selection when activation should also select or clear. */
180
349
  setSelectedIds: (ids: readonly string[]) => void;
181
350
  };
351
+
352
+ /**
353
+ * Read-only interaction exceptions for viewers that should stay immutable while
354
+ * still allowing explicit activation/selection clicks.
355
+ *
356
+ * Canvu-owned editing remains disabled: no placement, draw, move, resize,
357
+ * rotate, eraser, paste/drop mutation, text editing, handles, or inspector.
358
+ * Explicit user callbacks may still call `updateItem` when that is the desired
359
+ * app-level exception.
360
+ *
361
+ * @example
362
+ * ```tsx
363
+ * <VectorViewport
364
+ * interactive={false}
365
+ * readOnlyInteraction={{
366
+ * itemClicks: "all",
367
+ * onItemClick: ({ item, setSelectedIds }) => {
368
+ * setSelectedIds([item.id]);
369
+ * return "handled";
370
+ * },
371
+ * }}
372
+ * />
373
+ * ```
374
+ */
375
+ type ReadOnlyInteractionOptions = {
376
+ /**
377
+ * Which read-only item clicks Canvu should capture.
378
+ *
379
+ * Defaults to `"custom"`, which only activates custom placement/tool items
380
+ * that provide `onSelectModeItemClick`.
381
+ */
382
+ itemClicks?: ReadOnlyItemClickScope;
383
+ /**
384
+ * Optional handler for read-only item clicks enabled by `itemClicks`.
385
+ *
386
+ * Custom placement `onSelectModeItemClick` handlers run first for their own
387
+ * items. For non-custom opt-in clicks, returning `undefined` lets Canvu apply
388
+ * default read-only selection semantics.
389
+ */
390
+ onItemClick?: (detail: SelectModeItemClickDetail) => SelectModeItemClickResult;
391
+ };
182
392
  /**
183
393
  * Best-effort reason attached to a React viewport item change.
184
394
  *
@@ -338,8 +548,10 @@ type CustomShapePlacementOptions = {
338
548
  * Optional select-mode click handler for items created by this placement.
339
549
  *
340
550
  * If provided, a plain click on the item in select mode calls this handler instead
341
- * of selecting or moving the item. Resize and rotate handles still win first, and
342
- * dragging past the tap threshold falls back to normal move behavior.
551
+ * of selecting or moving the item. Resize and rotate handles still win first in
552
+ * interactive viewports, and dragging past the tap threshold falls back to normal
553
+ * move behavior. In read-only viewports, the same click still activates the item
554
+ * by default, while dragging past the tap threshold cancels activation.
343
555
  */
344
556
  onSelectModeItemClick?: (detail: SelectModeItemClickDetail) => SelectModeItemClickResult;
345
557
  };
@@ -361,6 +573,15 @@ type VectorViewportProps = {
361
573
  * Requires `onItemsChange` to persist mutations.
362
574
  */
363
575
  interactive?: boolean;
576
+ /**
577
+ * Read-only click exceptions used when `interactive` is false.
578
+ *
579
+ * By default, read-only viewports still allow custom placement/tool items
580
+ * with `onSelectModeItemClick` to activate. Opt into `"all"` or a predicate
581
+ * when a read-only viewer should also allow regular item selection/clicks.
582
+ * Canvu-owned editing stays disabled; only explicit callbacks may mutate.
583
+ */
584
+ readOnlyInteraction?: ReadOnlyInteractionOptions;
364
585
  /** Selected item ids (order preserved). Empty = none. */
365
586
  selectedIds?: readonly string[];
366
587
  onSelectionChange?: (ids: string[]) => void;
@@ -427,6 +648,25 @@ type VectorViewportProps = {
427
648
  * Use this to reposition or theme the default left-side style panel without replacing it.
428
649
  */
429
650
  selectionInspectorProps?: Pick<VectorSelectionInspectorProps, "className" | "style">;
651
+ /**
652
+ * Shape context-menu additions or replacements merged by `id` after built-ins and plugins.
653
+ *
654
+ * Use this for app-level customization such as replacing
655
+ * `SHAPE_CONTEXT_MENU_ITEM_IDS.copy` or adding one extra command. Matching ids
656
+ * replace earlier items in place; new ids append. For exact full replacement,
657
+ * use `contextMenu`.
658
+ */
659
+ contextMenuItems?: readonly ShapeContextMenuItem[];
660
+ /**
661
+ * Full replacement for the shape context menu, or `null` to disable it.
662
+ *
663
+ * The render context includes the merged item list so a custom renderer can reuse
664
+ * Canvu/plugin/app item composition while owning all markup. Omit this prop to
665
+ * render the built-in {@link ShapeContextMenu}.
666
+ */
667
+ contextMenu?: ((ctx: ShapeContextMenuRenderContext & {
668
+ items: readonly ShapeContextMenuItem[];
669
+ }) => ReactNode) | null;
430
670
  /**
431
671
  * Plug and play board extensions.
432
672
  *
@@ -524,6 +764,13 @@ type CanvasPluginContribution = {
524
764
  tools?: readonly VectorToolDefinition[];
525
765
  /** Transform the resolved tool list (reorder, replace, or filter existing tools). */
526
766
  toolTransform?: (tools: VectorToolDefinition[]) => VectorToolDefinition[];
767
+ /**
768
+ * Additional shape context-menu items merged by id after Canvu's built-ins.
769
+ *
770
+ * Matching ids replace existing items in place, new ids append, and plugin order
771
+ * determines tie-breaking before app-level `VectorViewport.contextMenuItems`.
772
+ */
773
+ contextMenuItems?: readonly ShapeContextMenuItem[];
527
774
  /** Multiple custom placements contributed by plugins; the active one is resolved by `toolId`. */
528
775
  customPlacements?: readonly CustomShapePlacementOptions[];
529
776
  /** Simple viewport prop overrides owned by the plugin runtime. */
@@ -624,4 +871,4 @@ declare function useCanvuResolvedTools(): VectorToolDefinition[];
624
871
  */
625
872
  declare function useCanvuPluginContribution(pluginId: string, contribution: CanvasPluginContribution): void;
626
873
 
627
- export { VectorViewport as A, type BoardComponentPosition as B, type CustomShapePlacementOptions as C, type VectorViewportHandle as D, type VectorViewportProps as E, createCanvuPlugin as F, getBoardPositionStyle as G, useCanvuChromeContext as H, useCanvuDocumentContext as I, useCanvuPluginContext as J, useCanvuPluginContribution as K, useCanvuResolvedTools as L, useCanvuViewportContext as M, type PlacementPreview as P, type SelectModeItemClickDetail as S, type VectorToolDefinition as V, type WorldPointerDownDetail as W, type CanvuBeforeInteractionHook as a, type CanvuAfterInteractionHook as b, type CanvasPlugin as c, VectorSelectionInspector as d, type CanvasPluginComponentProps as e, type CanvasPluginContribution as f, type CanvasPluginItemsChangeMiddlewareContext as g, type CanvasPluginRenderContext as h, type CanvuAfterInteractionDetail as i, type CanvuBeforeInteractionResult as j, type CanvuChromeActiveToolStyle as k, CanvuChromeContext as l, type CanvuChromeContextValue as m, type CanvuChromeSelectionStyleChange as n, type CanvuInteractionDetail as o, type CanvuInteractionKind as p, type CanvuInteractionOutcome as q, type CanvuInteractionPoint as r, CanvuPluginContext as s, type CanvuPluginContextValue as t, type CanvuPluginViewportSnapshot as u, type SelectModeItemClickResult as v, type VectorCanvasSpacePosition as w, type VectorItemsChangeInfo as x, type VectorItemsChangeMotive as y, type VectorSelectionInspectorProps as z };
874
+ export { type ShapeContextMenuItem as A, type BoardComponentPosition as B, type CustomShapePlacementOptions as C, type ShapeContextMenuProps as D, type ShapeContextMenuRenderContext as E, type VectorCanvasSpacePosition as F, type VectorItemsChangeInfo as G, type VectorItemsChangeMotive as H, type VectorSelectionInspectorProps as I, VectorViewport as J, type VectorViewportHandle as K, type VectorViewportProps as L, createCanvuPlugin as M, getBoardPositionStyle as N, useCanvuChromeContext as O, type PlacementPreview as P, useCanvuDocumentContext as Q, type ReadOnlyInteractionOptions as R, SHAPE_CONTEXT_MENU_ITEM_IDS as S, useCanvuPluginContext as T, useCanvuPluginContribution as U, type VectorToolDefinition as V, type WorldPointerDownDetail as W, useCanvuResolvedTools as X, useCanvuViewportContext as Y, type CanvuBeforeInteractionHook as a, type CanvuAfterInteractionHook as b, type CanvasPlugin as c, VectorSelectionInspector as d, type CanvasPluginComponentProps as e, type CanvasPluginContribution as f, type CanvasPluginItemsChangeMiddlewareContext as g, type CanvasPluginRenderContext as h, type CanvuAfterInteractionDetail as i, type CanvuBeforeInteractionResult as j, type CanvuChromeActiveToolStyle as k, CanvuChromeContext as l, type CanvuChromeContextValue as m, type CanvuChromeSelectionStyleChange as n, type CanvuInteractionDetail as o, type CanvuInteractionKind as p, type CanvuInteractionOutcome as q, type CanvuInteractionPoint as r, CanvuPluginContext as s, type CanvuPluginContextValue as t, type CanvuPluginViewportSnapshot as u, type ReadOnlyItemClickCandidateDetail as v, type ReadOnlyItemClickScope as w, type SelectModeItemClickDetail as x, type SelectModeItemClickResult as y, ShapeContextMenu as z };
@@ -118,6 +118,175 @@ type VectorToolDefinition = {
118
118
  ariaLabel?: string;
119
119
  };
120
120
 
121
+ type ReadOnlyItemClickCandidateDetail = {
122
+ /** The item Canvu hit-tested under the read-only click. */
123
+ item: VectorSceneItem;
124
+ /** World-space pointer location at press time. */
125
+ worldX: number;
126
+ worldY: number;
127
+ /** Viewport/client pointer location at press time. */
128
+ clientX: number;
129
+ clientY: number;
130
+ pointerType: string;
131
+ shiftKey: boolean;
132
+ altKey: boolean;
133
+ metaKey: boolean;
134
+ ctrlKey: boolean;
135
+ /** Current viewport items. */
136
+ items: readonly VectorSceneItem[];
137
+ /** Selection snapshot before read-only activation handles the click. */
138
+ selectedIds: readonly string[];
139
+ };
140
+ /**
141
+ * Controls which items may receive select-mode click activation while
142
+ * `VectorViewport` is read-only (`interactive={false}`).
143
+ *
144
+ * `"custom"` keeps the default high-level behavior: custom placement/tool
145
+ * items with `onSelectModeItemClick` remain clickable, while regular items do
146
+ * not become selectable unless the app opts in. Use `"all"` or a predicate for
147
+ * read-only viewers that still need item selection or item-specific side effects.
148
+ */
149
+ type ReadOnlyItemClickScope = "custom" | "all" | ((detail: ReadOnlyItemClickCandidateDetail) => boolean);
150
+
151
+ /**
152
+ * Stable ids for Canvu's built-in shape context-menu items.
153
+ *
154
+ * Use these with `ShapeContextMenu.itemContributions`, `VectorViewport.contextMenuItems`,
155
+ * or plugin `contextMenuItems` when replacing a default item without relying on magic strings.
156
+ */
157
+ declare const SHAPE_CONTEXT_MENU_ITEM_IDS: {
158
+ readonly cut: "cut";
159
+ readonly copy: "copy";
160
+ readonly duplicate: "duplicate";
161
+ readonly reorderDivider: "reorder-divider";
162
+ readonly bringToFront: "bring-to-front";
163
+ readonly bringForward: "bring-forward";
164
+ readonly sendBackward: "send-backward";
165
+ readonly sendToBack: "send-to-back";
166
+ readonly lockDivider: "lock-divider";
167
+ readonly toggleLock: "toggle-lock";
168
+ readonly delete: "delete";
169
+ };
170
+ /**
171
+ * Runtime data and built-in commands exposed to shape context-menu item renderers.
172
+ *
173
+ * Use this for small menu item customizations and plugin actions. The built-in action
174
+ * functions already close the menu after running, matching Canvu's default menu behavior.
175
+ * For full viewport-level replacement, use {@link VectorViewportProps.contextMenu}.
176
+ */
177
+ type ShapeContextMenuRenderContext = {
178
+ /** Viewport client coordinates where the menu opened. */
179
+ position: {
180
+ x: number;
181
+ y: number;
182
+ };
183
+ /** Selected item ids targeted by this menu. */
184
+ selectedIds: readonly string[];
185
+ /** Selected scene items targeted by this menu. */
186
+ selectedItems: readonly VectorSceneItem[];
187
+ /** True when every selected item is locked. */
188
+ allSelectedLocked: boolean;
189
+ /** True when at least one selected item is locked. */
190
+ anySelectedLocked: boolean;
191
+ /** Close the floating context menu without running an action. */
192
+ close: () => void;
193
+ /** Cut the selected items to Canvu's internal clipboard, then close the menu. */
194
+ cut: () => void;
195
+ /** Copy the selected items to Canvu's internal clipboard, then close the menu. */
196
+ copy: () => void;
197
+ /** Duplicate selected items, then close the menu. */
198
+ duplicate: () => void;
199
+ /** Toggle lock state for selected items, then close the menu. */
200
+ toggleLock: () => void;
201
+ /** Move selected items above all siblings, then close the menu. */
202
+ bringToFront: () => void;
203
+ /** Move selected items forward one layer, then close the menu. */
204
+ bringForward: () => void;
205
+ /** Move selected items backward one layer, then close the menu. */
206
+ sendBackward: () => void;
207
+ /** Move selected items behind all siblings, then close the menu. */
208
+ sendToBack: () => void;
209
+ /** Delete selected items, then close the menu. */
210
+ delete: () => void;
211
+ /** Alias for {@link ShapeContextMenuRenderContext.delete}. */
212
+ deleteSelection: () => void;
213
+ };
214
+ /**
215
+ * One renderable context-menu row, divider, or custom block.
216
+ *
217
+ * Items compose by `id`: passing an item with a built-in id such as
218
+ * `SHAPE_CONTEXT_MENU_ITEM_IDS.copy` replaces that item in place, while new ids append.
219
+ *
220
+ * @example
221
+ * ```tsx
222
+ * const replaceCopy = {
223
+ * id: SHAPE_CONTEXT_MENU_ITEM_IDS.copy,
224
+ * render: (ctx) => <button onClick={ctx.copy}>Copy shape ids</button>,
225
+ * };
226
+ * ```
227
+ */
228
+ type ShapeContextMenuItem = {
229
+ /** Stable id used for merge-by-id replacement and ordering. */
230
+ id: string;
231
+ /** Render the item using the current selection and built-in menu actions. */
232
+ render: (ctx: ShapeContextMenuRenderContext) => ReactNode;
233
+ };
234
+ /**
235
+ * Props for {@link ShapeContextMenu}.
236
+ *
237
+ * Use `itemContributions` for small add-or-replace customizations, `items` when you
238
+ * want to own the exact base list, and `children` when you want custom contents inside
239
+ * the floating menu shell. App code usually customizes the menu through
240
+ * {@link VectorViewportProps.contextMenuItems} instead of rendering this component directly.
241
+ */
242
+ type ShapeContextMenuProps = {
243
+ /** Viewport client coordinates (from `clientX` / `clientY`). */
244
+ x: number;
245
+ y: number;
246
+ /** Selected item ids targeted by this menu. */
247
+ selectedIds?: readonly string[];
248
+ /** Selected scene items targeted by this menu. */
249
+ selectedItems?: readonly VectorSceneItem[];
250
+ /** When true, all selected items are locked and the built-in action is "unlock". */
251
+ allSelectedLocked?: boolean;
252
+ onClose: () => void;
253
+ onToggleLock: () => void;
254
+ onCut: () => void;
255
+ onCopy: () => void;
256
+ onBringToFront: () => void;
257
+ onBringForward: () => void;
258
+ onSendBackward: () => void;
259
+ onSendToBack: () => void;
260
+ onDuplicate: () => void;
261
+ onDelete: () => void;
262
+ /**
263
+ * Exact base menu item list. Omit to use Canvu's built-in shape actions.
264
+ *
265
+ * This replaces the default base list; use `itemContributions` for add-or-replace
266
+ * customization without duplicating defaults.
267
+ */
268
+ items?: readonly ShapeContextMenuItem[];
269
+ /**
270
+ * Menu item additions or replacements merged by `id`.
271
+ *
272
+ * Matching ids replace the base item in place, new ids append, and later contributions win.
273
+ * Ignored when `children` are provided because custom children render exact markup.
274
+ */
275
+ itemContributions?: readonly ShapeContextMenuItem[];
276
+ /**
277
+ * Full custom menu contents. JSX nodes are rendered exactly; a render function receives
278
+ * the same selection/action context as menu items. Default items are not rendered.
279
+ */
280
+ children?: ReactNode | ((ctx: ShapeContextMenuRenderContext) => ReactNode);
281
+ };
282
+ /**
283
+ * Floating menu for shape actions (reorder, clipboard, lock, delete).
284
+ *
285
+ * This is a web-only React component. It renders in a portal, closes on Escape or
286
+ * outside click, and composes menu rows by `id` unless exact `children` are provided.
287
+ */
288
+ declare function ShapeContextMenu({ x, y, selectedIds, selectedItems, allSelectedLocked, onClose, onToggleLock, onCut, onCopy, onBringToFront, onBringForward, onSendBackward, onSendToBack, onDuplicate, onDelete, items, itemContributions, children, }: ShapeContextMenuProps): react.ReactPortal | null;
289
+
121
290
  type ActiveToolStyle = StrokeStyle & {
122
291
  toolKind: "draw" | "marker" | "text" | "rect" | "ellipse" | "architectural-cloud" | "line" | "arrow";
123
292
  label?: string;
@@ -179,6 +348,47 @@ type SelectModeItemClickDetail = {
179
348
  /** Set the viewport selection when activation should also select or clear. */
180
349
  setSelectedIds: (ids: readonly string[]) => void;
181
350
  };
351
+
352
+ /**
353
+ * Read-only interaction exceptions for viewers that should stay immutable while
354
+ * still allowing explicit activation/selection clicks.
355
+ *
356
+ * Canvu-owned editing remains disabled: no placement, draw, move, resize,
357
+ * rotate, eraser, paste/drop mutation, text editing, handles, or inspector.
358
+ * Explicit user callbacks may still call `updateItem` when that is the desired
359
+ * app-level exception.
360
+ *
361
+ * @example
362
+ * ```tsx
363
+ * <VectorViewport
364
+ * interactive={false}
365
+ * readOnlyInteraction={{
366
+ * itemClicks: "all",
367
+ * onItemClick: ({ item, setSelectedIds }) => {
368
+ * setSelectedIds([item.id]);
369
+ * return "handled";
370
+ * },
371
+ * }}
372
+ * />
373
+ * ```
374
+ */
375
+ type ReadOnlyInteractionOptions = {
376
+ /**
377
+ * Which read-only item clicks Canvu should capture.
378
+ *
379
+ * Defaults to `"custom"`, which only activates custom placement/tool items
380
+ * that provide `onSelectModeItemClick`.
381
+ */
382
+ itemClicks?: ReadOnlyItemClickScope;
383
+ /**
384
+ * Optional handler for read-only item clicks enabled by `itemClicks`.
385
+ *
386
+ * Custom placement `onSelectModeItemClick` handlers run first for their own
387
+ * items. For non-custom opt-in clicks, returning `undefined` lets Canvu apply
388
+ * default read-only selection semantics.
389
+ */
390
+ onItemClick?: (detail: SelectModeItemClickDetail) => SelectModeItemClickResult;
391
+ };
182
392
  /**
183
393
  * Best-effort reason attached to a React viewport item change.
184
394
  *
@@ -338,8 +548,10 @@ type CustomShapePlacementOptions = {
338
548
  * Optional select-mode click handler for items created by this placement.
339
549
  *
340
550
  * If provided, a plain click on the item in select mode calls this handler instead
341
- * of selecting or moving the item. Resize and rotate handles still win first, and
342
- * dragging past the tap threshold falls back to normal move behavior.
551
+ * of selecting or moving the item. Resize and rotate handles still win first in
552
+ * interactive viewports, and dragging past the tap threshold falls back to normal
553
+ * move behavior. In read-only viewports, the same click still activates the item
554
+ * by default, while dragging past the tap threshold cancels activation.
343
555
  */
344
556
  onSelectModeItemClick?: (detail: SelectModeItemClickDetail) => SelectModeItemClickResult;
345
557
  };
@@ -361,6 +573,15 @@ type VectorViewportProps = {
361
573
  * Requires `onItemsChange` to persist mutations.
362
574
  */
363
575
  interactive?: boolean;
576
+ /**
577
+ * Read-only click exceptions used when `interactive` is false.
578
+ *
579
+ * By default, read-only viewports still allow custom placement/tool items
580
+ * with `onSelectModeItemClick` to activate. Opt into `"all"` or a predicate
581
+ * when a read-only viewer should also allow regular item selection/clicks.
582
+ * Canvu-owned editing stays disabled; only explicit callbacks may mutate.
583
+ */
584
+ readOnlyInteraction?: ReadOnlyInteractionOptions;
364
585
  /** Selected item ids (order preserved). Empty = none. */
365
586
  selectedIds?: readonly string[];
366
587
  onSelectionChange?: (ids: string[]) => void;
@@ -427,6 +648,25 @@ type VectorViewportProps = {
427
648
  * Use this to reposition or theme the default left-side style panel without replacing it.
428
649
  */
429
650
  selectionInspectorProps?: Pick<VectorSelectionInspectorProps, "className" | "style">;
651
+ /**
652
+ * Shape context-menu additions or replacements merged by `id` after built-ins and plugins.
653
+ *
654
+ * Use this for app-level customization such as replacing
655
+ * `SHAPE_CONTEXT_MENU_ITEM_IDS.copy` or adding one extra command. Matching ids
656
+ * replace earlier items in place; new ids append. For exact full replacement,
657
+ * use `contextMenu`.
658
+ */
659
+ contextMenuItems?: readonly ShapeContextMenuItem[];
660
+ /**
661
+ * Full replacement for the shape context menu, or `null` to disable it.
662
+ *
663
+ * The render context includes the merged item list so a custom renderer can reuse
664
+ * Canvu/plugin/app item composition while owning all markup. Omit this prop to
665
+ * render the built-in {@link ShapeContextMenu}.
666
+ */
667
+ contextMenu?: ((ctx: ShapeContextMenuRenderContext & {
668
+ items: readonly ShapeContextMenuItem[];
669
+ }) => ReactNode) | null;
430
670
  /**
431
671
  * Plug and play board extensions.
432
672
  *
@@ -524,6 +764,13 @@ type CanvasPluginContribution = {
524
764
  tools?: readonly VectorToolDefinition[];
525
765
  /** Transform the resolved tool list (reorder, replace, or filter existing tools). */
526
766
  toolTransform?: (tools: VectorToolDefinition[]) => VectorToolDefinition[];
767
+ /**
768
+ * Additional shape context-menu items merged by id after Canvu's built-ins.
769
+ *
770
+ * Matching ids replace existing items in place, new ids append, and plugin order
771
+ * determines tie-breaking before app-level `VectorViewport.contextMenuItems`.
772
+ */
773
+ contextMenuItems?: readonly ShapeContextMenuItem[];
527
774
  /** Multiple custom placements contributed by plugins; the active one is resolved by `toolId`. */
528
775
  customPlacements?: readonly CustomShapePlacementOptions[];
529
776
  /** Simple viewport prop overrides owned by the plugin runtime. */
@@ -624,4 +871,4 @@ declare function useCanvuResolvedTools(): VectorToolDefinition[];
624
871
  */
625
872
  declare function useCanvuPluginContribution(pluginId: string, contribution: CanvasPluginContribution): void;
626
873
 
627
- export { VectorViewport as A, type BoardComponentPosition as B, type CustomShapePlacementOptions as C, type VectorViewportHandle as D, type VectorViewportProps as E, createCanvuPlugin as F, getBoardPositionStyle as G, useCanvuChromeContext as H, useCanvuDocumentContext as I, useCanvuPluginContext as J, useCanvuPluginContribution as K, useCanvuResolvedTools as L, useCanvuViewportContext as M, type PlacementPreview as P, type SelectModeItemClickDetail as S, type VectorToolDefinition as V, type WorldPointerDownDetail as W, type CanvuBeforeInteractionHook as a, type CanvuAfterInteractionHook as b, type CanvasPlugin as c, VectorSelectionInspector as d, type CanvasPluginComponentProps as e, type CanvasPluginContribution as f, type CanvasPluginItemsChangeMiddlewareContext as g, type CanvasPluginRenderContext as h, type CanvuAfterInteractionDetail as i, type CanvuBeforeInteractionResult as j, type CanvuChromeActiveToolStyle as k, CanvuChromeContext as l, type CanvuChromeContextValue as m, type CanvuChromeSelectionStyleChange as n, type CanvuInteractionDetail as o, type CanvuInteractionKind as p, type CanvuInteractionOutcome as q, type CanvuInteractionPoint as r, CanvuPluginContext as s, type CanvuPluginContextValue as t, type CanvuPluginViewportSnapshot as u, type SelectModeItemClickResult as v, type VectorCanvasSpacePosition as w, type VectorItemsChangeInfo as x, type VectorItemsChangeMotive as y, type VectorSelectionInspectorProps as z };
874
+ export { type ShapeContextMenuItem as A, type BoardComponentPosition as B, type CustomShapePlacementOptions as C, type ShapeContextMenuProps as D, type ShapeContextMenuRenderContext as E, type VectorCanvasSpacePosition as F, type VectorItemsChangeInfo as G, type VectorItemsChangeMotive as H, type VectorSelectionInspectorProps as I, VectorViewport as J, type VectorViewportHandle as K, type VectorViewportProps as L, createCanvuPlugin as M, getBoardPositionStyle as N, useCanvuChromeContext as O, type PlacementPreview as P, useCanvuDocumentContext as Q, type ReadOnlyInteractionOptions as R, SHAPE_CONTEXT_MENU_ITEM_IDS as S, useCanvuPluginContext as T, useCanvuPluginContribution as U, type VectorToolDefinition as V, type WorldPointerDownDetail as W, useCanvuResolvedTools as X, useCanvuViewportContext as Y, type CanvuBeforeInteractionHook as a, type CanvuAfterInteractionHook as b, type CanvasPlugin as c, VectorSelectionInspector as d, type CanvasPluginComponentProps as e, type CanvasPluginContribution as f, type CanvasPluginItemsChangeMiddlewareContext as g, type CanvasPluginRenderContext as h, type CanvuAfterInteractionDetail as i, type CanvuBeforeInteractionResult as j, type CanvuChromeActiveToolStyle as k, CanvuChromeContext as l, type CanvuChromeContextValue as m, type CanvuChromeSelectionStyleChange as n, type CanvuInteractionDetail as o, type CanvuInteractionKind as p, type CanvuInteractionOutcome as q, type CanvuInteractionPoint as r, CanvuPluginContext as s, type CanvuPluginContextValue as t, type CanvuPluginViewportSnapshot as u, type ReadOnlyItemClickCandidateDetail as v, type ReadOnlyItemClickScope as w, type SelectModeItemClickDetail as x, type SelectModeItemClickResult as y, ShapeContextMenu as z };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "canvu-react",
3
- "version": "0.4.64",
3
+ "version": "0.4.66",
4
4
  "description": "Vector-first infinite canvas (SVG) with pan, zoom, React bindings, and optional plugins",
5
5
  "license": "MIT",
6
6
  "type": "module",