@nocobase/flow-engine 2.0.0-alpha.4 → 2.0.0-alpha.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (462) hide show
  1. package/lib/BlockScopedFlowEngine.d.ts +23 -0
  2. package/lib/BlockScopedFlowEngine.js +90 -0
  3. package/lib/FlowContextProvider.d.ts +2 -2
  4. package/lib/FlowContextProvider.js +3 -3
  5. package/lib/FlowDefinition.d.ts +4 -2
  6. package/lib/JSRunner.js +3 -0
  7. package/lib/ViewScopedFlowEngine.d.ts +1 -1
  8. package/lib/ViewScopedFlowEngine.js +12 -0
  9. package/lib/acl/Acl.d.ts +1 -0
  10. package/lib/acl/Acl.js +11 -0
  11. package/lib/components/FieldModelRenderer.js +14 -6
  12. package/lib/components/FieldSkeleton.d.ts +10 -0
  13. package/lib/components/FieldSkeleton.js +64 -0
  14. package/lib/components/FlowContextSelector.js +7 -2
  15. package/lib/components/FlowModelRenderer.d.ts +3 -6
  16. package/lib/components/FlowModelRenderer.js +16 -47
  17. package/lib/components/FormItem.js +5 -1
  18. package/lib/components/MobilePopup.d.ts +20 -0
  19. package/lib/components/MobilePopup.js +102 -0
  20. package/lib/components/MobilePopup.style.d.ts +17 -0
  21. package/lib/components/MobilePopup.style.js +186 -0
  22. package/lib/components/index.d.ts +1 -0
  23. package/lib/components/index.js +3 -1
  24. package/lib/components/settings/independents/dropdown/FlowsDropdownButton.js +2 -2
  25. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +17 -5
  26. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +2 -2
  27. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +2 -2
  28. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +35 -6
  29. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +7 -1
  30. package/lib/components/variables/VariableInput.js +19 -5
  31. package/lib/components/variables/VariableTag.js +43 -2
  32. package/lib/components/variables/types.d.ts +2 -0
  33. package/lib/data-source/index.d.ts +15 -4
  34. package/lib/data-source/index.js +42 -13
  35. package/lib/data-source/sortCollectionsByInherits.d.ts +10 -0
  36. package/lib/data-source/sortCollectionsByInherits.js +71 -0
  37. package/lib/emitter.d.ts +6 -0
  38. package/lib/emitter.js +12 -0
  39. package/lib/executor/FlowExecutor.d.ts +4 -5
  40. package/lib/executor/FlowExecutor.js +168 -99
  41. package/lib/flow-registry/GlobalFlowRegistry.d.ts +1 -0
  42. package/lib/flow-registry/GlobalFlowRegistry.js +3 -0
  43. package/lib/flow-registry/InstanceFlowRegistry.d.ts +1 -0
  44. package/lib/flow-registry/InstanceFlowRegistry.js +3 -0
  45. package/lib/flowContext.d.ts +37 -5
  46. package/lib/flowContext.js +215 -87
  47. package/lib/flowEngine.d.ts +24 -1
  48. package/lib/flowEngine.js +56 -6
  49. package/lib/flowSettings.d.ts +2 -1
  50. package/lib/flowSettings.js +12 -8
  51. package/lib/hooks/useApplyAutoFlows.js +2 -1
  52. package/lib/index.d.ts +7 -1
  53. package/lib/index.js +32 -3
  54. package/lib/locale/en-US.json +4 -2
  55. package/lib/locale/index.d.ts +4 -0
  56. package/lib/locale/zh-CN.json +4 -2
  57. package/lib/models/CollectionFieldModel.d.ts +3 -0
  58. package/lib/models/CollectionFieldModel.js +48 -5
  59. package/lib/models/flowModel.d.ts +30 -29
  60. package/lib/models/flowModel.js +135 -98
  61. package/lib/models/forkFlowModel.d.ts +8 -4
  62. package/lib/models/forkFlowModel.js +38 -8
  63. package/lib/provider.d.ts +3 -1
  64. package/lib/provider.js +7 -5
  65. package/lib/resources/multiRecordResource.js +25 -1
  66. package/lib/resources/singleRecordResource.js +19 -1
  67. package/lib/resources/sqlResource.d.ts +1 -0
  68. package/lib/resources/sqlResource.js +20 -24
  69. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.d.ts +1 -6
  70. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +27 -20
  71. package/lib/runjs-context/contexts/JSBlockRunJSContext.d.ts +1 -6
  72. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +46 -33
  73. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.d.ts +1 -2
  74. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.js +14 -15
  75. package/lib/runjs-context/contexts/{LinkageRunJSContext.d.ts → JSColumnRunJSContext.d.ts} +6 -3
  76. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +78 -0
  77. package/lib/runjs-context/contexts/JSFieldRunJSContext.d.ts +1 -6
  78. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +28 -24
  79. package/lib/runjs-context/contexts/JSItemRunJSContext.d.ts +1 -6
  80. package/lib/runjs-context/contexts/JSItemRunJSContext.js +24 -20
  81. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.d.ts +1 -2
  82. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.js +16 -17
  83. package/lib/runjs-context/contexts/base.d.ts +9 -0
  84. package/lib/runjs-context/contexts/base.js +183 -0
  85. package/lib/runjs-context/helpers.d.ts +5 -2
  86. package/lib/runjs-context/helpers.js +36 -27
  87. package/lib/runjs-context/registry.d.ts +7 -4
  88. package/lib/runjs-context/registry.js +10 -42
  89. package/lib/runjs-context/setup.d.ts +9 -0
  90. package/lib/runjs-context/setup.js +82 -0
  91. package/lib/runjs-context/snippets/global/{copy-record-json.snippet.js → api-request.snippet.js} +25 -10
  92. package/lib/runjs-context/snippets/global/clipboard-copy-text.snippet.js +61 -0
  93. package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.js → import-esm.snippet.js} +26 -12
  94. package/lib/runjs-context/snippets/global/message-error.snippet.js +6 -0
  95. package/lib/runjs-context/snippets/global/message-success.snippet.js +6 -0
  96. package/lib/runjs-context/snippets/global/notification-open.snippet.d.ts +3 -8
  97. package/lib/runjs-context/snippets/global/notification-open.snippet.js +8 -1
  98. package/lib/runjs-context/snippets/global/open-view-dialog.snippet.js +10 -3
  99. package/lib/runjs-context/snippets/global/open-view-drawer.snippet.js +10 -3
  100. package/lib/runjs-context/snippets/global/query-selector.snippet.js +53 -0
  101. package/lib/runjs-context/snippets/global/require-amd.snippet.d.ts +11 -0
  102. package/lib/runjs-context/snippets/global/{requireAsync.snippet.js → require-amd.snippet.js} +16 -13
  103. package/lib/runjs-context/snippets/global/window-open.snippet.d.ts +3 -8
  104. package/lib/runjs-context/snippets/global/window-open.snippet.js +8 -1
  105. package/lib/runjs-context/snippets/index.d.ts +14 -3
  106. package/lib/runjs-context/snippets/index.js +161 -40
  107. package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.d.ts +11 -0
  108. package/lib/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.js +11 -2
  109. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.d.ts +11 -0
  110. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +64 -0
  111. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.d.ts +11 -0
  112. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +99 -0
  113. package/lib/runjs-context/snippets/{libs → scene/block}/echarts-init.snippet.js +24 -7
  114. package/lib/runjs-context/snippets/scene/block/render-button-handler.snippet.d.ts +11 -0
  115. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-button-handler.snippet.js +17 -9
  116. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.d.ts +11 -0
  117. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +57 -0
  118. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.d.ts +11 -0
  119. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.js +71 -0
  120. package/lib/runjs-context/snippets/scene/block/render-react-jsx.snippet.d.ts +11 -0
  121. package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.js → block/render-react-jsx.snippet.js} +26 -13
  122. package/lib/runjs-context/snippets/scene/block/render-react.snippet.d.ts +11 -0
  123. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-react.snippet.js +18 -17
  124. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.d.ts +11 -0
  125. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +95 -0
  126. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.d.ts +11 -0
  127. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +84 -0
  128. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.d.ts +11 -0
  129. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +60 -0
  130. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.d.ts +11 -0
  131. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +283 -0
  132. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.d.ts +11 -0
  133. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +124 -0
  134. package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.d.ts +11 -0
  135. package/lib/runjs-context/snippets/scene/{jsfield → detail}/color-by-value.snippet.js +13 -3
  136. package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.d.ts +11 -0
  137. package/lib/runjs-context/snippets/{global → scene/detail}/copy-to-clipboard.snippet.js +28 -6
  138. package/lib/runjs-context/snippets/scene/detail/format-number.snippet.d.ts +11 -0
  139. package/lib/runjs-context/snippets/scene/{jsfield → detail}/format-number.snippet.js +13 -3
  140. package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.d.ts +11 -0
  141. package/lib/runjs-context/snippets/scene/{jsfield → detail}/innerHTML-value.snippet.js +13 -3
  142. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.d.ts +11 -0
  143. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +82 -0
  144. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.d.ts +11 -0
  145. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +80 -0
  146. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.d.ts +11 -0
  147. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +74 -0
  148. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.d.ts +11 -0
  149. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.js +63 -0
  150. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.d.ts +11 -0
  151. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +81 -0
  152. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.d.ts +11 -0
  153. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.js +64 -0
  154. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.d.ts +11 -0
  155. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.js +74 -0
  156. package/lib/runjs-context/snippets/scene/form/render-basic.snippet.d.ts +11 -0
  157. package/lib/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.js +11 -2
  158. package/lib/runjs-context/snippets/scene/form/set-disabled.snippet.d.ts +11 -0
  159. package/lib/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.js +12 -3
  160. package/lib/runjs-context/snippets/scene/form/set-field-value.snippet.d.ts +11 -0
  161. package/lib/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.js +12 -3
  162. package/lib/runjs-context/snippets/scene/form/set-required.snippet.d.ts +11 -0
  163. package/lib/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.js +12 -3
  164. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.d.ts +11 -0
  165. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.js +67 -0
  166. package/lib/runjs-context/snippets/scene/form/toggle-visible.snippet.d.ts +11 -0
  167. package/lib/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.js +12 -3
  168. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.d.ts +11 -0
  169. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +64 -0
  170. package/lib/runjs-context/snippets/scene/table/collection-selected-count.snippet.d.ts +11 -0
  171. package/lib/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.js +11 -2
  172. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.d.ts +11 -0
  173. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +79 -0
  174. package/lib/runjs-context/snippets/scene/table/destroy-selected.snippet.d.ts +11 -0
  175. package/lib/runjs-context/snippets/{global/log-json-record.snippet.js → scene/table/destroy-selected.snippet.js} +24 -11
  176. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.d.ts +11 -0
  177. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.js +64 -0
  178. package/lib/runjs-context/snippets/scene/table/iterate-selected-rows.snippet.d.ts +11 -0
  179. package/lib/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.js +11 -2
  180. package/lib/runjs-context/snippets/types.d.ts +9 -1
  181. package/lib/scheduler/ModelOperationScheduler.d.ts +51 -0
  182. package/lib/scheduler/ModelOperationScheduler.js +262 -0
  183. package/lib/types.d.ts +28 -3
  184. package/lib/types.js +4 -3
  185. package/lib/utils/associationObjectVariable.d.ts +32 -0
  186. package/lib/utils/associationObjectVariable.js +157 -0
  187. package/lib/utils/buildSettingsViewInputArgs.d.ts +19 -0
  188. package/lib/utils/buildSettingsViewInputArgs.js +75 -0
  189. package/lib/utils/createEphemeralContext.d.ts +13 -0
  190. package/lib/utils/createEphemeralContext.js +140 -0
  191. package/lib/utils/flows.d.ts +10 -0
  192. package/lib/{runjs-context/snippets/global/api-request-post.snippet.js → utils/flows.js} +21 -15
  193. package/lib/utils/index.d.ts +7 -2
  194. package/lib/utils/index.js +19 -2
  195. package/lib/utils/jsxTransform.d.ts +15 -0
  196. package/lib/utils/jsxTransform.js +68 -0
  197. package/lib/utils/params-resolvers.js +3 -3
  198. package/lib/utils/parsePathnameToViewParams.d.ts +1 -1
  199. package/lib/utils/parsePathnameToViewParams.js +41 -5
  200. package/lib/utils/pruneFilter.d.ts +21 -0
  201. package/lib/{runjs-context/snippets/global/try-catch-async.snippet.js → utils/pruneFilter.js} +24 -16
  202. package/lib/utils/safeGlobals.d.ts +5 -3
  203. package/lib/utils/safeGlobals.js +40 -0
  204. package/lib/utils/schema-utils.js +2 -2
  205. package/lib/utils/serverContextParams.d.ts +1 -0
  206. package/lib/utils/translation.d.ts +4 -1
  207. package/lib/utils/translation.js +6 -2
  208. package/lib/utils/variablesParams.d.ts +9 -5
  209. package/lib/utils/variablesParams.js +47 -36
  210. package/lib/views/DrawerComponent.js +21 -2
  211. package/lib/views/PageComponent.js +2 -1
  212. package/lib/views/ViewNavigation.d.ts +3 -9
  213. package/lib/views/ViewNavigation.js +16 -2
  214. package/lib/views/createViewMeta.d.ts +29 -1
  215. package/lib/views/createViewMeta.js +338 -72
  216. package/lib/views/index.d.ts +1 -0
  217. package/lib/views/index.js +3 -0
  218. package/lib/views/useDialog.d.ts +8 -8
  219. package/lib/views/useDialog.js +8 -7
  220. package/lib/views/useDrawer.d.ts +8 -8
  221. package/lib/views/useDrawer.js +43 -28
  222. package/lib/views/usePage.d.ts +8 -8
  223. package/lib/views/usePage.js +8 -7
  224. package/package.json +6 -3
  225. package/src/BlockScopedFlowEngine.ts +86 -0
  226. package/src/FlowContextProvider.tsx +4 -2
  227. package/src/JSRunner.ts +3 -0
  228. package/src/ViewScopedFlowEngine.ts +15 -1
  229. package/src/__tests__/JSRunner.test.ts +62 -53
  230. package/src/__tests__/blockScopedFlowEngine.test.ts +154 -0
  231. package/src/__tests__/createViewMeta.popup.test.ts +132 -0
  232. package/src/__tests__/flow-engine.test.ts +3 -0
  233. package/src/__tests__/flowContext.test.ts +52 -0
  234. package/src/__tests__/flowContextCreateJSRunner.test.ts +163 -0
  235. package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
  236. package/src/__tests__/flowRunJSContextDefine.test.ts +508 -0
  237. package/src/__tests__/flowSettings.open.test.tsx +2 -0
  238. package/src/__tests__/flowSettings.test.ts +2 -0
  239. package/src/__tests__/globalFlowRegistry.test.ts +1 -1
  240. package/src/__tests__/modelOperationScheduler.test.ts +346 -0
  241. package/src/__tests__/objectVariable.test.ts +464 -0
  242. package/src/__tests__/runjsContext.test.ts +216 -35
  243. package/src/__tests__/runjsContextImplementations.test.ts +217 -0
  244. package/src/__tests__/runjsContextRuntime.test.ts +269 -0
  245. package/src/__tests__/runjsEdgeCases.test.ts +281 -0
  246. package/src/__tests__/runjsLocales.test.ts +36 -0
  247. package/src/__tests__/runjsRuntimeFeatures.test.ts +449 -0
  248. package/src/__tests__/runjsSnippets.test.ts +140 -0
  249. package/src/__tests__/viewScopedFlowEngine.test.ts +3 -3
  250. package/src/acl/Acl.tsx +14 -0
  251. package/src/acl/__tests__/Acl.test.tsx +43 -0
  252. package/src/components/DynamicFlowsEditor.tsx +3 -4
  253. package/src/components/FieldModelRenderer.tsx +20 -7
  254. package/src/components/FieldSkeleton.tsx +27 -0
  255. package/src/components/FlowContextSelector.tsx +6 -2
  256. package/src/components/FlowModelRenderer.tsx +33 -81
  257. package/src/components/FormItem.tsx +8 -1
  258. package/src/components/MobilePopup.style.ts +220 -0
  259. package/src/components/MobilePopup.tsx +86 -0
  260. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +6 -6
  261. package/src/components/index.ts +1 -0
  262. package/src/components/settings/independents/dropdown/FlowsDropdownButton.tsx +1 -1
  263. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +17 -4
  264. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +1 -1
  265. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +39 -10
  266. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +14 -1
  267. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +425 -0
  268. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +5 -7
  269. package/src/components/variables/VariableInput.tsx +25 -5
  270. package/src/components/variables/VariableTag.tsx +54 -2
  271. package/src/components/variables/types.ts +2 -0
  272. package/src/data-source/__tests__/sortCollectionsByInherits.test.ts +125 -0
  273. package/src/data-source/index.ts +42 -11
  274. package/src/data-source/sortCollectionsByInherits.ts +61 -0
  275. package/src/emitter.ts +14 -0
  276. package/src/executor/FlowExecutor.ts +213 -119
  277. package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
  278. package/src/executor/__tests__/flowExecutor.test.ts +151 -5
  279. package/src/flow-registry/GlobalFlowRegistry.ts +1 -0
  280. package/src/flow-registry/InstanceFlowRegistry.ts +1 -0
  281. package/src/flow-registry/__tests__/globalFlowRegistry.test.ts +54 -0
  282. package/src/flowContext.ts +290 -104
  283. package/src/flowEngine.ts +74 -6
  284. package/src/flowSettings.ts +9 -4
  285. package/src/hooks/useApplyAutoFlows.ts +3 -1
  286. package/src/index.ts +12 -1
  287. package/src/locale/en-US.json +4 -2
  288. package/src/locale/zh-CN.json +4 -2
  289. package/src/models/CollectionFieldModel.tsx +56 -7
  290. package/src/models/__tests__/dispatchEvent.behavior.test.ts +169 -0
  291. package/src/models/__tests__/flowModel.getFlows.sort.test.ts +33 -9
  292. package/src/models/__tests__/flowModel.scheduleModelOperation.test.tsx +129 -0
  293. package/src/models/__tests__/flowModel.test.ts +234 -111
  294. package/src/models/__tests__/forkFlowModel.test.ts +40 -7
  295. package/src/models/flowModel.tsx +180 -132
  296. package/src/models/forkFlowModel.ts +48 -8
  297. package/src/provider.tsx +10 -7
  298. package/src/resources/multiRecordResource.ts +28 -1
  299. package/src/resources/singleRecordResource.ts +19 -1
  300. package/src/resources/sqlResource.ts +20 -25
  301. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +28 -21
  302. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +46 -34
  303. package/src/runjs-context/contexts/JSCollectionActionRunJSContext.ts +15 -16
  304. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +58 -0
  305. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +30 -25
  306. package/src/runjs-context/contexts/JSItemRunJSContext.ts +25 -21
  307. package/src/runjs-context/contexts/JSRecordActionRunJSContext.ts +17 -18
  308. package/src/runjs-context/contexts/base.ts +171 -0
  309. package/src/runjs-context/helpers.ts +32 -30
  310. package/src/runjs-context/registry.ts +16 -47
  311. package/src/runjs-context/setup.ts +51 -0
  312. package/src/runjs-context/snippets/global/api-request.snippet.ts +38 -0
  313. package/src/runjs-context/snippets/global/clipboard-copy-text.snippet.ts +42 -0
  314. package/src/runjs-context/snippets/global/import-esm.snippet.ts +39 -0
  315. package/src/runjs-context/snippets/global/message-error.snippet.ts +6 -0
  316. package/src/runjs-context/snippets/global/message-success.snippet.ts +6 -0
  317. package/src/runjs-context/snippets/global/notification-open.snippet.ts +11 -1
  318. package/src/runjs-context/snippets/global/open-view-dialog.snippet.ts +10 -3
  319. package/src/runjs-context/snippets/global/open-view-drawer.snippet.ts +10 -3
  320. package/src/runjs-context/snippets/global/query-selector.snippet.ts +34 -0
  321. package/src/runjs-context/snippets/global/require-amd.snippet.ts +30 -0
  322. package/src/runjs-context/snippets/global/window-open.snippet.ts +11 -1
  323. package/src/runjs-context/snippets/index.ts +177 -39
  324. package/src/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.ts +14 -2
  325. package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +45 -0
  326. package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +80 -0
  327. package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +44 -0
  328. package/src/runjs-context/snippets/scene/block/render-button-handler.snippet.ts +35 -0
  329. package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +38 -0
  330. package/src/runjs-context/snippets/scene/block/render-info-card.snippet.ts +52 -0
  331. package/src/runjs-context/snippets/scene/block/render-react-jsx.snippet.ts +39 -0
  332. package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +38 -0
  333. package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +76 -0
  334. package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +65 -0
  335. package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +46 -0
  336. package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +264 -0
  337. package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +105 -0
  338. package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +33 -0
  339. package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +45 -0
  340. package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +32 -0
  341. package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +31 -0
  342. package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +63 -0
  343. package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +61 -0
  344. package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +55 -0
  345. package/src/runjs-context/snippets/scene/form/calculate-total.snippet.ts +44 -0
  346. package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +62 -0
  347. package/src/runjs-context/snippets/scene/form/conditional-required.snippet.ts +45 -0
  348. package/src/runjs-context/snippets/scene/form/copy-field-values.snippet.ts +55 -0
  349. package/src/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.ts +14 -2
  350. package/src/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.ts +15 -3
  351. package/src/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.ts +15 -3
  352. package/src/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.ts +15 -3
  353. package/src/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.ts +48 -0
  354. package/src/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.ts +15 -3
  355. package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +45 -0
  356. package/src/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.ts +14 -2
  357. package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +60 -0
  358. package/src/runjs-context/snippets/scene/table/destroy-selected.snippet.ts +36 -0
  359. package/src/runjs-context/snippets/scene/table/export-selected-json.snippet.ts +45 -0
  360. package/src/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.ts +14 -2
  361. package/src/runjs-context/snippets/types.ts +5 -1
  362. package/src/scheduler/ModelOperationScheduler.ts +304 -0
  363. package/src/types.ts +34 -0
  364. package/src/utils/__tests__/flows.test.ts +65 -0
  365. package/src/utils/__tests__/jsxTransform.test.ts +38 -0
  366. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +25 -0
  367. package/src/utils/__tests__/pruneFilter.test.ts +38 -0
  368. package/src/utils/__tests__/safeGlobals.test.ts +22 -1
  369. package/src/utils/associationObjectVariable.ts +180 -0
  370. package/src/utils/buildSettingsViewInputArgs.ts +72 -0
  371. package/src/utils/createEphemeralContext.ts +142 -0
  372. package/src/utils/flows.ts +23 -0
  373. package/src/utils/index.ts +10 -2
  374. package/src/utils/jsxTransform.ts +39 -0
  375. package/src/utils/params-resolvers.ts +2 -2
  376. package/src/utils/parsePathnameToViewParams.ts +50 -6
  377. package/src/utils/pruneFilter.ts +41 -0
  378. package/src/utils/safeGlobals.ts +49 -3
  379. package/src/utils/schema-utils.ts +1 -1
  380. package/src/utils/serverContextParams.ts +1 -0
  381. package/src/utils/translation.ts +7 -2
  382. package/src/utils/variablesParams.ts +50 -38
  383. package/src/views/DrawerComponent.tsx +21 -2
  384. package/src/views/PageComponent.tsx +1 -1
  385. package/src/views/ViewNavigation.ts +20 -12
  386. package/src/views/__tests__/ViewNavigation.test.ts +10 -0
  387. package/src/views/createViewMeta.ts +393 -70
  388. package/src/views/index.tsx +1 -0
  389. package/src/views/useDialog.tsx +12 -10
  390. package/src/views/useDrawer.tsx +62 -37
  391. package/src/views/usePage.tsx +13 -10
  392. package/lib/runjs-context/contexts/FlowRunJSContext.d.ts +0 -38
  393. package/lib/runjs-context/contexts/FlowRunJSContext.js +0 -217
  394. package/lib/runjs-context/contexts/LinkageRunJSContext.js +0 -62
  395. package/lib/runjs-context/index.d.ts +0 -19
  396. package/lib/runjs-context/index.js +0 -57
  397. package/lib/runjs-context/snippets/global/api-request-get.snippet.d.ts +0 -16
  398. package/lib/runjs-context/snippets/global/api-request-get.snippet.js +0 -42
  399. package/lib/runjs-context/snippets/global/api-request-post.snippet.d.ts +0 -16
  400. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.d.ts +0 -16
  401. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.js +0 -41
  402. package/lib/runjs-context/snippets/global/requireAsync.snippet.d.ts +0 -16
  403. package/lib/runjs-context/snippets/global/sleep.snippet.d.ts +0 -16
  404. package/lib/runjs-context/snippets/global/sleep.snippet.js +0 -43
  405. package/lib/runjs-context/snippets/global/try-catch-async.snippet.d.ts +0 -16
  406. package/lib/runjs-context/snippets/libs/echarts-init.snippet.d.ts +0 -15
  407. package/lib/runjs-context/snippets/scene/actions/collection-selected-count.snippet.d.ts +0 -15
  408. package/lib/runjs-context/snippets/scene/actions/iterate-selected-rows.snippet.d.ts +0 -15
  409. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.d.ts +0 -15
  410. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.js +0 -43
  411. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.d.ts +0 -15
  412. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.js +0 -40
  413. package/lib/runjs-context/snippets/scene/jsblock/add-event-listener.snippet.d.ts +0 -15
  414. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.d.ts +0 -15
  415. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.js +0 -42
  416. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.d.ts +0 -15
  417. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.js +0 -46
  418. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.d.ts +0 -15
  419. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.js +0 -41
  420. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.d.ts +0 -15
  421. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.js +0 -41
  422. package/lib/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.d.ts +0 -15
  423. package/lib/runjs-context/snippets/scene/jsblock/render-react.snippet.d.ts +0 -15
  424. package/lib/runjs-context/snippets/scene/jsfield/color-by-value.snippet.d.ts +0 -15
  425. package/lib/runjs-context/snippets/scene/jsfield/format-number.snippet.d.ts +0 -15
  426. package/lib/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.d.ts +0 -15
  427. package/lib/runjs-context/snippets/scene/jsitem/render-basic.snippet.d.ts +0 -15
  428. package/lib/runjs-context/snippets/scene/linkage/set-disabled.snippet.d.ts +0 -15
  429. package/lib/runjs-context/snippets/scene/linkage/set-field-value.snippet.d.ts +0 -15
  430. package/lib/runjs-context/snippets/scene/linkage/set-required.snippet.d.ts +0 -15
  431. package/lib/runjs-context/snippets/scene/linkage/toggle-visible.snippet.d.ts +0 -15
  432. package/src/runjs-context/contexts/FlowRunJSContext.ts +0 -190
  433. package/src/runjs-context/contexts/LinkageRunJSContext.ts +0 -35
  434. package/src/runjs-context/index.ts +0 -20
  435. package/src/runjs-context/snippets/global/api-request-get.snippet.ts +0 -20
  436. package/src/runjs-context/snippets/global/api-request-post.snippet.ts +0 -20
  437. package/src/runjs-context/snippets/global/console-log-ctx.snippet.ts +0 -19
  438. package/src/runjs-context/snippets/global/copy-record-json.snippet.ts +0 -21
  439. package/src/runjs-context/snippets/global/copy-to-clipboard.snippet.ts +0 -21
  440. package/src/runjs-context/snippets/global/log-json-record.snippet.ts +0 -21
  441. package/src/runjs-context/snippets/global/requireAsync.snippet.ts +0 -24
  442. package/src/runjs-context/snippets/global/sleep.snippet.ts +0 -21
  443. package/src/runjs-context/snippets/global/try-catch-async.snippet.ts +0 -22
  444. package/src/runjs-context/snippets/global/view-navigation-push.snippet.ts +0 -23
  445. package/src/runjs-context/snippets/libs/echarts-init.snippet.ts +0 -24
  446. package/src/runjs-context/snippets/scene/actions/record-id-message.snippet.ts +0 -21
  447. package/src/runjs-context/snippets/scene/actions/run-action-basic.snippet.ts +0 -18
  448. package/src/runjs-context/snippets/scene/jsblock/append-style.snippet.ts +0 -20
  449. package/src/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.ts +0 -24
  450. package/src/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.ts +0 -19
  451. package/src/runjs-context/snippets/scene/jsblock/render-basic.snippet.ts +0 -24
  452. package/src/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.ts +0 -24
  453. package/src/runjs-context/snippets/scene/jsblock/render-card.snippet.ts +0 -30
  454. package/src/runjs-context/snippets/scene/jsblock/render-react.snippet.ts +0 -34
  455. package/src/runjs-context/snippets/scene/jsfield/color-by-value.snippet.ts +0 -20
  456. package/src/runjs-context/snippets/scene/jsfield/format-number.snippet.ts +0 -19
  457. package/src/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.ts +0 -18
  458. /package/lib/runjs-context/snippets/global/{copy-record-json.snippet.d.ts → api-request.snippet.d.ts} +0 -0
  459. /package/lib/runjs-context/snippets/global/{copy-to-clipboard.snippet.d.ts → clipboard-copy-text.snippet.d.ts} +0 -0
  460. /package/lib/runjs-context/snippets/global/{log-json-record.snippet.d.ts → import-esm.snippet.d.ts} +0 -0
  461. /package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.d.ts → query-selector.snippet.d.ts} +0 -0
  462. /package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.d.ts → block/echarts-init.snippet.d.ts} +0 -0
@@ -13,7 +13,6 @@ import { render, cleanup, waitFor } from '@testing-library/react';
13
13
  import { App, ConfigProvider } from 'antd';
14
14
  import { FlowEngine } from '../../flowEngine';
15
15
  import { FlowModel, ModelRenderMode } from '../../models/flowModel';
16
- import { DefaultSettingsIcon } from '../settings/wrappers/contextual/DefaultSettingsIcon';
17
16
  import { FlowEngineProvider } from '../../provider';
18
17
  import { FlowModelRenderer } from '../FlowModelRenderer';
19
18
 
@@ -81,6 +80,7 @@ vi.mock('antd', () => {
81
80
  Alert,
82
81
  Skeleton,
83
82
  Spin,
83
+ theme: { useToken: () => ({}) },
84
84
  } as any;
85
85
  });
86
86
 
@@ -124,7 +124,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
124
124
  <ConfigProvider>
125
125
  <App>
126
126
  <FlowEngineProvider engine={engine}>
127
- <FlowModelRenderer model={model} showFlowSettings showErrorFallback skipApplyAutoFlows />
127
+ <FlowModelRenderer model={model} showFlowSettings showErrorFallback />
128
128
  </FlowEngineProvider>
129
129
  </App>
130
130
  </ConfigProvider>,
@@ -141,7 +141,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
141
141
  return (
142
142
  <div>
143
143
  {items.map((m: FlowModel) => (
144
- <FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback skipApplyAutoFlows />
144
+ <FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback />
145
145
  ))}
146
146
  </div>
147
147
  );
@@ -167,7 +167,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
167
167
  <ConfigProvider>
168
168
  <App>
169
169
  <FlowEngineProvider engine={engine}>
170
- <FlowModelRenderer model={parent} skipApplyAutoFlows />
170
+ <FlowModelRenderer model={parent} />
171
171
  </FlowEngineProvider>
172
172
  </App>
173
173
  </ConfigProvider>,
@@ -186,7 +186,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
186
186
  return (
187
187
  <div>
188
188
  {cells.map((m: FlowModel) => (
189
- <FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback skipApplyAutoFlows />
189
+ <FlowModelRenderer key={m.uid} model={m} showFlowSettings showErrorFallback />
190
190
  ))}
191
191
  </div>
192
192
  );
@@ -212,7 +212,7 @@ describe('Delete problematic model via FlowSettings menu', () => {
212
212
  <ConfigProvider>
213
213
  <App>
214
214
  <FlowEngineProvider engine={engine}>
215
- <FlowModelRenderer model={parent} skipApplyAutoFlows />
215
+ <FlowModelRenderer model={parent} />
216
216
  </FlowEngineProvider>
217
217
  </App>
218
218
  </ConfigProvider>,
@@ -17,4 +17,5 @@ export * from './FormItem';
17
17
  export * from './settings';
18
18
  export * from './subModel';
19
19
  export * from './variables';
20
+ export * from './MobilePopup';
20
21
  //
@@ -132,7 +132,7 @@ const FlowsDropdownButtonWithModel: React.FC<ModelProvidedProps> = observer(
132
132
  // 如果step使用了action,也获取action的uiSchema
133
133
  let actionUiSchema = {};
134
134
  if (actionStep.use) {
135
- const action = model.flowEngine?.getAction?.(actionStep.use);
135
+ const action = model.getAction?.(actionStep.use);
136
136
  if (action && action.uiSchema) {
137
137
  actionUiSchema = action.uiSchema;
138
138
  }
@@ -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
- message.error(t('Copy failed, please copy [{{uid}}] manually.', { uid }));
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
- const flows = targetModel.getFlows();
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.flowEngine?.getAction?.(actionStep.use);
294
+ const action = targetModel.getAction?.(actionStep.use);
282
295
  hasActionUiSchema = action && action.uiSchema != null;
283
- stepTitle = stepTitle || action.title;
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.flowEngine?.getAction?.(actionStep.use);
165
+ const action = model.getAction?.(actionStep.use);
166
166
  if (action && action.uiSchema) {
167
167
  actionUiSchema = action.uiSchema;
168
168
  }
@@ -80,8 +80,29 @@ const renderToolbarItems = (
80
80
  });
81
81
  };
82
82
 
83
+ // Width in pixels per toolbar item (icon width + spacing)
84
+ const TOOLBAR_ITEM_WIDTH = 19;
85
+
86
+ const toolbarPositionToCSS = {
87
+ inside: `
88
+ top: 2px;
89
+ `,
90
+ above: `
91
+ top: 0px;
92
+ transform: translateY(-100%);
93
+ padding-bottom: 0px;
94
+ margin-bottom: -2px;
95
+ `,
96
+ below: `
97
+ top: 0px;
98
+ transform: translateY(100%);
99
+ padding-top: 2px;
100
+ margin-top: -2px;
101
+ `,
102
+ };
103
+
83
104
  // 使用与 NocoBase 一致的悬浮工具栏样式
84
- const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition }) => css`
105
+ const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition = 'inside', toolbarCount }) => css`
85
106
  position: relative;
86
107
  display: inline;
87
108
 
@@ -117,6 +138,7 @@ const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition
117
138
  border: ${showBorder ? '2px solid var(--colorBorderSettingsHover)' : ''};
118
139
  border-radius: ${ctx.themeToken.borderRadiusLG}px;
119
140
  pointer-events: none;
141
+ min-width: ${TOOLBAR_ITEM_WIDTH * toolbarCount}px;
120
142
 
121
143
  &.nb-in-template {
122
144
  background: var(--colorTemplateBgSettingsHover);
@@ -147,10 +169,7 @@ const floatContainerStyles = ({ showBackground, showBorder, ctx, toolbarPosition
147
169
  display: none; // 防止遮挡其它 icons
148
170
  position: absolute;
149
171
  right: 2px;
150
- top: ${toolbarPosition === 'above' ? '0px' : '2px'};
151
- ${toolbarPosition === 'above' ? 'transform: translateY(-100%);' : ''}
152
- ${toolbarPosition === 'above' ? 'padding-bottom: 2px;' : ''}
153
- ${toolbarPosition === 'above' ? 'margin-bottom: -2px;' : ''}
172
+ ${toolbarPositionToCSS[toolbarPosition] || ''}
154
173
  line-height: 16px;
155
174
  pointer-events: all;
156
175
 
@@ -297,7 +316,7 @@ interface ModelProvidedProps {
297
316
  /**
298
317
  * @default 'inside'
299
318
  */
300
- toolbarPosition?: 'inside' | 'above';
319
+ toolbarPosition?: 'inside' | 'above' | 'below';
301
320
  }
302
321
 
303
322
  interface ModelByIdProps {
@@ -332,7 +351,7 @@ interface ModelByIdProps {
332
351
  /**
333
352
  * @default 'inside'
334
353
  */
335
- toolbarPosition?: 'inside' | 'above';
354
+ toolbarPosition?: 'inside' | 'above' | 'below';
336
355
  }
337
356
 
338
357
  type FlowsFloatContextMenuProps = ModelProvidedProps | ModelByIdProps;
@@ -565,9 +584,13 @@ const FlowsFloatContextMenuWithModel: React.FC<ModelProvidedProps> = observer(
565
584
  return (
566
585
  <div
567
586
  ref={containerRef}
568
- className={`${floatContainerStyles({ showBackground, showBorder, ctx: model.context, toolbarPosition })} ${
569
- hideMenu ? 'hide-parent-menu' : ''
570
- } ${hasButton ? 'has-button-child' : ''} ${className || ''}`}
587
+ className={`${floatContainerStyles({
588
+ showBackground,
589
+ showBorder,
590
+ ctx: model.context,
591
+ toolbarPosition,
592
+ toolbarCount: getToolbarCount(flowEngine, extraToolbarItems),
593
+ })} ${hideMenu ? 'hide-parent-menu' : ''} ${hasButton ? 'has-button-child' : ''} ${className || ''}`}
571
594
  style={containerStyle}
572
595
  data-has-float-menu="true"
573
596
  onMouseMove={handleChildHover}
@@ -653,3 +676,9 @@ const FlowsFloatContextMenuWithModelById: React.FC<ModelByIdProps> = observer(
653
676
  );
654
677
 
655
678
  export { FlowsFloatContextMenu };
679
+
680
+ function getToolbarCount(flowEngine, extraToolbarItems) {
681
+ const toolbarItems = flowEngine?.flowSettings?.getToolbarItems?.() || [];
682
+ const allToolbarItems = [...toolbarItems, ...(extraToolbarItems || [])];
683
+ return allToolbarItems.length;
684
+ }
@@ -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 { compileUiSchema, FlowExitException, getT, resolveDefaultParams, resolveStepUiSchema } from '../../../../utils';
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,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
+ });