orcasvn-react-diagrams 0.2.12 → 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 +14 -0
- package/README.md +15 -11
- package/ai/api-contract.json +47 -0
- package/ai/invariants.json +6 -2
- package/ai/manifest.json +1 -1
- package/dist/cjs/examples.js +427 -48
- package/dist/cjs/index.js +226 -36
- package/dist/cjs/types/api/createDiagramEditor.d.ts +2 -1
- package/dist/cjs/types/api/types.d.ts +50 -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/engine/DiagramEngine.d.ts +9 -0
- package/dist/esm/examples.js +427 -48
- package/dist/esm/examples.js.map +1 -1
- package/dist/esm/index.js +226 -36
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/types/api/createDiagramEditor.d.ts +2 -1
- package/dist/esm/types/api/types.d.ts +50 -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/engine/DiagramEngine.d.ts +9 -0
- package/dist/examples.d.ts +51 -0
- package/dist/index.d.ts +52 -1
- package/docs/API_CONTRACT.md +48 -1
- package/docs/ARCHITECTURE.md +9 -7
- package/docs/CAPABILITIES.md +3 -0
- package/docs/COMMANDS_EVENTS.md +15 -6
- package/docs/DOCUMENTATION_WORKFLOW.md +3 -1
- package/docs/INTEGRATION_PLAYBOOK.md +4 -0
- package/docs/PORTING_CHECKLIST.md +4 -1
- package/package.json +1 -1
- package/src/displaybox/demos/DeletionEventsDemoTab.tsx +167 -9
- package/src/displaybox/demos/deletionEventsDemo.ts +2 -2
- package/src/displaybox/demos/focusElementDemo.ts +91 -0
- package/src/displaybox/demos/index.tsx +2 -0
|
@@ -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} />
|
|
@@ -2,7 +2,7 @@ import type { DiagramState } from '../../api';
|
|
|
2
2
|
import type { DemoConfig } from '../types';
|
|
3
3
|
import { baseElementShapes, basePortShapes } from './shared';
|
|
4
4
|
|
|
5
|
-
const createDeletionEventsState = (): DiagramState => ({
|
|
5
|
+
export const createDeletionEventsState = (): DiagramState => ({
|
|
6
6
|
elements: [
|
|
7
7
|
{
|
|
8
8
|
id: 'delete-source',
|
|
@@ -81,7 +81,7 @@ const createDeletionEventsState = (): DiagramState => ({
|
|
|
81
81
|
export const deletionEventsDemoConfig: DemoConfig = {
|
|
82
82
|
id: 'deletion-events',
|
|
83
83
|
title: 'Deletion Events',
|
|
84
|
-
description: 'Inspect
|
|
84
|
+
description: 'Inspect cancellable delete lifecycle events, post-delete payloads, and element double click in one integration-focused scenario.',
|
|
85
85
|
createState: createDeletionEventsState,
|
|
86
86
|
elementShapes: baseElementShapes,
|
|
87
87
|
portShapes: basePortShapes,
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import type { DiagramState } from '../../api';
|
|
2
|
+
import type { DemoAction, DemoConfig } from '../types';
|
|
3
|
+
import { baseElementShapes, basePortShapes } from './shared';
|
|
4
|
+
|
|
5
|
+
export const createFocusElementState = (): DiagramState => ({
|
|
6
|
+
elements: [
|
|
7
|
+
{
|
|
8
|
+
id: 'focus-left',
|
|
9
|
+
position: { x: 40, y: 80 },
|
|
10
|
+
size: { width: 140, height: 90 },
|
|
11
|
+
shapeId: 'default',
|
|
12
|
+
style: { fill: '#e0f2fe', stroke: '#0369a1', strokeWidth: 2 },
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
id: 'focus-right',
|
|
16
|
+
position: { x: 820, y: 120 },
|
|
17
|
+
size: { width: 180, height: 110 },
|
|
18
|
+
shapeId: 'panel',
|
|
19
|
+
style: { fill: '#fef3c7', stroke: '#b45309', strokeWidth: 2 },
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
id: 'focus-parent',
|
|
23
|
+
position: { x: 520, y: 420 },
|
|
24
|
+
size: { width: 220, height: 160 },
|
|
25
|
+
shapeId: 'default',
|
|
26
|
+
style: { fill: '#ecfccb', stroke: '#4d7c0f', strokeWidth: 2 },
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
id: 'focus-child',
|
|
30
|
+
parentId: 'focus-parent',
|
|
31
|
+
position: { x: 110, y: 60 },
|
|
32
|
+
size: { width: 70, height: 50 },
|
|
33
|
+
shapeId: 'default',
|
|
34
|
+
anchorCenter: true,
|
|
35
|
+
style: { fill: '#dcfce7', stroke: '#15803d', strokeWidth: 2 },
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
ports: [],
|
|
39
|
+
links: [],
|
|
40
|
+
texts: [
|
|
41
|
+
{
|
|
42
|
+
id: 'focus-copy',
|
|
43
|
+
content: 'Compare explicit element focus against zoom-to-fit.',
|
|
44
|
+
position: { x: 40, y: 26 },
|
|
45
|
+
},
|
|
46
|
+
],
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const focusLeftAction: DemoAction = {
|
|
50
|
+
id: 'focus-left-default',
|
|
51
|
+
label: 'Focus Left (Keep Zoom)',
|
|
52
|
+
run: (editor) => {
|
|
53
|
+
editor.focusElement('focus-left');
|
|
54
|
+
},
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const focusRightZoomAction: DemoAction = {
|
|
58
|
+
id: 'focus-right-zoom',
|
|
59
|
+
label: 'Focus Right (Zoom 2.2)',
|
|
60
|
+
run: (editor) => {
|
|
61
|
+
editor.focusElement('focus-right', { zoom: 2.2 });
|
|
62
|
+
},
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
const focusChildZoomAction: DemoAction = {
|
|
66
|
+
id: 'focus-child-zoom',
|
|
67
|
+
label: 'Focus Nested Child (Zoom 1.8)',
|
|
68
|
+
run: (editor) => {
|
|
69
|
+
editor.focusElement('focus-child', { zoom: 1.8 });
|
|
70
|
+
},
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
const fitAllAction: DemoAction = {
|
|
74
|
+
id: 'focus-fit-all',
|
|
75
|
+
label: 'Zoom-To-Fit For Comparison',
|
|
76
|
+
run: (editor) => {
|
|
77
|
+
editor.zoomToFitElements({ padding: 40 });
|
|
78
|
+
},
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
export const focusElementDemoConfig: DemoConfig = {
|
|
82
|
+
id: 'focus-element',
|
|
83
|
+
title: 'Focus Element',
|
|
84
|
+
description: 'Centers one explicit element, optionally with a chosen zoom, unlike zoom-to-fit which frames the whole scene.',
|
|
85
|
+
createState: createFocusElementState,
|
|
86
|
+
elementShapes: baseElementShapes,
|
|
87
|
+
portShapes: basePortShapes,
|
|
88
|
+
defaultElementShapeId: 'default',
|
|
89
|
+
defaultPortShapeId: 'port-circle',
|
|
90
|
+
actions: [focusLeftAction, focusRightZoomAction, focusChildZoomAction, fitAllAction],
|
|
91
|
+
};
|
|
@@ -53,6 +53,7 @@ import AsymmetricPortMultiAnchorDemo from './AsymmetricPortMultiAnchorDemoTab';
|
|
|
53
53
|
import VertexControlLinkSessionDemo from './VertexControlLinkSessionDemoTab';
|
|
54
54
|
import { offsetAnchorAvoidanceDemoConfig } from './offsetAnchorAvoidanceDemo';
|
|
55
55
|
import { zoomToFitElementsDemoConfig } from './zoomToFitElementsDemo';
|
|
56
|
+
import { focusElementDemoConfig } from './focusElementDemo';
|
|
56
57
|
|
|
57
58
|
export {
|
|
58
59
|
baseElementShapes,
|
|
@@ -120,6 +121,7 @@ const basics = [
|
|
|
120
121
|
registerDemo(selectionDemoConfig, wrapSimpleDemo(selectionDemoConfig), 'basics', 4),
|
|
121
122
|
registerDemo(elementVisibilitySelectionDemoConfig, wrapSimpleDemo(elementVisibilitySelectionDemoConfig), 'basics', 5),
|
|
122
123
|
registerDemo(zoomToFitElementsDemoConfig, ZoomToFitElementsDemo, 'basics', 6),
|
|
124
|
+
registerDemo(focusElementDemoConfig, wrapSimpleDemo(focusElementDemoConfig), 'basics', 7),
|
|
123
125
|
];
|
|
124
126
|
|
|
125
127
|
const layoutAndText = [
|