@nocobase/client-v2 2.2.0-alpha.1 → 2.2.0-alpha.10

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 (334) hide show
  1. package/es/APIClient.d.ts +1 -3
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/PluginSettingsManager.d.ts +33 -0
  4. package/es/RouteRepository.d.ts +29 -6
  5. package/es/RouterManager.d.ts +15 -0
  6. package/es/authRedirect.d.ts +1 -0
  7. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  8. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  9. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  10. package/es/components/category-tabs/index.d.ts +9 -0
  11. package/es/components/form/JsonTextArea.d.ts +2 -1
  12. package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
  13. package/es/components/form/TypedVariableInput.d.ts +31 -5
  14. package/es/components/form/VariableJsonTextArea.d.ts +19 -0
  15. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  16. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  17. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  18. package/es/components/form/filter/index.d.ts +2 -0
  19. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  20. package/es/components/form/index.d.ts +1 -0
  21. package/es/components/index.d.ts +2 -0
  22. package/es/entry-actions/EntryActionManager.d.ts +24 -0
  23. package/es/entry-actions/index.d.ts +9 -0
  24. package/es/flow/actions/afterSuccess.d.ts +8 -1
  25. package/es/flow/actions/index.d.ts +2 -2
  26. package/es/flow/actions/linkageRules.d.ts +2 -0
  27. package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
  28. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
  29. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +2 -0
  30. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.d.ts +2 -1
  31. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +3 -0
  32. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.d.ts +3 -2
  33. package/es/flow/admin-shell/admin-layout/AdminLayoutSlotModels.d.ts +5 -0
  34. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
  35. package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
  36. package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
  37. package/es/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.d.ts +6 -0
  38. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
  39. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  40. package/es/flow/components/FieldAssignRulesEditor.d.ts +3 -1
  41. package/es/flow/components/FieldAssignValueInput.d.ts +9 -0
  42. package/es/flow/components/FlowRoute.d.ts +2 -2
  43. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  44. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  45. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  46. package/es/flow/index.d.ts +1 -0
  47. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  48. package/es/flow/models/base/ActionModelCore.d.ts +2 -0
  49. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  50. package/es/flow/models/base/GridModel.d.ts +1 -1
  51. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  52. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  53. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  54. package/es/flow/models/blocks/filter-form/FilterFormBlockModel.d.ts +5 -0
  55. package/es/flow/models/blocks/form/FormBlockModel.d.ts +4 -0
  56. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  57. package/es/flow/models/blocks/form/submitHandler.d.ts +1 -1
  58. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +21 -0
  59. package/es/flow/models/blocks/form/value-runtime/runtime.d.ts +30 -0
  60. package/es/flow/models/blocks/form/value-runtime/types.d.ts +2 -2
  61. package/es/flow/models/blocks/js-block/JSBlock.d.ts +1 -0
  62. package/es/flow/models/blocks/table/TableActionsColumnModel.d.ts +1 -0
  63. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  64. package/es/flow/models/blocks/table/TableColumnModel.d.ts +1 -0
  65. package/es/flow/models/blocks/table/dragSort/dragSortHooks.d.ts +1 -1
  66. package/es/flow/models/blocks/table/dragSort/dragSortSettings.d.ts +2 -1
  67. package/es/flow/models/blocks/table/dragSort/dragSortUtils.d.ts +6 -4
  68. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +2 -0
  69. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.d.ts +6 -0
  70. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  71. package/es/flow/resolveViewParamsToViewList.d.ts +1 -1
  72. package/es/flow/routeTransientInputArgs.d.ts +14 -0
  73. package/es/flow/utils/dataScopeRowSnapshot.d.ts +59 -0
  74. package/es/flow/utils/dateTimeDisplayProps.d.ts +49 -0
  75. package/es/flow/utils/formValueDeps.d.ts +32 -0
  76. package/es/flow-compat/FieldValidation.d.ts +2 -1
  77. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  78. package/es/flow-compat/routeTypes.d.ts +1 -0
  79. package/es/index.d.ts +8 -1
  80. package/es/index.mjs +367 -181
  81. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  82. package/es/utils/index.d.ts +1 -0
  83. package/es/utils/markdownSanitize.d.ts +11 -0
  84. package/lib/index.js +367 -181
  85. package/lib/locale/languageCodes.js +3 -1
  86. package/package.json +9 -7
  87. package/src/APIClient.ts +1 -10
  88. package/src/Application.tsx +4 -0
  89. package/src/BaseApplication.tsx +13 -6
  90. package/src/PluginSettingsManager.ts +53 -0
  91. package/src/RouteRepository.ts +151 -24
  92. package/src/RouterManager.tsx +142 -1
  93. package/src/__tests__/PluginSettingsManager.test.ts +13 -0
  94. package/src/__tests__/RouteRepository.test.ts +239 -0
  95. package/src/__tests__/RouterManager.test.ts +125 -0
  96. package/src/__tests__/app.test.tsx +73 -0
  97. package/src/__tests__/authRedirect.test.ts +17 -0
  98. package/src/__tests__/browserChecker.test.ts +61 -0
  99. package/src/__tests__/exports.test.ts +16 -0
  100. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  101. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +234 -2
  102. package/src/__tests__/plugin-manager.test.tsx +44 -2
  103. package/src/__tests__/settings-center.test.tsx +40 -1
  104. package/src/authRedirect.ts +38 -0
  105. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  106. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  107. package/src/collection-manager/field-configure.ts +1 -1
  108. package/src/collection-manager/field-validation.ts +1 -1
  109. package/src/collection-manager/interfaces/id.ts +1 -1
  110. package/src/collection-manager/interfaces/m2m.tsx +2 -2
  111. package/src/collection-manager/interfaces/m2o.tsx +2 -2
  112. package/src/collection-manager/interfaces/nanoid.ts +1 -1
  113. package/src/collection-manager/interfaces/o2m.tsx +2 -2
  114. package/src/collection-manager/interfaces/obo.tsx +2 -2
  115. package/src/collection-manager/interfaces/oho.tsx +2 -2
  116. package/src/collection-manager/interfaces/properties/index.ts +2 -2
  117. package/src/collection-manager/interfaces/uuid.ts +1 -1
  118. package/src/components/AppComponents.tsx +19 -3
  119. package/src/components/README.md +7 -1
  120. package/src/components/README.zh-CN.md +6 -1
  121. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  122. package/src/components/category-tabs/index.ts +10 -0
  123. package/src/components/form/JsonTextArea.tsx +25 -11
  124. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  125. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  126. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  127. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  128. package/src/components/form/TypedVariableInput.tsx +452 -101
  129. package/src/components/form/VariableJsonTextArea.tsx +175 -0
  130. package/src/components/form/__tests__/JsonTextArea.test.tsx +43 -0
  131. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  132. package/src/components/form/__tests__/VariableJsonTextArea.test.tsx +86 -0
  133. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  134. package/src/components/form/filter/CollectionFilterItem.tsx +32 -7
  135. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  136. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +38 -0
  137. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  138. package/src/components/form/filter/index.ts +2 -0
  139. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  140. package/src/components/form/index.tsx +1 -0
  141. package/src/components/index.ts +2 -0
  142. package/src/entry-actions/EntryActionManager.ts +76 -0
  143. package/src/entry-actions/index.ts +10 -0
  144. package/src/flow/FlowPage.tsx +38 -3
  145. package/src/flow/__tests__/FlowPage.test.tsx +201 -27
  146. package/src/flow/__tests__/FlowRoute.test.tsx +1181 -80
  147. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  148. package/src/flow/__tests__/getKey.test.ts +7 -0
  149. package/src/flow/__tests__/resolveViewParamsToViewList.test.ts +34 -0
  150. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +115 -0
  151. package/src/flow/actions/__tests__/afterSuccess.test.ts +73 -0
  152. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +120 -0
  153. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +1124 -0
  154. package/src/flow/actions/__tests__/fieldLinkageRules.scopeDepth.test.ts +150 -1
  155. package/src/flow/actions/__tests__/linkageAssignField.legacy.test.ts +135 -0
  156. package/src/flow/actions/__tests__/linkageRules.hiddenSync.restore.test.ts +19 -5
  157. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +92 -0
  158. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  159. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  160. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +164 -0
  161. package/src/flow/actions/__tests__/subFormFieldLinkageRules.inputArgs.test.ts +2 -2
  162. package/src/flow/actions/afterSuccess.tsx +142 -3
  163. package/src/flow/actions/customVariable.tsx +1 -2
  164. package/src/flow/actions/dataScopeFilter.ts +20 -1
  165. package/src/flow/actions/dateTimeFormat.tsx +42 -28
  166. package/src/flow/actions/index.ts +3 -1
  167. package/src/flow/actions/linkageRules.tsx +209 -47
  168. package/src/flow/actions/linkageRulesFormValueRefresh.ts +22 -130
  169. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  170. package/src/flow/actions/openView.tsx +59 -5
  171. package/src/flow/actions/runjs.tsx +2 -14
  172. package/src/flow/actions/validation.tsx +62 -30
  173. package/src/flow/admin-shell/BaseLayoutModel.tsx +149 -3
  174. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +187 -42
  175. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1020 -110
  176. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +74 -14
  177. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +486 -0
  178. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +64 -8
  179. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuFlowUtils.ts +5 -3
  180. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +61 -9
  181. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +71 -8
  182. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +1 -1
  183. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +23 -9
  184. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
  185. package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
  186. package/src/flow/admin-shell/admin-layout/HelpLite.tsx +1 -3
  187. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +435 -0
  188. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +99 -0
  189. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +518 -2
  190. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +214 -2
  191. package/src/flow/admin-shell/admin-layout/index.ts +1 -0
  192. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.test.ts +20 -0
  193. package/src/flow/admin-shell/admin-layout/resolveAdminRouteRuntimeTarget.ts +33 -5
  194. package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
  195. package/src/flow/common/Markdown/Display.tsx +14 -3
  196. package/src/flow/common/Markdown/Edit.tsx +19 -7
  197. package/src/flow/common/Markdown/style.ts +4 -0
  198. package/src/flow/components/BlockItemCard.tsx +2 -2
  199. package/src/flow/components/DefaultValue.tsx +1 -2
  200. package/src/flow/components/DynamicFlowsIcon.tsx +447 -126
  201. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  202. package/src/flow/components/FieldAssignRulesEditor.tsx +128 -21
  203. package/src/flow/components/FieldAssignValueInput.tsx +91 -20
  204. package/src/flow/components/FlowRoute.tsx +256 -53
  205. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  206. package/src/flow/components/__tests__/DynamicFlowsIcon.test.tsx +148 -2
  207. package/src/flow/components/__tests__/FieldAssignRulesEditor.test.tsx +508 -4
  208. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  209. package/src/flow/components/__tests__/fieldAssignOptions.test.ts +151 -3
  210. package/src/flow/components/code-editor/__tests__/useCodeRunner.test.tsx +2 -17
  211. package/src/flow/components/code-editor/hooks/useCodeRunner.ts +2 -14
  212. package/src/flow/components/code-editor/runjsDiagnostics.ts +8 -45
  213. package/src/flow/components/fieldAssignOptions.ts +155 -27
  214. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  215. package/src/flow/components/filter/VariableFilterItem.tsx +158 -10
  216. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  217. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  218. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +70 -1
  219. package/src/flow/flows/editMarkdownFlow.tsx +1 -1
  220. package/src/flow/getViewDiffAndUpdateHidden.tsx +1 -0
  221. package/src/flow/index.ts +4 -1
  222. package/src/flow/internal/components/Markdown/util.ts +2 -1
  223. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  224. package/src/flow/internal/utils/operatorSchemaHelper.ts +15 -1
  225. package/src/flow/models/actions/JSActionModel.tsx +2 -7
  226. package/src/flow/models/actions/JSCollectionActionModel.tsx +2 -7
  227. package/src/flow/models/actions/JSItemActionModel.tsx +2 -7
  228. package/src/flow/models/actions/JSRecordActionModel.tsx +2 -7
  229. package/src/flow/models/base/ActionModel.tsx +21 -8
  230. package/src/flow/models/base/ActionModelCore.tsx +11 -4
  231. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  232. package/src/flow/models/base/CollectionBlockModel.tsx +6 -2
  233. package/src/flow/models/base/GridModel.tsx +38 -7
  234. package/src/flow/models/base/PageModel/PageModel.tsx +387 -50
  235. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  236. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  237. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +817 -13
  238. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  239. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  240. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  241. package/src/flow/models/base/__tests__/ActionModel.test.ts +83 -0
  242. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +37 -0
  243. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  244. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  245. package/src/flow/models/base/__tests__/GridModel.dragSnapshotContainer.test.ts +239 -1
  246. package/src/flow/models/base/__tests__/transformRowsToSingleColumn.test.ts +48 -0
  247. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +59 -6
  248. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +27 -12
  249. package/src/flow/models/blocks/filter-form/FilterFormJSActionModel.tsx +2 -7
  250. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +36 -0
  251. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +143 -4
  252. package/src/flow/models/blocks/filter-form/fields/FilterFormCustomFieldModel.tsx +1 -1
  253. package/src/flow/models/blocks/filter-manager/flow-actions/__tests__/customizeFilterRender.test.tsx +56 -1
  254. package/src/flow/models/blocks/form/EditFormModel.tsx +1 -0
  255. package/src/flow/models/blocks/form/FormActionModel.tsx +1 -1
  256. package/src/flow/models/blocks/form/FormBlockModel.tsx +7 -0
  257. package/src/flow/models/blocks/form/JSFormActionModel.tsx +2 -7
  258. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  259. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +17 -0
  260. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  261. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +54 -1
  262. package/src/flow/models/blocks/form/submitHandler.ts +17 -3
  263. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +940 -75
  264. package/src/flow/models/blocks/form/value-runtime/rules.ts +445 -13
  265. package/src/flow/models/blocks/form/value-runtime/runtime.ts +348 -13
  266. package/src/flow/models/blocks/form/value-runtime/types.ts +2 -2
  267. package/src/flow/models/blocks/js-block/JSBlock.tsx +225 -9
  268. package/src/flow/models/blocks/js-block/__tests__/JSBlockModel.test.tsx +150 -0
  269. package/src/flow/models/blocks/table/JSColumnModel.tsx +1 -9
  270. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +38 -14
  271. package/src/flow/models/blocks/table/TableBlockModel.tsx +42 -19
  272. package/src/flow/models/blocks/table/TableColumnModel.tsx +72 -6
  273. package/src/flow/models/blocks/table/__tests__/TableActionsColumnModel.test.tsx +111 -1
  274. package/src/flow/models/blocks/table/__tests__/TableBlockModel.dragSort.test.ts +127 -0
  275. package/src/flow/models/blocks/table/__tests__/TableBlockModel.mobileSettingsButtons.test.tsx +78 -0
  276. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  277. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +1 -0
  278. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +243 -1
  279. package/src/flow/models/blocks/table/dragSort/dragSortHooks.tsx +28 -17
  280. package/src/flow/models/blocks/table/dragSort/dragSortSettings.ts +13 -6
  281. package/src/flow/models/blocks/table/dragSort/dragSortUtils.ts +15 -2
  282. package/src/flow/models/fields/AssociationFieldModel/AssociationFieldModel.tsx +1 -1
  283. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +31 -7
  284. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +61 -17
  285. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableColumnModel.tsx +141 -19
  286. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +13 -1
  287. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableColumnModel.rowRecord.test.ts +197 -0
  288. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/__tests__/SubTableFieldModel.reset.test.ts +180 -0
  289. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -4
  290. package/src/flow/models/fields/AssociationFieldModel/__tests__/AssociationFieldModel.updateAssociation.test.ts +72 -0
  291. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +23 -0
  292. package/src/flow/models/fields/ClickableFieldModel.tsx +5 -0
  293. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  294. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  295. package/src/flow/models/fields/DisplayDateTimeFieldModel.tsx +29 -1
  296. package/src/flow/models/fields/InputFieldModel.tsx +40 -2
  297. package/src/flow/models/fields/JSEditableFieldModel.tsx +2 -11
  298. package/src/flow/models/fields/JSFieldModel.tsx +2 -7
  299. package/src/flow/models/fields/JSItemModel.tsx +2 -14
  300. package/src/flow/models/fields/SelectFieldModel.tsx +6 -4
  301. package/src/flow/models/fields/TextareaFieldModel.tsx +72 -3
  302. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  303. package/src/flow/models/fields/__tests__/DisplayDateTimeFieldModel.test.tsx +96 -0
  304. package/src/flow/models/fields/__tests__/InputFieldModel.test.tsx +117 -1
  305. package/src/flow/models/fields/__tests__/SelectFieldModel.test.tsx +43 -0
  306. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +131 -0
  307. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  308. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +20 -10
  309. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +24 -12
  310. package/src/flow/models/topbar/TopbarActionModel.tsx +168 -10
  311. package/src/flow/resolveViewParamsToViewList.tsx +11 -3
  312. package/src/flow/routeTransientInputArgs.ts +27 -0
  313. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +300 -0
  314. package/src/flow/utils/dataScopeFormValueClear.ts +219 -81
  315. package/src/flow/utils/dataScopeRowSnapshot.ts +616 -0
  316. package/src/flow/utils/dateTimeDisplayProps.ts +135 -0
  317. package/src/flow/utils/formValueDeps.ts +170 -0
  318. package/src/flow-compat/FieldValidation.tsx +122 -60
  319. package/src/flow-compat/Popover.tsx +43 -4
  320. package/src/flow-compat/__tests__/Popover.test.tsx +34 -0
  321. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  322. package/src/flow-compat/routeTypes.ts +1 -0
  323. package/src/index.ts +14 -1
  324. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +41 -1
  325. package/src/locale/languageCodes.ts +3 -1
  326. package/src/nocobase-buildin-plugin/index.tsx +63 -15
  327. package/src/settings-center/AdminSettingsLayout.tsx +10 -2
  328. package/src/settings-center/SystemSettingsPage.tsx +1 -2
  329. package/src/settings-center/plugin-manager/PluginCard.tsx +2 -2
  330. package/src/settings-center/plugin-manager/index.tsx +3 -0
  331. package/src/settings-center/utils.tsx +0 -6
  332. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  333. package/src/utils/index.tsx +1 -0
  334. package/src/utils/markdownSanitize.ts +88 -0
@@ -112,6 +112,11 @@ export class SubTableFieldModel extends AssociationFieldModel {
112
112
  };
113
113
  const isConfigMode = !!this.context.flowSettingsEnabled;
114
114
  const fieldPathArray = this.context.fieldPathArray ?? this.parent?.context?.fieldPathArray;
115
+ const onResetFieldValue = () => {
116
+ const value = [];
117
+ this.setProps({ value });
118
+ this.context.blockModel?.setFieldValue?.(fieldPathArray, value);
119
+ };
115
120
  return (
116
121
  <SubTableField
117
122
  {...this.props}
@@ -124,6 +129,7 @@ export class SubTableFieldModel extends AssociationFieldModel {
124
129
  formValuesChangeEmitter={this.context.blockModel?.emitter}
125
130
  fieldPathArray={fieldPathArray}
126
131
  getCurrentValue={() => this.getCurrentValue()}
132
+ onResetFieldValue={onResetFieldValue}
127
133
  />
128
134
  );
129
135
  }
@@ -136,10 +142,6 @@ export class SubTableFieldModel extends AssociationFieldModel {
136
142
  this.context.defineProperty('collection', {
137
143
  get: () => this.context.collectionField.targetCollection,
138
144
  });
139
- // 监听表单reset
140
- this.context.blockModel.emitter.on('onFieldReset', () => {
141
- this.props.onChange([]);
142
- });
143
145
  this.onSelectExitRecordClick = (setCurrentPage, currentPageSize) => {
144
146
  this.setCurrentPage = setCurrentPage;
145
147
  this.currentPageSize = currentPageSize;
@@ -0,0 +1,72 @@
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 { FlowEngine, FlowModel } from '@nocobase/flow-engine';
11
+ import { describe, expect, it, vi } from 'vitest';
12
+ import { AssociationFieldModel } from '../AssociationFieldModel';
13
+
14
+ class DefaultAssociationFieldModel extends AssociationFieldModel {}
15
+
16
+ class UpdateAssociationFieldModel extends AssociationFieldModel {
17
+ updateAssociation = true;
18
+ }
19
+
20
+ async function runAssociationFieldInit(FieldModelClass: typeof AssociationFieldModel) {
21
+ const engine = new FlowEngine();
22
+ engine.registerModels({
23
+ DefaultAssociationFieldModel,
24
+ UpdateAssociationFieldModel,
25
+ });
26
+
27
+ const addUpdateAssociationValues = vi.fn();
28
+ const blockModel = engine.createModel<FlowModel>({
29
+ use: 'FlowModel',
30
+ uid: 'form-block',
31
+ });
32
+ blockModel.context.defineProperty('resource', {
33
+ value: { addUpdateAssociationValues },
34
+ });
35
+
36
+ const formItem = engine.createModel<FlowModel>({
37
+ use: 'FlowModel',
38
+ uid: 'form-item',
39
+ });
40
+ (formItem as FlowModel & { fieldPath: string }).fieldPath = 'author';
41
+ formItem.context.defineProperty('blockModel', { value: blockModel });
42
+
43
+ const field = engine.createModel<AssociationFieldModel>({
44
+ use: FieldModelClass,
45
+ uid: 'association-field',
46
+ parentId: formItem.uid,
47
+ });
48
+ field.context.defineProperty('collectionField', {
49
+ value: {
50
+ dataSourceKey: 'main',
51
+ target: 'users',
52
+ },
53
+ });
54
+
55
+ await field.applyFlow('AssociationFieldInit');
56
+
57
+ return addUpdateAssociationValues;
58
+ }
59
+
60
+ describe('AssociationFieldModel updateAssociation', () => {
61
+ it('does not add updateAssociationValues by default', async () => {
62
+ const addUpdateAssociationValues = await runAssociationFieldInit(DefaultAssociationFieldModel);
63
+
64
+ expect(addUpdateAssociationValues).not.toHaveBeenCalled();
65
+ });
66
+
67
+ it('adds updateAssociationValues for opt-in association fields', async () => {
68
+ const addUpdateAssociationValues = await runAssociationFieldInit(UpdateAssociationFieldModel);
69
+
70
+ expect(addUpdateAssociationValues).toHaveBeenCalledWith('author');
71
+ });
72
+ });
@@ -28,6 +28,7 @@ import {
28
28
  normalizeRecordPickerValue,
29
29
  shouldClearRecordPickerValueOnMultipleChange,
30
30
  } from '../RecordPickerFieldModel';
31
+ import { getAssociationHydrationNamePath, getAssociationHydrationSetterContext } from '../RecordSelectFieldModel';
31
32
 
32
33
  function createMockCollection() {
33
34
  return {
@@ -93,6 +94,28 @@ describe('RecordPickerFieldModel item context', () => {
93
94
  expect(shouldClearRecordPickerValueOnMultipleChange({ type: 'belongsTo' }, true, false)).toBe(false);
94
95
  });
95
96
 
97
+ it('uses the current field context for association hydration writes', () => {
98
+ const setFormValue = vi.fn();
99
+ const blockSetFormValue = vi.fn();
100
+ const model = {
101
+ props: {
102
+ name: 'orders',
103
+ },
104
+ context: {
105
+ fieldPathArray: ['orders', 0, 'lines', 1, 'product'],
106
+ setFormValue,
107
+ blockModel: {
108
+ context: {
109
+ setFormValue: blockSetFormValue,
110
+ },
111
+ },
112
+ },
113
+ };
114
+
115
+ expect(getAssociationHydrationNamePath(model)).toEqual(['orders', 0, 'lines', 1, 'product']);
116
+ expect(getAssociationHydrationSetterContext(model)).toBe(model.context);
117
+ });
118
+
96
119
  it('itemChain helpers: createParentItemAccessorsFromInputArgs works', () => {
97
120
  const inputArgs = {
98
121
  parentItem: { value: { id: 1 } },
@@ -197,6 +197,11 @@ export class ClickableFieldModel extends FieldModel {
197
197
  titleField,
198
198
  overflowMode,
199
199
  disabled,
200
+ dateOnly,
201
+ dateFormat,
202
+ format,
203
+ picker,
204
+ showTime,
200
205
  timeFormat,
201
206
  ...restProps
202
207
  } = this.props;
@@ -17,6 +17,8 @@ import { DateTimeNoTzPicker } from '../DateTimeNoTzFieldModel';
17
17
  let capturedDatePickerProps: any;
18
18
  let currentForm: any;
19
19
  const mockResolveJsonTemplate = vi.fn();
20
+ const mockRunjs = vi.fn();
21
+ const mockLoggerWarn = vi.fn();
20
22
 
21
23
  vi.mock('@nocobase/flow-engine', async (importOriginal) => {
22
24
  const actual = await importOriginal<typeof import('@nocobase/flow-engine')>();
@@ -32,6 +34,10 @@ vi.mock('@nocobase/flow-engine', async (importOriginal) => {
32
34
  }),
33
35
  useFlowContext: () => ({
34
36
  resolveJsonTemplate: mockResolveJsonTemplate,
37
+ runjs: mockRunjs,
38
+ logger: {
39
+ warn: mockLoggerWarn,
40
+ },
35
41
  }),
36
42
  };
37
43
  });
@@ -64,6 +70,9 @@ describe('DateTimeNoTzPicker date range limit', () => {
64
70
  currentForm = undefined;
65
71
  capturedDatePickerProps = undefined;
66
72
  mockResolveJsonTemplate.mockReset();
73
+ mockRunjs.mockReset();
74
+ mockLoggerWarn.mockReset();
75
+ mockRunjs.mockResolvedValue({ success: true, value: undefined });
67
76
  mockResolveJsonTemplate.mockImplementation(async (params) => ({
68
77
  ...params,
69
78
  _maxDate: currentForm?.getFieldValue?.('b'),
@@ -239,4 +248,144 @@ describe('DateTimeNoTzPicker date range limit', () => {
239
248
  expect(timeConfig?.disabledMinutes?.(12)).toEqual([]);
240
249
  expect(timeConfig?.disabledSeconds?.(12, 34)).toEqual([]);
241
250
  });
251
+
252
+ it('evaluates RunJS before applying minDate', async () => {
253
+ const code = 'return new Date("2026-05-10T08:09:10.000Z").toISOString();';
254
+ mockRunjs.mockResolvedValue({ success: true, value: '2026-05-10T08:09:10.000Z' });
255
+ mockResolveJsonTemplate.mockImplementation(async (params) => params);
256
+
257
+ render(<TestWrapper picker="date" showTime _minDate={{ code, version: 'v2' }} onChange={vi.fn()} value={null} />);
258
+
259
+ await waitFor(() => {
260
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-05-09 00:00:00'))).toBe(true);
261
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-05-10 00:00:00'))).toBe(false);
262
+ });
263
+
264
+ expect(mockRunjs.mock.calls[0]?.[0]).toBe(code);
265
+ expect(mockRunjs.mock.calls[0]?.[2]).toEqual({ version: 'v2' });
266
+ expect(mockResolveJsonTemplate).toHaveBeenCalledWith({
267
+ _minDate: '2026-05-10T08:09:10.000Z',
268
+ _maxDate: undefined,
269
+ });
270
+ });
271
+
272
+ it('supports a RunJS minDate together with a variable maxDate', async () => {
273
+ mockRunjs.mockResolvedValue({ success: true, value: '2026-05-10 08:00:00' });
274
+ mockResolveJsonTemplate.mockImplementation(async (params) => ({
275
+ ...params,
276
+ _maxDate: '2026-05-12 18:30:40',
277
+ }));
278
+
279
+ render(
280
+ <TestWrapper
281
+ picker="date"
282
+ showTime
283
+ _minDate={{ code: 'return "2026-05-10 08:00:00";', version: 'v2' }}
284
+ _maxDate={'{{ $nForm.max }}'}
285
+ onChange={vi.fn()}
286
+ value={null}
287
+ />,
288
+ );
289
+
290
+ await waitFor(() => {
291
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-05-09 00:00:00'))).toBe(true);
292
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-05-13 00:00:00'))).toBe(true);
293
+ });
294
+
295
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-05-11 00:00:00'))).toBe(false);
296
+ });
297
+
298
+ it('clears stale restrictions when RunJS execution fails', async () => {
299
+ mockResolveJsonTemplate.mockImplementation(async (params) => params);
300
+ mockRunjs.mockImplementation(async (code) => {
301
+ if (code === 'throw new Error("failed");') {
302
+ return { success: false, error: new Error('failed') };
303
+ }
304
+ return { success: true, value: '2026-05-10 08:00:00' };
305
+ });
306
+
307
+ const view = render(
308
+ <TestWrapper
309
+ picker="date"
310
+ showTime
311
+ _minDate={{ code: 'return "2026-05-10 08:00:00";', version: 'v2' }}
312
+ onChange={vi.fn()}
313
+ value={null}
314
+ />,
315
+ );
316
+
317
+ await waitFor(() => {
318
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-05-09 00:00:00'))).toBe(true);
319
+ });
320
+
321
+ view.rerender(
322
+ <TestWrapper
323
+ picker="date"
324
+ showTime
325
+ _minDate={{ code: 'throw new Error("failed");', version: 'v2' }}
326
+ onChange={vi.fn()}
327
+ value={null}
328
+ />,
329
+ );
330
+
331
+ await waitFor(() => {
332
+ expect(mockLoggerWarn).toHaveBeenCalled();
333
+ expect(capturedDatePickerProps?.minDate).toBeNull();
334
+ expect(capturedDatePickerProps?.disabledDate).toBeNull();
335
+ expect(capturedDatePickerProps?.disabledTime).toBeNull();
336
+ });
337
+ });
338
+
339
+ it('keeps the latest RunJS result when an earlier execution finishes later', async () => {
340
+ const slowResolvers: Array<(result: { success: boolean; value: string }) => void> = [];
341
+ mockResolveJsonTemplate.mockImplementation(async (params) => params);
342
+ mockRunjs.mockImplementation((code) => {
343
+ if (code === 'return "slow";') {
344
+ return new Promise((resolve) => {
345
+ slowResolvers.push(resolve);
346
+ });
347
+ }
348
+ return Promise.resolve({ success: true, value: '2026-06-01 00:00:00' });
349
+ });
350
+
351
+ const view = render(
352
+ <TestWrapper
353
+ picker="date"
354
+ showTime
355
+ _minDate={{ code: 'return "slow";', version: 'v2' }}
356
+ onChange={vi.fn()}
357
+ value={null}
358
+ />,
359
+ );
360
+
361
+ await waitFor(() => {
362
+ expect(slowResolvers.length).toBeGreaterThan(0);
363
+ });
364
+
365
+ view.rerender(
366
+ <TestWrapper
367
+ picker="date"
368
+ showTime
369
+ _minDate={{ code: 'return "fast";', version: 'v2' }}
370
+ onChange={vi.fn()}
371
+ value={null}
372
+ />,
373
+ );
374
+
375
+ await waitFor(() => {
376
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-05-31 00:00:00'))).toBe(true);
377
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-07-01 00:00:00'))).toBe(false);
378
+ });
379
+
380
+ slowResolvers.forEach((resolve) => resolve({ success: true, value: '2026-12-01 00:00:00' }));
381
+
382
+ await waitFor(() => {
383
+ expect(mockResolveJsonTemplate).toHaveBeenCalledWith({
384
+ _minDate: '2026-12-01 00:00:00',
385
+ _maxDate: undefined,
386
+ });
387
+ });
388
+
389
+ expect(capturedDatePickerProps?.disabledDate?.(dayjs('2026-07-01 00:00:00'))).toBe(false);
390
+ });
242
391
  });
@@ -8,140 +8,176 @@
8
8
  */
9
9
 
10
10
  import { autorun } from '@formily/reactive';
11
- import { Form } from 'antd';
12
- import { useFlowContext } from '@nocobase/flow-engine';
11
+ import { Form, type FormInstance } from 'antd';
12
+ import { resolveRunJSObjectValues, useFlowContext } from '@nocobase/flow-engine';
13
13
  import { dayjs } from '@nocobase/utils/client';
14
+ import type { ConfigType, Dayjs } from 'dayjs';
14
15
  import { first, last } from 'lodash';
15
- import React, { useEffect, useRef, useState } from 'react';
16
+ import { useCallback, useEffect, useRef, useState } from 'react';
16
17
 
17
18
  type DateLimitProps = {
18
- _minDate?: any;
19
- _maxDate?: any;
20
- currentForm?: any;
19
+ _minDate?: unknown;
20
+ _maxDate?: unknown;
21
+ currentForm?: unknown;
21
22
  };
22
23
 
23
- export function useDateLimit(props: DateLimitProps) {
24
- const ctx = useFlowContext();
25
- const isAntdFormInstance = typeof props.currentForm?.getFieldsValue === 'function';
26
- const currentFormValues = Form.useWatch([], isAntdFormInstance ? props.currentForm : undefined);
27
- const [minDate, setMinDate] = useState<dayjs.Dayjs | null>(null);
28
- const [maxDate, setMaxDate] = useState<dayjs.Dayjs | null>(null);
29
- const [disabledDate, setDisabledDate] = useState<any>(null);
30
- const [disabledTime, setDisabledTime] = useState<any>(null);
31
- const disposeRef = useRef<any>(null);
24
+ type DisabledTimeConfig = {
25
+ disabledHours: () => number[];
26
+ disabledMinutes: (selectedHour: number) => number[];
27
+ disabledSeconds: (selectedHour: number, selectedMinute: number) => number[];
28
+ };
32
29
 
33
- useEffect(() => {
34
- if (disposeRef.current) {
35
- disposeRef.current();
36
- }
30
+ type DisabledDate = (current: Dayjs) => boolean;
31
+ type DisabledTime = (current: Dayjs | null) => DisabledTimeConfig;
37
32
 
38
- disposeRef.current = autorun(() => {
39
- void limitDate();
40
- });
33
+ function isAntdFormInstance(value: unknown): value is FormInstance {
34
+ if (!value || typeof value !== 'object') return false;
35
+ return typeof (value as Partial<FormInstance>).getFieldsValue === 'function';
36
+ }
41
37
 
42
- return () => {
43
- disposeRef.current?.();
44
- };
45
- }, [ctx, currentFormValues, props._maxDate, props._minDate]);
38
+ function parseDateLimitValue(value: unknown): Dayjs | null {
39
+ if (!value) return null;
40
+ const parsed = dayjs(value as ConfigType);
41
+ return parsed.isValid() ? parsed : null;
42
+ }
46
43
 
47
- const limitDate = async () => {
48
- const resolvedParams = await ctx.resolveJsonTemplate({
49
- _minDate: props._minDate,
50
- _maxDate: props._maxDate,
51
- });
44
+ export function useDateLimit(props: DateLimitProps) {
45
+ const ctx = useFlowContext();
46
+ const currentForm = isAntdFormInstance(props.currentForm) ? props.currentForm : undefined;
47
+ const currentFormValues = Form.useWatch([], currentForm);
48
+ const [minDate, setMinDate] = useState<Dayjs | null>(null);
49
+ const [maxDate, setMaxDate] = useState<Dayjs | null>(null);
50
+ const [disabledDate, setDisabledDate] = useState<DisabledDate | null>(null);
51
+ const [disabledTime, setDisabledTime] = useState<DisabledTime | null>(null);
52
+ const evaluationIdRef = useRef(0);
53
+
54
+ const limitDate = useCallback(async () => {
55
+ const evaluationId = ++evaluationIdRef.current;
56
+
57
+ try {
58
+ const runJSResolvedParams = await resolveRunJSObjectValues(ctx, {
59
+ _minDate: props._minDate,
60
+ _maxDate: props._maxDate,
61
+ });
62
+ const resolvedParams = (await ctx.resolveJsonTemplate({
63
+ _minDate: runJSResolvedParams._minDate,
64
+ _maxDate: runJSResolvedParams._maxDate,
65
+ })) as { _minDate?: unknown; _maxDate?: unknown };
66
+
67
+ if (evaluationId !== evaluationIdRef.current) return;
68
+
69
+ const nextMinRaw = Array.isArray(resolvedParams?._minDate)
70
+ ? first(resolvedParams._minDate)
71
+ : resolvedParams?._minDate;
72
+ const nextMaxRaw = Array.isArray(resolvedParams?._maxDate)
73
+ ? last(resolvedParams._maxDate)
74
+ : resolvedParams?._maxDate;
75
+ const nextMinDate = parseDateLimitValue(nextMinRaw);
76
+ const nextMaxDate = parseDateLimitValue(nextMaxRaw);
77
+
78
+ setMinDate(nextMinDate);
79
+ setMaxDate(nextMaxDate);
80
+
81
+ const fullTimeArr = Array.from({ length: 60 }, (_, i) => i);
82
+
83
+ const nextDisabledDate: DisabledDate = (current) => {
84
+ if (!dayjs.isDayjs(current)) return false;
85
+
86
+ const min = nextMinDate?.startOf('day') || null;
87
+ const max = nextMaxDate?.endOf('day') || null;
88
+
89
+ if (min && current.startOf('day').isBefore(min)) {
90
+ return true;
91
+ }
92
+ if (max && current.startOf('day').isAfter(max)) {
93
+ return true;
94
+ }
95
+ return false;
96
+ };
52
97
 
53
- const nextMinRaw = Array.isArray(resolvedParams?._minDate)
54
- ? first(resolvedParams._minDate)
55
- : resolvedParams?._minDate;
56
- const nextMaxRaw = Array.isArray(resolvedParams?._maxDate)
57
- ? last(resolvedParams._maxDate)
58
- : resolvedParams?._maxDate;
59
- const nextMinDate = nextMinRaw ? dayjs(nextMinRaw) : null;
60
- const nextMaxDate = nextMaxRaw ? dayjs(nextMaxRaw) : null;
61
-
62
- setMinDate(nextMinDate?.isValid?.() ? nextMinDate : null);
63
- setMaxDate(nextMaxDate?.isValid?.() ? nextMaxDate : null);
64
-
65
- const fullTimeArr = Array.from({ length: 60 }, (_, i) => i);
66
-
67
- const nextDisabledDate = (current: dayjs.Dayjs) => {
68
- if (!dayjs.isDayjs(current)) return false;
69
-
70
- const min = nextMinDate?.isValid?.() ? nextMinDate.startOf('day') : null;
71
- const max = nextMaxDate?.isValid?.() ? nextMaxDate.endOf('day') : null;
72
-
73
- if (min && current.startOf('day').isBefore(min)) {
74
- return true;
75
- }
76
- if (max && current.startOf('day').isAfter(max)) {
77
- return true;
78
- }
79
- return false;
80
- };
98
+ const nextDisabledTime: DisabledTime = (current) => {
99
+ if (!current || (!nextMinDate && !nextMaxDate)) {
100
+ return { disabledHours: () => [], disabledMinutes: () => [], disabledSeconds: () => [] };
101
+ }
81
102
 
82
- const nextDisabledTime = (current: dayjs.Dayjs) => {
83
- if (!current || (!nextMinDate?.isValid?.() && !nextMaxDate?.isValid?.())) {
84
- return { disabledHours: () => [], disabledMinutes: () => [], disabledSeconds: () => [] };
85
- }
103
+ const isCurrentMinDay = !!nextMinDate && current.isSame(nextMinDate, 'day');
104
+ const isCurrentMaxDay = !!nextMaxDate && current.isSame(nextMaxDate, 'day');
86
105
 
87
- const isCurrentMinDay = !!nextMinDate?.isValid?.() && current.isSame(nextMinDate, 'day');
88
- const isCurrentMaxDay = !!nextMaxDate?.isValid?.() && current.isSame(nextMaxDate, 'day');
106
+ const disabledHours = () => {
107
+ const hours = [];
108
+ if (isCurrentMinDay && nextMinDate) {
109
+ for (let hour = 0; hour < nextMinDate.hour(); hour++) {
110
+ hours.push(hour);
111
+ }
112
+ }
113
+ if (isCurrentMaxDay && nextMaxDate) {
114
+ for (let hour = nextMaxDate.hour() + 1; hour < 24; hour++) {
115
+ hours.push(hour);
116
+ }
117
+ }
118
+ return hours;
119
+ };
89
120
 
90
- const disabledHours = () => {
91
- const hours = [];
92
- if (isCurrentMinDay && nextMinDate) {
93
- for (let hour = 0; hour < nextMinDate.hour(); hour++) {
94
- hours.push(hour);
121
+ const disabledMinutes = (selectedHour: number) => {
122
+ if (isCurrentMinDay && nextMinDate && selectedHour === nextMinDate.hour()) {
123
+ return fullTimeArr.filter((minute) => minute < nextMinDate.minute());
95
124
  }
96
- }
97
- if (isCurrentMaxDay && nextMaxDate) {
98
- for (let hour = nextMaxDate.hour() + 1; hour < 24; hour++) {
99
- hours.push(hour);
125
+ if (isCurrentMaxDay && nextMaxDate && selectedHour === nextMaxDate.hour()) {
126
+ return fullTimeArr.filter((minute) => minute > nextMaxDate.minute());
100
127
  }
101
- }
102
- return hours;
128
+ return [];
129
+ };
130
+
131
+ const disabledSeconds = (selectedHour: number, selectedMinute: number) => {
132
+ if (
133
+ isCurrentMinDay &&
134
+ nextMinDate &&
135
+ selectedHour === nextMinDate.hour() &&
136
+ selectedMinute === nextMinDate.minute()
137
+ ) {
138
+ return fullTimeArr.filter((second) => second < nextMinDate.second());
139
+ }
140
+ if (
141
+ isCurrentMaxDay &&
142
+ nextMaxDate &&
143
+ selectedHour === nextMaxDate.hour() &&
144
+ selectedMinute === nextMaxDate.minute()
145
+ ) {
146
+ return fullTimeArr.filter((second) => second > nextMaxDate.second());
147
+ }
148
+ return [];
149
+ };
150
+
151
+ return {
152
+ disabledHours,
153
+ disabledMinutes,
154
+ disabledSeconds,
155
+ };
103
156
  };
104
157
 
105
- const disabledMinutes = (selectedHour: number) => {
106
- if (isCurrentMinDay && nextMinDate && selectedHour === nextMinDate.hour()) {
107
- return fullTimeArr.filter((minute) => minute < nextMinDate.minute());
108
- }
109
- if (isCurrentMaxDay && nextMaxDate && selectedHour === nextMaxDate.hour()) {
110
- return fullTimeArr.filter((minute) => minute > nextMaxDate.minute());
111
- }
112
- return [];
113
- };
158
+ setDisabledDate(() => nextDisabledDate);
159
+ setDisabledTime(() => nextDisabledTime);
160
+ } catch (error) {
161
+ if (evaluationId !== evaluationIdRef.current) return;
114
162
 
115
- const disabledSeconds = (selectedHour: number, selectedMinute: number) => {
116
- if (
117
- isCurrentMinDay &&
118
- nextMinDate &&
119
- selectedHour === nextMinDate.hour() &&
120
- selectedMinute === nextMinDate.minute()
121
- ) {
122
- return fullTimeArr.filter((second) => second < nextMinDate.second());
123
- }
124
- if (
125
- isCurrentMaxDay &&
126
- nextMaxDate &&
127
- selectedHour === nextMaxDate.hour() &&
128
- selectedMinute === nextMaxDate.minute()
129
- ) {
130
- return fullTimeArr.filter((second) => second > nextMaxDate.second());
131
- }
132
- return [];
133
- };
163
+ ctx.logger?.warn?.({ err: error }, 'Failed to resolve date range limit');
164
+ setMinDate(null);
165
+ setMaxDate(null);
166
+ setDisabledDate(null);
167
+ setDisabledTime(null);
168
+ }
169
+ }, [ctx, props._maxDate, props._minDate]);
134
170
 
135
- return {
136
- disabledHours,
137
- disabledMinutes,
138
- disabledSeconds,
139
- };
140
- };
171
+ useEffect(() => {
172
+ const dispose = autorun(() => {
173
+ limitDate();
174
+ });
141
175
 
142
- setDisabledDate(() => nextDisabledDate);
143
- setDisabledTime(() => nextDisabledTime);
144
- };
176
+ return () => {
177
+ evaluationIdRef.current += 1;
178
+ dispose();
179
+ };
180
+ }, [currentFormValues, limitDate]);
145
181
 
146
182
  return {
147
183
  minDate,
@@ -8,14 +8,42 @@
8
8
  */
9
9
 
10
10
  import { DisplayItemModel, tExpr } from '@nocobase/flow-engine';
11
+ import { getDateTimeFormat, getPickerFormat } from '@nocobase/utils/client';
11
12
  import dayjs from 'dayjs';
12
13
  import React from 'react';
13
14
  import { ClickableFieldModel } from './ClickableFieldModel';
14
15
 
16
+ const stripTimeFromFormat = (format?: string) =>
17
+ format ? format.replace(/\s*[Hh]{1,2}:mm(?::ss)?(?:\.SSS)?(?:\s*[aA])?/g, '').trim() : format;
18
+
19
+ interface DisplayDateTimeFormatProps {
20
+ dateOnly?: boolean;
21
+ picker?: string;
22
+ format?: string;
23
+ dateFormat?: string;
24
+ showTime?: boolean;
25
+ timeFormat?: string;
26
+ }
27
+
28
+ const resolveDisplayDateTimeFormat = (props: DisplayDateTimeFormatProps) => {
29
+ const { dateOnly, picker = 'date', format, dateFormat, showTime, timeFormat } = props;
30
+ const normalizedFormat = stripTimeFromFormat(format);
31
+ if (picker !== 'date') {
32
+ return dateFormat || normalizedFormat || getPickerFormat(picker);
33
+ }
34
+
35
+ if (!dateOnly && !dateFormat && typeof showTime === 'undefined' && !timeFormat && normalizedFormat) {
36
+ return format;
37
+ }
38
+
39
+ const normalizedDateFormat = dateFormat || normalizedFormat || getPickerFormat(picker);
40
+ return getDateTimeFormat(picker, normalizedDateFormat, showTime, timeFormat);
41
+ };
42
+
15
43
  export class DisplayDateTimeFieldModel extends ClickableFieldModel {
16
44
  public renderComponent(value) {
17
45
  const { className, style } = this.props;
18
- const finalFormat = this.props.format;
46
+ const finalFormat = resolveDisplayDateTimeFormat(this.props);
19
47
  let formattedValue = '';
20
48
  if (value) {
21
49
  const day = dayjs(value);