@nocobase/client-v2 2.2.0-beta.8 → 2.2.0

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 (271) hide show
  1. package/es/APIClient.d.ts +13 -3
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/RouteRepository.d.ts +8 -0
  4. package/es/RouterManager.d.ts +15 -0
  5. package/es/authRedirect.d.ts +1 -0
  6. package/es/collection-field-interface/CollectionFieldInterface.d.ts +1 -0
  7. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +1 -0
  8. package/es/components/category-tabs/SortableCategoryTabs.d.ts +55 -0
  9. package/es/components/category-tabs/index.d.ts +9 -0
  10. package/es/components/form/ScanInput/useCodeScanner.d.ts +3 -2
  11. package/es/components/form/TypedVariableInput.d.ts +31 -5
  12. package/es/components/form/filter/CollectionFilter.d.ts +2 -0
  13. package/es/components/form/filter/CollectionFilterItem.d.ts +11 -1
  14. package/es/components/form/filter/CollectionFilterPanel.d.ts +2 -0
  15. package/es/components/form/filter/index.d.ts +2 -0
  16. package/es/components/form/filter/useFilterActionProps.d.ts +2 -0
  17. package/es/components/index.d.ts +1 -0
  18. package/es/entry-actions/EntryActionManager.d.ts +24 -0
  19. package/es/entry-actions/index.d.ts +9 -0
  20. package/es/flow/actions/index.d.ts +1 -1
  21. package/es/flow/actions/linkageRules.d.ts +27 -0
  22. package/es/flow/admin-shell/BaseLayoutModel.d.ts +6 -0
  23. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +7 -0
  24. package/es/flow/admin-shell/admin-layout/AdminLayoutMenuModels.d.ts +1 -0
  25. package/es/flow/admin-shell/admin-layout/AppListRender.d.ts +2 -1
  26. package/es/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.d.ts +24 -0
  27. package/es/flow/admin-shell/admin-layout/index.d.ts +1 -0
  28. package/es/flow/admin-shell/admin-layout/useApplications.d.ts +7 -2
  29. package/es/flow/components/FieldAssignExactDatePicker.d.ts +1 -0
  30. package/es/flow/components/FieldAssignValueInput.d.ts +7 -24
  31. package/es/flow/components/FlowRoute.d.ts +2 -2
  32. package/es/flow/components/RunJSValueEditor.d.ts +1 -0
  33. package/es/flow/components/field-value-variable/DateVariableEditor.d.ts +24 -0
  34. package/es/flow/components/field-value-variable/FieldValueVariableInput.d.ts +31 -0
  35. package/es/flow/components/field-value-variable/dateValue.d.ts +36 -0
  36. package/es/flow/components/field-value-variable/index.d.ts +11 -0
  37. package/es/flow/components/filter/FilterGroup.d.ts +1 -0
  38. package/es/flow/components/filter/VariableFilterItem.d.ts +6 -1
  39. package/es/flow/components/placeholders/BlockPlaceholder.d.ts +1 -0
  40. package/es/flow/index.d.ts +1 -0
  41. package/es/flow/internal/registerDeviceTypeContext.d.ts +10 -0
  42. package/es/flow/internal/utils/operatorSchemaHelper.d.ts +2 -2
  43. package/es/flow/models/actions/UpdateRecordActionUtils.d.ts +8 -2
  44. package/es/flow/models/base/ActionModelCore.d.ts +6 -2
  45. package/es/flow/models/base/BlockGridModel.d.ts +3 -0
  46. package/es/flow/models/base/CollectionBlockModel.d.ts +3 -0
  47. package/es/flow/models/base/PageModel/PageModel.d.ts +12 -0
  48. package/es/flow/models/base/PageModel/PageModelTabBar.d.ts +22 -0
  49. package/es/flow/models/base/PageModel/PageTabModel.d.ts +12 -0
  50. package/es/flow/models/blocks/details/DetailsBlockModel.d.ts +3 -0
  51. package/es/flow/models/blocks/filter-form/FilterFormGridModel.d.ts +1 -0
  52. package/es/flow/models/blocks/filter-form/FilterFormSubmitActionModel.d.ts +3 -1
  53. package/es/flow/models/blocks/form/FormBlockModel.d.ts +7 -0
  54. package/es/flow/models/blocks/form/FormGridModel.d.ts +6 -0
  55. package/es/flow/models/blocks/form/QuickEditFormModel.d.ts +17 -2
  56. package/es/flow/models/blocks/form/value-runtime/rules.d.ts +1 -0
  57. package/es/flow/models/blocks/table/JSColumnModel.d.ts +2 -0
  58. package/es/flow/models/blocks/table/TableBlockModel.d.ts +1 -0
  59. package/es/flow/models/blocks/table/TableColumnModel.d.ts +12 -0
  60. package/es/flow/models/blocks/table/utils.d.ts +1 -0
  61. package/es/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.d.ts +2 -1
  62. package/es/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.d.ts +0 -17
  63. package/es/flow/models/fields/AssociationFieldModel/recordSelectShared.d.ts +41 -0
  64. package/es/flow/models/fields/DateTimeFieldModel/dateLimit.d.ts +15 -7
  65. package/es/flow/routeTransientInputArgs.d.ts +14 -0
  66. package/es/flow-compat/fieldValidationConstants.d.ts +1 -1
  67. package/es/flow-compat/passwordUtils.d.ts +1 -1
  68. package/es/flow-compat/routeTypes.d.ts +1 -0
  69. package/es/index.d.ts +7 -0
  70. package/es/index.mjs +301 -177
  71. package/es/ui-operation/index.d.ts +15 -0
  72. package/es/ui-operation/ui-operation-codec.d.ts +10 -0
  73. package/es/utils/getRouteRuntimeVersion.d.ts +23 -0
  74. package/es/utils/index.d.ts +1 -0
  75. package/es/utils/markdownSanitize.d.ts +11 -0
  76. package/lib/index.js +313 -189
  77. package/lib/locale/languageCodes.js +3 -1
  78. package/package.json +9 -7
  79. package/src/APIClient.ts +90 -7
  80. package/src/Application.tsx +7 -1
  81. package/src/BaseApplication.tsx +13 -6
  82. package/src/RouteRepository.ts +25 -0
  83. package/src/RouterManager.tsx +142 -1
  84. package/src/__tests__/APIClient.test.tsx +58 -0
  85. package/src/__tests__/RouteRepository.test.ts +23 -0
  86. package/src/__tests__/RouterManager.test.ts +125 -0
  87. package/src/__tests__/app.test.tsx +73 -0
  88. package/src/__tests__/authRedirect.test.ts +17 -0
  89. package/src/__tests__/browserChecker.test.ts +61 -0
  90. package/src/__tests__/getRouteRuntimeVersion.test.ts +68 -0
  91. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +35 -2
  92. package/src/__tests__/settings-center.test.tsx +270 -6
  93. package/src/acl/ACLProvider.tsx +2 -0
  94. package/src/acl/__tests__/ACLProvider.test.tsx +92 -0
  95. package/src/authRedirect.ts +38 -0
  96. package/src/collection-field-interface/CollectionFieldInterface.ts +1 -0
  97. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +1 -0
  98. package/src/collection-manager/field-validation.ts +1 -1
  99. package/src/components/README.md +7 -1
  100. package/src/components/README.zh-CN.md +6 -1
  101. package/src/components/category-tabs/SortableCategoryTabs.tsx +210 -0
  102. package/src/components/category-tabs/index.ts +10 -0
  103. package/src/components/form/JsonTextArea.tsx +4 -0
  104. package/src/components/form/ScanInput/CodeScanner.tsx +79 -35
  105. package/src/components/form/ScanInput/__tests__/CodeScanner.test.tsx +101 -0
  106. package/src/components/form/ScanInput/__tests__/useCodeScanner.test.tsx +284 -11
  107. package/src/components/form/ScanInput/useCodeScanner.ts +332 -24
  108. package/src/components/form/TypedVariableInput.tsx +452 -101
  109. package/src/components/form/__tests__/TypedVariableInput.test.tsx +364 -12
  110. package/src/components/form/filter/CollectionFilter.tsx +4 -0
  111. package/src/components/form/filter/CollectionFilterItem.tsx +36 -9
  112. package/src/components/form/filter/CollectionFilterPanel.tsx +4 -0
  113. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +55 -0
  114. package/src/components/form/filter/__tests__/useFilterActionProps.test.tsx +95 -0
  115. package/src/components/form/filter/index.ts +2 -0
  116. package/src/components/form/filter/useFilterActionProps.ts +37 -6
  117. package/src/components/form/table/Table.tsx +81 -6
  118. package/src/components/form/table/__tests__/Table.columnWidth.test.tsx +433 -0
  119. package/src/components/index.ts +1 -0
  120. package/src/entry-actions/EntryActionManager.ts +76 -0
  121. package/src/entry-actions/index.ts +10 -0
  122. package/src/flow/FlowPage.tsx +38 -3
  123. package/src/flow/__tests__/FlowPage.test.tsx +201 -27
  124. package/src/flow/__tests__/FlowRoute.test.tsx +675 -12
  125. package/src/flow/__tests__/PluginFlowEngine.test.ts +58 -0
  126. package/src/flow/actions/__tests__/actionLinkageRules.forkProps.test.ts +314 -0
  127. package/src/flow/actions/__tests__/dataScopeFilter.test.ts +62 -0
  128. package/src/flow/actions/__tests__/dataScopeFormValueClear.test.ts +102 -0
  129. package/src/flow/actions/__tests__/linkageRules.actionStates.test.ts +33 -0
  130. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +72 -0
  131. package/src/flow/actions/__tests__/linkageRules.tab.test.ts +171 -0
  132. package/src/flow/actions/__tests__/linkageRulesRefresh.test.ts +7 -3
  133. package/src/flow/actions/__tests__/openView.defineProps.route.test.tsx +80 -20
  134. package/src/flow/actions/dataScopeFilter.ts +10 -1
  135. package/src/flow/actions/dateTimeFormat.tsx +2 -2
  136. package/src/flow/actions/index.ts +2 -0
  137. package/src/flow/actions/linkageRules.tsx +144 -26
  138. package/src/flow/actions/linkageRulesRefresh.tsx +2 -6
  139. package/src/flow/actions/openView.tsx +21 -1
  140. package/src/flow/admin-shell/BaseLayoutModel.tsx +124 -0
  141. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +184 -42
  142. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +1016 -119
  143. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +41 -5
  144. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.test.tsx +177 -1
  145. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +20 -0
  146. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuUtils.tsx +78 -59
  147. package/src/flow/admin-shell/admin-layout/AppListRender.tsx +13 -2
  148. package/src/flow/admin-shell/admin-layout/AppSwitcherActionPanelModel.tsx +289 -0
  149. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutComponent.test.tsx +253 -6
  150. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +228 -0
  151. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +436 -2
  152. package/src/flow/admin-shell/admin-layout/__tests__/TopbarActionsBar.test.tsx +48 -0
  153. package/src/flow/admin-shell/admin-layout/index.ts +1 -0
  154. package/src/flow/admin-shell/admin-layout/useApplications.tsx +81 -12
  155. package/src/flow/common/Markdown/Display.tsx +14 -3
  156. package/src/flow/common/Markdown/Edit.tsx +19 -7
  157. package/src/flow/common/Markdown/style.ts +4 -0
  158. package/src/flow/components/BlockItemCard.tsx +2 -2
  159. package/src/flow/components/FieldAssignExactDatePicker.tsx +25 -11
  160. package/src/flow/components/FieldAssignValueInput.tsx +72 -584
  161. package/src/flow/components/FlowRoute.tsx +204 -46
  162. package/src/flow/components/RunJSValueEditor.tsx +9 -1
  163. package/src/flow/components/__tests__/FieldAssignValueInput.context.test.tsx +216 -0
  164. package/src/flow/components/field-value-variable/DateVariableEditor.tsx +181 -0
  165. package/src/flow/components/field-value-variable/FieldValueVariableInput.tsx +326 -0
  166. package/src/flow/components/field-value-variable/__tests__/FieldValueVariableInput.test.tsx +380 -0
  167. package/src/flow/components/field-value-variable/dateValue.ts +223 -0
  168. package/src/flow/components/field-value-variable/index.ts +12 -0
  169. package/src/flow/components/filter/FilterGroup.tsx +33 -5
  170. package/src/flow/components/filter/VariableFilterItem.tsx +170 -13
  171. package/src/flow/components/filter/__tests__/FilterGroup.test.tsx +45 -0
  172. package/src/flow/components/filter/__tests__/VariableFilterItem.leftMetaTree.test.tsx +9 -0
  173. package/src/flow/components/filter/__tests__/VariableFilterItem.test.tsx +195 -1
  174. package/src/flow/components/placeholders/BlockPlaceholder.tsx +70 -23
  175. package/src/flow/components/placeholders/__tests__/BlockPlaceholder.test.tsx +57 -7
  176. package/src/flow/index.ts +3 -0
  177. package/src/flow/internal/components/Markdown/util.ts +2 -1
  178. package/src/flow/internal/registerDeviceTypeContext.ts +39 -0
  179. package/src/flow/internal/utils/operatorSchemaHelper.ts +3 -2
  180. package/src/flow/models/actions/UpdateRecordActionModel.tsx +18 -1
  181. package/src/flow/models/actions/UpdateRecordActionUtils.ts +18 -6
  182. package/src/flow/models/actions/__tests__/UpdateRecordActionModel.test.ts +76 -4
  183. package/src/flow/models/base/ActionModel.tsx +10 -8
  184. package/src/flow/models/base/ActionModelCore.tsx +16 -2
  185. package/src/flow/models/base/BlockGridModel.tsx +26 -0
  186. package/src/flow/models/base/CollectionBlockModel.tsx +38 -3
  187. package/src/flow/models/base/PageModel/PageModel.tsx +387 -53
  188. package/src/flow/models/base/PageModel/PageModelTabBar.tsx +114 -0
  189. package/src/flow/models/base/PageModel/PageTabModel.tsx +249 -18
  190. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +790 -10
  191. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.module-isolation.test.tsx +130 -0
  192. package/src/flow/models/base/PageModel/__tests__/PageModelTabBar.test.tsx +118 -0
  193. package/src/flow/models/base/PageModel/__tests__/PageTabModel.test.ts +1075 -7
  194. package/src/flow/models/base/__tests__/ActionModelCore.render.test.tsx +49 -0
  195. package/src/flow/models/base/__tests__/BlockGridModel.selectSceneActivation.test.ts +124 -0
  196. package/src/flow/models/base/__tests__/CollectionBlockModel.addAppends.test.ts +41 -0
  197. package/src/flow/models/base/__tests__/CollectionBlockModel.initialBeforeRenderRefresh.test.ts +125 -9
  198. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +28 -53
  199. package/src/flow/models/blocks/details/DetailsBlockModel.tsx +8 -3
  200. package/src/flow/models/blocks/details/__tests__/DetailsBlockModel.initialPaginationChangeRefresh.test.ts +32 -1
  201. package/src/flow/models/blocks/filter-form/FilterFormBlockModel.tsx +28 -3
  202. package/src/flow/models/blocks/filter-form/FilterFormGridModel.tsx +36 -5
  203. package/src/flow/models/blocks/filter-form/FilterFormItemModel.tsx +155 -25
  204. package/src/flow/models/blocks/filter-form/FilterFormSubmitActionModel.tsx +57 -4
  205. package/src/flow/models/blocks/filter-form/__tests__/FilterFormGridModel.onModelCreated.test.ts +174 -1
  206. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.defineChildren.test.ts +395 -1
  207. package/src/flow/models/blocks/filter-form/__tests__/FilterFormItemModel.getFilterValue.test.ts +72 -0
  208. package/src/flow/models/blocks/filter-form/__tests__/FilterFormSubmitActionModel.lifecycle.test.ts +90 -0
  209. package/src/flow/models/blocks/filter-form/__tests__/defaultValues.wiring.test.ts +66 -4
  210. package/src/flow/models/blocks/filter-manager/FilterManager.ts +5 -0
  211. package/src/flow/models/blocks/filter-manager/__tests__/FilterManager.test.ts +40 -0
  212. package/src/flow/models/blocks/form/FormBlockModel.tsx +47 -0
  213. package/src/flow/models/blocks/form/FormGridModel.tsx +4 -0
  214. package/src/flow/models/blocks/form/QuickEditFormModel.tsx +189 -36
  215. package/src/flow/models/blocks/form/__tests__/FormBlockModel.test.tsx +8 -2
  216. package/src/flow/models/blocks/form/__tests__/QuickEditFormModel.quickEdit.test.ts +350 -2
  217. package/src/flow/models/blocks/form/__tests__/runJsFormSubmit.test.ts +131 -0
  218. package/src/flow/models/blocks/form/value-runtime/__tests__/runtime.test.ts +435 -0
  219. package/src/flow/models/blocks/form/value-runtime/rules.ts +67 -14
  220. package/src/flow/models/blocks/form/value-runtime/runtime.ts +43 -19
  221. package/src/flow/models/blocks/js-block/JSBlock.tsx +1 -1
  222. package/src/flow/models/blocks/table/JSColumnModel.tsx +10 -1
  223. package/src/flow/models/blocks/table/TableActionsColumnModel.tsx +2 -1
  224. package/src/flow/models/blocks/table/TableBlockModel.tsx +29 -3
  225. package/src/flow/models/blocks/table/TableColumnModel.tsx +54 -11
  226. package/src/flow/models/blocks/table/__tests__/JSColumnModel.test.tsx +52 -5
  227. package/src/flow/models/blocks/table/__tests__/TableBlockModel.filterReset.test.ts +78 -0
  228. package/src/flow/models/blocks/table/__tests__/TableBlockModel.quickEditRefresh.test.ts +12 -0
  229. package/src/flow/models/blocks/table/__tests__/TableColumnModel.test.tsx +84 -0
  230. package/src/flow/models/blocks/table/utils.ts +7 -0
  231. package/src/flow/models/fields/AssociationFieldModel/CascadeSelectFieldModel.tsx +33 -1
  232. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/PopupSubTableFieldModel.tsx +10 -1
  233. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/__tests__/popupContext.test.ts +120 -0
  234. package/src/flow/models/fields/AssociationFieldModel/PopupSubTableFieldModel/actions/PopupSubTableEditActionModel.tsx +10 -2
  235. package/src/flow/models/fields/AssociationFieldModel/RecordPickerFieldModel.tsx +24 -1
  236. package/src/flow/models/fields/AssociationFieldModel/RecordSelectFieldModel.tsx +54 -149
  237. package/src/flow/models/fields/AssociationFieldModel/__tests__/RecordPickerFieldModel.itemContext.test.ts +167 -3
  238. package/src/flow/models/fields/AssociationFieldModel/recordSelectShared.tsx +156 -0
  239. package/src/flow/models/fields/DateTimeFieldModel/__tests__/DateTimeNoTzFieldModel.dateLimit.test.tsx +149 -0
  240. package/src/flow/models/fields/DateTimeFieldModel/dateLimit.ts +149 -113
  241. package/src/flow/models/fields/DisplayNumberFieldModel.tsx +1 -1
  242. package/src/flow/models/fields/JsonFieldModel.tsx +31 -8
  243. package/src/flow/models/fields/NumberFieldModel.tsx +1 -1
  244. package/src/flow/models/fields/TextareaFieldModel.tsx +42 -19
  245. package/src/flow/models/fields/VariableFieldFormModel.tsx +3 -3
  246. package/src/flow/models/fields/__tests__/DisplayNumberFieldModel.test.ts +33 -0
  247. package/src/flow/models/fields/__tests__/JsonFieldModel.test.ts +82 -0
  248. package/src/flow/models/fields/__tests__/NumberFieldModel.test.tsx +47 -0
  249. package/src/flow/models/fields/__tests__/TextareaFieldModel.test.tsx +32 -1
  250. package/src/flow/models/fields/__tests__/VariableFieldFormModel.test.tsx +51 -0
  251. package/src/flow/models/fields/mobile-components/MobileLazySelect.tsx +26 -10
  252. package/src/flow/models/fields/mobile-components/MobileSelect.tsx +51 -14
  253. package/src/flow/models/fields/mobile-components/__tests__/MobileSelect.test.tsx +139 -13
  254. package/src/flow/models/topbar/TopbarActionModel.tsx +78 -3
  255. package/src/flow/routeTransientInputArgs.ts +27 -0
  256. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +42 -0
  257. package/src/flow/utils/dataScopeFormValueClear.ts +4 -3
  258. package/src/flow-compat/fieldValidationConstants.ts +1 -1
  259. package/src/flow-compat/routeTypes.ts +1 -0
  260. package/src/index.ts +7 -0
  261. package/src/layout-manager/LayoutContentRoute.tsx +2 -1
  262. package/src/layout-manager/LayoutRoute.tsx +2 -1
  263. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +87 -1
  264. package/src/locale/languageCodes.ts +3 -1
  265. package/src/nocobase-buildin-plugin/index.tsx +19 -1
  266. package/src/settings-center/AdminSettingsLayout.tsx +23 -2
  267. package/src/ui-operation/index.ts +33 -0
  268. package/src/ui-operation/ui-operation-codec.ts +54 -0
  269. package/src/utils/getRouteRuntimeVersion.ts +185 -0
  270. package/src/utils/index.tsx +1 -0
  271. package/src/utils/markdownSanitize.ts +88 -0
@@ -0,0 +1,181 @@
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 { useFlowContext, type CtxDateExpressionConfig, type CtxDateRelativeUnit } from '@nocobase/flow-engine';
11
+ import { AutoComplete, Input, InputNumber, Select, Space } from 'antd';
12
+ import type { Dayjs } from 'dayjs';
13
+ import React from 'react';
14
+ import { FieldAssignExactDatePicker, type ExactDatePickerValue } from '../FieldAssignExactDatePicker';
15
+ import { toExactPickerDisplayValue, type DateVariableComponentProps } from './dateValue';
16
+
17
+ const DATE_FORMAT_OPTIONS = [
18
+ 'YYYY-MM-DD',
19
+ 'YYYY-MM-DD HH:mm:ss',
20
+ 'YYYY/MM/DD',
21
+ 'MM/DD/YYYY',
22
+ 'DD/MM/YYYY',
23
+ 'YYYYMMDD',
24
+ ].map((value) => ({ value }));
25
+
26
+ const PRESET_LABELS: Record<string, string> = {
27
+ now: 'Now',
28
+ today: 'Today',
29
+ yesterday: 'Yesterday',
30
+ tomorrow: 'Tomorrow',
31
+ thisWeek: 'This Week',
32
+ lastWeek: 'Last Week',
33
+ nextWeek: 'Next Week',
34
+ thisMonth: 'This Month',
35
+ lastMonth: 'Last Month',
36
+ nextMonth: 'Next Month',
37
+ thisQuarter: 'This Quarter',
38
+ lastQuarter: 'Last Quarter',
39
+ nextQuarter: 'Next Quarter',
40
+ thisYear: 'This Year',
41
+ lastYear: 'Last Year',
42
+ nextYear: 'Next Year',
43
+ };
44
+
45
+ export function getDefaultDateVariableFormat(config: CtxDateExpressionConfig): string {
46
+ return config.kind === 'preset' && config.preset === 'now' ? 'YYYY-MM-DD HH:mm:ss' : 'YYYY-MM-DD';
47
+ }
48
+
49
+ export function serializeExactDatePickerValue(
50
+ value: ExactDatePickerValue,
51
+ showTime: boolean,
52
+ ): string | [string, string] {
53
+ const serialize = (item: Dayjs) => (showTime ? item.toISOString() : item.format('YYYY-MM-DD'));
54
+ if (Array.isArray(value)) {
55
+ return [serialize(value[0]), serialize(value[1])];
56
+ }
57
+ return value ? serialize(value) : '';
58
+ }
59
+
60
+ type DateVariableEditorProps = {
61
+ value?: CtxDateExpressionConfig;
62
+ onChange?: (value: CtxDateExpressionConfig) => void;
63
+ isDateLikeField: boolean;
64
+ dateComponentProps: DateVariableComponentProps;
65
+ style?: React.CSSProperties;
66
+ disabled?: boolean;
67
+ };
68
+
69
+ const DateFormatEditor: React.FC<{
70
+ value: string;
71
+ placeholder: string;
72
+ onChange: (value: string) => void;
73
+ disabled?: boolean;
74
+ }> = ({ value, placeholder, onChange, disabled }) => {
75
+ const ctx = useFlowContext();
76
+ return (
77
+ <Space.Compact style={{ width: '100%' }}>
78
+ <Input
79
+ value={ctx.t('Format')}
80
+ readOnly
81
+ disabled={disabled}
82
+ tabIndex={-1}
83
+ aria-hidden
84
+ style={{ width: 88, pointerEvents: 'none' }}
85
+ />
86
+ <AutoComplete
87
+ value={value}
88
+ placeholder={placeholder}
89
+ options={DATE_FORMAT_OPTIONS}
90
+ onChange={(nextValue) => onChange(nextValue.slice(0, 128))}
91
+ disabled={disabled}
92
+ style={{ flex: 1, minWidth: 0 }}
93
+ aria-label={ctx.t('Format')}
94
+ />
95
+ </Space.Compact>
96
+ );
97
+ };
98
+
99
+ export const DateVariableEditor: React.FC<DateVariableEditorProps> = ({
100
+ value,
101
+ onChange,
102
+ isDateLikeField,
103
+ dateComponentProps,
104
+ style,
105
+ disabled = false,
106
+ }) => {
107
+ const ctx = useFlowContext();
108
+ if (!value) return null;
109
+
110
+ const updateFormat = (format: string) => onChange?.({ ...value, format });
111
+ const format = value.format || '';
112
+
113
+ const renderValueEditor = () => {
114
+ if (value.kind === 'exact') {
115
+ const isRange = Array.isArray(value.value);
116
+ return (
117
+ <FieldAssignExactDatePicker
118
+ picker={dateComponentProps.picker}
119
+ showTime={dateComponentProps.showTime}
120
+ timeFormat={dateComponentProps.timeFormat}
121
+ format={dateComponentProps.format}
122
+ isRange={isRange}
123
+ value={toExactPickerDisplayValue(value.value, {
124
+ format: dateComponentProps.format,
125
+ isRange,
126
+ })}
127
+ onChange={(nextValue) => {
128
+ onChange?.({ ...value, value: serializeExactDatePickerValue(nextValue, dateComponentProps.showTime) });
129
+ }}
130
+ disabled={disabled}
131
+ style={{ width: '100%' }}
132
+ />
133
+ );
134
+ }
135
+
136
+ if (value.kind === 'relative') {
137
+ return (
138
+ <Space.Compact style={{ width: '100%' }}>
139
+ <InputNumber
140
+ min={1}
141
+ precision={0}
142
+ value={value.amount}
143
+ disabled={disabled}
144
+ onChange={(amount) => onChange?.({ ...value, amount: typeof amount === 'number' ? amount : 1 })}
145
+ aria-label={ctx.t(value.direction === 'past' ? 'Past' : 'Next')}
146
+ style={{ flex: '1 1 auto', minWidth: 80 }}
147
+ />
148
+ <Select
149
+ value={value.unit}
150
+ disabled={disabled}
151
+ onChange={(unit: CtxDateRelativeUnit) => onChange?.({ ...value, unit })}
152
+ aria-label={ctx.t('Unit')}
153
+ style={{ flex: '0 0 130px', minWidth: 130 }}
154
+ options={[
155
+ { value: 'day', label: ctx.t('Day') },
156
+ { value: 'week', label: ctx.t('Calendar week') },
157
+ { value: 'month', label: ctx.t('Calendar Month') },
158
+ { value: 'year', label: ctx.t('Calendar Year') },
159
+ ]}
160
+ />
161
+ </Space.Compact>
162
+ );
163
+ }
164
+
165
+ return <Input value={ctx.t(PRESET_LABELS[value.preset] || value.preset)} readOnly disabled={disabled} />;
166
+ };
167
+
168
+ return (
169
+ <div style={{ display: 'flex', flex: 1, flexDirection: 'column', gap: 8, width: '100%', minWidth: 0, ...style }}>
170
+ {renderValueEditor()}
171
+ {!isDateLikeField && (
172
+ <DateFormatEditor
173
+ value={format}
174
+ placeholder={getDefaultDateVariableFormat(value)}
175
+ onChange={updateFormat}
176
+ disabled={disabled}
177
+ />
178
+ )}
179
+ </div>
180
+ );
181
+ };
@@ -0,0 +1,326 @@
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 {
11
+ isRunJSValue,
12
+ isVariableExpression,
13
+ parseCtxDateExpressionConfig,
14
+ parseValueToPath,
15
+ serializeCtxDateExpressionConfig,
16
+ tExpr,
17
+ VariableInput,
18
+ type CtxDateExpressionConfig,
19
+ type CtxDatePreset,
20
+ type MetaTreeNode,
21
+ type VariableInputProps,
22
+ } from '@nocobase/flow-engine';
23
+ import { dayjs } from '@nocobase/utils/client';
24
+ import type { Dayjs } from 'dayjs';
25
+ import React from 'react';
26
+ import { DateVariableEditor, getDefaultDateVariableFormat } from './DateVariableEditor';
27
+ import { normalizeDateVariableExactValue, type DateVariableComponentProps } from './dateValue';
28
+
29
+ const DATE_PRESET_NODES: Array<{ name: CtxDatePreset; title: string }> = [
30
+ { name: 'now', title: 'Now' },
31
+ { name: 'today', title: 'Today' },
32
+ { name: 'yesterday', title: 'Yesterday' },
33
+ { name: 'tomorrow', title: 'Tomorrow' },
34
+ { name: 'thisWeek', title: 'This Week' },
35
+ { name: 'lastWeek', title: 'Last Week' },
36
+ { name: 'nextWeek', title: 'Next Week' },
37
+ { name: 'thisMonth', title: 'This Month' },
38
+ { name: 'lastMonth', title: 'Last Month' },
39
+ { name: 'nextMonth', title: 'Next Month' },
40
+ { name: 'thisQuarter', title: 'This Quarter' },
41
+ { name: 'lastQuarter', title: 'Last Quarter' },
42
+ { name: 'nextQuarter', title: 'Next Quarter' },
43
+ { name: 'thisYear', title: 'This Year' },
44
+ { name: 'lastYear', title: 'Last Year' },
45
+ { name: 'nextYear', title: 'Next Year' },
46
+ ];
47
+
48
+ type ValueEditorComponent = React.ComponentType<{
49
+ value?: unknown;
50
+ onChange?: (value: unknown) => void;
51
+ style?: React.CSSProperties;
52
+ disabled?: boolean;
53
+ }>;
54
+
55
+ const DATE_VARIABLE_CONFIG_MARK = Symbol('field-value-date-variable-config');
56
+
57
+ type DateVariableEditConfig = CtxDateExpressionConfig & {
58
+ [DATE_VARIABLE_CONFIG_MARK]: 'editable' | 'restored';
59
+ };
60
+
61
+ export type FieldValueVariableInputProps = Omit<
62
+ VariableInputProps,
63
+ 'value' | 'onChange' | 'metaTree' | 'converters'
64
+ > & {
65
+ value?: unknown;
66
+ onChange: (value: unknown) => void;
67
+ baseMetaTree: MetaTreeNode[] | (() => MetaTreeNode[] | Promise<MetaTreeNode[]>);
68
+ constantComponent: ValueEditorComponent;
69
+ nullComponent: ValueEditorComponent;
70
+ runJSComponent: ValueEditorComponent;
71
+ isDateLikeField: boolean;
72
+ dateComponentProps: DateVariableComponentProps;
73
+ allowRunJS?: boolean;
74
+ converters?: VariableInputProps['converters'];
75
+ };
76
+
77
+ function createDateVariableEditConfig(
78
+ config: CtxDateExpressionConfig,
79
+ source: DateVariableEditConfig[typeof DATE_VARIABLE_CONFIG_MARK] = 'editable',
80
+ ): DateVariableEditConfig {
81
+ return { ...config, [DATE_VARIABLE_CONFIG_MARK]: source } as DateVariableEditConfig;
82
+ }
83
+
84
+ function isDateVariableEditConfig(value: unknown): value is DateVariableEditConfig {
85
+ if (!value || typeof value !== 'object' || Array.isArray(value)) return false;
86
+ const source = (value as Partial<DateVariableEditConfig>)[DATE_VARIABLE_CONFIG_MARK];
87
+ return source === 'editable' || source === 'restored';
88
+ }
89
+
90
+ function getDateNodeName(config: CtxDateExpressionConfig): string {
91
+ if (config.kind === 'exact') return 'exact';
92
+ if (config.kind === 'relative') return config.direction;
93
+ return config.preset;
94
+ }
95
+
96
+ function isDayjsValue(value: unknown): value is Dayjs {
97
+ return dayjs.isDayjs(value);
98
+ }
99
+
100
+ function createInitialDateConfig(
101
+ nodeName: string,
102
+ isDateLikeField: boolean,
103
+ componentProps: DateVariableComponentProps,
104
+ ): DateVariableEditConfig | undefined {
105
+ let config: CtxDateExpressionConfig | undefined;
106
+
107
+ if (nodeName === 'exact') {
108
+ const normalized = normalizeDateVariableExactValue(dayjs(), componentProps);
109
+ const value = isDayjsValue(normalized) ? normalized.format('YYYY-MM-DD') : String(normalized || '');
110
+ config = { kind: 'exact', value };
111
+ } else if (nodeName === 'past' || nodeName === 'next') {
112
+ config = { kind: 'relative', direction: nodeName, amount: 1, unit: 'day' };
113
+ } else if (
114
+ DATE_PRESET_NODES.some((item) => item.name === nodeName) &&
115
+ !(nodeName === 'now' && componentProps.exactNormalizeMode === 'date')
116
+ ) {
117
+ config = { kind: 'preset', preset: nodeName as CtxDatePreset };
118
+ }
119
+
120
+ if (!config) return undefined;
121
+ const initialConfig = isDateLikeField ? config : { ...config, format: getDefaultDateVariableFormat(config) };
122
+ return createDateVariableEditConfig(initialConfig);
123
+ }
124
+
125
+ function normalizeDateConfigForStore(
126
+ config: CtxDateExpressionConfig,
127
+ componentProps: DateVariableComponentProps,
128
+ omitFormat: boolean,
129
+ ): CtxDateExpressionConfig {
130
+ const rawFormat = typeof config.format === 'string' ? config.format : '';
131
+ const normalizedConfig = omitFormat
132
+ ? { ...config, format: undefined }
133
+ : { ...config, format: rawFormat.trim() ? rawFormat : getDefaultDateVariableFormat(config) };
134
+ if (normalizedConfig.kind !== 'exact') return normalizedConfig;
135
+ const normalized = normalizeDateVariableExactValue(normalizedConfig.value, componentProps);
136
+ if (Array.isArray(normalized)) {
137
+ return { ...normalizedConfig, value: [String(normalized[0]), String(normalized[1])] };
138
+ }
139
+ return { ...normalizedConfig, value: String(normalized || '') };
140
+ }
141
+
142
+ export const FieldValueVariableInput: React.FC<FieldValueVariableInputProps> = ({
143
+ value,
144
+ onChange,
145
+ baseMetaTree,
146
+ constantComponent: ConstantComponent,
147
+ nullComponent: NullComponent,
148
+ runJSComponent: RunJSComponent,
149
+ isDateLikeField,
150
+ dateComponentProps,
151
+ allowRunJS = true,
152
+ converters,
153
+ clearValue = '',
154
+ disabled = false,
155
+ ...variableInputProps
156
+ }) => {
157
+ const DateEditor = React.useMemo<ValueEditorComponent>(() => {
158
+ const Component: ValueEditorComponent = (props) => (
159
+ <DateVariableEditor
160
+ value={isDateVariableEditConfig(props.value) ? props.value : undefined}
161
+ onChange={(nextConfig) => props.onChange?.(createDateVariableEditConfig(nextConfig))}
162
+ isDateLikeField={isDateLikeField}
163
+ dateComponentProps={dateComponentProps}
164
+ style={props.style}
165
+ disabled={props.disabled}
166
+ />
167
+ );
168
+ return Component;
169
+ }, [dateComponentProps, isDateLikeField]);
170
+
171
+ const parsedDateConfig = parseCtxDateExpressionConfig(value);
172
+ const restoreLegacyNowForPureDate =
173
+ dateComponentProps.exactNormalizeMode === 'date' &&
174
+ parsedDateConfig?.kind === 'preset' &&
175
+ parsedDateConfig.preset === 'now';
176
+
177
+ const metaTree = React.useMemo<() => Promise<MetaTreeNode[]>>(() => {
178
+ return async () => {
179
+ const base = typeof baseMetaTree === 'function' ? await baseMetaTree() : baseMetaTree;
180
+ const presetNodes =
181
+ dateComponentProps.exactNormalizeMode === 'date' && !restoreLegacyNowForPureDate
182
+ ? DATE_PRESET_NODES.filter((item) => item.name !== 'now')
183
+ : DATE_PRESET_NODES;
184
+ const dateChildren: MetaTreeNode[] = [
185
+ {
186
+ title: tExpr('Exact day'),
187
+ name: 'exact',
188
+ type: 'date',
189
+ paths: ['date', 'exact'],
190
+ render: (props) => <DateEditor {...props} />,
191
+ },
192
+ {
193
+ title: tExpr('Past'),
194
+ name: 'past',
195
+ type: 'date',
196
+ paths: ['date', 'past'],
197
+ render: (props) => <DateEditor {...props} />,
198
+ },
199
+ {
200
+ title: tExpr('Next'),
201
+ name: 'next',
202
+ type: 'date',
203
+ paths: ['date', 'next'],
204
+ render: (props) => <DateEditor {...props} />,
205
+ },
206
+ ...presetNodes.map<MetaTreeNode>(({ name, title }) => ({
207
+ title: tExpr(title),
208
+ name,
209
+ type: 'date',
210
+ paths: ['date', name],
211
+ ...(name === 'now' && dateComponentProps.exactNormalizeMode === 'date'
212
+ ? { disabled: true, selectable: false }
213
+ : {}),
214
+ render: (props) => <DateEditor {...props} />,
215
+ })),
216
+ ];
217
+
218
+ const specialNodes: MetaTreeNode[] = [
219
+ {
220
+ title: tExpr('Constant'),
221
+ name: 'constant',
222
+ type: 'string',
223
+ paths: ['constant'],
224
+ render: (props) => <ConstantComponent {...props} />,
225
+ },
226
+ {
227
+ title: tExpr('Null'),
228
+ name: 'null',
229
+ type: 'object',
230
+ paths: ['null'],
231
+ render: (props) => <NullComponent {...props} />,
232
+ },
233
+ {
234
+ title: tExpr('Date'),
235
+ name: 'date',
236
+ type: 'date',
237
+ paths: ['date'],
238
+ selectable: false,
239
+ children: dateChildren,
240
+ },
241
+ ...(allowRunJS
242
+ ? [
243
+ {
244
+ title: tExpr('RunJS'),
245
+ name: 'runjs',
246
+ type: 'object',
247
+ paths: ['runjs'],
248
+ render: (props) => <RunJSComponent {...props} />,
249
+ } satisfies MetaTreeNode,
250
+ ]
251
+ : []),
252
+ ];
253
+ return [...specialNodes, ...(Array.isArray(base) ? base : [])];
254
+ };
255
+ }, [
256
+ ConstantComponent,
257
+ DateEditor,
258
+ NullComponent,
259
+ RunJSComponent,
260
+ allowRunJS,
261
+ baseMetaTree,
262
+ dateComponentProps.exactNormalizeMode,
263
+ restoreLegacyNowForPureDate,
264
+ ]);
265
+
266
+ const displayValue = parsedDateConfig ? createDateVariableEditConfig(parsedDateConfig, 'restored') : value;
267
+
268
+ const handleChange = React.useCallback(
269
+ (nextValue: unknown) => {
270
+ if (disabled) return;
271
+ if (!isDateVariableEditConfig(nextValue)) {
272
+ onChange(nextValue);
273
+ return;
274
+ }
275
+ if (nextValue[DATE_VARIABLE_CONFIG_MARK] === 'restored') return;
276
+ const normalized = normalizeDateConfigForStore(nextValue, dateComponentProps, isDateLikeField);
277
+ onChange(serializeCtxDateExpressionConfig(normalized) || '');
278
+ },
279
+ [dateComponentProps, disabled, isDateLikeField, onChange],
280
+ );
281
+
282
+ return (
283
+ <VariableInput
284
+ {...variableInputProps}
285
+ value={displayValue}
286
+ onChange={handleChange}
287
+ metaTree={metaTree}
288
+ clearValue={clearValue}
289
+ disabled={disabled}
290
+ converters={{
291
+ renderInputComponent: (meta) => {
292
+ const external = converters?.renderInputComponent?.(meta ?? null);
293
+ if (external) return external;
294
+ const firstPath = meta?.paths?.[0];
295
+ if (firstPath === 'constant') return ConstantComponent;
296
+ if (firstPath === 'null') return NullComponent;
297
+ if (firstPath === 'date') return DateEditor;
298
+ if (allowRunJS && firstPath === 'runjs') return RunJSComponent;
299
+ return null;
300
+ },
301
+ resolveValueFromPath: (item) => {
302
+ const external = converters?.resolveValueFromPath?.(item);
303
+ if (external !== undefined) return external;
304
+ const firstPath = item?.paths?.[0];
305
+ if (firstPath === 'constant') return '';
306
+ if (firstPath === 'null') return null;
307
+ if (firstPath === 'date') {
308
+ return createInitialDateConfig(item.paths[1], isDateLikeField, dateComponentProps);
309
+ }
310
+ if (allowRunJS && firstPath === 'runjs') return { code: '', version: 'v2' };
311
+ return undefined;
312
+ },
313
+ resolvePathFromValue: (currentValue) => {
314
+ const external = converters?.resolvePathFromValue?.(currentValue);
315
+ if (external !== undefined) return external;
316
+ if (currentValue === null) return ['null'];
317
+ if (allowRunJS && isRunJSValue(currentValue)) return ['runjs'];
318
+ if (isDateVariableEditConfig(currentValue)) return ['date', getDateNodeName(currentValue)];
319
+ return typeof currentValue === 'string' && isVariableExpression(currentValue)
320
+ ? parseValueToPath(currentValue)
321
+ : ['constant'];
322
+ },
323
+ }}
324
+ />
325
+ );
326
+ };