@nocobase/flow-engine 2.0.0-alpha.3 → 2.0.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.
- package/lib/BlockScopedFlowEngine.d.ts +23 -0
- package/lib/BlockScopedFlowEngine.js +90 -0
- package/lib/FlowContextProvider.d.ts +2 -2
- package/lib/FlowContextProvider.js +3 -3
- package/lib/FlowDefinition.d.ts +4 -2
- package/lib/JSRunner.js +3 -0
- package/lib/ViewScopedFlowEngine.d.ts +1 -1
- package/lib/components/FieldModelRenderer.js +10 -4
- package/lib/components/FieldSkeleton.d.ts +10 -0
- package/lib/components/FieldSkeleton.js +64 -0
- package/lib/components/FlowContextSelector.js +7 -2
- package/lib/components/FlowModelRenderer.d.ts +2 -5
- package/lib/components/FlowModelRenderer.js +16 -47
- package/lib/components/FormItem.js +5 -1
- package/lib/{runjs-context/snippets/global/requireAsync.snippet.d.ts → components/dnd/findModelUidPosition.d.ts} +4 -7
- package/lib/{runjs-context/snippets/scene/jsblock/jsx-mount.snippet.js → components/dnd/findModelUidPosition.js} +23 -19
- package/lib/components/dnd/gridDragPlanner.d.ts +130 -0
- package/lib/components/dnd/gridDragPlanner.js +497 -0
- package/lib/components/dnd/index.d.ts +2 -2
- package/lib/components/dnd/index.js +5 -5
- package/lib/components/settings/independents/dropdown/FlowsDropdownButton.js +2 -2
- package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +17 -5
- package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +2 -2
- package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +7 -1
- package/lib/components/variables/VariableInput.js +16 -2
- package/lib/components/variables/VariableTag.js +43 -2
- package/lib/components/variables/types.d.ts +2 -0
- package/lib/data-source/index.d.ts +12 -4
- package/lib/data-source/index.js +19 -13
- package/lib/data-source/sortCollectionsByInherits.d.ts +10 -0
- package/lib/data-source/sortCollectionsByInherits.js +71 -0
- package/lib/executor/FlowExecutor.d.ts +4 -5
- package/lib/executor/FlowExecutor.js +135 -100
- package/lib/flowContext.d.ts +33 -5
- package/lib/flowContext.js +193 -76
- package/lib/flowEngine.d.ts +8 -1
- package/lib/flowEngine.js +18 -6
- package/lib/flowSettings.d.ts +2 -1
- package/lib/flowSettings.js +12 -8
- package/lib/hooks/useApplyAutoFlows.js +2 -1
- package/lib/index.d.ts +7 -1
- package/lib/index.js +32 -3
- package/lib/locale/en-US.json +4 -2
- package/lib/locale/index.d.ts +4 -0
- package/lib/locale/zh-CN.json +4 -2
- package/lib/models/CollectionFieldModel.d.ts +2 -0
- package/lib/models/CollectionFieldModel.js +43 -3
- package/lib/models/flowModel.d.ts +28 -29
- package/lib/models/flowModel.js +114 -92
- package/lib/models/forkFlowModel.d.ts +4 -4
- package/lib/models/forkFlowModel.js +32 -8
- package/lib/provider.d.ts +3 -1
- package/lib/provider.js +7 -5
- package/lib/resources/multiRecordResource.js +2 -0
- package/lib/resources/singleRecordResource.js +1 -0
- package/lib/resources/sqlResource.d.ts +1 -0
- package/lib/resources/sqlResource.js +20 -24
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +27 -20
- package/lib/runjs-context/contexts/JSBlockRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/JSBlockRunJSContext.js +46 -33
- package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.d.ts +1 -2
- package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.js +14 -15
- package/lib/runjs-context/contexts/{LinkageRunJSContext.d.ts → JSColumnRunJSContext.d.ts} +6 -3
- package/lib/runjs-context/contexts/JSColumnRunJSContext.js +78 -0
- package/lib/runjs-context/contexts/JSFieldRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/JSFieldRunJSContext.js +28 -24
- package/lib/runjs-context/contexts/JSItemRunJSContext.d.ts +1 -6
- package/lib/runjs-context/contexts/JSItemRunJSContext.js +24 -20
- package/lib/runjs-context/contexts/JSRecordActionRunJSContext.d.ts +1 -2
- package/lib/runjs-context/contexts/JSRecordActionRunJSContext.js +16 -17
- package/lib/runjs-context/contexts/base.d.ts +9 -0
- package/lib/runjs-context/contexts/base.js +183 -0
- package/lib/runjs-context/helpers.d.ts +5 -2
- package/lib/runjs-context/helpers.js +36 -27
- package/lib/runjs-context/registry.d.ts +7 -4
- package/lib/runjs-context/registry.js +10 -42
- package/lib/runjs-context/setup.d.ts +9 -0
- package/lib/runjs-context/setup.js +82 -0
- package/lib/runjs-context/snippets/global/{copy-record-json.snippet.js → api-request.snippet.js} +25 -10
- package/lib/runjs-context/snippets/global/clipboard-copy-text.snippet.js +61 -0
- package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.js → import-esm.snippet.js} +26 -12
- package/lib/runjs-context/snippets/global/message-error.snippet.js +6 -0
- package/lib/runjs-context/snippets/global/message-success.snippet.js +6 -0
- package/lib/runjs-context/snippets/global/notification-open.snippet.d.ts +3 -8
- package/lib/runjs-context/snippets/global/notification-open.snippet.js +8 -1
- package/lib/runjs-context/snippets/global/open-view-dialog.snippet.js +10 -3
- package/lib/runjs-context/snippets/global/open-view-drawer.snippet.js +10 -3
- package/lib/runjs-context/snippets/global/query-selector.snippet.js +53 -0
- package/lib/runjs-context/snippets/global/require-amd.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/global/{requireAsync.snippet.js → require-amd.snippet.js} +16 -13
- package/lib/runjs-context/snippets/global/window-open.snippet.d.ts +3 -8
- package/lib/runjs-context/snippets/global/window-open.snippet.js +8 -1
- package/lib/runjs-context/snippets/index.d.ts +14 -3
- package/lib/runjs-context/snippets/index.js +161 -40
- package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.js +11 -2
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +99 -0
- package/lib/runjs-context/snippets/{libs → scene/block}/echarts-init.snippet.js +24 -7
- package/lib/runjs-context/snippets/scene/block/render-button-handler.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock → block}/render-button-handler.snippet.js +17 -9
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +57 -0
- package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.js +71 -0
- package/lib/runjs-context/snippets/scene/block/render-react-jsx.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.js → block/render-react-jsx.snippet.js} +26 -13
- package/lib/runjs-context/snippets/scene/block/render-react.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsblock → block}/render-react.snippet.js +18 -17
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +95 -0
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +84 -0
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +60 -0
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +283 -0
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +124 -0
- package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsfield → detail}/color-by-value.snippet.js +13 -3
- package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/{global → scene/detail}/copy-to-clipboard.snippet.js +28 -6
- package/lib/runjs-context/snippets/scene/detail/format-number.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsfield → detail}/format-number.snippet.js +13 -3
- package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsfield → detail}/innerHTML-value.snippet.js +13 -3
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +82 -0
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +80 -0
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +74 -0
- package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.js +63 -0
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +81 -0
- package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.js +74 -0
- package/lib/runjs-context/snippets/scene/form/render-basic.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.js +11 -2
- package/lib/runjs-context/snippets/scene/form/set-disabled.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/form/set-field-value.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/form/set-required.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.js +67 -0
- package/lib/runjs-context/snippets/scene/form/toggle-visible.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.js +12 -3
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/table/collection-selected-count.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.js +11 -2
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +79 -0
- package/lib/runjs-context/snippets/scene/table/destroy-selected.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/{global/log-json-record.snippet.js → scene/table/destroy-selected.snippet.js} +24 -11
- package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.js +64 -0
- package/lib/runjs-context/snippets/scene/table/iterate-selected-rows.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.js +11 -2
- package/lib/runjs-context/snippets/types.d.ts +9 -1
- package/lib/types.d.ts +28 -3
- package/lib/types.js +4 -3
- package/lib/utils/buildSettingsViewInputArgs.d.ts +19 -0
- package/lib/utils/buildSettingsViewInputArgs.js +75 -0
- package/lib/utils/createEphemeralContext.d.ts +13 -0
- package/lib/utils/createEphemeralContext.js +140 -0
- package/lib/utils/index.d.ts +3 -2
- package/lib/utils/index.js +5 -2
- package/lib/utils/jsxTransform.d.ts +15 -0
- package/lib/utils/jsxTransform.js +68 -0
- package/lib/utils/params-resolvers.js +3 -3
- package/lib/utils/safeGlobals.d.ts +5 -3
- package/lib/utils/safeGlobals.js +40 -0
- package/lib/utils/schema-utils.js +2 -2
- package/lib/utils/serverContextParams.d.ts +1 -0
- package/lib/utils/variablesParams.d.ts +9 -5
- package/lib/utils/variablesParams.js +47 -36
- package/lib/views/PageComponent.js +2 -1
- package/lib/views/createViewMeta.d.ts +29 -1
- package/lib/views/createViewMeta.js +338 -72
- package/lib/views/index.d.ts +1 -0
- package/lib/views/index.js +3 -0
- package/lib/views/useDialog.d.ts +8 -8
- package/lib/views/useDialog.js +8 -7
- package/lib/views/useDrawer.d.ts +8 -8
- package/lib/views/useDrawer.js +40 -26
- package/lib/views/usePage.d.ts +8 -8
- package/lib/views/usePage.js +8 -7
- package/package.json +5 -3
- package/src/BlockScopedFlowEngine.ts +86 -0
- package/src/FlowContextProvider.tsx +4 -2
- package/src/JSRunner.ts +3 -0
- package/src/ViewScopedFlowEngine.ts +1 -1
- package/src/__tests__/JSRunner.test.ts +62 -53
- package/src/__tests__/blockScopedFlowEngine.test.ts +154 -0
- package/src/__tests__/createViewMeta.popup.test.ts +132 -0
- package/src/__tests__/flow-engine.test.ts +3 -0
- package/src/__tests__/flowContextCreateJSRunner.test.ts +163 -0
- package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
- package/src/__tests__/flowRunJSContextDefine.test.ts +508 -0
- package/src/__tests__/globalFlowRegistry.test.ts +1 -1
- package/src/__tests__/runjsContext.test.ts +216 -35
- package/src/__tests__/runjsContextImplementations.test.ts +217 -0
- package/src/__tests__/runjsContextRuntime.test.ts +269 -0
- package/src/__tests__/runjsEdgeCases.test.ts +281 -0
- package/src/__tests__/runjsLocales.test.ts +36 -0
- package/src/__tests__/runjsRuntimeFeatures.test.ts +449 -0
- package/src/__tests__/runjsSnippets.test.ts +140 -0
- package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
- package/src/components/DynamicFlowsEditor.tsx +3 -4
- package/src/components/FieldModelRenderer.tsx +16 -5
- package/src/components/FieldSkeleton.tsx +27 -0
- package/src/components/FlowContextSelector.tsx +6 -2
- package/src/components/FlowModelRenderer.tsx +30 -78
- package/src/components/FormItem.tsx +8 -1
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +5 -5
- package/src/components/__tests__/gridDragPlanner.test.ts +494 -0
- package/src/components/dnd/README.md +149 -0
- package/src/components/dnd/findModelUidPosition.ts +26 -0
- package/src/components/dnd/gridDragPlanner.ts +659 -0
- package/src/components/dnd/index.tsx +3 -3
- package/src/components/settings/independents/dropdown/FlowsDropdownButton.tsx +1 -1
- package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +17 -4
- package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +1 -1
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +14 -1
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +424 -0
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +5 -7
- package/src/components/variables/VariableInput.tsx +22 -2
- package/src/components/variables/VariableTag.tsx +54 -2
- package/src/components/variables/types.ts +2 -0
- package/src/data-source/__tests__/sortCollectionsByInherits.test.ts +125 -0
- package/src/data-source/index.ts +17 -11
- package/src/data-source/sortCollectionsByInherits.ts +61 -0
- package/src/executor/FlowExecutor.ts +178 -121
- package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
- package/src/executor/__tests__/flowExecutor.test.ts +151 -5
- package/src/flowContext.ts +266 -97
- package/src/flowEngine.ts +21 -6
- package/src/flowSettings.ts +9 -4
- package/src/hooks/useApplyAutoFlows.ts +3 -1
- package/src/index.ts +12 -1
- package/src/locale/en-US.json +4 -2
- package/src/locale/zh-CN.json +4 -2
- package/src/models/CollectionFieldModel.tsx +43 -4
- package/src/models/__tests__/flowModel.getFlows.sort.test.ts +4 -4
- package/src/models/__tests__/flowModel.test.ts +234 -111
- package/src/models/__tests__/forkFlowModel.test.ts +22 -7
- package/src/models/flowModel.tsx +149 -125
- package/src/models/forkFlowModel.ts +41 -8
- package/src/provider.tsx +10 -7
- package/src/resources/multiRecordResource.ts +2 -0
- package/src/resources/singleRecordResource.ts +1 -0
- package/src/resources/sqlResource.ts +20 -25
- package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +28 -21
- package/src/runjs-context/contexts/JSBlockRunJSContext.ts +46 -34
- package/src/runjs-context/contexts/JSCollectionActionRunJSContext.ts +15 -16
- package/src/runjs-context/contexts/JSColumnRunJSContext.ts +58 -0
- package/src/runjs-context/contexts/JSFieldRunJSContext.ts +30 -25
- package/src/runjs-context/contexts/JSItemRunJSContext.ts +25 -21
- package/src/runjs-context/contexts/JSRecordActionRunJSContext.ts +17 -18
- package/src/runjs-context/contexts/base.ts +171 -0
- package/src/runjs-context/helpers.ts +32 -30
- package/src/runjs-context/registry.ts +16 -47
- package/src/runjs-context/setup.ts +51 -0
- package/src/runjs-context/snippets/global/api-request.snippet.ts +38 -0
- package/src/runjs-context/snippets/global/clipboard-copy-text.snippet.ts +42 -0
- package/src/runjs-context/snippets/global/import-esm.snippet.ts +39 -0
- package/src/runjs-context/snippets/global/message-error.snippet.ts +6 -0
- package/src/runjs-context/snippets/global/message-success.snippet.ts +6 -0
- package/src/runjs-context/snippets/global/notification-open.snippet.ts +11 -1
- package/src/runjs-context/snippets/global/open-view-dialog.snippet.ts +10 -3
- package/src/runjs-context/snippets/global/open-view-drawer.snippet.ts +10 -3
- package/src/runjs-context/snippets/global/query-selector.snippet.ts +34 -0
- package/src/runjs-context/snippets/global/require-amd.snippet.ts +30 -0
- package/src/runjs-context/snippets/global/window-open.snippet.ts +11 -1
- package/src/runjs-context/snippets/index.ts +177 -39
- package/src/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.ts +14 -2
- package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +80 -0
- package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +44 -0
- package/src/runjs-context/snippets/scene/block/render-button-handler.snippet.ts +35 -0
- package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +38 -0
- package/src/runjs-context/snippets/scene/block/render-info-card.snippet.ts +52 -0
- package/src/runjs-context/snippets/scene/block/render-react-jsx.snippet.ts +39 -0
- package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +38 -0
- package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +76 -0
- package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +65 -0
- package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +46 -0
- package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +264 -0
- package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +105 -0
- package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +33 -0
- package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +32 -0
- package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +31 -0
- package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +63 -0
- package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +61 -0
- package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +55 -0
- package/src/runjs-context/snippets/scene/form/calculate-total.snippet.ts +44 -0
- package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +62 -0
- package/src/runjs-context/snippets/scene/form/conditional-required.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/form/copy-field-values.snippet.ts +55 -0
- package/src/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.ts +14 -2
- package/src/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.ts +48 -0
- package/src/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.ts +15 -3
- package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.ts +14 -2
- package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +60 -0
- package/src/runjs-context/snippets/scene/table/destroy-selected.snippet.ts +36 -0
- package/src/runjs-context/snippets/scene/table/export-selected-json.snippet.ts +45 -0
- package/src/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.ts +14 -2
- package/src/runjs-context/snippets/types.ts +5 -1
- package/src/types.ts +34 -0
- package/src/utils/__tests__/jsxTransform.test.ts +38 -0
- package/src/utils/__tests__/safeGlobals.test.ts +22 -1
- package/src/utils/buildSettingsViewInputArgs.ts +72 -0
- package/src/utils/createEphemeralContext.ts +142 -0
- package/src/utils/index.ts +2 -2
- package/src/utils/jsxTransform.ts +39 -0
- package/src/utils/params-resolvers.ts +2 -2
- package/src/utils/safeGlobals.ts +49 -3
- package/src/utils/schema-utils.ts +1 -1
- package/src/utils/serverContextParams.ts +1 -0
- package/src/utils/variablesParams.ts +50 -38
- package/src/views/PageComponent.tsx +1 -1
- package/src/views/createViewMeta.ts +393 -70
- package/src/views/index.tsx +1 -0
- package/src/views/useDialog.tsx +12 -10
- package/src/views/useDrawer.tsx +60 -36
- package/src/views/usePage.tsx +13 -10
- package/lib/components/dnd/getMousePositionOnElement.d.ts +0 -50
- package/lib/components/dnd/getMousePositionOnElement.js +0 -95
- package/lib/components/dnd/moveBlock.d.ts +0 -33
- package/lib/components/dnd/moveBlock.js +0 -302
- package/lib/runjs-context/contexts/FlowRunJSContext.d.ts +0 -38
- package/lib/runjs-context/contexts/FlowRunJSContext.js +0 -217
- package/lib/runjs-context/contexts/LinkageRunJSContext.js +0 -62
- package/lib/runjs-context/index.d.ts +0 -19
- package/lib/runjs-context/index.js +0 -57
- package/lib/runjs-context/snippets/global/api-request-get.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/api-request-get.snippet.js +0 -42
- package/lib/runjs-context/snippets/global/api-request-post.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/api-request-post.snippet.js +0 -42
- package/lib/runjs-context/snippets/global/console-log-ctx.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/console-log-ctx.snippet.js +0 -41
- package/lib/runjs-context/snippets/global/sleep.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/sleep.snippet.js +0 -43
- package/lib/runjs-context/snippets/global/try-catch-async.snippet.d.ts +0 -16
- package/lib/runjs-context/snippets/global/try-catch-async.snippet.js +0 -44
- package/lib/runjs-context/snippets/libs/echarts-init.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/collection-selected-count.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/iterate-selected-rows.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.js +0 -43
- package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.js +0 -40
- package/lib/runjs-context/snippets/scene/jsblock/add-event-listener.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.js +0 -42
- package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.js +0 -41
- package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.js +0 -41
- package/lib/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsblock/render-react.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsfield/color-by-value.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsfield/format-number.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/jsitem/render-basic.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/set-disabled.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/set-field-value.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/set-required.snippet.d.ts +0 -15
- package/lib/runjs-context/snippets/scene/linkage/toggle-visible.snippet.d.ts +0 -15
- package/src/components/dnd/getMousePositionOnElement.ts +0 -115
- package/src/components/dnd/moveBlock.ts +0 -379
- package/src/runjs-context/contexts/FlowRunJSContext.ts +0 -190
- package/src/runjs-context/contexts/LinkageRunJSContext.ts +0 -35
- package/src/runjs-context/index.ts +0 -20
- package/src/runjs-context/snippets/global/api-request-get.snippet.ts +0 -20
- package/src/runjs-context/snippets/global/api-request-post.snippet.ts +0 -20
- package/src/runjs-context/snippets/global/console-log-ctx.snippet.ts +0 -19
- package/src/runjs-context/snippets/global/copy-record-json.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/copy-to-clipboard.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/log-json-record.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/requireAsync.snippet.ts +0 -24
- package/src/runjs-context/snippets/global/sleep.snippet.ts +0 -21
- package/src/runjs-context/snippets/global/try-catch-async.snippet.ts +0 -22
- package/src/runjs-context/snippets/global/view-navigation-push.snippet.ts +0 -23
- package/src/runjs-context/snippets/libs/echarts-init.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/actions/record-id-message.snippet.ts +0 -21
- package/src/runjs-context/snippets/scene/actions/run-action-basic.snippet.ts +0 -18
- package/src/runjs-context/snippets/scene/jsblock/append-style.snippet.ts +0 -20
- package/src/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.ts +0 -19
- package/src/runjs-context/snippets/scene/jsblock/render-basic.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.ts +0 -24
- package/src/runjs-context/snippets/scene/jsblock/render-card.snippet.ts +0 -30
- package/src/runjs-context/snippets/scene/jsblock/render-react.snippet.ts +0 -34
- package/src/runjs-context/snippets/scene/jsfield/color-by-value.snippet.ts +0 -20
- package/src/runjs-context/snippets/scene/jsfield/format-number.snippet.ts +0 -19
- package/src/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.ts +0 -18
- /package/lib/runjs-context/snippets/global/{copy-record-json.snippet.d.ts → api-request.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/global/{copy-to-clipboard.snippet.d.ts → clipboard-copy-text.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/global/{log-json-record.snippet.d.ts → import-esm.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.d.ts → query-selector.snippet.d.ts} +0 -0
- /package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.d.ts → block/echarts-init.snippet.d.ts} +0 -0
|
@@ -126,7 +126,19 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
126
126
|
message.success(t('UID copied to clipboard'));
|
|
127
127
|
} catch (error) {
|
|
128
128
|
console.error(t('Copy failed'), ':', error);
|
|
129
|
-
|
|
129
|
+
// 如果不是 HTTPS 协议,给出更具体的提示:HTTP 下剪贴板 API 不可用
|
|
130
|
+
const isHttps = typeof window !== 'undefined' && window.location?.protocol === 'https:';
|
|
131
|
+
if (!isHttps) {
|
|
132
|
+
message.error(
|
|
133
|
+
t(
|
|
134
|
+
'Copy failed under HTTP. Clipboard API is unavailable on non-HTTPS pages. Please copy [{{uid}}] manually.',
|
|
135
|
+
{ uid },
|
|
136
|
+
),
|
|
137
|
+
);
|
|
138
|
+
return;
|
|
139
|
+
} else {
|
|
140
|
+
message.error(t('Copy failed, please copy [{{uid}}] manually.', { uid }));
|
|
141
|
+
}
|
|
130
142
|
}
|
|
131
143
|
},
|
|
132
144
|
[message, t],
|
|
@@ -255,7 +267,8 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
255
267
|
const getModelConfigurableFlowsAndSteps = useCallback(
|
|
256
268
|
async (targetModel: FlowModel, modelKey?: string): Promise<FlowInfo[]> => {
|
|
257
269
|
try {
|
|
258
|
-
|
|
270
|
+
// 仅使用静态流(类级全局注册的 flows),排除实例动态流
|
|
271
|
+
const flows = (targetModel.constructor as typeof FlowModel).globalFlowRegistry.getFlows();
|
|
259
272
|
|
|
260
273
|
const flowsArray = Array.from(flows.values());
|
|
261
274
|
|
|
@@ -278,9 +291,9 @@ export const DefaultSettingsIcon: React.FC<DefaultSettingsIconProps> = ({
|
|
|
278
291
|
let stepTitle = actionStep.title;
|
|
279
292
|
if (actionStep.use) {
|
|
280
293
|
try {
|
|
281
|
-
const action = targetModel.
|
|
294
|
+
const action = targetModel.getAction?.(actionStep.use);
|
|
282
295
|
hasActionUiSchema = action && action.uiSchema != null;
|
|
283
|
-
stepTitle = stepTitle || action
|
|
296
|
+
stepTitle = stepTitle || action?.title;
|
|
284
297
|
} catch (error) {
|
|
285
298
|
console.warn(t('Failed to get action {{action}}', { action: actionStep.use }), ':', error);
|
|
286
299
|
}
|
|
@@ -162,7 +162,7 @@ const FlowsContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
|
|
|
162
162
|
// 如果step使用了action,也获取action的uiSchema
|
|
163
163
|
let actionUiSchema = {};
|
|
164
164
|
if (actionStep.use) {
|
|
165
|
-
const action = model.
|
|
165
|
+
const action = model.getAction?.(actionStep.use);
|
|
166
166
|
if (action && action.uiSchema) {
|
|
167
167
|
actionUiSchema = action.uiSchema;
|
|
168
168
|
}
|
|
@@ -14,7 +14,14 @@ import { Button, Space } from 'antd';
|
|
|
14
14
|
import React, { useEffect } from 'react';
|
|
15
15
|
import { FlowSettingsContextProvider, useFlowSettingsContext } from '../../../../hooks/useFlowSettingsContext';
|
|
16
16
|
import { StepSettingsDialogProps } from '../../../../types';
|
|
17
|
-
import {
|
|
17
|
+
import {
|
|
18
|
+
compileUiSchema,
|
|
19
|
+
FlowExitException,
|
|
20
|
+
getT,
|
|
21
|
+
resolveDefaultParams,
|
|
22
|
+
resolveStepUiSchema,
|
|
23
|
+
buildSettingsViewInputArgs,
|
|
24
|
+
} from '../../../../utils';
|
|
18
25
|
|
|
19
26
|
const SchemaField = createSchemaField();
|
|
20
27
|
|
|
@@ -135,6 +142,12 @@ const openStepSettingsDialog = async ({
|
|
|
135
142
|
width: dialogWidth,
|
|
136
143
|
destroyOnClose: true,
|
|
137
144
|
...toJS(uiModeProps),
|
|
145
|
+
// 透传 navigation,便于变量元信息根据真实视图栈推断父级弹窗
|
|
146
|
+
inputArgs: buildSettingsViewInputArgs(
|
|
147
|
+
model as any,
|
|
148
|
+
{ ...(toJS(uiModeProps)?.inputArgs || {}), __isSettingsPopup: true },
|
|
149
|
+
{ navigationOverride: ctx?.view?.navigation },
|
|
150
|
+
),
|
|
138
151
|
onClose: () => {
|
|
139
152
|
if (cleanup) {
|
|
140
153
|
cleanup();
|
|
@@ -0,0 +1,424 @@
|
|
|
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 React from 'react';
|
|
11
|
+
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
|
12
|
+
import { render, cleanup, waitFor } from '@testing-library/react';
|
|
13
|
+
import { App, ConfigProvider } from 'antd';
|
|
14
|
+
|
|
15
|
+
import { FlowEngine } from '../../../../../flowEngine';
|
|
16
|
+
import { FlowModel } from '../../../../../models/flowModel';
|
|
17
|
+
import { DefaultSettingsIcon } from '../DefaultSettingsIcon';
|
|
18
|
+
|
|
19
|
+
// ---- Mock antd to capture Dropdown menu props ----
|
|
20
|
+
const dropdownMenus: any[] = [];
|
|
21
|
+
vi.mock('antd', async (importOriginal) => {
|
|
22
|
+
const Dropdown = (props: any) => {
|
|
23
|
+
(globalThis as any).__lastDropdownMenu = props.menu;
|
|
24
|
+
dropdownMenus.push(props.menu);
|
|
25
|
+
return React.createElement('span', { 'data-testid': 'dropdown' }, props.children);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const App = Object.assign(({ children }: any) => React.createElement(React.Fragment, null, children), {
|
|
29
|
+
useApp: () => ({ message: { success: () => {}, error: () => {}, info: () => {} } }),
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const ConfigProvider = ({ children }: any) => React.createElement(React.Fragment, null, children);
|
|
33
|
+
const Modal = {
|
|
34
|
+
confirm: (opts: any) => {
|
|
35
|
+
if (opts && typeof opts.onOk === 'function') return opts.onOk();
|
|
36
|
+
},
|
|
37
|
+
error: vi.fn(),
|
|
38
|
+
};
|
|
39
|
+
const Typography = {
|
|
40
|
+
Paragraph: ({ children }: any) => React.createElement('p', null, children ?? 'Paragraph'),
|
|
41
|
+
Text: ({ children }: any) => React.createElement('span', null, children ?? 'Text'),
|
|
42
|
+
};
|
|
43
|
+
const Collapse = Object.assign((props: any) => React.createElement('div', null, props.children ?? 'Collapse'), {
|
|
44
|
+
Panel: (props: any) => React.createElement('div', null, props.children ?? 'Panel'),
|
|
45
|
+
});
|
|
46
|
+
const Space = ({ children }: any) => React.createElement('div', null, children);
|
|
47
|
+
const FormItem = (props: any) => React.createElement('div', null, props.children ?? 'FormItem');
|
|
48
|
+
const Form = Object.assign((props: any) => React.createElement('form', null, props.children ?? 'Form'), {
|
|
49
|
+
Item: FormItem,
|
|
50
|
+
useForm: () => [{ setFieldsValue: (_: any) => {} }],
|
|
51
|
+
});
|
|
52
|
+
const Input: any = (props: any) => React.createElement('input', props);
|
|
53
|
+
Input.TextArea = (props: any) => React.createElement('textarea', props);
|
|
54
|
+
const InputNumber = (props: any) => React.createElement('input', { ...props, type: 'number' });
|
|
55
|
+
const Select = (props: any) => React.createElement('select', props);
|
|
56
|
+
const Switch = (props: any) => React.createElement('input', { ...props, type: 'checkbox' });
|
|
57
|
+
const Alert = (props: any) => React.createElement('div', { role: 'alert' }, props.message ?? 'Alert');
|
|
58
|
+
const Button = (props: any) => React.createElement('button', props, props.children ?? 'Button');
|
|
59
|
+
const Result = (props: any) => React.createElement('div', null, props.children ?? 'Result');
|
|
60
|
+
|
|
61
|
+
// Keep other components from original mock/default
|
|
62
|
+
return {
|
|
63
|
+
Dropdown,
|
|
64
|
+
App,
|
|
65
|
+
ConfigProvider,
|
|
66
|
+
Modal,
|
|
67
|
+
Typography,
|
|
68
|
+
Collapse,
|
|
69
|
+
Space,
|
|
70
|
+
Form,
|
|
71
|
+
Input,
|
|
72
|
+
InputNumber,
|
|
73
|
+
Select,
|
|
74
|
+
Switch,
|
|
75
|
+
Alert,
|
|
76
|
+
Button,
|
|
77
|
+
Result,
|
|
78
|
+
};
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
describe('DefaultSettingsIcon - only static flows are shown', () => {
|
|
82
|
+
beforeEach(() => {
|
|
83
|
+
dropdownMenus.length = 0;
|
|
84
|
+
(globalThis as any).__lastDropdownMenu = undefined;
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
afterEach(() => {
|
|
88
|
+
cleanup();
|
|
89
|
+
vi.clearAllMocks();
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('excludes instance (dynamic) flows from the settings menu', async () => {
|
|
93
|
+
class TestFlowModel extends FlowModel {}
|
|
94
|
+
|
|
95
|
+
const engine = new FlowEngine();
|
|
96
|
+
const model = new TestFlowModel({ uid: 'model-static-only', flowEngine: engine });
|
|
97
|
+
|
|
98
|
+
// register one static flow with a visible step
|
|
99
|
+
TestFlowModel.registerFlow({
|
|
100
|
+
key: 'static1',
|
|
101
|
+
title: 'Static Flow',
|
|
102
|
+
steps: {
|
|
103
|
+
general: {
|
|
104
|
+
title: 'General',
|
|
105
|
+
uiSchema: {
|
|
106
|
+
field: { type: 'string', 'x-component': 'Input' },
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
// add a dynamic (instance) flow which should NOT appear in menu
|
|
113
|
+
model.flowRegistry.addFlow('dyn1', {
|
|
114
|
+
title: 'Dynamic Flow',
|
|
115
|
+
steps: {
|
|
116
|
+
general: {
|
|
117
|
+
title: 'General (Dyn)',
|
|
118
|
+
uiSchema: {
|
|
119
|
+
field: { type: 'string', 'x-component': 'Input' },
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
render(
|
|
126
|
+
React.createElement(
|
|
127
|
+
ConfigProvider as any,
|
|
128
|
+
null,
|
|
129
|
+
React.createElement(
|
|
130
|
+
App as any,
|
|
131
|
+
null,
|
|
132
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
133
|
+
model,
|
|
134
|
+
// 关闭常用操作,避免干扰断言
|
|
135
|
+
showDeleteButton: false,
|
|
136
|
+
showCopyUidButton: false,
|
|
137
|
+
}),
|
|
138
|
+
),
|
|
139
|
+
),
|
|
140
|
+
);
|
|
141
|
+
|
|
142
|
+
// 等待菜单内出现静态流分组,确保异步加载完成
|
|
143
|
+
await waitFor(() => {
|
|
144
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
145
|
+
expect(menu).toBeTruthy();
|
|
146
|
+
const items = (menu?.items || []) as any[];
|
|
147
|
+
const groupLabels = items.filter((it) => it.type === 'group').map((it) => String(it.label));
|
|
148
|
+
expect(groupLabels).toContain('Static Flow');
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
152
|
+
const items = (menu?.items || []) as any[];
|
|
153
|
+
|
|
154
|
+
// groups for flows are labeled with flow.title; ensure static group exists, dynamic group不存在
|
|
155
|
+
const groupLabels = items.filter((it) => it.type === 'group').map((it) => String(it.label));
|
|
156
|
+
expect(groupLabels).toContain('Static Flow');
|
|
157
|
+
expect(groupLabels).not.toContain('Dynamic Flow');
|
|
158
|
+
|
|
159
|
+
// 静态流的 step 存在(key: `${flowKey}:${stepKey}`),动态流 step 不存在
|
|
160
|
+
expect(items.some((it) => String(it.key || '').startsWith('static1:'))).toBe(true);
|
|
161
|
+
expect(items.some((it) => String(it.key || '').startsWith('dyn1:'))).toBe(false);
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
it('filters out steps with hideInSettings and keeps visible ones', async () => {
|
|
165
|
+
class TestFlowModel extends FlowModel {}
|
|
166
|
+
const engine = new FlowEngine();
|
|
167
|
+
const model = new TestFlowModel({ uid: 'm-hide', flowEngine: engine });
|
|
168
|
+
|
|
169
|
+
TestFlowModel.registerFlow({
|
|
170
|
+
key: 'flowA',
|
|
171
|
+
title: 'Flow A',
|
|
172
|
+
steps: {
|
|
173
|
+
hidden: { title: 'Hidden', hideInSettings: true, uiSchema: { a: { type: 'string', 'x-component': 'Input' } } },
|
|
174
|
+
visible: { title: 'Visible', uiSchema: { b: { type: 'string', 'x-component': 'Input' } } },
|
|
175
|
+
},
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
render(
|
|
179
|
+
React.createElement(
|
|
180
|
+
ConfigProvider as any,
|
|
181
|
+
null,
|
|
182
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
183
|
+
),
|
|
184
|
+
);
|
|
185
|
+
|
|
186
|
+
await waitFor(() => {
|
|
187
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
188
|
+
const items = (menu?.items || []) as any[];
|
|
189
|
+
expect(items.some((it) => String(it.key || '') === 'flowA:visible')).toBe(true);
|
|
190
|
+
expect(items.some((it) => String(it.key || '') === 'flowA:hidden')).toBe(false);
|
|
191
|
+
});
|
|
192
|
+
});
|
|
193
|
+
|
|
194
|
+
it('includes step when uiSchema provided by action (step.use)', async () => {
|
|
195
|
+
class TestFlowModel extends FlowModel {}
|
|
196
|
+
const engine = new FlowEngine();
|
|
197
|
+
const model = new TestFlowModel({ uid: 'm-action', flowEngine: engine });
|
|
198
|
+
|
|
199
|
+
// Step has no uiSchema but uses an action that provides uiSchema
|
|
200
|
+
TestFlowModel.registerFlow({
|
|
201
|
+
key: 'flowB',
|
|
202
|
+
title: 'Flow B',
|
|
203
|
+
steps: {
|
|
204
|
+
useAction: { title: 'Use Action', use: 'act' },
|
|
205
|
+
},
|
|
206
|
+
});
|
|
207
|
+
|
|
208
|
+
// Stub getAction to provide uiSchema
|
|
209
|
+
(model as any).getAction = vi.fn().mockReturnValue({
|
|
210
|
+
name: 'act',
|
|
211
|
+
title: 'Action Title',
|
|
212
|
+
uiSchema: { x: { type: 'string', 'x-component': 'Input' } },
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
render(
|
|
216
|
+
React.createElement(
|
|
217
|
+
ConfigProvider as any,
|
|
218
|
+
null,
|
|
219
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
220
|
+
),
|
|
221
|
+
);
|
|
222
|
+
|
|
223
|
+
await waitFor(() => {
|
|
224
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
225
|
+
const items = (menu?.items || []) as any[];
|
|
226
|
+
expect(items.some((it) => String(it.key || '') === 'flowB:useAction')).toBe(true);
|
|
227
|
+
});
|
|
228
|
+
});
|
|
229
|
+
|
|
230
|
+
it('clicking a step item opens flow settings with correct args', async () => {
|
|
231
|
+
class TestFlowModel extends FlowModel {}
|
|
232
|
+
const engine = new FlowEngine();
|
|
233
|
+
const model = new TestFlowModel({ uid: 'm-open', flowEngine: engine });
|
|
234
|
+
const openSpy = vi.spyOn(model, 'openFlowSettings').mockResolvedValue(undefined as any);
|
|
235
|
+
|
|
236
|
+
TestFlowModel.registerFlow({
|
|
237
|
+
key: 'flowC',
|
|
238
|
+
title: 'Flow C',
|
|
239
|
+
steps: {
|
|
240
|
+
general: { title: 'General', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } },
|
|
241
|
+
},
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
render(
|
|
245
|
+
React.createElement(
|
|
246
|
+
ConfigProvider as any,
|
|
247
|
+
null,
|
|
248
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
249
|
+
),
|
|
250
|
+
);
|
|
251
|
+
|
|
252
|
+
await waitFor(() => {
|
|
253
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
254
|
+
});
|
|
255
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
256
|
+
menu.onClick?.({ key: 'flowC:general' });
|
|
257
|
+
expect(openSpy).toHaveBeenCalledWith({ flowKey: 'flowC', stepKey: 'general' });
|
|
258
|
+
});
|
|
259
|
+
|
|
260
|
+
it('copy UID action writes model uid to clipboard', async () => {
|
|
261
|
+
class TestFlowModel extends FlowModel {}
|
|
262
|
+
const engine = new FlowEngine();
|
|
263
|
+
const model = new TestFlowModel({ uid: 'm-copy', flowEngine: engine });
|
|
264
|
+
|
|
265
|
+
TestFlowModel.registerFlow({
|
|
266
|
+
key: 'flowD',
|
|
267
|
+
title: 'Flow D',
|
|
268
|
+
steps: { s: { title: 'S', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } } },
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
// mock clipboard
|
|
272
|
+
Object.defineProperty(window.navigator, 'clipboard', {
|
|
273
|
+
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
274
|
+
configurable: true,
|
|
275
|
+
});
|
|
276
|
+
|
|
277
|
+
render(
|
|
278
|
+
React.createElement(
|
|
279
|
+
ConfigProvider as any,
|
|
280
|
+
null,
|
|
281
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
282
|
+
),
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
await waitFor(() => {
|
|
286
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
287
|
+
});
|
|
288
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
289
|
+
menu.onClick?.({ key: 'copy-uid' });
|
|
290
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('m-copy');
|
|
291
|
+
});
|
|
292
|
+
|
|
293
|
+
it('delete action calls model.destroy()', async () => {
|
|
294
|
+
class TestFlowModel extends FlowModel {}
|
|
295
|
+
const engine = new FlowEngine();
|
|
296
|
+
const model = new TestFlowModel({ uid: 'm-del', flowEngine: engine });
|
|
297
|
+
const destroySpy = vi.spyOn(model, 'destroy').mockResolvedValue(undefined as any);
|
|
298
|
+
|
|
299
|
+
TestFlowModel.registerFlow({
|
|
300
|
+
key: 'flowE',
|
|
301
|
+
title: 'Flow E',
|
|
302
|
+
steps: { s: { title: 'S', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } } },
|
|
303
|
+
});
|
|
304
|
+
|
|
305
|
+
render(
|
|
306
|
+
React.createElement(
|
|
307
|
+
ConfigProvider as any,
|
|
308
|
+
null,
|
|
309
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
310
|
+
),
|
|
311
|
+
);
|
|
312
|
+
|
|
313
|
+
await waitFor(() => {
|
|
314
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
315
|
+
});
|
|
316
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
317
|
+
menu.onClick?.({ key: 'delete' });
|
|
318
|
+
expect(destroySpy).toHaveBeenCalled();
|
|
319
|
+
});
|
|
320
|
+
|
|
321
|
+
it('shows sub-model steps with modelKey when flattenSubMenus=false and menuLevels=2', async () => {
|
|
322
|
+
class Parent extends FlowModel {}
|
|
323
|
+
class Child extends FlowModel {}
|
|
324
|
+
const engine = new FlowEngine();
|
|
325
|
+
const parent = new Parent({ uid: 'parent-1', flowEngine: engine });
|
|
326
|
+
const child = new Child({ uid: 'child-1', flowEngine: engine });
|
|
327
|
+
|
|
328
|
+
// child static flow
|
|
329
|
+
Child.registerFlow({
|
|
330
|
+
key: 'childFlow',
|
|
331
|
+
title: 'Child Flow',
|
|
332
|
+
steps: { cstep: { title: 'C', uiSchema: { x: { type: 'string', 'x-component': 'Input' } } } },
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
parent.addSubModel('items', child);
|
|
336
|
+
|
|
337
|
+
render(
|
|
338
|
+
React.createElement(
|
|
339
|
+
ConfigProvider as any,
|
|
340
|
+
null,
|
|
341
|
+
React.createElement(
|
|
342
|
+
App as any,
|
|
343
|
+
null,
|
|
344
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
345
|
+
model: parent,
|
|
346
|
+
menuLevels: 2,
|
|
347
|
+
flattenSubMenus: false,
|
|
348
|
+
}),
|
|
349
|
+
),
|
|
350
|
+
),
|
|
351
|
+
);
|
|
352
|
+
|
|
353
|
+
await waitFor(() => {
|
|
354
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
355
|
+
expect(menu).toBeTruthy();
|
|
356
|
+
const items = (menu?.items || []) as any[];
|
|
357
|
+
const subMenu = items.find((it) => Array.isArray(it?.children));
|
|
358
|
+
expect(subMenu).toBeTruthy();
|
|
359
|
+
expect(subMenu!.children.some((it: any) => String(it.key).startsWith('items[0]:childFlow:cstep'))).toBe(true);
|
|
360
|
+
});
|
|
361
|
+
});
|
|
362
|
+
|
|
363
|
+
it('adds "Copy popup UID" for popupSettings flow (current model and sub-model)', async () => {
|
|
364
|
+
class Parent extends FlowModel {}
|
|
365
|
+
class Child extends FlowModel {}
|
|
366
|
+
const engine = new FlowEngine();
|
|
367
|
+
const parent = new Parent({ uid: 'parent-2', flowEngine: engine });
|
|
368
|
+
const child = new Child({ uid: 'child-2', flowEngine: engine });
|
|
369
|
+
|
|
370
|
+
// current model popupSettings
|
|
371
|
+
Parent.registerFlow({
|
|
372
|
+
key: 'popupSettings',
|
|
373
|
+
title: 'Popup',
|
|
374
|
+
steps: { stage: { title: 'Stage', uiSchema: { a: { type: 'string', 'x-component': 'Input' } } } },
|
|
375
|
+
});
|
|
376
|
+
// sub model popupSettings
|
|
377
|
+
Child.registerFlow({
|
|
378
|
+
key: 'popupSettings',
|
|
379
|
+
title: 'Popup Child',
|
|
380
|
+
steps: { stage: { title: 'Stage', uiSchema: { a: { type: 'string', 'x-component': 'Input' } } } },
|
|
381
|
+
});
|
|
382
|
+
parent.addSubModel('items', child);
|
|
383
|
+
|
|
384
|
+
// mock clipboard
|
|
385
|
+
Object.defineProperty(window.navigator, 'clipboard', {
|
|
386
|
+
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
387
|
+
configurable: true,
|
|
388
|
+
});
|
|
389
|
+
|
|
390
|
+
render(
|
|
391
|
+
React.createElement(
|
|
392
|
+
ConfigProvider as any,
|
|
393
|
+
null,
|
|
394
|
+
React.createElement(
|
|
395
|
+
App as any,
|
|
396
|
+
null,
|
|
397
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
398
|
+
model: parent,
|
|
399
|
+
menuLevels: 2,
|
|
400
|
+
flattenSubMenus: true,
|
|
401
|
+
}),
|
|
402
|
+
),
|
|
403
|
+
),
|
|
404
|
+
);
|
|
405
|
+
|
|
406
|
+
// 等待“Copy popup UID”对应的菜单项出现,避免异步时序导致的偶发失败
|
|
407
|
+
await waitFor(() => {
|
|
408
|
+
const m = (globalThis as any).__lastDropdownMenu;
|
|
409
|
+
const is = (m?.items || []) as any[];
|
|
410
|
+
const current = is.find((it) => String(it.key) === 'copy-pop-uid:popupSettings:stage');
|
|
411
|
+
const sub = is.find((it) => String(it.key).startsWith('copy-pop-uid:items[0]:popupSettings:stage'));
|
|
412
|
+
expect(current).toBeTruthy();
|
|
413
|
+
expect(sub).toBeTruthy();
|
|
414
|
+
});
|
|
415
|
+
|
|
416
|
+
// click and verify clipboard(直接使用最新的 menu)
|
|
417
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
418
|
+
menu.onClick?.({ key: 'copy-pop-uid:popupSettings:stage' });
|
|
419
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('parent-2');
|
|
420
|
+
|
|
421
|
+
menu.onClick?.({ key: 'copy-pop-uid:items[0]:popupSettings:stage' });
|
|
422
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('child-2');
|
|
423
|
+
});
|
|
424
|
+
});
|
|
@@ -847,6 +847,7 @@ describe('AddSubModelButton toggleable behavior', () => {
|
|
|
847
847
|
save = vi.fn().mockResolvedValue({});
|
|
848
848
|
destroy = vi.fn().mockResolvedValue(true);
|
|
849
849
|
move = vi.fn().mockResolvedValue(undefined);
|
|
850
|
+
duplicate = vi.fn().mockResolvedValue(null);
|
|
850
851
|
}
|
|
851
852
|
|
|
852
853
|
function setup() {
|
|
@@ -942,13 +943,10 @@ describe('AddSubModelButton toggleable behavior', () => {
|
|
|
942
943
|
expect(screen.getByText('Child A')).toBeInTheDocument();
|
|
943
944
|
expect(screen.getByText('Child B')).toBeInTheDocument();
|
|
944
945
|
|
|
945
|
-
// ensure destroy
|
|
946
|
-
await waitFor(
|
|
947
|
-
()
|
|
948
|
-
|
|
949
|
-
},
|
|
950
|
-
{ timeout: 5000 },
|
|
951
|
-
);
|
|
946
|
+
// ensure destroy has been called (avoid flakiness on exact call counts)
|
|
947
|
+
await waitFor(() => {
|
|
948
|
+
expect(repo.destroy).toHaveBeenCalled();
|
|
949
|
+
});
|
|
952
950
|
});
|
|
953
951
|
|
|
954
952
|
test('toggle state updates without menu closing', async () => {
|
|
@@ -85,6 +85,7 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
|
|
|
85
85
|
showValueComponent = true,
|
|
86
86
|
onlyLeafSelectable = false,
|
|
87
87
|
clearValue,
|
|
88
|
+
ignoreFieldNames,
|
|
88
89
|
...restProps
|
|
89
90
|
}) => {
|
|
90
91
|
const [currentMetaTreeNode, setCurrentMetaTreeNode] = useState<MetaTreeNode | null>(null);
|
|
@@ -254,11 +255,29 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
|
|
|
254
255
|
if (disabled) {
|
|
255
256
|
return;
|
|
256
257
|
}
|
|
257
|
-
setCurrentMetaTreeNode(null);
|
|
258
258
|
const cleared = clearValue !== undefined ? clearValue : null;
|
|
259
259
|
setInnerValue(cleared);
|
|
260
|
+
|
|
261
|
+
// 若 clearValue 能解析到某个路径(例如 ['constant']),
|
|
262
|
+
// 则尝试立即定位到对应的 MetaTreeNode,以便渲染正确的常量组件。
|
|
263
|
+
try {
|
|
264
|
+
const path = resolvePathFromValue?.(cleared);
|
|
265
|
+
if (Array.isArray(resolvedMetaTree) && path && path.length > 0) {
|
|
266
|
+
const node = findMetaTreeNodeByPath(resolvedMetaTree as MetaTreeNode[], path as string[]);
|
|
267
|
+
if (node) {
|
|
268
|
+
setCurrentMetaTreeNode(node);
|
|
269
|
+
emitChange(cleared as any, node);
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
} catch (_) {
|
|
274
|
+
// 忽略解析异常,走默认回退
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
// 默认回退(无法定位具体 MetaTreeNode 时)
|
|
278
|
+
setCurrentMetaTreeNode(null);
|
|
260
279
|
emitChange(cleared as any);
|
|
261
|
-
}, [emitChange, disabled, clearValue]);
|
|
280
|
+
}, [emitChange, disabled, clearValue, resolvedMetaTree, resolvePathFromValue]);
|
|
262
281
|
|
|
263
282
|
const stableProps = useMemo(() => {
|
|
264
283
|
const { style, onFocus, onBlur, disabled, ...otherProps } = restProps;
|
|
@@ -333,6 +352,7 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
|
|
|
333
352
|
parseValueToPath={resolvePathFromValue}
|
|
334
353
|
formatPathToValue={resolveValueFromPath}
|
|
335
354
|
onlyLeafSelectable={onlyLeafSelectable}
|
|
355
|
+
ignoreFieldNames={ignoreFieldNames}
|
|
336
356
|
{...(!showValueComponent && { children: null, placeholder: restProps?.placeholder })}
|
|
337
357
|
/>
|
|
338
358
|
</Space.Compact>
|
|
@@ -15,6 +15,7 @@ import { parseValueToPath } from './utils';
|
|
|
15
15
|
import { useResolvedMetaTree } from './useResolvedMetaTree';
|
|
16
16
|
import { useRequest } from 'ahooks';
|
|
17
17
|
import { useFlowContext } from '../../FlowContextProvider';
|
|
18
|
+
import type { MetaTreeNode } from '../../flowContext';
|
|
18
19
|
|
|
19
20
|
const VariableTagComponent: React.FC<VariableTagProps> = ({
|
|
20
21
|
value,
|
|
@@ -29,14 +30,65 @@ const VariableTagComponent: React.FC<VariableTagProps> = ({
|
|
|
29
30
|
|
|
30
31
|
const { data: displayedValue } = useRequest(
|
|
31
32
|
async () => {
|
|
33
|
+
// 1) 优先使用已解析到的节点(包含完整父标题链)
|
|
32
34
|
if (metaTreeNode) {
|
|
33
35
|
return metaTreeNode.parentTitles
|
|
34
36
|
? [...metaTreeNode.parentTitles, metaTreeNode.title].map(ctx.t).join('/')
|
|
35
37
|
: ctx.t(metaTreeNode.title) || '';
|
|
36
38
|
}
|
|
39
|
+
|
|
40
|
+
// 2) 无 metaTreeNode 时,尝试从值还原路径,并在 metaTree 中寻找“最深前缀节点”,
|
|
41
|
+
// 即便最后一段无效,也能显示前缀的翻译标题。
|
|
37
42
|
if (!value) return String(value);
|
|
38
|
-
const
|
|
39
|
-
|
|
43
|
+
const rawPath = parseValueToPath(value);
|
|
44
|
+
if (!rawPath || !Array.isArray(resolvedMetaTree)) {
|
|
45
|
+
return Array.isArray(rawPath) ? rawPath.join('/') : String(value);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
// 兼容 metaTree 为子树:顶层不含首段时,裁剪首段
|
|
49
|
+
const topNames = new Set((resolvedMetaTree || []).map((n: any) => String(n?.name)));
|
|
50
|
+
const path = !topNames.has(String(rawPath[0])) ? rawPath.slice(1) : rawPath;
|
|
51
|
+
if (!path.length) return '';
|
|
52
|
+
|
|
53
|
+
let nodes: MetaTreeNode[] | undefined = resolvedMetaTree as MetaTreeNode[];
|
|
54
|
+
let deepest: MetaTreeNode | null = null;
|
|
55
|
+
let matchedCount = 0;
|
|
56
|
+
for (let i = 0; i < path.length; i++) {
|
|
57
|
+
if (!nodes) break;
|
|
58
|
+
const seg = String(path[i]);
|
|
59
|
+
const node = nodes.find((n) => String(n?.name) === seg) as MetaTreeNode | undefined;
|
|
60
|
+
if (!node) break; // 停在第一个无效段之前
|
|
61
|
+
deepest = node;
|
|
62
|
+
matchedCount = i + 1;
|
|
63
|
+
if (i < path.length - 1) {
|
|
64
|
+
if (Array.isArray(node.children)) {
|
|
65
|
+
nodes = node.children as any;
|
|
66
|
+
} else if (typeof node.children === 'function') {
|
|
67
|
+
try {
|
|
68
|
+
const childNodes = await (node.children as any)();
|
|
69
|
+
(node as any).children = childNodes;
|
|
70
|
+
nodes = childNodes as any;
|
|
71
|
+
} catch {
|
|
72
|
+
nodes = undefined;
|
|
73
|
+
}
|
|
74
|
+
} else {
|
|
75
|
+
nodes = undefined;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
if (deepest) {
|
|
81
|
+
const titles = deepest.parentTitles ? [...deepest.parentTitles, deepest.title] : [deepest.title];
|
|
82
|
+
let label = titles.map(ctx.t).join('/');
|
|
83
|
+
if (matchedCount < path.length) {
|
|
84
|
+
const tail = path.slice(matchedCount).join('/');
|
|
85
|
+
label = tail ? `${label}/${tail}` : label;
|
|
86
|
+
}
|
|
87
|
+
return label;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// 3) 完全找不到任何前缀时,退回原始路径字符串
|
|
91
|
+
return path.join('/');
|
|
40
92
|
},
|
|
41
93
|
{ refreshDeps: [resolvedMetaTree, value, metaTreeNode] },
|
|
42
94
|
);
|
|
@@ -21,6 +21,7 @@ export interface FlowContextSelectorProps
|
|
|
21
21
|
formatPathToValue?: (item: MetaTreeNode) => string;
|
|
22
22
|
open?: boolean;
|
|
23
23
|
onlyLeafSelectable?: boolean;
|
|
24
|
+
ignoreFieldNames?: string[];
|
|
24
25
|
}
|
|
25
26
|
|
|
26
27
|
export interface ContextSelectorItem {
|
|
@@ -69,6 +70,7 @@ export interface VariableInputProps {
|
|
|
69
70
|
* 默认行为为 null;可设置为 '' 等,以便清空后默认切换为“常量-空字符串”。
|
|
70
71
|
*/
|
|
71
72
|
clearValue?: any;
|
|
73
|
+
ignoreFieldNames?: string[];
|
|
72
74
|
[key: string]: any;
|
|
73
75
|
}
|
|
74
76
|
|