orcasvn-react-diagrams 0.2.11 → 0.2.13
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/CHANGELOG.md +27 -0
- package/README.md +15 -7
- package/ai/api-contract.json +90 -1
- package/ai/invariants.json +6 -2
- package/ai/manifest.json +1 -1
- package/dist/cjs/examples.js +916 -331
- package/dist/cjs/index.js +406 -97
- package/dist/cjs/types/api/createDiagramEditor.d.ts +3 -1
- package/dist/cjs/types/api/types.d.ts +64 -0
- package/dist/cjs/types/displaybox/demos/LinkPortCreationDemoTab.d.ts +3 -0
- package/dist/cjs/types/displaybox/demos/deletionEventsDemo.d.ts +2 -0
- package/dist/cjs/types/displaybox/demos/focusElementDemo.d.ts +4 -0
- package/dist/cjs/types/displaybox/demos/linkLabelsDemo.d.ts +4 -0
- package/dist/cjs/types/displaybox/types.d.ts +3 -0
- package/dist/cjs/types/displaybox/useDemoEditor.d.ts +3 -2
- package/dist/cjs/types/engine/DiagramEngine.d.ts +11 -1
- package/dist/cjs/types/engine/TextLayoutService.d.ts +1 -0
- package/dist/cjs/types/examples/index.d.ts +1 -1
- package/dist/cjs/types/renderer/konva/KonvaInteraction.d.ts +10 -1
- package/dist/esm/examples.js +916 -331
- package/dist/esm/examples.js.map +1 -1
- package/dist/esm/index.js +406 -97
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/api/createDiagramEditor.d.ts +3 -1
- package/dist/esm/types/api/types.d.ts +64 -0
- package/dist/esm/types/displaybox/demos/LinkPortCreationDemoTab.d.ts +3 -0
- package/dist/esm/types/displaybox/demos/deletionEventsDemo.d.ts +2 -0
- package/dist/esm/types/displaybox/demos/focusElementDemo.d.ts +4 -0
- package/dist/esm/types/displaybox/demos/linkLabelsDemo.d.ts +4 -0
- package/dist/esm/types/displaybox/types.d.ts +3 -0
- package/dist/esm/types/displaybox/useDemoEditor.d.ts +3 -2
- package/dist/esm/types/engine/DiagramEngine.d.ts +11 -1
- package/dist/esm/types/engine/TextLayoutService.d.ts +1 -0
- package/dist/esm/types/examples/index.d.ts +1 -1
- package/dist/esm/types/renderer/konva/KonvaInteraction.d.ts +10 -1
- package/dist/examples.d.ts +55 -1
- package/dist/index.d.ts +67 -1
- package/docs/API_CONTRACT.md +88 -1
- package/docs/ARCHITECTURE.md +9 -7
- package/docs/CAPABILITIES.md +5 -0
- package/docs/COMMANDS_EVENTS.md +16 -6
- package/docs/DOCUMENTATION_WORKFLOW.md +5 -1
- package/docs/INTEGRATION_PLAYBOOK.md +8 -0
- package/docs/PORTING_CHECKLIST.md +4 -1
- package/docs/STATE_INVARIANTS.md +3 -0
- package/package.json +1 -1
- package/src/displaybox/demos/DeletionEventsDemoTab.tsx +167 -9
- package/src/displaybox/demos/LinkPortCreationDemoTab.tsx +98 -0
- package/src/displaybox/demos/deletionEventsDemo.ts +2 -2
- package/src/displaybox/demos/focusElementDemo.ts +91 -0
- package/src/displaybox/demos/index.tsx +124 -221
- package/src/displaybox/demos/linkLabelsDemo.ts +164 -0
- package/src/displaybox/demos/linkPortCreationDemo.ts +7 -7
- package/src/displaybox/types.ts +21 -11
- package/src/examples/index.ts +1 -0
package/docs/API_CONTRACT.md
CHANGED
|
@@ -26,6 +26,7 @@ Primary definitions:
|
|
|
26
26
|
- `elementShapeHoverControls?: ElementShapeHoverControls`
|
|
27
27
|
- `onElementShapeHoverControlInteraction?: (event: ElementShapeHoverControlInteractionEvent) => void`
|
|
28
28
|
- `onElementShapeHoverControlActivated?: (event: ElementShapeHoverControlActivationEvent) => void` (deprecated callback contract)
|
|
29
|
+
- `onCreateLinkTargetPort?: (context: LinkTargetPortCreationContext) => Partial<LinkTargetPortDraft> | void`
|
|
29
30
|
- `onChange?: (event: EngineChangeEvent) => void`
|
|
30
31
|
- `onSelection?: (event: EngineSelectionEvent) => void`
|
|
31
32
|
- `linkRouteRefreshPolicy?: LinkRouteRefreshPolicy`
|
|
@@ -104,6 +105,12 @@ Defaults:
|
|
|
104
105
|
- non-finite values fall back to defaults
|
|
105
106
|
- `minZoom`/`maxZoom` are clamped to positive values and normalized into ascending order
|
|
106
107
|
|
|
108
|
+
### `FocusElementOptions`
|
|
109
|
+
- Optional `zoom?: number`
|
|
110
|
+
- Semantics:
|
|
111
|
+
- omitted `zoom` preserves the current viewport zoom
|
|
112
|
+
- provided `zoom` is clamped to the same normalized min/max range used by viewport fit helpers
|
|
113
|
+
|
|
107
114
|
### `TextInteractionPolicy`
|
|
108
115
|
- Optional `movable?: boolean` (default behavior: `true`)
|
|
109
116
|
- Optional `editable?: boolean` (default behavior: `true`)
|
|
@@ -138,12 +145,40 @@ Defaults:
|
|
|
138
145
|
- `anchorCenter`: `true`
|
|
139
146
|
- `orientToHostBorder`: `true`
|
|
140
147
|
|
|
148
|
+
### `LinkTargetPortCreationPhase`
|
|
149
|
+
- `'preview' | 'commit'`
|
|
150
|
+
|
|
151
|
+
### `LinkTargetPortDraft`
|
|
152
|
+
- Alias of `PortData` without `id` and `elementId`
|
|
153
|
+
- `position` uses target-element-local coordinates
|
|
154
|
+
- Returned overrides are normalized against the target element before placeholder render or commit
|
|
155
|
+
|
|
156
|
+
### `LinkTargetPortCreationContext`
|
|
157
|
+
- Required: `phase`, `pointer`, `sourcePortId`, `sourceElementId`, `targetElementId`, `sourcePort`, `targetElement`, `defaultPort`
|
|
158
|
+
- `phase` indicates whether the callback is shaping the placeholder draft (`'preview'`) or the commit draft (`'commit'`)
|
|
159
|
+
- `pointer` is the current world-space pointer/drop coordinate
|
|
160
|
+
- `sourcePort` is a snapshot of the source port data used to derive the destination-port defaults
|
|
161
|
+
- `targetElement` is a snapshot of the target element receiving the created port
|
|
162
|
+
- `defaultPort` is the pre-normalization default draft before host overrides are applied
|
|
163
|
+
|
|
164
|
+
### `LinkTargetPortCreationResult`
|
|
165
|
+
- `Partial<LinkTargetPortDraft> | void`
|
|
166
|
+
|
|
167
|
+
### `LinkTargetPortCreationHandler`
|
|
168
|
+
- Signature: `(context: LinkTargetPortCreationContext) => LinkTargetPortCreationResult`
|
|
169
|
+
- Applies only to link-to-element completion flows
|
|
170
|
+
- Does not cancel creation; use `elementLinkConnecting.cancel()` for host veto
|
|
171
|
+
|
|
141
172
|
### `LinkData`
|
|
142
173
|
- Required: `id`, `sourcePortId`, `targetPortId`, `points`
|
|
143
174
|
- Optional: `routing`, `style`, `textIds`
|
|
144
175
|
- Defaults at runtime/model:
|
|
145
176
|
- `routing`: `'auto'`
|
|
146
177
|
- `textIds`: `[]`
|
|
178
|
+
- Link-label contract:
|
|
179
|
+
- link labels are represented as ordinary `TextData`
|
|
180
|
+
- a link-owned label uses `TextData.ownerId = link.id`
|
|
181
|
+
- `LinkData.textIds` is the owner-side index only; there is no separate `LinkData.label` or `LinkData.labels` field
|
|
147
182
|
|
|
148
183
|
### `LinkRouteRefreshPolicy`
|
|
149
184
|
- Optional `mode?: 'mutation-only' | 'redraw-two-endpoint-change'` (default: `'mutation-only'`)
|
|
@@ -168,12 +203,47 @@ Defaults:
|
|
|
168
203
|
- `beforeRows` / `afterRows`: arrays of `{ childIds: string[] }` snapshots describing derived row membership before and after the topology change
|
|
169
204
|
- Emitted only when enabled grid-child width resizing causes a real row-topology change
|
|
170
205
|
|
|
206
|
+
### `ElementDoubleClickEvent`
|
|
207
|
+
- Required: `elementId`, `pointer`, `isMulti`
|
|
208
|
+
- Emitted for eligible element-body double click / double tap
|
|
209
|
+
- Observational only; does not cancel or replace the normal click-selection flow
|
|
210
|
+
|
|
211
|
+
### `DeletionTrigger`
|
|
212
|
+
- `'direct' | 'selection' | 'cascade'`
|
|
213
|
+
|
|
214
|
+
### `DeletionRootRef`
|
|
215
|
+
- Required: `entity`, `id`
|
|
216
|
+
- `entity`: `'element' | 'port' | 'link' | 'text'`
|
|
217
|
+
- `id` is the original requested delete root for the active delete pass
|
|
218
|
+
|
|
219
|
+
### `ElementDeletingEvent`
|
|
220
|
+
- Required: `elementId`, `trigger`, `root`, `cancel`, `cancelled`
|
|
221
|
+
- Emitted before an element deletion commits, including descendant element cascades
|
|
222
|
+
|
|
223
|
+
### `PortDeletingEvent`
|
|
224
|
+
- Required: `portId`, `elementId`, `trigger`, `root`, `cancel`, `cancelled`
|
|
225
|
+
- Emitted before a port deletion commits, including cascades caused by parent element removal
|
|
226
|
+
|
|
227
|
+
### `LinkDeletingEvent`
|
|
228
|
+
- Required: `linkId`, `sourcePortId`, `targetPortId`, `trigger`, `root`, `cancel`, `cancelled`
|
|
229
|
+
- Emitted before a link deletion commits, including cascades caused by element or port removal
|
|
230
|
+
|
|
231
|
+
### `TextDeletingEvent`
|
|
232
|
+
- Required: `textId`, `trigger`, `root`, `cancel`, `cancelled`
|
|
233
|
+
- Optional: `ownerId?: string | null`
|
|
234
|
+
- Emitted before a text deletion commits, including cascades caused by owner deletion
|
|
235
|
+
|
|
171
236
|
### `TextData`
|
|
172
237
|
- Required: `id`, `content`, `position`
|
|
173
238
|
- Optional: `size`, `style`, `ownerId`, `layout`, `interaction`, `displayContent`, `displayOffset`, `displayClipSize`
|
|
174
239
|
- Defaults at runtime/model:
|
|
175
240
|
- `ownerId`: `null`
|
|
176
241
|
- `displayContent`: mirrors `content` at creation
|
|
242
|
+
- Ownership semantics:
|
|
243
|
+
- element-owned text stores owner-relative local position
|
|
244
|
+
- port-owned text stores owner-relative local position
|
|
245
|
+
- link-owned text stores local offset from the owning link's current geometric midpoint
|
|
246
|
+
- when a link reroutes or endpoints move, link-owned text keeps the same stored local offset and resolves a new world position from the recomputed midpoint
|
|
177
247
|
|
|
178
248
|
### `TextLayout`
|
|
179
249
|
- Optional `boundsMode?: 'owner-width' | 'owner-box' | 'fixed'`
|
|
@@ -217,6 +287,7 @@ Defaults:
|
|
|
217
287
|
- `completeLinkToElement(targetElementId, pointer)`
|
|
218
288
|
- `cancelLink()`
|
|
219
289
|
- `zoomToFitElements(options?)`
|
|
290
|
+
- `focusElement(elementId, options?)`
|
|
220
291
|
- `exportImage(options?)`
|
|
221
292
|
- `resize(width, height)`
|
|
222
293
|
- `setElementShapeHoverControls(controls?)`
|
|
@@ -256,13 +327,18 @@ Defaults:
|
|
|
256
327
|
- `portMoved: PortMovedEvent`
|
|
257
328
|
- `portSelected: PortSelectedEvent`
|
|
258
329
|
- `elementClick: ElementPointerEvent`
|
|
330
|
+
- `elementDoubleClick: ElementDoubleClickEvent`
|
|
259
331
|
- `elementDragged: ElementDropEvent`
|
|
260
332
|
- `elementMoved: ElementMovedEvent`
|
|
261
333
|
- `elementResized: ElementResizedEvent`
|
|
262
334
|
- `gridLayoutChanged: GridLayoutChangedEvent`
|
|
335
|
+
- `elementDeleting: ElementDeletingEvent`
|
|
263
336
|
- `elementDeleted: ElementDeletedEvent`
|
|
337
|
+
- `portDeleting: PortDeletingEvent`
|
|
264
338
|
- `portDeleted: PortDeletedEvent`
|
|
339
|
+
- `linkDeleting: LinkDeletingEvent`
|
|
265
340
|
- `linkDeleted: LinkDeletedEvent`
|
|
341
|
+
- `textDeleting: TextDeletingEvent`
|
|
266
342
|
- `textDeleted: TextDeletedEvent`
|
|
267
343
|
- `textUpdated: TextUpdatedEvent`
|
|
268
344
|
- `elementSelected: ElementSelectedEvent`
|
|
@@ -278,14 +354,25 @@ Defaults:
|
|
|
278
354
|
- `zoomToFitElements` computes bounds from `elements[]` only, ignores ports/links/texts for fit expansion, and no-ops when no elements exist.
|
|
279
355
|
- `zoomToFitElements` ignores hidden elements when computing fit bounds.
|
|
280
356
|
- `zoomToFitElements` applies its result through `setViewport`, so hosts observe the standard viewport patch/change path.
|
|
357
|
+
- `focusElement(elementId, options?)` centers the target element bounds in the current editor viewport and preserves the current zoom unless `options.zoom` is provided.
|
|
358
|
+
- `focusElement(...)` no-ops when the target id does not resolve to an element world position.
|
|
281
359
|
- `exportImage({ fitToContent })` derives crop bounds from visible scene entities only.
|
|
282
360
|
- Built-in empty-paper panning now starts from plain primary-button drag and no longer requires `panKey`; marquee selection uses `Shift + primary drag`.
|
|
361
|
+
- `elementDoubleClick` is observational; normal click selection still runs, and text double click keeps built-in text-edit precedence instead of also emitting `elementDoubleClick`.
|
|
283
362
|
- `clientToWorld` uses: `world = (client - containerRect - pan) / zoom`, with zoom fallback to `1` when zoom is `0`.
|
|
284
363
|
- `rerouteLinks(ids)` skips manual links unless `options.includeManual === true`.
|
|
285
364
|
- `linkRouteRefreshPolicy.mode` defaults to `'mutation-only'`; `'redraw-two-endpoint-change'` enables redraw-cycle reroute checks for links whose two endpoints changed.
|
|
286
365
|
- `updateLinkPoints` always marks the link as manual routing.
|
|
287
366
|
- `linkColorPoolPolicy` is opt-in; it applies to newly created links when enabled, and it may also fill loaded links with missing strokes when `assignOnLoadWhenMissingStroke` is enabled. Explicit non-empty `style.stroke` values are preserved.
|
|
367
|
+
- `onCreateLinkTargetPort` applies only when a link completes onto an element body; direct port-to-port linking and direct `addPortToElement(...)` behavior are unchanged.
|
|
368
|
+
- `onCreateLinkTargetPort` runs during placeholder preview and again during commit, and both phases use the same post-callback target-port normalization path.
|
|
369
|
+
- `onCreateLinkTargetPort` shapes the destination-port draft only; host cancellation remains exclusively on `elementLinkConnecting`.
|
|
370
|
+
- `completeLinkToElement(...)` uses the same target-port callback and cancellation lifecycle as pointer-driven link drag.
|
|
371
|
+
- Link labels use the existing text lifecycle and event surface: `addText(...)`, `moveTextTo(...)`, `updateText(...)`, `removeText(...)`, `textUpdated`, and `textDeleted`.
|
|
372
|
+
- Link-owned text defaults to unconstrained layout unless `TextLayout.boundsMode` is explicitly set; element-only owner-width defaults do not apply to link labels.
|
|
288
373
|
- `gridLayoutChanged` is additive and does not replace standard `change` / `elementResized` flows.
|
|
374
|
+
- Deletion mutators emit cancellable `elementDeleting` / `portDeleting` / `linkDeleting` / `textDeleting` before commit and matching `...Deleted` events after successful removal.
|
|
375
|
+
- Canceling any dependent `...Deleting` event aborts the current root deletion request; `deleteSelection()` still continues evaluating unrelated selected roots.
|
|
289
376
|
- `ElementData.visible = false` hides only the element body from built-in renderer sync, hit testing, marquee selection, persisted selection, and fit/export helpers.
|
|
290
377
|
- Descendant element visibility is not suppressed by hidden ancestors; direct-owned parent ports/texts still follow the hidden parent, and links remain visible whenever both endpoint ports are visible.
|
|
291
378
|
- `ElementData.selectable = false` blocks built-in element-body selection while still allowing visible owned ports to participate in their normal interaction flows.
|
|
@@ -294,7 +381,7 @@ Defaults:
|
|
|
294
381
|
- `TextData.interaction.movable = false` blocks built-in drag only; selection still works and programmatic movement remains allowed.
|
|
295
382
|
- `TextData.interaction.editable = false` blocks built-in textarea editing only; selection still works and programmatic text updates remain allowed.
|
|
296
383
|
- Text layout/overflow resolution updates `displayContent` and emits `textUpdated` when `updateText` is called.
|
|
297
|
-
-
|
|
384
|
+
- Successful cascade removal still emits the matching dependent `...Deleted` events for removed entities.
|
|
298
385
|
|
|
299
386
|
## Verification References
|
|
300
387
|
- `src/api/__tests__/createDiagramEditor.test.ts`
|
package/docs/ARCHITECTURE.md
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
> Sections: [Module Boundaries](#module-boundaries) | [Runtime Flow](#runtime-flow) | [Integration Boundary](#integration-boundary) | [Key Sources](#key-sources)
|
|
4
4
|
|
|
5
5
|
## Module Boundaries
|
|
6
|
-
- `src/api/`: public entry points and
|
|
7
|
-
- `src/engine/`: command execution, event bus, selection, viewport, routing/snap integration.
|
|
6
|
+
- `src/api/`: public entry points, type contracts, and editor-only helpers such as `zoomToFitElements(...)` and `focusElement(...)`.
|
|
7
|
+
- `src/engine/`: command execution, event bus, selection, viewport, routing/snap integration, and delete-lifecycle orchestration.
|
|
8
8
|
- `src/engine/`: `DiagramEngine` orchestration plus extracted services (`TextLayoutService`, `AutoLayoutService`, `LinkRoutingService`, `MutationPipeline`).
|
|
9
9
|
- `src/models/`: in-memory state graph for elements/ports/links/texts.
|
|
10
10
|
- `src/renderer/`: renderer interface and shape registry contracts.
|
|
@@ -14,15 +14,17 @@
|
|
|
14
14
|
- `src/utils/`: geometry, patch helpers, guards, ids.
|
|
15
15
|
|
|
16
16
|
## Runtime Flow
|
|
17
|
-
1. Host calls API mutator.
|
|
18
|
-
2.
|
|
19
|
-
3. Engine
|
|
20
|
-
4.
|
|
21
|
-
5.
|
|
17
|
+
1. Host calls API mutator or editor helper.
|
|
18
|
+
2. Interaction or API helper resolves target intent and any required lifecycle hooks.
|
|
19
|
+
3. Engine executes command(s) against model or applies viewport state updates.
|
|
20
|
+
4. Engine emits patches + latest state via `change`.
|
|
21
|
+
5. Scheduler requests render.
|
|
22
|
+
6. Renderer syncs model to view nodes.
|
|
22
23
|
|
|
23
24
|
## Integration Boundary
|
|
24
25
|
- Engine core does not require Konva if you use `createDiagramEngine` with a custom `Renderer`.
|
|
25
26
|
- `createDiagramEditor` wires Konva stage, hit testing, and interaction defaults.
|
|
27
|
+
- The Konva-backed editor surface also owns editor-only navigation helpers such as `focusElement(...)`.
|
|
26
28
|
|
|
27
29
|
## Key Sources
|
|
28
30
|
- `src/api/createDiagramEditor.ts`
|
package/docs/CAPABILITIES.md
CHANGED
|
@@ -18,7 +18,12 @@
|
|
|
18
18
|
- Optional grid-child width topology editing with `gridLayoutChanged` host events.
|
|
19
19
|
- Parent-owned direct-child drag suppression through `ElementData.childElementInteraction`.
|
|
20
20
|
- Element-level visibility/selectability policy through `ElementData.visible` and `ElementData.selectable`, with element-body-only hiding, direct-owned port/text suppression, and visible descendants that keep their own links/rendering behavior.
|
|
21
|
+
- Host-callable viewport focus through `focusElement(elementId, options?)`.
|
|
21
22
|
- Element shape hover controls with edge/vertex/midpoint targets and interaction callbacks.
|
|
23
|
+
- Observational `elementDoubleClick` event for element-body double click / double tap.
|
|
24
|
+
- Cancellable delete lifecycle events (`elementDeleting`, `portDeleting`, `linkDeleting`, `textDeleting`) before direct, selection, and cascade removals commit.
|
|
25
|
+
- Link labels through link-owned `TextData`, including midpoint-relative attachment that follows reroute and endpoint movement.
|
|
26
|
+
- Optional host callback for shaping auto-created destination ports during link-to-element preview and commit.
|
|
22
27
|
- Optional random link color assignment from configurable pools during link creation, plus opt-in load-time fill for links missing `style.stroke`.
|
|
23
28
|
- Per-text interaction policy for suppressing built-in text drag and/or built-in text editing.
|
|
24
29
|
- Overlay shapes for external drag ghosting and previews.
|
package/docs/COMMANDS_EVENTS.md
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
- Engine mutators route through command factories in `src/engine/EngineCommands.ts`.
|
|
7
7
|
- Each command applies model mutation and returns one or more patches.
|
|
8
8
|
- `emitChange` publishes `{ patches, state }` on `change`.
|
|
9
|
-
- Editor helpers such as `zoomToFitElements(options?)` compose existing
|
|
9
|
+
- Editor helpers such as `zoomToFitElements(options?)` and `focusElement(elementId, options?)` compose existing viewport updates rather than introducing separate patch types.
|
|
10
10
|
|
|
11
11
|
## Patch Entities
|
|
12
12
|
- `element`
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
## Mutation To Events Mapping
|
|
21
21
|
- `add/move/resize/remove element`
|
|
22
22
|
- Always `change`
|
|
23
|
-
- Plus `elementMoved`, `elementResized`, or `elementDeleted` when applicable
|
|
23
|
+
- Plus `elementMoved`, `elementResized`, `elementDeleting`, or `elementDeleted` when applicable
|
|
24
24
|
- `resize element` with enabled grid-child width topology editing
|
|
25
25
|
- Standard `change`
|
|
26
26
|
- Standard `elementResized` when size changes
|
|
@@ -28,22 +28,26 @@
|
|
|
28
28
|
- `add/move/remove port`
|
|
29
29
|
- Always `change`
|
|
30
30
|
- Plus `portMoved` on effective movement
|
|
31
|
-
- Plus `portDeleted` on removal (including cascade removal)
|
|
31
|
+
- Plus `portDeleting` before removal and `portDeleted` on successful removal (including cascade removal)
|
|
32
32
|
- `add/update/remove link`
|
|
33
33
|
- Always `change`
|
|
34
|
-
- Plus `linkDeleted` on removal (including cascade removal)
|
|
34
|
+
- Plus `linkDeleting` before removal and `linkDeleted` on successful removal (including cascade removal)
|
|
35
35
|
- `add/update/move/remove text`
|
|
36
36
|
- Always `change`
|
|
37
37
|
- Plus `textUpdated` when `updateText` resolves display layout
|
|
38
|
-
- Plus `textDeleted` on removal (including cascade removal)
|
|
38
|
+
- Plus `textDeleting` before removal and `textDeleted` on successful removal (including cascade removal)
|
|
39
39
|
- `setSelection/toggleSelection/deleteSelection`
|
|
40
40
|
- `selection` plus derived selected entity events
|
|
41
|
+
- `deleteSelection` evaluates each selected root independently, so a vetoed root can be skipped while unrelated roots still delete
|
|
41
42
|
- hidden element bodies, direct-owned hidden artifacts, and `selectable:false` element IDs are filtered out before selection persists/emits
|
|
42
43
|
- `setViewport`
|
|
43
44
|
- `change` with viewport patch (`render: false`)
|
|
44
45
|
- `zoomToFitElements`
|
|
45
46
|
- delegates to `setViewport` when at least one visible element exists
|
|
46
47
|
- emits the same viewport `change` patch path
|
|
48
|
+
- `focusElement`
|
|
49
|
+
- delegates to `setViewport` when the target element resolves
|
|
50
|
+
- preserves the current zoom unless `options.zoom` is supplied
|
|
47
51
|
- `setRouting/setSnapping/registerShape`
|
|
48
52
|
- `config` events
|
|
49
53
|
|
|
@@ -53,19 +57,25 @@
|
|
|
53
57
|
- legacy `element-drop`
|
|
54
58
|
- `elementDragged`
|
|
55
59
|
- Click paper: `paperClick`
|
|
60
|
+
- Double click/tap element body:
|
|
61
|
+
- `elementDoubleClick`
|
|
56
62
|
- Linking:
|
|
57
63
|
- `elementLinkStarted`
|
|
64
|
+
- optional `onCreateLinkTargetPort(...)` preview/commit shaping for link-to-element completion
|
|
58
65
|
- `elementLinkConnecting` (cancelable via `cancel()`)
|
|
59
66
|
- `elementLinkEnded`
|
|
60
67
|
|
|
61
68
|
## Preconditions
|
|
62
|
-
- Events are emitted only after engine state transition logic.
|
|
69
|
+
- Events are emitted only after engine state transition logic resolves a valid action.
|
|
70
|
+
- Cancellable `...Deleting` events emit before the corresponding mutation commits.
|
|
63
71
|
- Derived events require selected IDs to resolve to known entity types.
|
|
64
72
|
|
|
65
73
|
## Failure Modes
|
|
66
74
|
- Unknown IDs do not emit entity-specific movement/selection events.
|
|
67
75
|
- Canceled link creation emits `elementLinkEnded` with `cancelled=true` and no link creation.
|
|
76
|
+
- `focusElement(...)` emits no viewport change when the target element id does not resolve.
|
|
68
77
|
- Built-in element-body selection emits no persisted element selection when the target element is hidden or sets `selectable = false`.
|
|
78
|
+
- Text double click continues text editing precedence and does not also emit `elementDoubleClick`.
|
|
69
79
|
- Built-in drag on a direct child element emits no move/drop mutation when its parent sets `childElementInteraction.movable = false`.
|
|
70
80
|
- `textUpdated` emits only when target text exists; missing IDs remain no-op.
|
|
71
81
|
|
|
@@ -59,7 +59,7 @@ When resuming later:
|
|
|
59
59
|
5. Re-run packaging verification.
|
|
60
60
|
|
|
61
61
|
## 6. Current Status
|
|
62
|
-
- Last updated: 2026-
|
|
62
|
+
- Last updated: 2026-07-07
|
|
63
63
|
- Last completed step: 10
|
|
64
64
|
- Next step: 7 (optional additional fixture coverage for deeper nested/manual-route scenarios)
|
|
65
65
|
- Owner: Codex (with repository maintainers)
|
|
@@ -91,3 +91,7 @@ When resuming later:
|
|
|
91
91
|
- 2026-05-27: Completed release-doc pass for `v0.2.10`; refreshed release highlights, local-body visibility semantics, state/event integration notes, and machine-readable contract metadata.
|
|
92
92
|
- 2026-06-20: Completed release-doc pass for `v0.2.11`; refreshed release highlights, link color pool load-time fill contract text, integration guidance, and machine-readable contract metadata.
|
|
93
93
|
- 2026-06-20: Ran release validations: `npm run typecheck`, `npm test -- --watchAll=false`, `npm run build`, `npm run rollup-build-lib`, and `npm pack --dry-run`.
|
|
94
|
+
- 2026-06-26: Completed release-doc pass for `v0.2.12`; refreshed release highlights, link-created target-port callback contract text, integration guidance, and machine-readable contract metadata.
|
|
95
|
+
- 2026-06-26: Ran release validations: `npm run typecheck`, `npm test -- --watchAll=false`, `npm run build`, `npm run rollup-build-lib`, and `npm pack --dry-run`.
|
|
96
|
+
- 2026-07-07: Completed release-doc pass for `v0.2.13`; refreshed release highlights, focus/navigation and delete-lifecycle API/event docs, and machine-readable contract metadata.
|
|
97
|
+
- 2026-07-07: Ran release validations: `npm run typecheck`, `npm test -- --watchAll=false`, `npm run build`, `npm run rollup-build-lib`, and `npm pack --dry-run`.
|
|
@@ -21,6 +21,10 @@ Embed this library into another diagram host while preserving deterministic stat
|
|
|
21
21
|
8. Set `ElementData.childElementInteraction` on parent elements when direct child nodes should stay selectable but not be draggable through built-in interaction.
|
|
22
22
|
9. Set `ElementData.visible` and `ElementData.selectable` when hosts need hidden-but-retained nodes or visible-but-nonselectable element bodies; hidden elements hide only their own body while direct-owned ports/texts still follow the hidden parent and visible descendants remain available to built-in view helpers.
|
|
23
23
|
10. Set `linkColorPoolPolicy` when hosts want random link colors on creation and optional load-time backfill for loaded links whose `style.stroke` is missing or blank.
|
|
24
|
+
11. Create link labels with ordinary `TextData` by setting `ownerId` to the target link id; the stored `position` is midpoint-relative for link-owned text rather than a free world-space coordinate.
|
|
25
|
+
12. Set `onCreateLinkTargetPort` when hosts need to customize the auto-created destination port for link-to-element drops; keep using `elementLinkConnecting` when the host needs to cancel the drop entirely.
|
|
26
|
+
13. Use `focusElement(elementId, options?)` when the host needs to center one editor element in response to navigation, search, or external selection.
|
|
27
|
+
14. Subscribe to `elementDoubleClick` and cancellable `...Deleting` events when the host needs double-click intent or pre-delete confirmation/guard logic.
|
|
24
28
|
|
|
25
29
|
## Path B: Engine-Only Adapter
|
|
26
30
|
1. Implement `Renderer`:
|
|
@@ -37,10 +41,14 @@ Embed this library into another diagram host while preserving deterministic stat
|
|
|
37
41
|
- host port/anchor -> `PortData`
|
|
38
42
|
- host edge -> `LinkData`
|
|
39
43
|
- host label -> `TextData`
|
|
44
|
+
- host edge label -> `TextData` with `ownerId = link.id`
|
|
40
45
|
- Event mapping:
|
|
41
46
|
- host click -> `setSelection` / `emitElementClick`-equivalent behavior
|
|
47
|
+
- host double click / double tap -> `elementDoubleClick`
|
|
42
48
|
- host drag end -> `emitElementDrop`-equivalent payload
|
|
43
49
|
- host link gesture -> `elementLinkStarted/Connecting/Ended` lifecycle
|
|
50
|
+
- optional host target-port shaping -> `onCreateLinkTargetPort(...)` for link-to-element preview/commit only
|
|
51
|
+
- host delete confirmation / veto surface -> cancellable `elementDeleting` / `portDeleting` / `linkDeleting` / `textDeleting`
|
|
44
52
|
|
|
45
53
|
## Behavioral Checklist Per Integration Step
|
|
46
54
|
- Preconditions
|
|
@@ -32,14 +32,17 @@
|
|
|
32
32
|
- Ensure host can emulate:
|
|
33
33
|
- `elementDrop` + legacy `element-drop`
|
|
34
34
|
- `paperClick`
|
|
35
|
+
- `elementDoubleClick`
|
|
35
36
|
- `elementLinkStarted/Connecting/Ended`
|
|
36
37
|
- `portMouseDown/portMouseUp`
|
|
37
|
-
-
|
|
38
|
+
- cancellable delete lifecycle events (`elementDeleting`, `portDeleting`, `linkDeleting`, `textDeleting`)
|
|
39
|
+
- post-delete and text lifecycle events (`elementDeleted`, `portDeleted`, `linkDeleted`, `textDeleted`, `textUpdated`)
|
|
38
40
|
|
|
39
41
|
## 6. Verify Cascading Deletes
|
|
40
42
|
- Deleting element should remove descendant ports, connected links, owned texts.
|
|
41
43
|
- Deleting port should remove connected links and owned texts.
|
|
42
44
|
- Deleting link should remove owned texts.
|
|
45
|
+
- A veto from any `...Deleting` handler should abort that root delete request before mutation commit.
|
|
43
46
|
|
|
44
47
|
## 7. Run Fixture-Based Validation
|
|
45
48
|
- Load fixtures from `fixtures/*.json`.
|
package/docs/STATE_INVARIANTS.md
CHANGED
|
@@ -39,12 +39,15 @@
|
|
|
39
39
|
- Manual routing preserves interior bends on endpoint movement.
|
|
40
40
|
- Links with unresolved source/target world positions are skipped by reroute/update paths.
|
|
41
41
|
- Link visible-state requires both endpoint ports to remain visible.
|
|
42
|
+
- Deleting a link cascades to any link-owned text labels.
|
|
42
43
|
|
|
43
44
|
## Text
|
|
44
45
|
- `ownerId` can reference element, port, or link.
|
|
45
46
|
- Owned text position is stored owner-relative.
|
|
47
|
+
- Link-owned text stores owner-relative position as a local offset from the owning link midpoint.
|
|
46
48
|
- Text with missing owner behaves as standalone world-position text.
|
|
47
49
|
- Owned text visible-state follows its owner visibility when the owner resolves to an element, port, or link.
|
|
50
|
+
- Link-owned text recomputes world position from the current link midpoint plus stored local offset.
|
|
48
51
|
- Text interaction policy is persisted separately from style/layout metadata.
|
|
49
52
|
- `interaction.movable = false` suppresses built-in drag without blocking selection or programmatic movement.
|
|
50
53
|
- `interaction.editable = false` suppresses built-in editing without blocking selection or programmatic content updates.
|
package/package.json
CHANGED
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
2
|
import type {
|
|
3
|
+
ElementDeletingEvent,
|
|
4
|
+
ElementDoubleClickEvent,
|
|
3
5
|
ElementDeletedEvent,
|
|
6
|
+
LinkDeletingEvent,
|
|
4
7
|
LinkDeletedEvent,
|
|
8
|
+
PortDeletingEvent,
|
|
5
9
|
PortDeletedEvent,
|
|
10
|
+
TextDeletingEvent,
|
|
6
11
|
TextDeletedEvent,
|
|
7
12
|
} from '../../api';
|
|
8
13
|
import DisplayBoxControls from '../DisplayBoxControls';
|
|
@@ -14,17 +19,41 @@ import type { DemoActionHelpers } from '../types';
|
|
|
14
19
|
import { deletionEventsDemoConfig } from './deletionEventsDemo';
|
|
15
20
|
|
|
16
21
|
type EventPayloads = {
|
|
22
|
+
elementDeleting: ElementDeletingEvent | null;
|
|
17
23
|
elementDeleted: ElementDeletedEvent | null;
|
|
24
|
+
portDeleting: PortDeletingEvent | null;
|
|
18
25
|
portDeleted: PortDeletedEvent | null;
|
|
26
|
+
linkDeleting: LinkDeletingEvent | null;
|
|
19
27
|
linkDeleted: LinkDeletedEvent | null;
|
|
28
|
+
textDeleting: TextDeletingEvent | null;
|
|
20
29
|
textDeleted: TextDeletedEvent | null;
|
|
30
|
+
elementDoubleClick: ElementDoubleClickEvent | null;
|
|
21
31
|
};
|
|
22
32
|
|
|
23
33
|
const initialPayloads: EventPayloads = {
|
|
34
|
+
elementDeleting: null,
|
|
24
35
|
elementDeleted: null,
|
|
36
|
+
portDeleting: null,
|
|
25
37
|
portDeleted: null,
|
|
38
|
+
linkDeleting: null,
|
|
26
39
|
linkDeleted: null,
|
|
40
|
+
textDeleting: null,
|
|
27
41
|
textDeleted: null,
|
|
42
|
+
elementDoubleClick: null,
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
type CancelFlags = {
|
|
46
|
+
element: boolean;
|
|
47
|
+
port: boolean;
|
|
48
|
+
link: boolean;
|
|
49
|
+
text: boolean;
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const initialCancelFlags: CancelFlags = {
|
|
53
|
+
element: false,
|
|
54
|
+
port: false,
|
|
55
|
+
link: false,
|
|
56
|
+
text: false,
|
|
28
57
|
};
|
|
29
58
|
|
|
30
59
|
const eventBoxStyle: React.CSSProperties = {
|
|
@@ -61,21 +90,79 @@ const DeletionEventsDemo = () => {
|
|
|
61
90
|
});
|
|
62
91
|
|
|
63
92
|
const [payloads, setPayloads] = useState<EventPayloads>(initialPayloads);
|
|
93
|
+
const [cancelFlags, setCancelFlags] = useState<CancelFlags>(initialCancelFlags);
|
|
94
|
+
const [eventLog, setEventLog] = useState<string[]>([]);
|
|
95
|
+
|
|
96
|
+
const appendLog = React.useCallback((eventName: string, payload: unknown) => {
|
|
97
|
+
setEventLog((prev) => [
|
|
98
|
+
`${eventName}: ${JSON.stringify(payload)}`,
|
|
99
|
+
...prev,
|
|
100
|
+
].slice(0, 18));
|
|
101
|
+
}, []);
|
|
64
102
|
|
|
65
103
|
useEffect(() => {
|
|
66
104
|
const editor = editorRef.current;
|
|
67
105
|
if (!editor) return undefined;
|
|
68
106
|
setPayloads(initialPayloads);
|
|
107
|
+
setEventLog([]);
|
|
69
108
|
const unsubs = [
|
|
70
|
-
editor.on('
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
109
|
+
editor.on('elementDeleting', (payload) => {
|
|
110
|
+
if (cancelFlags.element) {
|
|
111
|
+
payload.cancel();
|
|
112
|
+
}
|
|
113
|
+
setPayloads((prev) => ({ ...prev, elementDeleting: payload }));
|
|
114
|
+
appendLog('elementDeleting', payload);
|
|
115
|
+
}),
|
|
116
|
+
editor.on('elementDeleted', (payload) => {
|
|
117
|
+
setPayloads((prev) => ({ ...prev, elementDeleted: payload }));
|
|
118
|
+
appendLog('elementDeleted', payload);
|
|
119
|
+
}),
|
|
120
|
+
editor.on('portDeleting', (payload) => {
|
|
121
|
+
if (cancelFlags.port) {
|
|
122
|
+
payload.cancel();
|
|
123
|
+
}
|
|
124
|
+
setPayloads((prev) => ({ ...prev, portDeleting: payload }));
|
|
125
|
+
appendLog('portDeleting', payload);
|
|
126
|
+
}),
|
|
127
|
+
editor.on('portDeleted', (payload) => {
|
|
128
|
+
setPayloads((prev) => ({ ...prev, portDeleted: payload }));
|
|
129
|
+
appendLog('portDeleted', payload);
|
|
130
|
+
}),
|
|
131
|
+
editor.on('linkDeleting', (payload) => {
|
|
132
|
+
if (cancelFlags.link) {
|
|
133
|
+
payload.cancel();
|
|
134
|
+
}
|
|
135
|
+
setPayloads((prev) => ({ ...prev, linkDeleting: payload }));
|
|
136
|
+
appendLog('linkDeleting', payload);
|
|
137
|
+
}),
|
|
138
|
+
editor.on('linkDeleted', (payload) => {
|
|
139
|
+
setPayloads((prev) => ({ ...prev, linkDeleted: payload }));
|
|
140
|
+
appendLog('linkDeleted', payload);
|
|
141
|
+
}),
|
|
142
|
+
editor.on('textDeleting', (payload) => {
|
|
143
|
+
if (cancelFlags.text) {
|
|
144
|
+
payload.cancel();
|
|
145
|
+
}
|
|
146
|
+
setPayloads((prev) => ({ ...prev, textDeleting: payload }));
|
|
147
|
+
appendLog('textDeleting', payload);
|
|
148
|
+
}),
|
|
149
|
+
editor.on('textDeleted', (payload) => {
|
|
150
|
+
setPayloads((prev) => ({ ...prev, textDeleted: payload }));
|
|
151
|
+
appendLog('textDeleted', payload);
|
|
152
|
+
}),
|
|
153
|
+
editor.on('elementDoubleClick', (payload) => {
|
|
154
|
+
setPayloads((prev) => ({ ...prev, elementDoubleClick: payload }));
|
|
155
|
+
appendLog('elementDoubleClick', payload);
|
|
156
|
+
}),
|
|
74
157
|
];
|
|
75
158
|
return () => {
|
|
76
|
-
unsubs.forEach((unsub) =>
|
|
159
|
+
unsubs.forEach((unsub) => {
|
|
160
|
+
if (typeof unsub === 'function') {
|
|
161
|
+
unsub();
|
|
162
|
+
}
|
|
163
|
+
});
|
|
77
164
|
};
|
|
78
|
-
}, [editorRef]);
|
|
165
|
+
}, [appendLog, cancelFlags, editorRef]);
|
|
79
166
|
|
|
80
167
|
const hasPort = (diagramState?.ports ?? []).some((port) => port.id === 'delete-source-free-port');
|
|
81
168
|
const hasLink = (diagramState?.links ?? []).some((link) => link.id === 'delete-link');
|
|
@@ -87,8 +174,10 @@ const DeletionEventsDemo = () => {
|
|
|
87
174
|
<div style={{ marginBottom: 12 }}>
|
|
88
175
|
<h2 style={{ marginTop: 0, marginBottom: 4 }}>{demo.title}</h2>
|
|
89
176
|
<p style={{ marginTop: 0 }}>
|
|
90
|
-
Use direct delete buttons for port/link/text, then delete <code>delete-source</code> to trigger
|
|
91
|
-
|
|
177
|
+
Use the direct delete buttons for port/link/text, then delete <code>delete-source</code> to trigger
|
|
178
|
+
cascades. Turn cancellation toggles on to veto deletes before commit, double click an element body to log
|
|
179
|
+
<code> elementDoubleClick</code>, and use the batch-selection helper before pressing Delete to verify
|
|
180
|
+
partial success.
|
|
92
181
|
</p>
|
|
93
182
|
</div>
|
|
94
183
|
<DisplayBoxControls
|
|
@@ -123,24 +212,93 @@ const DeletionEventsDemo = () => {
|
|
|
123
212
|
<button type="button" onClick={() => editorRef.current?.removeElement('delete-source')} disabled={!hasCascadeElement}>
|
|
124
213
|
Delete Element (Cascade)
|
|
125
214
|
</button>
|
|
215
|
+
<button
|
|
216
|
+
type="button"
|
|
217
|
+
onClick={() => editorRef.current?.setSelection(['delete-free-text', 'delete-target'])}
|
|
218
|
+
disabled={!hasText}
|
|
219
|
+
>
|
|
220
|
+
Prepare Batch Delete Selection
|
|
221
|
+
</button>
|
|
222
|
+
</div>
|
|
223
|
+
<div style={{ display: 'flex', flexWrap: 'wrap', gap: 12 }}>
|
|
224
|
+
<label>
|
|
225
|
+
<input
|
|
226
|
+
type="checkbox"
|
|
227
|
+
checked={cancelFlags.element}
|
|
228
|
+
onChange={(event) => setCancelFlags((prev) => ({ ...prev, element: event.target.checked }))}
|
|
229
|
+
/>
|
|
230
|
+
{' '}
|
|
231
|
+
Cancel elementDeleting
|
|
232
|
+
</label>
|
|
233
|
+
<label>
|
|
234
|
+
<input
|
|
235
|
+
type="checkbox"
|
|
236
|
+
checked={cancelFlags.port}
|
|
237
|
+
onChange={(event) => setCancelFlags((prev) => ({ ...prev, port: event.target.checked }))}
|
|
238
|
+
/>
|
|
239
|
+
{' '}
|
|
240
|
+
Cancel portDeleting
|
|
241
|
+
</label>
|
|
242
|
+
<label>
|
|
243
|
+
<input
|
|
244
|
+
type="checkbox"
|
|
245
|
+
checked={cancelFlags.link}
|
|
246
|
+
onChange={(event) => setCancelFlags((prev) => ({ ...prev, link: event.target.checked }))}
|
|
247
|
+
/>
|
|
248
|
+
{' '}
|
|
249
|
+
Cancel linkDeleting
|
|
250
|
+
</label>
|
|
251
|
+
<label>
|
|
252
|
+
<input
|
|
253
|
+
type="checkbox"
|
|
254
|
+
checked={cancelFlags.text}
|
|
255
|
+
onChange={(event) => setCancelFlags((prev) => ({ ...prev, text: event.target.checked }))}
|
|
256
|
+
/>
|
|
257
|
+
{' '}
|
|
258
|
+
Cancel textDeleting
|
|
259
|
+
</label>
|
|
126
260
|
</div>
|
|
127
|
-
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(
|
|
261
|
+
<div style={{ display: 'grid', gridTemplateColumns: 'repeat(3, minmax(0, 1fr))', gap: 12 }}>
|
|
262
|
+
<div>
|
|
263
|
+
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>elementDeleting</label>
|
|
264
|
+
<textarea readOnly value={payloads.elementDeleting ? JSON.stringify(payloads.elementDeleting, null, 2) : ''} style={eventBoxStyle} />
|
|
265
|
+
</div>
|
|
128
266
|
<div>
|
|
129
267
|
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>elementDeleted</label>
|
|
130
268
|
<textarea readOnly value={payloads.elementDeleted ? JSON.stringify(payloads.elementDeleted, null, 2) : ''} style={eventBoxStyle} />
|
|
131
269
|
</div>
|
|
270
|
+
<div>
|
|
271
|
+
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>portDeleting</label>
|
|
272
|
+
<textarea readOnly value={payloads.portDeleting ? JSON.stringify(payloads.portDeleting, null, 2) : ''} style={eventBoxStyle} />
|
|
273
|
+
</div>
|
|
132
274
|
<div>
|
|
133
275
|
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>portDeleted</label>
|
|
134
276
|
<textarea readOnly value={payloads.portDeleted ? JSON.stringify(payloads.portDeleted, null, 2) : ''} style={eventBoxStyle} />
|
|
135
277
|
</div>
|
|
278
|
+
<div>
|
|
279
|
+
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>linkDeleting</label>
|
|
280
|
+
<textarea readOnly value={payloads.linkDeleting ? JSON.stringify(payloads.linkDeleting, null, 2) : ''} style={eventBoxStyle} />
|
|
281
|
+
</div>
|
|
136
282
|
<div>
|
|
137
283
|
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>linkDeleted</label>
|
|
138
284
|
<textarea readOnly value={payloads.linkDeleted ? JSON.stringify(payloads.linkDeleted, null, 2) : ''} style={eventBoxStyle} />
|
|
139
285
|
</div>
|
|
286
|
+
<div>
|
|
287
|
+
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>textDeleting</label>
|
|
288
|
+
<textarea readOnly value={payloads.textDeleting ? JSON.stringify(payloads.textDeleting, null, 2) : ''} style={eventBoxStyle} />
|
|
289
|
+
</div>
|
|
140
290
|
<div>
|
|
141
291
|
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>textDeleted</label>
|
|
142
292
|
<textarea readOnly value={payloads.textDeleted ? JSON.stringify(payloads.textDeleted, null, 2) : ''} style={eventBoxStyle} />
|
|
143
293
|
</div>
|
|
294
|
+
<div>
|
|
295
|
+
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>elementDoubleClick</label>
|
|
296
|
+
<textarea readOnly value={payloads.elementDoubleClick ? JSON.stringify(payloads.elementDoubleClick, null, 2) : ''} style={eventBoxStyle} />
|
|
297
|
+
</div>
|
|
298
|
+
</div>
|
|
299
|
+
<div>
|
|
300
|
+
<label style={{ display: 'block', fontWeight: 600, marginBottom: 6 }}>Event Log</label>
|
|
301
|
+
<textarea readOnly value={eventLog.join('\n')} style={{ ...eventBoxStyle, minHeight: 180 }} />
|
|
144
302
|
</div>
|
|
145
303
|
</div>
|
|
146
304
|
<DisplayBoxStage containerRef={containerRef} />
|