@nocobase/flow-engine 2.1.0-alpha.3 → 2.1.0-alpha.30

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 (160) hide show
  1. package/LICENSE +201 -661
  2. package/README.md +79 -10
  3. package/lib/JSRunner.d.ts +10 -1
  4. package/lib/JSRunner.js +50 -5
  5. package/lib/ViewScopedFlowEngine.js +5 -1
  6. package/lib/components/FieldModelRenderer.js +2 -2
  7. package/lib/components/FlowModelRenderer.d.ts +3 -1
  8. package/lib/components/FlowModelRenderer.js +12 -6
  9. package/lib/components/MobilePopup.js +6 -5
  10. package/lib/components/dnd/gridDragPlanner.d.ts +59 -2
  11. package/lib/components/dnd/gridDragPlanner.js +601 -21
  12. package/lib/components/dnd/index.d.ts +19 -1
  13. package/lib/components/dnd/index.js +243 -23
  14. package/lib/components/settings/wrappers/component/SelectWithTitle.d.ts +2 -1
  15. package/lib/components/settings/wrappers/component/SelectWithTitle.js +14 -12
  16. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.d.ts +3 -0
  17. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +68 -10
  18. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +23 -43
  19. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +352 -295
  20. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +16 -2
  21. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.d.ts +36 -0
  22. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.js +274 -0
  23. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.d.ts +30 -0
  24. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +315 -0
  25. package/lib/components/subModel/AddSubModelButton.js +27 -1
  26. package/lib/components/subModel/index.d.ts +1 -0
  27. package/lib/components/subModel/index.js +19 -0
  28. package/lib/components/subModel/utils.d.ts +1 -1
  29. package/lib/components/subModel/utils.js +2 -2
  30. package/lib/data-source/index.d.ts +73 -0
  31. package/lib/data-source/index.js +211 -1
  32. package/lib/executor/FlowExecutor.js +31 -8
  33. package/lib/flowContext.d.ts +2 -0
  34. package/lib/flowContext.js +31 -1
  35. package/lib/flowEngine.d.ts +151 -1
  36. package/lib/flowEngine.js +389 -15
  37. package/lib/flowI18n.js +2 -1
  38. package/lib/flowSettings.d.ts +14 -6
  39. package/lib/flowSettings.js +34 -6
  40. package/lib/lazy-helper.d.ts +14 -0
  41. package/lib/lazy-helper.js +71 -0
  42. package/lib/locale/en-US.json +1 -0
  43. package/lib/locale/index.d.ts +2 -0
  44. package/lib/locale/zh-CN.json +1 -0
  45. package/lib/models/DisplayItemModel.d.ts +1 -1
  46. package/lib/models/EditableItemModel.d.ts +1 -1
  47. package/lib/models/FilterableItemModel.d.ts +1 -1
  48. package/lib/models/flowModel.d.ts +13 -10
  49. package/lib/models/flowModel.js +78 -18
  50. package/lib/provider.js +38 -23
  51. package/lib/reactive/observer.js +46 -16
  52. package/lib/runjs-context/registry.d.ts +1 -1
  53. package/lib/runjs-context/setup.js +20 -12
  54. package/lib/runjs-context/snippets/index.js +13 -2
  55. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.d.ts +11 -0
  56. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.js +50 -0
  57. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.d.ts +11 -0
  58. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.js +54 -0
  59. package/lib/scheduler/ModelOperationScheduler.d.ts +5 -1
  60. package/lib/scheduler/ModelOperationScheduler.js +3 -2
  61. package/lib/types.d.ts +47 -1
  62. package/lib/utils/createCollectionContextMeta.js +6 -2
  63. package/lib/utils/index.d.ts +2 -2
  64. package/lib/utils/index.js +4 -0
  65. package/lib/utils/parsePathnameToViewParams.js +1 -1
  66. package/lib/utils/runjsTemplateCompat.js +1 -1
  67. package/lib/utils/runjsValue.js +41 -11
  68. package/lib/utils/schema-utils.d.ts +7 -1
  69. package/lib/utils/schema-utils.js +19 -0
  70. package/lib/views/FlowView.d.ts +7 -1
  71. package/lib/views/runViewBeforeClose.d.ts +10 -0
  72. package/lib/views/runViewBeforeClose.js +45 -0
  73. package/lib/views/useDialog.d.ts +2 -1
  74. package/lib/views/useDialog.js +20 -3
  75. package/lib/views/useDrawer.d.ts +2 -1
  76. package/lib/views/useDrawer.js +20 -3
  77. package/lib/views/usePage.d.ts +2 -1
  78. package/lib/views/usePage.js +10 -3
  79. package/package.json +6 -5
  80. package/src/JSRunner.ts +68 -4
  81. package/src/ViewScopedFlowEngine.ts +4 -0
  82. package/src/__tests__/JSRunner.test.ts +27 -1
  83. package/src/__tests__/flow-engine.test.ts +166 -0
  84. package/src/__tests__/flowContext.test.ts +65 -1
  85. package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
  86. package/src/__tests__/flowSettings.test.ts +94 -15
  87. package/src/__tests__/objectVariable.test.ts +24 -0
  88. package/src/__tests__/provider.test.tsx +24 -2
  89. package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
  90. package/src/__tests__/runjsContext.test.ts +16 -0
  91. package/src/__tests__/runjsContextRuntime.test.ts +2 -0
  92. package/src/__tests__/runjsPreprocessDefault.test.ts +23 -0
  93. package/src/__tests__/runjsSnippets.test.ts +21 -0
  94. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  95. package/src/components/FieldModelRenderer.tsx +2 -1
  96. package/src/components/FlowModelRenderer.tsx +18 -6
  97. package/src/components/MobilePopup.tsx +4 -2
  98. package/src/components/__tests__/FlowModelRenderer.test.tsx +65 -2
  99. package/src/components/__tests__/dnd.test.ts +44 -0
  100. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +20 -10
  101. package/src/components/__tests__/gridDragPlanner.test.ts +512 -3
  102. package/src/components/dnd/__tests__/DndProvider.test.tsx +98 -0
  103. package/src/components/dnd/gridDragPlanner.ts +743 -19
  104. package/src/components/dnd/index.tsx +291 -27
  105. package/src/components/settings/wrappers/component/SelectWithTitle.tsx +21 -9
  106. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +88 -10
  107. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +487 -440
  108. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +18 -2
  109. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +189 -3
  110. package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +778 -0
  111. package/src/components/settings/wrappers/contextual/useFloatToolbarPortal.ts +360 -0
  112. package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +361 -0
  113. package/src/components/subModel/AddSubModelButton.tsx +32 -2
  114. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +142 -32
  115. package/src/components/subModel/index.ts +1 -0
  116. package/src/components/subModel/utils.ts +1 -1
  117. package/src/data-source/__tests__/index.test.ts +34 -1
  118. package/src/data-source/index.ts +258 -2
  119. package/src/executor/FlowExecutor.ts +34 -9
  120. package/src/executor/__tests__/flowExecutor.test.ts +57 -0
  121. package/src/flowContext.ts +37 -3
  122. package/src/flowEngine.ts +445 -11
  123. package/src/flowI18n.ts +2 -1
  124. package/src/flowSettings.ts +40 -6
  125. package/src/lazy-helper.tsx +57 -0
  126. package/src/locale/en-US.json +1 -0
  127. package/src/locale/zh-CN.json +1 -0
  128. package/src/models/DisplayItemModel.tsx +1 -1
  129. package/src/models/EditableItemModel.tsx +1 -1
  130. package/src/models/FilterableItemModel.tsx +1 -1
  131. package/src/models/__tests__/dispatchEvent.when.test.ts +214 -0
  132. package/src/models/__tests__/flowModel.test.ts +19 -3
  133. package/src/models/flowModel.tsx +119 -33
  134. package/src/provider.tsx +41 -25
  135. package/src/reactive/__tests__/observer.test.tsx +82 -0
  136. package/src/reactive/observer.tsx +87 -25
  137. package/src/runjs-context/registry.ts +1 -1
  138. package/src/runjs-context/setup.ts +22 -12
  139. package/src/runjs-context/snippets/index.ts +12 -1
  140. package/src/runjs-context/snippets/scene/detail/set-field-style.snippet.ts +30 -0
  141. package/src/runjs-context/snippets/scene/table/set-cell-style.snippet.ts +34 -0
  142. package/src/scheduler/ModelOperationScheduler.ts +14 -3
  143. package/src/types.ts +60 -0
  144. package/src/utils/__tests__/createCollectionContextMeta.test.ts +48 -0
  145. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +7 -0
  146. package/src/utils/__tests__/runjsValue.test.ts +11 -0
  147. package/src/utils/__tests__/utils.test.ts +62 -0
  148. package/src/utils/createCollectionContextMeta.ts +6 -2
  149. package/src/utils/index.ts +2 -1
  150. package/src/utils/parsePathnameToViewParams.ts +2 -2
  151. package/src/utils/runjsTemplateCompat.ts +1 -1
  152. package/src/utils/runjsValue.ts +50 -11
  153. package/src/utils/schema-utils.ts +30 -1
  154. package/src/views/FlowView.tsx +11 -1
  155. package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
  156. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +13 -12
  157. package/src/views/runViewBeforeClose.ts +19 -0
  158. package/src/views/useDialog.tsx +25 -3
  159. package/src/views/useDrawer.tsx +25 -3
  160. package/src/views/usePage.tsx +12 -3
@@ -0,0 +1,361 @@
1
+ /**
2
+ * This file is part of the NocoBase (R) project.
3
+ * Copyright (c) 2020-2024 NocoBase Co., Ltd.
4
+ * Authors: NocoBase Team.
5
+ *
6
+ * This project is dual-licensed under AGPL-3.0 and NocoBase Commercial License.
7
+ * For more information, please refer to: https://www.nocobase.com/agreement.
8
+ */
9
+
10
+ import { useCallback, useEffect, useRef, useState } from 'react';
11
+ import type { MouseEvent as ReactMouseEvent, RefObject } from 'react';
12
+ import { TOOLBAR_DRAG_ACTIVITY_EVENT } from '../../../dnd';
13
+
14
+ const TOOLBAR_HIDE_DELAY = 180;
15
+ const CHILD_FLOAT_MENU_ACTIVITY_EVENT = 'nb-float-menu-child-activity';
16
+
17
+ interface UseFloatToolbarVisibilityOptions {
18
+ modelUid: string;
19
+ containerRef: RefObject<HTMLDivElement>;
20
+ toolbarContainerRef: RefObject<HTMLDivElement>;
21
+ updatePortalRect: () => void;
22
+ schedulePortalRectUpdate: () => void;
23
+ }
24
+
25
+ interface UseFloatToolbarVisibilityResult {
26
+ isToolbarVisible: boolean;
27
+ shouldRenderToolbar: boolean;
28
+ handleSettingsMenuOpenChange: (open: boolean) => void;
29
+ handleChildHover: (e: ReactMouseEvent) => void;
30
+ handleHostMouseEnter: () => void;
31
+ handleHostMouseLeave: (e: ReactMouseEvent<HTMLDivElement>) => void;
32
+ handleToolbarMouseEnter: () => void;
33
+ handleToolbarMouseLeave: (e: ReactMouseEvent<HTMLDivElement>) => void;
34
+ handleResizeDragStart: () => void;
35
+ handleResizeDragEnd: () => void;
36
+ }
37
+
38
+ const isNodeWithin = (target: EventTarget | null, container: HTMLElement | null): boolean => {
39
+ return target instanceof Node && !!container?.contains(target);
40
+ };
41
+
42
+ const getToolbarModelUidFromTarget = (target: EventTarget | null): string | null => {
43
+ if (!(target instanceof Element)) {
44
+ return null;
45
+ }
46
+
47
+ return target.closest('.nb-toolbar-container[data-model-uid]')?.getAttribute('data-model-uid') || null;
48
+ };
49
+
50
+ const isNodeWithinDescendantFloatToolbar = (
51
+ target: EventTarget | null,
52
+ container: HTMLElement | null,
53
+ currentModelUid: string,
54
+ ): boolean => {
55
+ const targetModelUid = getToolbarModelUidFromTarget(target);
56
+ if (!container || !targetModelUid || targetModelUid === currentModelUid) {
57
+ return false;
58
+ }
59
+
60
+ return Array.from(
61
+ container.querySelectorAll<HTMLElement>('[data-has-float-menu="true"][data-float-menu-model-uid]'),
62
+ ).some(
63
+ (hostElement) =>
64
+ hostElement !== container && hostElement.getAttribute('data-float-menu-model-uid') === targetModelUid,
65
+ );
66
+ };
67
+
68
+ export const useFloatToolbarVisibility = ({
69
+ modelUid,
70
+ containerRef,
71
+ toolbarContainerRef,
72
+ updatePortalRect,
73
+ schedulePortalRectUpdate,
74
+ }: UseFloatToolbarVisibilityOptions): UseFloatToolbarVisibilityResult => {
75
+ const [hideMenu, setHideMenu] = useState(false);
76
+ const [isHostHovered, setIsHostHovered] = useState(false);
77
+ const [isToolbarHovered, setIsToolbarHovered] = useState(false);
78
+ const [isDraggingToolbar, setIsDraggingToolbar] = useState(false);
79
+ const [isDraggingToolbarItem, setIsDraggingToolbarItem] = useState(false);
80
+ const [isToolbarPinned, setIsToolbarPinned] = useState(false);
81
+ const [isHidePending, setIsHidePending] = useState(false);
82
+ const [activeChildToolbarIds, setActiveChildToolbarIds] = useState<string[]>([]);
83
+ const hideToolbarTimerRef = useRef<number | null>(null);
84
+ const reportedChildActivityToAncestorsRef = useRef(false);
85
+ const isHostHoveredRef = useRef(false);
86
+ const isToolbarHoveredRef = useRef(false);
87
+ const isDraggingToolbarRef = useRef(false);
88
+ const isDraggingToolbarItemRef = useRef(false);
89
+ const isToolbarPinnedRef = useRef(false);
90
+
91
+ const setHostHovered = useCallback((value: boolean) => {
92
+ isHostHoveredRef.current = value;
93
+ setIsHostHovered(value);
94
+ }, []);
95
+
96
+ const setToolbarHovered = useCallback((value: boolean) => {
97
+ isToolbarHoveredRef.current = value;
98
+ setIsToolbarHovered(value);
99
+ }, []);
100
+
101
+ const setDraggingToolbar = useCallback((value: boolean) => {
102
+ isDraggingToolbarRef.current = value;
103
+ setIsDraggingToolbar(value);
104
+ }, []);
105
+
106
+ const setDraggingToolbarItem = useCallback((value: boolean) => {
107
+ isDraggingToolbarItemRef.current = value;
108
+ setIsDraggingToolbarItem(value);
109
+ }, []);
110
+
111
+ const setToolbarPinned = useCallback((value: boolean) => {
112
+ isToolbarPinnedRef.current = value;
113
+ setIsToolbarPinned(value);
114
+ }, []);
115
+
116
+ const hasActiveChildToolbar = activeChildToolbarIds.length > 0;
117
+ const isToolbarVisible =
118
+ !hideMenu &&
119
+ !hasActiveChildToolbar &&
120
+ (isHostHovered || isToolbarHovered || isDraggingToolbar || isDraggingToolbarItem || isToolbarPinned);
121
+ const shouldRenderToolbar = isToolbarVisible || isToolbarPinned || isDraggingToolbar || isDraggingToolbarItem;
122
+ const isToolbarInteractionActive =
123
+ isHostHovered || isToolbarHovered || isDraggingToolbar || isDraggingToolbarItem || isToolbarPinned || isHidePending;
124
+
125
+ const clearHideToolbarTimer = useCallback(() => {
126
+ if (hideToolbarTimerRef.current !== null) {
127
+ window.clearTimeout(hideToolbarTimerRef.current);
128
+ hideToolbarTimerRef.current = null;
129
+ }
130
+ setIsHidePending(false);
131
+ }, []);
132
+
133
+ const scheduleHideToolbar = useCallback(() => {
134
+ clearHideToolbarTimer();
135
+ setIsHidePending(true);
136
+ hideToolbarTimerRef.current = window.setTimeout(() => {
137
+ hideToolbarTimerRef.current = null;
138
+ setIsHidePending(false);
139
+ if (isDraggingToolbarRef.current || isDraggingToolbarItemRef.current || isToolbarPinnedRef.current) {
140
+ return;
141
+ }
142
+ setHostHovered(false);
143
+ setToolbarHovered(false);
144
+ }, TOOLBAR_HIDE_DELAY);
145
+ }, [clearHideToolbarTimer, setHostHovered, setToolbarHovered]);
146
+
147
+ const handleSettingsMenuOpenChange = useCallback(
148
+ (open: boolean) => {
149
+ setToolbarPinned(open);
150
+ },
151
+ [setToolbarPinned],
152
+ );
153
+
154
+ useEffect(() => {
155
+ const hostElement = containerRef.current;
156
+ if (!hostElement) {
157
+ return;
158
+ }
159
+
160
+ const handleChildToolbarActivity = (event: Event) => {
161
+ const customEvent = event as CustomEvent<{ active?: boolean; modelUid?: string }>;
162
+ if (!(customEvent.target instanceof HTMLElement) || customEvent.target === hostElement) {
163
+ return;
164
+ }
165
+
166
+ const childModelUid = customEvent.detail?.modelUid;
167
+ if (!childModelUid) {
168
+ return;
169
+ }
170
+
171
+ setActiveChildToolbarIds((prevIds) => {
172
+ return customEvent.detail?.active
173
+ ? prevIds.includes(childModelUid)
174
+ ? prevIds
175
+ : [...prevIds, childModelUid]
176
+ : prevIds.filter((id) => id !== childModelUid);
177
+ });
178
+ };
179
+
180
+ hostElement.addEventListener(CHILD_FLOAT_MENU_ACTIVITY_EVENT, handleChildToolbarActivity as EventListener);
181
+ return () => {
182
+ hostElement.removeEventListener(CHILD_FLOAT_MENU_ACTIVITY_EVENT, handleChildToolbarActivity as EventListener);
183
+ };
184
+ }, [containerRef]);
185
+
186
+ useEffect(() => {
187
+ const hostElement = containerRef.current;
188
+ const ownerDocument = hostElement?.ownerDocument;
189
+ if (!ownerDocument) {
190
+ return;
191
+ }
192
+
193
+ const handleToolbarDragActivity = (event: Event) => {
194
+ const customEvent = event as CustomEvent<{ active?: boolean; modelUid?: string }>;
195
+ if (customEvent.detail?.modelUid !== modelUid) {
196
+ return;
197
+ }
198
+
199
+ if (customEvent.detail?.active) {
200
+ clearHideToolbarTimer();
201
+ setDraggingToolbarItem(true);
202
+ return;
203
+ }
204
+
205
+ setDraggingToolbarItem(false);
206
+ if (isHostHoveredRef.current || isToolbarHoveredRef.current || isToolbarPinnedRef.current) {
207
+ clearHideToolbarTimer();
208
+ return;
209
+ }
210
+
211
+ scheduleHideToolbar();
212
+ };
213
+
214
+ ownerDocument.addEventListener(TOOLBAR_DRAG_ACTIVITY_EVENT, handleToolbarDragActivity as EventListener);
215
+ return () => {
216
+ ownerDocument.removeEventListener(TOOLBAR_DRAG_ACTIVITY_EVENT, handleToolbarDragActivity as EventListener);
217
+ };
218
+ }, [clearHideToolbarTimer, containerRef, modelUid, scheduleHideToolbar, setDraggingToolbarItem]);
219
+
220
+ useEffect(() => {
221
+ const hostElement = containerRef.current;
222
+ if (!hostElement || reportedChildActivityToAncestorsRef.current === isToolbarInteractionActive) {
223
+ return;
224
+ }
225
+
226
+ reportedChildActivityToAncestorsRef.current = isToolbarInteractionActive;
227
+ hostElement.dispatchEvent(
228
+ new CustomEvent(CHILD_FLOAT_MENU_ACTIVITY_EVENT, {
229
+ bubbles: true,
230
+ detail: { active: isToolbarInteractionActive, modelUid },
231
+ }),
232
+ );
233
+ }, [containerRef, isToolbarInteractionActive, modelUid]);
234
+
235
+ useEffect(() => {
236
+ const hostElement = containerRef.current;
237
+
238
+ return () => {
239
+ if (hostElement && reportedChildActivityToAncestorsRef.current) {
240
+ hostElement.dispatchEvent(
241
+ new CustomEvent(CHILD_FLOAT_MENU_ACTIVITY_EVENT, {
242
+ bubbles: true,
243
+ detail: { active: false, modelUid },
244
+ }),
245
+ );
246
+ reportedChildActivityToAncestorsRef.current = false;
247
+ }
248
+ clearHideToolbarTimer();
249
+ };
250
+ }, [clearHideToolbarTimer, containerRef, modelUid]);
251
+
252
+ useEffect(() => {
253
+ if (isToolbarPinned) {
254
+ clearHideToolbarTimer();
255
+ updatePortalRect();
256
+ }
257
+ }, [clearHideToolbarTimer, isToolbarPinned, updatePortalRect]);
258
+
259
+ const handleChildHover = useCallback(
260
+ (e: ReactMouseEvent) => {
261
+ const target = e.target as HTMLElement;
262
+ const childWithMenu = target.closest('[data-has-float-menu]');
263
+ const isCurrentHostTarget = !childWithMenu || childWithMenu === containerRef.current;
264
+
265
+ if (isCurrentHostTarget) {
266
+ clearHideToolbarTimer();
267
+ setHostHovered(true);
268
+ }
269
+
270
+ setHideMenu(!!childWithMenu && childWithMenu !== containerRef.current);
271
+ },
272
+ [clearHideToolbarTimer, containerRef, setHostHovered],
273
+ );
274
+
275
+ const handleHostMouseEnter = useCallback(() => {
276
+ clearHideToolbarTimer();
277
+ setHideMenu(false);
278
+ updatePortalRect();
279
+ setHostHovered(true);
280
+ }, [clearHideToolbarTimer, setHostHovered, updatePortalRect]);
281
+
282
+ const handleHostMouseLeave = useCallback(
283
+ (e: ReactMouseEvent<HTMLDivElement>) => {
284
+ if (isToolbarPinnedRef.current) {
285
+ setHostHovered(false);
286
+ return;
287
+ }
288
+ if (isNodeWithin(e.relatedTarget, toolbarContainerRef.current)) {
289
+ clearHideToolbarTimer();
290
+ setHostHovered(false);
291
+ setToolbarHovered(true);
292
+ return;
293
+ }
294
+ if (isNodeWithinDescendantFloatToolbar(e.relatedTarget, containerRef.current, modelUid)) {
295
+ clearHideToolbarTimer();
296
+ setHideMenu(false);
297
+ setHostHovered(true);
298
+ return;
299
+ }
300
+ scheduleHideToolbar();
301
+ },
302
+ [
303
+ clearHideToolbarTimer,
304
+ containerRef,
305
+ modelUid,
306
+ scheduleHideToolbar,
307
+ setHostHovered,
308
+ setToolbarHovered,
309
+ toolbarContainerRef,
310
+ ],
311
+ );
312
+
313
+ const handleToolbarMouseEnter = useCallback(() => {
314
+ clearHideToolbarTimer();
315
+ updatePortalRect();
316
+ setHostHovered(false);
317
+ setToolbarHovered(true);
318
+ }, [clearHideToolbarTimer, setHostHovered, setToolbarHovered, updatePortalRect]);
319
+
320
+ const handleToolbarMouseLeave = useCallback(
321
+ (e: ReactMouseEvent<HTMLDivElement>) => {
322
+ if (isToolbarPinnedRef.current || isDraggingToolbarItemRef.current) {
323
+ clearHideToolbarTimer();
324
+ setToolbarHovered(false);
325
+ return;
326
+ }
327
+ setToolbarHovered(false);
328
+ if (isNodeWithin(e.relatedTarget, containerRef.current)) {
329
+ clearHideToolbarTimer();
330
+ setHostHovered(true);
331
+ return;
332
+ }
333
+ scheduleHideToolbar();
334
+ },
335
+ [clearHideToolbarTimer, containerRef, scheduleHideToolbar, setHostHovered, setToolbarHovered],
336
+ );
337
+
338
+ const handleResizeDragStart = useCallback(() => {
339
+ updatePortalRect();
340
+ setDraggingToolbar(true);
341
+ schedulePortalRectUpdate();
342
+ }, [schedulePortalRectUpdate, setDraggingToolbar, updatePortalRect]);
343
+
344
+ const handleResizeDragEnd = useCallback(() => {
345
+ setDraggingToolbar(false);
346
+ schedulePortalRectUpdate();
347
+ }, [schedulePortalRectUpdate, setDraggingToolbar]);
348
+
349
+ return {
350
+ isToolbarVisible,
351
+ shouldRenderToolbar,
352
+ handleSettingsMenuOpenChange,
353
+ handleChildHover,
354
+ handleHostMouseEnter,
355
+ handleHostMouseLeave,
356
+ handleToolbarMouseEnter,
357
+ handleToolbarMouseLeave,
358
+ handleResizeDragStart,
359
+ handleResizeDragEnd,
360
+ };
361
+ };
@@ -9,7 +9,7 @@
9
9
 
10
10
  import { Switch } from 'antd';
11
11
  import _ from 'lodash';
12
- import React, { useMemo } from 'react';
12
+ import React, { useEffect, useMemo } from 'react';
13
13
  import { FlowModelContext } from '../../flowContext';
14
14
  import { FlowModel } from '../../models';
15
15
  import { CreateModelOptions, ModelConstructor } from '../../types';
@@ -542,6 +542,22 @@ const AddSubModelButtonCore = function AddSubModelButton({
542
542
  [model, subModelKey, subModelType],
543
543
  );
544
544
 
545
+ React.useEffect(() => {
546
+ const handleSubModelChanged = () => {
547
+ setRefreshTick((x) => x + 1);
548
+ };
549
+
550
+ model.emitter?.on('onSubModelAdded', handleSubModelChanged);
551
+ model.emitter?.on('onSubModelRemoved', handleSubModelChanged);
552
+ model.emitter?.on('onSubModelReplaced', handleSubModelChanged);
553
+
554
+ return () => {
555
+ model.emitter?.off('onSubModelAdded', handleSubModelChanged);
556
+ model.emitter?.off('onSubModelRemoved', handleSubModelChanged);
557
+ model.emitter?.off('onSubModelReplaced', handleSubModelChanged);
558
+ };
559
+ }, [model]);
560
+
545
561
  // 点击处理逻辑
546
562
  const onClick = async (info: any) => {
547
563
  const clickedItem = info.originalItem || info;
@@ -594,7 +610,7 @@ const AddSubModelButtonCore = function AddSubModelButton({
594
610
  let addedModel: FlowModel | undefined;
595
611
 
596
612
  try {
597
- addedModel = model.flowEngine.createModel({
613
+ addedModel = await model.flowEngine.createModelAsync({
598
614
  ..._.cloneDeep(createOpts),
599
615
  parentId: model.uid,
600
616
  subKey: subModelKey,
@@ -651,6 +667,20 @@ const AddSubModelButtonCore = function AddSubModelButton({
651
667
  [finalItems, model, subModelKey, subModelType],
652
668
  );
653
669
 
670
+ useEffect(() => {
671
+ const handleSubModelChange = () => {
672
+ setRefreshTick((x) => x + 1);
673
+ };
674
+
675
+ model.emitter.on('onSubModelAdded', handleSubModelChange);
676
+ model.emitter.on('onSubModelRemoved', handleSubModelChange);
677
+
678
+ return () => {
679
+ model.emitter.off('onSubModelAdded', handleSubModelChange);
680
+ model.emitter.off('onSubModelRemoved', handleSubModelChange);
681
+ };
682
+ }, [model]);
683
+
654
684
  return (
655
685
  <LazyDropdown
656
686
  menu={{