@nocobase/client-v2 2.1.0-beta.37 → 2.1.0-beta.40

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (253) hide show
  1. package/es/Application.d.ts +7 -0
  2. package/es/BaseApplication.d.ts +3 -0
  3. package/es/RouterManager.d.ts +1 -0
  4. package/es/collection-field-interface/CollectionFieldInterface.d.ts +51 -15
  5. package/es/collection-field-interface/CollectionFieldInterfaceManager.d.ts +82 -3
  6. package/es/collection-manager/field-configure.d.ts +80 -0
  7. package/es/collection-manager/field-validation.d.ts +43 -0
  8. package/es/collection-manager/filter-operators/index.d.ts +46 -0
  9. package/es/collection-manager/filter-operators/operators.d.ts +30 -0
  10. package/es/collection-manager/interfaces/checkbox.d.ts +1 -41
  11. package/es/collection-manager/interfaces/checkboxGroup.d.ts +12 -44
  12. package/es/collection-manager/interfaces/collection.d.ts +12 -51
  13. package/es/collection-manager/interfaces/color.d.ts +1 -16
  14. package/es/collection-manager/interfaces/createdAt.d.ts +1 -44
  15. package/es/collection-manager/interfaces/createdBy.d.ts +0 -4
  16. package/es/collection-manager/interfaces/dateOnly.d.ts +7 -44
  17. package/es/collection-manager/interfaces/datetime.d.ts +1 -44
  18. package/es/collection-manager/interfaces/datetimeNoTz.d.ts +1 -44
  19. package/es/collection-manager/interfaces/email.d.ts +1 -29
  20. package/es/collection-manager/interfaces/id.d.ts +1 -16
  21. package/es/collection-manager/interfaces/index.d.ts +2 -3
  22. package/es/collection-manager/interfaces/input.d.ts +1 -102
  23. package/es/collection-manager/interfaces/integer.d.ts +1 -95
  24. package/es/collection-manager/interfaces/json.d.ts +16 -7
  25. package/es/collection-manager/interfaces/m2m.d.ts +11 -19
  26. package/es/collection-manager/interfaces/m2o.d.ts +11 -19
  27. package/es/collection-manager/interfaces/markdown.d.ts +1 -63
  28. package/es/collection-manager/interfaces/multipleSelect.d.ts +12 -44
  29. package/es/collection-manager/interfaces/nanoid.d.ts +1 -34
  30. package/es/collection-manager/interfaces/number.d.ts +1 -87
  31. package/es/collection-manager/interfaces/o2m.d.ts +12 -24
  32. package/es/collection-manager/interfaces/obo.d.ts +207 -0
  33. package/es/collection-manager/interfaces/oho.d.ts +207 -0
  34. package/es/collection-manager/interfaces/password.d.ts +1 -56
  35. package/es/collection-manager/interfaces/percent.d.ts +1 -84
  36. package/es/collection-manager/interfaces/phone.d.ts +1 -25
  37. package/es/collection-manager/interfaces/properties/index.d.ts +0 -28
  38. package/es/collection-manager/interfaces/radioGroup.d.ts +1 -29
  39. package/es/collection-manager/interfaces/richText.d.ts +1 -63
  40. package/es/collection-manager/interfaces/select.d.ts +12 -44
  41. package/es/collection-manager/interfaces/snowflake-id.d.ts +1 -34
  42. package/es/collection-manager/interfaces/tableoid.d.ts +1 -10
  43. package/es/collection-manager/interfaces/textarea.d.ts +1 -51
  44. package/es/collection-manager/interfaces/time.d.ts +1 -16
  45. package/es/collection-manager/interfaces/types.d.ts +3 -12
  46. package/es/collection-manager/interfaces/unixTimestamp.d.ts +1 -44
  47. package/es/collection-manager/interfaces/updatedAt.d.ts +1 -44
  48. package/es/collection-manager/interfaces/updatedBy.d.ts +0 -4
  49. package/es/collection-manager/interfaces/url.d.ts +1 -20
  50. package/es/collection-manager/interfaces/uuid.d.ts +1 -34
  51. package/es/collection-manager/template-fields.d.ts +53 -0
  52. package/es/components/KeepAlive.d.ts +22 -0
  53. package/es/components/RouterBridge.d.ts +9 -0
  54. package/es/components/form/VariableInput.d.ts +53 -2
  55. package/es/components/form/filter/CollectionFilter.d.ts +9 -1
  56. package/es/components/form/filter/CollectionFilterItem.d.ts +10 -2
  57. package/es/data-source/ExtendCollectionsProvider.d.ts +28 -2
  58. package/es/flow/FlowPage.d.ts +2 -1
  59. package/es/flow/admin-shell/AdminLayoutRouteCoordinator.d.ts +8 -40
  60. package/es/flow/admin-shell/BaseLayoutModel.d.ts +89 -0
  61. package/es/flow/admin-shell/BaseLayoutRouteCoordinator.d.ts +74 -0
  62. package/es/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.d.ts +12 -0
  63. package/es/flow/admin-shell/admin-layout/AdminLayoutModel.d.ts +7 -92
  64. package/es/flow/admin-shell/admin-layout/index.d.ts +2 -0
  65. package/es/flow/admin-shell/useAdminLayoutRoutePage.d.ts +2 -2
  66. package/es/flow/admin-shell/useLayoutRoutePage.d.ts +23 -0
  67. package/es/flow/components/FlowRoute.d.ts +10 -1
  68. package/es/flow/components/filter/useFilterOptions.d.ts +13 -1
  69. package/es/flow/index.d.ts +4 -0
  70. package/es/flow/models/base/PageModel/PageModel.d.ts +3 -1
  71. package/es/flow/models/blocks/assign-form/assignFieldValuesFlow.d.ts +84 -0
  72. package/es/flow/models/blocks/assign-form/index.d.ts +1 -0
  73. package/es/flow/models/blocks/form/FormActionGroupModel.d.ts +1 -0
  74. package/es/flow/models/blocks/form/FormActionModel.d.ts +9 -2
  75. package/es/flow/models/blocks/table/TableBlockModel.d.ts +10 -0
  76. package/es/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.d.ts +1 -1
  77. package/es/index.d.ts +5 -0
  78. package/es/index.mjs +470 -460
  79. package/es/layout-manager/LayoutContentRoute.d.ts +14 -0
  80. package/es/layout-manager/LayoutManager.d.ts +22 -0
  81. package/es/layout-manager/LayoutRoute.d.ts +14 -0
  82. package/es/layout-manager/index.d.ts +13 -0
  83. package/es/layout-manager/types.d.ts +20 -0
  84. package/es/layout-manager/utils.d.ts +14 -0
  85. package/es/settings-center/index.d.ts +1 -1
  86. package/es/settings-center/plugin-manager/BulkEnableButton.d.ts +15 -0
  87. package/es/settings-center/plugin-manager/PluginCard.d.ts +15 -0
  88. package/es/settings-center/plugin-manager/PluginDetail.d.ts +16 -0
  89. package/es/settings-center/{PluginManagerPage.d.ts → plugin-manager/index.d.ts} +1 -7
  90. package/es/settings-center/plugin-manager/types.d.ts +34 -0
  91. package/lib/index.js +470 -460
  92. package/package.json +8 -7
  93. package/src/Application.tsx +51 -12
  94. package/src/BaseApplication.tsx +6 -0
  95. package/src/PluginSettingsManager.ts +1 -1
  96. package/src/RouterManager.tsx +17 -1
  97. package/src/__tests__/PluginSettingsManager.test.ts +41 -2
  98. package/src/__tests__/app.test.tsx +8 -1
  99. package/src/__tests__/globalDeps.test.ts +1 -0
  100. package/src/__tests__/nocobase-buildin-plugin-auth.test.tsx +45 -2
  101. package/src/__tests__/plugin-manager.test.tsx +177 -0
  102. package/src/__tests__/settings-center.test.tsx +24 -2
  103. package/src/collection-field-interface/CollectionFieldInterface.ts +71 -77
  104. package/src/collection-field-interface/CollectionFieldInterfaceManager.ts +201 -4
  105. package/src/collection-manager/field-configure.ts +548 -0
  106. package/src/collection-manager/field-validation.ts +195 -0
  107. package/src/collection-manager/filter-operators/index.ts +176 -0
  108. package/src/collection-manager/{interfaces/properties → filter-operators}/operators.ts +24 -13
  109. package/src/collection-manager/interfaces/checkbox.ts +2 -9
  110. package/src/collection-manager/interfaces/checkboxGroup.ts +2 -10
  111. package/src/collection-manager/interfaces/collection.ts +2 -15
  112. package/src/collection-manager/interfaces/color.ts +2 -2
  113. package/src/collection-manager/interfaces/createdAt.ts +2 -2
  114. package/src/collection-manager/interfaces/createdBy.ts +1 -12
  115. package/src/collection-manager/interfaces/dateOnly.ts +8 -2
  116. package/src/collection-manager/interfaces/datetime.ts +2 -2
  117. package/src/collection-manager/interfaces/datetimeNoTz.ts +2 -2
  118. package/src/collection-manager/interfaces/email.ts +2 -9
  119. package/src/collection-manager/interfaces/id.ts +1 -2
  120. package/src/collection-manager/interfaces/index.ts +2 -3
  121. package/src/collection-manager/interfaces/input.ts +2 -133
  122. package/src/collection-manager/interfaces/integer.ts +2 -71
  123. package/src/collection-manager/interfaces/json.tsx +17 -11
  124. package/src/collection-manager/interfaces/m2m.tsx +0 -21
  125. package/src/collection-manager/interfaces/m2o.tsx +0 -22
  126. package/src/collection-manager/interfaces/markdown.ts +2 -51
  127. package/src/collection-manager/interfaces/multipleSelect.ts +2 -14
  128. package/src/collection-manager/interfaces/nanoid.ts +2 -2
  129. package/src/collection-manager/interfaces/number.ts +2 -85
  130. package/src/collection-manager/interfaces/o2m.tsx +1 -22
  131. package/src/collection-manager/interfaces/obo.tsx +145 -0
  132. package/src/collection-manager/interfaces/oho.tsx +145 -0
  133. package/src/collection-manager/interfaces/password.ts +2 -44
  134. package/src/collection-manager/interfaces/percent.ts +2 -74
  135. package/src/collection-manager/interfaces/phone.ts +2 -2
  136. package/src/collection-manager/interfaces/properties/index.ts +0 -133
  137. package/src/collection-manager/interfaces/radioGroup.ts +2 -2
  138. package/src/collection-manager/interfaces/richText.ts +2 -51
  139. package/src/collection-manager/interfaces/select.ts +2 -14
  140. package/src/collection-manager/interfaces/snowflake-id.ts +2 -2
  141. package/src/collection-manager/interfaces/tableoid.ts +1 -2
  142. package/src/collection-manager/interfaces/textarea.ts +2 -51
  143. package/src/collection-manager/interfaces/time.ts +2 -2
  144. package/src/collection-manager/interfaces/types.ts +4 -12
  145. package/src/collection-manager/interfaces/unixTimestamp.tsx +2 -2
  146. package/src/collection-manager/interfaces/updatedAt.ts +2 -2
  147. package/src/collection-manager/interfaces/updatedBy.ts +1 -12
  148. package/src/collection-manager/interfaces/url.ts +2 -4
  149. package/src/collection-manager/interfaces/uuid.ts +2 -2
  150. package/src/collection-manager/template-fields.ts +109 -0
  151. package/src/components/KeepAlive.tsx +131 -0
  152. package/src/components/README.md +1 -0
  153. package/src/components/README.zh-CN.md +1 -0
  154. package/src/components/RouterBridge.tsx +28 -4
  155. package/src/components/__tests__/KeepAlive.test.tsx +63 -0
  156. package/src/components/__tests__/RouterBridge.test.tsx +27 -0
  157. package/src/components/form/VariableInput.tsx +101 -28
  158. package/src/components/form/__tests__/VariableInput.test.ts +85 -0
  159. package/src/components/form/filter/CollectionFilter.tsx +11 -1
  160. package/src/components/form/filter/CollectionFilterItem.tsx +12 -4
  161. package/src/components/form/filter/__tests__/CollectionFilterItem.test.tsx +42 -0
  162. package/src/components/form/filter/useFilterActionProps.ts +7 -4
  163. package/src/data-source/ExtendCollectionsProvider.tsx +94 -20
  164. package/src/data-source/__tests__/ExtendCollectionsProvider.test.tsx +264 -0
  165. package/src/flow/FlowPage.tsx +35 -7
  166. package/src/flow/__tests__/FlowPage.test.tsx +79 -0
  167. package/src/flow/__tests__/FlowRoute.test.tsx +529 -2
  168. package/src/flow/actions/__tests__/linkageRules.subFormSetFieldProps.test.ts +191 -0
  169. package/src/flow/actions/__tests__/openView.subModelKey.test.tsx +33 -0
  170. package/src/flow/actions/aclCheck.tsx +4 -0
  171. package/src/flow/actions/aclCheckRefresh.tsx +4 -0
  172. package/src/flow/actions/dateTimeFormat.tsx +12 -8
  173. package/src/flow/actions/linkageRules.tsx +122 -0
  174. package/src/flow/actions/openView.tsx +28 -4
  175. package/src/flow/admin-shell/AdminLayoutRouteCoordinator.ts +11 -329
  176. package/src/flow/admin-shell/BaseLayoutModel.tsx +455 -0
  177. package/src/flow/admin-shell/BaseLayoutRouteCoordinator.ts +502 -0
  178. package/src/flow/admin-shell/__tests__/AdminLayoutRouteCoordinator.test.ts +547 -3
  179. package/src/flow/admin-shell/admin-layout/AdminLayoutComponent.tsx +4 -4
  180. package/src/flow/admin-shell/admin-layout/AdminLayoutEntryGuard.tsx +160 -0
  181. package/src/flow/admin-shell/admin-layout/AdminLayoutMenuModels.tsx +0 -12
  182. package/src/flow/admin-shell/admin-layout/AdminLayoutModel.tsx +28 -201
  183. package/src/flow/admin-shell/admin-layout/AdminLayoutSlotModels.tsx +11 -2
  184. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutMenuModels.test.ts +1 -26
  185. package/src/flow/admin-shell/admin-layout/__tests__/AdminLayoutModel.test.tsx +149 -27
  186. package/src/flow/admin-shell/admin-layout/index.ts +2 -0
  187. package/src/flow/admin-shell/useAdminLayoutRoutePage.ts +10 -26
  188. package/src/flow/admin-shell/useLayoutRoutePage.ts +61 -0
  189. package/src/flow/components/AdminLayout.tsx +4 -154
  190. package/src/flow/components/FlowRoute.tsx +105 -15
  191. package/src/flow/components/filter/useFilterOptions.ts +27 -5
  192. package/src/flow/index.ts +4 -0
  193. package/src/flow/models/actions/UpdateRecordActionModel.tsx +14 -95
  194. package/src/flow/models/actions/UpdateRecordActionUtils.ts +4 -7
  195. package/src/flow/models/actions/__tests__/AssignFormRefill.test.ts +26 -1
  196. package/src/flow/models/base/ActionModel.tsx +8 -1
  197. package/src/flow/models/base/PageModel/PageModel.tsx +51 -18
  198. package/src/flow/models/base/PageModel/RootPageModel.tsx +6 -13
  199. package/src/flow/models/base/PageModel/__tests__/PageModel.test.ts +102 -1
  200. package/src/flow/models/base/RouteModel.tsx +1 -1
  201. package/src/flow/models/blocks/assign-form/AssignFormItemModel.tsx +63 -2
  202. package/src/flow/models/blocks/assign-form/assignFieldValuesFlow.tsx +206 -0
  203. package/src/flow/models/blocks/assign-form/index.ts +1 -0
  204. package/src/flow/models/blocks/form/FormActionGroupModel.tsx +14 -0
  205. package/src/flow/models/blocks/form/FormActionModel.tsx +30 -3
  206. package/src/flow/models/blocks/form/FormItemModel.tsx +8 -1
  207. package/src/flow/models/blocks/form/__tests__/FormActionGroupModel.test.ts +46 -0
  208. package/src/flow/models/blocks/form/__tests__/submitHandler.test.ts +71 -0
  209. package/src/flow/models/blocks/form/submitHandler.ts +8 -1
  210. package/src/flow/models/blocks/form/submitValues.ts +4 -1
  211. package/src/flow/models/blocks/table/TableBlockModel.tsx +118 -16
  212. package/src/flow/models/blocks/table/__tests__/TableBlockModel.rowSelection.test.tsx +114 -0
  213. package/src/flow/models/fields/AssociationFieldModel/SubFormFieldModel.tsx +7 -1
  214. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/SubTableField.tsx +1 -1
  215. package/src/flow/models/fields/AssociationFieldModel/SubTableFieldModel/index.tsx +6 -5
  216. package/src/flow/models/fields/ClickableFieldModel.tsx +9 -1
  217. package/src/flow/models/fields/CollectionSelectorFieldModel.tsx +8 -2
  218. package/src/flow/models/fields/DisplayEnumFieldModel.tsx +8 -2
  219. package/src/flow/models/fields/DisplayTimeFieldModel.tsx +1 -1
  220. package/src/flow/models/fields/TimeFieldModel.tsx +1 -1
  221. package/src/flow/models/fields/__tests__/TimeFieldModel.test.tsx +61 -0
  222. package/src/flow/models/fields/mobile-components/MobileDatePicker.tsx +19 -3
  223. package/src/flow/models/fields/mobile-components/__tests__/MobileDatePicker.test.tsx +94 -0
  224. package/src/flow/models/topbar/TopbarActionModel.tsx +1 -1
  225. package/src/flow/utils/__tests__/dateTimeFormat.test.ts +91 -0
  226. package/src/index.ts +5 -0
  227. package/src/layout-manager/LayoutContentRoute.tsx +90 -0
  228. package/src/layout-manager/LayoutManager.tsx +185 -0
  229. package/src/layout-manager/LayoutRoute.tsx +138 -0
  230. package/src/layout-manager/__tests__/LayoutManager.test.tsx +335 -0
  231. package/src/layout-manager/__tests__/LayoutRoute.test.tsx +473 -0
  232. package/src/layout-manager/index.ts +14 -0
  233. package/src/layout-manager/types.ts +22 -0
  234. package/src/layout-manager/utils.ts +37 -0
  235. package/src/nocobase-buildin-plugin/index.tsx +56 -48
  236. package/src/settings-center/index.ts +1 -1
  237. package/src/settings-center/plugin-manager/BulkEnableButton.tsx +111 -0
  238. package/src/settings-center/plugin-manager/PluginCard.tsx +270 -0
  239. package/src/settings-center/plugin-manager/PluginDetail.tsx +195 -0
  240. package/src/settings-center/plugin-manager/index.tsx +254 -0
  241. package/src/settings-center/plugin-manager/types.ts +35 -0
  242. package/src/settings-center/utils.tsx +8 -1
  243. package/src/theme/__tests__/globalStyles.test.ts +24 -0
  244. package/src/theme/globalStyles.ts +10 -0
  245. package/src/utils/globalDeps.ts +2 -0
  246. package/es/collection-manager/interfaces/linkTo.d.ts +0 -90
  247. package/es/collection-manager/interfaces/o2o.d.ts +0 -621
  248. package/es/collection-manager/interfaces/properties/operators.d.ts +0 -294
  249. package/es/collection-manager/interfaces/subTable.d.ts +0 -172
  250. package/src/collection-manager/interfaces/linkTo.ts +0 -120
  251. package/src/collection-manager/interfaces/o2o.tsx +0 -561
  252. package/src/collection-manager/interfaces/subTable.ts +0 -218
  253. package/src/settings-center/PluginManagerPage.tsx +0 -162
@@ -0,0 +1,131 @@
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 { RouteContext } from '@ant-design/pro-layout';
11
+ import _ from 'lodash';
12
+ import React, { createContext, FC, memo, useContext, useRef } from 'react';
13
+ import {
14
+ UNSAFE_DataRouterContext,
15
+ UNSAFE_DataRouterStateContext,
16
+ UNSAFE_LocationContext,
17
+ UNSAFE_RouteContext,
18
+ } from 'react-router-dom';
19
+
20
+ const KeepAliveContext = createContext(true);
21
+ const hidden = { display: 'none' };
22
+
23
+ export const KeepAliveProvider: FC<{ active: boolean; parentActive: boolean }> = memo(
24
+ ({ children, active, parentActive }) => {
25
+ const currentLocationContext = useContext(UNSAFE_LocationContext);
26
+ const currentRouteContext = useContext(UNSAFE_RouteContext);
27
+ const currentDataRouterContext = useContext(UNSAFE_DataRouterContext);
28
+ const currentDataRouterStateContext = useContext(UNSAFE_DataRouterStateContext);
29
+ const routeContextValue = useContext(RouteContext);
30
+
31
+ const prevLocationContextRef = useRef(currentLocationContext);
32
+ const prevRouteContextRef = useRef(currentRouteContext);
33
+ const prevDataRouterContextRef = useRef(currentDataRouterContext);
34
+ const prevDataRouterStateContextRef = useRef(currentDataRouterStateContext);
35
+ const prevRouteContextValueRef = useRef(routeContextValue);
36
+
37
+ if (active) {
38
+ prevDataRouterContextRef.current = currentDataRouterContext;
39
+ prevDataRouterStateContextRef.current = currentDataRouterStateContext;
40
+ prevRouteContextValueRef.current = routeContextValue;
41
+ }
42
+
43
+ if (
44
+ active &&
45
+ !_.isEqual(_.omit(prevLocationContextRef.current.location, 'key'), _.omit(currentLocationContext.location, 'key'))
46
+ ) {
47
+ prevLocationContextRef.current = currentLocationContext;
48
+ }
49
+
50
+ if (active && !_.isEqual(prevRouteContextRef.current, currentRouteContext)) {
51
+ prevRouteContextRef.current = currentRouteContext;
52
+ }
53
+
54
+ return (
55
+ <div style={active ? { height: '100%' } : hidden}>
56
+ <RouteContext.Provider value={prevRouteContextValueRef.current}>
57
+ <UNSAFE_DataRouterContext.Provider value={prevDataRouterContextRef.current}>
58
+ <UNSAFE_DataRouterStateContext.Provider value={prevDataRouterStateContextRef.current}>
59
+ <UNSAFE_LocationContext.Provider value={prevLocationContextRef.current}>
60
+ <UNSAFE_RouteContext.Provider value={prevRouteContextRef.current}>
61
+ <KeepAliveContext.Provider value={parentActive === false ? false : active}>
62
+ {children}
63
+ </KeepAliveContext.Provider>
64
+ </UNSAFE_RouteContext.Provider>
65
+ </UNSAFE_LocationContext.Provider>
66
+ </UNSAFE_DataRouterStateContext.Provider>
67
+ </UNSAFE_DataRouterContext.Provider>
68
+ </RouteContext.Provider>
69
+ </div>
70
+ );
71
+ },
72
+ );
73
+
74
+ export const useKeepAlive = () => {
75
+ const active = useContext(KeepAliveContext);
76
+ return { active };
77
+ };
78
+
79
+ interface KeepAliveProps {
80
+ uid: string;
81
+ children: (uid: string) => React.ReactNode;
82
+ }
83
+
84
+ const MINIMUM_CACHED_PAGES = 5;
85
+ const MAXIMUM_CACHED_PAGES = 15;
86
+
87
+ const getMaxPageCount = () => {
88
+ const baseCount = MINIMUM_CACHED_PAGES;
89
+
90
+ try {
91
+ const memory = (navigator as any).deviceMemory;
92
+ if (memory) {
93
+ return Math.min(Math.max(baseCount, memory * 3), MAXIMUM_CACHED_PAGES);
94
+ }
95
+
96
+ const cores = navigator.hardwareConcurrency;
97
+ if (cores) {
98
+ return cores >= 8 ? MAXIMUM_CACHED_PAGES : cores >= 4 ? 7 : baseCount;
99
+ }
100
+
101
+ return baseCount;
102
+ } catch (e) {
103
+ return baseCount;
104
+ }
105
+ };
106
+
107
+ const MAX_RENDERED_PAGE_COUNT = getMaxPageCount();
108
+
109
+ export const KeepAlive: FC<KeepAliveProps> = React.memo(({ children, uid }) => {
110
+ const { active } = useKeepAlive();
111
+ const renderedUidListRef = useRef<string[]>([]);
112
+
113
+ if (!renderedUidListRef.current.includes(uid)) {
114
+ renderedUidListRef.current.push(uid);
115
+ if (renderedUidListRef.current.length > MAX_RENDERED_PAGE_COUNT) {
116
+ renderedUidListRef.current = renderedUidListRef.current.slice(-MAX_RENDERED_PAGE_COUNT);
117
+ }
118
+ }
119
+
120
+ return (
121
+ <>
122
+ {renderedUidListRef.current.map((renderedUid) => (
123
+ <KeepAliveProvider active={renderedUid === uid} key={renderedUid} parentActive={active}>
124
+ {children(renderedUid)}
125
+ </KeepAliveProvider>
126
+ ))}
127
+ </>
128
+ );
129
+ });
130
+
131
+ KeepAlive.displayName = 'KeepAlive';
@@ -164,6 +164,7 @@ Key props:
164
164
  - `namespaces`: restrict the picker to specific top-level namespaces. Omit to expose every registered top-level property
165
165
  - `extraNodes`: static leaves appended after the namespace-filtered nodes. Use for variables that only make sense in the current page (e.g. `$resetLink`)
166
166
  - `converters`: override the default path ↔ string converters. `EnvVariableInput` uses this hook to lock its output to `$env`
167
+ - `delimiters`: token pair wrapping the stored variable reference. Defaults to `['{{', '}}']` (Handlebars HTML-escaped). Pass `['{{{', '}}}']` for fields rendered as HTML where escaping would corrupt the variable value — e.g. the in-app message body
167
168
  - `value` / `onChange` / `placeholder` / `disabled`: standard controlled-input props
168
169
 
169
170
  Under the hood `VariableInput` wraps `VariableHybridInput` (inline pills), `VariableTextArea` wraps `TextAreaWithContextSelector` (textarea + variable button). Both share the same MetaTree.
@@ -164,6 +164,7 @@ import { VariableInput, VariableTextArea } from '@nocobase/client-v2';
164
164
  - `namespaces`:限定可选的顶层命名空间。不传就用 `flowEngine.context` 里全部已注册的
165
165
  - `extraNodes`:在命名空间过滤后追加几条静态变量(用于 `$resetLink` 这类只在当前页面有意义的局部变量)
166
166
  - `converters`:覆盖默认的 path ↔ string 转换器。`EnvVariableInput` 就是用这个钩子把输出锁定到 `$env`
167
+ - `delimiters`:变量在存储字符串里使用的开闭分隔符,默认 `['{{', '}}']`(对应 Handlebars 的 HTML 转义形式)。若字段最终以 HTML 渲染、转义会破坏变量内容(如站内信正文),传 `['{{{', '}}}']` 走 Handlebars 的原样输出形式
167
168
  - `value` / `onChange` / `placeholder` / `disabled`:标准受控字段属性
168
169
 
169
170
  底层共用 `VariableHybridInput`(`VariableInput`)和 `TextAreaWithContextSelector`(`VariableTextArea`),用同一套 MetaTree 数据。
@@ -7,11 +7,32 @@
7
7
  * For more information, please refer to: https://www.nocobase.com/agreement.
8
8
  */
9
9
 
10
- import { observable } from '@formily/reactive';
11
- import { useEffect, useMemo } from 'react';
12
- import { useLocation, useMatch, useMatches, useParams } from 'react-router-dom';
10
+ import { useEffect } from 'react';
11
+ import { useLocation, useMatches, useParams } from 'react-router-dom';
13
12
  import { Application } from '../Application';
14
13
 
14
+ type LayoutMatchLike = {
15
+ id: string;
16
+ pathname: string;
17
+ };
18
+
19
+ type LayoutDefinitionLike = {
20
+ routeName: string;
21
+ };
22
+
23
+ export function findDeepestLayoutMatch(layouts: LayoutDefinitionLike[] = [], matches: LayoutMatchLike[] = []) {
24
+ const layoutRouteNames = new Set(layouts.map((layout) => layout.routeName));
25
+
26
+ for (let index = matches.length - 1; index >= 0; index -= 1) {
27
+ const match = matches[index];
28
+ if (layoutRouteNames.has(match.id)) {
29
+ return match;
30
+ }
31
+ }
32
+
33
+ return null;
34
+ }
35
+
15
36
  export function useRouterSync(app: Application) {
16
37
  const params = useParams();
17
38
  const location = useLocation();
@@ -20,13 +41,16 @@ export function useRouterSync(app: Application) {
20
41
  useEffect(() => {
21
42
  const last = matches[matches.length - 1];
22
43
  if (!last) return;
44
+ const layoutMatch = findDeepestLayoutMatch(app.layoutManager?.listLayouts?.(), matches);
23
45
  engine.context['_observableCache']['route'] = {
24
46
  name: last.id,
25
47
  pathname: last.pathname,
26
48
  path: last.handle?.['path'] || null,
27
49
  params,
50
+ layoutRouteName: layoutMatch?.id,
51
+ layoutBasePathname: layoutMatch?.pathname,
28
52
  };
29
- }, [engine.context, params, matches]);
53
+ }, [app, engine.context, params, matches]);
30
54
  useEffect(() => {
31
55
  engine.context['_observableCache']['location'] = location;
32
56
  }, [engine.context, location]);
@@ -0,0 +1,63 @@
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 { render, screen, waitFor } from '@testing-library/react';
11
+ import React, { useEffect } from 'react';
12
+ import { createMemoryRouter, Outlet, RouterProvider, useParams } from 'react-router-dom';
13
+ import { describe, expect, it } from 'vitest';
14
+ import { KeepAlive } from '../KeepAlive';
15
+
16
+ describe('KeepAlive', () => {
17
+ it('keeps inactive outlet pages mounted while switching route params', async () => {
18
+ const events: string[] = [];
19
+
20
+ const Page = () => {
21
+ const { name } = useParams();
22
+ useEffect(() => {
23
+ events.push(`mount:${name}`);
24
+ return () => {
25
+ events.push(`unmount:${name}`);
26
+ };
27
+ }, [name]);
28
+
29
+ return <div>page {name}</div>;
30
+ };
31
+
32
+ const Layout = () => {
33
+ const { name } = useParams();
34
+ return <KeepAlive uid={name || ''}>{() => <Outlet />}</KeepAlive>;
35
+ };
36
+
37
+ const router = createMemoryRouter(
38
+ [
39
+ {
40
+ path: '/:name',
41
+ element: <Layout />,
42
+ children: [{ index: true, element: <Page /> }],
43
+ },
44
+ ],
45
+ {
46
+ initialEntries: ['/page-a'],
47
+ },
48
+ );
49
+
50
+ render(<RouterProvider router={router} />);
51
+
52
+ expect(await screen.findByText('page page-a')).toBeInTheDocument();
53
+
54
+ await router.navigate('/page-b');
55
+
56
+ expect(await screen.findByText('page page-b')).toBeInTheDocument();
57
+ expect(screen.getByText('page page-a')).toBeInTheDocument();
58
+
59
+ await waitFor(() => {
60
+ expect(events).toEqual(['mount:page-a', 'mount:page-b']);
61
+ });
62
+ });
63
+ });
@@ -0,0 +1,27 @@
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 { describe, expect, it } from 'vitest';
11
+ import { findDeepestLayoutMatch } from '../RouterBridge';
12
+
13
+ describe('RouterBridge', () => {
14
+ it('uses the deepest matched layout route as layout base pathname', () => {
15
+ const match = findDeepestLayoutMatch(
16
+ [{ routeName: 'admin' }, { routeName: 'admin.settings.publicForms' }],
17
+ [
18
+ { id: 'admin', pathname: '/admin' },
19
+ { id: 'admin.settings', pathname: '/admin/settings' },
20
+ { id: 'admin.settings.publicForms', pathname: '/admin/settings/public-forms' },
21
+ { id: 'admin.settings.publicForms.page.view', pathname: '/admin/settings/public-forms/form-1/view/popup' },
22
+ ],
23
+ );
24
+
25
+ expect(match?.pathname).toBe('/admin/settings/public-forms');
26
+ });
27
+ });
@@ -25,28 +25,75 @@ import { TextAreaWithContextSelector } from '../../flow/components/TextAreaWithC
25
25
  * stored values round-trip to a labelled pill instead of falling back to a
26
26
  * raw `{{…}}` literal.
27
27
  */
28
- const VARIABLE_EXPR_RE = /^\{\{\s*(.+?)\s*\}\}$/;
29
-
30
- export function parseVariablePath(value?: string): string[] | undefined {
31
- if (typeof value !== 'string') return undefined;
32
- const match = value.trim().match(VARIABLE_EXPR_RE);
33
- if (!match) return undefined;
34
- let pathString = match[1];
35
- // Backwards-compat: accept the legacy `ctx.` prefix so values produced by
36
- // pre-fix versions of the picker still resolve to a labelled pill.
37
- if (pathString === 'ctx') return [];
38
- if (pathString.startsWith('ctx.')) pathString = pathString.slice(4);
39
- return pathString.split('.');
28
+
29
+ /**
30
+ * Variable delimiters: opening + closing tokens. Default `['{{', '}}']`
31
+ * matches NocoBase server template convention (Handlebars HTML-escaped
32
+ * output). Pass `['{{{', '}}}']` to switch to Handlebars' raw/unescaped
33
+ * form — required for fields whose content is rendered as HTML (e.g.
34
+ * in-app message body) so the variable expansion bypasses HTML escaping.
35
+ *
36
+ * Restrict to literal-token pairs, since the regex builder escapes them
37
+ * verbatim.
38
+ */
39
+ export type VariableDelimiters = readonly [string, string];
40
+
41
+ const DEFAULT_DELIMITERS: VariableDelimiters = ['{{', '}}'];
42
+
43
+ function escapeForRegExp(s: string): string {
44
+ return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
40
45
  }
41
46
 
42
- export function formatVariablePath(meta?: MetaTreeNode): string | undefined {
43
- const paths = meta?.paths || [];
44
- if (paths.length === 0) return undefined;
45
- // No inner spaces — matches the v1 storage shape exactly so round-trips
46
- // through the API stay byte-stable.
47
- return `{{${paths.join('.')}}}`;
47
+ /**
48
+ * Factory: returns a `parseValueToPath` bound to the given delimiters.
49
+ * Anchored (`^…$`) only treats the whole input as a single variable
50
+ * reference, matching the v1 single-line picker behaviour.
51
+ */
52
+ export function makeParseVariablePath(delimiters: VariableDelimiters = DEFAULT_DELIMITERS) {
53
+ const [open, close] = delimiters;
54
+ const re = new RegExp(`^${escapeForRegExp(open)}\\s*(.+?)\\s*${escapeForRegExp(close)}$`);
55
+ return (value?: string): string[] | undefined => {
56
+ if (typeof value !== 'string') return undefined;
57
+ const match = value.trim().match(re);
58
+ if (!match) return undefined;
59
+ let pathString = match[1];
60
+ // Backwards-compat: accept the legacy `ctx.` prefix so values produced by
61
+ // pre-fix versions of the picker still resolve to a labelled pill.
62
+ if (pathString === 'ctx') return [];
63
+ if (pathString.startsWith('ctx.')) pathString = pathString.slice(4);
64
+ return pathString.split('.');
65
+ };
48
66
  }
49
67
 
68
+ /**
69
+ * Factory: returns a `formatPathToValue` bound to the given delimiters.
70
+ * No inner spaces — matches the v1 storage shape exactly so round-trips
71
+ * through the API stay byte-stable.
72
+ */
73
+ export function makeFormatVariablePath(delimiters: VariableDelimiters = DEFAULT_DELIMITERS) {
74
+ const [open, close] = delimiters;
75
+ return (meta?: MetaTreeNode): string | undefined => {
76
+ const paths = meta?.paths || [];
77
+ if (paths.length === 0) return undefined;
78
+ return `${open}${paths.join('.')}${close}`;
79
+ };
80
+ }
81
+
82
+ /**
83
+ * Factory: returns a global regex matching every occurrence of the
84
+ * variable token within a longer string. Used by `VariableHybridInput`
85
+ * to render embedded variables as pills.
86
+ */
87
+ export function makeVariableRegExp(delimiters: VariableDelimiters = DEFAULT_DELIMITERS): RegExp {
88
+ const [open, close] = delimiters;
89
+ return new RegExp(`${escapeForRegExp(open)}\\s*([^{}]+?)\\s*${escapeForRegExp(close)}`, 'g');
90
+ }
91
+
92
+ // Default exports — `{{ ... }}` for the most common case. Kept named so
93
+ // existing callers don't need to switch to the factory.
94
+ export const parseVariablePath = makeParseVariablePath();
95
+ export const formatVariablePath = makeFormatVariablePath();
96
+
50
97
  const META_TREE_CACHE_PREFIX = '@nocobase/client-v2:VariableInput:metaTree';
51
98
 
52
99
  /**
@@ -123,8 +170,22 @@ export interface VariableInputProps {
123
170
  * Override the converters used by the underlying `VariableHybridInput`.
124
171
  * Mostly useful when the caller wants to constrain `formatPathToValue` to a
125
172
  * specific namespace (see `EnvVariableInput` for that pattern).
173
+ *
174
+ * Takes precedence over `delimiters` when both are set on the same field
175
+ * (an explicit converter wins over the delimiter-derived one).
126
176
  */
127
177
  converters?: VariableHybridInputConverters;
178
+ /**
179
+ * Token pair wrapping variable references in the stored string. Defaults
180
+ * to `['{{', '}}']` — the standard NocoBase server-template form,
181
+ * HTML-escaped by Handlebars. Pass `['{{{', '}}}']` for fields rendered
182
+ * as HTML where escaping would corrupt the variable value (e.g. the
183
+ * in-app message body).
184
+ *
185
+ * Ignored when `converters` is also supplied — caller-provided converters
186
+ * win.
187
+ */
188
+ delimiters?: VariableDelimiters;
128
189
  className?: string;
129
190
  style?: React.CSSProperties;
130
191
  }
@@ -136,16 +197,24 @@ export interface VariableInputProps {
136
197
  * line of mixed literal+variable content is appropriate.
137
198
  */
138
199
  export function VariableInput(props: VariableInputProps) {
139
- const { namespaces, extraNodes, converters, ...rest } = props;
200
+ const { namespaces, extraNodes, converters, delimiters, ...rest } = props;
140
201
  const metaTree = useFilteredMetaTree({ namespaces, extraNodes });
141
- const mergedConverters = useMemo<VariableHybridInputConverters>(
142
- () => ({
143
- formatPathToValue: formatVariablePath,
144
- parseValueToPath: parseVariablePath,
202
+ const mergedConverters = useMemo<VariableHybridInputConverters>(() => {
203
+ // Default delimiters → reuse the pre-built singletons.
204
+ if (!delimiters) {
205
+ return {
206
+ formatPathToValue: formatVariablePath,
207
+ parseValueToPath: parseVariablePath,
208
+ ...converters,
209
+ };
210
+ }
211
+ return {
212
+ formatPathToValue: makeFormatVariablePath(delimiters),
213
+ parseValueToPath: makeParseVariablePath(delimiters),
214
+ variableRegExp: makeVariableRegExp(delimiters),
145
215
  ...converters,
146
- }),
147
- [converters],
148
- );
216
+ };
217
+ }, [converters, delimiters]);
149
218
  return <VariableHybridInput {...rest} converters={mergedConverters} metaTree={metaTree} />;
150
219
  }
151
220
 
@@ -161,9 +230,13 @@ export interface VariableTextAreaProps extends Omit<VariableInputProps, 'convert
161
230
  * is desirable (the server expands them at render time).
162
231
  */
163
232
  export function VariableTextArea(props: VariableTextAreaProps) {
164
- const { namespaces, extraNodes, rows, maxRows, style, ...rest } = props;
233
+ const { namespaces, extraNodes, rows, maxRows, style, delimiters, ...rest } = props;
165
234
  const metaTree = useFilteredMetaTree({ namespaces, extraNodes });
166
235
  const metaTreeGetter = useMemo(() => () => metaTree, [metaTree]);
236
+ const formatPathToValue = useMemo(
237
+ () => (delimiters ? makeFormatVariablePath(delimiters) : formatVariablePath),
238
+ [delimiters],
239
+ );
167
240
  return (
168
241
  <TextAreaWithContextSelector
169
242
  {...rest}
@@ -171,7 +244,7 @@ export function VariableTextArea(props: VariableTextAreaProps) {
171
244
  maxRows={maxRows}
172
245
  style={style}
173
246
  metaTree={metaTreeGetter}
174
- formatPathToValue={(meta) => formatVariablePath(meta) ?? ''}
247
+ formatPathToValue={(meta) => formatPathToValue(meta) ?? ''}
175
248
  />
176
249
  );
177
250
  }
@@ -0,0 +1,85 @@
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 { describe, expect, it } from 'vitest';
11
+ import {
12
+ formatVariablePath,
13
+ makeFormatVariablePath,
14
+ makeParseVariablePath,
15
+ makeVariableRegExp,
16
+ parseVariablePath,
17
+ } from '../VariableInput';
18
+
19
+ describe('VariableInput delimiter helpers', () => {
20
+ describe('default delimiters {{ }}', () => {
21
+ it('formats meta nodes into `{{x.y}}` with no inner spaces', () => {
22
+ expect(formatVariablePath({ paths: ['$user', 'id'] } as any)).toBe('{{$user.id}}');
23
+ });
24
+
25
+ it('parses `{{x.y}}` into a path array', () => {
26
+ expect(parseVariablePath('{{ $user.id }}')).toEqual(['$user', 'id']);
27
+ expect(parseVariablePath('{{$user.id}}')).toEqual(['$user', 'id']);
28
+ });
29
+
30
+ it('strips a legacy `ctx.` prefix for backwards-compat', () => {
31
+ expect(parseVariablePath('{{ctx.$user.id}}')).toEqual(['$user', 'id']);
32
+ expect(parseVariablePath('{{ctx}}')).toEqual([]);
33
+ });
34
+
35
+ it('returns undefined for plain text', () => {
36
+ expect(parseVariablePath('hello world')).toBeUndefined();
37
+ expect(parseVariablePath('')).toBeUndefined();
38
+ expect(parseVariablePath(undefined)).toBeUndefined();
39
+ });
40
+ });
41
+
42
+ describe('triple-brace delimiters {{{ }}}', () => {
43
+ const format = makeFormatVariablePath(['{{{', '}}}']);
44
+ const parse = makeParseVariablePath(['{{{', '}}}']);
45
+
46
+ it('formats meta nodes into `{{{x.y}}}`', () => {
47
+ expect(format({ paths: ['$user', 'id'] } as any)).toBe('{{{$user.id}}}');
48
+ });
49
+
50
+ it('parses `{{{x.y}}}` into a path array', () => {
51
+ expect(parse('{{{$user.id}}}')).toEqual(['$user', 'id']);
52
+ expect(parse('{{{ $user.id }}}')).toEqual(['$user', 'id']);
53
+ });
54
+
55
+ it('does not match the double-brace form', () => {
56
+ expect(parse('{{$user.id}}')).toBeUndefined();
57
+ });
58
+ });
59
+
60
+ describe('makeVariableRegExp', () => {
61
+ it('matches every occurrence in a longer string under default delimiters', () => {
62
+ const re = makeVariableRegExp();
63
+ const matches = Array.from('hello {{ $user.id }} world {{ $env.X }}'.matchAll(re)).map((m) => m[1]);
64
+ expect(matches).toEqual(['$user.id', '$env.X']);
65
+ });
66
+
67
+ it('matches triple-brace occurrences when configured', () => {
68
+ const re = makeVariableRegExp(['{{{', '}}}']);
69
+ const matches = Array.from('hi {{{$user.id}}} :)'.matchAll(re)).map((m) => m[1]);
70
+ expect(matches).toEqual(['$user.id']);
71
+ });
72
+
73
+ it('does not match double-brace occurrences under triple-brace config', () => {
74
+ const re = makeVariableRegExp(['{{{', '}}}']);
75
+ const matches = Array.from('plain {{ x }} text'.matchAll(re));
76
+ expect(matches).toHaveLength(0);
77
+ });
78
+ });
79
+
80
+ it('handles empty paths consistently', () => {
81
+ expect(formatVariablePath({ paths: [] } as any)).toBeUndefined();
82
+ expect(formatVariablePath(undefined)).toBeUndefined();
83
+ expect(makeFormatVariablePath(['{{{', '}}}'])({ paths: [] } as any)).toBeUndefined();
84
+ });
85
+ });
@@ -25,7 +25,15 @@ export interface CollectionFilterProps {
25
25
  t?: (key: string, options?: Record<string, any>) => string;
26
26
  /** Whitelist of root-level field names to expose. */
27
27
  filterableFieldNames?: string[];
28
- /** Bypass the `filterableFieldNames` whitelist. */
28
+ /**
29
+ * Blacklist of root-level field names to drop. Mirrors v1's `nonfilterable: [...]` on `Filter.Action`. When both `filterableFieldNames` and this prop are supplied, both apply (final = whitelist ∩ ¬blacklist).
30
+ */
31
+ nonfilterableFieldNames?: string[];
32
+ /**
33
+ * Bypass the `filterableFieldNames` whitelist.
34
+ *
35
+ * Legacy escape hatch — prefer adjusting `filterableFieldNames` / `nonfilterableFieldNames` instead.
36
+ */
29
37
  noIgnore?: boolean;
30
38
  /** Override the trigger button's label. Defaults to `t('Filter')`, or the v1-style `t('{{count}} filter items', { count })` when conditions are present. */
31
39
  buttonText?: React.ReactNode;
@@ -50,6 +58,7 @@ export const CollectionFilter: FC<CollectionFilterProps> = (props) => {
50
58
  onChange,
51
59
  t = identity,
52
60
  filterableFieldNames,
61
+ nonfilterableFieldNames,
53
62
  noIgnore,
54
63
  buttonText,
55
64
  showCount = true,
@@ -63,6 +72,7 @@ export const CollectionFilter: FC<CollectionFilterProps> = (props) => {
63
72
  const filterAction = useFilterActionProps({
64
73
  collection,
65
74
  filterableFieldNames,
75
+ nonfilterableFieldNames,
66
76
  noIgnore,
67
77
  t,
68
78
  onApply: (filter: CompiledFilter, action: FilterApplyAction) => {
@@ -44,7 +44,15 @@ export interface CollectionFilterItemProps {
44
44
  collection: Collection;
45
45
  /** Whitelist of field names to expose; empty/undefined means all filterable fields. */
46
46
  filterableFieldNames?: string[];
47
- /** Bypass the `filterableFieldNames` whitelist (matches the legacy FilterItem `noIgnore`). */
47
+ /**
48
+ * Blacklist of field names to drop. Mirrors v1's `nonfilterable: [...]` on `Filter.Action`. When both whitelist and blacklist are supplied, both apply (final = whitelist ∩ ¬blacklist).
49
+ */
50
+ nonfilterableFieldNames?: string[];
51
+ /**
52
+ * Bypass the `filterableFieldNames` whitelist (matches the legacy FilterItem `noIgnore`).
53
+ *
54
+ * Legacy escape hatch — prefer adjusting `filterableFieldNames` / `nonfilterableFieldNames` instead.
55
+ */
48
56
  noIgnore?: boolean;
49
57
  /** Translator; defaults to identity so callers can omit it. */
50
58
  t?: (key: string) => string;
@@ -84,10 +92,10 @@ const toCascaderOptions = (options: FilterOption[]): CascaderOption[] =>
84
92
  */
85
93
  export const CollectionFilterItem: FC<CollectionFilterItemProps> = observer(
86
94
  (props) => {
87
- const { collection, filterableFieldNames, noIgnore = false, t = identity } = props;
95
+ const { collection, filterableFieldNames, nonfilterableFieldNames, noIgnore = false, t = identity } = props;
88
96
  const { path: leftValue, operator, value: rightValue } = props.value;
89
97
 
90
- const options = useFilterOptions(collection, { filterableFieldNames, noIgnore, t });
98
+ const options = useFilterOptions(collection, { filterableFieldNames, nonfilterableFieldNames, noIgnore, t });
91
99
 
92
100
  const cascaderOptions = useMemo(() => toCascaderOptions(options), [options]);
93
101
 
@@ -164,7 +172,7 @@ export const CollectionFilterItem: FC<CollectionFilterItemProps> = observer(
164
172
  */
165
173
  export function createCollectionFilterItem(
166
174
  collection: Collection,
167
- bound?: Pick<CollectionFilterItemProps, 'filterableFieldNames' | 'noIgnore' | 't'>,
175
+ bound?: Pick<CollectionFilterItemProps, 'filterableFieldNames' | 'nonfilterableFieldNames' | 'noIgnore' | 't'>,
168
176
  ) {
169
177
  const Component: FC<{ value: CollectionFilterItemValue }> = (props) => (
170
178
  <CollectionFilterItem {...bound} value={props.value} collection={collection} />