@finos/legend-extension-dsl-diagram 8.1.195 → 8.1.197

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.
Files changed (95) hide show
  1. package/lib/components/DiagramRenderer.d.ts +39 -2
  2. package/lib/components/DiagramRenderer.d.ts.map +1 -1
  3. package/lib/components/DiagramRenderer.js +83 -61
  4. package/lib/components/DiagramRenderer.js.map +1 -1
  5. package/lib/components/DiagramViewer.d.ts +39 -0
  6. package/lib/components/DiagramViewer.d.ts.map +1 -0
  7. package/lib/components/DiagramViewer.js +170 -0
  8. package/lib/components/DiagramViewer.js.map +1 -0
  9. package/lib/components/DiagramViewerState.d.ts +44 -0
  10. package/lib/components/DiagramViewerState.d.ts.map +1 -0
  11. package/lib/components/DiagramViewerState.js +136 -0
  12. package/lib/components/DiagramViewerState.js.map +1 -0
  13. package/lib/components/index.d.ts +4 -1
  14. package/lib/components/index.d.ts.map +1 -1
  15. package/lib/components/index.js +4 -1
  16. package/lib/components/index.js.map +1 -1
  17. package/lib/graph-manager/index.d.ts +3 -0
  18. package/lib/graph-manager/index.d.ts.map +1 -1
  19. package/lib/graph-manager/index.js +3 -0
  20. package/lib/graph-manager/index.js.map +1 -1
  21. package/lib/index.css +2 -2
  22. package/lib/index.css.map +1 -1
  23. package/lib/index.d.ts +0 -1
  24. package/lib/index.d.ts.map +1 -1
  25. package/lib/index.js +0 -1
  26. package/lib/index.js.map +1 -1
  27. package/lib/package.json +4 -5
  28. package/package.json +8 -9
  29. package/src/components/DiagramRenderer.ts +221 -103
  30. package/src/components/DiagramViewer.tsx +650 -0
  31. package/src/components/DiagramViewerState.ts +171 -0
  32. package/src/components/index.ts +4 -0
  33. package/src/graph-manager/index.ts +3 -0
  34. package/src/index.ts +0 -1
  35. package/tsconfig.json +2 -12
  36. package/lib/__lib__/studio/DSL_Diagram_LegendStudioApplicationNavigationContext.d.ts +0 -19
  37. package/lib/__lib__/studio/DSL_Diagram_LegendStudioApplicationNavigationContext.d.ts.map +0 -1
  38. package/lib/__lib__/studio/DSL_Diagram_LegendStudioApplicationNavigationContext.js +0 -20
  39. package/lib/__lib__/studio/DSL_Diagram_LegendStudioApplicationNavigationContext.js.map +0 -1
  40. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCodeSnippet.d.ts +0 -20
  41. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCodeSnippet.d.ts.map +0 -1
  42. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCodeSnippet.js +0 -81
  43. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCodeSnippet.js.map +0 -1
  44. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCommand.d.ts +0 -28
  45. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCommand.d.ts.map +0 -1
  46. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCommand.js +0 -62
  47. package/lib/__lib__/studio/DSL_Diagram_LegendStudioCommand.js.map +0 -1
  48. package/lib/__lib__/studio/DSL_Diagram_LegendStudioDocumentation.d.ts +0 -20
  49. package/lib/__lib__/studio/DSL_Diagram_LegendStudioDocumentation.d.ts.map +0 -1
  50. package/lib/__lib__/studio/DSL_Diagram_LegendStudioDocumentation.js +0 -21
  51. package/lib/__lib__/studio/DSL_Diagram_LegendStudioDocumentation.js.map +0 -1
  52. package/lib/__lib__/studio/DSL_Diagram_LegendStudioTesting.d.ts +0 -20
  53. package/lib/__lib__/studio/DSL_Diagram_LegendStudioTesting.d.ts.map +0 -1
  54. package/lib/__lib__/studio/DSL_Diagram_LegendStudioTesting.js +0 -21
  55. package/lib/__lib__/studio/DSL_Diagram_LegendStudioTesting.js.map +0 -1
  56. package/lib/components/studio/ClassDiagramPreview.d.ts +0 -22
  57. package/lib/components/studio/ClassDiagramPreview.d.ts.map +0 -1
  58. package/lib/components/studio/ClassDiagramPreview.js +0 -64
  59. package/lib/components/studio/ClassDiagramPreview.js.map +0 -1
  60. package/lib/components/studio/DSL_Diagram_LegendStudioApplicationPlugin.d.ts +0 -42
  61. package/lib/components/studio/DSL_Diagram_LegendStudioApplicationPlugin.d.ts.map +0 -1
  62. package/lib/components/studio/DSL_Diagram_LegendStudioApplicationPlugin.js +0 -216
  63. package/lib/components/studio/DSL_Diagram_LegendStudioApplicationPlugin.js.map +0 -1
  64. package/lib/components/studio/DiagramEditor.d.ts +0 -19
  65. package/lib/components/studio/DiagramEditor.d.ts.map +0 -1
  66. package/lib/components/studio/DiagramEditor.js +0 -436
  67. package/lib/components/studio/DiagramEditor.js.map +0 -1
  68. package/lib/components/studio/InheritanceDiagramRenderer.d.ts +0 -22
  69. package/lib/components/studio/InheritanceDiagramRenderer.d.ts.map +0 -1
  70. package/lib/components/studio/InheritanceDiagramRenderer.js +0 -34
  71. package/lib/components/studio/InheritanceDiagramRenderer.js.map +0 -1
  72. package/lib/components/studio/index.d.ts +0 -18
  73. package/lib/components/studio/index.d.ts.map +0 -1
  74. package/lib/components/studio/index.js +0 -18
  75. package/lib/components/studio/index.js.map +0 -1
  76. package/lib/stores/studio/DSL_Diagram_GraphModifierHelper.d.ts +0 -57
  77. package/lib/stores/studio/DSL_Diagram_GraphModifierHelper.d.ts.map +0 -1
  78. package/lib/stores/studio/DSL_Diagram_GraphModifierHelper.js +0 -94
  79. package/lib/stores/studio/DSL_Diagram_GraphModifierHelper.js.map +0 -1
  80. package/lib/stores/studio/DiagramEditorState.d.ts +0 -88
  81. package/lib/stores/studio/DiagramEditorState.d.ts.map +0 -1
  82. package/lib/stores/studio/DiagramEditorState.js +0 -341
  83. package/lib/stores/studio/DiagramEditorState.js.map +0 -1
  84. package/src/__lib__/studio/DSL_Diagram_LegendStudioApplicationNavigationContext.ts +0 -19
  85. package/src/__lib__/studio/DSL_Diagram_LegendStudioCodeSnippet.ts +0 -88
  86. package/src/__lib__/studio/DSL_Diagram_LegendStudioCommand.ts +0 -64
  87. package/src/__lib__/studio/DSL_Diagram_LegendStudioDocumentation.ts +0 -20
  88. package/src/__lib__/studio/DSL_Diagram_LegendStudioTesting.ts +0 -20
  89. package/src/components/studio/ClassDiagramPreview.tsx +0 -83
  90. package/src/components/studio/DSL_Diagram_LegendStudioApplicationPlugin.tsx +0 -313
  91. package/src/components/studio/DiagramEditor.tsx +0 -1514
  92. package/src/components/studio/InheritanceDiagramRenderer.ts +0 -50
  93. package/src/components/studio/index.ts +0 -19
  94. package/src/stores/studio/DSL_Diagram_GraphModifierHelper.ts +0 -166
  95. package/src/stores/studio/DiagramEditorState.ts +0 -487
@@ -1,1514 +0,0 @@
1
- /**
2
- * Copyright (c) 2020-present, Goldman Sachs
3
- *
4
- * Licensed under the Apache License, Version 2.0 (the "License");
5
- * you may not use this file except in compliance with the License.
6
- * You may obtain a copy of the License at
7
- *
8
- * http://www.apache.org/licenses/LICENSE-2.0
9
- *
10
- * Unless required by applicable law or agreed to in writing, software
11
- * distributed under the License is distributed on an "AS IS" BASIS,
12
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- * See the License for the specific language governing permissions and
14
- * limitations under the License.
15
- */
16
-
17
- import { useRef, useState, useEffect, useCallback, forwardRef } from 'react';
18
- import { type DropTargetMonitor, useDrop } from 'react-dnd';
19
- import { observer } from 'mobx-react-lite';
20
- import {
21
- DIAGRAM_ALIGNER_OPERATOR,
22
- DiagramRenderer,
23
- DIAGRAM_INTERACTION_MODE,
24
- DIAGRAM_RELATIONSHIP_EDIT_MODE,
25
- DIAGRAM_ZOOM_LEVELS,
26
- } from '../DiagramRenderer.js';
27
- import {
28
- type DiagramEditorInlineClassCreatorState,
29
- type DiagramEditorInlineClassRenamerState,
30
- type DiagramEditorInlinePropertyEditorState,
31
- DIAGRAM_EDITOR_SIDE_PANEL_TAB,
32
- DiagramEditorClassViewEditorSidePanelState,
33
- DiagramEditorState,
34
- } from '../../stores/studio/DiagramEditorState.js';
35
- import {
36
- type ResizablePanelHandlerProps,
37
- ContextMenu,
38
- getCollapsiblePanelGroupProps,
39
- BasePopover,
40
- BlankPanelContent,
41
- CaretDownIcon,
42
- CheckSquareIcon,
43
- clsx,
44
- createFilter,
45
- CustomSelectorInput,
46
- KeyboardIcon,
47
- ControlledDropdownMenu,
48
- MenuContent,
49
- MenuContentDivider,
50
- MenuContentItem,
51
- PlusIcon,
52
- SquareIcon,
53
- ResizablePanelGroup,
54
- ResizablePanelSplitter,
55
- ResizablePanel,
56
- ResizeIcon,
57
- MinusIcon,
58
- MousePointerIcon,
59
- MoveIcon,
60
- PlusCircleIcon,
61
- SidebarIcon,
62
- TriangleIcon,
63
- ZoomInIcon,
64
- ZoomOutIcon,
65
- Dialog,
66
- AlignEndIcon,
67
- DistributeHorizontalIcon,
68
- DistributeVerticalIcon,
69
- AlignStartIcon,
70
- AlignCenterIcon,
71
- AlignTopIcon,
72
- AlignMiddleIcon,
73
- AlignBottomIcon,
74
- useResizeDetector,
75
- Modal,
76
- ModalBody,
77
- ModalHeader,
78
- } from '@finos/legend-art';
79
- import {
80
- type Type,
81
- type Multiplicity,
82
- Class,
83
- DerivedProperty,
84
- Property,
85
- ELEMENT_PATH_DELIMITER,
86
- MULTIPLICITY_INFINITE,
87
- GenericType,
88
- createPath,
89
- isValidFullPath,
90
- isValidPathIdentifier,
91
- resolvePackagePathAndElementName,
92
- } from '@finos/legend-graph';
93
- import { guaranteeNonNullable, prettyCONSTName } from '@finos/legend-shared';
94
- import { flowResult } from 'mobx';
95
- import {
96
- useApplicationStore,
97
- useApplicationNavigationContext,
98
- useCommands,
99
- } from '@finos/legend-application';
100
- import {
101
- ClassFormEditor,
102
- CORE_DND_TYPE,
103
- ElementDragSource,
104
- useEditorStore,
105
- property_setName,
106
- property_setGenericType,
107
- property_setMultiplicity,
108
- queryClass,
109
- } from '@finos/legend-application-studio';
110
- import { cleanUpDeadReferencesInDiagram } from '../../graph/helpers/DSL_Diagram_Helper.js';
111
- import { Point } from '../../graph/metamodel/pure/packageableElements/diagram/geometry/DSL_Diagram_Point.js';
112
- import {
113
- classView_setHideProperties,
114
- classView_setHideStereotypes,
115
- classView_setHideTaggedValues,
116
- } from '../../stores/studio/DSL_Diagram_GraphModifierHelper.js';
117
- import { DSL_DIAGRAM_LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../__lib__/studio/DSL_Diagram_LegendStudioApplicationNavigationContext.js';
118
- import { DSL_DIAGRAM_TEST_ID } from '../../__lib__/studio/DSL_Diagram_LegendStudioTesting.js';
119
- import {
120
- buildElementOption,
121
- type PackageableElementOption,
122
- } from '@finos/legend-lego/graph-editor';
123
-
124
- const DiagramEditorContextMenu = observer(
125
- forwardRef<
126
- HTMLDivElement,
127
- {
128
- diagramEditorState: DiagramEditorState;
129
- }
130
- >(function DiagramEditorContextMenu(props, ref) {
131
- const { diagramEditorState } = props;
132
- const editorStore = useEditorStore();
133
-
134
- // actions
135
- const buildQuery = editorStore.applicationStore.guardUnhandledError(
136
- async () => {
137
- const classView = guaranteeNonNullable(
138
- diagramEditorState.contextMenuClassView,
139
- );
140
- await queryClass(classView.class.value, editorStore);
141
- },
142
- );
143
-
144
- return (
145
- <MenuContent>
146
- <MenuContentItem onClick={buildQuery}>Query...</MenuContentItem>
147
- </MenuContent>
148
- );
149
- }),
150
- );
151
-
152
- const DiagramRendererHotkeyInfosModal = observer(
153
- (props: { open: boolean; onClose: () => void }) => {
154
- const { open, onClose } = props;
155
- const applicationStore = useApplicationStore();
156
- return (
157
- <Dialog
158
- open={open}
159
- onClose={onClose}
160
- classes={{
161
- root: 'editor-modal__root-container',
162
- container: 'editor-modal__container',
163
- paper: 'editor-modal__content--scrollable',
164
- }}
165
- >
166
- <Modal
167
- darkMode={
168
- !applicationStore.layoutService.TEMPORARY__isLightColorThemeEnabled
169
- }
170
- className="modal--scrollable diagram-editor__hotkeys__dialog"
171
- >
172
- <ModalHeader title="Diagram Hotkeys" />
173
- <ModalBody>
174
- <div className="diagram-editor__hotkey__groups">
175
- <div className="diagram-editor__hotkey__group">
176
- <div className="diagram-editor__hotkey__annotation">
177
- Use view tool
178
- </div>
179
- <div className="hotkey__combination diagram-editor__hotkey__keys">
180
- <div className="hotkey__key">V</div>
181
- </div>
182
- </div>
183
- <div className="diagram-editor__hotkey__group">
184
- <div className="diagram-editor__hotkey__annotation">
185
- Use pan tool
186
- </div>
187
- <div className="hotkey__combination diagram-editor__hotkey__keys">
188
- <div className="hotkey__key">M</div>
189
- </div>
190
- </div>
191
- <div className="diagram-editor__hotkey__group">
192
- <div className="diagram-editor__hotkey__annotation">Zoom</div>
193
- <div className="hotkey__combination diagram-editor__hotkey__keys">
194
- <div className="hotkey__key">Z</div>
195
- </div>
196
- </div>
197
- <div className="diagram-editor__hotkey__group">
198
- <div className="diagram-editor__hotkey__annotation">
199
- Recenter
200
- </div>
201
- <div className="hotkey__combination diagram-editor__hotkey__keys">
202
- <div className="hotkey__key">R</div>
203
- </div>
204
- </div>
205
-
206
- <div className="diagram-editor__hotkey__groups__divider" />
207
- <div className="diagram-editor__hotkey__group">
208
- <div className="diagram-editor__hotkey__annotation">
209
- Remove selected element(s)
210
- </div>
211
- <div className="hotkey__combination diagram-editor__hotkey__keys">
212
- <div className="hotkey__key">Remove</div>
213
- </div>
214
- </div>
215
- <div className="diagram-editor__hotkey__group">
216
- <div className="diagram-editor__hotkey__annotation">
217
- Edit the selected element
218
- </div>
219
- <div className="hotkey__combination diagram-editor__hotkey__keys">
220
- <div className="hotkey__key">E</div>
221
- </div>
222
- </div>
223
-
224
- <div className="diagram-editor__hotkey__groups__divider" />
225
- <div className="diagram-editor__hotkey__group">
226
- <div className="diagram-editor__hotkey__annotation">
227
- Use property tool
228
- </div>
229
- <div className="hotkey__combination diagram-editor__hotkey__keys">
230
- <div className="hotkey__key">P</div>
231
- </div>
232
- </div>
233
- <div className="diagram-editor__hotkey__group">
234
- <div className="diagram-editor__hotkey__annotation">
235
- Use inheritance tool
236
- </div>
237
- <div className="hotkey__combination diagram-editor__hotkey__keys">
238
- <div className="hotkey__key">I</div>
239
- </div>
240
- </div>
241
- <div className="diagram-editor__hotkey__group">
242
- <div className="diagram-editor__hotkey__annotation">
243
- Add class
244
- </div>
245
- <div className="hotkey__combination diagram-editor__hotkey__keys">
246
- <div className="hotkey__key">C</div>
247
- </div>
248
- </div>
249
- <div className="diagram-editor__hotkey__group">
250
- <div className="diagram-editor__hotkey__annotation">
251
- Add simple property to selected class
252
- </div>
253
- <div className="hotkey__combination diagram-editor__hotkey__keys">
254
- <div className="hotkey__key">Alt</div>
255
- <div className="hotkey__plus">
256
- <PlusIcon />
257
- </div>
258
- <div className="hotkey__key">&darr;</div>
259
- </div>
260
- </div>
261
-
262
- <div className="diagram-editor__hotkey__groups__divider" />
263
- <div className="diagram-editor__hotkey__group">
264
- <div className="diagram-editor__hotkey__annotation">
265
- Toggle display for properties of selected classes
266
- </div>
267
- <div className="hotkey__combination diagram-editor__hotkey__keys">
268
- <div className="hotkey__key">Alt</div>
269
- <div className="hotkey__plus">
270
- <PlusIcon />
271
- </div>
272
- <div className="hotkey__key">P</div>
273
- </div>
274
- </div>
275
- <div className="diagram-editor__hotkey__group">
276
- <div className="diagram-editor__hotkey__annotation">
277
- Toggle display for tagged values of selected classes
278
- </div>
279
- <div className="hotkey__combination diagram-editor__hotkey__keys">
280
- <div className="hotkey__key">Alt</div>
281
- <div className="hotkey__plus">
282
- <PlusIcon />
283
- </div>
284
- <div className="hotkey__key">T</div>
285
- </div>
286
- </div>
287
- <div className="diagram-editor__hotkey__group">
288
- <div className="diagram-editor__hotkey__annotation">
289
- Toggle display for stereotypes of selected classes
290
- </div>
291
- <div className="hotkey__combination diagram-editor__hotkey__keys">
292
- <div className="hotkey__key">Alt</div>
293
- <div className="hotkey__plus">
294
- <PlusIcon />
295
- </div>
296
- <div className="hotkey__key">S</div>
297
- </div>
298
- </div>
299
-
300
- <div className="diagram-editor__hotkey__groups__divider" />
301
- <div className="diagram-editor__hotkey__group">
302
- <div className="diagram-editor__hotkey__annotation">
303
- Eject the property
304
- </div>
305
- <div className="hotkey__combination diagram-editor__hotkey__keys">
306
- <div className="hotkey__key">&rarr;</div>
307
- </div>
308
- </div>
309
- <div className="diagram-editor__hotkey__group">
310
- <div className="diagram-editor__hotkey__annotation">
311
- Add subtypes of the selected classes to the diagram
312
- </div>
313
- <div className="hotkey__combination diagram-editor__hotkey__keys">
314
- <div className="hotkey__key">&darr;</div>
315
- </div>
316
- </div>
317
- <div className="diagram-editor__hotkey__group">
318
- <div className="diagram-editor__hotkey__annotation">
319
- Add supertypes of the selected classes to the diagram
320
- </div>
321
- <div className="hotkey__combination diagram-editor__hotkey__keys">
322
- <div className="hotkey__key">&uarr;</div>
323
- </div>
324
- </div>
325
- </div>
326
- </ModalBody>
327
- </Modal>
328
- </Dialog>
329
- );
330
- },
331
- );
332
-
333
- const DiagramEditorToolPanel = observer(
334
- (props: { diagramEditorState: DiagramEditorState }) => {
335
- const { diagramEditorState } = props;
336
- const renderer = diagramEditorState.renderer;
337
- const isReadOnly = diagramEditorState.isReadOnly;
338
- const showDiagramRendererHokeysModal = (): void =>
339
- diagramEditorState.setShowHotkeyInfosModal(true);
340
- const hideDiagramRendererHokeysModal = (): void =>
341
- diagramEditorState.setShowHotkeyInfosModal(false);
342
- const createModeSwitcher =
343
- (
344
- editMode: DIAGRAM_INTERACTION_MODE,
345
- relationshipMode: DIAGRAM_RELATIONSHIP_EDIT_MODE,
346
- ): (() => void) =>
347
- (): void => {
348
- if (!isReadOnly) {
349
- renderer.changeMode(editMode, relationshipMode);
350
- }
351
- };
352
-
353
- return (
354
- <div className="diagram-editor__tools">
355
- <button
356
- className={clsx('diagram-editor__tool', {
357
- 'diagram-editor__tool--active':
358
- renderer.interactionMode === DIAGRAM_INTERACTION_MODE.LAYOUT,
359
- })}
360
- tabIndex={-1}
361
- onClick={createModeSwitcher(
362
- DIAGRAM_INTERACTION_MODE.LAYOUT,
363
- DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE,
364
- )}
365
- title="View Tool (V)"
366
- >
367
- <MousePointerIcon className="diagram-editor__icon--layout" />
368
- </button>
369
- <button
370
- className={clsx('diagram-editor__tool', {
371
- 'diagram-editor__tool--active':
372
- renderer.interactionMode === DIAGRAM_INTERACTION_MODE.PAN,
373
- })}
374
- tabIndex={-1}
375
- onClick={createModeSwitcher(
376
- DIAGRAM_INTERACTION_MODE.PAN,
377
- DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE,
378
- )}
379
- title="Pan Tool (M)"
380
- >
381
- <MoveIcon className="diagram-editor__icon--pan" />
382
- </button>
383
- <button
384
- className={clsx('diagram-editor__tool', {
385
- 'diagram-editor__tool--active':
386
- renderer.interactionMode === DIAGRAM_INTERACTION_MODE.ZOOM_IN,
387
- })}
388
- tabIndex={-1}
389
- title="Zoom In (Z)"
390
- onClick={createModeSwitcher(
391
- DIAGRAM_INTERACTION_MODE.ZOOM_IN,
392
- DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE,
393
- )}
394
- >
395
- <ZoomInIcon className="diagram-editor__icon--zoom-in" />
396
- </button>
397
- <button
398
- className={clsx('diagram-editor__tool', {
399
- 'diagram-editor__tool--active':
400
- renderer.interactionMode === DIAGRAM_INTERACTION_MODE.ZOOM_OUT,
401
- })}
402
- tabIndex={-1}
403
- title="Zoom Out (Z)"
404
- onClick={createModeSwitcher(
405
- DIAGRAM_INTERACTION_MODE.ZOOM_OUT,
406
- DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE,
407
- )}
408
- >
409
- <ZoomOutIcon className="diagram-editor__icon--zoom-out" />
410
- </button>
411
- <div className="diagram-editor__tools__divider" />
412
- <button
413
- className={clsx('diagram-editor__tool', {
414
- 'diagram-editor__tool--active':
415
- renderer.interactionMode ===
416
- DIAGRAM_INTERACTION_MODE.ADD_RELATIONSHIP &&
417
- renderer.relationshipMode ===
418
- DIAGRAM_RELATIONSHIP_EDIT_MODE.PROPERTY,
419
- })}
420
- tabIndex={-1}
421
- title="Property Tool (P)"
422
- disabled={isReadOnly}
423
- onClick={createModeSwitcher(
424
- DIAGRAM_INTERACTION_MODE.ADD_RELATIONSHIP,
425
- DIAGRAM_RELATIONSHIP_EDIT_MODE.PROPERTY,
426
- )}
427
- >
428
- <MinusIcon className="diagram-editor__icon--property" />
429
- </button>
430
- <button
431
- className={clsx('diagram-editor__tool', {
432
- 'diagram-editor__tool--active':
433
- renderer.interactionMode ===
434
- DIAGRAM_INTERACTION_MODE.ADD_RELATIONSHIP &&
435
- renderer.relationshipMode ===
436
- DIAGRAM_RELATIONSHIP_EDIT_MODE.INHERITANCE,
437
- })}
438
- tabIndex={-1}
439
- title="Inheritance Tool (I)"
440
- disabled={isReadOnly}
441
- onClick={createModeSwitcher(
442
- DIAGRAM_INTERACTION_MODE.ADD_RELATIONSHIP,
443
- DIAGRAM_RELATIONSHIP_EDIT_MODE.INHERITANCE,
444
- )}
445
- >
446
- <TriangleIcon className="diagram-editor__icon--inheritance" />
447
- </button>
448
- <button
449
- className={clsx('diagram-editor__tool', {
450
- // 'diagram-editor__tool--active':
451
- // diagramRenderer.editMode === DIAGRAM_EDIT_MODE.RELATIONSHIP &&
452
- // diagramRenderer.relationshipMode ===
453
- // DIAGRAM_RELATIONSHIP_EDIT_MODE.ASSOCIATION,
454
- })}
455
- tabIndex={-1}
456
- title="Association Tool"
457
- disabled={true}
458
- // onClick={changeMode(
459
- // DIAGRAM_INTERACTION_MODE.ADD_RELATIONSHIP,
460
- // DIAGRAM_RELATIONSHIP_EDIT_MODE.ASSOCIATION,
461
- // )}
462
- >
463
- <ResizeIcon className="diagram-editor__icon--association" />
464
- </button>
465
- <button
466
- className={clsx('diagram-editor__tool', {
467
- 'diagram-editor__tool--active':
468
- renderer.interactionMode === DIAGRAM_INTERACTION_MODE.ADD_CLASS,
469
- })}
470
- tabIndex={-1}
471
- title="Add class tool (C)"
472
- disabled={isReadOnly}
473
- onClick={createModeSwitcher(
474
- DIAGRAM_INTERACTION_MODE.ADD_CLASS,
475
- DIAGRAM_RELATIONSHIP_EDIT_MODE.NONE,
476
- )}
477
- >
478
- <PlusCircleIcon className="diagram-editor__icon--add-class" />
479
- </button>
480
- <div className="diagram-editor__tools__divider" />
481
- <button
482
- className="diagram-editor__tool"
483
- tabIndex={-1}
484
- title="Show Hotkeys"
485
- onClick={showDiagramRendererHokeysModal}
486
- >
487
- <KeyboardIcon className="diagram-editor__icon--hotkey-info" />
488
- </button>
489
- <DiagramRendererHotkeyInfosModal
490
- open={diagramEditorState.showHotkeyInfosModal}
491
- onClose={hideDiagramRendererHokeysModal}
492
- />
493
- </div>
494
- );
495
- },
496
- );
497
-
498
- const DiagramEditorClassViewEditor = observer(
499
- (props: {
500
- classViewEditorState: DiagramEditorClassViewEditorSidePanelState;
501
- }) => {
502
- const { classViewEditorState } = props;
503
- const editorStore = useEditorStore();
504
- const classView = classViewEditorState.classView;
505
- const diagramEditorState = classViewEditorState.diagramEditorState;
506
- const isReadOnly = diagramEditorState.isReadOnly;
507
-
508
- // Tabs
509
- const selectedTab = classViewEditorState.selectedTab;
510
- const tabs = [
511
- DIAGRAM_EDITOR_SIDE_PANEL_TAB.ELEMENT,
512
- DIAGRAM_EDITOR_SIDE_PANEL_TAB.VIEW,
513
- ];
514
- const changeTab =
515
- (tab: DIAGRAM_EDITOR_SIDE_PANEL_TAB): (() => void) =>
516
- (): void => {
517
- classViewEditorState.setSelectedTab(tab);
518
- };
519
-
520
- const redrawOnClassChange = useCallback((): void => {
521
- cleanUpDeadReferencesInDiagram(
522
- diagramEditorState.diagram,
523
- editorStore.graphManagerState.graph,
524
- );
525
- diagramEditorState.renderer.render();
526
- }, [diagramEditorState, editorStore]);
527
-
528
- const toggleHideProperties = (): void => {
529
- if (isReadOnly) {
530
- return;
531
- }
532
- classView_setHideProperties(classView, !classView.hideProperties);
533
- diagramEditorState.renderer.render();
534
- };
535
- const toggleHideTaggedValues = (): void => {
536
- if (isReadOnly) {
537
- return;
538
- }
539
- classView_setHideTaggedValues(classView, !classView.hideTaggedValues);
540
- diagramEditorState.renderer.render();
541
- };
542
- const toggleHideStereotypes = (): void => {
543
- if (isReadOnly) {
544
- return;
545
- }
546
- classView_setHideStereotypes(classView, !classView.hideStereotypes);
547
- diagramEditorState.renderer.render();
548
- };
549
-
550
- return (
551
- <div className="diagram-editor__class-view-editor">
552
- <div className="diagram-editor__class-view-editor__header">
553
- <div className="diagram-editor__class-view-editor__header__tabs">
554
- {tabs.map((tab) => (
555
- <div
556
- key={tab}
557
- onClick={changeTab(tab)}
558
- className={clsx(
559
- 'diagram-editor__class-view-editor__header__tab',
560
- {
561
- 'diagram-editor__class-view-editor__header__tab--active':
562
- tab === selectedTab,
563
- },
564
- )}
565
- >
566
- {prettyCONSTName(tab)}
567
- </div>
568
- ))}
569
- </div>
570
- </div>
571
- <div className="diagram-editor__class-view-editor__content">
572
- {DIAGRAM_EDITOR_SIDE_PANEL_TAB.ELEMENT === selectedTab && (
573
- <ClassFormEditor
574
- _class={classViewEditorState.classEditorState.class}
575
- editorState={classViewEditorState.classEditorState}
576
- onHashChange={redrawOnClassChange}
577
- />
578
- )}
579
- {DIAGRAM_EDITOR_SIDE_PANEL_TAB.VIEW === selectedTab && (
580
- <div className="panel__content__form diagram-editor__class-view-editor__content__form">
581
- <div className="panel__content__form__section">
582
- {/* Hide properties */}
583
- <div
584
- className={clsx('panel__content__form__section__toggler')}
585
- onClick={toggleHideProperties}
586
- >
587
- <button
588
- className={clsx(
589
- 'panel__content__form__section__toggler__btn',
590
- {
591
- 'panel__content__form__section__toggler__btn--toggled':
592
- classView.hideProperties,
593
- },
594
- )}
595
- disabled={isReadOnly}
596
- >
597
- {classView.hideProperties ? (
598
- <CheckSquareIcon />
599
- ) : (
600
- <SquareIcon />
601
- )}
602
- </button>
603
- <div className="panel__content__form__section__toggler__prompt">
604
- Specifies if properties should be hidden
605
- </div>
606
- </div>
607
- {/* Hide tagged-values */}
608
- <div
609
- className={clsx('panel__content__form__section__toggler')}
610
- onClick={toggleHideTaggedValues}
611
- >
612
- <button
613
- className={clsx(
614
- 'panel__content__form__section__toggler__btn',
615
- {
616
- 'panel__content__form__section__toggler__btn--toggled':
617
- classView.hideTaggedValues,
618
- },
619
- )}
620
- disabled={isReadOnly}
621
- >
622
- {classView.hideTaggedValues ? (
623
- <CheckSquareIcon />
624
- ) : (
625
- <SquareIcon />
626
- )}
627
- </button>
628
- <div className="panel__content__form__section__toggler__prompt">
629
- Specifies if tagged values should be hidden
630
- </div>
631
- </div>
632
- {/* Hide stereotypes */}
633
- <div
634
- className={clsx('panel__content__form__section__toggler')}
635
- onClick={toggleHideStereotypes}
636
- >
637
- <button
638
- className={clsx(
639
- 'panel__content__form__section__toggler__btn',
640
- {
641
- 'panel__content__form__section__toggler__btn--toggled':
642
- classView.hideStereotypes,
643
- },
644
- )}
645
- disabled={isReadOnly}
646
- >
647
- {classView.hideStereotypes ? (
648
- <CheckSquareIcon />
649
- ) : (
650
- <SquareIcon />
651
- )}
652
- </button>
653
- <div className="panel__content__form__section__toggler__prompt">
654
- Specifies if stereotypes should be hidden
655
- </div>
656
- </div>
657
- </div>
658
- </div>
659
- )}
660
- </div>
661
- </div>
662
- );
663
- },
664
- );
665
-
666
- const DiagramEditorOverlay = observer(
667
- (props: { diagramEditorState: DiagramEditorState }) => {
668
- const { diagramEditorState } = props;
669
- const sidePanelState = diagramEditorState.sidePanelState;
670
-
671
- const resizeSidePanel = (handleProps: ResizablePanelHandlerProps): void =>
672
- diagramEditorState.sidePanelDisplayState.setSize(
673
- (handleProps.domElement as HTMLDivElement).getBoundingClientRect()
674
- .width,
675
- );
676
-
677
- // layout
678
- const sidePanelCollapsiblePanelGroupProps = getCollapsiblePanelGroupProps(
679
- diagramEditorState.sidePanelDisplayState.size === 0,
680
- {
681
- classes: ['diagram-editor__overlay__panel'],
682
- onStopResize: resizeSidePanel,
683
- size: diagramEditorState.sidePanelDisplayState.size,
684
- },
685
- );
686
-
687
- return (
688
- <ResizablePanelGroup
689
- className="diagram-editor__overlay"
690
- orientation="vertical"
691
- >
692
- <ResizablePanel
693
- {...sidePanelCollapsiblePanelGroupProps.remainingPanel}
694
- minSize={300}
695
- >
696
- <div className="diagram-editor__view-finder" />
697
- </ResizablePanel>
698
- <ResizablePanelSplitter className="diagram-editor__overlay__panel-resizer" />
699
- <ResizablePanel
700
- {...sidePanelCollapsiblePanelGroupProps.collapsiblePanel}
701
- direction={-1}
702
- >
703
- <div className="panel diagram-editor__side-panel">
704
- {sidePanelState instanceof
705
- DiagramEditorClassViewEditorSidePanelState && (
706
- <DiagramEditorClassViewEditor
707
- classViewEditorState={sidePanelState}
708
- />
709
- )}
710
- {!sidePanelState && (
711
- <BlankPanelContent>No element selected</BlankPanelContent>
712
- )}
713
- </div>
714
- </ResizablePanel>
715
- </ResizablePanelGroup>
716
- );
717
- },
718
- );
719
-
720
- const DiagramEditorInlineClassRenamerContent = observer(
721
- (props: {
722
- inlineClassRenamerState: DiagramEditorInlineClassRenamerState;
723
- }) => {
724
- const { inlineClassRenamerState } = props;
725
- const editorStore = useEditorStore();
726
- const applicationStore = useApplicationStore();
727
- const diagramEditorState = inlineClassRenamerState.diagramEditorState;
728
- const _class = inlineClassRenamerState.classView.class.value;
729
- const isReadOnly = diagramEditorState.isReadOnly;
730
- const [name, setName] = useState(_class.name);
731
- const [packagePath] = resolvePackagePathAndElementName(_class.path);
732
- const newClassPath = createPath(packagePath, name);
733
- const isClassNameNonEmpty = name !== '';
734
- const isClassNameValid = isValidPathIdentifier(name);
735
- const existingElement =
736
- editorStore.graphManagerState.graph.getNullableElement(newClassPath);
737
- const isClassNameUnique = !existingElement || existingElement === _class;
738
- // const class
739
- const classCreationValidationErrorMessage = !isClassNameNonEmpty
740
- ? `Class name cannot be empty`
741
- : !isClassNameValid
742
- ? `Class name is not valid`
743
- : !isClassNameUnique
744
- ? `Element of the same name already existed`
745
- : undefined;
746
- const canRenameClass =
747
- isClassNameNonEmpty && isClassNameValid && isClassNameUnique;
748
-
749
- const close = (event: React.MouseEvent<HTMLButtonElement>): void => {
750
- event.preventDefault();
751
- if (canRenameClass) {
752
- diagramEditorState.setInlineClassRenamerState(undefined);
753
- flowResult(
754
- editorStore.graphEditorMode.renameElement(_class, newClassPath),
755
- ).catch(applicationStore.alertUnhandledError);
756
- }
757
- };
758
- const pathInputRef = useRef<HTMLInputElement>(null);
759
-
760
- const changePath: React.ChangeEventHandler<HTMLInputElement> = (event) =>
761
- setName(event.target.value);
762
-
763
- useEffect(() => {
764
- pathInputRef.current?.focus();
765
- }, [inlineClassRenamerState]);
766
-
767
- return (
768
- <form className="diagram-editor__inline-class-creator">
769
- <div className="input-group">
770
- <input
771
- className="diagram-editor__inline-class-creator__path input-group__input input--dark"
772
- ref={pathInputRef}
773
- disabled={isReadOnly}
774
- value={name}
775
- placeholder="Enter class name"
776
- onChange={changePath}
777
- />
778
- {classCreationValidationErrorMessage && (
779
- <div className="input-group__error-message">
780
- {classCreationValidationErrorMessage}
781
- </div>
782
- )}
783
- </div>
784
- <button
785
- type="submit"
786
- className="diagram-editor__inline-class-creator__close-btn"
787
- onClick={close}
788
- />
789
- </form>
790
- );
791
- },
792
- );
793
-
794
- const DiagramEditorInlineClassRenamer = observer(
795
- (props: { diagramEditorState: DiagramEditorState }) => {
796
- const { diagramEditorState } = props;
797
- const closeEditor = (): void => {
798
- diagramEditorState.setInlineClassRenamerState(undefined);
799
- };
800
- const inlineClassRenamerState = diagramEditorState.inlineClassRenamerState;
801
- const anchorPositionPoint = inlineClassRenamerState
802
- ? diagramEditorState.renderer.canvasCoordinateToEventCoordinate(
803
- diagramEditorState.renderer.modelCoordinateToCanvasCoordinate(
804
- inlineClassRenamerState.point,
805
- ),
806
- )
807
- : new Point(0, 0);
808
-
809
- return (
810
- <BasePopover
811
- onClose={closeEditor}
812
- anchorPosition={{
813
- left: anchorPositionPoint.x,
814
- top: anchorPositionPoint.y,
815
- }}
816
- anchorReference="anchorPosition"
817
- open={Boolean(inlineClassRenamerState)}
818
- BackdropProps={{
819
- invisible: true,
820
- }}
821
- elevation={0}
822
- marginThreshold={0}
823
- disableRestoreFocus={true}
824
- >
825
- <div className="diagram-editor__inline-class-creator__container">
826
- {inlineClassRenamerState && (
827
- <DiagramEditorInlineClassRenamerContent
828
- inlineClassRenamerState={inlineClassRenamerState}
829
- />
830
- )}
831
- </div>
832
- </BasePopover>
833
- );
834
- },
835
- );
836
-
837
- const DiagramEditorInlineClassCreatorContent = observer(
838
- (props: {
839
- inlineClassCreatorState: DiagramEditorInlineClassCreatorState;
840
- }) => {
841
- const { inlineClassCreatorState } = props;
842
- const editorStore = useEditorStore();
843
- const applicationStore = useApplicationStore();
844
- const diagramEditorState = inlineClassCreatorState.diagramEditorState;
845
- const isReadOnly = diagramEditorState.isReadOnly;
846
- const [path, setPath] = useState(
847
- `${
848
- diagramEditorState.diagram.package
849
- ? `${diagramEditorState.diagram.package.path}${ELEMENT_PATH_DELIMITER}`
850
- : ''
851
- }Class_${editorStore.graphManagerState.graph.ownClasses.length + 1}`,
852
- );
853
- const isClassPathNonEmpty = path !== '';
854
- const isNotTopLevelClass = path.includes(ELEMENT_PATH_DELIMITER);
855
- const isValidPath = isValidFullPath(path);
856
- const isClassUnique =
857
- !editorStore.graphManagerState.graph.getNullableElement(path);
858
- const classCreationValidationErrorMessage = !isClassPathNonEmpty
859
- ? `Class path cannot be empty`
860
- : !isNotTopLevelClass
861
- ? `Creating top level class is not allowed`
862
- : !isValidPath
863
- ? `Class path is not valid`
864
- : !isClassUnique
865
- ? `Class already existed`
866
- : undefined;
867
- const canCreateClass =
868
- isClassPathNonEmpty && isNotTopLevelClass && isValidPath && isClassUnique;
869
-
870
- const createClass = async (
871
- event: React.MouseEvent<HTMLButtonElement>,
872
- ): Promise<void> => {
873
- event.preventDefault();
874
- if (canCreateClass) {
875
- diagramEditorState.setInlineClassCreatorState(undefined);
876
- const [packagePath, name] = resolvePackagePathAndElementName(path);
877
- const _class = new Class(name);
878
- await flowResult(
879
- editorStore.graphEditorMode.addElement(_class, packagePath, false),
880
- );
881
- diagramEditorState.renderer.addClassView(
882
- _class,
883
- inlineClassCreatorState.point,
884
- );
885
- }
886
- };
887
- const close = (event: React.MouseEvent<HTMLButtonElement>): void => {
888
- createClass(event).catch(applicationStore.alertUnhandledError);
889
- };
890
- const pathInputRef = useRef<HTMLInputElement>(null);
891
-
892
- const changePath: React.ChangeEventHandler<HTMLInputElement> = (event) =>
893
- setPath(event.target.value);
894
-
895
- useEffect(() => {
896
- pathInputRef.current?.focus();
897
- }, [inlineClassCreatorState]);
898
-
899
- return (
900
- <form className="diagram-editor__inline-class-creator">
901
- <div className="input-group">
902
- <input
903
- className="diagram-editor__inline-class-creator__path input-group__input input--dark"
904
- ref={pathInputRef}
905
- disabled={isReadOnly}
906
- value={path}
907
- placeholder="Enter class path"
908
- onChange={changePath}
909
- />
910
- {classCreationValidationErrorMessage && (
911
- <div className="input-group__error-message">
912
- {classCreationValidationErrorMessage}
913
- </div>
914
- )}
915
- </div>
916
- <button
917
- type="submit"
918
- className="diagram-editor__inline-class-creator__close-btn"
919
- onClick={close}
920
- />
921
- </form>
922
- );
923
- },
924
- );
925
-
926
- const DiagramEditorInlineClassCreator = observer(
927
- (props: { diagramEditorState: DiagramEditorState }) => {
928
- const { diagramEditorState } = props;
929
- const closeEditor = (): void => {
930
- diagramEditorState.setInlineClassCreatorState(undefined);
931
- };
932
- const inlineClassCreatorState = diagramEditorState.inlineClassCreatorState;
933
- const anchorPositionPoint = inlineClassCreatorState
934
- ? diagramEditorState.renderer.canvasCoordinateToEventCoordinate(
935
- diagramEditorState.renderer.modelCoordinateToCanvasCoordinate(
936
- inlineClassCreatorState.point,
937
- ),
938
- )
939
- : new Point(0, 0);
940
-
941
- return (
942
- <BasePopover
943
- onClose={closeEditor}
944
- anchorPosition={{
945
- left: anchorPositionPoint.x,
946
- top: anchorPositionPoint.y,
947
- }}
948
- anchorReference="anchorPosition"
949
- open={Boolean(inlineClassCreatorState)}
950
- BackdropProps={{
951
- invisible: true,
952
- }}
953
- elevation={0}
954
- marginThreshold={0}
955
- disableRestoreFocus={true}
956
- >
957
- <div className="diagram-editor__inline-class-creator__container">
958
- {inlineClassCreatorState && (
959
- <DiagramEditorInlineClassCreatorContent
960
- inlineClassCreatorState={inlineClassCreatorState}
961
- />
962
- )}
963
- </div>
964
- </BasePopover>
965
- );
966
- },
967
- );
968
-
969
- const DiagramEditorInlinePropertyMultiplicityEditor = observer(
970
- (props: {
971
- value: Multiplicity;
972
- updateValue: (val: Multiplicity) => void;
973
- isReadOnly: boolean;
974
- }) => {
975
- const { value, updateValue, isReadOnly } = props;
976
- const [lowerBound, setLowerBound] = useState<string | number>(
977
- value.lowerBound,
978
- );
979
- const [upperBound, setUpperBound] = useState<string | number>(
980
- value.upperBound ?? MULTIPLICITY_INFINITE,
981
- );
982
- const editorStore = useEditorStore();
983
- const updateMultiplicity = (
984
- lower: number | string,
985
- upper: number | string,
986
- ): void => {
987
- const lBound = typeof lower === 'number' ? lower : parseInt(lower, 10);
988
- const uBound =
989
- upper === MULTIPLICITY_INFINITE
990
- ? undefined
991
- : typeof upper === 'number'
992
- ? upper
993
- : parseInt(upper, 10);
994
- if (!isNaN(lBound) && (uBound === undefined || !isNaN(uBound))) {
995
- updateValue(
996
- editorStore.graphManagerState.graph.getMultiplicity(lBound, uBound),
997
- );
998
- }
999
- };
1000
- const changeLowerBound: React.ChangeEventHandler<HTMLInputElement> = (
1001
- event,
1002
- ) => {
1003
- setLowerBound(event.target.value);
1004
- updateMultiplicity(event.target.value, upperBound);
1005
- };
1006
- const changeUpperBound: React.ChangeEventHandler<HTMLInputElement> = (
1007
- event,
1008
- ) => {
1009
- setUpperBound(event.target.value);
1010
- updateMultiplicity(lowerBound, event.target.value);
1011
- };
1012
-
1013
- return (
1014
- <div className="diagram-editor__inline-property-editor__multiplicity-editor">
1015
- <input
1016
- className="diagram-editor__inline-property-editor__multiplicity-editor__bound input--dark"
1017
- disabled={isReadOnly}
1018
- spellCheck={false}
1019
- value={lowerBound}
1020
- onChange={changeLowerBound}
1021
- />
1022
- <div className="diagram-editor__inline-property-editor__multiplicity-editor__range">
1023
- ..
1024
- </div>
1025
- <input
1026
- className="diagram-editor__inline-property-editor__multiplicity-editor__bound input--dark"
1027
- disabled={isReadOnly}
1028
- spellCheck={false}
1029
- value={upperBound}
1030
- onChange={changeUpperBound}
1031
- />
1032
- </div>
1033
- );
1034
- },
1035
- );
1036
-
1037
- const DiagramEditorInlinePropertyEditorContent = observer(
1038
- (props: {
1039
- inlinePropertyEditorState: DiagramEditorInlinePropertyEditorState;
1040
- }) => {
1041
- const { inlinePropertyEditorState } = props;
1042
- const editorStore = useEditorStore();
1043
- const applicationStore = editorStore.applicationStore;
1044
- const diagramEditorState = inlinePropertyEditorState.diagramEditorState;
1045
- const isReadOnly = diagramEditorState.isReadOnly;
1046
- const propertyNameInputRef = useRef<HTMLInputElement>(null);
1047
- const property = inlinePropertyEditorState.property.value;
1048
- const close = (event: React.MouseEvent<HTMLButtonElement>): void => {
1049
- event.preventDefault();
1050
- diagramEditorState.setInlinePropertyEditorState(undefined);
1051
- };
1052
-
1053
- const changePropertyName: React.ChangeEventHandler<HTMLInputElement> = (
1054
- event,
1055
- ) => {
1056
- if (property instanceof DerivedProperty || property instanceof Property) {
1057
- property_setName(property, event.target.value);
1058
- diagramEditorState.renderer.render();
1059
- }
1060
- };
1061
-
1062
- const changeMultiplicity = (val: Multiplicity): void => {
1063
- if (property instanceof DerivedProperty || property instanceof Property) {
1064
- property_setMultiplicity(property, val);
1065
- diagramEditorState.renderer.render();
1066
- }
1067
- };
1068
-
1069
- // Type
1070
- const currentPropertyType = property.genericType.value.rawType;
1071
- const propertyTypeOptions =
1072
- editorStore.graphManagerState.usableClassPropertyTypes.map(
1073
- buildElementOption,
1074
- );
1075
- const propertyTypeFilterOption = createFilter({
1076
- ignoreCase: true,
1077
- ignoreAccents: false,
1078
- stringify: (option: { data: PackageableElementOption<Type> }): string =>
1079
- option.data.value.path,
1080
- });
1081
- const selectedPropertyType = {
1082
- value: currentPropertyType,
1083
- label: currentPropertyType.name,
1084
- };
1085
- const changePropertyType = (val: PackageableElementOption<Type>): void => {
1086
- if (property instanceof Property || property instanceof DerivedProperty) {
1087
- property_setGenericType(property, new GenericType(val.value));
1088
- }
1089
- };
1090
-
1091
- useEffect(() => {
1092
- propertyNameInputRef.current?.focus();
1093
- }, [inlinePropertyEditorState]);
1094
-
1095
- return (
1096
- <form
1097
- className={clsx('diagram-editor__inline-property-editor', {
1098
- 'diagram-editor__inline-property-editor--with-type':
1099
- !inlinePropertyEditorState.isEditingPropertyView,
1100
- })}
1101
- >
1102
- <input
1103
- className="diagram-editor__inline-property-editor__name input--dark"
1104
- ref={propertyNameInputRef}
1105
- disabled={isReadOnly}
1106
- value={property.name}
1107
- onChange={changePropertyName}
1108
- />
1109
- {!inlinePropertyEditorState.isEditingPropertyView && (
1110
- <CustomSelectorInput
1111
- className="diagram-editor__inline-property-editor__type"
1112
- disabled={isReadOnly}
1113
- options={propertyTypeOptions}
1114
- onChange={changePropertyType}
1115
- value={selectedPropertyType}
1116
- placeholder="Choose a type..."
1117
- darkMode={
1118
- !applicationStore.layoutService
1119
- .TEMPORARY__isLightColorThemeEnabled
1120
- }
1121
- filterOption={propertyTypeFilterOption}
1122
- />
1123
- )}
1124
- <DiagramEditorInlinePropertyMultiplicityEditor
1125
- isReadOnly={isReadOnly}
1126
- value={property.multiplicity}
1127
- updateValue={changeMultiplicity}
1128
- />
1129
- <button
1130
- type="submit"
1131
- className="diagram-editor__inline-property-editor__close-btn"
1132
- onClick={close}
1133
- />
1134
- </form>
1135
- );
1136
- },
1137
- );
1138
-
1139
- const DiagramEditorInlinePropertyEditor = observer(
1140
- (props: { diagramEditorState: DiagramEditorState }) => {
1141
- const { diagramEditorState } = props;
1142
- const closeEditor = (): void => {
1143
- diagramEditorState.setInlinePropertyEditorState(undefined);
1144
- };
1145
- const inlinePropertyEditorState =
1146
- diagramEditorState.inlinePropertyEditorState;
1147
- const anchorPositionPoint = inlinePropertyEditorState
1148
- ? diagramEditorState.renderer.canvasCoordinateToEventCoordinate(
1149
- diagramEditorState.renderer.modelCoordinateToCanvasCoordinate(
1150
- inlinePropertyEditorState.point,
1151
- ),
1152
- )
1153
- : new Point(0, 0);
1154
-
1155
- return (
1156
- <BasePopover
1157
- onClose={closeEditor}
1158
- anchorPosition={{
1159
- left: anchorPositionPoint.x,
1160
- top: anchorPositionPoint.y,
1161
- }}
1162
- anchorReference="anchorPosition"
1163
- open={Boolean(inlinePropertyEditorState)}
1164
- BackdropProps={{
1165
- invisible: true,
1166
- }}
1167
- elevation={0}
1168
- marginThreshold={0}
1169
- disableRestoreFocus={true}
1170
- >
1171
- <div className="diagram-editor__inline-property-editor__container">
1172
- {inlinePropertyEditorState && (
1173
- <DiagramEditorInlinePropertyEditorContent
1174
- inlinePropertyEditorState={inlinePropertyEditorState}
1175
- />
1176
- )}
1177
- </div>
1178
- </BasePopover>
1179
- );
1180
- },
1181
- );
1182
-
1183
- const DiagramEditorDiagramCanvas = observer(
1184
- forwardRef<
1185
- HTMLDivElement,
1186
- {
1187
- diagramEditorState: DiagramEditorState;
1188
- }
1189
- >(function DiagramEditorDiagramCanvas(props, _ref) {
1190
- const { diagramEditorState } = props;
1191
- const ref = _ref as React.RefObject<HTMLDivElement>;
1192
- const isReadOnly = diagramEditorState.isReadOnly;
1193
-
1194
- const { width, height } = useResizeDetector<HTMLDivElement>({
1195
- refreshMode: 'debounce',
1196
- refreshRate: 50,
1197
- targetRef: ref,
1198
- });
1199
-
1200
- useEffect(() => {
1201
- const renderer = new DiagramRenderer(
1202
- ref.current,
1203
- diagramEditorState.diagram,
1204
- );
1205
- diagramEditorState.setRenderer(renderer);
1206
- diagramEditorState.setupRenderer();
1207
- renderer.render({ initial: true });
1208
- }, [ref, diagramEditorState]);
1209
-
1210
- useEffect(() => {
1211
- // since after the diagram render is initialized, we start
1212
- // showing the toolbar and the header, which causes the auto-zoom fit
1213
- // to be off, we need to call this method again
1214
- if (diagramEditorState.isDiagramRendererInitialized) {
1215
- diagramEditorState.renderer.render({ initial: true });
1216
- }
1217
- }, [diagramEditorState, diagramEditorState.isDiagramRendererInitialized]);
1218
-
1219
- useEffect(() => {
1220
- if (diagramEditorState.isDiagramRendererInitialized) {
1221
- diagramEditorState.renderer.refresh();
1222
- }
1223
- }, [diagramEditorState, width, height]);
1224
-
1225
- // Drag and Drop
1226
- const handleDrop = useCallback(
1227
- (item: ElementDragSource, monitor: DropTargetMonitor): void => {
1228
- if (!isReadOnly) {
1229
- if (item instanceof ElementDragSource) {
1230
- if (item.data.packageableElement instanceof Class) {
1231
- const dropPosition = monitor.getClientOffset();
1232
- diagramEditorState.renderer.addClassView(
1233
- item.data.packageableElement,
1234
- dropPosition
1235
- ? diagramEditorState.renderer.canvasCoordinateToModelCoordinate(
1236
- diagramEditorState.renderer.eventCoordinateToCanvasCoordinate(
1237
- new Point(dropPosition.x, dropPosition.y),
1238
- ),
1239
- )
1240
- : undefined,
1241
- );
1242
- }
1243
- }
1244
- }
1245
- },
1246
- [diagramEditorState, isReadOnly],
1247
- );
1248
- const [, dropConnector] = useDrop<ElementDragSource>(
1249
- () => ({
1250
- accept: CORE_DND_TYPE.PROJECT_EXPLORER_CLASS,
1251
- drop: (item, monitor): void => handleDrop(item, monitor),
1252
- }),
1253
- [handleDrop],
1254
- );
1255
- dropConnector(ref);
1256
-
1257
- return (
1258
- <div
1259
- ref={ref}
1260
- className={clsx(
1261
- 'diagram-canvas diagram-editor__canvas',
1262
- diagramEditorState.diagramCursorClass,
1263
- )}
1264
- data-testid={DSL_DIAGRAM_TEST_ID.DIAGRAM_EDITOR}
1265
- tabIndex={0}
1266
- />
1267
- );
1268
- }),
1269
- );
1270
-
1271
- const DiagramEditorHeader = observer(
1272
- (props: { diagramEditorState: DiagramEditorState }) => {
1273
- const { diagramEditorState } = props;
1274
- const createCenterZoomer =
1275
- (zoomLevel: number): (() => void) =>
1276
- (): void => {
1277
- diagramEditorState.renderer.zoomCenter(zoomLevel / 100);
1278
- };
1279
- const zoomToFit = (): void => diagramEditorState.renderer.zoomToFit();
1280
-
1281
- const toggleSidePanel = (): void => {
1282
- diagramEditorState.sidePanelDisplayState.toggle();
1283
- if (!diagramEditorState.sidePanelDisplayState.isOpen) {
1284
- diagramEditorState.setSidePanelState(undefined);
1285
- }
1286
- };
1287
- const isAlignerDisabled =
1288
- diagramEditorState.renderer.selectedClasses.length < 2;
1289
-
1290
- return (
1291
- <>
1292
- <div className="diagram-editor__header__group">
1293
- <button
1294
- className="diagram-editor__header__action diagram-editor__header__group__action"
1295
- title="Align left"
1296
- disabled={isAlignerDisabled}
1297
- tabIndex={-1}
1298
- onClick={(): void =>
1299
- diagramEditorState.renderer.align(
1300
- DIAGRAM_ALIGNER_OPERATOR.ALIGN_LEFT,
1301
- )
1302
- }
1303
- >
1304
- <AlignStartIcon className="diagram-editor__icon--aligner" />
1305
- </button>
1306
- <button
1307
- className="diagram-editor__header__action diagram-editor__header__group__action"
1308
- title="Align center"
1309
- disabled={isAlignerDisabled}
1310
- tabIndex={-1}
1311
- onClick={(): void =>
1312
- diagramEditorState.renderer.align(
1313
- DIAGRAM_ALIGNER_OPERATOR.ALIGN_CENTER,
1314
- )
1315
- }
1316
- >
1317
- <AlignCenterIcon className="diagram-editor__icon--aligner" />
1318
- </button>
1319
- <button
1320
- className="diagram-editor__header__action diagram-editor__header__group__action"
1321
- title="Align right"
1322
- disabled={isAlignerDisabled}
1323
- tabIndex={-1}
1324
- onClick={(): void =>
1325
- diagramEditorState.renderer.align(
1326
- DIAGRAM_ALIGNER_OPERATOR.ALIGN_RIGHT,
1327
- )
1328
- }
1329
- >
1330
- <AlignEndIcon className="diagram-editor__icon--aligner" />
1331
- </button>
1332
- </div>
1333
- <div className="diagram-editor__header__group__separator" />
1334
- <div className="diagram-editor__header__group">
1335
- <button
1336
- className="diagram-editor__header__action diagram-editor__header__group__action"
1337
- title="Align top"
1338
- disabled={isAlignerDisabled}
1339
- tabIndex={-1}
1340
- onClick={(): void =>
1341
- diagramEditorState.renderer.align(
1342
- DIAGRAM_ALIGNER_OPERATOR.ALIGN_TOP,
1343
- )
1344
- }
1345
- >
1346
- <AlignTopIcon className="diagram-editor__icon--aligner" />
1347
- </button>
1348
- <button
1349
- className="diagram-editor__header__action diagram-editor__header__group__action"
1350
- title="Align middle"
1351
- disabled={isAlignerDisabled}
1352
- tabIndex={-1}
1353
- onClick={(): void =>
1354
- diagramEditorState.renderer.align(
1355
- DIAGRAM_ALIGNER_OPERATOR.ALIGN_MIDDLE,
1356
- )
1357
- }
1358
- >
1359
- <AlignMiddleIcon className="diagram-editor__icon--aligner" />
1360
- </button>
1361
- <button
1362
- className="diagram-editor__header__action diagram-editor__header__group__action"
1363
- title="Align bottom"
1364
- disabled={isAlignerDisabled}
1365
- tabIndex={-1}
1366
- onClick={(): void =>
1367
- diagramEditorState.renderer.align(
1368
- DIAGRAM_ALIGNER_OPERATOR.ALIGN_BOTTOM,
1369
- )
1370
- }
1371
- >
1372
- <AlignBottomIcon className="diagram-editor__icon--aligner" />
1373
- </button>
1374
- </div>
1375
- <div className="diagram-editor__header__group__separator" />
1376
- <div className="diagram-editor__header__group">
1377
- <button
1378
- className="diagram-editor__header__action diagram-editor__header__group__action"
1379
- title="Space horizontally"
1380
- disabled={isAlignerDisabled}
1381
- tabIndex={-1}
1382
- onClick={(): void =>
1383
- diagramEditorState.renderer.align(
1384
- DIAGRAM_ALIGNER_OPERATOR.SPACE_HORIZONTALLY,
1385
- )
1386
- }
1387
- >
1388
- <DistributeHorizontalIcon className="diagram-editor__icon--aligner" />
1389
- </button>
1390
- <button
1391
- className="diagram-editor__header__action diagram-editor__header__group__action"
1392
- title="Space vertically"
1393
- disabled={isAlignerDisabled}
1394
- tabIndex={-1}
1395
- onClick={(): void =>
1396
- diagramEditorState.renderer.align(
1397
- DIAGRAM_ALIGNER_OPERATOR.SPACE_VERTICALLY,
1398
- )
1399
- }
1400
- >
1401
- <DistributeVerticalIcon className="diagram-editor__icon--aligner" />
1402
- </button>
1403
- </div>
1404
- <ControlledDropdownMenu
1405
- className="diagram-editor__header__dropdown"
1406
- title="Zoom..."
1407
- content={
1408
- <MenuContent>
1409
- <MenuContentItem
1410
- className="diagram-editor__header__zoomer__dropdown__menu__item"
1411
- onClick={zoomToFit}
1412
- >
1413
- Fit
1414
- </MenuContentItem>
1415
- <MenuContentDivider />
1416
- {DIAGRAM_ZOOM_LEVELS.map((zoomLevel) => (
1417
- <MenuContentItem
1418
- key={zoomLevel}
1419
- className="diagram-editor__header__zoomer__dropdown__menu__item"
1420
- onClick={createCenterZoomer(zoomLevel)}
1421
- >
1422
- {zoomLevel}%
1423
- </MenuContentItem>
1424
- ))}
1425
- </MenuContent>
1426
- }
1427
- menuProps={{
1428
- anchorOrigin: { vertical: 'bottom', horizontal: 'right' },
1429
- transformOrigin: { vertical: 'top', horizontal: 'right' },
1430
- elevation: 7,
1431
- }}
1432
- >
1433
- <div className="diagram-editor__header__dropdown__label diagram-editor__header__zoomer__dropdown__label">
1434
- {Math.round(diagramEditorState.renderer.zoom * 100)}%
1435
- </div>
1436
- <div className="diagram-editor__header__dropdown__trigger diagram-editor__header__zoomer__dropdown__trigger">
1437
- <CaretDownIcon />
1438
- </div>
1439
- </ControlledDropdownMenu>
1440
- <div className="diagram-editor__header__actions">
1441
- <button
1442
- className={clsx('diagram-editor__header__action', {
1443
- 'diagram-editor__header__action--active':
1444
- diagramEditorState.sidePanelDisplayState.isOpen,
1445
- })}
1446
- tabIndex={-1}
1447
- onClick={toggleSidePanel}
1448
- >
1449
- <SidebarIcon className="diagram-editor__icon--sidebar" />
1450
- </button>
1451
- </div>
1452
- </>
1453
- );
1454
- },
1455
- );
1456
-
1457
- export const DiagramEditor = observer(() => {
1458
- const editorStore = useEditorStore();
1459
- const diagramEditorState =
1460
- editorStore.tabManagerState.getCurrentEditorState(DiagramEditorState);
1461
- const diagramCanvasRef = useRef<HTMLDivElement>(null);
1462
- const onContextMenuClose = (): void => diagramEditorState.closeContextMenu();
1463
-
1464
- useApplicationNavigationContext(
1465
- DSL_DIAGRAM_LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY.DIAGRAM_EDITOR,
1466
- );
1467
-
1468
- useCommands(diagramEditorState);
1469
-
1470
- return (
1471
- <div className="diagram-editor">
1472
- <div className="diagram-editor__header">
1473
- {diagramEditorState.isDiagramRendererInitialized && (
1474
- <DiagramEditorHeader diagramEditorState={diagramEditorState} />
1475
- )}
1476
- </div>
1477
- <div className="diagram-editor__content">
1478
- {diagramEditorState.isDiagramRendererInitialized && (
1479
- <DiagramEditorOverlay diagramEditorState={diagramEditorState} />
1480
- )}
1481
- <ContextMenu
1482
- className="diagram-editor__stage"
1483
- content={
1484
- <DiagramEditorContextMenu diagramEditorState={diagramEditorState} />
1485
- }
1486
- disabled={!diagramEditorState.showContextMenu}
1487
- menuProps={{ elevation: 7 }}
1488
- onClose={onContextMenuClose}
1489
- >
1490
- {diagramEditorState.isDiagramRendererInitialized && (
1491
- <DiagramEditorToolPanel diagramEditorState={diagramEditorState} />
1492
- )}
1493
- <DiagramEditorDiagramCanvas
1494
- diagramEditorState={diagramEditorState}
1495
- ref={diagramCanvasRef}
1496
- />
1497
- {diagramEditorState.isDiagramRendererInitialized && (
1498
- <>
1499
- <DiagramEditorInlinePropertyEditor
1500
- diagramEditorState={diagramEditorState}
1501
- />
1502
- <DiagramEditorInlineClassCreator
1503
- diagramEditorState={diagramEditorState}
1504
- />
1505
- <DiagramEditorInlineClassRenamer
1506
- diagramEditorState={diagramEditorState}
1507
- />
1508
- </>
1509
- )}
1510
- </ContextMenu>
1511
- </div>
1512
- </div>
1513
- );
1514
- });