@nocobase/flow-engine 2.0.0-alpha.4 → 2.0.0-alpha.41
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/ViewScopedFlowEngine.js +12 -0
- package/lib/acl/Acl.d.ts +1 -0
- package/lib/acl/Acl.js +11 -0
- package/lib/components/FieldModelRenderer.js +14 -6
- 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 +3 -6
- package/lib/components/FlowModelRenderer.js +16 -47
- package/lib/components/FormItem.js +5 -1
- package/lib/components/MobilePopup.d.ts +20 -0
- package/lib/components/MobilePopup.js +102 -0
- package/lib/components/MobilePopup.style.d.ts +17 -0
- package/lib/components/MobilePopup.style.js +186 -0
- package/lib/components/index.d.ts +1 -0
- package/lib/components/index.js +3 -1
- 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/FlowsFloatContextMenu.d.ts +2 -2
- package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +35 -6
- package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +7 -1
- package/lib/components/variables/VariableInput.js +19 -5
- package/lib/components/variables/VariableTag.js +43 -2
- package/lib/components/variables/types.d.ts +2 -0
- package/lib/data-source/index.d.ts +15 -4
- package/lib/data-source/index.js +42 -13
- package/lib/data-source/sortCollectionsByInherits.d.ts +10 -0
- package/lib/data-source/sortCollectionsByInherits.js +71 -0
- package/lib/emitter.d.ts +6 -0
- package/lib/emitter.js +12 -0
- package/lib/executor/FlowExecutor.d.ts +4 -5
- package/lib/executor/FlowExecutor.js +168 -99
- package/lib/flow-registry/GlobalFlowRegistry.d.ts +1 -0
- package/lib/flow-registry/GlobalFlowRegistry.js +3 -0
- package/lib/flow-registry/InstanceFlowRegistry.d.ts +1 -0
- package/lib/flow-registry/InstanceFlowRegistry.js +3 -0
- package/lib/flowContext.d.ts +37 -5
- package/lib/flowContext.js +224 -87
- package/lib/flowEngine.d.ts +24 -1
- package/lib/flowEngine.js +81 -8
- package/lib/flowSettings.d.ts +2 -1
- package/lib/flowSettings.js +16 -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 +3 -0
- package/lib/models/CollectionFieldModel.js +52 -6
- package/lib/models/flowModel.d.ts +30 -29
- package/lib/models/flowModel.js +153 -100
- package/lib/models/forkFlowModel.d.ts +8 -4
- package/lib/models/forkFlowModel.js +38 -8
- package/lib/provider.d.ts +3 -1
- package/lib/provider.js +7 -5
- package/lib/resources/multiRecordResource.js +25 -1
- package/lib/resources/singleRecordResource.js +19 -1
- 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 +205 -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 +162 -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-antd-icons.snippet.d.ts +11 -0
- package/lib/runjs-context/snippets/scene/block/render-antd-icons.snippet.js +65 -0
- 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/scheduler/ModelOperationScheduler.d.ts +51 -0
- package/lib/scheduler/ModelOperationScheduler.js +262 -0
- package/lib/types.d.ts +28 -3
- package/lib/types.js +4 -3
- package/lib/utils/associationObjectVariable.d.ts +32 -0
- package/lib/utils/associationObjectVariable.js +157 -0
- 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/flows.d.ts +10 -0
- package/lib/{runjs-context/snippets/global/api-request-post.snippet.js → utils/flows.js} +21 -15
- package/lib/utils/index.d.ts +7 -2
- package/lib/utils/index.js +21 -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/parsePathnameToViewParams.d.ts +1 -1
- package/lib/utils/parsePathnameToViewParams.js +41 -5
- package/lib/utils/pruneFilter.d.ts +21 -0
- package/lib/{runjs-context/snippets/global/try-catch-async.snippet.js → utils/pruneFilter.js} +24 -16
- 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/translation.d.ts +4 -1
- package/lib/utils/translation.js +6 -2
- package/lib/utils/variablesParams.d.ts +22 -5
- package/lib/utils/variablesParams.js +132 -36
- package/lib/views/DrawerComponent.js +21 -2
- package/lib/views/PageComponent.js +2 -1
- package/lib/views/ViewNavigation.d.ts +3 -9
- package/lib/views/ViewNavigation.js +16 -2
- 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 +9 -8
- package/lib/views/useDialog.js +19 -14
- package/lib/views/useDrawer.d.ts +9 -8
- package/lib/views/useDrawer.js +54 -35
- package/lib/views/usePage.d.ts +9 -8
- package/lib/views/usePage.js +20 -14
- package/package.json +6 -3
- package/src/BlockScopedFlowEngine.ts +86 -0
- package/src/FlowContextProvider.tsx +4 -2
- package/src/JSRunner.ts +3 -0
- package/src/ViewScopedFlowEngine.ts +15 -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__/flowContext.test.ts +52 -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__/flowSettings.open.test.tsx +2 -0
- package/src/__tests__/flowSettings.test.ts +2 -0
- package/src/__tests__/globalFlowRegistry.test.ts +1 -1
- package/src/__tests__/modelOperationScheduler.test.ts +346 -0
- package/src/__tests__/objectVariable.test.ts +464 -0
- 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 +461 -0
- package/src/__tests__/runjsSnippets.test.ts +140 -0
- package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
- package/src/acl/Acl.tsx +14 -0
- package/src/acl/__tests__/Acl.test.tsx +43 -0
- package/src/components/DynamicFlowsEditor.tsx +3 -4
- package/src/components/FieldModelRenderer.tsx +20 -7
- package/src/components/FieldSkeleton.tsx +27 -0
- package/src/components/FlowContextSelector.tsx +6 -2
- package/src/components/FlowModelRenderer.tsx +33 -81
- package/src/components/FormItem.tsx +8 -1
- package/src/components/MobilePopup.style.ts +220 -0
- package/src/components/MobilePopup.tsx +86 -0
- package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +6 -6
- package/src/components/index.ts +1 -0
- 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/FlowsFloatContextMenu.tsx +39 -10
- package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +14 -1
- package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +425 -0
- package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +5 -7
- package/src/components/variables/VariableInput.tsx +25 -5
- 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 +42 -11
- package/src/data-source/sortCollectionsByInherits.ts +61 -0
- package/src/emitter.ts +14 -0
- package/src/executor/FlowExecutor.ts +213 -119
- package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
- package/src/executor/__tests__/flowExecutor.test.ts +151 -5
- package/src/flow-registry/GlobalFlowRegistry.ts +1 -0
- package/src/flow-registry/InstanceFlowRegistry.ts +1 -0
- package/src/flow-registry/__tests__/globalFlowRegistry.test.ts +54 -0
- package/src/flowContext.ts +303 -104
- package/src/flowEngine.ts +96 -6
- package/src/flowSettings.ts +12 -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 +60 -8
- package/src/models/__tests__/dispatchEvent.behavior.test.ts +169 -0
- package/src/models/__tests__/flowModel.getFlows.sort.test.ts +33 -9
- package/src/models/__tests__/flowModel.scheduleModelOperation.test.tsx +129 -0
- package/src/models/__tests__/flowModel.test.ts +234 -111
- package/src/models/__tests__/forkFlowModel.test.ts +40 -7
- package/src/models/flowModel.tsx +197 -132
- package/src/models/forkFlowModel.ts +48 -8
- package/src/provider.tsx +10 -7
- package/src/resources/multiRecordResource.ts +28 -1
- package/src/resources/singleRecordResource.ts +19 -1
- 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 +196 -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 +178 -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-antd-icons.snippet.ts +46 -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/scheduler/ModelOperationScheduler.ts +304 -0
- package/src/types.ts +34 -0
- package/src/utils/__tests__/flows.test.ts +65 -0
- package/src/utils/__tests__/jsxTransform.test.ts +38 -0
- package/src/utils/__tests__/parsePathnameToViewParams.test.ts +25 -0
- package/src/utils/__tests__/pruneFilter.test.ts +38 -0
- package/src/utils/__tests__/safeGlobals.test.ts +22 -1
- package/src/utils/__tests__/variablesParams.test.ts +63 -0
- package/src/utils/associationObjectVariable.ts +180 -0
- package/src/utils/buildSettingsViewInputArgs.ts +72 -0
- package/src/utils/createEphemeralContext.ts +142 -0
- package/src/utils/flows.ts +23 -0
- package/src/utils/index.ts +11 -2
- package/src/utils/jsxTransform.ts +39 -0
- package/src/utils/params-resolvers.ts +2 -2
- package/src/utils/parsePathnameToViewParams.ts +50 -6
- package/src/utils/pruneFilter.ts +41 -0
- 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/translation.ts +7 -2
- package/src/utils/variablesParams.ts +156 -38
- package/src/views/DrawerComponent.tsx +21 -2
- package/src/views/PageComponent.tsx +1 -1
- package/src/views/ViewNavigation.ts +20 -12
- package/src/views/__tests__/ViewNavigation.test.ts +10 -0
- package/src/views/createViewMeta.ts +393 -70
- package/src/views/index.tsx +1 -0
- package/src/views/useDialog.tsx +24 -18
- package/src/views/useDrawer.tsx +74 -45
- package/src/views/usePage.tsx +26 -17
- 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/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/requireAsync.snippet.d.ts +0 -16
- 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/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-mount.snippet.js +0 -46
- 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/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
|
@@ -0,0 +1,425 @@
|
|
|
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
|
+
theme: { useToken: () => ({}) },
|
|
79
|
+
};
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
describe('DefaultSettingsIcon - only static flows are shown', () => {
|
|
83
|
+
beforeEach(() => {
|
|
84
|
+
dropdownMenus.length = 0;
|
|
85
|
+
(globalThis as any).__lastDropdownMenu = undefined;
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
afterEach(() => {
|
|
89
|
+
cleanup();
|
|
90
|
+
vi.clearAllMocks();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('excludes instance (dynamic) flows from the settings menu', async () => {
|
|
94
|
+
class TestFlowModel extends FlowModel {}
|
|
95
|
+
|
|
96
|
+
const engine = new FlowEngine();
|
|
97
|
+
const model = new TestFlowModel({ uid: 'model-static-only', flowEngine: engine });
|
|
98
|
+
|
|
99
|
+
// register one static flow with a visible step
|
|
100
|
+
TestFlowModel.registerFlow({
|
|
101
|
+
key: 'static1',
|
|
102
|
+
title: 'Static Flow',
|
|
103
|
+
steps: {
|
|
104
|
+
general: {
|
|
105
|
+
title: 'General',
|
|
106
|
+
uiSchema: {
|
|
107
|
+
field: { type: 'string', 'x-component': 'Input' },
|
|
108
|
+
},
|
|
109
|
+
},
|
|
110
|
+
},
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
// add a dynamic (instance) flow which should NOT appear in menu
|
|
114
|
+
model.flowRegistry.addFlow('dyn1', {
|
|
115
|
+
title: 'Dynamic Flow',
|
|
116
|
+
steps: {
|
|
117
|
+
general: {
|
|
118
|
+
title: 'General (Dyn)',
|
|
119
|
+
uiSchema: {
|
|
120
|
+
field: { type: 'string', 'x-component': 'Input' },
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
},
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
render(
|
|
127
|
+
React.createElement(
|
|
128
|
+
ConfigProvider as any,
|
|
129
|
+
null,
|
|
130
|
+
React.createElement(
|
|
131
|
+
App as any,
|
|
132
|
+
null,
|
|
133
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
134
|
+
model,
|
|
135
|
+
// 关闭常用操作,避免干扰断言
|
|
136
|
+
showDeleteButton: false,
|
|
137
|
+
showCopyUidButton: false,
|
|
138
|
+
}),
|
|
139
|
+
),
|
|
140
|
+
),
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
// 等待菜单内出现静态流分组,确保异步加载完成
|
|
144
|
+
await waitFor(() => {
|
|
145
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
146
|
+
expect(menu).toBeTruthy();
|
|
147
|
+
const items = (menu?.items || []) as any[];
|
|
148
|
+
const groupLabels = items.filter((it) => it.type === 'group').map((it) => String(it.label));
|
|
149
|
+
expect(groupLabels).toContain('Static Flow');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
153
|
+
const items = (menu?.items || []) as any[];
|
|
154
|
+
|
|
155
|
+
// groups for flows are labeled with flow.title; ensure static group exists, dynamic group不存在
|
|
156
|
+
const groupLabels = items.filter((it) => it.type === 'group').map((it) => String(it.label));
|
|
157
|
+
expect(groupLabels).toContain('Static Flow');
|
|
158
|
+
expect(groupLabels).not.toContain('Dynamic Flow');
|
|
159
|
+
|
|
160
|
+
// 静态流的 step 存在(key: `${flowKey}:${stepKey}`),动态流 step 不存在
|
|
161
|
+
expect(items.some((it) => String(it.key || '').startsWith('static1:'))).toBe(true);
|
|
162
|
+
expect(items.some((it) => String(it.key || '').startsWith('dyn1:'))).toBe(false);
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
it('filters out steps with hideInSettings and keeps visible ones', async () => {
|
|
166
|
+
class TestFlowModel extends FlowModel {}
|
|
167
|
+
const engine = new FlowEngine();
|
|
168
|
+
const model = new TestFlowModel({ uid: 'm-hide', flowEngine: engine });
|
|
169
|
+
|
|
170
|
+
TestFlowModel.registerFlow({
|
|
171
|
+
key: 'flowA',
|
|
172
|
+
title: 'Flow A',
|
|
173
|
+
steps: {
|
|
174
|
+
hidden: { title: 'Hidden', hideInSettings: true, uiSchema: { a: { type: 'string', 'x-component': 'Input' } } },
|
|
175
|
+
visible: { title: 'Visible', uiSchema: { b: { type: 'string', 'x-component': 'Input' } } },
|
|
176
|
+
},
|
|
177
|
+
});
|
|
178
|
+
|
|
179
|
+
render(
|
|
180
|
+
React.createElement(
|
|
181
|
+
ConfigProvider as any,
|
|
182
|
+
null,
|
|
183
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
184
|
+
),
|
|
185
|
+
);
|
|
186
|
+
|
|
187
|
+
await waitFor(() => {
|
|
188
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
189
|
+
const items = (menu?.items || []) as any[];
|
|
190
|
+
expect(items.some((it) => String(it.key || '') === 'flowA:visible')).toBe(true);
|
|
191
|
+
expect(items.some((it) => String(it.key || '') === 'flowA:hidden')).toBe(false);
|
|
192
|
+
});
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('includes step when uiSchema provided by action (step.use)', async () => {
|
|
196
|
+
class TestFlowModel extends FlowModel {}
|
|
197
|
+
const engine = new FlowEngine();
|
|
198
|
+
const model = new TestFlowModel({ uid: 'm-action', flowEngine: engine });
|
|
199
|
+
|
|
200
|
+
// Step has no uiSchema but uses an action that provides uiSchema
|
|
201
|
+
TestFlowModel.registerFlow({
|
|
202
|
+
key: 'flowB',
|
|
203
|
+
title: 'Flow B',
|
|
204
|
+
steps: {
|
|
205
|
+
useAction: { title: 'Use Action', use: 'act' },
|
|
206
|
+
},
|
|
207
|
+
});
|
|
208
|
+
|
|
209
|
+
// Stub getAction to provide uiSchema
|
|
210
|
+
(model as any).getAction = vi.fn().mockReturnValue({
|
|
211
|
+
name: 'act',
|
|
212
|
+
title: 'Action Title',
|
|
213
|
+
uiSchema: { x: { type: 'string', 'x-component': 'Input' } },
|
|
214
|
+
});
|
|
215
|
+
|
|
216
|
+
render(
|
|
217
|
+
React.createElement(
|
|
218
|
+
ConfigProvider as any,
|
|
219
|
+
null,
|
|
220
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
221
|
+
),
|
|
222
|
+
);
|
|
223
|
+
|
|
224
|
+
await waitFor(() => {
|
|
225
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
226
|
+
const items = (menu?.items || []) as any[];
|
|
227
|
+
expect(items.some((it) => String(it.key || '') === 'flowB:useAction')).toBe(true);
|
|
228
|
+
});
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
it('clicking a step item opens flow settings with correct args', async () => {
|
|
232
|
+
class TestFlowModel extends FlowModel {}
|
|
233
|
+
const engine = new FlowEngine();
|
|
234
|
+
const model = new TestFlowModel({ uid: 'm-open', flowEngine: engine });
|
|
235
|
+
const openSpy = vi.spyOn(model, 'openFlowSettings').mockResolvedValue(undefined as any);
|
|
236
|
+
|
|
237
|
+
TestFlowModel.registerFlow({
|
|
238
|
+
key: 'flowC',
|
|
239
|
+
title: 'Flow C',
|
|
240
|
+
steps: {
|
|
241
|
+
general: { title: 'General', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } },
|
|
242
|
+
},
|
|
243
|
+
});
|
|
244
|
+
|
|
245
|
+
render(
|
|
246
|
+
React.createElement(
|
|
247
|
+
ConfigProvider as any,
|
|
248
|
+
null,
|
|
249
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
250
|
+
),
|
|
251
|
+
);
|
|
252
|
+
|
|
253
|
+
await waitFor(() => {
|
|
254
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
255
|
+
});
|
|
256
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
257
|
+
menu.onClick?.({ key: 'flowC:general' });
|
|
258
|
+
expect(openSpy).toHaveBeenCalledWith({ flowKey: 'flowC', stepKey: 'general' });
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
it('copy UID action writes model uid to clipboard', async () => {
|
|
262
|
+
class TestFlowModel extends FlowModel {}
|
|
263
|
+
const engine = new FlowEngine();
|
|
264
|
+
const model = new TestFlowModel({ uid: 'm-copy', flowEngine: engine });
|
|
265
|
+
|
|
266
|
+
TestFlowModel.registerFlow({
|
|
267
|
+
key: 'flowD',
|
|
268
|
+
title: 'Flow D',
|
|
269
|
+
steps: { s: { title: 'S', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } } },
|
|
270
|
+
});
|
|
271
|
+
|
|
272
|
+
// mock clipboard
|
|
273
|
+
Object.defineProperty(window.navigator, 'clipboard', {
|
|
274
|
+
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
275
|
+
configurable: true,
|
|
276
|
+
});
|
|
277
|
+
|
|
278
|
+
render(
|
|
279
|
+
React.createElement(
|
|
280
|
+
ConfigProvider as any,
|
|
281
|
+
null,
|
|
282
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
283
|
+
),
|
|
284
|
+
);
|
|
285
|
+
|
|
286
|
+
await waitFor(() => {
|
|
287
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
288
|
+
});
|
|
289
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
290
|
+
menu.onClick?.({ key: 'copy-uid' });
|
|
291
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('m-copy');
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
it('delete action calls model.destroy()', async () => {
|
|
295
|
+
class TestFlowModel extends FlowModel {}
|
|
296
|
+
const engine = new FlowEngine();
|
|
297
|
+
const model = new TestFlowModel({ uid: 'm-del', flowEngine: engine });
|
|
298
|
+
const destroySpy = vi.spyOn(model, 'destroy').mockResolvedValue(undefined as any);
|
|
299
|
+
|
|
300
|
+
TestFlowModel.registerFlow({
|
|
301
|
+
key: 'flowE',
|
|
302
|
+
title: 'Flow E',
|
|
303
|
+
steps: { s: { title: 'S', uiSchema: { f: { type: 'string', 'x-component': 'Input' } } } },
|
|
304
|
+
});
|
|
305
|
+
|
|
306
|
+
render(
|
|
307
|
+
React.createElement(
|
|
308
|
+
ConfigProvider as any,
|
|
309
|
+
null,
|
|
310
|
+
React.createElement(App as any, null, React.createElement(DefaultSettingsIcon as any, { model })),
|
|
311
|
+
),
|
|
312
|
+
);
|
|
313
|
+
|
|
314
|
+
await waitFor(() => {
|
|
315
|
+
expect((globalThis as any).__lastDropdownMenu).toBeTruthy();
|
|
316
|
+
});
|
|
317
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
318
|
+
menu.onClick?.({ key: 'delete' });
|
|
319
|
+
expect(destroySpy).toHaveBeenCalled();
|
|
320
|
+
});
|
|
321
|
+
|
|
322
|
+
it('shows sub-model steps with modelKey when flattenSubMenus=false and menuLevels=2', async () => {
|
|
323
|
+
class Parent extends FlowModel {}
|
|
324
|
+
class Child extends FlowModel {}
|
|
325
|
+
const engine = new FlowEngine();
|
|
326
|
+
const parent = new Parent({ uid: 'parent-1', flowEngine: engine });
|
|
327
|
+
const child = new Child({ uid: 'child-1', flowEngine: engine });
|
|
328
|
+
|
|
329
|
+
// child static flow
|
|
330
|
+
Child.registerFlow({
|
|
331
|
+
key: 'childFlow',
|
|
332
|
+
title: 'Child Flow',
|
|
333
|
+
steps: { cstep: { title: 'C', uiSchema: { x: { type: 'string', 'x-component': 'Input' } } } },
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
parent.addSubModel('items', child);
|
|
337
|
+
|
|
338
|
+
render(
|
|
339
|
+
React.createElement(
|
|
340
|
+
ConfigProvider as any,
|
|
341
|
+
null,
|
|
342
|
+
React.createElement(
|
|
343
|
+
App as any,
|
|
344
|
+
null,
|
|
345
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
346
|
+
model: parent,
|
|
347
|
+
menuLevels: 2,
|
|
348
|
+
flattenSubMenus: false,
|
|
349
|
+
}),
|
|
350
|
+
),
|
|
351
|
+
),
|
|
352
|
+
);
|
|
353
|
+
|
|
354
|
+
await waitFor(() => {
|
|
355
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
356
|
+
expect(menu).toBeTruthy();
|
|
357
|
+
const items = (menu?.items || []) as any[];
|
|
358
|
+
const subMenu = items.find((it) => Array.isArray(it?.children));
|
|
359
|
+
expect(subMenu).toBeTruthy();
|
|
360
|
+
expect(subMenu!.children.some((it: any) => String(it.key).startsWith('items[0]:childFlow:cstep'))).toBe(true);
|
|
361
|
+
});
|
|
362
|
+
});
|
|
363
|
+
|
|
364
|
+
it('adds "Copy popup UID" for popupSettings flow (current model and sub-model)', async () => {
|
|
365
|
+
class Parent extends FlowModel {}
|
|
366
|
+
class Child extends FlowModel {}
|
|
367
|
+
const engine = new FlowEngine();
|
|
368
|
+
const parent = new Parent({ uid: 'parent-2', flowEngine: engine });
|
|
369
|
+
const child = new Child({ uid: 'child-2', flowEngine: engine });
|
|
370
|
+
|
|
371
|
+
// current model popupSettings
|
|
372
|
+
Parent.registerFlow({
|
|
373
|
+
key: 'popupSettings',
|
|
374
|
+
title: 'Popup',
|
|
375
|
+
steps: { stage: { title: 'Stage', uiSchema: { a: { type: 'string', 'x-component': 'Input' } } } },
|
|
376
|
+
});
|
|
377
|
+
// sub model popupSettings
|
|
378
|
+
Child.registerFlow({
|
|
379
|
+
key: 'popupSettings',
|
|
380
|
+
title: 'Popup Child',
|
|
381
|
+
steps: { stage: { title: 'Stage', uiSchema: { a: { type: 'string', 'x-component': 'Input' } } } },
|
|
382
|
+
});
|
|
383
|
+
parent.addSubModel('items', child);
|
|
384
|
+
|
|
385
|
+
// mock clipboard
|
|
386
|
+
Object.defineProperty(window.navigator, 'clipboard', {
|
|
387
|
+
value: { writeText: vi.fn().mockResolvedValue(undefined) },
|
|
388
|
+
configurable: true,
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
render(
|
|
392
|
+
React.createElement(
|
|
393
|
+
ConfigProvider as any,
|
|
394
|
+
null,
|
|
395
|
+
React.createElement(
|
|
396
|
+
App as any,
|
|
397
|
+
null,
|
|
398
|
+
React.createElement(DefaultSettingsIcon as any, {
|
|
399
|
+
model: parent,
|
|
400
|
+
menuLevels: 2,
|
|
401
|
+
flattenSubMenus: true,
|
|
402
|
+
}),
|
|
403
|
+
),
|
|
404
|
+
),
|
|
405
|
+
);
|
|
406
|
+
|
|
407
|
+
// 等待“Copy popup UID”对应的菜单项出现,避免异步时序导致的偶发失败
|
|
408
|
+
await waitFor(() => {
|
|
409
|
+
const m = (globalThis as any).__lastDropdownMenu;
|
|
410
|
+
const is = (m?.items || []) as any[];
|
|
411
|
+
const current = is.find((it) => String(it.key) === 'copy-pop-uid:popupSettings:stage');
|
|
412
|
+
const sub = is.find((it) => String(it.key).startsWith('copy-pop-uid:items[0]:popupSettings:stage'));
|
|
413
|
+
expect(current).toBeTruthy();
|
|
414
|
+
expect(sub).toBeTruthy();
|
|
415
|
+
});
|
|
416
|
+
|
|
417
|
+
// click and verify clipboard(直接使用最新的 menu)
|
|
418
|
+
const menu = (globalThis as any).__lastDropdownMenu;
|
|
419
|
+
menu.onClick?.({ key: 'copy-pop-uid:popupSettings:stage' });
|
|
420
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('parent-2');
|
|
421
|
+
|
|
422
|
+
menu.onClick?.({ key: 'copy-pop-uid:items[0]:popupSettings:stage' });
|
|
423
|
+
expect((navigator as any).clipboard.writeText).toHaveBeenCalledWith('child-2');
|
|
424
|
+
});
|
|
425
|
+
});
|
|
@@ -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);
|
|
@@ -138,7 +139,7 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
|
|
|
138
139
|
// 当 value 存在但 currentMetaTreeNode 还未恢复,尝试按路径逐级加载(支持 children 为函数的场景)
|
|
139
140
|
useEffect(() => {
|
|
140
141
|
const restoreFromValue = async () => {
|
|
141
|
-
if (!Array.isArray(resolvedMetaTree) ||
|
|
142
|
+
if (!Array.isArray(resolvedMetaTree) || value == null) return;
|
|
142
143
|
|
|
143
144
|
// 若已存在且路径匹配,跳过
|
|
144
145
|
if (currentMetaTreeNode) {
|
|
@@ -202,7 +203,7 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
|
|
|
202
203
|
|
|
203
204
|
useEffect(() => {
|
|
204
205
|
if (!resolvedMetaTreeNode) return;
|
|
205
|
-
if (!Array.isArray(resolvedMetaTree) ||
|
|
206
|
+
if (!Array.isArray(resolvedMetaTree) || innerValue == null) return;
|
|
206
207
|
const finalValue = resolveValueFromPath?.(resolvedMetaTreeNode) || innerValue;
|
|
207
208
|
emitChange(finalValue, resolvedMetaTreeNode);
|
|
208
209
|
setCurrentMetaTreeNode(resolvedMetaTreeNode);
|
|
@@ -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;
|
|
@@ -267,7 +286,7 @@ const VariableInputComponent: React.FC<VariableInputProps> = ({
|
|
|
267
286
|
|
|
268
287
|
const inputProps = useMemo(() => {
|
|
269
288
|
const baseProps = {
|
|
270
|
-
value: innerValue
|
|
289
|
+
value: innerValue ?? '',
|
|
271
290
|
onChange: handleInputChange,
|
|
272
291
|
disabled,
|
|
273
292
|
};
|
|
@@ -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
|
|