@finos/legend-application-studio 13.0.1 → 13.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (189) hide show
  1. package/lib/components/editor/command/project-search.css +1 -1
  2. package/lib/components/editor/command/project-search.css.map +1 -1
  3. package/lib/components/editor/command-center/ProjectSearchCommand.d.ts.map +1 -1
  4. package/lib/components/editor/command-center/ProjectSearchCommand.js +11 -3
  5. package/lib/components/editor/command-center/ProjectSearchCommand.js.map +1 -1
  6. package/lib/components/editor/edit-panel/FunctionEditor.d.ts.map +1 -1
  7. package/lib/components/editor/edit-panel/FunctionEditor.js +41 -34
  8. package/lib/components/editor/edit-panel/FunctionEditor.js.map +1 -1
  9. package/lib/components/editor/edit-panel/GenerationSpecificationEditor.d.ts.map +1 -1
  10. package/lib/components/editor/edit-panel/GenerationSpecificationEditor.js +35 -54
  11. package/lib/components/editor/edit-panel/GenerationSpecificationEditor.js.map +1 -1
  12. package/lib/components/editor/edit-panel/GrammarTextEditor.d.ts.map +1 -1
  13. package/lib/components/editor/edit-panel/GrammarTextEditor.js +2 -2
  14. package/lib/components/editor/edit-panel/GrammarTextEditor.js.map +1 -1
  15. package/lib/components/editor/edit-panel/RuntimeEditor.d.ts.map +1 -1
  16. package/lib/components/editor/edit-panel/RuntimeEditor.js +11 -20
  17. package/lib/components/editor/edit-panel/RuntimeEditor.js.map +1 -1
  18. package/lib/components/editor/edit-panel/data-editor/DataElementEditor.d.ts.map +1 -1
  19. package/lib/components/editor/edit-panel/data-editor/DataElementEditor.js +4 -8
  20. package/lib/components/editor/edit-panel/data-editor/DataElementEditor.js.map +1 -1
  21. package/lib/components/editor/edit-panel/data-editor/RelationalCSVDataEditor.js +1 -1
  22. package/lib/components/editor/edit-panel/data-editor/RelationalCSVDataEditor.js.map +1 -1
  23. package/lib/components/editor/edit-panel/element-generation-editor/FileGenerationEditor.d.ts.map +1 -1
  24. package/lib/components/editor/edit-panel/element-generation-editor/FileGenerationEditor.js +3 -7
  25. package/lib/components/editor/edit-panel/element-generation-editor/FileGenerationEditor.js.map +1 -1
  26. package/lib/components/editor/edit-panel/external-format-editor/BindingElementEditor.d.ts.map +1 -1
  27. package/lib/components/editor/edit-panel/external-format-editor/BindingElementEditor.js +2 -2
  28. package/lib/components/editor/edit-panel/external-format-editor/BindingElementEditor.js.map +1 -1
  29. package/lib/components/editor/edit-panel/external-format-editor/SchemaSetElementEditor.js +1 -1
  30. package/lib/components/editor/edit-panel/external-format-editor/SchemaSetElementEditor.js.map +1 -1
  31. package/lib/components/editor/edit-panel/mapping-editor/EnumerationMappingEditor.d.ts.map +1 -1
  32. package/lib/components/editor/edit-panel/mapping-editor/EnumerationMappingEditor.js +2 -9
  33. package/lib/components/editor/edit-panel/mapping-editor/EnumerationMappingEditor.js.map +1 -1
  34. package/lib/components/editor/edit-panel/mapping-editor/FlatDataPropertyMappingEditor.d.ts.map +1 -1
  35. package/lib/components/editor/edit-panel/mapping-editor/FlatDataPropertyMappingEditor.js +3 -4
  36. package/lib/components/editor/edit-panel/mapping-editor/FlatDataPropertyMappingEditor.js.map +1 -1
  37. package/lib/components/editor/edit-panel/mapping-editor/InstanceSetImplementationEditor.d.ts.map +1 -1
  38. package/lib/components/editor/edit-panel/mapping-editor/InstanceSetImplementationEditor.js +3 -10
  39. package/lib/components/editor/edit-panel/mapping-editor/InstanceSetImplementationEditor.js.map +1 -1
  40. package/lib/components/editor/edit-panel/mapping-editor/MappingExecutionBuilder.d.ts.map +1 -1
  41. package/lib/components/editor/edit-panel/mapping-editor/MappingExecutionBuilder.js +4 -10
  42. package/lib/components/editor/edit-panel/mapping-editor/MappingExecutionBuilder.js.map +1 -1
  43. package/lib/components/editor/edit-panel/mapping-editor/MappingExplorer.js +7 -9
  44. package/lib/components/editor/edit-panel/mapping-editor/MappingExplorer.js.map +1 -1
  45. package/lib/components/editor/edit-panel/mapping-editor/MappingTestEditor.d.ts.map +1 -1
  46. package/lib/components/editor/edit-panel/mapping-editor/MappingTestEditor.js +9 -12
  47. package/lib/components/editor/edit-panel/mapping-editor/MappingTestEditor.js.map +1 -1
  48. package/lib/components/editor/edit-panel/mapping-editor/MappingTestsExplorer.d.ts.map +1 -1
  49. package/lib/components/editor/edit-panel/mapping-editor/MappingTestsExplorer.js +12 -10
  50. package/lib/components/editor/edit-panel/mapping-editor/MappingTestsExplorer.js.map +1 -1
  51. package/lib/components/editor/edit-panel/mapping-editor/NewMappingElementModal.js +2 -2
  52. package/lib/components/editor/edit-panel/mapping-editor/NewMappingElementModal.js.map +1 -1
  53. package/lib/components/editor/edit-panel/mapping-editor/OperationSetImplementationEditor.d.ts.map +1 -1
  54. package/lib/components/editor/edit-panel/mapping-editor/OperationSetImplementationEditor.js +5 -7
  55. package/lib/components/editor/edit-panel/mapping-editor/OperationSetImplementationEditor.js.map +1 -1
  56. package/lib/components/editor/edit-panel/mapping-editor/PurePropertyMappingEditor.d.ts.map +1 -1
  57. package/lib/components/editor/edit-panel/mapping-editor/PurePropertyMappingEditor.js +3 -4
  58. package/lib/components/editor/edit-panel/mapping-editor/PurePropertyMappingEditor.js.map +1 -1
  59. package/lib/components/editor/edit-panel/mapping-editor/relational/RelationalPropertyMappingEditor.d.ts.map +1 -1
  60. package/lib/components/editor/edit-panel/mapping-editor/relational/RelationalPropertyMappingEditor.js +1 -1
  61. package/lib/components/editor/edit-panel/mapping-editor/relational/RelationalPropertyMappingEditor.js.map +1 -1
  62. package/lib/components/editor/edit-panel/service-editor/ServiceExecutionEditor.d.ts.map +1 -1
  63. package/lib/components/editor/edit-panel/service-editor/ServiceExecutionEditor.js +3 -5
  64. package/lib/components/editor/edit-panel/service-editor/ServiceExecutionEditor.js.map +1 -1
  65. package/lib/components/editor/edit-panel/service-editor/ServiceExecutionQueryEditor.d.ts.map +1 -1
  66. package/lib/components/editor/edit-panel/service-editor/ServiceExecutionQueryEditor.js +9 -6
  67. package/lib/components/editor/edit-panel/service-editor/ServiceExecutionQueryEditor.js.map +1 -1
  68. package/lib/components/editor/edit-panel/service-editor/testable/ServiceTestableEditor.js +1 -1
  69. package/lib/components/editor/edit-panel/service-editor/testable/ServiceTestableEditor.js.map +1 -1
  70. package/lib/components/editor/edit-panel/uml-editor/AssociationEditor.d.ts.map +1 -1
  71. package/lib/components/editor/edit-panel/uml-editor/AssociationEditor.js +4 -8
  72. package/lib/components/editor/edit-panel/uml-editor/AssociationEditor.js.map +1 -1
  73. package/lib/components/editor/edit-panel/uml-editor/ClassEditor.d.ts.map +1 -1
  74. package/lib/components/editor/edit-panel/uml-editor/ClassEditor.js +157 -57
  75. package/lib/components/editor/edit-panel/uml-editor/ClassEditor.js.map +1 -1
  76. package/lib/components/editor/edit-panel/uml-editor/EnumerationEditor.d.ts.map +1 -1
  77. package/lib/components/editor/edit-panel/uml-editor/EnumerationEditor.js +39 -21
  78. package/lib/components/editor/edit-panel/uml-editor/EnumerationEditor.js.map +1 -1
  79. package/lib/components/editor/edit-panel/uml-editor/ProfileEditor.d.ts.map +1 -1
  80. package/lib/components/editor/edit-panel/uml-editor/ProfileEditor.js +62 -9
  81. package/lib/components/editor/edit-panel/uml-editor/ProfileEditor.js.map +1 -1
  82. package/lib/components/editor/edit-panel/uml-editor/PropertyEditor.d.ts.map +1 -1
  83. package/lib/components/editor/edit-panel/uml-editor/PropertyEditor.js +4 -8
  84. package/lib/components/editor/edit-panel/uml-editor/PropertyEditor.js.map +1 -1
  85. package/lib/components/editor/edit-panel/uml-editor/StereotypeSelector.d.ts +7 -1
  86. package/lib/components/editor/edit-panel/uml-editor/StereotypeSelector.d.ts.map +1 -1
  87. package/lib/components/editor/edit-panel/uml-editor/StereotypeSelector.js +33 -5
  88. package/lib/components/editor/edit-panel/uml-editor/StereotypeSelector.js.map +1 -1
  89. package/lib/components/editor/edit-panel/uml-editor/TaggedValueEditor.d.ts +7 -1
  90. package/lib/components/editor/edit-panel/uml-editor/TaggedValueEditor.d.ts.map +1 -1
  91. package/lib/components/editor/edit-panel/uml-editor/TaggedValueEditor.js +35 -7
  92. package/lib/components/editor/edit-panel/uml-editor/TaggedValueEditor.js.map +1 -1
  93. package/lib/components/editor/side-bar/CreateNewElementModal.d.ts.map +1 -1
  94. package/lib/components/editor/side-bar/CreateNewElementModal.js +4 -4
  95. package/lib/components/editor/side-bar/CreateNewElementModal.js.map +1 -1
  96. package/lib/components/editor/side-bar/Explorer.d.ts.map +1 -1
  97. package/lib/components/editor/side-bar/Explorer.js +8 -15
  98. package/lib/components/editor/side-bar/Explorer.js.map +1 -1
  99. package/lib/components/shared/StudioLambdaEditor.d.ts +1 -0
  100. package/lib/components/shared/StudioLambdaEditor.d.ts.map +1 -1
  101. package/lib/components/shared/StudioLambdaEditor.js +2 -2
  102. package/lib/components/shared/StudioLambdaEditor.js.map +1 -1
  103. package/lib/components/workspace-setup/WorkspaceSetup.js +1 -1
  104. package/lib/components/workspace-setup/WorkspaceSetup.js.map +1 -1
  105. package/lib/index.css +2 -2
  106. package/lib/index.css.map +1 -1
  107. package/lib/package.json +5 -5
  108. package/lib/stores/EditorStore.d.ts.map +1 -1
  109. package/lib/stores/EditorStore.js +7 -7
  110. package/lib/stores/EditorStore.js.map +1 -1
  111. package/lib/stores/editor/NewElementState.d.ts.map +1 -1
  112. package/lib/stores/editor/NewElementState.js +8 -2
  113. package/lib/stores/editor/NewElementState.js.map +1 -1
  114. package/lib/stores/editor-state/GenerationSpecificationEditorState.d.ts +0 -2
  115. package/lib/stores/editor-state/GenerationSpecificationEditorState.d.ts.map +1 -1
  116. package/lib/stores/editor-state/GenerationSpecificationEditorState.js +1 -8
  117. package/lib/stores/editor-state/GenerationSpecificationEditorState.js.map +1 -1
  118. package/lib/stores/editor-state/element-editor-state/ElementEditorState.d.ts.map +1 -1
  119. package/lib/stores/editor-state/element-editor-state/ElementEditorState.js +2 -4
  120. package/lib/stores/editor-state/element-editor-state/ElementEditorState.js.map +1 -1
  121. package/lib/stores/editor-state/element-editor-state/mapping/MappingEditorState.js +1 -1
  122. package/lib/stores/editor-state/element-editor-state/mapping/MappingEditorState.js.map +1 -1
  123. package/lib/stores/editor-state/element-editor-state/mapping/MappingExecutionState.js +1 -1
  124. package/lib/stores/editor-state/element-editor-state/mapping/MappingExecutionState.js.map +1 -1
  125. package/lib/stores/editor-state/element-editor-state/mapping/MappingTestState.d.ts +4 -1
  126. package/lib/stores/editor-state/element-editor-state/mapping/MappingTestState.d.ts.map +1 -1
  127. package/lib/stores/editor-state/element-editor-state/mapping/MappingTestState.js +31 -15
  128. package/lib/stores/editor-state/element-editor-state/mapping/MappingTestState.js.map +1 -1
  129. package/lib/stores/editor-state/element-editor-state/service/ServiceExecutionState.d.ts +6 -3
  130. package/lib/stores/editor-state/element-editor-state/service/ServiceExecutionState.d.ts.map +1 -1
  131. package/lib/stores/editor-state/element-editor-state/service/ServiceExecutionState.js +30 -17
  132. package/lib/stores/editor-state/element-editor-state/service/ServiceExecutionState.js.map +1 -1
  133. package/lib/stores/editor-state/element-editor-state/service/ServiceRegistrationState.js +1 -1
  134. package/lib/stores/editor-state/element-editor-state/service/ServiceRegistrationState.js.map +1 -1
  135. package/lib/stores/graphModifier/DomainGraphModifierHelper.d.ts +10 -0
  136. package/lib/stores/graphModifier/DomainGraphModifierHelper.d.ts.map +1 -1
  137. package/lib/stores/graphModifier/DomainGraphModifierHelper.js +31 -1
  138. package/lib/stores/graphModifier/DomainGraphModifierHelper.js.map +1 -1
  139. package/lib/stores/shared/DnDUtil.d.ts +1 -2
  140. package/lib/stores/shared/DnDUtil.d.ts.map +1 -1
  141. package/lib/stores/shared/DnDUtil.js +0 -2
  142. package/lib/stores/shared/DnDUtil.js.map +1 -1
  143. package/package.json +13 -13
  144. package/src/components/editor/command-center/ProjectSearchCommand.tsx +13 -2
  145. package/src/components/editor/edit-panel/FunctionEditor.tsx +220 -152
  146. package/src/components/editor/edit-panel/GenerationSpecificationEditor.tsx +154 -184
  147. package/src/components/editor/edit-panel/GrammarTextEditor.tsx +4 -5
  148. package/src/components/editor/edit-panel/RuntimeEditor.tsx +89 -90
  149. package/src/components/editor/edit-panel/data-editor/DataElementEditor.tsx +57 -40
  150. package/src/components/editor/edit-panel/data-editor/RelationalCSVDataEditor.tsx +1 -1
  151. package/src/components/editor/edit-panel/element-generation-editor/FileGenerationEditor.tsx +50 -45
  152. package/src/components/editor/edit-panel/external-format-editor/BindingElementEditor.tsx +36 -32
  153. package/src/components/editor/edit-panel/external-format-editor/SchemaSetElementEditor.tsx +1 -1
  154. package/src/components/editor/edit-panel/mapping-editor/EnumerationMappingEditor.tsx +46 -41
  155. package/src/components/editor/edit-panel/mapping-editor/FlatDataPropertyMappingEditor.tsx +9 -7
  156. package/src/components/editor/edit-panel/mapping-editor/InstanceSetImplementationEditor.tsx +18 -21
  157. package/src/components/editor/edit-panel/mapping-editor/MappingExecutionBuilder.tsx +39 -29
  158. package/src/components/editor/edit-panel/mapping-editor/MappingExplorer.tsx +39 -39
  159. package/src/components/editor/edit-panel/mapping-editor/MappingTestEditor.tsx +82 -58
  160. package/src/components/editor/edit-panel/mapping-editor/MappingTestsExplorer.tsx +66 -50
  161. package/src/components/editor/edit-panel/mapping-editor/NewMappingElementModal.tsx +2 -2
  162. package/src/components/editor/edit-panel/mapping-editor/OperationSetImplementationEditor.tsx +48 -45
  163. package/src/components/editor/edit-panel/mapping-editor/PurePropertyMappingEditor.tsx +12 -8
  164. package/src/components/editor/edit-panel/mapping-editor/relational/RelationalPropertyMappingEditor.tsx +2 -3
  165. package/src/components/editor/edit-panel/service-editor/ServiceExecutionEditor.tsx +75 -72
  166. package/src/components/editor/edit-panel/service-editor/ServiceExecutionQueryEditor.tsx +63 -43
  167. package/src/components/editor/edit-panel/service-editor/testable/ServiceTestableEditor.tsx +1 -1
  168. package/src/components/editor/edit-panel/uml-editor/AssociationEditor.tsx +55 -38
  169. package/src/components/editor/edit-panel/uml-editor/ClassEditor.tsx +813 -499
  170. package/src/components/editor/edit-panel/uml-editor/EnumerationEditor.tsx +209 -113
  171. package/src/components/editor/edit-panel/uml-editor/ProfileEditor.tsx +184 -52
  172. package/src/components/editor/edit-panel/uml-editor/PropertyEditor.tsx +62 -39
  173. package/src/components/editor/edit-panel/uml-editor/StereotypeSelector.tsx +137 -52
  174. package/src/components/editor/edit-panel/uml-editor/TaggedValueEditor.tsx +171 -88
  175. package/src/components/editor/side-bar/CreateNewElementModal.tsx +4 -2
  176. package/src/components/editor/side-bar/Explorer.tsx +13 -20
  177. package/src/components/shared/StudioLambdaEditor.tsx +3 -0
  178. package/src/components/workspace-setup/WorkspaceSetup.tsx +1 -1
  179. package/src/stores/EditorStore.ts +7 -6
  180. package/src/stores/editor/NewElementState.ts +8 -2
  181. package/src/stores/editor-state/GenerationSpecificationEditorState.ts +1 -15
  182. package/src/stores/editor-state/element-editor-state/ElementEditorState.ts +2 -3
  183. package/src/stores/editor-state/element-editor-state/mapping/MappingEditorState.ts +1 -1
  184. package/src/stores/editor-state/element-editor-state/mapping/MappingExecutionState.ts +1 -1
  185. package/src/stores/editor-state/element-editor-state/mapping/MappingTestState.ts +46 -29
  186. package/src/stores/editor-state/element-editor-state/service/ServiceExecutionState.ts +39 -20
  187. package/src/stores/editor-state/element-editor-state/service/ServiceRegistrationState.ts +1 -1
  188. package/src/stores/graphModifier/DomainGraphModifierHelper.ts +92 -0
  189. package/src/stores/shared/DnDUtil.ts +0 -2
@@ -14,10 +14,9 @@
14
14
  * limitations under the License.
15
15
  */
16
16
 
17
- import { useState, useEffect, useCallback } from 'react';
17
+ import { useState, useEffect, useCallback, useRef } from 'react';
18
18
  import { observer } from 'mobx-react-lite';
19
19
  import { isNonNullable, prettyCONSTName } from '@finos/legend-shared';
20
- import { useDrop } from 'react-dnd';
21
20
  import {
22
21
  CORE_DND_TYPE,
23
22
  type ElementDragSource,
@@ -41,14 +40,26 @@ import {
41
40
  ArrowCircleRightIcon,
42
41
  FireIcon,
43
42
  StickArrowCircleRightIcon,
43
+ PanelEntryDragHandle,
44
+ PanelEntryDropZonePlaceholder,
45
+ DragPreviewLayer,
46
+ useDragPreviewLayer,
47
+ PanelDropZone,
44
48
  } from '@finos/legend-art';
45
49
  import { LEGEND_STUDIO_TEST_ID } from '../../../LegendStudioTestID.js';
46
50
  import { PropertyEditor } from './PropertyEditor.js';
47
- import { StereotypeSelector } from './StereotypeSelector.js';
48
- import { TaggedValueEditor } from './TaggedValueEditor.js';
51
+ import {
52
+ StereotypeSelector,
53
+ StereotypeDragPreviewLayer,
54
+ } from './StereotypeSelector.js';
55
+ import {
56
+ TaggedValueEditor,
57
+ TaggedValueDragPreviewLayer,
58
+ } from './TaggedValueEditor.js';
49
59
  import { UML_EDITOR_TAB } from '../../../../stores/editor-state/element-editor-state/UMLEditorState.js';
50
60
  import { ClassEditorState } from '../../../../stores/editor-state/element-editor-state/ClassEditorState.js';
51
61
  import { flowResult } from 'mobx';
62
+ import { type DropTargetMonitor, useDrop, useDrag } from 'react-dnd';
52
63
  import { useEditorStore } from '../../EditorStoreProvider.js';
53
64
  import {
54
65
  type StereotypeReference,
@@ -80,11 +91,12 @@ import {
80
91
  getAllSuperclasses,
81
92
  getAllClassConstraints,
82
93
  getAllClassDerivedProperties,
94
+ isElementReadOnly,
83
95
  } from '@finos/legend-graph';
84
96
  import { StudioLambdaEditor } from '../../../shared/StudioLambdaEditor.js';
85
97
  import {
86
98
  ApplicationNavigationContextData,
87
- getPackageableElementOptionalFormatter,
99
+ getPackageableElementOptionFormatter,
88
100
  useApplicationNavigationContext,
89
101
  useApplicationStore,
90
102
  type PackageableElementOption,
@@ -110,6 +122,10 @@ import {
110
122
  property_setName,
111
123
  property_setGenericType,
112
124
  property_setMultiplicity,
125
+ class_swapProperties,
126
+ class_swapDerivedProperties,
127
+ class_swapConstraints,
128
+ class_swapSuperTypes,
113
129
  setGenericTypeReferenceValue,
114
130
  } from '../../../../stores/graphModifier/DomainGraphModifierHelper.js';
115
131
  import {
@@ -118,6 +134,12 @@ import {
118
134
  } from '../../../../stores/shared/ModelUtil.js';
119
135
  import { LEGEND_STUDIO_APPLICATION_NAVIGATION_CONTEXT_KEY } from '../../../../stores/LegendStudioApplicationNavigationContext.js';
120
136
 
137
+ type ClassPropertyDragSource = {
138
+ property: Property;
139
+ };
140
+
141
+ const CLASS_PROPERTY_DND_TYPE = 'CLASS_PROPERTY';
142
+
121
143
  const PropertyBasicEditor = observer(
122
144
  (props: {
123
145
  _class: Class;
@@ -126,7 +148,9 @@ const PropertyBasicEditor = observer(
126
148
  deleteProperty: () => void;
127
149
  isReadOnly: boolean;
128
150
  }) => {
151
+ const ref = useRef<HTMLDivElement>(null);
129
152
  const { property, _class, editorState, deleteProperty, isReadOnly } = props;
153
+
130
154
  const editorStore = useEditorStore();
131
155
  const isInheritedProperty =
132
156
  property._OWNER instanceof Class && property._OWNER !== _class;
@@ -136,6 +160,7 @@ const PropertyBasicEditor = observer(
136
160
  _class.properties.filter((p) => p.name === val.name).length >= 2;
137
161
  const selectProperty = (): void =>
138
162
  editorState.setSelectedProperty(property);
163
+
139
164
  // Name
140
165
  const changeValue: React.ChangeEventHandler<HTMLInputElement> = (event) => {
141
166
  property_setName(property, event.target.value);
@@ -193,6 +218,51 @@ const PropertyBasicEditor = observer(
193
218
  setUpperBound(event.target.value);
194
219
  updateMultiplicity(lowerBound, event.target.value);
195
220
  };
221
+
222
+ // Drag and Drop
223
+ const handleHover = useCallback(
224
+ (item: ClassPropertyDragSource): void => {
225
+ const draggingProperty = item.property;
226
+ const hoveredProperty = property;
227
+ class_swapProperties(_class, draggingProperty, hoveredProperty);
228
+ },
229
+ [_class, property],
230
+ );
231
+
232
+ const [{ isBeingDraggedProperty }, dropConnector] = useDrop<
233
+ ClassPropertyDragSource,
234
+ void,
235
+ { isBeingDraggedProperty: Property | undefined }
236
+ >(
237
+ () => ({
238
+ accept: [CLASS_PROPERTY_DND_TYPE],
239
+ hover: (item) => handleHover(item),
240
+ collect: (
241
+ monitor,
242
+ ): {
243
+ isBeingDraggedProperty: Property | undefined;
244
+ } => ({
245
+ isBeingDraggedProperty:
246
+ monitor.getItem<ClassPropertyDragSource | null>()?.property,
247
+ }),
248
+ }),
249
+ [handleHover],
250
+ );
251
+ const isBeingDragged = property === isBeingDraggedProperty;
252
+
253
+ const [, dragConnector, dragPreviewConnector] =
254
+ useDrag<ClassPropertyDragSource>(
255
+ () => ({
256
+ type: CLASS_PROPERTY_DND_TYPE,
257
+ item: () => ({
258
+ property: property,
259
+ }),
260
+ }),
261
+ [property],
262
+ );
263
+ dragConnector(dropConnector(ref));
264
+ useDragPreviewLayer(dragPreviewConnector);
265
+
196
266
  // Other
197
267
  const openElement = (): void => {
198
268
  if (!(propertyType instanceof PrimitiveType)) {
@@ -206,178 +276,191 @@ const PropertyBasicEditor = observer(
206
276
  const visitOwner = (): void => editorStore.openElement(property._OWNER);
207
277
 
208
278
  return (
209
- <div className="property-basic-editor">
210
- {isIndirectProperty && (
211
- <div className="property-basic-editor__name--with-lock">
212
- <div className="property-basic-editor__name--with-lock__icon">
213
- <LockIcon />
214
- </div>
215
- <span className="property-basic-editor__name--with-lock__name">
216
- {property.name}
217
- </span>
218
- </div>
219
- )}
220
- {!isIndirectProperty && (
221
- <div className="input-group__input property-basic-editor__input">
222
- <InputWithInlineValidation
223
- className="property-basic-editor__input--with-validation input-group__input"
224
- disabled={isReadOnly}
225
- value={property.name}
226
- spellCheck={false}
227
- onChange={changeValue}
228
- placeholder={`Property name`}
229
- validationErrorMessage={
230
- isPropertyDuplicated(property)
231
- ? 'Duplicated property'
232
- : undefined
233
- }
234
- />
235
- </div>
236
- )}
237
- {!isIndirectProperty && !isReadOnly && isEditingType && (
238
- <CustomSelectorInput
239
- className="property-basic-editor__type"
240
- options={propertyTypeOptions}
241
- onChange={changePropertyType}
242
- value={selectedPropertyType}
243
- placeholder={'Choose a data type or enumeration'}
244
- filterOption={filterOption}
245
- formatOptionLabel={getPackageableElementOptionalFormatter()}
246
- />
247
- )}
248
- {!isIndirectProperty && !isReadOnly && !isEditingType && (
249
- <div
250
- className={clsx(
251
- 'property-basic-editor__type',
252
- 'property-basic-editor__type--show-click-hint',
253
- `background--${propertyTypeName.toLowerCase()}`,
254
- {
255
- 'property-basic-editor__type--has-visit-btn':
256
- propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
257
- },
279
+ <div ref={ref} className="property-basic-editor__container">
280
+ <PanelEntryDropZonePlaceholder showPlaceholder={isBeingDragged}>
281
+ <div className="property-basic-editor">
282
+ {!isIndirectProperty && <PanelEntryDragHandle />}
283
+ {isIndirectProperty && (
284
+ <div className="property-basic-editor__name property-basic-editor__name--with-lock">
285
+ <div className="property-basic-editor__name--with-lock__icon">
286
+ <LockIcon />
287
+ </div>
288
+ <span className="property-basic-editor__name--with-lock__name">
289
+ {property.name}
290
+ </span>
291
+ </div>
258
292
  )}
259
- >
260
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
261
- <div className="property-basic-editor__type__abbr">
262
- {getElementIcon(editorStore, propertyType)}
293
+ {!isIndirectProperty && (
294
+ <div className="input-group__input property-basic-editor__input">
295
+ <InputWithInlineValidation
296
+ className="property-basic-editor__input--with-validation input-group__input"
297
+ disabled={isReadOnly}
298
+ value={property.name}
299
+ spellCheck={false}
300
+ onChange={changeValue}
301
+ placeholder={`Property name`}
302
+ validationErrorMessage={
303
+ isPropertyDuplicated(property)
304
+ ? 'Duplicated property'
305
+ : undefined
306
+ }
307
+ />
263
308
  </div>
264
309
  )}
265
- <div className="property-basic-editor__type__label">
266
- {propertyType.name}
267
- </div>
268
- <div
269
- data-testid={
270
- LEGEND_STUDIO_TEST_ID.PROPERTY_BASIC_EDITOR__TYPE__LABEL_HOVER
271
- }
272
- className="property-basic-editor__type__label property-basic-editor__type__label--hover"
273
- onClick={(): void => setIsEditingType(true)}
274
- >
275
- Click to edit
310
+ {!isIndirectProperty && !isReadOnly && isEditingType && (
311
+ <CustomSelectorInput
312
+ className="property-basic-editor__type"
313
+ options={propertyTypeOptions}
314
+ onChange={changePropertyType}
315
+ value={selectedPropertyType}
316
+ placeholder={'Choose a data type or enumeration'}
317
+ filterOption={filterOption}
318
+ formatOptionLabel={getPackageableElementOptionFormatter({})}
319
+ />
320
+ )}
321
+ {!isIndirectProperty && !isReadOnly && !isEditingType && (
322
+ <div
323
+ className={clsx(
324
+ 'property-basic-editor__type',
325
+ 'property-basic-editor__type--show-click-hint',
326
+ `background--${propertyTypeName.toLowerCase()}`,
327
+ {
328
+ 'property-basic-editor__type--has-visit-btn':
329
+ propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
330
+ },
331
+ )}
332
+ >
333
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
334
+ <div className="property-basic-editor__type__abbr">
335
+ {getElementIcon(editorStore, propertyType)}
336
+ </div>
337
+ )}
338
+ <div className="property-basic-editor__type__label">
339
+ {propertyType.name}
340
+ </div>
341
+ <div
342
+ data-testid={
343
+ LEGEND_STUDIO_TEST_ID.PROPERTY_BASIC_EDITOR__TYPE__LABEL_HOVER
344
+ }
345
+ className="property-basic-editor__type__label property-basic-editor__type__label--hover"
346
+ onClick={(): void => setIsEditingType(true)}
347
+ >
348
+ Click to edit
349
+ </div>
350
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
351
+ <button
352
+ data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
353
+ className="property-basic-editor__type__visit-btn"
354
+ onClick={openElement}
355
+ tabIndex={-1}
356
+ title={'Visit element'}
357
+ >
358
+ <ArrowCircleRightIcon />
359
+ </button>
360
+ )}
361
+ </div>
362
+ )}
363
+ {(isIndirectProperty || isReadOnly) && (
364
+ <div
365
+ className={clsx(
366
+ 'property-basic-editor__type',
367
+ `background--${propertyTypeName.toLowerCase()}`,
368
+ {
369
+ 'property-basic-editor__type--has-visit-btn':
370
+ propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
371
+ },
372
+ )}
373
+ >
374
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
375
+ <div className="property-basic-editor__type__abbr">
376
+ {getElementIcon(editorStore, propertyType)}
377
+ </div>
378
+ )}
379
+ <div className="property-basic-editor__type__label">
380
+ {propertyType.name}
381
+ </div>
382
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
383
+ <button
384
+ data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
385
+ className="property-basic-editor__type__visit-btn"
386
+ onClick={openElement}
387
+ tabIndex={-1}
388
+ title={'Visit element'}
389
+ >
390
+ <ArrowCircleRightIcon />
391
+ </button>
392
+ )}
393
+ </div>
394
+ )}
395
+ <div className="property-basic-editor__multiplicity">
396
+ <input
397
+ className="property-basic-editor__multiplicity-bound"
398
+ disabled={isIndirectProperty || isReadOnly}
399
+ spellCheck={false}
400
+ value={lowerBound}
401
+ onChange={changeLowerBound}
402
+ />
403
+ <div className="property-basic-editor__multiplicity__range">
404
+ ..
405
+ </div>
406
+ <input
407
+ className="property-basic-editor__multiplicity-bound"
408
+ disabled={isIndirectProperty || isReadOnly}
409
+ spellCheck={false}
410
+ value={upperBound}
411
+ onChange={changeUpperBound}
412
+ />
276
413
  </div>
277
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
414
+ {!isIndirectProperty && (
278
415
  <button
279
- data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
280
- className="property-basic-editor__type__visit-btn"
281
- onClick={openElement}
416
+ className="uml-element-editor__basic__detail-btn"
417
+ onClick={selectProperty}
282
418
  tabIndex={-1}
283
- title={'Visit element'}
419
+ title={'See detail'}
284
420
  >
285
- <ArrowCircleRightIcon />
421
+ <LongArrowRightIcon />
286
422
  </button>
287
423
  )}
288
- </div>
289
- )}
290
- {(isIndirectProperty || isReadOnly) && (
291
- <div
292
- className={clsx(
293
- 'property-basic-editor__type',
294
- `background--${propertyTypeName.toLowerCase()}`,
295
- {
296
- 'property-basic-editor__type--has-visit-btn':
297
- propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
298
- },
424
+ {isIndirectProperty && (
425
+ <button
426
+ className="uml-element-editor__visit-parent-element-btn"
427
+ onClick={visitOwner}
428
+ tabIndex={-1}
429
+ title={`Visit ${
430
+ isInheritedProperty ? 'super type class' : 'association'
431
+ } '${property._OWNER.path}'`}
432
+ >
433
+ <ArrowCircleRightIcon />
434
+ </button>
299
435
  )}
300
- >
301
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
302
- <div className="property-basic-editor__type__abbr">
303
- {getElementIcon(editorStore, propertyType)}
304
- </div>
436
+ {isIndirectProperty && (
437
+ <div className="property-basic-editor__locked-property-end-block"></div>
305
438
  )}
306
- <div className="property-basic-editor__type__label">
307
- {propertyType.name}
308
- </div>
309
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
439
+ {!isIndirectProperty && !isReadOnly && (
310
440
  <button
311
- data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
312
- className="property-basic-editor__type__visit-btn"
313
- onClick={openElement}
441
+ className={clsx('uml-element-editor__remove-btn', {
442
+ 'uml-element-editor__remove-btn--hidden': isIndirectProperty,
443
+ })}
444
+ onClick={deleteProperty}
314
445
  tabIndex={-1}
315
- title={'Visit element'}
446
+ title={'Remove'}
316
447
  >
317
- <ArrowCircleRightIcon />
448
+ <TimesIcon />
318
449
  </button>
319
450
  )}
320
451
  </div>
321
- )}
322
- <div className="property-basic-editor__multiplicity">
323
- <input
324
- className="property-basic-editor__multiplicity-bound"
325
- disabled={isIndirectProperty || isReadOnly}
326
- spellCheck={false}
327
- value={lowerBound}
328
- onChange={changeLowerBound}
329
- />
330
- <div className="property-basic-editor__multiplicity__range">..</div>
331
- <input
332
- className="property-basic-editor__multiplicity-bound"
333
- disabled={isIndirectProperty || isReadOnly}
334
- spellCheck={false}
335
- value={upperBound}
336
- onChange={changeUpperBound}
337
- />
338
- </div>
339
- {!isIndirectProperty && (
340
- <button
341
- className="uml-element-editor__basic__detail-btn"
342
- onClick={selectProperty}
343
- tabIndex={-1}
344
- title={'See detail'}
345
- >
346
- <LongArrowRightIcon />
347
- </button>
348
- )}
349
- {isIndirectProperty && (
350
- <button
351
- className="uml-element-editor__visit-parent-element-btn"
352
- onClick={visitOwner}
353
- tabIndex={-1}
354
- title={`Visit ${
355
- isInheritedProperty ? 'super type class' : 'association'
356
- } '${property._OWNER.path}'`}
357
- >
358
- <ArrowCircleRightIcon />
359
- </button>
360
- )}
361
- {isIndirectProperty && (
362
- <div className="property-basic-editor__locked-property-end-block"></div>
363
- )}
364
- {!isIndirectProperty && !isReadOnly && (
365
- <button
366
- className={clsx('uml-element-editor__remove-btn', {
367
- 'uml-element-editor__remove-btn--hidden': isIndirectProperty,
368
- })}
369
- onClick={deleteProperty}
370
- tabIndex={-1}
371
- title={'Remove'}
372
- >
373
- <TimesIcon />
374
- </button>
375
- )}
452
+ </PanelEntryDropZonePlaceholder>
376
453
  </div>
377
454
  );
378
455
  },
379
456
  );
380
457
 
458
+ type ClassDerivedPropertyDragSource = {
459
+ derivedProperty: DerivedProperty;
460
+ };
461
+
462
+ const CLASS_DERIVED_PROPERTY_DND_TYPE = 'CLASS_DERIVED_PROPERTY';
463
+
381
464
  const DerivedPropertyBasicEditor = observer(
382
465
  (props: {
383
466
  _class: Class;
@@ -386,6 +469,7 @@ const DerivedPropertyBasicEditor = observer(
386
469
  deleteDerivedProperty: () => void;
387
470
  isReadOnly: boolean;
388
471
  }) => {
472
+ const ref = useRef<HTMLDivElement>(null);
389
473
  const {
390
474
  derivedProperty,
391
475
  _class,
@@ -462,6 +546,68 @@ const DerivedPropertyBasicEditor = observer(
462
546
  setUpperBound(event.target.value);
463
547
  updateMultiplicity(lowerBound, event.target.value);
464
548
  };
549
+
550
+ // Drag and Drop
551
+ const handleHover = useCallback(
552
+ (
553
+ item: ClassDerivedPropertyDragSource,
554
+ monitor: DropTargetMonitor,
555
+ ): void => {
556
+ const draggingProperty = item.derivedProperty;
557
+ const hoveredProperty = derivedProperty;
558
+
559
+ const dragIndex = _class.derivedProperties.findIndex(
560
+ (e) => e === item.derivedProperty,
561
+ );
562
+ const hoverIndex = _class.derivedProperties.findIndex(
563
+ (e) => e === derivedProperty,
564
+ );
565
+ // move the item being hovered on when the dragged item position is beyond the its middle point
566
+ const hoverBoundingReact = ref.current?.getBoundingClientRect();
567
+ const distanceThreshold =
568
+ ((hoverBoundingReact?.bottom ?? 0) - (hoverBoundingReact?.top ?? 0)) /
569
+ 2;
570
+ const dragDistance =
571
+ (monitor.getClientOffset()?.y ?? 0) - (hoverBoundingReact?.top ?? 0);
572
+ if (dragIndex < hoverIndex && dragDistance < distanceThreshold) {
573
+ return;
574
+ }
575
+ class_swapDerivedProperties(_class, draggingProperty, hoveredProperty);
576
+ },
577
+ [_class, derivedProperty],
578
+ );
579
+
580
+ const [{ isBeingDraggedDerivedProperty }, dropConnector] = useDrop<
581
+ ClassDerivedPropertyDragSource,
582
+ void,
583
+ { isBeingDraggedDerivedProperty: DerivedProperty | undefined }
584
+ >(
585
+ () => ({
586
+ accept: [CLASS_DERIVED_PROPERTY_DND_TYPE],
587
+ hover: (item, monitor): void => handleHover(item, monitor),
588
+ collect: (monitor) => ({
589
+ isBeingDraggedDerivedProperty:
590
+ monitor.getItem<ClassDerivedPropertyDragSource | null>()
591
+ ?.derivedProperty,
592
+ }),
593
+ }),
594
+ [handleHover],
595
+ );
596
+ const isBeingDragged = derivedProperty === isBeingDraggedDerivedProperty;
597
+
598
+ const [, dragConnector, dragPreviewConnector] =
599
+ useDrag<ClassDerivedPropertyDragSource>(
600
+ () => ({
601
+ type: CLASS_DERIVED_PROPERTY_DND_TYPE,
602
+ item: () => ({
603
+ derivedProperty: derivedProperty,
604
+ }),
605
+ }),
606
+ [derivedProperty],
607
+ );
608
+ dragConnector(dropConnector(ref));
609
+ useDragPreviewLayer(dragPreviewConnector);
610
+
465
611
  // Action
466
612
  const onLambdaEditorFocus = (): void =>
467
613
  applicationStore.navigationContextService.push(
@@ -484,185 +630,202 @@ const DerivedPropertyBasicEditor = observer(
484
630
  });
485
631
 
486
632
  return (
487
- <div
488
- className={clsx('derived-property-editor', {
489
- backdrop__element:
490
- dpState.parserError && !isInheritedProperty && !isReadOnly,
491
- })}
492
- >
493
- <div className="property-basic-editor">
494
- {isInheritedProperty && (
495
- <div className="property-basic-editor__name--with-lock">
496
- <div className="property-basic-editor__name--with-lock__icon">
497
- <LockIcon />
498
- </div>
499
- <span className="property-basic-editor__name--with-lock__name">
500
- {derivedProperty.name}
501
- </span>
502
- </div>
503
- )}
504
- {!isInheritedProperty && (
505
- <input
506
- disabled={isReadOnly}
507
- spellCheck={false}
508
- className="property-basic-editor__name property-basic-editor__qualififed-property__name"
509
- value={derivedProperty.name}
510
- placeholder="Property name"
511
- onChange={changeValue}
512
- />
513
- )}
514
- {!isInheritedProperty && !isReadOnly && isEditingType && (
515
- <CustomSelectorInput
516
- className="property-basic-editor__type property-basic-editor__qualififed-property__type"
517
- options={propertyTypeOptions}
518
- onChange={changePropertyType}
519
- value={selectedPropertyType}
520
- placeholder="Choose a data type or enumeration"
521
- filterOption={filterOption}
522
- formatOptionLabel={getPackageableElementOptionalFormatter()}
523
- />
524
- )}
525
- {!isInheritedProperty && !isReadOnly && !isEditingType && (
526
- <div
527
- className={clsx(
528
- 'property-basic-editor__type',
529
- 'property-basic-editor__type--show-click-hint',
530
- `background--${propertyTypeName.toLowerCase()}`,
531
- {
532
- 'property-basic-editor__type--has-visit-btn':
533
- propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
534
- },
633
+ <div ref={ref} className="derived-property-editor__container">
634
+ <PanelEntryDropZonePlaceholder
635
+ showPlaceholder={isBeingDragged}
636
+ className="derived-property-editor__dnd__placeholder"
637
+ >
638
+ <div
639
+ className={clsx('derived-property-editor', {
640
+ backdrop__element:
641
+ dpState.parserError && !isInheritedProperty && !isReadOnly,
642
+ })}
643
+ >
644
+ <div className="property-basic-editor">
645
+ {isInheritedProperty && (
646
+ <div className="property-basic-editor__name property-basic-editor__name--with-lock">
647
+ <div className="property-basic-editor__name--with-lock__icon">
648
+ <LockIcon />
649
+ </div>
650
+ <span className="property-basic-editor__name--with-lock__name">
651
+ {derivedProperty.name}
652
+ </span>
653
+ </div>
535
654
  )}
536
- >
537
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
538
- <div className="property-basic-editor__type__abbr">
539
- {getElementIcon(editorStore, propertyType)}
655
+ {!isInheritedProperty && <PanelEntryDragHandle />}
656
+ {!isInheritedProperty && (
657
+ <input
658
+ disabled={isReadOnly}
659
+ spellCheck={false}
660
+ className="property-basic-editor__name property-basic-editor__qualififed-property__name"
661
+ value={derivedProperty.name}
662
+ placeholder="Property name"
663
+ onChange={changeValue}
664
+ />
665
+ )}
666
+ {!isInheritedProperty && !isReadOnly && isEditingType && (
667
+ <CustomSelectorInput
668
+ className="property-basic-editor__type property-basic-editor__qualififed-property__type"
669
+ options={propertyTypeOptions}
670
+ onChange={changePropertyType}
671
+ value={selectedPropertyType}
672
+ placeholder="Choose a data type or enumeration"
673
+ filterOption={filterOption}
674
+ formatOptionLabel={getPackageableElementOptionFormatter({})}
675
+ />
676
+ )}
677
+ {!isInheritedProperty && !isReadOnly && !isEditingType && (
678
+ <div
679
+ className={clsx(
680
+ 'property-basic-editor__type',
681
+ 'property-basic-editor__type--show-click-hint',
682
+ `background--${propertyTypeName.toLowerCase()}`,
683
+ {
684
+ 'property-basic-editor__type--has-visit-btn':
685
+ propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
686
+ },
687
+ )}
688
+ >
689
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
690
+ <div className="property-basic-editor__type__abbr">
691
+ {getElementIcon(editorStore, propertyType)}
692
+ </div>
693
+ )}
694
+ <div className="property-basic-editor__type__label">
695
+ {propertyType.name}
696
+ </div>
697
+ <div
698
+ data-testid={
699
+ LEGEND_STUDIO_TEST_ID.PROPERTY_BASIC_EDITOR__TYPE__LABEL_HOVER
700
+ }
701
+ className="property-basic-editor__type__label property-basic-editor__type__label--hover"
702
+ onClick={(): void => setIsEditingType(true)}
703
+ >
704
+ Click to edit
705
+ </div>
706
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
707
+ <button
708
+ data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
709
+ className="property-basic-editor__type__visit-btn"
710
+ onClick={openElement}
711
+ tabIndex={-1}
712
+ title="Visit element"
713
+ >
714
+ <ArrowCircleRightIcon />
715
+ </button>
716
+ )}
540
717
  </div>
541
718
  )}
542
- <div className="property-basic-editor__type__label">
543
- {propertyType.name}
544
- </div>
545
- <div
546
- data-testid={
547
- LEGEND_STUDIO_TEST_ID.PROPERTY_BASIC_EDITOR__TYPE__LABEL_HOVER
548
- }
549
- className="property-basic-editor__type__label property-basic-editor__type__label--hover"
550
- onClick={(): void => setIsEditingType(true)}
551
- >
552
- Click to edit
719
+ {(isInheritedProperty || isReadOnly) && (
720
+ <div
721
+ className={clsx(
722
+ 'property-basic-editor__type',
723
+ `background--${propertyTypeName.toLowerCase()}`,
724
+ {
725
+ 'property-basic-editor__type--has-visit-btn':
726
+ propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
727
+ },
728
+ )}
729
+ >
730
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
731
+ <div className="property-basic-editor__type__abbr">
732
+ {getElementIcon(editorStore, propertyType)}
733
+ </div>
734
+ )}
735
+ <div className="property-basic-editor__type__label">
736
+ {propertyType.name}
737
+ </div>
738
+ {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
739
+ <button
740
+ data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
741
+ className="property-basic-editor__type__visit-btn"
742
+ onClick={openElement}
743
+ tabIndex={-1}
744
+ title="Visit element"
745
+ >
746
+ <ArrowCircleRightIcon />
747
+ </button>
748
+ )}
749
+ </div>
750
+ )}
751
+ <div className="property-basic-editor__multiplicity">
752
+ <input
753
+ className="property-basic-editor__multiplicity-bound"
754
+ spellCheck={false}
755
+ disabled={isInheritedProperty || isReadOnly}
756
+ value={lowerBound}
757
+ onChange={changeLowerBound}
758
+ />
759
+ <div className="property-basic-editor__multiplicity__range">
760
+ ..
761
+ </div>
762
+ <input
763
+ className="property-basic-editor__multiplicity-bound"
764
+ spellCheck={false}
765
+ disabled={isInheritedProperty || isReadOnly}
766
+ value={upperBound}
767
+ onChange={changeUpperBound}
768
+ />
553
769
  </div>
554
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
770
+ {!isInheritedProperty && (
555
771
  <button
556
- data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
557
- className="property-basic-editor__type__visit-btn"
558
- onClick={openElement}
772
+ className="uml-element-editor__basic__detail-btn"
773
+ onClick={selectDerivedProperty}
559
774
  tabIndex={-1}
560
- title="Visit element"
775
+ title="See detail"
561
776
  >
562
- <ArrowCircleRightIcon />
777
+ <LongArrowRightIcon />
563
778
  </button>
564
779
  )}
565
- </div>
566
- )}
567
- {(isInheritedProperty || isReadOnly) && (
568
- <div
569
- className={clsx(
570
- 'property-basic-editor__type',
571
- `background--${propertyTypeName.toLowerCase()}`,
572
- {
573
- 'property-basic-editor__type--has-visit-btn':
574
- propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE,
575
- },
576
- )}
577
- >
578
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
579
- <div className="property-basic-editor__type__abbr">
580
- {getElementIcon(editorStore, propertyType)}
581
- </div>
582
- )}
583
- <div className="property-basic-editor__type__label">
584
- {propertyType.name}
585
- </div>
586
- {propertyTypeName !== CLASS_PROPERTY_TYPE.PRIMITIVE && (
780
+ {isInheritedProperty && (
587
781
  <button
588
- data-testid={LEGEND_STUDIO_TEST_ID.TYPE_VISIT}
589
- className="property-basic-editor__type__visit-btn"
590
- onClick={openElement}
782
+ className="uml-element-editor__visit-parent-element-btn"
783
+ onClick={visitOwner}
591
784
  tabIndex={-1}
592
- title="Visit element"
785
+ title={`Visit super type class ${derivedProperty._OWNER.path}`}
593
786
  >
594
787
  <ArrowCircleRightIcon />
595
788
  </button>
596
789
  )}
790
+ {!isInheritedProperty && !isReadOnly && (
791
+ <button
792
+ className={clsx('uml-element-editor__remove-btn', {
793
+ 'uml-element-editor__remove-btn--hidden':
794
+ isInheritedProperty,
795
+ })}
796
+ onClick={remove}
797
+ tabIndex={-1}
798
+ title="Remove"
799
+ >
800
+ <TimesIcon />
801
+ </button>
802
+ )}
597
803
  </div>
598
- )}
599
- <div className="property-basic-editor__multiplicity">
600
- <input
601
- className="property-basic-editor__multiplicity-bound"
602
- spellCheck={false}
603
- disabled={isInheritedProperty || isReadOnly}
604
- value={lowerBound}
605
- onChange={changeLowerBound}
606
- />
607
- <div className="property-basic-editor__multiplicity__range">..</div>
608
- <input
609
- className="property-basic-editor__multiplicity-bound"
610
- spellCheck={false}
611
- disabled={isInheritedProperty || isReadOnly}
612
- value={upperBound}
613
- onChange={changeUpperBound}
804
+ <StudioLambdaEditor
805
+ disabled={
806
+ editorState.classState
807
+ .isConvertingDerivedPropertyLambdaObjects ||
808
+ isInheritedProperty ||
809
+ isReadOnly
810
+ }
811
+ lambdaEditorState={dpState}
812
+ forceBackdrop={hasParserError}
813
+ expectedType={propertyType}
814
+ onEditorFocusEventHandler={onLambdaEditorFocus}
614
815
  />
615
816
  </div>
616
- {!isInheritedProperty && (
617
- <button
618
- className="uml-element-editor__basic__detail-btn"
619
- onClick={selectDerivedProperty}
620
- tabIndex={-1}
621
- title="See detail"
622
- >
623
- <LongArrowRightIcon />
624
- </button>
625
- )}
626
- {isInheritedProperty && (
627
- <button
628
- className="uml-element-editor__visit-parent-element-btn"
629
- onClick={visitOwner}
630
- tabIndex={-1}
631
- title={`Visit super type class ${derivedProperty._OWNER.path}`}
632
- >
633
- <ArrowCircleRightIcon />
634
- </button>
635
- )}
636
- {!isInheritedProperty && !isReadOnly && (
637
- <button
638
- className={clsx('uml-element-editor__remove-btn', {
639
- 'uml-element-editor__remove-btn--hidden': isInheritedProperty,
640
- })}
641
- onClick={remove}
642
- tabIndex={-1}
643
- title="Remove"
644
- >
645
- <TimesIcon />
646
- </button>
647
- )}
648
- </div>
649
- <div onFocus={onLambdaEditorFocus}>
650
- <StudioLambdaEditor
651
- disabled={
652
- editorState.classState.isConvertingDerivedPropertyLambdaObjects ||
653
- isInheritedProperty ||
654
- isReadOnly
655
- }
656
- lambdaEditorState={dpState}
657
- forceBackdrop={hasParserError}
658
- expectedType={propertyType}
659
- />
660
- </div>
817
+ </PanelEntryDropZonePlaceholder>
661
818
  </div>
662
819
  );
663
820
  },
664
821
  );
665
822
 
823
+ type ClassConstraintDragSource = {
824
+ constraint: Constraint;
825
+ };
826
+
827
+ const CLASS_CONSTRAINT_DND_TYPE = 'CLASS_CONSTRAINT';
828
+
666
829
  const ConstraintEditor = observer(
667
830
  (props: {
668
831
  editorState: ClassEditorState;
@@ -671,6 +834,7 @@ const ConstraintEditor = observer(
671
834
  deleteConstraint: () => void;
672
835
  isReadOnly: boolean;
673
836
  }) => {
837
+ const ref = useRef<HTMLDivElement>(null);
674
838
  const { constraint, _class, deleteConstraint, editorState, isReadOnly } =
675
839
  props;
676
840
  const editorStore = useEditorStore();
@@ -684,6 +848,49 @@ const ConstraintEditor = observer(
684
848
  // Name
685
849
  const changeName: React.ChangeEventHandler<HTMLInputElement> = (event) =>
686
850
  constraint_setName(constraint, event.target.value);
851
+
852
+ // Drag and Drop
853
+ const handleHover = useCallback(
854
+ (item: ClassConstraintDragSource): void => {
855
+ const draggingProperty = item.constraint;
856
+ const hoveredProperty = constraint;
857
+ class_swapConstraints(_class, draggingProperty, hoveredProperty);
858
+ },
859
+ [_class, constraint],
860
+ );
861
+
862
+ const [{ isBeingDraggedConstraint }, dropConnector] = useDrop<
863
+ ClassConstraintDragSource,
864
+ void,
865
+ { isBeingDraggedConstraint: Constraint | undefined }
866
+ >(
867
+ () => ({
868
+ accept: [CLASS_CONSTRAINT_DND_TYPE],
869
+ hover: (item) => handleHover(item),
870
+ collect: (
871
+ monitor,
872
+ ): { isBeingDraggedConstraint: Constraint | undefined } => ({
873
+ isBeingDraggedConstraint:
874
+ monitor.getItem<ClassConstraintDragSource | null>()?.constraint,
875
+ }),
876
+ }),
877
+ [handleHover],
878
+ );
879
+ const isBeingDragged = constraint === isBeingDraggedConstraint;
880
+
881
+ const [, dragConnector, dragPreviewConnector] =
882
+ useDrag<ClassConstraintDragSource>(
883
+ () => ({
884
+ type: CLASS_CONSTRAINT_DND_TYPE,
885
+ item: () => ({
886
+ constraint: constraint,
887
+ }),
888
+ }),
889
+ [constraint],
890
+ );
891
+ dragConnector(dropConnector(ref));
892
+ useDragPreviewLayer(dragPreviewConnector);
893
+
687
894
  // Actions
688
895
  const onLambdaEditorFocus = (): void =>
689
896
  applicationStore.navigationContextService.push(
@@ -700,73 +907,86 @@ const ConstraintEditor = observer(
700
907
  const visitOwner = (): void => editorStore.openElement(constraint._OWNER);
701
908
 
702
909
  return (
703
- <div
704
- className={clsx('constraint-editor', {
705
- backdrop__element: constraintState.parserError,
706
- })}
707
- >
708
- <div className="constraint-editor__content">
709
- {isInheritedConstraint && (
710
- <div className="constraint-editor__content__name--with-lock">
711
- <div className="constraint-editor__content__name--with-lock__icon">
712
- <LockIcon />
713
- </div>
714
- <span className="constraint-editor__content__name--with-lock__name">
715
- {constraint.name}
716
- </span>
910
+ <div ref={ref} className="constraint-editor__container">
911
+ <PanelEntryDropZonePlaceholder
912
+ showPlaceholder={isBeingDragged}
913
+ className="constraint-editor__dnd__placeholder"
914
+ >
915
+ <div
916
+ className={clsx('constraint-editor', {
917
+ backdrop__element: constraintState.parserError,
918
+ })}
919
+ >
920
+ <div className="constraint-editor__content">
921
+ {!isInheritedConstraint && <PanelEntryDragHandle />}
922
+ {isInheritedConstraint && (
923
+ <div className="constraint-editor__content__name--with-lock">
924
+ <div className="constraint-editor__content__name--with-lock__icon">
925
+ <LockIcon />
926
+ </div>
927
+ <span className="constraint-editor__content__name--with-lock__name">
928
+ {constraint.name}
929
+ </span>
930
+ </div>
931
+ )}
932
+ {!isInheritedConstraint && (
933
+ <input
934
+ className="constraint-editor__content__name"
935
+ spellCheck={false}
936
+ disabled={isReadOnly || isInheritedConstraint}
937
+ value={constraint.name}
938
+ onChange={changeName}
939
+ placeholder="Constraint name"
940
+ />
941
+ )}
942
+ {isInheritedConstraint && (
943
+ <button
944
+ className="uml-element-editor__visit-parent-element-btn"
945
+ onClick={visitOwner}
946
+ tabIndex={-1}
947
+ title={`Visit super type class ${constraint._OWNER.path}`}
948
+ >
949
+ <ArrowCircleRightIcon />
950
+ </button>
951
+ )}
952
+ {!isInheritedConstraint && !isReadOnly && (
953
+ <button
954
+ className="uml-element-editor__remove-btn"
955
+ disabled={isInheritedConstraint}
956
+ onClick={remove}
957
+ tabIndex={-1}
958
+ title="Remove"
959
+ >
960
+ <TimesIcon />
961
+ </button>
962
+ )}
717
963
  </div>
718
- )}
719
- {!isInheritedConstraint && (
720
- <input
721
- className="constraint-editor__content__name"
722
- spellCheck={false}
723
- disabled={isReadOnly || isInheritedConstraint}
724
- value={constraint.name}
725
- onChange={changeName}
726
- placeholder="Constraint name"
964
+ <StudioLambdaEditor
965
+ disabled={
966
+ editorState.classState.isConvertingConstraintLambdaObjects ||
967
+ isReadOnly ||
968
+ isInheritedConstraint
969
+ }
970
+ lambdaEditorState={constraintState}
971
+ forceBackdrop={hasParserError}
972
+ expectedType={editorStore.graphManagerState.graph.getPrimitiveType(
973
+ PRIMITIVE_TYPE.BOOLEAN,
974
+ )}
975
+ onEditorFocusEventHandler={onLambdaEditorFocus}
727
976
  />
728
- )}
729
- {isInheritedConstraint && (
730
- <button
731
- className="uml-element-editor__visit-parent-element-btn"
732
- onClick={visitOwner}
733
- tabIndex={-1}
734
- title={`Visit super type class ${constraint._OWNER.path}`}
735
- >
736
- <ArrowCircleRightIcon />
737
- </button>
738
- )}
739
- {!isInheritedConstraint && !isReadOnly && (
740
- <button
741
- className="uml-element-editor__remove-btn"
742
- disabled={isInheritedConstraint}
743
- onClick={remove}
744
- tabIndex={-1}
745
- title="Remove"
746
- >
747
- <TimesIcon />
748
- </button>
749
- )}
750
- </div>
751
- <div onFocus={onLambdaEditorFocus}>
752
- <StudioLambdaEditor
753
- disabled={
754
- editorState.classState.isConvertingConstraintLambdaObjects ||
755
- isReadOnly ||
756
- isInheritedConstraint
757
- }
758
- lambdaEditorState={constraintState}
759
- forceBackdrop={hasParserError}
760
- expectedType={editorStore.graphManagerState.graph.getPrimitiveType(
761
- PRIMITIVE_TYPE.BOOLEAN,
762
- )}
763
- />
764
- </div>
977
+ </div>
978
+ </PanelEntryDropZonePlaceholder>
765
979
  </div>
766
980
  );
767
981
  },
768
982
  );
769
983
 
984
+ type ClassSuperTypeDragSource = {
985
+ superType: GenericTypeReference;
986
+ };
987
+
988
+ const CLASS_SUPER_TYPE_DND_TYPE = 'CLASS_SUPER_TYPE';
989
+
770
990
  const SuperTypeEditor = observer(
771
991
  (props: {
772
992
  _class: Class;
@@ -774,6 +994,7 @@ const SuperTypeEditor = observer(
774
994
  deleteSuperType: () => void;
775
995
  isReadOnly: boolean;
776
996
  }) => {
997
+ const ref = useRef<HTMLDivElement>(null);
777
998
  const { superType, _class, deleteSuperType, isReadOnly } = props;
778
999
  const editorStore = useEditorStore();
779
1000
  // Type
@@ -787,6 +1008,47 @@ const SuperTypeEditor = observer(
787
1008
  // Ensure there is no loop (might be expensive)
788
1009
  !getAllSuperclasses(classOption.value).includes(_class),
789
1010
  );
1011
+
1012
+ // Drag and Drop
1013
+ const handleHover = useCallback(
1014
+ (item: ClassSuperTypeDragSource): void => {
1015
+ const draggingProperty = item.superType;
1016
+ const hoveredProperty = superType;
1017
+ class_swapSuperTypes(_class, draggingProperty, hoveredProperty);
1018
+ },
1019
+ [_class, superType],
1020
+ );
1021
+
1022
+ const [{ isBeingDraggedSupertype }, dropConnector] = useDrop<
1023
+ ClassSuperTypeDragSource,
1024
+ void,
1025
+ { isBeingDraggedSupertype: GenericTypeReference | undefined }
1026
+ >(
1027
+ () => ({
1028
+ accept: [CLASS_SUPER_TYPE_DND_TYPE],
1029
+ hover: (item) => handleHover(item),
1030
+ collect: (monitor) => ({
1031
+ isBeingDraggedSupertype:
1032
+ monitor.getItem<ClassSuperTypeDragSource | null>()?.superType,
1033
+ }),
1034
+ }),
1035
+ [handleHover],
1036
+ );
1037
+ const isBeingDragged = superType === isBeingDraggedSupertype;
1038
+
1039
+ const [, dragConnector, dragPreviewConnector] =
1040
+ useDrag<ClassSuperTypeDragSource>(
1041
+ () => ({
1042
+ type: CLASS_SUPER_TYPE_DND_TYPE,
1043
+ item: () => ({
1044
+ superType: superType,
1045
+ }),
1046
+ }),
1047
+ [superType],
1048
+ );
1049
+ dragConnector(dropConnector(ref));
1050
+ useDragPreviewLayer(dragPreviewConnector);
1051
+
790
1052
  const rawType = superType.value.rawType;
791
1053
  const filterOption = createFilter({
792
1054
  ignoreCase: true,
@@ -800,36 +1062,41 @@ const SuperTypeEditor = observer(
800
1062
  const visitDerivationSource = (): void => editorStore.openElement(rawType);
801
1063
 
802
1064
  return (
803
- <div className="super-type-editor">
804
- <CustomSelectorInput
805
- className="super-type-editor__class"
806
- disabled={isReadOnly}
807
- options={superTypeOptions}
808
- onChange={changeType}
809
- value={selectedType}
810
- placeholder={'Choose a class'}
811
- filterOption={filterOption}
812
- formatOptionLabel={getPackageableElementOptionalFormatter()}
813
- />
814
- <button
815
- className="uml-element-editor__basic__detail-btn"
816
- onClick={visitDerivationSource}
817
- tabIndex={-1}
818
- title={'Visit super type'}
819
- >
820
- <LongArrowRightIcon />
821
- </button>
822
- {!isReadOnly && (
823
- <button
824
- className="uml-element-editor__remove-btn"
825
- disabled={isReadOnly}
826
- onClick={deleteSuperType}
827
- tabIndex={-1}
828
- title={'Remove'}
829
- >
830
- <TimesIcon />
831
- </button>
832
- )}
1065
+ <div ref={ref} className="super-type-editor__container">
1066
+ <PanelEntryDropZonePlaceholder showPlaceholder={isBeingDragged}>
1067
+ <div className="super-type-editor">
1068
+ <PanelEntryDragHandle />
1069
+ <CustomSelectorInput
1070
+ className="super-type-editor__class"
1071
+ disabled={isReadOnly}
1072
+ options={superTypeOptions}
1073
+ onChange={changeType}
1074
+ value={selectedType}
1075
+ placeholder={'Choose a class'}
1076
+ filterOption={filterOption}
1077
+ formatOptionLabel={getPackageableElementOptionFormatter({})}
1078
+ />
1079
+ <button
1080
+ className="uml-element-editor__basic__detail-btn"
1081
+ onClick={visitDerivationSource}
1082
+ tabIndex={-1}
1083
+ title={'Visit super type'}
1084
+ >
1085
+ <LongArrowRightIcon />
1086
+ </button>
1087
+ {!isReadOnly && (
1088
+ <button
1089
+ className="uml-element-editor__remove-btn"
1090
+ disabled={isReadOnly}
1091
+ onClick={deleteSuperType}
1092
+ tabIndex={-1}
1093
+ title={'Remove'}
1094
+ >
1095
+ <TimesIcon />
1096
+ </button>
1097
+ )}
1098
+ </div>
1099
+ </PanelEntryDropZonePlaceholder>
833
1100
  </div>
834
1101
  );
835
1102
  },
@@ -848,6 +1115,7 @@ const PropertiesEditor = observer(
848
1115
  editorState.setSelectedProperty(undefined);
849
1116
  }
850
1117
  };
1118
+
851
1119
  const indirectProperties = getAllClassProperties(_class)
852
1120
  .filter((property) => !_class.properties.includes(property))
853
1121
  .sort((p1, p2) => p1.name.localeCompare(p2.name))
@@ -867,14 +1135,18 @@ const PropertiesEditor = observer(
867
1135
  },
868
1136
  [_class, isReadOnly],
869
1137
  );
870
- const [{ isPropertyDragOver }, dropPropertyRef] = useDrop(
1138
+ const [{ isPropertyDragOver }, dropPropertyRef] = useDrop<
1139
+ ElementDragSource,
1140
+ void,
1141
+ { isPropertyDragOver: boolean }
1142
+ >(
871
1143
  () => ({
872
1144
  accept: [
873
1145
  CORE_DND_TYPE.PROJECT_EXPLORER_CLASS,
874
1146
  CORE_DND_TYPE.PROJECT_EXPLORER_ENUMERATION,
875
1147
  ],
876
- drop: (item: ElementDragSource): void => handleDropProperty(item),
877
- collect: (monitor): { isPropertyDragOver: boolean } => ({
1148
+ drop: (item) => handleDropProperty(item),
1149
+ collect: (monitor) => ({
878
1150
  isPropertyDragOver: monitor.isOver({ shallow: true }),
879
1151
  }),
880
1152
  }),
@@ -886,23 +1158,29 @@ const PropertiesEditor = observer(
886
1158
  );
887
1159
 
888
1160
  return (
889
- <div
890
- ref={dropPropertyRef}
891
- className={clsx('panel__content__lists', {
892
- 'panel__content__lists--dnd-over': isPropertyDragOver && !isReadOnly,
893
- })}
1161
+ <PanelDropZone
1162
+ isDragOver={isPropertyDragOver && !isReadOnly}
1163
+ dropTargetConnector={dropPropertyRef}
894
1164
  >
895
- {_class.properties.concat(indirectProperties).map((property) => (
896
- <PropertyBasicEditor
897
- key={property._UUID}
898
- property={property}
899
- _class={_class}
900
- editorState={editorState}
901
- deleteProperty={deleteProperty(property)}
902
- isReadOnly={isReadOnly}
1165
+ <div className="panel__content__lists">
1166
+ <DragPreviewLayer
1167
+ labelGetter={(item: ClassPropertyDragSource): string =>
1168
+ item.property.name === '' ? '(unknown)' : item.property.name
1169
+ }
1170
+ types={[CLASS_PROPERTY_DND_TYPE]}
903
1171
  />
904
- ))}
905
- </div>
1172
+ {_class.properties.concat(indirectProperties).map((property) => (
1173
+ <PropertyBasicEditor
1174
+ key={property._UUID}
1175
+ property={property}
1176
+ _class={_class}
1177
+ editorState={editorState}
1178
+ deleteProperty={deleteProperty(property)}
1179
+ isReadOnly={isReadOnly}
1180
+ />
1181
+ ))}
1182
+ </div>
1183
+ </PanelDropZone>
906
1184
  );
907
1185
  },
908
1186
  );
@@ -940,15 +1218,18 @@ const DerviedPropertiesEditor = observer(
940
1218
  },
941
1219
  [_class, classState, isReadOnly],
942
1220
  );
943
- const [{ isDerivedPropertyDragOver }, dropDerivedPropertyRef] = useDrop(
1221
+ const [{ isDerivedPropertyDragOver }, dropDerivedPropertyRef] = useDrop<
1222
+ ElementDragSource,
1223
+ void,
1224
+ { isDerivedPropertyDragOver: boolean }
1225
+ >(
944
1226
  () => ({
945
1227
  accept: [
946
1228
  CORE_DND_TYPE.PROJECT_EXPLORER_CLASS,
947
1229
  CORE_DND_TYPE.PROJECT_EXPLORER_ENUMERATION,
948
1230
  ],
949
- drop: (item: ElementDragSource): void =>
950
- handleDropDerivedProperty(item),
951
- collect: (monitor): { isDerivedPropertyDragOver: boolean } => ({
1231
+ drop: (item) => handleDropDerivedProperty(item),
1232
+ collect: (monitor) => ({
952
1233
  isDerivedPropertyDragOver: monitor.isOver({ shallow: true }),
953
1234
  }),
954
1235
  }),
@@ -960,29 +1241,36 @@ const DerviedPropertiesEditor = observer(
960
1241
  );
961
1242
 
962
1243
  return (
963
- <div
964
- ref={dropDerivedPropertyRef}
965
- className={clsx('panel__content__lists', {
966
- 'panel__content__lists--dnd-over':
967
- isDerivedPropertyDragOver && !isReadOnly,
968
- })}
1244
+ <PanelDropZone
1245
+ isDragOver={isDerivedPropertyDragOver && !isReadOnly}
1246
+ dropTargetConnector={dropDerivedPropertyRef}
969
1247
  >
970
- {_class.derivedProperties
971
- .concat(indirectDerivedProperties)
972
- .filter((dp): dp is DerivedProperty =>
973
- Boolean(classState.getNullableDerivedPropertyState(dp)),
974
- )
975
- .map((dp) => (
976
- <DerivedPropertyBasicEditor
977
- key={dp._UUID}
978
- derivedProperty={dp}
979
- _class={_class}
980
- editorState={editorState}
981
- deleteDerivedProperty={deleteDerivedProperty(dp)}
982
- isReadOnly={isReadOnly}
983
- />
984
- ))}
985
- </div>
1248
+ <div className="panel__content__lists">
1249
+ <DragPreviewLayer
1250
+ labelGetter={(item: ClassDerivedPropertyDragSource): string =>
1251
+ item.derivedProperty.name === ''
1252
+ ? '(unknown)'
1253
+ : item.derivedProperty.name
1254
+ }
1255
+ types={[CLASS_DERIVED_PROPERTY_DND_TYPE]}
1256
+ />
1257
+ {_class.derivedProperties
1258
+ .concat(indirectDerivedProperties)
1259
+ .filter((dp): dp is DerivedProperty =>
1260
+ Boolean(classState.getNullableDerivedPropertyState(dp)),
1261
+ )
1262
+ .map((dp) => (
1263
+ <DerivedPropertyBasicEditor
1264
+ key={dp._UUID}
1265
+ derivedProperty={dp}
1266
+ _class={_class}
1267
+ editorState={editorState}
1268
+ deleteDerivedProperty={deleteDerivedProperty(dp)}
1269
+ isReadOnly={isReadOnly}
1270
+ />
1271
+ ))}
1272
+ </div>
1273
+ </PanelDropZone>
986
1274
  );
987
1275
  },
988
1276
  );
@@ -1009,6 +1297,12 @@ const ConstraintsEditor = observer(
1009
1297
 
1010
1298
  return (
1011
1299
  <div className="panel__content__lists">
1300
+ <DragPreviewLayer
1301
+ labelGetter={(item: ClassConstraintDragSource): string =>
1302
+ item.constraint.name === '' ? '(unknown)' : item.constraint.name
1303
+ }
1304
+ types={[CLASS_CONSTRAINT_DND_TYPE]}
1305
+ />
1012
1306
  {_class.constraints
1013
1307
  .concat(inheritedConstraints)
1014
1308
  .filter((constraint): constraint is Constraint =>
@@ -1066,14 +1360,18 @@ const SupertypesEditor = observer(
1066
1360
  },
1067
1361
  [_class, isReadOnly],
1068
1362
  );
1069
- const [{ isSuperTypeDragOver }, dropSuperTypeRef] = useDrop(
1363
+ const [{ isSuperTypeDragOver }, dropSuperTypeRef] = useDrop<
1364
+ ElementDragSource,
1365
+ void,
1366
+ { isSuperTypeDragOver: boolean }
1367
+ >(
1070
1368
  () => ({
1071
1369
  accept: [
1072
1370
  CORE_DND_TYPE.PROJECT_EXPLORER_CLASS,
1073
1371
  CORE_DND_TYPE.PROJECT_EXPLORER_ENUMERATION,
1074
1372
  ],
1075
- drop: (item: ElementDragSource): void => handleDropSuperType(item),
1076
- collect: (monitor): { isSuperTypeDragOver: boolean } => ({
1373
+ drop: (item) => handleDropSuperType(item),
1374
+ collect: (monitor) => ({
1077
1375
  isSuperTypeDragOver: monitor.isOver({ shallow: true }),
1078
1376
  }),
1079
1377
  }),
@@ -1085,22 +1383,28 @@ const SupertypesEditor = observer(
1085
1383
  );
1086
1384
 
1087
1385
  return (
1088
- <div
1089
- ref={dropSuperTypeRef}
1090
- className={clsx('panel__content__lists', {
1091
- 'panel__content__lists--dnd-over': isSuperTypeDragOver && !isReadOnly,
1092
- })}
1386
+ <PanelDropZone
1387
+ isDragOver={isSuperTypeDragOver && !isReadOnly}
1388
+ dropTargetConnector={dropSuperTypeRef}
1093
1389
  >
1094
- {_class.generalizations.map((superType) => (
1095
- <SuperTypeEditor
1096
- key={superType.value._UUID}
1097
- superType={superType}
1098
- _class={_class}
1099
- deleteSuperType={deleteSuperType(superType)}
1100
- isReadOnly={isReadOnly}
1390
+ <div className="panel__content__lists">
1391
+ <DragPreviewLayer
1392
+ labelGetter={(item: ClassSuperTypeDragSource): string =>
1393
+ item.superType.value.rawType.name
1394
+ }
1395
+ types={[CLASS_SUPER_TYPE_DND_TYPE]}
1101
1396
  />
1102
- ))}
1103
- </div>
1397
+ {_class.generalizations.map((superType) => (
1398
+ <SuperTypeEditor
1399
+ key={superType.value._UUID}
1400
+ superType={superType}
1401
+ _class={_class}
1402
+ deleteSuperType={deleteSuperType(superType)}
1403
+ isReadOnly={isReadOnly}
1404
+ />
1405
+ ))}
1406
+ </div>
1407
+ </PanelDropZone>
1104
1408
  );
1105
1409
  },
1106
1410
  );
@@ -1126,11 +1430,15 @@ const TaggedValuesEditor = observer(
1126
1430
  },
1127
1431
  [_class, isReadOnly],
1128
1432
  );
1129
- const [{ isTaggedValueDragOver }, dropTaggedValueRef] = useDrop(
1433
+ const [{ isTaggedValueDragOver }, dropTaggedValueRef] = useDrop<
1434
+ ElementDragSource,
1435
+ void,
1436
+ { isTaggedValueDragOver: boolean }
1437
+ >(
1130
1438
  () => ({
1131
1439
  accept: [CORE_DND_TYPE.PROJECT_EXPLORER_PROFILE],
1132
- drop: (item: ElementDragSource): void => handleDropTaggedValue(item),
1133
- collect: (monitor): { isTaggedValueDragOver: boolean } => ({
1440
+ drop: (item) => handleDropTaggedValue(item),
1441
+ collect: (monitor) => ({
1134
1442
  isTaggedValueDragOver: monitor.isOver({ shallow: true }),
1135
1443
  }),
1136
1444
  }),
@@ -1138,22 +1446,23 @@ const TaggedValuesEditor = observer(
1138
1446
  );
1139
1447
 
1140
1448
  return (
1141
- <div
1142
- ref={dropTaggedValueRef}
1143
- className={clsx('panel__content__lists', {
1144
- 'panel__content__lists--dnd-over':
1145
- isTaggedValueDragOver && !isReadOnly,
1146
- })}
1449
+ <PanelDropZone
1450
+ isDragOver={isTaggedValueDragOver && !isReadOnly}
1451
+ dropTargetConnector={dropTaggedValueRef}
1147
1452
  >
1148
- {_class.taggedValues.map((taggedValue) => (
1149
- <TaggedValueEditor
1150
- key={taggedValue._UUID}
1151
- taggedValue={taggedValue}
1152
- deleteValue={deleteTaggedValue(taggedValue)}
1153
- isReadOnly={isReadOnly}
1154
- />
1155
- ))}
1156
- </div>
1453
+ <div className="panel__content__lists">
1454
+ <TaggedValueDragPreviewLayer />
1455
+ {_class.taggedValues.map((taggedValue) => (
1456
+ <TaggedValueEditor
1457
+ annotatedElement={_class}
1458
+ key={taggedValue._UUID}
1459
+ taggedValue={taggedValue}
1460
+ deleteValue={deleteTaggedValue(taggedValue)}
1461
+ isReadOnly={isReadOnly}
1462
+ />
1463
+ ))}
1464
+ </div>
1465
+ </PanelDropZone>
1157
1466
  );
1158
1467
  },
1159
1468
  );
@@ -1181,11 +1490,15 @@ const StereotypesEditor = observer(
1181
1490
  },
1182
1491
  [_class, isReadOnly],
1183
1492
  );
1184
- const [{ isStereotypeDragOver }, dropStereotypeRef] = useDrop(
1493
+ const [{ isStereotypeDragOver }, dropStereotypeRef] = useDrop<
1494
+ ElementDragSource,
1495
+ void,
1496
+ { isStereotypeDragOver: boolean }
1497
+ >(
1185
1498
  () => ({
1186
1499
  accept: [CORE_DND_TYPE.PROJECT_EXPLORER_PROFILE],
1187
- drop: (item: ElementDragSource): void => handleDropStereotype(item),
1188
- collect: (monitor): { isStereotypeDragOver: boolean } => ({
1500
+ drop: (item) => handleDropStereotype(item),
1501
+ collect: (monitor) => ({
1189
1502
  isStereotypeDragOver: monitor.isOver({ shallow: true }),
1190
1503
  }),
1191
1504
  }),
@@ -1193,22 +1506,23 @@ const StereotypesEditor = observer(
1193
1506
  );
1194
1507
 
1195
1508
  return (
1196
- <div
1197
- ref={dropStereotypeRef}
1198
- className={clsx('panel__content__lists', {
1199
- 'panel__content__lists--dnd-over':
1200
- isStereotypeDragOver && !isReadOnly,
1201
- })}
1509
+ <PanelDropZone
1510
+ isDragOver={isStereotypeDragOver && !isReadOnly}
1511
+ dropTargetConnector={dropStereotypeRef}
1202
1512
  >
1203
- {_class.stereotypes.map((stereotype) => (
1204
- <StereotypeSelector
1205
- key={stereotype.value._UUID}
1206
- stereotype={stereotype}
1207
- deleteStereotype={deleteStereotype(stereotype)}
1208
- isReadOnly={isReadOnly}
1209
- />
1210
- ))}
1211
- </div>
1513
+ <div className="panel__content__lists">
1514
+ <StereotypeDragPreviewLayer />
1515
+ {_class.stereotypes.map((stereotype) => (
1516
+ <StereotypeSelector
1517
+ key={stereotype._UUID}
1518
+ annotatedElement={_class}
1519
+ stereotype={stereotype}
1520
+ deleteStereotype={deleteStereotype(stereotype)}
1521
+ isReadOnly={isReadOnly}
1522
+ />
1523
+ ))}
1524
+ </div>
1525
+ </PanelDropZone>
1212
1526
  );
1213
1527
  },
1214
1528
  );
@@ -1222,7 +1536,7 @@ export const ClassFormEditor = observer(
1222
1536
  const { _class, editorState, onHashChange } = props;
1223
1537
  const editorStore = useEditorStore();
1224
1538
  const applicationStore = useApplicationStore();
1225
- const classHash = editorStore.graphManagerState.isElementReadOnly(_class)
1539
+ const classHash = isElementReadOnly(_class)
1226
1540
  ? undefined
1227
1541
  : applicationStore.notifyAndReturnAlternativeOnError(
1228
1542
  () => _class.hashCode,