@nocobase/flow-engine 2.1.0-beta.9 → 2.2.0-beta.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.
- package/lib/FlowContextProvider.d.ts +5 -1
- package/lib/FlowContextProvider.js +9 -2
- package/lib/components/FieldModelRenderer.js +2 -2
- package/lib/components/FlowModelRenderer.d.ts +3 -1
- package/lib/components/FlowModelRenderer.js +12 -6
- package/lib/components/FormItem.d.ts +6 -0
- package/lib/components/FormItem.js +11 -3
- package/lib/components/MobilePopup.js +6 -5
- package/lib/components/dnd/gridDragPlanner.d.ts +59 -2
- package/lib/components/dnd/gridDragPlanner.js +607 -19
- package/lib/components/dnd/index.d.ts +31 -2
- package/lib/components/dnd/index.js +244 -23
- package/lib/components/settings/wrappers/component/SelectWithTitle.d.ts +2 -1
- package/lib/components/settings/wrappers/component/SelectWithTitle.js +14 -12
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.d.ts +3 -0
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +152 -42
- package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +23 -43
- package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +352 -295
- package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.d.ts +36 -0
- package/lib/components/settings/wrappers/contextual/useFloatToolbarPortal.js +274 -0
- package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.d.ts +30 -0
- package/lib/components/settings/wrappers/contextual/useFloatToolbarVisibility.js +315 -0
- package/lib/components/subModel/AddSubModelButton.js +12 -1
- package/lib/components/subModel/LazyDropdown.js +301 -52
- package/lib/components/subModel/index.d.ts +1 -0
- package/lib/components/subModel/index.js +19 -0
- package/lib/components/subModel/utils.d.ts +2 -1
- package/lib/components/subModel/utils.js +15 -5
- package/lib/components/variables/VariableHybridInput.d.ts +27 -0
- package/lib/components/variables/VariableHybridInput.js +499 -0
- package/lib/components/variables/index.d.ts +2 -0
- package/lib/components/variables/index.js +3 -0
- package/lib/data-source/index.d.ts +84 -0
- package/lib/data-source/index.js +269 -7
- package/lib/executor/FlowExecutor.js +6 -3
- package/lib/flow-registry/DetachedFlowRegistry.d.ts +21 -0
- package/lib/flow-registry/DetachedFlowRegistry.js +80 -0
- package/lib/flow-registry/index.d.ts +1 -0
- package/lib/flow-registry/index.js +3 -1
- package/lib/flowContext.d.ts +9 -1
- package/lib/flowContext.js +77 -6
- package/lib/flowEngine.d.ts +136 -4
- package/lib/flowEngine.js +429 -51
- package/lib/flowI18n.js +2 -1
- package/lib/flowSettings.d.ts +14 -6
- package/lib/flowSettings.js +34 -6
- package/lib/index.d.ts +2 -0
- package/lib/index.js +7 -0
- package/lib/lazy-helper.d.ts +14 -0
- package/lib/lazy-helper.js +71 -0
- package/lib/locale/en-US.json +1 -0
- package/lib/locale/index.d.ts +2 -0
- package/lib/locale/zh-CN.json +1 -0
- package/lib/models/DisplayItemModel.d.ts +1 -1
- package/lib/models/EditableItemModel.d.ts +1 -1
- package/lib/models/FilterableItemModel.d.ts +1 -1
- package/lib/models/flowModel.d.ts +13 -10
- package/lib/models/flowModel.js +126 -34
- package/lib/provider.js +38 -23
- package/lib/reactive/observer.js +46 -16
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +4 -15
- package/lib/runjs-context/contexts/JSColumnRunJSContext.js +5 -2
- package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +5 -8
- package/lib/runjs-context/contexts/JSFieldRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +4 -3
- package/lib/runjs-context/contexts/base.js +464 -29
- package/lib/runjs-context/contexts/elementDoc.d.ts +11 -0
- package/lib/runjs-context/contexts/elementDoc.js +152 -0
- package/lib/runjs-context/setup.js +1 -0
- package/lib/runjs-context/snippets/index.js +13 -2
- package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/set-field-style.snippet.js +50 -0
- package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/set-cell-style.snippet.js +54 -0
- package/lib/types.d.ts +50 -2
- package/lib/types.js +1 -0
- package/lib/utils/createCollectionContextMeta.js +6 -2
- package/lib/utils/index.d.ts +3 -2
- package/lib/utils/index.js +7 -0
- package/lib/utils/loadedPageCache.d.ts +24 -0
- package/lib/utils/loadedPageCache.js +139 -0
- package/lib/utils/parsePathnameToViewParams.d.ts +5 -1
- package/lib/utils/parsePathnameToViewParams.js +28 -4
- package/lib/utils/randomId.d.ts +39 -0
- package/lib/utils/randomId.js +45 -0
- package/lib/utils/runjsTemplateCompat.js +1 -1
- package/lib/utils/runjsValue.js +41 -11
- package/lib/utils/schema-utils.d.ts +7 -1
- package/lib/utils/schema-utils.js +19 -0
- package/lib/views/FlowView.d.ts +7 -1
- package/lib/views/FlowView.js +11 -1
- package/lib/views/PageComponent.js +8 -6
- package/lib/views/ViewNavigation.d.ts +12 -2
- package/lib/views/ViewNavigation.js +28 -9
- package/lib/views/createViewMeta.js +114 -50
- package/lib/views/inheritLayoutContext.d.ts +10 -0
- package/lib/views/inheritLayoutContext.js +50 -0
- package/lib/views/runViewBeforeClose.d.ts +10 -0
- package/lib/views/runViewBeforeClose.js +45 -0
- package/lib/views/useDialog.d.ts +2 -1
- package/lib/views/useDialog.js +12 -3
- package/lib/views/useDrawer.d.ts +2 -1
- package/lib/views/useDrawer.js +12 -3
- package/lib/views/usePage.d.ts +5 -11
- package/lib/views/usePage.js +304 -144
- package/package.json +5 -4
- package/src/FlowContextProvider.tsx +9 -1
- package/src/__tests__/createViewMeta.popup.test.ts +115 -1
- package/src/__tests__/flow-engine.test.ts +166 -0
- package/src/__tests__/flowContext.test.ts +105 -1
- package/src/__tests__/flowEngine.modelLoaders.test.ts +245 -0
- package/src/__tests__/flowEngine.moveModel.test.ts +81 -1
- package/src/__tests__/flowEngine.removeModel.test.ts +47 -3
- package/src/__tests__/flowSettings.test.ts +94 -15
- package/src/__tests__/objectVariable.test.ts +24 -0
- package/src/__tests__/provider.test.tsx +24 -2
- package/src/__tests__/renderHiddenInConfig.test.tsx +6 -6
- package/src/__tests__/runjsContext.test.ts +21 -0
- package/src/__tests__/runjsContextImplementations.test.ts +9 -2
- package/src/__tests__/runjsContextRuntime.test.ts +2 -0
- package/src/__tests__/runjsLocales.test.ts +6 -5
- package/src/__tests__/runjsSnippets.test.ts +21 -0
- package/src/__tests__/viewScopedFlowEngine.test.ts +136 -3
- package/src/components/FieldModelRenderer.tsx +2 -1
- package/src/components/FlowModelRenderer.tsx +18 -6
- package/src/components/FormItem.tsx +7 -1
- package/src/components/MobilePopup.tsx +4 -2
- package/src/components/__tests__/FlowModelRenderer.test.tsx +65 -2
- package/src/components/__tests__/FormItem.test.tsx +25 -0
- package/src/components/__tests__/dnd.test.ts +44 -0
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +20 -10
- package/src/components/__tests__/gridDragPlanner.test.ts +472 -5
- package/src/components/dnd/__tests__/DndProvider.test.tsx +98 -0
- package/src/components/dnd/gridDragPlanner.ts +750 -17
- package/src/components/dnd/index.tsx +305 -28
- package/src/components/settings/wrappers/component/SelectWithTitle.tsx +21 -9
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +178 -48
- package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +487 -440
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +344 -8
- package/src/components/settings/wrappers/contextual/__tests__/FlowsFloatContextMenu.test.tsx +778 -0
- package/src/components/settings/wrappers/contextual/useFloatToolbarPortal.ts +360 -0
- package/src/components/settings/wrappers/contextual/useFloatToolbarVisibility.ts +361 -0
- package/src/components/subModel/AddSubModelButton.tsx +16 -2
- package/src/components/subModel/LazyDropdown.tsx +341 -56
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +524 -38
- package/src/components/subModel/__tests__/utils.test.ts +24 -0
- package/src/components/subModel/index.ts +1 -0
- package/src/components/subModel/utils.ts +13 -2
- package/src/components/variables/VariableHybridInput.tsx +531 -0
- package/src/components/variables/index.ts +2 -0
- package/src/data-source/__tests__/collection.test.ts +41 -2
- package/src/data-source/__tests__/index.test.ts +69 -2
- package/src/data-source/index.ts +332 -8
- package/src/executor/FlowExecutor.ts +6 -3
- package/src/executor/__tests__/flowExecutor.test.ts +57 -0
- package/src/flow-registry/DetachedFlowRegistry.ts +46 -0
- package/src/flow-registry/__tests__/detachedFlowRegistry.test.ts +47 -0
- package/src/flow-registry/index.ts +1 -0
- package/src/flowContext.ts +85 -6
- package/src/flowEngine.ts +484 -45
- package/src/flowI18n.ts +2 -1
- package/src/flowSettings.ts +40 -6
- package/src/index.ts +2 -0
- package/src/lazy-helper.tsx +57 -0
- package/src/locale/en-US.json +1 -0
- package/src/locale/zh-CN.json +1 -0
- package/src/models/DisplayItemModel.tsx +1 -1
- package/src/models/EditableItemModel.tsx +1 -1
- package/src/models/FilterableItemModel.tsx +1 -1
- package/src/models/__tests__/flowEngine.resolveUse.test.ts +0 -15
- package/src/models/__tests__/flowModel.test.ts +65 -37
- package/src/models/flowModel.tsx +184 -65
- package/src/provider.tsx +41 -25
- package/src/reactive/__tests__/observer.test.tsx +82 -0
- package/src/reactive/observer.tsx +87 -25
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +4 -15
- package/src/runjs-context/contexts/JSColumnRunJSContext.ts +4 -2
- package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +5 -9
- package/src/runjs-context/contexts/JSFieldRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +4 -3
- package/src/runjs-context/contexts/base.ts +467 -31
- package/src/runjs-context/contexts/elementDoc.ts +130 -0
- package/src/runjs-context/setup.ts +1 -0
- package/src/runjs-context/snippets/index.ts +12 -1
- package/src/runjs-context/snippets/scene/detail/set-field-style.snippet.ts +30 -0
- package/src/runjs-context/snippets/scene/table/set-cell-style.snippet.ts +34 -0
- package/src/types.ts +62 -0
- package/src/utils/__tests__/createCollectionContextMeta.test.ts +48 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +21 -0
- package/src/utils/__tests__/runjsValue.test.ts +11 -0
- package/src/utils/__tests__/utils.test.ts +62 -0
- package/src/utils/createCollectionContextMeta.ts +6 -2
- package/src/utils/index.ts +5 -1
- package/src/utils/loadedPageCache.ts +147 -0
- package/src/utils/parsePathnameToViewParams.ts +45 -5
- package/src/utils/randomId.ts +48 -0
- package/src/utils/runjsTemplateCompat.ts +1 -1
- package/src/utils/runjsValue.ts +50 -11
- package/src/utils/schema-utils.ts +30 -1
- package/src/views/FlowView.tsx +22 -2
- package/src/views/PageComponent.tsx +7 -4
- package/src/views/ViewNavigation.ts +46 -9
- package/src/views/__tests__/FlowView.usePage.test.tsx +243 -3
- package/src/views/__tests__/ViewNavigation.test.ts +52 -0
- package/src/views/__tests__/inheritLayoutContext.test.ts +53 -0
- package/src/views/__tests__/runViewBeforeClose.test.ts +30 -0
- package/src/views/__tests__/useDialog.closeDestroy.test.tsx +12 -12
- package/src/views/createViewMeta.ts +106 -34
- package/src/views/inheritLayoutContext.ts +26 -0
- package/src/views/runViewBeforeClose.ts +19 -0
- package/src/views/useDialog.tsx +13 -3
- package/src/views/useDrawer.tsx +13 -3
- package/src/views/usePage.tsx +367 -180
|
@@ -8,6 +8,7 @@
|
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
import { ExclamationCircleOutlined, MenuOutlined, QuestionCircleOutlined } from '@ant-design/icons';
|
|
11
|
+
import { css } from '@emotion/css';
|
|
11
12
|
import type { DropdownProps, MenuProps } from 'antd';
|
|
12
13
|
import { App, Dropdown, Modal, Tooltip, theme } from 'antd';
|
|
13
14
|
import React, { startTransition, useCallback, useEffect, useMemo, useState, FC } from 'react';
|
|
@@ -26,6 +27,26 @@ import { useNiceDropdownMaxHeight } from '../../../../hooks';
|
|
|
26
27
|
import { SwitchWithTitle } from '../component/SwitchWithTitle';
|
|
27
28
|
import { SelectWithTitle } from '../component/SelectWithTitle';
|
|
28
29
|
import type { FlowSettingsContext } from '../../../../flowContext';
|
|
30
|
+
|
|
31
|
+
const findExtraMenuItemByKey = (
|
|
32
|
+
items: FlowModelExtraMenuItem[],
|
|
33
|
+
targetKey: string,
|
|
34
|
+
): FlowModelExtraMenuItem | undefined => {
|
|
35
|
+
for (const item of items) {
|
|
36
|
+
const itemKey = String(item?.key ?? '');
|
|
37
|
+
if (itemKey === targetKey) {
|
|
38
|
+
return item;
|
|
39
|
+
}
|
|
40
|
+
if (item.children?.length) {
|
|
41
|
+
const matched = findExtraMenuItemByKey(item.children, targetKey);
|
|
42
|
+
if (matched) {
|
|
43
|
+
return matched;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return undefined;
|
|
48
|
+
};
|
|
49
|
+
|
|
29
50
|
// Type definitions for better type safety
|
|
30
51
|
interface StepInfo {
|
|
31
52
|
stepKey: string;
|
|
@@ -188,15 +209,51 @@ interface DefaultSettingsIconProps {
|
|
|
188
209
|
showCopyUidButton?: boolean;
|
|
189
210
|
menuLevels?: number; // Menu levels: 1=current model only (default), 2=include sub-models
|
|
190
211
|
flattenSubMenus?: boolean; // Whether to flatten sub-menus: false=group by model (default), true=flatten all
|
|
212
|
+
onDropdownVisibleChange?: (open: boolean) => void;
|
|
213
|
+
getPopupContainer?: DropdownProps['getPopupContainer'];
|
|
191
214
|
[key: string]: any; // Allow additional props
|
|
192
215
|
}
|
|
193
216
|
|
|
217
|
+
const TOOLBAR_ICONS_SELECTOR = '.nb-toolbar-container-icons';
|
|
218
|
+
const TOOLBAR_CONTAINER_SELECTOR = '.nb-toolbar-container';
|
|
219
|
+
const TOOLBAR_DROPDOWN_OVERLAY_CLASS = css`
|
|
220
|
+
width: max-content;
|
|
221
|
+
min-width: max-content;
|
|
222
|
+
|
|
223
|
+
.ant-dropdown-menu {
|
|
224
|
+
width: max-content;
|
|
225
|
+
min-width: max-content;
|
|
226
|
+
}
|
|
227
|
+
`;
|
|
228
|
+
|
|
229
|
+
const getToolbarPopupContainer = (triggerNode?: HTMLElement | null) => {
|
|
230
|
+
if (!triggerNode) {
|
|
231
|
+
return null;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return (
|
|
235
|
+
(triggerNode.closest(TOOLBAR_ICONS_SELECTOR) as HTMLElement | null) ||
|
|
236
|
+
(triggerNode.closest(TOOLBAR_CONTAINER_SELECTOR) as HTMLElement | null)
|
|
237
|
+
);
|
|
238
|
+
};
|
|
239
|
+
|
|
240
|
+
const removeExtraMenuItemClickHandlers = (item: FlowModelExtraMenuItem): FlowModelExtraMenuItem => {
|
|
241
|
+
const { onClick: _onClick, children, ...rest } = item;
|
|
242
|
+
|
|
243
|
+
return {
|
|
244
|
+
...rest,
|
|
245
|
+
children: children?.length ? children.map(removeExtraMenuItemClickHandlers) : undefined,
|
|
246
|
+
};
|
|
247
|
+
};
|
|
248
|
+
|
|
194
249
|
export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
195
250
|
model,
|
|
196
251
|
showDeleteButton = true,
|
|
197
252
|
showCopyUidButton = true,
|
|
198
253
|
menuLevels = 1, // 默认一级菜单
|
|
199
254
|
flattenSubMenus = true,
|
|
255
|
+
onDropdownVisibleChange,
|
|
256
|
+
getPopupContainer,
|
|
200
257
|
}) => {
|
|
201
258
|
const { message } = App.useApp();
|
|
202
259
|
const t = useMemo(() => getT(model), [model]);
|
|
@@ -206,43 +263,80 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
206
263
|
// 当模型发生子模型替换/增删等变化时,强制刷新菜单数据
|
|
207
264
|
const [refreshTick, setRefreshTick] = useState(0);
|
|
208
265
|
const [extraMenuItems, setExtraMenuItems] = useState<FlowModelExtraMenuItem[]>([]);
|
|
266
|
+
const [extraMenuItemsLoaded, setExtraMenuItemsLoaded] = useState(false);
|
|
209
267
|
const [configurableFlowsAndSteps, setConfigurableFlowsAndSteps] = useState<FlowInfo[]>([]);
|
|
210
268
|
const [isLoading, setIsLoading] = useState(true);
|
|
269
|
+
const commonExtras = useMemo(
|
|
270
|
+
() => extraMenuItems.filter((it) => it.group === 'common-actions').sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)),
|
|
271
|
+
[extraMenuItems],
|
|
272
|
+
);
|
|
273
|
+
const hasCommonActions = showCopyUidButton || showDeleteButton || commonExtras.length > 0;
|
|
274
|
+
const shouldDeferConfigLoading = flattenSubMenus && menuLevels > 1 && hasCommonActions;
|
|
275
|
+
const shouldWaitForCommonActionProbe =
|
|
276
|
+
flattenSubMenus && menuLevels > 1 && !showCopyUidButton && !showDeleteButton && !extraMenuItemsLoaded;
|
|
277
|
+
const canRenderIcon = hasCommonActions || (!isLoading && configurableFlowsAndSteps.length > 0);
|
|
211
278
|
const closeDropdown = useCallback(() => {
|
|
212
279
|
setVisible(false);
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
280
|
+
onDropdownVisibleChange?.(false);
|
|
281
|
+
}, [onDropdownVisibleChange]);
|
|
282
|
+
const resolvePopupContainer = useCallback<NonNullable<DropdownProps['getPopupContainer']>>(
|
|
283
|
+
(triggerNode) => {
|
|
284
|
+
// 工具栏自身容器必须优先,保证鼠标从 icon 移到菜单时仍处于同一 hover 树。
|
|
285
|
+
// 弹窗场景的裁剪问题由 useFloatToolbarPortal 负责把 toolbar 挂到正确的 popup host。
|
|
286
|
+
return (
|
|
287
|
+
getToolbarPopupContainer(triggerNode) ||
|
|
288
|
+
getPopupContainer?.(triggerNode) ||
|
|
289
|
+
triggerNode?.parentElement ||
|
|
290
|
+
document.body
|
|
291
|
+
);
|
|
292
|
+
},
|
|
293
|
+
[getPopupContainer],
|
|
294
|
+
);
|
|
295
|
+
const handleOpenChange: DropdownProps['onOpenChange'] = useCallback(
|
|
296
|
+
(nextOpen: boolean, info) => {
|
|
297
|
+
if (info.source === 'trigger' || nextOpen) {
|
|
298
|
+
// 当鼠标快速滑过时,终止菜单的渲染,防止卡顿
|
|
299
|
+
startTransition(() => {
|
|
300
|
+
setVisible(nextOpen);
|
|
301
|
+
});
|
|
302
|
+
onDropdownVisibleChange?.(nextOpen);
|
|
303
|
+
}
|
|
304
|
+
},
|
|
305
|
+
[onDropdownVisibleChange],
|
|
306
|
+
);
|
|
307
|
+
useEffect(() => {
|
|
308
|
+
return () => {
|
|
309
|
+
onDropdownVisibleChange?.(false);
|
|
310
|
+
};
|
|
311
|
+
}, [onDropdownVisibleChange]);
|
|
222
312
|
const dropdownMaxHeight = useNiceDropdownMaxHeight([visible]);
|
|
223
313
|
useEffect(() => {
|
|
224
314
|
let mounted = true;
|
|
225
315
|
const loadExtras = async () => {
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
modelsToProcess
|
|
230
|
-
|
|
316
|
+
setExtraMenuItemsLoaded(false);
|
|
317
|
+
try {
|
|
318
|
+
const allExtras: FlowModelExtraMenuItem[] = [];
|
|
319
|
+
const modelsToProcess: Array<{ model: FlowModel; modelKey?: string }> = [];
|
|
320
|
+
walkSubModels(model, { maxDepth: menuLevels, arrayLimit: 50, mode: 'stack' }, (targetModel, { modelKey }) => {
|
|
321
|
+
modelsToProcess.push({ model: targetModel, modelKey });
|
|
322
|
+
});
|
|
231
323
|
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
324
|
+
for (const { model: targetModel, modelKey } of modelsToProcess) {
|
|
325
|
+
const Cls = targetModel.constructor as typeof FlowModel;
|
|
326
|
+
const extras = await Cls.getExtraMenuItems?.(targetModel, t);
|
|
327
|
+
if (extras?.length) {
|
|
328
|
+
allExtras.push(
|
|
329
|
+
...extras.map((item) => ({
|
|
330
|
+
...item,
|
|
331
|
+
key: modelKey ? `${modelKey}:${item.key}` : item.key,
|
|
332
|
+
})),
|
|
333
|
+
);
|
|
334
|
+
}
|
|
242
335
|
}
|
|
243
|
-
}
|
|
244
336
|
|
|
245
|
-
|
|
337
|
+
if (!mounted) {
|
|
338
|
+
return;
|
|
339
|
+
}
|
|
246
340
|
const seen = new Set<string>();
|
|
247
341
|
const dedupedExtras = allExtras.filter((item) => {
|
|
248
342
|
if (seen.has(`${item.key}`)) {
|
|
@@ -252,16 +346,22 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
252
346
|
return true;
|
|
253
347
|
});
|
|
254
348
|
setExtraMenuItems(dedupedExtras);
|
|
349
|
+
} catch (error) {
|
|
350
|
+
console.error('Failed to load extra menu items:', error);
|
|
351
|
+
if (mounted) {
|
|
352
|
+
setExtraMenuItems([]);
|
|
353
|
+
}
|
|
354
|
+
} finally {
|
|
355
|
+
if (mounted) {
|
|
356
|
+
setExtraMenuItemsLoaded(true);
|
|
357
|
+
}
|
|
255
358
|
}
|
|
256
359
|
};
|
|
257
|
-
|
|
258
|
-
if (visible) {
|
|
259
|
-
loadExtras();
|
|
260
|
-
}
|
|
360
|
+
loadExtras();
|
|
261
361
|
return () => {
|
|
262
362
|
mounted = false;
|
|
263
363
|
};
|
|
264
|
-
}, [model, menuLevels, t, refreshTick
|
|
364
|
+
}, [model, menuLevels, t, refreshTick]);
|
|
265
365
|
|
|
266
366
|
// 统一的复制 UID 方法
|
|
267
367
|
const copyUidToClipboard = useCallback(
|
|
@@ -422,7 +522,11 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
422
522
|
return;
|
|
423
523
|
}
|
|
424
524
|
|
|
425
|
-
const extra =
|
|
525
|
+
const extra =
|
|
526
|
+
findExtraMenuItemByKey(extraMenuItems, originalKey) || findExtraMenuItemByKey(extraMenuItems, cleanKey);
|
|
527
|
+
if (extra?.disabled) {
|
|
528
|
+
return;
|
|
529
|
+
}
|
|
426
530
|
if (extra?.onClick) {
|
|
427
531
|
closeDropdown();
|
|
428
532
|
extra.onClick();
|
|
@@ -548,7 +652,7 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
548
652
|
return [];
|
|
549
653
|
}
|
|
550
654
|
},
|
|
551
|
-
[],
|
|
655
|
+
[t],
|
|
552
656
|
);
|
|
553
657
|
|
|
554
658
|
// 获取可配置的flows和steps
|
|
@@ -591,21 +695,50 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
591
695
|
}, [model, menuLevels, refreshTick]);
|
|
592
696
|
|
|
593
697
|
useEffect(() => {
|
|
698
|
+
let mounted = true;
|
|
594
699
|
const loadConfigurableFlowsAndSteps = async () => {
|
|
595
700
|
setIsLoading(true);
|
|
701
|
+
if (shouldDeferConfigLoading) {
|
|
702
|
+
setConfigurableFlowsAndSteps([]);
|
|
703
|
+
}
|
|
596
704
|
try {
|
|
597
705
|
const flows = await getConfigurableFlowsAndSteps();
|
|
598
|
-
|
|
706
|
+
if (mounted) {
|
|
707
|
+
setConfigurableFlowsAndSteps(flows);
|
|
708
|
+
}
|
|
599
709
|
} catch (error) {
|
|
600
710
|
console.error('Failed to load configurable flows and steps:', error);
|
|
601
|
-
|
|
711
|
+
if (mounted) {
|
|
712
|
+
setConfigurableFlowsAndSteps([]);
|
|
713
|
+
}
|
|
602
714
|
} finally {
|
|
603
|
-
|
|
715
|
+
if (mounted) {
|
|
716
|
+
setIsLoading(false);
|
|
717
|
+
}
|
|
604
718
|
}
|
|
605
719
|
};
|
|
606
720
|
|
|
721
|
+
if (shouldWaitForCommonActionProbe) {
|
|
722
|
+
setConfigurableFlowsAndSteps([]);
|
|
723
|
+
setIsLoading(false);
|
|
724
|
+
return () => {
|
|
725
|
+
mounted = false;
|
|
726
|
+
};
|
|
727
|
+
}
|
|
728
|
+
|
|
729
|
+
if (!visible && shouldDeferConfigLoading) {
|
|
730
|
+
setConfigurableFlowsAndSteps([]);
|
|
731
|
+
setIsLoading(false);
|
|
732
|
+
return () => {
|
|
733
|
+
mounted = false;
|
|
734
|
+
};
|
|
735
|
+
}
|
|
736
|
+
|
|
607
737
|
loadConfigurableFlowsAndSteps();
|
|
608
|
-
|
|
738
|
+
return () => {
|
|
739
|
+
mounted = false;
|
|
740
|
+
};
|
|
741
|
+
}, [getConfigurableFlowsAndSteps, refreshTick, shouldDeferConfigLoading, shouldWaitForCommonActionProbe, visible]);
|
|
609
742
|
|
|
610
743
|
// 构建菜单项,包含错误处理和记忆化
|
|
611
744
|
const menuItems = useMemo((): NonNullable<MenuProps['items']> => {
|
|
@@ -772,16 +905,12 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
772
905
|
}
|
|
773
906
|
|
|
774
907
|
return items;
|
|
775
|
-
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, t]);
|
|
908
|
+
}, [configurableFlowsAndSteps, disabledIconColor, flattenSubMenus, message, model, t]);
|
|
776
909
|
|
|
777
910
|
// 向菜单项添加额外按钮
|
|
778
911
|
const finalMenuItems = useMemo((): NonNullable<MenuProps['items']> => {
|
|
779
912
|
const items = [...menuItems];
|
|
780
913
|
|
|
781
|
-
const commonExtras = extraMenuItems
|
|
782
|
-
.filter((it) => it.group === 'common-actions')
|
|
783
|
-
.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0));
|
|
784
|
-
|
|
785
914
|
if (showCopyUidButton || showDeleteButton || commonExtras.length > 0) {
|
|
786
915
|
items.push({
|
|
787
916
|
type: 'divider',
|
|
@@ -795,7 +924,8 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
795
924
|
// });
|
|
796
925
|
|
|
797
926
|
if (commonExtras.length > 0) {
|
|
798
|
-
|
|
927
|
+
// Antd Menu 会同时触发 item.onClick 和 menu.onClick,这里统一交给 handleMenuClick 执行。
|
|
928
|
+
items.push(...(commonExtras.map(removeExtraMenuItemClickHandlers) as MenuProps['items']));
|
|
799
929
|
}
|
|
800
930
|
|
|
801
931
|
// 添加复制uid按钮
|
|
@@ -816,12 +946,9 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
816
946
|
}
|
|
817
947
|
|
|
818
948
|
return items;
|
|
819
|
-
}, [menuItems, showCopyUidButton, showDeleteButton, model.uid, model.destroy, t
|
|
820
|
-
|
|
821
|
-
// 如果正在加载或没有可配置的flows且不显示删除按钮和复制UID按钮,不显示菜单
|
|
822
|
-
const hasExtras = extraMenuItems.some((it) => it.group === 'common-actions');
|
|
949
|
+
}, [menuItems, showCopyUidButton, showDeleteButton, commonExtras, model.uid, model.destroy, t]);
|
|
823
950
|
|
|
824
|
-
if (
|
|
951
|
+
if (!canRenderIcon) {
|
|
825
952
|
return null;
|
|
826
953
|
}
|
|
827
954
|
|
|
@@ -833,6 +960,9 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
833
960
|
|
|
834
961
|
return (
|
|
835
962
|
<Dropdown
|
|
963
|
+
getPopupContainer={resolvePopupContainer}
|
|
964
|
+
overlayClassName={TOOLBAR_DROPDOWN_OVERLAY_CLASS}
|
|
965
|
+
overlayStyle={{ width: 'max-content', minWidth: 'max-content' }}
|
|
836
966
|
onOpenChange={handleOpenChange}
|
|
837
967
|
open={visible}
|
|
838
968
|
menu={{
|