@nocobase/flow-engine 2.0.0-alpha.7 → 2.0.0-alpha.71

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 (589) hide show
  1. package/lib/BlockScopedFlowEngine.d.ts +23 -0
  2. package/lib/BlockScopedFlowEngine.js +91 -0
  3. package/lib/FlowContextProvider.d.ts +2 -2
  4. package/lib/FlowContextProvider.js +3 -3
  5. package/lib/FlowDefinition.d.ts +6 -4
  6. package/lib/JSRunner.d.ts +6 -0
  7. package/lib/JSRunner.js +27 -1
  8. package/lib/ViewScopedFlowEngine.d.ts +1 -1
  9. package/lib/ViewScopedFlowEngine.js +18 -1
  10. package/lib/acl/Acl.d.ts +12 -12
  11. package/lib/acl/Acl.js +88 -30
  12. package/lib/components/DynamicFlowsEditor.js +2 -4
  13. package/lib/components/FieldModelRenderer.js +17 -9
  14. package/lib/components/FieldSkeleton.d.ts +10 -0
  15. package/lib/components/FieldSkeleton.js +64 -0
  16. package/lib/components/FlowContextSelector.js +19 -3
  17. package/lib/components/FlowModelRenderer.d.ts +4 -6
  18. package/lib/components/FlowModelRenderer.js +35 -53
  19. package/lib/components/FormItem.js +5 -1
  20. package/lib/components/MobilePopup.d.ts +20 -0
  21. package/lib/components/MobilePopup.js +102 -0
  22. package/lib/components/MobilePopup.style.d.ts +17 -0
  23. package/lib/components/MobilePopup.style.js +186 -0
  24. package/lib/components/common/withFlowDesignMode.d.ts +1 -1
  25. package/lib/components/common/withFlowDesignMode.js +5 -5
  26. package/lib/components/dnd/gridDragPlanner.d.ts +1 -0
  27. package/lib/components/dnd/gridDragPlanner.js +53 -1
  28. package/lib/components/index.d.ts +1 -0
  29. package/lib/components/index.js +3 -1
  30. package/lib/components/settings/independents/dropdown/FlowsDropdownButton.js +71 -53
  31. package/lib/components/settings/wrappers/component/SelectWithTitle.d.ts +19 -0
  32. package/lib/components/settings/wrappers/component/SelectWithTitle.js +136 -0
  33. package/lib/components/settings/wrappers/component/SwitchWithTitle.d.ts +10 -0
  34. package/lib/components/settings/wrappers/component/SwitchWithTitle.js +111 -0
  35. package/lib/components/settings/wrappers/contextual/DefaultSettingsIcon.js +233 -97
  36. package/lib/components/settings/wrappers/contextual/FlowsContextMenu.js +71 -54
  37. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.d.ts +2 -2
  38. package/lib/components/settings/wrappers/contextual/FlowsFloatContextMenu.js +63 -23
  39. package/lib/components/settings/wrappers/contextual/StepSettingsDialog.js +13 -2
  40. package/lib/components/settings/wrappers/embedded/FlowSettings.js +42 -28
  41. package/lib/components/settings/wrappers/embedded/FlowsSettings.js +3 -3
  42. package/lib/components/settings/wrappers/embedded/FlowsSettingsContent.js +52 -32
  43. package/lib/components/subModel/AddSubModelButton.d.ts +7 -0
  44. package/lib/components/subModel/AddSubModelButton.js +78 -8
  45. package/lib/components/subModel/LazyDropdown.js +14 -15
  46. package/lib/components/subModel/utils.d.ts +1 -1
  47. package/lib/components/subModel/utils.js +21 -11
  48. package/lib/components/variables/VariableInput.js +26 -6
  49. package/lib/components/variables/VariableTag.js +43 -2
  50. package/lib/components/variables/types.d.ts +2 -0
  51. package/lib/components/variables/utils.js +4 -2
  52. package/lib/data-source/index.d.ts +23 -4
  53. package/lib/data-source/index.js +135 -14
  54. package/lib/data-source/jioToJoiSchema.js +1 -0
  55. package/lib/emitter.d.ts +6 -0
  56. package/lib/emitter.js +12 -0
  57. package/lib/executor/FlowExecutor.d.ts +6 -6
  58. package/lib/executor/FlowExecutor.js +302 -99
  59. package/lib/flow-registry/GlobalFlowRegistry.d.ts +1 -0
  60. package/lib/flow-registry/GlobalFlowRegistry.js +3 -0
  61. package/lib/flow-registry/InstanceFlowRegistry.d.ts +1 -0
  62. package/lib/flow-registry/InstanceFlowRegistry.js +3 -0
  63. package/lib/flowContext.d.ts +105 -6
  64. package/lib/flowContext.js +447 -139
  65. package/lib/flowEngine.d.ts +71 -1
  66. package/lib/flowEngine.js +319 -16
  67. package/lib/flowSettings.d.ts +4 -3
  68. package/lib/flowSettings.js +45 -21
  69. package/lib/hooks/useApplyAutoFlows.d.ts +1 -0
  70. package/lib/hooks/useApplyAutoFlows.js +3 -2
  71. package/lib/index.d.ts +14 -3
  72. package/lib/index.js +54 -7
  73. package/lib/locale/de-DE.json +62 -0
  74. package/lib/locale/en-US.json +57 -45
  75. package/lib/locale/es-ES.json +62 -0
  76. package/lib/locale/fr-FR.json +62 -0
  77. package/lib/locale/hu-HU.json +62 -0
  78. package/lib/locale/id-ID.json +62 -0
  79. package/lib/locale/index.d.ts +114 -90
  80. package/lib/locale/it-IT.json +62 -0
  81. package/lib/locale/ja-JP.json +62 -0
  82. package/lib/locale/ko-KR.json +62 -0
  83. package/lib/locale/nl-NL.json +62 -0
  84. package/lib/locale/pt-BR.json +62 -0
  85. package/lib/locale/ru-RU.json +62 -0
  86. package/lib/locale/tr-TR.json +62 -0
  87. package/lib/locale/uk-UA.json +62 -0
  88. package/lib/locale/vi-VN.json +62 -0
  89. package/lib/locale/zh-CN.json +58 -46
  90. package/lib/locale/zh-TW.json +62 -0
  91. package/lib/models/CollectionFieldModel.d.ts +7 -2
  92. package/lib/models/CollectionFieldModel.js +63 -16
  93. package/lib/models/flowModel.d.ts +76 -32
  94. package/lib/models/flowModel.js +300 -112
  95. package/lib/models/forkFlowModel.d.ts +8 -4
  96. package/lib/models/forkFlowModel.js +38 -8
  97. package/lib/provider.d.ts +3 -1
  98. package/lib/provider.js +14 -11
  99. package/lib/reactive/index.d.ts +10 -0
  100. package/lib/{runjs-context/snippets/global/api-request-post.snippet.js → reactive/index.js} +14 -15
  101. package/lib/reactive/observer.d.ts +19 -0
  102. package/lib/reactive/observer.js +109 -0
  103. package/lib/resources/baseRecordResource.d.ts +6 -0
  104. package/lib/resources/baseRecordResource.js +38 -3
  105. package/lib/resources/multiRecordResource.d.ts +5 -2
  106. package/lib/resources/multiRecordResource.js +26 -10
  107. package/lib/resources/singleRecordResource.js +8 -3
  108. package/lib/resources/sqlResource.d.ts +5 -3
  109. package/lib/resources/sqlResource.js +30 -28
  110. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.d.ts +1 -6
  111. package/lib/runjs-context/contexts/FormJSFieldItemRunJSContext.js +37 -20
  112. package/lib/runjs-context/contexts/JSBlockRunJSContext.d.ts +1 -6
  113. package/lib/runjs-context/contexts/JSBlockRunJSContext.js +46 -33
  114. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.d.ts +1 -2
  115. package/lib/runjs-context/contexts/JSCollectionActionRunJSContext.js +14 -15
  116. package/lib/runjs-context/contexts/{LinkageRunJSContext.d.ts → JSColumnRunJSContext.d.ts} +6 -3
  117. package/lib/runjs-context/contexts/JSColumnRunJSContext.js +78 -0
  118. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.d.ts +16 -0
  119. package/lib/runjs-context/contexts/JSEditableFieldRunJSContext.js +125 -0
  120. package/lib/runjs-context/contexts/JSFieldRunJSContext.d.ts +1 -6
  121. package/lib/runjs-context/contexts/JSFieldRunJSContext.js +28 -24
  122. package/lib/runjs-context/contexts/JSItemRunJSContext.d.ts +1 -6
  123. package/lib/runjs-context/contexts/JSItemRunJSContext.js +34 -20
  124. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.d.ts +1 -2
  125. package/lib/runjs-context/contexts/JSRecordActionRunJSContext.js +16 -17
  126. package/lib/runjs-context/contexts/base.d.ts +9 -0
  127. package/lib/runjs-context/contexts/base.js +879 -0
  128. package/lib/runjs-context/contributions.d.ts +33 -0
  129. package/lib/runjs-context/contributions.js +88 -0
  130. package/lib/runjs-context/helpers.d.ts +5 -2
  131. package/lib/runjs-context/helpers.js +36 -27
  132. package/lib/runjs-context/registry.d.ts +7 -4
  133. package/lib/runjs-context/registry.js +10 -42
  134. package/lib/runjs-context/setup.d.ts +9 -0
  135. package/lib/runjs-context/setup.js +88 -0
  136. package/lib/runjs-context/snippets/global/{copy-record-json.snippet.js → api-request.snippet.js} +25 -10
  137. package/lib/runjs-context/snippets/global/clipboard-copy-text.snippet.js +61 -0
  138. package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.js → import-esm.snippet.js} +26 -12
  139. package/lib/runjs-context/snippets/global/message-error.snippet.js +6 -0
  140. package/lib/runjs-context/snippets/global/message-success.snippet.js +6 -0
  141. package/lib/runjs-context/snippets/global/notification-open.snippet.d.ts +3 -8
  142. package/lib/runjs-context/snippets/global/notification-open.snippet.js +8 -1
  143. package/lib/runjs-context/snippets/global/open-view-dialog.snippet.js +10 -3
  144. package/lib/runjs-context/snippets/global/open-view-drawer.snippet.js +10 -3
  145. package/lib/runjs-context/snippets/global/query-selector.snippet.js +53 -0
  146. package/lib/runjs-context/snippets/global/require-amd.snippet.d.ts +11 -0
  147. package/lib/runjs-context/snippets/global/{requireAsync.snippet.js → require-amd.snippet.js} +16 -13
  148. package/lib/runjs-context/snippets/global/window-open.snippet.d.ts +3 -8
  149. package/lib/runjs-context/snippets/global/window-open.snippet.js +8 -1
  150. package/lib/runjs-context/snippets/index.d.ts +24 -3
  151. package/lib/runjs-context/snippets/index.js +183 -40
  152. package/lib/runjs-context/snippets/scene/block/add-event-listener.snippet.d.ts +11 -0
  153. package/lib/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.js +11 -2
  154. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.d.ts +11 -0
  155. package/lib/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.js +64 -0
  156. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.d.ts +11 -0
  157. package/lib/runjs-context/snippets/scene/block/chartjs-bar.snippet.js +99 -0
  158. package/lib/runjs-context/snippets/{libs → scene/block}/echarts-init.snippet.js +24 -7
  159. package/lib/runjs-context/snippets/scene/block/render-antd-icons.snippet.d.ts +11 -0
  160. package/lib/runjs-context/snippets/scene/block/render-antd-icons.snippet.js +65 -0
  161. package/lib/runjs-context/snippets/scene/block/render-button-handler.snippet.d.ts +11 -0
  162. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-button-handler.snippet.js +17 -9
  163. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.d.ts +11 -0
  164. package/lib/runjs-context/snippets/scene/block/render-iframe.snippet.js +57 -0
  165. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.d.ts +11 -0
  166. package/lib/runjs-context/snippets/scene/block/render-info-card.snippet.js +71 -0
  167. package/lib/runjs-context/snippets/scene/block/render-react-jsx.snippet.d.ts +11 -0
  168. package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.js → block/render-react-jsx.snippet.js} +26 -13
  169. package/lib/runjs-context/snippets/scene/block/render-react.snippet.d.ts +11 -0
  170. package/lib/runjs-context/snippets/scene/{jsblock → block}/render-react.snippet.js +18 -17
  171. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.d.ts +11 -0
  172. package/lib/runjs-context/snippets/scene/block/render-statistics.snippet.js +95 -0
  173. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.d.ts +11 -0
  174. package/lib/runjs-context/snippets/scene/block/render-timeline.snippet.js +84 -0
  175. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.d.ts +11 -0
  176. package/lib/runjs-context/snippets/scene/block/resource-example.snippet.js +60 -0
  177. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.d.ts +11 -0
  178. package/lib/runjs-context/snippets/scene/block/three-users-orbit.snippet.js +283 -0
  179. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.d.ts +11 -0
  180. package/lib/runjs-context/snippets/scene/block/vue-component.snippet.js +124 -0
  181. package/lib/runjs-context/snippets/scene/detail/color-by-value.snippet.d.ts +11 -0
  182. package/lib/runjs-context/snippets/scene/{jsfield → detail}/color-by-value.snippet.js +13 -3
  183. package/lib/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.d.ts +11 -0
  184. package/lib/runjs-context/snippets/{global → scene/detail}/copy-to-clipboard.snippet.js +28 -6
  185. package/lib/runjs-context/snippets/scene/detail/format-number.snippet.d.ts +11 -0
  186. package/lib/runjs-context/snippets/scene/{jsfield → detail}/format-number.snippet.js +13 -3
  187. package/lib/runjs-context/snippets/scene/detail/innerHTML-value.snippet.d.ts +11 -0
  188. package/lib/runjs-context/snippets/scene/{jsfield → detail}/innerHTML-value.snippet.js +13 -3
  189. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.d.ts +11 -0
  190. package/lib/runjs-context/snippets/scene/detail/percentage-bar.snippet.js +82 -0
  191. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.d.ts +11 -0
  192. package/lib/runjs-context/snippets/scene/detail/relative-time.snippet.js +80 -0
  193. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.d.ts +11 -0
  194. package/lib/runjs-context/snippets/scene/detail/status-tag.snippet.js +74 -0
  195. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.d.ts +11 -0
  196. package/lib/runjs-context/snippets/scene/form/calculate-total.snippet.js +63 -0
  197. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.d.ts +11 -0
  198. package/lib/runjs-context/snippets/scene/form/cascade-select.snippet.js +81 -0
  199. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.d.ts +11 -0
  200. package/lib/runjs-context/snippets/scene/form/conditional-required.snippet.js +64 -0
  201. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.d.ts +11 -0
  202. package/lib/runjs-context/snippets/scene/form/copy-field-values.snippet.js +74 -0
  203. package/lib/runjs-context/snippets/scene/form/render-basic.snippet.d.ts +11 -0
  204. package/lib/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.js +11 -2
  205. package/lib/runjs-context/snippets/scene/form/set-disabled.snippet.d.ts +11 -0
  206. package/lib/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.js +12 -3
  207. package/lib/runjs-context/snippets/scene/form/set-field-value.snippet.d.ts +11 -0
  208. package/lib/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.js +12 -3
  209. package/lib/runjs-context/snippets/scene/form/set-required.snippet.d.ts +11 -0
  210. package/lib/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.js +12 -3
  211. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.d.ts +11 -0
  212. package/lib/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.js +67 -0
  213. package/lib/runjs-context/snippets/scene/form/toggle-visible.snippet.d.ts +11 -0
  214. package/lib/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.js +12 -3
  215. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.d.ts +11 -0
  216. package/lib/runjs-context/snippets/scene/table/cell-open-dialog.snippet.js +64 -0
  217. package/lib/runjs-context/snippets/scene/table/collection-selected-count.snippet.d.ts +11 -0
  218. package/lib/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.js +11 -2
  219. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.d.ts +11 -0
  220. package/lib/runjs-context/snippets/scene/table/concat-fields.snippet.js +79 -0
  221. package/lib/runjs-context/snippets/scene/table/destroy-selected.snippet.d.ts +11 -0
  222. package/lib/runjs-context/snippets/{global/log-json-record.snippet.js → scene/table/destroy-selected.snippet.js} +24 -11
  223. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.d.ts +11 -0
  224. package/lib/runjs-context/snippets/scene/table/export-selected-json.snippet.js +64 -0
  225. package/lib/runjs-context/snippets/scene/table/iterate-selected-rows.snippet.d.ts +11 -0
  226. package/lib/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.js +11 -2
  227. package/lib/runjs-context/snippets/types.d.ts +9 -1
  228. package/lib/runjsLibs.d.ts +28 -0
  229. package/lib/runjsLibs.js +532 -0
  230. package/lib/scheduler/ModelOperationScheduler.d.ts +53 -0
  231. package/lib/scheduler/ModelOperationScheduler.js +262 -0
  232. package/lib/types.d.ts +66 -7
  233. package/lib/types.js +4 -3
  234. package/lib/utils/associationObjectVariable.d.ts +32 -0
  235. package/lib/utils/associationObjectVariable.js +157 -0
  236. package/lib/utils/createCollectionContextMeta.d.ts +1 -1
  237. package/lib/utils/createCollectionContextMeta.js +9 -4
  238. package/lib/utils/createEphemeralContext.d.ts +13 -0
  239. package/lib/utils/createEphemeralContext.js +140 -0
  240. package/lib/utils/flows.d.ts +10 -0
  241. package/lib/{runjs-context/snippets/global/console-log-ctx.snippet.js → utils/flows.js} +21 -14
  242. package/lib/utils/index.d.ts +9 -3
  243. package/lib/utils/index.js +30 -2
  244. package/lib/utils/jsxTransform.d.ts +15 -0
  245. package/lib/utils/jsxTransform.js +68 -0
  246. package/lib/utils/params-resolvers.js +19 -12
  247. package/lib/utils/parsePathnameToViewParams.d.ts +1 -1
  248. package/lib/utils/parsePathnameToViewParams.js +41 -5
  249. package/lib/utils/pruneFilter.d.ts +21 -0
  250. package/lib/{runjs-context/snippets/global/try-catch-async.snippet.js → utils/pruneFilter.js} +24 -16
  251. package/lib/utils/resolveModuleUrl.d.ts +58 -0
  252. package/lib/utils/resolveModuleUrl.js +65 -0
  253. package/lib/utils/runjsModuleLoader.d.ts +58 -0
  254. package/lib/utils/runjsModuleLoader.js +422 -0
  255. package/lib/utils/runjsTemplateCompat.d.ts +35 -0
  256. package/lib/utils/runjsTemplateCompat.js +743 -0
  257. package/lib/utils/safeGlobals.d.ts +6 -8
  258. package/lib/utils/safeGlobals.js +169 -14
  259. package/lib/utils/schema-utils.d.ts +6 -0
  260. package/lib/utils/schema-utils.js +71 -6
  261. package/lib/utils/serverContextParams.d.ts +4 -0
  262. package/lib/utils/serverContextParams.js +2 -0
  263. package/lib/utils/translation.d.ts +4 -1
  264. package/lib/utils/translation.js +6 -2
  265. package/lib/utils/variablesParams.d.ts +22 -5
  266. package/lib/utils/variablesParams.js +141 -61
  267. package/lib/views/DialogComponent.js +1 -5
  268. package/lib/views/DrawerComponent.js +18 -9
  269. package/lib/views/PageComponent.js +5 -5
  270. package/lib/views/ViewNavigation.d.ts +11 -15
  271. package/lib/views/ViewNavigation.js +37 -19
  272. package/lib/views/createViewMeta.d.ts +27 -5
  273. package/lib/views/createViewMeta.js +338 -72
  274. package/lib/views/index.d.ts +1 -1
  275. package/lib/views/index.js +4 -0
  276. package/lib/views/useDialog.d.ts +10 -9
  277. package/lib/views/useDialog.js +46 -34
  278. package/lib/views/useDrawer.d.ts +10 -9
  279. package/lib/views/useDrawer.js +74 -48
  280. package/lib/views/usePage.d.ts +14 -9
  281. package/lib/views/usePage.js +82 -33
  282. package/lib/views/usePopover.js +4 -1
  283. package/lib/views/viewEvents.d.ts +17 -0
  284. package/lib/views/viewEvents.js +90 -0
  285. package/package.json +6 -3
  286. package/src/BlockScopedFlowEngine.ts +85 -0
  287. package/src/FlowContextProvider.tsx +4 -2
  288. package/src/JSRunner.ts +39 -1
  289. package/src/ViewScopedFlowEngine.ts +21 -1
  290. package/src/__tests__/JSRunner.test.ts +125 -52
  291. package/src/__tests__/blockScopedFlowEngine.test.ts +154 -0
  292. package/src/__tests__/createViewMeta.popup.test.ts +203 -0
  293. package/src/__tests__/flow-engine.test.ts +3 -0
  294. package/src/__tests__/flowContext.test.ts +160 -0
  295. package/src/__tests__/flowContextCreateJSRunner.test.ts +163 -0
  296. package/src/__tests__/flowEngine.dataSourceDirty.test.ts +63 -0
  297. package/src/__tests__/flowEngine.destroyModel.test.ts +74 -0
  298. package/src/__tests__/flowEngine.moveModel.test.ts +43 -0
  299. package/src/__tests__/flowEngine.removeModel.test.ts +72 -0
  300. package/src/__tests__/flowEngine.saveModel.test.ts +4 -0
  301. package/src/__tests__/flowModel.openView.navigation.test.ts +31 -2
  302. package/src/__tests__/flowRunJSContextDefine.test.ts +508 -0
  303. package/src/__tests__/flowSettings.open.test.tsx +71 -15
  304. package/src/__tests__/flowSettings.test.ts +2 -0
  305. package/src/__tests__/globalFlowRegistry.test.ts +1 -1
  306. package/src/__tests__/modelOperationScheduler.test.ts +346 -0
  307. package/src/__tests__/objectVariable.test.ts +464 -0
  308. package/src/__tests__/provider.test.tsx +0 -5
  309. package/src/__tests__/runjsContext.test.ts +219 -35
  310. package/src/__tests__/runjsContextImplementations.test.ts +217 -0
  311. package/src/__tests__/runjsContextRuntime.test.ts +269 -0
  312. package/src/__tests__/runjsEdgeCases.test.ts +281 -0
  313. package/src/__tests__/runjsExternalLibs.test.ts +242 -0
  314. package/src/__tests__/runjsLibsLazyLoading.test.ts +44 -0
  315. package/src/__tests__/runjsLocales.test.ts +39 -0
  316. package/src/__tests__/runjsPreprocessDefault.test.ts +49 -0
  317. package/src/__tests__/runjsRuntimeFeatures.test.ts +461 -0
  318. package/src/__tests__/runjsSnippets.test.ts +140 -0
  319. package/src/__tests__/viewScopedFlowEngine.test.ts +101 -3
  320. package/src/acl/Acl.tsx +85 -31
  321. package/src/acl/__tests__/Acl.test.tsx +43 -1
  322. package/src/components/DynamicFlowsEditor.tsx +0 -10
  323. package/src/components/FieldModelRenderer.tsx +22 -9
  324. package/src/components/FieldSkeleton.tsx +27 -0
  325. package/src/components/FlowContextSelector.tsx +20 -2
  326. package/src/components/FlowModelRenderer.tsx +52 -84
  327. package/src/components/FormItem.tsx +8 -1
  328. package/src/components/MobilePopup.style.ts +220 -0
  329. package/src/components/MobilePopup.tsx +86 -0
  330. package/src/components/__tests__/FlowModelRenderer.test.tsx +89 -0
  331. package/src/components/__tests__/flow-model-render-error-fallback.test.tsx +6 -6
  332. package/src/components/__tests__/gridDragPlanner.test.ts +141 -1
  333. package/src/components/common/withFlowDesignMode.tsx +5 -5
  334. package/src/components/dnd/gridDragPlanner.ts +60 -0
  335. package/src/components/index.ts +1 -0
  336. package/src/components/settings/independents/dropdown/FlowsDropdownButton.tsx +34 -17
  337. package/src/components/settings/wrappers/component/SelectWithTitle.tsx +110 -0
  338. package/src/components/settings/wrappers/component/SwitchWithTitle.tsx +83 -0
  339. package/src/components/settings/wrappers/component/__tests__/InlineControls.test.tsx +74 -0
  340. package/src/components/settings/wrappers/contextual/DefaultSettingsIcon.tsx +272 -125
  341. package/src/components/settings/wrappers/contextual/FlowsContextMenu.tsx +34 -18
  342. package/src/components/settings/wrappers/contextual/FlowsFloatContextMenu.tsx +56 -18
  343. package/src/components/settings/wrappers/contextual/StepSettings.tsx +1 -2
  344. package/src/components/settings/wrappers/contextual/StepSettingsDialog.tsx +13 -1
  345. package/src/components/settings/wrappers/contextual/__tests__/DefaultSettingsIcon.test.tsx +624 -0
  346. package/src/components/settings/wrappers/embedded/FlowSettings.tsx +47 -35
  347. package/src/components/settings/wrappers/embedded/FlowsSettings.tsx +1 -1
  348. package/src/components/settings/wrappers/embedded/FlowsSettingsContent.tsx +64 -42
  349. package/src/components/subModel/AddSubModelButton.tsx +104 -9
  350. package/src/components/subModel/LazyDropdown.tsx +14 -14
  351. package/src/components/subModel/__tests__/AddSubModelButton.test.tsx +168 -7
  352. package/src/components/subModel/__tests__/utils.test.ts +12 -12
  353. package/src/components/subModel/utils.ts +25 -6
  354. package/src/components/variables/VariableInput.tsx +32 -6
  355. package/src/components/variables/VariableTag.tsx +54 -2
  356. package/src/components/variables/types.ts +2 -0
  357. package/src/components/variables/utils.ts +7 -3
  358. package/src/data-source/index.ts +143 -12
  359. package/src/data-source/jioToJoiSchema.ts +1 -0
  360. package/src/emitter.ts +14 -0
  361. package/src/executor/FlowExecutor.ts +383 -119
  362. package/src/executor/__tests__/ctx-defs-injection.test.ts +197 -0
  363. package/src/executor/__tests__/flowExecutor.test.ts +217 -5
  364. package/src/flow-registry/GlobalFlowRegistry.ts +1 -0
  365. package/src/flow-registry/InstanceFlowRegistry.ts +1 -0
  366. package/src/flow-registry/__tests__/globalFlowRegistry.test.ts +54 -0
  367. package/src/flowContext.ts +646 -158
  368. package/src/flowEngine.ts +385 -14
  369. package/src/flowSettings.ts +59 -30
  370. package/src/hooks/useApplyAutoFlows.ts +5 -3
  371. package/src/index.ts +26 -3
  372. package/src/locale/de-DE.json +62 -0
  373. package/src/locale/en-US.json +57 -45
  374. package/src/locale/es-ES.json +62 -0
  375. package/src/locale/fr-FR.json +62 -0
  376. package/src/locale/hu-HU.json +62 -0
  377. package/src/locale/id-ID.json +62 -0
  378. package/src/locale/it-IT.json +62 -0
  379. package/src/locale/ja-JP.json +62 -0
  380. package/src/locale/ko-KR.json +62 -0
  381. package/src/locale/nl-NL.json +62 -0
  382. package/src/locale/pt-BR.json +62 -0
  383. package/src/locale/ru-RU.json +62 -0
  384. package/src/locale/tr-TR.json +62 -0
  385. package/src/locale/uk-UA.json +62 -0
  386. package/src/locale/vi-VN.json +62 -0
  387. package/src/locale/zh-CN.json +58 -46
  388. package/src/locale/zh-TW.json +62 -0
  389. package/src/models/CollectionFieldModel.tsx +82 -18
  390. package/src/models/__tests__/dispatchEvent.behavior.test.ts +169 -0
  391. package/src/models/__tests__/dispatchEvent.when.test.ts +356 -0
  392. package/src/models/__tests__/flowEngine.resolveUse.test.ts +170 -0
  393. package/src/models/__tests__/flowModel.clone.test.ts +416 -0
  394. package/src/models/__tests__/flowModel.getFlows.sort.test.ts +33 -9
  395. package/src/models/__tests__/flowModel.scheduleModelOperation.test.tsx +129 -0
  396. package/src/models/__tests__/flowModel.test.ts +296 -119
  397. package/src/models/__tests__/forkFlowModel.test.ts +40 -7
  398. package/src/models/flowModel.tsx +426 -148
  399. package/src/models/forkFlowModel.ts +48 -8
  400. package/src/provider.tsx +18 -14
  401. package/src/reactive/__tests__/observer.test.tsx +211 -0
  402. package/src/reactive/index.ts +11 -0
  403. package/src/reactive/observer.tsx +101 -0
  404. package/src/resources/__tests__/multiRecordResource.test.ts +44 -0
  405. package/src/resources/__tests__/sqlResource.test.ts +60 -0
  406. package/src/resources/baseRecordResource.ts +46 -3
  407. package/src/resources/multiRecordResource.ts +28 -12
  408. package/src/resources/singleRecordResource.ts +9 -3
  409. package/src/resources/sqlResource.ts +33 -32
  410. package/src/runjs-context/contexts/FormJSFieldItemRunJSContext.ts +38 -21
  411. package/src/runjs-context/contexts/JSBlockRunJSContext.ts +50 -34
  412. package/src/runjs-context/contexts/JSCollectionActionRunJSContext.ts +15 -16
  413. package/src/runjs-context/contexts/JSColumnRunJSContext.ts +58 -0
  414. package/src/runjs-context/contexts/JSEditableFieldRunJSContext.ts +106 -0
  415. package/src/runjs-context/contexts/JSFieldRunJSContext.ts +30 -25
  416. package/src/runjs-context/contexts/JSItemRunJSContext.ts +35 -21
  417. package/src/runjs-context/contexts/JSRecordActionRunJSContext.ts +17 -18
  418. package/src/runjs-context/contexts/base.ts +871 -0
  419. package/src/runjs-context/contributions.ts +88 -0
  420. package/src/runjs-context/helpers.ts +32 -30
  421. package/src/runjs-context/registry.ts +16 -47
  422. package/src/runjs-context/setup.ts +57 -0
  423. package/src/runjs-context/snippets/global/api-request.snippet.ts +38 -0
  424. package/src/runjs-context/snippets/global/clipboard-copy-text.snippet.ts +42 -0
  425. package/src/runjs-context/snippets/global/import-esm.snippet.ts +39 -0
  426. package/src/runjs-context/snippets/global/message-error.snippet.ts +6 -0
  427. package/src/runjs-context/snippets/global/message-success.snippet.ts +6 -0
  428. package/src/runjs-context/snippets/global/notification-open.snippet.ts +11 -1
  429. package/src/runjs-context/snippets/global/open-view-dialog.snippet.ts +10 -3
  430. package/src/runjs-context/snippets/global/open-view-drawer.snippet.ts +10 -3
  431. package/src/runjs-context/snippets/global/query-selector.snippet.ts +34 -0
  432. package/src/runjs-context/snippets/global/require-amd.snippet.ts +30 -0
  433. package/src/runjs-context/snippets/global/window-open.snippet.ts +11 -1
  434. package/src/runjs-context/snippets/index.ts +212 -39
  435. package/src/runjs-context/snippets/scene/{jsblock → block}/add-event-listener.snippet.ts +14 -2
  436. package/src/runjs-context/snippets/scene/block/api-fetch-render-list.snippet.ts +45 -0
  437. package/src/runjs-context/snippets/scene/block/chartjs-bar.snippet.ts +80 -0
  438. package/src/runjs-context/snippets/scene/block/echarts-init.snippet.ts +44 -0
  439. package/src/runjs-context/snippets/scene/block/render-antd-icons.snippet.ts +46 -0
  440. package/src/runjs-context/snippets/scene/block/render-button-handler.snippet.ts +35 -0
  441. package/src/runjs-context/snippets/scene/block/render-iframe.snippet.ts +38 -0
  442. package/src/runjs-context/snippets/scene/block/render-info-card.snippet.ts +52 -0
  443. package/src/runjs-context/snippets/scene/block/render-react-jsx.snippet.ts +39 -0
  444. package/src/runjs-context/snippets/scene/block/render-react.snippet.ts +38 -0
  445. package/src/runjs-context/snippets/scene/block/render-statistics.snippet.ts +76 -0
  446. package/src/runjs-context/snippets/scene/block/render-timeline.snippet.ts +65 -0
  447. package/src/runjs-context/snippets/scene/block/resource-example.snippet.ts +46 -0
  448. package/src/runjs-context/snippets/scene/block/three-users-orbit.snippet.ts +264 -0
  449. package/src/runjs-context/snippets/scene/block/vue-component.snippet.ts +105 -0
  450. package/src/runjs-context/snippets/scene/detail/color-by-value.snippet.ts +33 -0
  451. package/src/runjs-context/snippets/scene/detail/copy-to-clipboard.snippet.ts +45 -0
  452. package/src/runjs-context/snippets/scene/detail/format-number.snippet.ts +32 -0
  453. package/src/runjs-context/snippets/scene/detail/innerHTML-value.snippet.ts +31 -0
  454. package/src/runjs-context/snippets/scene/detail/percentage-bar.snippet.ts +63 -0
  455. package/src/runjs-context/snippets/scene/detail/relative-time.snippet.ts +61 -0
  456. package/src/runjs-context/snippets/scene/detail/status-tag.snippet.ts +55 -0
  457. package/src/runjs-context/snippets/scene/form/calculate-total.snippet.ts +44 -0
  458. package/src/runjs-context/snippets/scene/form/cascade-select.snippet.ts +62 -0
  459. package/src/runjs-context/snippets/scene/form/conditional-required.snippet.ts +45 -0
  460. package/src/runjs-context/snippets/scene/form/copy-field-values.snippet.ts +55 -0
  461. package/src/runjs-context/snippets/scene/{jsitem → form}/render-basic.snippet.ts +14 -2
  462. package/src/runjs-context/snippets/scene/{linkage → form}/set-disabled.snippet.ts +15 -3
  463. package/src/runjs-context/snippets/scene/{linkage → form}/set-field-value.snippet.ts +15 -3
  464. package/src/runjs-context/snippets/scene/{linkage → form}/set-required.snippet.ts +15 -3
  465. package/src/runjs-context/snippets/scene/form/toggle-multiple-fields.snippet.ts +48 -0
  466. package/src/runjs-context/snippets/scene/{linkage → form}/toggle-visible.snippet.ts +15 -3
  467. package/src/runjs-context/snippets/scene/table/cell-open-dialog.snippet.ts +45 -0
  468. package/src/runjs-context/snippets/scene/{actions → table}/collection-selected-count.snippet.ts +14 -2
  469. package/src/runjs-context/snippets/scene/table/concat-fields.snippet.ts +60 -0
  470. package/src/runjs-context/snippets/scene/table/destroy-selected.snippet.ts +36 -0
  471. package/src/runjs-context/snippets/scene/table/export-selected-json.snippet.ts +45 -0
  472. package/src/runjs-context/snippets/scene/{actions → table}/iterate-selected-rows.snippet.ts +14 -2
  473. package/src/runjs-context/snippets/types.ts +5 -1
  474. package/src/runjsLibs.ts +622 -0
  475. package/src/scheduler/ModelOperationScheduler.ts +306 -0
  476. package/src/types.ts +86 -5
  477. package/src/utils/__tests__/createCollectionContextMeta.test.ts +51 -0
  478. package/src/utils/__tests__/flows.test.ts +65 -0
  479. package/src/utils/__tests__/jsxTransform.test.ts +38 -0
  480. package/src/utils/__tests__/params-resolvers.test.ts +40 -0
  481. package/src/utils/__tests__/parsePathnameToViewParams.test.ts +25 -0
  482. package/src/utils/__tests__/pruneFilter.test.ts +38 -0
  483. package/src/utils/__tests__/runjsRequireAsyncAutoWhitelist.test.ts +38 -0
  484. package/src/utils/__tests__/runjsTemplateCompat.test.ts +159 -0
  485. package/src/utils/__tests__/safeGlobals.test.ts +79 -2
  486. package/src/utils/__tests__/utils.test.ts +114 -15
  487. package/src/utils/__tests__/variablesParams.test.ts +120 -0
  488. package/src/utils/associationObjectVariable.ts +180 -0
  489. package/src/utils/createCollectionContextMeta.ts +9 -3
  490. package/src/utils/createEphemeralContext.ts +142 -0
  491. package/src/utils/flows.ts +23 -0
  492. package/src/utils/index.ts +17 -3
  493. package/src/utils/jsxTransform.ts +39 -0
  494. package/src/utils/params-resolvers.ts +25 -11
  495. package/src/utils/parsePathnameToViewParams.ts +50 -6
  496. package/src/utils/pruneFilter.ts +41 -0
  497. package/src/utils/resolveModuleUrl.ts +91 -0
  498. package/src/utils/runjsModuleLoader.ts +553 -0
  499. package/src/utils/runjsTemplateCompat.ts +828 -0
  500. package/src/utils/safeGlobals.ts +181 -15
  501. package/src/utils/schema-utils.ts +81 -3
  502. package/src/utils/serverContextParams.ts +6 -0
  503. package/src/utils/translation.ts +7 -2
  504. package/src/utils/variablesParams.ts +164 -72
  505. package/src/views/DialogComponent.tsx +1 -4
  506. package/src/views/DrawerComponent.tsx +19 -7
  507. package/src/views/PageComponent.tsx +3 -5
  508. package/src/views/ViewNavigation.ts +49 -43
  509. package/src/views/__tests__/FlowView.usePage.test.tsx +186 -0
  510. package/src/views/__tests__/ViewNavigation.test.ts +54 -34
  511. package/src/views/__tests__/useDialog.closeDestroy.test.tsx +159 -0
  512. package/src/views/__tests__/viewEvents.resolveOpenerEngine.test.ts +28 -0
  513. package/src/views/createViewMeta.ts +402 -73
  514. package/src/views/index.tsx +1 -1
  515. package/src/views/useDialog.tsx +52 -31
  516. package/src/views/useDrawer.tsx +99 -55
  517. package/src/views/usePage.tsx +101 -33
  518. package/src/views/usePopover.tsx +4 -1
  519. package/src/views/viewEvents.ts +55 -0
  520. package/lib/runjs-context/contexts/FlowRunJSContext.d.ts +0 -38
  521. package/lib/runjs-context/contexts/FlowRunJSContext.js +0 -217
  522. package/lib/runjs-context/contexts/LinkageRunJSContext.js +0 -62
  523. package/lib/runjs-context/index.d.ts +0 -19
  524. package/lib/runjs-context/index.js +0 -57
  525. package/lib/runjs-context/snippets/global/api-request-get.snippet.d.ts +0 -16
  526. package/lib/runjs-context/snippets/global/api-request-get.snippet.js +0 -42
  527. package/lib/runjs-context/snippets/global/api-request-post.snippet.d.ts +0 -16
  528. package/lib/runjs-context/snippets/global/console-log-ctx.snippet.d.ts +0 -16
  529. package/lib/runjs-context/snippets/global/requireAsync.snippet.d.ts +0 -16
  530. package/lib/runjs-context/snippets/global/sleep.snippet.d.ts +0 -16
  531. package/lib/runjs-context/snippets/global/sleep.snippet.js +0 -43
  532. package/lib/runjs-context/snippets/global/try-catch-async.snippet.d.ts +0 -16
  533. package/lib/runjs-context/snippets/libs/echarts-init.snippet.d.ts +0 -15
  534. package/lib/runjs-context/snippets/scene/actions/collection-selected-count.snippet.d.ts +0 -15
  535. package/lib/runjs-context/snippets/scene/actions/iterate-selected-rows.snippet.d.ts +0 -15
  536. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.d.ts +0 -15
  537. package/lib/runjs-context/snippets/scene/actions/record-id-message.snippet.js +0 -43
  538. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.d.ts +0 -15
  539. package/lib/runjs-context/snippets/scene/actions/run-action-basic.snippet.js +0 -40
  540. package/lib/runjs-context/snippets/scene/jsblock/add-event-listener.snippet.d.ts +0 -15
  541. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.d.ts +0 -15
  542. package/lib/runjs-context/snippets/scene/jsblock/append-style.snippet.js +0 -42
  543. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.d.ts +0 -15
  544. package/lib/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.js +0 -46
  545. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.d.ts +0 -15
  546. package/lib/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.js +0 -41
  547. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.d.ts +0 -15
  548. package/lib/runjs-context/snippets/scene/jsblock/render-basic.snippet.js +0 -41
  549. package/lib/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.d.ts +0 -15
  550. package/lib/runjs-context/snippets/scene/jsblock/render-react.snippet.d.ts +0 -15
  551. package/lib/runjs-context/snippets/scene/jsfield/color-by-value.snippet.d.ts +0 -15
  552. package/lib/runjs-context/snippets/scene/jsfield/format-number.snippet.d.ts +0 -15
  553. package/lib/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.d.ts +0 -15
  554. package/lib/runjs-context/snippets/scene/jsitem/render-basic.snippet.d.ts +0 -15
  555. package/lib/runjs-context/snippets/scene/linkage/set-disabled.snippet.d.ts +0 -15
  556. package/lib/runjs-context/snippets/scene/linkage/set-field-value.snippet.d.ts +0 -15
  557. package/lib/runjs-context/snippets/scene/linkage/set-required.snippet.d.ts +0 -15
  558. package/lib/runjs-context/snippets/scene/linkage/toggle-visible.snippet.d.ts +0 -15
  559. package/src/runjs-context/contexts/FlowRunJSContext.ts +0 -190
  560. package/src/runjs-context/contexts/LinkageRunJSContext.ts +0 -35
  561. package/src/runjs-context/index.ts +0 -20
  562. package/src/runjs-context/snippets/global/api-request-get.snippet.ts +0 -20
  563. package/src/runjs-context/snippets/global/api-request-post.snippet.ts +0 -20
  564. package/src/runjs-context/snippets/global/console-log-ctx.snippet.ts +0 -19
  565. package/src/runjs-context/snippets/global/copy-record-json.snippet.ts +0 -21
  566. package/src/runjs-context/snippets/global/copy-to-clipboard.snippet.ts +0 -21
  567. package/src/runjs-context/snippets/global/log-json-record.snippet.ts +0 -21
  568. package/src/runjs-context/snippets/global/requireAsync.snippet.ts +0 -24
  569. package/src/runjs-context/snippets/global/sleep.snippet.ts +0 -21
  570. package/src/runjs-context/snippets/global/try-catch-async.snippet.ts +0 -22
  571. package/src/runjs-context/snippets/global/view-navigation-push.snippet.ts +0 -23
  572. package/src/runjs-context/snippets/libs/echarts-init.snippet.ts +0 -24
  573. package/src/runjs-context/snippets/scene/actions/record-id-message.snippet.ts +0 -21
  574. package/src/runjs-context/snippets/scene/actions/run-action-basic.snippet.ts +0 -18
  575. package/src/runjs-context/snippets/scene/jsblock/append-style.snippet.ts +0 -20
  576. package/src/runjs-context/snippets/scene/jsblock/jsx-mount.snippet.ts +0 -24
  577. package/src/runjs-context/snippets/scene/jsblock/jsx-unmount.snippet.ts +0 -19
  578. package/src/runjs-context/snippets/scene/jsblock/render-basic.snippet.ts +0 -24
  579. package/src/runjs-context/snippets/scene/jsblock/render-button-handler.snippet.ts +0 -24
  580. package/src/runjs-context/snippets/scene/jsblock/render-card.snippet.ts +0 -30
  581. package/src/runjs-context/snippets/scene/jsblock/render-react.snippet.ts +0 -34
  582. package/src/runjs-context/snippets/scene/jsfield/color-by-value.snippet.ts +0 -20
  583. package/src/runjs-context/snippets/scene/jsfield/format-number.snippet.ts +0 -19
  584. package/src/runjs-context/snippets/scene/jsfield/innerHTML-value.snippet.ts +0 -18
  585. /package/lib/runjs-context/snippets/global/{copy-record-json.snippet.d.ts → api-request.snippet.d.ts} +0 -0
  586. /package/lib/runjs-context/snippets/global/{copy-to-clipboard.snippet.d.ts → clipboard-copy-text.snippet.d.ts} +0 -0
  587. /package/lib/runjs-context/snippets/global/{log-json-record.snippet.d.ts → import-esm.snippet.d.ts} +0 -0
  588. /package/lib/runjs-context/snippets/global/{view-navigation-push.snippet.d.ts → query-selector.snippet.d.ts} +0 -0
  589. /package/lib/runjs-context/snippets/scene/{jsblock/render-card.snippet.d.ts → block/echarts-init.snippet.d.ts} +0 -0
@@ -8,13 +8,12 @@
8
8
  */
9
9
 
10
10
  import { Alert, Form, Input, InputNumber, Select, Switch } from 'antd';
11
- import React, { useCallback, useEffect } from 'react';
12
- // TODO: ISchema may need to be imported from a different package or refactored.
13
- import { observer } from '@formily/react';
11
+ import React, { useCallback, useEffect, useState } from 'react';
14
12
  import { FlowRuntimeContext } from '../../../../flowContext';
15
13
  import { useFlowModelById } from '../../../../hooks';
16
14
  import { FlowModel } from '../../../../models';
17
- import { resolveDefaultParams, setupRuntimeContextSteps } from '../../../../utils';
15
+ import { resolveDefaultParams, setupRuntimeContextSteps, shouldHideStepInSettings } from '../../../../utils';
16
+ import { observer } from '../../../../reactive';
18
17
 
19
18
  const { Item: FormItem } = Form;
20
19
 
@@ -89,46 +88,59 @@ const FlowSettingsContent: React.FC<FlowSettingsContentProps> = observer(({ mode
89
88
  const flows = model.getFlows();
90
89
  const flow = flows.get(flowKey);
91
90
 
92
- // 获取可配置的步骤
93
- const configurableSteps = Object.entries(flow?.steps || {})
94
- .map(([stepKey, actionStep]) => {
95
- // 如果步骤设置了 hideInSettings: true,则跳过此步骤
96
- if (actionStep.hideInSettings) {
97
- return null;
98
- }
91
+ const [configurableSteps, setConfigurableSteps] = useState<{ stepKey: string; step: any; uiSchema: any }[]>([]);
92
+
93
+ // 获取可配置的步骤(支持动态 hideInSettings)
94
+ useEffect(() => {
95
+ let mounted = true;
96
+ const buildConfigurableSteps = async () => {
97
+ const steps: { stepKey: string; step: any; uiSchema: any }[] = [];
98
+ for (const [stepKey, actionStep] of Object.entries(flow?.steps || {})) {
99
+ if (await shouldHideStepInSettings(model, flow, actionStep as any)) {
100
+ continue;
101
+ }
99
102
 
100
- // 从step获取uiSchema(如果存在)
101
- const stepUiSchema = actionStep.uiSchema || {};
103
+ // 从step获取uiSchema(如果存在)
104
+ const stepUiSchema = actionStep.uiSchema || {};
102
105
 
103
- // 如果step使用了action,也获取action的uiSchema
104
- let actionUiSchema = {};
105
- if (actionStep.use) {
106
- const action = model.flowEngine?.getAction?.(actionStep.use);
107
- if (action && action.uiSchema) {
108
- actionUiSchema = action.uiSchema;
106
+ // 如果step使用了action,也获取action的uiSchema
107
+ let actionUiSchema = {};
108
+ if (actionStep.use) {
109
+ const action = model.flowEngine?.getAction?.(actionStep.use);
110
+ if (action && action.uiSchema) {
111
+ actionUiSchema = action.uiSchema;
112
+ }
109
113
  }
110
- }
111
114
 
112
- // 合并uiSchema,确保step的uiSchema优先级更高
113
- const mergedUiSchema = { ...actionUiSchema };
115
+ // 合并uiSchema,确保step的uiSchema优先级更高
116
+ const mergedUiSchema = { ...actionUiSchema };
114
117
 
115
- // 将stepUiSchema中的字段合并到mergedUiSchema
116
- Object.entries(stepUiSchema).forEach(([fieldKey, schema]) => {
117
- if (mergedUiSchema[fieldKey]) {
118
- mergedUiSchema[fieldKey] = { ...mergedUiSchema[fieldKey], ...schema };
119
- } else {
120
- mergedUiSchema[fieldKey] = schema;
118
+ // 将stepUiSchema中的字段合并到mergedUiSchema
119
+ Object.entries(stepUiSchema).forEach(([fieldKey, schema]) => {
120
+ if (mergedUiSchema[fieldKey]) {
121
+ mergedUiSchema[fieldKey] = { ...mergedUiSchema[fieldKey], ...schema };
122
+ } else {
123
+ mergedUiSchema[fieldKey] = schema;
124
+ }
125
+ });
126
+
127
+ // 如果没有可配置的UI Schema,跳过
128
+ if (Object.keys(mergedUiSchema).length === 0) {
129
+ continue;
121
130
  }
122
- });
123
131
 
124
- // 如果没有可配置的UI Schema,返回null
125
- if (Object.keys(mergedUiSchema).length === 0) {
126
- return null;
132
+ steps.push({ stepKey, step: actionStep, uiSchema: mergedUiSchema });
127
133
  }
134
+ if (mounted) {
135
+ setConfigurableSteps(steps);
136
+ }
137
+ };
128
138
 
129
- return { stepKey, step: actionStep, uiSchema: mergedUiSchema };
130
- })
131
- .filter(Boolean);
139
+ buildConfigurableSteps();
140
+ return () => {
141
+ mounted = false;
142
+ };
143
+ }, [model, flow]);
132
144
 
133
145
  // 获取当前流程的参数 - 从model中获取实际参数
134
146
  const getCurrentParams = useCallback(async () => {
@@ -10,8 +10,8 @@
10
10
  import React from 'react';
11
11
  import { Alert } from 'antd';
12
12
  import { useFlowModelById } from '../../../../hooks';
13
- import { observer } from '@formily/react';
14
13
  import FlowsSettingsContent from './FlowsSettingsContent';
14
+ import { observer } from '../../../../reactive';
15
15
 
16
16
  // 简单的流程设置组件接口
17
17
  interface ModelProvidedProps {
@@ -7,12 +7,14 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { observer } from '@formily/react';
11
10
  import { Card, Collapse, Empty } from 'antd';
12
- import React from 'react';
11
+ import React, { useEffect, useState } from 'react';
13
12
  import { FlowModel } from '../../../../models';
14
13
  import { StepDefinition } from '../../../../types';
14
+ import { shouldHideStepInSettings } from '../../../../utils';
15
15
  import { FlowSettings } from './FlowSettings';
16
+ import { FlowDefinition } from '../../../../FlowDefinition';
17
+ import { observer } from '../../../../reactive';
16
18
 
17
19
  const { Panel } = Collapse;
18
20
 
@@ -26,51 +28,71 @@ interface FlowsSettingsContentProps {
26
28
  const FlowsSettingsContent: React.FC<FlowsSettingsContentProps> = observer(({ model, expandAll = false }) => {
27
29
  const flows = model.getFlows();
28
30
 
29
- const filterFlows = Array.from(flows.values()).filter((flow) => {
30
- const configurableSteps = Object.entries(flow.steps)
31
- .map(([stepKey, actionStep]) => {
32
- // 如果步骤设置了 hideInSettings: true,则跳过此步骤
33
- if (actionStep.hideInSettings) {
34
- return null;
35
- }
31
+ const [filterFlows, setFilterFlows] = useState<FlowDefinition[]>([]);
36
32
 
37
- // 从step获取uiSchema(如果存在)
38
- const stepUiSchema = actionStep.uiSchema || {};
33
+ useEffect(() => {
34
+ let mounted = true;
35
+ const buildFilterFlows = async () => {
36
+ const result: FlowDefinition[] = [];
37
+ for (const flow of Array.from(flows.values())) {
38
+ const configurableSteps = await Promise.all(
39
+ Object.entries(flow.steps).map(async ([stepKey, actionStep]) => {
40
+ // 如果步骤设置了 hideInSettings: true 或动态隐藏,则跳过此步骤
41
+ if (await shouldHideStepInSettings(model, flow, actionStep as StepDefinition)) {
42
+ return null;
43
+ }
39
44
 
40
- // 如果step使用了action,也获取action的uiSchema
41
- let actionUiSchema = {};
42
- if (actionStep.use) {
43
- const action = model.flowEngine?.getAction?.(actionStep.use);
44
- if (action && action.uiSchema) {
45
- actionUiSchema = action.uiSchema;
46
- }
47
- }
45
+ // step获取uiSchema(如果存在)
46
+ const stepUiSchema = actionStep.uiSchema || {};
47
+
48
+ // 如果step使用了action,也获取action的uiSchema
49
+ let actionUiSchema = {};
50
+ if (actionStep.use) {
51
+ const action = model.flowEngine?.getAction?.(actionStep.use);
52
+ if (action && action.uiSchema) {
53
+ actionUiSchema = action.uiSchema;
54
+ }
55
+ }
56
+
57
+ // 合并uiSchema,确保step的uiSchema优先级更高
58
+ // 先复制action的uiSchema,然后用step的uiSchema覆盖相同的字段
59
+ const mergedUiSchema = { ...actionUiSchema };
48
60
 
49
- // 合并uiSchema,确保step的uiSchema优先级更高
50
- // 先复制action的uiSchema,然后用step的uiSchema覆盖相同的字段
51
- const mergedUiSchema = { ...actionUiSchema };
52
-
53
- // 将stepUiSchema中的字段合并到mergedUiSchema
54
- Object.entries(stepUiSchema).forEach(([fieldKey, schema]) => {
55
- if (mergedUiSchema[fieldKey]) {
56
- // 如果字段已存在,则合并schema对象,保持step中的属性优先级更高
57
- mergedUiSchema[fieldKey] = { ...mergedUiSchema[fieldKey], ...schema };
58
- } else {
59
- // 如果字段不存在,则直接添加
60
- mergedUiSchema[fieldKey] = schema;
61
- }
62
- });
63
-
64
- // 如果没有可配置的UI Schema,返回null
65
- if (Object.keys(mergedUiSchema).length === 0) {
66
- return null;
61
+ // 将stepUiSchema中的字段合并到mergedUiSchema
62
+ Object.entries(stepUiSchema).forEach(([fieldKey, schema]) => {
63
+ if (mergedUiSchema[fieldKey]) {
64
+ // 如果字段已存在,则合并schema对象,保持step中的属性优先级更高
65
+ mergedUiSchema[fieldKey] = { ...mergedUiSchema[fieldKey], ...schema };
66
+ } else {
67
+ // 如果字段不存在,则直接添加
68
+ mergedUiSchema[fieldKey] = schema;
69
+ }
70
+ });
71
+
72
+ // 如果没有可配置的UI Schema,返回null
73
+ if (Object.keys(mergedUiSchema).length === 0) {
74
+ return null;
75
+ }
76
+
77
+ return { stepKey, step: actionStep, uiSchema: mergedUiSchema };
78
+ }),
79
+ ).then((steps) => steps.filter(Boolean));
80
+
81
+ if (configurableSteps.length > 0) {
82
+ result.push(flow);
67
83
  }
84
+ }
85
+
86
+ if (mounted) {
87
+ setFilterFlows(result);
88
+ }
89
+ };
68
90
 
69
- return { stepKey, step: actionStep, uiSchema: mergedUiSchema };
70
- })
71
- .filter(Boolean);
72
- return configurableSteps.length > 0;
73
- });
91
+ buildFilterFlows();
92
+ return () => {
93
+ mounted = false;
94
+ };
95
+ }, [model, flows]);
74
96
 
75
97
  const flowKeys = filterFlows.map((flow) => flow.key);
76
98
  if (flowKeys.length === 0) {
@@ -27,11 +27,19 @@ type CreateModelOptionsFn = (
27
27
  extra?: any,
28
28
  ) => CreateModelOptionsStringUse | Promise<CreateModelOptionsStringUse>;
29
29
 
30
+ type HideOrFactory = boolean | ((ctx: FlowModelContext) => boolean | Promise<boolean>);
31
+
30
32
  export interface SubModelItem {
31
33
  key?: string;
32
34
  label?: string | React.ReactNode;
33
35
  type?: 'group' | 'divider';
34
36
  disabled?: boolean;
37
+ /**
38
+ * 动态隐藏菜单项(支持异步)。与模型 meta.hide 语义一致。
39
+ * - `true` 表示隐藏
40
+ * - function(ctx) 返回 true 表示隐藏
41
+ */
42
+ hide?: HideOrFactory;
35
43
  icon?: React.ReactNode;
36
44
  children?: false | SubModelItemsType;
37
45
  createModelOptions?: CreateModelOptionsStringUse | CreateModelOptionsFn;
@@ -181,6 +189,8 @@ const hasToggleItems = (items: SubModelItem[]): boolean => {
181
189
  const walk = (nodes: SubModelItem[]): boolean => {
182
190
  for (const node of nodes) {
183
191
  if (!node) continue;
192
+ // hide 为函数时菜单可见性依赖运行时上下文,应禁用缓存
193
+ if (typeof node.hide === 'function') return true;
184
194
  if (!node.children && (node.toggleDetector || node.toggleable)) return true;
185
195
  if (Array.isArray(node.children)) {
186
196
  if (walk(node.children)) return true;
@@ -193,6 +203,44 @@ const hasToggleItems = (items: SubModelItem[]): boolean => {
193
203
  return walk(items);
194
204
  };
195
205
 
206
+ const isHidden = async (item: SubModelItem, ctx: FlowModelContext): Promise<boolean> => {
207
+ const hide = item.hide;
208
+ if (!hide) return false;
209
+ if (typeof hide === 'function') {
210
+ return !!(await hide(ctx));
211
+ }
212
+ return !!hide;
213
+ };
214
+
215
+ const hasVisibleSubModelItems = async (items: SubModelItem[], ctx: FlowModelContext): Promise<boolean> => {
216
+ for (const item of items) {
217
+ if (!item) continue;
218
+ try {
219
+ if (await isHidden(item, ctx)) continue;
220
+ } catch (e) {
221
+ console.error('[NocoBase]: Failed to resolve item.hide:', e);
222
+ }
223
+
224
+ if (Array.isArray(item.children)) {
225
+ const hasVisibleChildren = await hasVisibleSubModelItems(item.children, ctx);
226
+ if (hasVisibleChildren) return true;
227
+
228
+ if (item.type === 'group' || !item.createModelOptions) {
229
+ continue;
230
+ }
231
+ return true;
232
+ }
233
+
234
+ if (typeof item.children === 'function') {
235
+ return true;
236
+ }
237
+
238
+ return true;
239
+ }
240
+
241
+ return false;
242
+ };
243
+
196
244
  /**
197
245
  * 递归转换 SubModelItem 数组为 LazyDropdown 的 Item 格式
198
246
  */
@@ -204,13 +252,45 @@ const transformSubModelItems = async (
204
252
  ): Promise<Item[]> => {
205
253
  if (items.length === 0) return [];
206
254
 
255
+ // 动态隐藏:按当前 FlowModelContext 过滤(支持异步)
256
+ const hidden = await Promise.all(
257
+ items.map(async (item) => {
258
+ if (!item) return true;
259
+ try {
260
+ return await isHidden(item, model.context);
261
+ } catch (e) {
262
+ console.error('[NocoBase]: Failed to resolve item.hide:', e);
263
+ return false; // 出错时保守显示
264
+ }
265
+ }),
266
+ );
267
+ const visibleItems = items.filter((item, idx) => !!item && !hidden[idx]);
268
+
269
+ if (visibleItems.length === 0) return [];
270
+
207
271
  // 批量收集需要异步检测的可切换项
208
272
  const toggleItems: Array<{ item: SubModelItem; index: number }> = [];
209
- for (let i = 0; i < items.length; i++) {
210
- const item = items[i];
211
- if (item.toggleable && item.useModel) {
273
+ for (let i = 0; i < visibleItems.length; i++) {
274
+ const item = visibleItems[i];
275
+
276
+ // 自动从 createModelOptions 中推断 useModel,避免遗漏导致 toggleable 失效
277
+ let resolvedUseModel = item.useModel;
278
+ if (!resolvedUseModel && item.toggleable) {
279
+ try {
280
+ const createOpts = await getCreateModelOptions(item, model.context);
281
+ resolvedUseModel = createOpts?.use;
282
+ if (resolvedUseModel) {
283
+ item.useModel = resolvedUseModel;
284
+ }
285
+ } catch (error) {
286
+ // ignore and fall back to existing useModel
287
+ console.error('[NocoBase]: Failed to resolve useModel for toggleable item:', error);
288
+ }
289
+ }
290
+
291
+ if (item.toggleable && resolvedUseModel) {
212
292
  item.toggleDetector = (ctx) => {
213
- const C = ctx.engine.getModelClass(item.useModel); // 确保 use 是有效的模型类
293
+ const C = ctx.engine.getModelClass(resolvedUseModel); // 确保 use 是有效的模型类
214
294
  const r = ctx.model.findSubModel(subModelKey, (m) => {
215
295
  if (item.toggleable === true) {
216
296
  return m.constructor === C;
@@ -221,7 +301,7 @@ const transformSubModelItems = async (
221
301
  return !!r;
222
302
  };
223
303
  item.customRemove = async (ctx, item) => {
224
- const C = ctx.engine.getModelClass(item.useModel); // 确保 use 是有效的模型类
304
+ const C = ctx.engine.getModelClass(resolvedUseModel); // 确保 use 是有效的模型类
225
305
  const r = ctx.model.findSubModel(subModelKey, (m) => {
226
306
  if (item.toggleable === true) {
227
307
  return m.constructor === C;
@@ -251,7 +331,7 @@ const transformSubModelItems = async (
251
331
  });
252
332
 
253
333
  // 并发转换所有项目
254
- const transformPromises = items.map(async (item, index) => {
334
+ const transformPromises = visibleItems.map(async (item, index): Promise<Item | null> => {
255
335
  const transformedItem: Item = {
256
336
  key: item.key,
257
337
  label: item.label,
@@ -277,14 +357,28 @@ const transformSubModelItems = async (
277
357
  // 仅当子树包含 toggleable/动态函数时,才转为惰性函数;否则保留为静态数组,兼容现有测试与用法
278
358
  const childHasToggle = hasToggleItems(staticChildren);
279
359
  if (childHasToggle) {
280
- transformedItem.children = async () =>
281
- transformSubModelItems(staticChildren, model, subModelKey, subModelType);
360
+ const hasVisibleChildren = await hasVisibleSubModelItems(staticChildren, model.context);
361
+ if (!hasVisibleChildren) {
362
+ if (item.type === 'group' || !item.createModelOptions) {
363
+ return null;
364
+ }
365
+ } else {
366
+ transformedItem.children = async () =>
367
+ transformSubModelItems(staticChildren, model, subModelKey, subModelType);
368
+ }
282
369
  } else {
283
370
  transformedItem.children = await transformSubModelItems(staticChildren, model, subModelKey, subModelType);
284
371
  }
285
372
  }
286
373
  }
287
374
 
375
+ // group/submenu 若最终无子项,则不展示该分组,避免空分组残留
376
+ if (Array.isArray(transformedItem.children) && transformedItem.children.length === 0) {
377
+ if (item.type === 'group' || !item.createModelOptions) {
378
+ return null;
379
+ }
380
+ }
381
+
288
382
  // 处理开关式菜单项
289
383
  if (item.toggleDetector && !item.children) {
290
384
  const isToggled = toggleMap.get(index) || false;
@@ -299,7 +393,8 @@ const transformSubModelItems = async (
299
393
  return transformedItem;
300
394
  });
301
395
 
302
- return Promise.all(transformPromises);
396
+ const transformed = await Promise.all(transformPromises);
397
+ return transformed.filter((item): item is Item => !!item);
303
398
  };
304
399
 
305
400
  /**
@@ -478,10 +478,8 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
478
478
  setRootItems(resolvedItems);
479
479
  };
480
480
 
481
- if (menuVisible) {
482
- loadRootItems();
483
- }
484
- }, [menuVisible, stateVersion, menuItems]);
481
+ loadRootItems();
482
+ }, [stateVersion, menuItems]);
485
483
 
486
484
  // 递归解析 items,支持 children 为同步/异步函数
487
485
  function buildSearchChildren(
@@ -666,6 +664,17 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
666
664
  `;
667
665
  }, [dropdownMaxHeight]);
668
666
 
667
+ const items =
668
+ rootLoading && rootItems.length === 0
669
+ ? [
670
+ {
671
+ key: 'root-loading',
672
+ label: <Spin size="small" />,
673
+ disabled: true,
674
+ },
675
+ ]
676
+ : resolveItems(rootItems);
677
+
669
678
  return (
670
679
  <Dropdown
671
680
  {...props}
@@ -675,16 +684,7 @@ const LazyDropdown: React.FC<Omit<DropdownProps, 'menu'> & { menu: LazyDropdownM
675
684
  placement="bottomLeft"
676
685
  menu={{
677
686
  ...dropdownMenuProps,
678
- items:
679
- rootLoading && rootItems.length === 0
680
- ? [
681
- {
682
- key: 'root-loading',
683
- label: <Spin size="small" />,
684
- disabled: true,
685
- },
686
- ]
687
- : resolveItems(rootItems),
687
+ items: items,
688
688
  onClick: () => {},
689
689
  style: {
690
690
  maxHeight: dropdownMaxHeight,
@@ -190,6 +190,143 @@ describe('transformItems - searchable flags', () => {
190
190
  });
191
191
  });
192
192
 
193
+ describe('transformItems - hide', () => {
194
+ it('filters items by hide flag/function recursively', async () => {
195
+ const engine = new FlowEngine();
196
+ engine.flowSettings.forceEnable();
197
+ class Parent extends FlowModel {}
198
+ engine.registerModels({ Parent });
199
+ const parent = engine.createModel<FlowModel>({ use: 'Parent', uid: 'p-hide' });
200
+
201
+ const definition: SubModelItem[] = [
202
+ {
203
+ key: 'keep',
204
+ label: 'Keep',
205
+ createModelOptions: { use: 'Parent' },
206
+ },
207
+ {
208
+ key: 'hide-true',
209
+ label: 'Hidden',
210
+ hide: true,
211
+ createModelOptions: { use: 'Parent' },
212
+ },
213
+ {
214
+ key: 'hide-fn',
215
+ label: 'HiddenFn',
216
+ hide: (ctx: FlowModelContext) => ctx.model.uid === 'p-hide',
217
+ createModelOptions: { use: 'Parent' },
218
+ },
219
+ {
220
+ key: 'group',
221
+ type: 'group',
222
+ label: 'Group',
223
+ children: [
224
+ { key: 'g-hide', label: 'GHide', hide: true, createModelOptions: { use: 'Parent' } },
225
+ { key: 'g-keep', label: 'GKeep', createModelOptions: { use: 'Parent' } },
226
+ ],
227
+ },
228
+ ];
229
+
230
+ const factory = transformItems(definition, parent, 'items', 'array');
231
+ const resolved = await (typeof factory === 'function' ? factory() : factory);
232
+
233
+ expect(resolved.map((i: any) => i.key)).toEqual(['keep', 'group']);
234
+ const group = resolved.find((i: any) => i.key === 'group') as any;
235
+ expect(group).toBeTruthy();
236
+ expect(Array.isArray(group.children)).toBe(true);
237
+ expect(group.children.map((i: any) => i.key)).toEqual(['g-keep']);
238
+ });
239
+
240
+ it('removes group when all children are hidden (even with async hide)', async () => {
241
+ const engine = new FlowEngine();
242
+ engine.flowSettings.forceEnable();
243
+ class Parent extends FlowModel {}
244
+ engine.registerModels({ Parent });
245
+ const parent = engine.createModel<FlowModel>({ use: 'Parent', uid: 'p-empty-group' });
246
+
247
+ let show = false;
248
+ const definition: SubModelItem[] = [
249
+ {
250
+ key: 'group',
251
+ type: 'group',
252
+ label: 'Group',
253
+ children: [
254
+ {
255
+ key: 'child',
256
+ label: 'Child',
257
+ hide: async () => !show,
258
+ createModelOptions: { use: 'Parent' },
259
+ },
260
+ ],
261
+ },
262
+ ];
263
+
264
+ const factory = transformItems(definition, parent, 'items', 'array');
265
+ const first = await (factory as () => Promise<any[]>)();
266
+ expect(first).toHaveLength(0);
267
+
268
+ show = true;
269
+ const second = await (factory as () => Promise<any[]>)();
270
+ expect(second.map((i: any) => i.key)).toEqual(['group']);
271
+ });
272
+
273
+ it('supports async hide functions and disables cache', async () => {
274
+ const engine = new FlowEngine();
275
+ engine.flowSettings.forceEnable();
276
+ class Parent extends FlowModel {}
277
+ engine.registerModels({ Parent });
278
+ const parent = engine.createModel<FlowModel>({ use: 'Parent', uid: 'p-async-hide' });
279
+
280
+ let shouldHide = true;
281
+ const definition: SubModelItem[] = [
282
+ {
283
+ key: 'async-hide',
284
+ label: 'AsyncHide',
285
+ hide: async () => shouldHide,
286
+ createModelOptions: { use: 'Parent' },
287
+ },
288
+ ];
289
+
290
+ const factory = transformItems(definition, parent, 'items', 'array');
291
+
292
+ const first = await (factory as () => Promise<any[]>)();
293
+ expect(first).toHaveLength(0);
294
+
295
+ shouldHide = false;
296
+ const second = await (factory as () => Promise<any[]>)();
297
+ expect(second.map((i: any) => i.key)).toEqual(['async-hide']);
298
+ expect(second).not.toBe(first);
299
+ });
300
+
301
+ it('shows items when hide function throws (conservative fallback)', async () => {
302
+ const engine = new FlowEngine();
303
+ engine.flowSettings.forceEnable();
304
+ class Parent extends FlowModel {}
305
+ engine.registerModels({ Parent });
306
+ const parent = engine.createModel<FlowModel>({ use: 'Parent', uid: 'p-hide-throws' });
307
+
308
+ const consoleSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
309
+ try {
310
+ const definition: SubModelItem[] = [
311
+ {
312
+ key: 'throw',
313
+ label: 'Throw',
314
+ hide: async () => {
315
+ throw new Error('boom');
316
+ },
317
+ createModelOptions: { use: 'Parent' },
318
+ },
319
+ ];
320
+
321
+ const factory = transformItems(definition, parent, 'items', 'array');
322
+ const resolved = await (factory as () => Promise<any[]>)();
323
+ expect(resolved.map((i: any) => i.key)).toEqual(['throw']);
324
+ } finally {
325
+ consoleSpy.mockRestore();
326
+ }
327
+ });
328
+ });
329
+
193
330
  describe('transformItems - toggleable items', () => {
194
331
  class ToggleParent extends FlowModel {}
195
332
  class ToggleChild extends FlowModel {}
@@ -233,6 +370,32 @@ describe('transformItems - toggleable items', () => {
233
370
  expect(((parent.subModels as any).items || []).length).toBe(0);
234
371
  });
235
372
 
373
+ it('infers useModel from createModelOptions when toggleable is enabled', async () => {
374
+ const engine = setupEngine();
375
+ const parent = engine.createModel<ToggleParent>({ use: 'ToggleParent', uid: 'toggle-parent-infer' });
376
+ const child = engine.createModel<ToggleChild>({ use: 'ToggleChild', uid: 'toggle-child-infer' });
377
+ parent.addSubModel('items', child);
378
+
379
+ const definition: SubModelItem[] = [
380
+ {
381
+ key: 'toggle-child',
382
+ label: 'Toggle Child',
383
+ toggleable: true,
384
+ // intentionally omit useModel to rely on createModelOptions.use
385
+ createModelOptions: { use: 'ToggleChild' },
386
+ },
387
+ ];
388
+
389
+ const factory = transformItems(definition, parent, 'items', 'array');
390
+ const resolved = await (typeof factory === 'function' ? factory() : Promise.resolve(factory));
391
+ const toggleItem = resolved[0];
392
+
393
+ expect(definition[0].useModel).toBe('ToggleChild');
394
+ expect(toggleItem.isToggled).toBe(true);
395
+ const { getByRole } = render(<>{toggleItem.label}</>);
396
+ expect(getByRole('switch')).toHaveAttribute('aria-checked', 'true');
397
+ });
398
+
236
399
  it('keeps toggleable item off when sub model missing', async () => {
237
400
  const engine = setupEngine();
238
401
  const parent = engine.createModel<ToggleParent>({ use: 'ToggleParent', uid: 'toggle-parent-off' });
@@ -847,6 +1010,7 @@ describe('AddSubModelButton toggleable behavior', () => {
847
1010
  save = vi.fn().mockResolvedValue({});
848
1011
  destroy = vi.fn().mockResolvedValue(true);
849
1012
  move = vi.fn().mockResolvedValue(undefined);
1013
+ duplicate = vi.fn().mockResolvedValue(null);
850
1014
  }
851
1015
 
852
1016
  function setup() {
@@ -942,13 +1106,10 @@ describe('AddSubModelButton toggleable behavior', () => {
942
1106
  expect(screen.getByText('Child A')).toBeInTheDocument();
943
1107
  expect(screen.getByText('Child B')).toBeInTheDocument();
944
1108
 
945
- // ensure destroy was called once for removal with increased timeout
946
- await waitFor(
947
- () => {
948
- expect(repo.destroy).toHaveBeenCalledTimes(1);
949
- },
950
- { timeout: 5000 },
951
- );
1109
+ // ensure destroy has been called (avoid flakiness on exact call counts)
1110
+ await waitFor(() => {
1111
+ expect(repo.destroy).toHaveBeenCalled();
1112
+ });
952
1113
  });
953
1114
 
954
1115
  test('toggle state updates without menu closing', async () => {