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

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
@@ -8,7 +8,8 @@
8
8
  */
9
9
 
10
10
  import { Html5Qrcode, Html5QrcodeScannerState, Html5QrcodeSupportedFormats } from 'html5-qrcode';
11
- import { useCallback, useEffect, useState } from 'react';
11
+ import jsQR from 'jsqr';
12
+ import { useCallback, useEffect, useRef, useState } from 'react';
12
13
  import type { CodeFormatsToSupport } from './types';
13
14
 
14
15
  type ScannerSize = {
@@ -20,12 +21,44 @@ type UseCodeScannerOptions = {
20
21
  enabled: boolean;
21
22
  elementId: string;
22
23
  formatsToSupport?: CodeFormatsToSupport;
23
- scanBoxSize?: ScannerSize;
24
24
  onScannerSizeChanged?: (size: ScannerSize) => void;
25
25
  onScanSuccess: (text: string) => void;
26
26
  onScanFailure?: () => void;
27
+ onCameraStartFailure?: (error: unknown) => void;
27
28
  };
28
29
 
30
+ type ImageDataVariant = {
31
+ imageData: ImageData;
32
+ maxSize: number;
33
+ };
34
+
35
+ type JsQRImageTransform = {
36
+ contrast?: number;
37
+ threshold?: number;
38
+ };
39
+
40
+ type FocusMediaTrackCapabilities = MediaTrackCapabilities & {
41
+ focusMode?: string[];
42
+ };
43
+
44
+ type FocusMediaTrackConstraintSet = MediaTrackConstraintSet & {
45
+ focusMode?: string;
46
+ };
47
+
48
+ const QR_SCAN_IMAGE_SIZES = [3200, 2400, 1600, 1000];
49
+ const LIVE_QR_SCAN_INTERVAL = 120;
50
+ const LIVE_QR_SCAN_MAX_WIDTH = 960;
51
+ const LIVE_QR_SCAN_MAX_HEIGHT = 540;
52
+ const QR_SCAN_IMAGE_TRANSFORMS: JsQRImageTransform[] = [
53
+ {},
54
+ { contrast: 3, threshold: 105 },
55
+ { contrast: 2, threshold: 105 },
56
+ { contrast: 3, threshold: 120 },
57
+ { contrast: 2, threshold: 120 },
58
+ { contrast: 3, threshold: 90 },
59
+ { contrast: 4, threshold: 105 },
60
+ ];
61
+
29
62
  export const DEFAULT_CODE_FORMATS: CodeFormatsToSupport = [
30
63
  Html5QrcodeSupportedFormats.QR_CODE,
31
64
  Html5QrcodeSupportedFormats.CODE_128,
@@ -43,8 +76,79 @@ export const DEFAULT_CODE_FORMATS: CodeFormatsToSupport = [
43
76
 
44
77
  export function getCodeScanBoxSize(width: number, height: number) {
45
78
  return {
46
- width: Math.floor(Math.min(width * 0.82, 520)),
47
- height: Math.floor(Math.min(height * 0.32, 240)),
79
+ width: Math.floor(Math.min((width * 90) / 100, 1152)),
80
+ height: Math.floor(Math.min((height * 70) / 100, 540)),
81
+ };
82
+ }
83
+
84
+ export function scanQrVideoFrame(video: HTMLVideoElement, canvas: HTMLCanvasElement) {
85
+ if (!video.videoWidth || !video.videoHeight || video.readyState < HTMLMediaElement.HAVE_CURRENT_DATA) {
86
+ return;
87
+ }
88
+
89
+ const viewfinderWidth = video.clientWidth || video.videoWidth;
90
+ const viewfinderHeight = video.clientHeight || video.videoHeight;
91
+ const scanBoxSize = getCodeScanBoxSize(viewfinderWidth, viewfinderHeight);
92
+ const sourceWidth = Math.min(video.videoWidth, Math.floor(scanBoxSize.width * (video.videoWidth / viewfinderWidth)));
93
+ const sourceHeight = Math.min(
94
+ video.videoHeight,
95
+ Math.floor(scanBoxSize.height * (video.videoHeight / viewfinderHeight)),
96
+ );
97
+ const sourceX = Math.floor((video.videoWidth - sourceWidth) / 2);
98
+ const sourceY = Math.floor((video.videoHeight - sourceHeight) / 2);
99
+ const targetScale = Math.min(1, LIVE_QR_SCAN_MAX_WIDTH / sourceWidth, LIVE_QR_SCAN_MAX_HEIGHT / sourceHeight);
100
+ const targetWidth = Math.max(1, Math.floor(sourceWidth * targetScale));
101
+ const targetHeight = Math.max(1, Math.floor(sourceHeight * targetScale));
102
+ if (canvas.width !== targetWidth) {
103
+ canvas.width = targetWidth;
104
+ }
105
+ if (canvas.height !== targetHeight) {
106
+ canvas.height = targetHeight;
107
+ }
108
+ const context = canvas.getContext('2d', { willReadFrequently: true });
109
+ if (!context) {
110
+ return;
111
+ }
112
+
113
+ context.drawImage(video, sourceX, sourceY, sourceWidth, sourceHeight, 0, 0, targetWidth, targetHeight);
114
+ const imageData = context.getImageData(0, 0, targetWidth, targetHeight);
115
+ return jsQR(imageData.data, imageData.width, imageData.height, { inversionAttempts: 'dontInvert' })?.data;
116
+ }
117
+
118
+ function startLiveQrScan(elementId: string, onScanSuccess: (text: string) => void) {
119
+ const canvas = document.createElement('canvas');
120
+ let timer: number | undefined;
121
+ let stopped = false;
122
+
123
+ const scan = () => {
124
+ if (stopped) {
125
+ return;
126
+ }
127
+ const video = document.getElementById(elementId)?.querySelector('video');
128
+ if (video) {
129
+ const decodedText = scanQrVideoFrame(video, canvas);
130
+ if (decodedText) {
131
+ stopped = true;
132
+ onScanSuccess(decodedText);
133
+ return;
134
+ }
135
+ }
136
+ timer = window.setTimeout(scan, LIVE_QR_SCAN_INTERVAL);
137
+ };
138
+
139
+ timer = window.setTimeout(scan, 0);
140
+ return () => {
141
+ stopped = true;
142
+ if (timer !== undefined) {
143
+ window.clearTimeout(timer);
144
+ }
145
+ };
146
+ }
147
+
148
+ function clampCodeScanBoxSize(scanBoxSize: ScannerSize, width: number, height: number) {
149
+ return {
150
+ width: Math.min(scanBoxSize.width, width),
151
+ height: Math.min(scanBoxSize.height, height),
48
152
  };
49
153
  }
50
154
 
@@ -62,39 +166,238 @@ async function stopScanner(scanner?: Html5Qrcode, options: { clear?: boolean } =
62
166
  }
63
167
  }
64
168
 
169
+ async function enableContinuousFocus(scanner: Html5Qrcode) {
170
+ try {
171
+ const capabilities = scanner.getRunningTrackCapabilities() as FocusMediaTrackCapabilities;
172
+ if (!capabilities.focusMode?.includes('continuous')) {
173
+ return;
174
+ }
175
+ const focusConstraints: FocusMediaTrackConstraintSet = { focusMode: 'continuous' };
176
+ await scanner.applyVideoConstraints({ advanced: [focusConstraints] });
177
+ } catch {
178
+ // Some browsers expose incomplete camera capability APIs. Scanning should continue without explicit focus control.
179
+ }
180
+ }
181
+
182
+ function isSafariBrowser() {
183
+ const { userAgent, vendor } = navigator;
184
+ return /Apple/i.test(vendor) && /Safari/i.test(userAgent) && !/CriOS|FxiOS|EdgiOS|Chrome/i.test(userAgent);
185
+ }
186
+
187
+ function loadImage(file: File) {
188
+ return new Promise<HTMLImageElement>((resolve, reject) => {
189
+ const image = new Image();
190
+ const url = URL.createObjectURL(file);
191
+ const cleanup = () => URL.revokeObjectURL(url);
192
+
193
+ image.onload = () => {
194
+ cleanup();
195
+ resolve(image);
196
+ };
197
+ image.onerror = (event) => {
198
+ cleanup();
199
+ reject(event);
200
+ };
201
+ image.onabort = (event) => {
202
+ cleanup();
203
+ reject(event);
204
+ };
205
+ image.src = url;
206
+ });
207
+ }
208
+
209
+ function getScanImageSizes(naturalWidth: number, naturalHeight: number) {
210
+ const maxSize = Math.max(naturalWidth, naturalHeight);
211
+ const cappedMaxSize = Math.min(maxSize, QR_SCAN_IMAGE_SIZES[0]);
212
+ return Array.from(new Set([cappedMaxSize, ...QR_SCAN_IMAGE_SIZES.filter((size) => size < cappedMaxSize)])).sort(
213
+ (a, b) => b - a,
214
+ );
215
+ }
216
+
217
+ function getImageDataFromImage(image: HTMLImageElement, maxSize: number): ImageDataVariant | undefined {
218
+ const naturalWidth = image.naturalWidth || image.width;
219
+ const naturalHeight = image.naturalHeight || image.height;
220
+
221
+ const scale = Math.min(1, maxSize / Math.max(naturalWidth, naturalHeight));
222
+ const width = Math.max(1, Math.round(naturalWidth * scale));
223
+ const height = Math.max(1, Math.round(naturalHeight * scale));
224
+ const canvas = document.createElement('canvas');
225
+ canvas.width = width;
226
+ canvas.height = height;
227
+ const context = canvas.getContext('2d');
228
+ if (!context) {
229
+ return;
230
+ }
231
+
232
+ context.drawImage(image, 0, 0, width, height);
233
+ return {
234
+ imageData: context.getImageData(0, 0, width, height),
235
+ maxSize,
236
+ };
237
+ }
238
+
239
+ async function getImageDataVariants(file: File) {
240
+ const image = await loadImage(file);
241
+ const naturalWidth = image.naturalWidth || image.width;
242
+ const naturalHeight = image.naturalHeight || image.height;
243
+
244
+ if (!naturalWidth || !naturalHeight) {
245
+ return;
246
+ }
247
+
248
+ return getScanImageSizes(naturalWidth, naturalHeight)
249
+ .map((maxSize) => getImageDataFromImage(image, maxSize))
250
+ .filter((variant): variant is ImageDataVariant => !!variant);
251
+ }
252
+
253
+ function getTransformedImageData(imageData: ImageData, transform: JsQRImageTransform) {
254
+ const { contrast = 1, threshold } = transform;
255
+ if (contrast === 1 && threshold == null) {
256
+ return imageData.data;
257
+ }
258
+
259
+ const data = new Uint8ClampedArray(imageData.data);
260
+ for (let index = 0; index < data.length; index += 4) {
261
+ let luminance = data[index] * 0.299 + data[index + 1] * 0.587 + data[index + 2] * 0.114;
262
+ luminance = Math.max(0, Math.min(255, (luminance - 128) * contrast + 128));
263
+ if (threshold != null) {
264
+ luminance = luminance < threshold ? 0 : 255;
265
+ }
266
+ data[index] = luminance;
267
+ data[index + 1] = luminance;
268
+ data[index + 2] = luminance;
269
+ }
270
+ return data;
271
+ }
272
+
273
+ async function scanFileWithJsQR(file: File, formatsToSupport?: CodeFormatsToSupport) {
274
+ const formats = formatsToSupport?.length ? formatsToSupport : DEFAULT_CODE_FORMATS;
275
+ if (!formats.includes(Html5QrcodeSupportedFormats.QR_CODE)) {
276
+ throw new Error('QR_CODE is not included in the requested formats');
277
+ }
278
+
279
+ const variants = await getImageDataVariants(file);
280
+ if (!variants?.length) {
281
+ throw new Error('Failed to prepare uploaded image for QR decoding');
282
+ }
283
+
284
+ for (const { imageData } of variants) {
285
+ for (const transform of QR_SCAN_IMAGE_TRANSFORMS) {
286
+ const data = getTransformedImageData(imageData, transform);
287
+ const qrCode = jsQR(data, imageData.width, imageData.height, { inversionAttempts: 'attemptBoth' });
288
+ if (qrCode?.data) {
289
+ return qrCode.data;
290
+ }
291
+ }
292
+ }
293
+
294
+ throw new Error('No QR code decoded by jsQR');
295
+ }
296
+
297
+ function shouldScanQrWithJsQR(formatsToSupport?: CodeFormatsToSupport) {
298
+ const formats = formatsToSupport?.length ? formatsToSupport : DEFAULT_CODE_FORMATS;
299
+ return formats.includes(Html5QrcodeSupportedFormats.QR_CODE);
300
+ }
301
+
65
302
  export function useCodeScanner({
66
303
  enabled,
67
304
  elementId,
68
305
  formatsToSupport,
69
- scanBoxSize,
70
306
  onScannerSizeChanged,
71
307
  onScanSuccess,
72
308
  onScanFailure,
309
+ onCameraStartFailure,
73
310
  }: UseCodeScannerOptions) {
74
311
  const [scanner, setScanner] = useState<Html5Qrcode>();
312
+ const liveQrScanStopRef = useRef<() => void>();
313
+ const scanSucceededRef = useRef(false);
314
+ const scanSessionRef = useRef(0);
315
+
316
+ const stopLiveQrScan = useCallback(() => {
317
+ liveQrScanStopRef.current?.();
318
+ liveQrScanStopRef.current = undefined;
319
+ }, []);
320
+
321
+ const cancelActiveScan = useCallback(() => {
322
+ scanSessionRef.current += 1;
323
+ stopLiveQrScan();
324
+ }, [stopLiveQrScan]);
325
+
326
+ const reportScanSuccess = useCallback(
327
+ (text: string) => {
328
+ if (scanSucceededRef.current) {
329
+ return;
330
+ }
331
+ scanSucceededRef.current = true;
332
+ stopLiveQrScan();
333
+ onScanSuccess(text);
334
+ },
335
+ [onScanSuccess, stopLiveQrScan],
336
+ );
75
337
 
76
338
  const startScanCamera = useCallback(
77
339
  async (scannerInstance: Html5Qrcode) => {
78
- await scannerInstance.start(
79
- { facingMode: 'environment' },
80
- {
81
- fps: 10,
82
- qrbox(width, height) {
83
- onScannerSizeChanged?.({ width, height });
84
- return scanBoxSize ?? getCodeScanBoxSize(width, height);
340
+ cancelActiveScan();
341
+ const scanSession = scanSessionRef.current;
342
+ scanSucceededRef.current = false;
343
+ try {
344
+ await scannerInstance.start(
345
+ { facingMode: 'environment' },
346
+ {
347
+ fps: 8,
348
+ disableFlip: false,
349
+ videoConstraints: {
350
+ facingMode: { ideal: 'environment' },
351
+ width: { ideal: 1920 },
352
+ height: { ideal: 1080 },
353
+ frameRate: { ideal: 30 },
354
+ },
355
+ qrbox(width, height) {
356
+ onScannerSizeChanged?.({ width, height });
357
+ return clampCodeScanBoxSize(getCodeScanBoxSize(width, height), width, height);
358
+ },
85
359
  },
86
- },
87
- (decodedText) => {
88
- onScanSuccess(decodedText);
89
- },
90
- undefined,
91
- );
360
+ (decodedText) => {
361
+ reportScanSuccess(decodedText);
362
+ },
363
+ undefined,
364
+ );
365
+ } catch (error) {
366
+ if (scanSession !== scanSessionRef.current) {
367
+ return;
368
+ }
369
+ throw error;
370
+ }
371
+ if (scanSession !== scanSessionRef.current) {
372
+ try {
373
+ await stopScanner(scannerInstance, { clear: true });
374
+ } catch {
375
+ // The scanner may already have been cleared by the canceled session cleanup.
376
+ }
377
+ return;
378
+ }
379
+ if (shouldScanQrWithJsQR(formatsToSupport)) {
380
+ liveQrScanStopRef.current = startLiveQrScan(elementId, reportScanSuccess);
381
+ }
382
+ await enableContinuousFocus(scannerInstance);
92
383
  },
93
- [onScanSuccess, onScannerSizeChanged, scanBoxSize],
384
+ [cancelActiveScan, elementId, formatsToSupport, onScannerSizeChanged, reportScanSuccess],
94
385
  );
95
386
 
96
387
  const startScanFile = useCallback(
97
388
  async (file: File) => {
389
+ cancelActiveScan();
390
+ scanSucceededRef.current = false;
391
+ if (isSafariBrowser() && shouldScanQrWithJsQR(formatsToSupport)) {
392
+ try {
393
+ const decodedText = await scanFileWithJsQR(file, formatsToSupport);
394
+ reportScanSuccess(decodedText);
395
+ return;
396
+ } catch {
397
+ // Fall through to html5-qrcode so barcode uploads still work in Safari.
398
+ }
399
+ }
400
+
98
401
  if (!scanner) {
99
402
  return;
100
403
  }
@@ -102,13 +405,13 @@ export function useCodeScanner({
102
405
  await stopScanner(scanner);
103
406
  try {
104
407
  const result = await scanner.scanFileV2(file, false);
105
- onScanSuccess(result.decodedText);
106
- } catch (error) {
408
+ reportScanSuccess(result.decodedText);
409
+ } catch {
107
410
  onScanFailure?.();
108
411
  await startScanCamera(scanner);
109
412
  }
110
413
  },
111
- [onScanFailure, onScanSuccess, scanner, startScanCamera],
414
+ [cancelActiveScan, formatsToSupport, onScanFailure, reportScanSuccess, scanner, startScanCamera],
112
415
  );
113
416
 
114
417
  useEffect(() => {
@@ -121,14 +424,19 @@ export function useCodeScanner({
121
424
  verbose: false,
122
425
  });
123
426
  setScanner(scannerInstance);
124
- startScanCamera(scannerInstance).catch(() => {
427
+ startScanCamera(scannerInstance).catch((error: unknown) => {
428
+ if (onCameraStartFailure) {
429
+ onCameraStartFailure(error);
430
+ return;
431
+ }
125
432
  onScanFailure?.();
126
433
  });
127
434
 
128
435
  return () => {
436
+ cancelActiveScan();
129
437
  stopScanner(scannerInstance, { clear: true }).catch(() => undefined);
130
438
  };
131
- }, [elementId, enabled, formatsToSupport, onScanFailure, startScanCamera]);
439
+ }, [cancelActiveScan, elementId, enabled, formatsToSupport, onCameraStartFailure, onScanFailure, startScanCamera]);
132
440
 
133
441
  return {
134
442
  startScanFile,