@nocobase/flow-engine 2.1.0-beta.9 → 2.2.0-alpha.1

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 (215) hide show
  1. package/lib/FlowContextProvider.d.ts +5 -1
  2. package/lib/FlowContextProvider.js +9 -2
  3. package/lib/components/FieldModelRenderer.js +2 -2
  4. package/lib/components/FlowModelRenderer.d.ts +3 -1
  5. package/lib/components/FlowModelRenderer.js +12 -6
  6. package/lib/components/FormItem.d.ts +6 -0
  7. package/lib/components/FormItem.js +11 -3
  8. package/lib/components/MobilePopup.js +6 -5
  9. package/lib/components/dnd/gridDragPlanner.d.ts +59 -2
  10. package/lib/components/dnd/gridDragPlanner.js +607 -19
  11. package/lib/components/dnd/index.d.ts +31 -2
  12. package/lib/components/dnd/index.js +244 -23
  13. package/lib/components/settings/wrappers/component/SelectWithTitle.d.ts +2 -1
  14. package/lib/components/settings/wrappers/component/SelectWithTitle.js +14 -12
  15. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.d.ts +3 -0
  16. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +152 -42
  17. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +23 -43
  18. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +352 -295
  19. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.d.ts +36 -0
  20. package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.js +274 -0
  21. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.d.ts +30 -0
  22. package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +315 -0
  23. package/lib/components/subModel/AddSubModelButton.js +12 -1
  24. package/lib/components/subModel/LazyDropdown.js +301 -52
  25. package/lib/components/subModel/index.d.ts +1 -0
  26. package/lib/components/subModel/index.js +19 -0
  27. package/lib/components/subModel/utils.d.ts +2 -1
  28. package/lib/components/subModel/utils.js +15 -5
  29. package/lib/components/variables/VariableHybridInput.d.ts +27 -0
  30. package/lib/components/variables/VariableHybridInput.js +499 -0
  31. package/lib/components/variables/index.d.ts +2 -0
  32. package/lib/components/variables/index.js +3 -0
  33. package/lib/data-source/index.d.ts +84 -0
  34. package/lib/data-source/index.js +269 -7
  35. package/lib/executor/FlowExecutor.js +6 -3
  36. package/lib/flow-registry/DetachedFlowRegistry.d.ts +21 -0
  37. package/lib/flow-registry/DetachedFlowRegistry.js +80 -0
  38. package/lib/flow-registry/index.d.ts +1 -0
  39. package/lib/flow-registry/index.js +3 -1
  40. package/lib/flowContext.d.ts +9 -1
  41. package/lib/flowContext.js +77 -6
  42. package/lib/flowEngine.d.ts +136 -4
  43. package/lib/flowEngine.js +429 -51
  44. package/lib/flowI18n.js +2 -1
  45. package/lib/flowSettings.d.ts +14 -6
  46. package/lib/flowSettings.js +34 -6
  47. package/lib/index.d.ts +2 -0
  48. package/lib/index.js +7 -0
  49. package/lib/lazy-helper.d.ts +14 -0
  50. package/lib/lazy-helper.js +71 -0
  51. package/lib/locale/en-US.json +1 -0
  52. package/lib/locale/index.d.ts +2 -0
  53. package/lib/locale/zh-CN.json +1 -0
  54. package/lib/models/DisplayItemModel.d.ts +1 -1
  55. package/lib/models/EditableItemModel.d.ts +1 -1
  56. package/lib/models/FilterableItemModel.d.ts +1 -1
  57. package/lib/models/flowModel.d.ts +13 -10
  58. package/lib/models/flowModel.js +126 -34
  59. package/lib/provider.js +38 -23
  60. package/lib/reactive/observer.js +46 -16
  61. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +4 -3
  62. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +4 -15
  63. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +5 -2
  64. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +5 -8
  65. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +4 -3
  66. package/lib/runjs-context/contexts/JSItemRunJSContext.js +4 -3
  67. package/lib/runjs-context/contexts/base.js +464 -29
  68. package/lib/runjs-context/contexts/elementDoc.d.ts +11 -0
  69. package/lib/runjs-context/contexts/elementDoc.js +152 -0
  70. package/lib/runjs-context/setup.js +1 -0
  71. package/lib/runjs-context/snippets/index.js +13 -2
  72. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.d.ts +11 -0
  73. package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.js +50 -0
  74. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.d.ts +11 -0
  75. package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.js +54 -0
  76. package/lib/types.d.ts +50 -2
  77. package/lib/types.js +1 -0
  78. package/lib/utils/createCollectionContextMeta.js +6 -2
  79. package/lib/utils/index.d.ts +3 -2
  80. package/lib/utils/index.js +7 -0
  81. package/lib/utils/loadedPageCache.d.ts +24 -0
  82. package/lib/utils/loadedPageCache.js +139 -0
  83. package/lib/utils/parsePathnameToViewParams.d.ts +5 -1
  84. package/lib/utils/parsePathnameToViewParams.js +28 -4
  85. package/lib/utils/randomId.d.ts +39 -0
  86. package/lib/utils/randomId.js +45 -0
  87. package/lib/utils/runjsTemplateCompat.js +1 -1
  88. package/lib/utils/runjsValue.js +41 -11
  89. package/lib/utils/schema-utils.d.ts +7 -1
  90. package/lib/utils/schema-utils.js +19 -0
  91. package/lib/views/FlowView.d.ts +7 -1
  92. package/lib/views/FlowView.js +11 -1
  93. package/lib/views/PageComponent.js +8 -6
  94. package/lib/views/ViewNavigation.d.ts +12 -2
  95. package/lib/views/ViewNavigation.js +28 -9
  96. package/lib/views/createViewMeta.js +114 -50
  97. package/lib/views/inheritLayoutContext.d.ts +10 -0
  98. package/lib/views/inheritLayoutContext.js +50 -0
  99. package/lib/views/runViewBeforeClose.d.ts +10 -0
  100. package/lib/views/runViewBeforeClose.js +45 -0
  101. package/lib/views/useDialog.d.ts +2 -1
  102. package/lib/views/useDialog.js +12 -3
  103. package/lib/views/useDrawer.d.ts +2 -1
  104. package/lib/views/useDrawer.js +12 -3
  105. package/lib/views/usePage.d.ts +5 -11
  106. package/lib/views/usePage.js +304 -144
  107. package/package.json +5 -4
  108. package/src/FlowContextProvider.tsx +9 -1
  109. package/src/__tests__/createViewMeta.popup.test.ts +115 -1
  110. package/src/__tests__/flow-engine.test.ts +166 -0
  111. package/src/__tests__/flowContext.test.ts +105 -1
  112. package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
  113. package/src/__tests__/flowEngine.moveModel.test.ts +81 -1
  114. package/src/__tests__/flowEngine.removeModel.test.ts +47 -3
  115. package/src/__tests__/flowSettings.test.ts +94 -15
  116. package/src/__tests__/objectVariable.test.ts +24 -0
  117. package/src/__tests__/provider.test.tsx +24 -2
  118. package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
  119. package/src/__tests__/runjsContext.test.ts +21 -0
  120. package/src/__tests__/runjsContextImplementations.test.ts +9 -2
  121. package/src/__tests__/runjsContextRuntime.test.ts +2 -0
  122. package/src/__tests__/runjsLocales.test.ts +6 -5
  123. package/src/__tests__/runjsSnippets.test.ts +21 -0
  124. package/src/__tests__/viewScopedFlowEngine.test.ts +136 -3
  125. package/src/components/FieldModelRenderer.tsx +2 -1
  126. package/src/components/FlowModelRenderer.tsx +18 -6
  127. package/src/components/FormItem.tsx +7 -1
  128. package/src/components/MobilePopup.tsx +4 -2
  129. package/src/components/__tests__/FlowModelRenderer.test.tsx +65 -2
  130. package/src/components/__tests__/FormItem.test.tsx +25 -0
  131. package/src/components/__tests__/dnd.test.ts +44 -0
  132. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +20 -10
  133. package/src/components/__tests__/gridDragPlanner.test.ts +472 -5
  134. package/src/components/dnd/__tests__/DndProvider.test.tsx +98 -0
  135. package/src/components/dnd/gridDragPlanner.ts +750 -17
  136. package/src/components/dnd/index.tsx +305 -28
  137. package/src/components/settings/wrappers/component/SelectWithTitle.tsx +21 -9
  138. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +178 -48
  139. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +487 -440
  140. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +344 -8
  141. package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +778 -0
  142. package/src/components/settings/wrappers/contextual/useFloatToolbarPortal.ts +360 -0
  143. package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +361 -0
  144. package/src/components/subModel/AddSubModelButton.tsx +16 -2
  145. package/src/components/subModel/LazyDropdown.tsx +341 -56
  146. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +524 -38
  147. package/src/components/subModel/__tests__/utils.test.ts +24 -0
  148. package/src/components/subModel/index.ts +1 -0
  149. package/src/components/subModel/utils.ts +13 -2
  150. package/src/components/variables/VariableHybridInput.tsx +531 -0
  151. package/src/components/variables/index.ts +2 -0
  152. package/src/data-source/__tests__/collection.test.ts +41 -2
  153. package/src/data-source/__tests__/index.test.ts +69 -2
  154. package/src/data-source/index.ts +332 -8
  155. package/src/executor/FlowExecutor.ts +6 -3
  156. package/src/executor/__tests__/flowExecutor.test.ts +57 -0
  157. package/src/flow-registry/DetachedFlowRegistry.ts +46 -0
  158. package/src/flow-registry/__tests__/detachedFlowRegistry.test.ts +47 -0
  159. package/src/flow-registry/index.ts +1 -0
  160. package/src/flowContext.ts +85 -6
  161. package/src/flowEngine.ts +484 -45
  162. package/src/flowI18n.ts +2 -1
  163. package/src/flowSettings.ts +40 -6
  164. package/src/index.ts +2 -0
  165. package/src/lazy-helper.tsx +57 -0
  166. package/src/locale/en-US.json +1 -0
  167. package/src/locale/zh-CN.json +1 -0
  168. package/src/models/DisplayItemModel.tsx +1 -1
  169. package/src/models/EditableItemModel.tsx +1 -1
  170. package/src/models/FilterableItemModel.tsx +1 -1
  171. package/src/models/__tests__/flowEngine.resolveUse.test.ts +0 -15
  172. package/src/models/__tests__/flowModel.test.ts +65 -37
  173. package/src/models/flowModel.tsx +184 -65
  174. package/src/provider.tsx +41 -25
  175. package/src/reactive/__tests__/observer.test.tsx +82 -0
  176. package/src/reactive/observer.tsx +87 -25
  177. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +4 -3
  178. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +4 -15
  179. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +4 -2
  180. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +5 -9
  181. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +4 -3
  182. package/src/runjs-context/contexts/JSItemRunJSContext.ts +4 -3
  183. package/src/runjs-context/contexts/base.ts +467 -31
  184. package/src/runjs-context/contexts/elementDoc.ts +130 -0
  185. package/src/runjs-context/setup.ts +1 -0
  186. package/src/runjs-context/snippets/index.ts +12 -1
  187. package/src/runjs-context/snippets/scene/detail/set-field-style.snippet.ts +30 -0
  188. package/src/runjs-context/snippets/scene/table/set-cell-style.snippet.ts +34 -0
  189. package/src/types.ts +62 -0
  190. package/src/utils/__tests__/createCollectionContextMeta.test.ts +48 -0
  191. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +21 -0
  192. package/src/utils/__tests__/runjsValue.test.ts +11 -0
  193. package/src/utils/__tests__/utils.test.ts +62 -0
  194. package/src/utils/createCollectionContextMeta.ts +6 -2
  195. package/src/utils/index.ts +5 -1
  196. package/src/utils/loadedPageCache.ts +147 -0
  197. package/src/utils/parsePathnameToViewParams.ts +45 -5
  198. package/src/utils/randomId.ts +48 -0
  199. package/src/utils/runjsTemplateCompat.ts +1 -1
  200. package/src/utils/runjsValue.ts +50 -11
  201. package/src/utils/schema-utils.ts +30 -1
  202. package/src/views/FlowView.tsx +22 -2
  203. package/src/views/PageComponent.tsx +7 -4
  204. package/src/views/ViewNavigation.ts +46 -9
  205. package/src/views/__tests__/FlowView.usePage.test.tsx +243 -3
  206. package/src/views/__tests__/ViewNavigation.test.ts +52 -0
  207. package/src/views/__tests__/inheritLayoutContext.test.ts +53 -0
  208. package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
  209. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +12 -12
  210. package/src/views/createViewMeta.ts +106 -34
  211. package/src/views/inheritLayoutContext.ts +26 -0
  212. package/src/views/runViewBeforeClose.ts +19 -0
  213. package/src/views/useDialog.tsx +13 -3
  214. package/src/views/useDrawer.tsx +13 -3
  215. package/src/views/usePage.tsx +367 -180
@@ -41,6 +41,7 @@ __export(DefaultSettingsIcon_exports, {
41
41
  });
42
42
  module.exports = __toCommonJS(DefaultSettingsIcon_exports);
43
43
  var import_icons = require("@ant-design/icons");
44
+ var import_css = require("@emotion/css");
44
45
  var import_antd = require("antd");
45
46
  var import_react = __toESM(require("react"));
46
47
  var import_models = require("../../../../models");
@@ -48,6 +49,22 @@ var import_utils = require("../../../../utils");
48
49
  var import_hooks = require("../../../../hooks");
49
50
  var import_SwitchWithTitle = require("../component/SwitchWithTitle");
50
51
  var import_SelectWithTitle = require("../component/SelectWithTitle");
52
+ const findExtraMenuItemByKey = /* @__PURE__ */ __name((items, targetKey) => {
53
+ var _a;
54
+ for (const item of items) {
55
+ const itemKey = String((item == null ? void 0 : item.key) ?? "");
56
+ if (itemKey === targetKey) {
57
+ return item;
58
+ }
59
+ if ((_a = item.children) == null ? void 0 : _a.length) {
60
+ const matched = findExtraMenuItemByKey(item.children, targetKey);
61
+ if (matched) {
62
+ return matched;
63
+ }
64
+ }
65
+ }
66
+ return void 0;
67
+ }, "findExtraMenuItemByKey");
51
68
  const walkSubModels = /* @__PURE__ */ __name((rootModel, options, cb) => {
52
69
  const maxDepth = options.maxDepth;
53
70
  const arrayLimit = typeof options.arrayLimit === "number" ? options.arrayLimit : Number.POSITIVE_INFINITY;
@@ -149,13 +166,39 @@ const MenuLabelItem = /* @__PURE__ */ __name(({ title, uiMode, itemProps }) => {
149
166
  }
150
167
  return /* @__PURE__ */ import_react.default.createElement("span", { style: { display: "inline-flex", alignItems: "center", gap: 6 } }, content, /* @__PURE__ */ import_react.default.createElement(import_antd.Tooltip, { title: disabledReason, placement: "right", destroyTooltipOnHide: true }, /* @__PURE__ */ import_react.default.createElement(import_icons.QuestionCircleOutlined, { style: { color: disabledIconColor } })));
151
168
  }, "MenuLabelItem");
169
+ const TOOLBAR_ICONS_SELECTOR = ".nb-toolbar-container-icons";
170
+ const TOOLBAR_CONTAINER_SELECTOR = ".nb-toolbar-container";
171
+ const TOOLBAR_DROPDOWN_OVERLAY_CLASS = import_css.css`
172
+ width: max-content;
173
+ min-width: max-content;
174
+
175
+ .ant-dropdown-menu {
176
+ width: max-content;
177
+ min-width: max-content;
178
+ }
179
+ `;
180
+ const getToolbarPopupContainer = /* @__PURE__ */ __name((triggerNode) => {
181
+ if (!triggerNode) {
182
+ return null;
183
+ }
184
+ return triggerNode.closest(TOOLBAR_ICONS_SELECTOR) || triggerNode.closest(TOOLBAR_CONTAINER_SELECTOR);
185
+ }, "getToolbarPopupContainer");
186
+ const removeExtraMenuItemClickHandlers = /* @__PURE__ */ __name((item) => {
187
+ const { onClick: _onClick, children, ...rest } = item;
188
+ return {
189
+ ...rest,
190
+ children: (children == null ? void 0 : children.length) ? children.map(removeExtraMenuItemClickHandlers) : void 0
191
+ };
192
+ }, "removeExtraMenuItemClickHandlers");
152
193
  const DefaultSettingsIcon = /* @__PURE__ */ __name(({
153
194
  model,
154
195
  showDeleteButton = true,
155
196
  showCopyUidButton = true,
156
197
  menuLevels = 1,
157
198
  // 默认一级菜单
158
- flattenSubMenus = true
199
+ flattenSubMenus = true,
200
+ onDropdownVisibleChange,
201
+ getPopupContainer
159
202
  }) => {
160
203
  const { message } = import_antd.App.useApp();
161
204
  const t = (0, import_react.useMemo)(() => (0, import_utils.getT)(model), [model]);
@@ -164,41 +207,70 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
164
207
  const [visible, setVisible] = (0, import_react.useState)(false);
165
208
  const [refreshTick, setRefreshTick] = (0, import_react.useState)(0);
166
209
  const [extraMenuItems, setExtraMenuItems] = (0, import_react.useState)([]);
210
+ const [extraMenuItemsLoaded, setExtraMenuItemsLoaded] = (0, import_react.useState)(false);
167
211
  const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = (0, import_react.useState)([]);
168
212
  const [isLoading, setIsLoading] = (0, import_react.useState)(true);
213
+ const commonExtras = (0, import_react.useMemo)(
214
+ () => extraMenuItems.filter((it) => it.group === "common-actions").sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)),
215
+ [extraMenuItems]
216
+ );
217
+ const hasCommonActions = showCopyUidButton || showDeleteButton || commonExtras.length > 0;
218
+ const shouldDeferConfigLoading = flattenSubMenus && menuLevels > 1 && hasCommonActions;
219
+ const shouldWaitForCommonActionProbe = flattenSubMenus && menuLevels > 1 && !showCopyUidButton && !showDeleteButton && !extraMenuItemsLoaded;
220
+ const canRenderIcon = hasCommonActions || !isLoading && configurableFlowsAndSteps.length > 0;
169
221
  const closeDropdown = (0, import_react.useCallback)(() => {
170
222
  setVisible(false);
171
- }, []);
172
- const handleOpenChange = (0, import_react.useCallback)((nextOpen, info) => {
173
- if (info.source === "trigger" || nextOpen) {
174
- (0, import_react.startTransition)(() => {
175
- setVisible(nextOpen);
176
- });
177
- }
178
- }, []);
223
+ onDropdownVisibleChange == null ? void 0 : onDropdownVisibleChange(false);
224
+ }, [onDropdownVisibleChange]);
225
+ const resolvePopupContainer = (0, import_react.useCallback)(
226
+ (triggerNode) => {
227
+ return getToolbarPopupContainer(triggerNode) || (getPopupContainer == null ? void 0 : getPopupContainer(triggerNode)) || (triggerNode == null ? void 0 : triggerNode.parentElement) || document.body;
228
+ },
229
+ [getPopupContainer]
230
+ );
231
+ const handleOpenChange = (0, import_react.useCallback)(
232
+ (nextOpen, info) => {
233
+ if (info.source === "trigger" || nextOpen) {
234
+ (0, import_react.startTransition)(() => {
235
+ setVisible(nextOpen);
236
+ });
237
+ onDropdownVisibleChange == null ? void 0 : onDropdownVisibleChange(nextOpen);
238
+ }
239
+ },
240
+ [onDropdownVisibleChange]
241
+ );
242
+ (0, import_react.useEffect)(() => {
243
+ return () => {
244
+ onDropdownVisibleChange == null ? void 0 : onDropdownVisibleChange(false);
245
+ };
246
+ }, [onDropdownVisibleChange]);
179
247
  const dropdownMaxHeight = (0, import_hooks.useNiceDropdownMaxHeight)([visible]);
180
248
  (0, import_react.useEffect)(() => {
181
249
  let mounted = true;
182
250
  const loadExtras = /* @__PURE__ */ __name(async () => {
183
251
  var _a;
184
- const allExtras = [];
185
- const modelsToProcess = [];
186
- walkSubModels(model, { maxDepth: menuLevels, arrayLimit: 50, mode: "stack" }, (targetModel, { modelKey }) => {
187
- modelsToProcess.push({ model: targetModel, modelKey });
188
- });
189
- for (const { model: targetModel, modelKey } of modelsToProcess) {
190
- const Cls = targetModel.constructor;
191
- const extras = await ((_a = Cls.getExtraMenuItems) == null ? void 0 : _a.call(Cls, targetModel, t));
192
- if (extras == null ? void 0 : extras.length) {
193
- allExtras.push(
194
- ...extras.map((item) => ({
195
- ...item,
196
- key: modelKey ? `${modelKey}:${item.key}` : item.key
197
- }))
198
- );
252
+ setExtraMenuItemsLoaded(false);
253
+ try {
254
+ const allExtras = [];
255
+ const modelsToProcess = [];
256
+ walkSubModels(model, { maxDepth: menuLevels, arrayLimit: 50, mode: "stack" }, (targetModel, { modelKey }) => {
257
+ modelsToProcess.push({ model: targetModel, modelKey });
258
+ });
259
+ for (const { model: targetModel, modelKey } of modelsToProcess) {
260
+ const Cls = targetModel.constructor;
261
+ const extras = await ((_a = Cls.getExtraMenuItems) == null ? void 0 : _a.call(Cls, targetModel, t));
262
+ if (extras == null ? void 0 : extras.length) {
263
+ allExtras.push(
264
+ ...extras.map((item) => ({
265
+ ...item,
266
+ key: modelKey ? `${modelKey}:${item.key}` : item.key
267
+ }))
268
+ );
269
+ }
270
+ }
271
+ if (!mounted) {
272
+ return;
199
273
  }
200
- }
201
- if (mounted) {
202
274
  const seen = /* @__PURE__ */ new Set();
203
275
  const dedupedExtras = allExtras.filter((item) => {
204
276
  if (seen.has(`${item.key}`)) {
@@ -208,15 +280,22 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
208
280
  return true;
209
281
  });
210
282
  setExtraMenuItems(dedupedExtras);
283
+ } catch (error) {
284
+ console.error("Failed to load extra menu items:", error);
285
+ if (mounted) {
286
+ setExtraMenuItems([]);
287
+ }
288
+ } finally {
289
+ if (mounted) {
290
+ setExtraMenuItemsLoaded(true);
291
+ }
211
292
  }
212
293
  }, "loadExtras");
213
- if (visible) {
214
- loadExtras();
215
- }
294
+ loadExtras();
216
295
  return () => {
217
296
  mounted = false;
218
297
  };
219
- }, [model, menuLevels, t, refreshTick, visible]);
298
+ }, [model, menuLevels, t, refreshTick]);
220
299
  const copyUidToClipboard = (0, import_react.useCallback)(
221
300
  async (uid) => {
222
301
  var _a;
@@ -353,7 +432,10 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
353
432
  handleCopyPopupUid(cleanKey);
354
433
  return;
355
434
  }
356
- const extra = extraMenuItems.find((it) => (it == null ? void 0 : it.key) === originalKey || (it == null ? void 0 : it.key) === cleanKey);
435
+ const extra = findExtraMenuItemByKey(extraMenuItems, originalKey) || findExtraMenuItemByKey(extraMenuItems, cleanKey);
436
+ if (extra == null ? void 0 : extra.disabled) {
437
+ return;
438
+ }
357
439
  if (extra == null ? void 0 : extra.onClick) {
358
440
  closeDropdown();
359
441
  extra.onClick();
@@ -458,7 +540,7 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
458
540
  return [];
459
541
  }
460
542
  },
461
- []
543
+ [t]
462
544
  );
463
545
  const getConfigurableFlowsAndSteps = (0, import_react.useCallback)(async () => {
464
546
  const result = [];
@@ -496,20 +578,47 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
496
578
  };
497
579
  }, [model, menuLevels, refreshTick]);
498
580
  (0, import_react.useEffect)(() => {
581
+ let mounted = true;
499
582
  const loadConfigurableFlowsAndSteps = /* @__PURE__ */ __name(async () => {
500
583
  setIsLoading(true);
584
+ if (shouldDeferConfigLoading) {
585
+ setConfigurableFlowsAndSteps([]);
586
+ }
501
587
  try {
502
588
  const flows = await getConfigurableFlowsAndSteps();
503
- setConfigurableFlowsAndSteps(flows);
589
+ if (mounted) {
590
+ setConfigurableFlowsAndSteps(flows);
591
+ }
504
592
  } catch (error) {
505
593
  console.error("Failed to load configurable flows and steps:", error);
506
- setConfigurableFlowsAndSteps([]);
594
+ if (mounted) {
595
+ setConfigurableFlowsAndSteps([]);
596
+ }
507
597
  } finally {
508
- setIsLoading(false);
598
+ if (mounted) {
599
+ setIsLoading(false);
600
+ }
509
601
  }
510
602
  }, "loadConfigurableFlowsAndSteps");
603
+ if (shouldWaitForCommonActionProbe) {
604
+ setConfigurableFlowsAndSteps([]);
605
+ setIsLoading(false);
606
+ return () => {
607
+ mounted = false;
608
+ };
609
+ }
610
+ if (!visible && shouldDeferConfigLoading) {
611
+ setConfigurableFlowsAndSteps([]);
612
+ setIsLoading(false);
613
+ return () => {
614
+ mounted = false;
615
+ };
616
+ }
511
617
  loadConfigurableFlowsAndSteps();
512
- }, [getConfigurableFlowsAndSteps, refreshTick]);
618
+ return () => {
619
+ mounted = false;
620
+ };
621
+ }, [getConfigurableFlowsAndSteps, refreshTick, shouldDeferConfigLoading, shouldWaitForCommonActionProbe, visible]);
513
622
  const menuItems = (0, import_react.useMemo)(() => {
514
623
  const items = [];
515
624
  const keyCounter = /* @__PURE__ */ new Map();
@@ -634,16 +743,15 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
634
743
  }
635
744
  }
636
745
  return items;
637
- }, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, t]);
746
+ }, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, message, model, t]);
638
747
  const finalMenuItems = (0, import_react.useMemo)(() => {
639
748
  const items = [...menuItems];
640
- const commonExtras = extraMenuItems.filter((it) => it.group === "common-actions").sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
641
749
  if (showCopyUidButton || showDeleteButton || commonExtras.length > 0) {
642
750
  items.push({
643
751
  type: "divider"
644
752
  });
645
753
  if (commonExtras.length > 0) {
646
- items.push(...commonExtras);
754
+ items.push(...commonExtras.map(removeExtraMenuItemClickHandlers));
647
755
  }
648
756
  if (showCopyUidButton && model.uid) {
649
757
  items.push({
@@ -659,9 +767,8 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
659
767
  }
660
768
  }
661
769
  return items;
662
- }, [menuItems, showCopyUidButton, showDeleteButton, model.uid, model.destroy, t, extraMenuItems]);
663
- const hasExtras = extraMenuItems.some((it) => it.group === "common-actions");
664
- if (isLoading || configurableFlowsAndSteps.length === 0 && !showDeleteButton && !showCopyUidButton && !hasExtras) {
770
+ }, [menuItems, showCopyUidButton, showDeleteButton, commonExtras, model.uid, model.destroy, t]);
771
+ if (!canRenderIcon) {
665
772
  return null;
666
773
  }
667
774
  if (!model || !model.uid) {
@@ -671,6 +778,9 @@ const DefaultSettingsIcon = /* @__PURE__ */ __name(({
671
778
  return /* @__PURE__ */ import_react.default.createElement(
672
779
  import_antd.Dropdown,
673
780
  {
781
+ getPopupContainer: resolvePopupContainer,
782
+ overlayClassName: TOOLBAR_DROPDOWN_OVERLAY_CLASS,
783
+ overlayStyle: { width: "max-content", minWidth: "max-content" },
674
784
  onOpenChange: handleOpenChange,
675
785
  open: visible,
676
786
  menu: {
@@ -9,13 +9,14 @@
9
9
  import React from 'react';
10
10
  import { FlowModel } from '../../../../models';
11
11
  import { ToolbarItemConfig } from '../../../../types';
12
- interface ModelProvidedProps {
13
- model: FlowModel<any>;
12
+ type ToolbarPosition = 'inside' | 'above' | 'below';
13
+ interface BaseFloatContextMenuProps {
14
14
  children?: React.ReactNode;
15
15
  enabled?: boolean;
16
16
  showDeleteButton?: boolean;
17
17
  showCopyUidButton?: boolean;
18
18
  containerStyle?: React.CSSProperties;
19
+ /** 自定义工具栏样式,`top/left/right/bottom` 会作为 portal overlay 的 inset 使用。 */
19
20
  toolbarStyle?: React.CSSProperties;
20
21
  className?: string;
21
22
  /**
@@ -42,68 +43,47 @@ interface ModelProvidedProps {
42
43
  * Extra toolbar items to add to this context menu instance
43
44
  */
44
45
  extraToolbarItems?: ToolbarItemConfig[];
45
- /**
46
- * @default 'inside'
47
- */
48
- toolbarPosition?: 'inside' | 'above' | 'below';
49
- }
50
- interface ModelByIdProps {
51
- uid: string;
52
- modelClassName: string;
53
- children?: React.ReactNode;
54
- enabled?: boolean;
55
- showDeleteButton?: boolean;
56
- showCopyUidButton?: boolean;
57
- containerStyle?: React.CSSProperties;
58
- className?: string;
59
46
  /**
60
47
  * @default true
61
48
  */
62
- showBorder?: boolean;
63
- /**
64
- * @default true
65
- */
66
- showBackground?: boolean;
67
- /**
68
- * @default false
69
- */
70
- showTitle?: boolean;
71
- /**
72
- * Settings menu levels: 1=current model only (default), 2=include sub-models
73
- */
74
- settingsMenuLevel?: number;
75
- /**
76
- * Extra toolbar items to add to this context menu instance
77
- */
78
- extraToolbarItems?: ToolbarItemConfig[];
49
+ showDynamicFlowsEditor?: boolean;
79
50
  /**
80
51
  * @default 'inside'
81
52
  */
82
- toolbarPosition?: 'inside' | 'above' | 'below';
53
+ toolbarPosition?: ToolbarPosition;
54
+ }
55
+ interface ModelProvidedProps extends BaseFloatContextMenuProps {
56
+ model: FlowModel<any>;
57
+ }
58
+ interface ModelByIdProps extends BaseFloatContextMenuProps {
59
+ uid: string;
60
+ modelClassName: string;
83
61
  }
84
62
  type FlowsFloatContextMenuProps = ModelProvidedProps | ModelByIdProps;
85
63
  /**
86
- * FlowsFloatContextMenu组件 - 悬浮配置图标组件
64
+ * FlowsFloatContextMenu组件 - 悬浮配置工具栏组件
87
65
  *
88
66
  * 功能特性:
89
67
  * - 鼠标悬浮显示右上角配置图标
90
68
  * - 点击图标显示配置菜单
91
69
  * - 支持删除功能
92
70
  * - Wrapper 模式支持
93
- * - 使用与 NocoBase x-settings 一致的样式
94
- * - 按flow分组显示steps
71
+ * - 使用 portal overlay 避免被宿主或祖先裁剪
72
+ * - 设置菜单与工具栏共享同一个 popup 容器
95
73
  *
96
74
  * 支持两种使用方式:
97
- * 1. 直接提供model: <FlowsFloatContextMenu model={myModel}>{children}</FlowsFloatContextMenu>
98
- * 2. 通过uid和modelClassName获取model: <FlowsFloatContextMenu uid="model1" modelClassName="MyModel">{children}</FlowsFloatContextMenu>
75
+ * 1. 直接提供 model: `<FlowsFloatContextMenu model={myModel}>{children}</FlowsFloatContextMenu>`
76
+ * 2. 通过 uid modelClassName 获取 model:
77
+ * `<FlowsFloatContextMenu uid="model1" modelClassName="MyModel">{children}</FlowsFloatContextMenu>`
99
78
  *
100
79
  * @param props.children 子组件,必须提供
101
- * @param props.enabled 是否启用悬浮菜单,默认为true
102
- * @param props.showDeleteButton 是否显示删除按钮,默认为true
103
- * @param props.showCopyUidButton 是否显示复制UID按钮,默认为true
80
+ * @param props.enabled 是否启用悬浮菜单,默认为 true
81
+ * @param props.showDeleteButton 是否显示删除按钮,默认为 true
82
+ * @param props.showCopyUidButton 是否显示复制 UID 按钮,默认为 true
104
83
  * @param props.containerStyle 容器自定义样式
84
+ * @param props.toolbarStyle 工具栏自定义样式;`top/left/right/bottom` 会作为 portal overlay 的 inset 使用
105
85
  * @param props.className 容器自定义类名
106
- * @param props.showTitle 是否在边框左上角显示模型title,默认为false
86
+ * @param props.showTitle 是否在边框左上角显示模型 title,默认为 false
107
87
  * @param props.settingsMenuLevel 设置菜单层级:1=仅当前模型(默认),2=包含子模型
108
88
  * @param props.extraToolbarItems 额外的工具栏项目,仅应用于此实例
109
89
  */