ding-react-admin 1.0.4 → 2.0.4

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 (230) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +254 -201
  3. package/dist/index.css +2 -0
  4. package/dist/index.js +2073 -1202
  5. package/dist/src/components/NavMenuLabel.d.ts +8 -0
  6. package/dist/src/components/NavMenuLabel.d.ts.map +1 -0
  7. package/dist/src/components/NavMenuSearch.d.ts +9 -0
  8. package/dist/src/components/NavMenuSearch.d.ts.map +1 -0
  9. package/dist/src/components/ScrollableArea.d.ts +10 -0
  10. package/dist/src/components/ScrollableArea.d.ts.map +1 -0
  11. package/dist/src/components/index.d.ts +3 -0
  12. package/dist/src/components/index.d.ts.map +1 -1
  13. package/dist/src/context/AuthProvider.d.ts +2 -0
  14. package/dist/src/context/AuthProvider.d.ts.map +1 -1
  15. package/dist/src/crud/FormGlobalErrorsAlert.d.ts +5 -0
  16. package/dist/src/crud/FormGlobalErrorsAlert.d.ts.map +1 -0
  17. package/dist/src/crud/InlineFormSet.d.ts +2 -17
  18. package/dist/src/crud/InlineFormSet.d.ts.map +1 -1
  19. package/dist/src/crud/ResourceForm.d.ts +2 -10
  20. package/dist/src/crud/ResourceForm.d.ts.map +1 -1
  21. package/dist/src/crud/ResourceFormModal.d.ts +7 -1
  22. package/dist/src/crud/ResourceFormModal.d.ts.map +1 -1
  23. package/dist/src/crud/ResourceList.d.ts.map +1 -1
  24. package/dist/src/crud/ResourceRecordForm.d.ts +27 -0
  25. package/dist/src/crud/ResourceRecordForm.d.ts.map +1 -0
  26. package/dist/src/crud/columns/ImageColumn.d.ts +14 -0
  27. package/dist/src/crud/columns/ImageColumn.d.ts.map +1 -0
  28. package/dist/src/crud/context/InlineFieldsRegistry.d.ts +15 -0
  29. package/dist/src/crud/context/InlineFieldsRegistry.d.ts.map +1 -0
  30. package/dist/src/crud/context/PayloadFieldsContext.d.ts +18 -0
  31. package/dist/src/crud/context/PayloadFieldsContext.d.ts.map +1 -0
  32. package/dist/src/crud/fields/FileField.d.ts +12 -0
  33. package/dist/src/crud/fields/FileField.d.ts.map +1 -0
  34. package/dist/src/crud/fields/ImageField.d.ts +14 -0
  35. package/dist/src/crud/fields/ImageField.d.ts.map +1 -0
  36. package/dist/src/crud/fields/ReferenceField.d.ts +12 -2
  37. package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -1
  38. package/dist/src/crud/fields/ReferenceInputActions.d.ts +16 -0
  39. package/dist/src/crud/fields/ReferenceInputActions.d.ts.map +1 -0
  40. package/dist/src/crud/fields/ReferenceManyField.d.ts +10 -1
  41. package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -1
  42. package/dist/src/crud/fields/uploadFieldUtils.d.ts +5 -0
  43. package/dist/src/crud/fields/uploadFieldUtils.d.ts.map +1 -0
  44. package/dist/src/crud/fields/useUploadPreviewUrl.d.ts +4 -0
  45. package/dist/src/crud/fields/useUploadPreviewUrl.d.ts.map +1 -0
  46. package/dist/src/crud/filters/ReferenceFilter.d.ts +1 -1
  47. package/dist/src/crud/filters/ReferenceFilter.d.ts.map +1 -1
  48. package/dist/src/crud/index.d.ts +27 -9
  49. package/dist/src/crud/index.d.ts.map +1 -1
  50. package/dist/src/crud/types.d.ts +21 -7
  51. package/dist/src/crud/types.d.ts.map +1 -1
  52. package/dist/src/crud/utils/buildFormPayload.d.ts +6 -0
  53. package/dist/src/crud/utils/buildFormPayload.d.ts.map +1 -0
  54. package/dist/src/crud/utils/buildInlineRowsPayload.d.ts +6 -0
  55. package/dist/src/crud/utils/buildInlineRowsPayload.d.ts.map +1 -0
  56. package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts +5 -0
  57. package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts.map +1 -0
  58. package/dist/src/crud/utils/choiceSelectionUtils.d.ts +10 -0
  59. package/dist/src/crud/utils/choiceSelectionUtils.d.ts.map +1 -0
  60. package/dist/src/crud/utils/choiceSelectionUtils.test.d.ts +2 -0
  61. package/dist/src/crud/utils/choiceSelectionUtils.test.d.ts.map +1 -0
  62. package/dist/src/crud/utils/formErrors.d.ts +18 -7
  63. package/dist/src/crud/utils/formErrors.d.ts.map +1 -1
  64. package/dist/src/crud/utils/formErrors.test.d.ts +2 -0
  65. package/dist/src/crud/utils/formErrors.test.d.ts.map +1 -0
  66. package/dist/src/crud/utils/getFormValue.d.ts +3 -0
  67. package/dist/src/crud/utils/getFormValue.d.ts.map +1 -0
  68. package/dist/src/crud/utils/hasUploadValues.d.ts +3 -0
  69. package/dist/src/crud/utils/hasUploadValues.d.ts.map +1 -0
  70. package/dist/src/crud/utils/nestedFieldPath.d.ts +3 -0
  71. package/dist/src/crud/utils/nestedFieldPath.d.ts.map +1 -0
  72. package/dist/src/crud/utils/prepareFormSubmitBody.d.ts +8 -0
  73. package/dist/src/crud/utils/prepareFormSubmitBody.d.ts.map +1 -0
  74. package/dist/src/crud/utils/referenceSelectNotFoundContent.d.ts +4 -0
  75. package/dist/src/crud/utils/referenceSelectNotFoundContent.d.ts.map +1 -0
  76. package/dist/src/crud/utils/setFormValue.d.ts +3 -0
  77. package/dist/src/crud/utils/setFormValue.d.ts.map +1 -0
  78. package/dist/src/crud/utils/toFormData.d.ts +13 -0
  79. package/dist/src/crud/utils/toFormData.d.ts.map +1 -0
  80. package/dist/src/crud/utils/uploadReferenceUtils.d.ts +5 -0
  81. package/dist/src/crud/utils/uploadReferenceUtils.d.ts.map +1 -0
  82. package/dist/src/crud/utils/useChoices.d.ts +27 -1
  83. package/dist/src/crud/utils/useChoices.d.ts.map +1 -1
  84. package/dist/src/crud/utils/useFormRecord.d.ts +44 -0
  85. package/dist/src/crud/utils/useFormRecord.d.ts.map +1 -0
  86. package/dist/src/data/createRestResourceHandlers.d.ts +5 -5
  87. package/dist/src/data/createRestResourceHandlers.d.ts.map +1 -1
  88. package/dist/src/data/dataProviderTypes.d.ts +6 -5
  89. package/dist/src/data/dataProviderTypes.d.ts.map +1 -1
  90. package/dist/src/data/parseFormErrorHelpers.d.ts +23 -6
  91. package/dist/src/data/parseFormErrorHelpers.d.ts.map +1 -1
  92. package/dist/src/data/resourceHandlers.d.ts +1 -1
  93. package/dist/src/data/resourceHandlers.d.ts.map +1 -1
  94. package/dist/src/index.d.ts +4 -4
  95. package/dist/src/index.d.ts.map +1 -1
  96. package/dist/src/layouts/AdminLayout.d.ts +1 -1
  97. package/dist/src/layouts/AdminLayout.d.ts.map +1 -1
  98. package/dist/src/layouts/AuthPageLayout.d.ts.map +1 -1
  99. package/dist/src/layouts/navFilter.d.ts +5 -0
  100. package/dist/src/layouts/navFilter.d.ts.map +1 -0
  101. package/dist/src/layouts/navMenuItems.d.ts +6 -0
  102. package/dist/src/layouts/navMenuItems.d.ts.map +1 -0
  103. package/dist/src/types.d.ts +12 -0
  104. package/dist/src/types.d.ts.map +1 -1
  105. package/package.json +79 -59
  106. package/src/app/AdminApp.tsx +34 -34
  107. package/src/components/AuthAlternateLink.tsx +23 -23
  108. package/src/components/DensitySwitch.tsx +31 -31
  109. package/src/components/NavMenuLabel.tsx +26 -0
  110. package/src/components/NavMenuSearch.tsx +76 -0
  111. package/src/components/ScrollableArea.tsx +46 -0
  112. package/src/components/ThemeSwitch.tsx +28 -28
  113. package/src/components/ThemeToolbar.tsx +34 -34
  114. package/src/components/index.ts +7 -4
  115. package/src/components/navMenu.css +21 -0
  116. package/src/components/scrollable.css +28 -0
  117. package/src/context/AppThemeProvider.tsx +128 -128
  118. package/src/context/AuthProvider.tsx +89 -77
  119. package/src/context/DataProvider.tsx +30 -30
  120. package/src/context/PermissionsProvider.tsx +57 -57
  121. package/src/crud/FilterBar.tsx +36 -36
  122. package/src/crud/FormGlobalErrorsAlert.tsx +36 -0
  123. package/src/crud/FormSteps.tsx +135 -135
  124. package/src/crud/FormTabs.tsx +126 -126
  125. package/src/crud/InlineFormSet.tsx +162 -219
  126. package/src/crud/ListActionsBar.tsx +106 -106
  127. package/src/crud/ResourceForm.tsx +83 -166
  128. package/src/crud/ResourceFormModal.tsx +75 -133
  129. package/src/crud/ResourceList.tsx +1 -0
  130. package/src/crud/ResourceRecordForm.tsx +166 -0
  131. package/src/crud/columns/BooleanColumn.tsx +32 -32
  132. package/src/crud/columns/CustomColumn.tsx +33 -33
  133. package/src/crud/columns/DateColumn.tsx +28 -28
  134. package/src/crud/columns/ImageColumn.tsx +58 -0
  135. package/src/crud/columns/NumberColumn.tsx +31 -31
  136. package/src/crud/columns/ReferenceColumn.tsx +85 -85
  137. package/src/crud/columns/ReferenceManyColumn.tsx +67 -67
  138. package/src/crud/columns/TextColumn.tsx +38 -38
  139. package/src/crud/context/FilterContext.tsx +71 -71
  140. package/src/crud/context/FormContext.tsx +39 -39
  141. package/src/crud/context/FormSectionContext.tsx +22 -22
  142. package/src/crud/context/InlineFieldsRegistry.tsx +61 -0
  143. package/src/crud/context/ListContext.tsx +93 -93
  144. package/src/crud/context/PayloadFieldsContext.tsx +62 -0
  145. package/src/crud/fields/BooleanField.tsx +38 -38
  146. package/src/crud/fields/DateField.tsx +53 -53
  147. package/src/crud/fields/FieldWrapper.tsx +87 -87
  148. package/src/crud/fields/FileField.tsx +123 -0
  149. package/src/crud/fields/ImageField.tsx +120 -0
  150. package/src/crud/fields/NumberField.tsx +52 -52
  151. package/src/crud/fields/PasswordField.tsx +99 -99
  152. package/src/crud/fields/ReferenceField.tsx +237 -96
  153. package/src/crud/fields/ReferenceInputActions.tsx +106 -0
  154. package/src/crud/fields/ReferenceManyField.tsx +200 -76
  155. package/src/crud/fields/SelectField.tsx +48 -48
  156. package/src/crud/fields/TextField.tsx +46 -46
  157. package/src/crud/fields/uploadFieldUtils.ts +21 -0
  158. package/src/crud/fields/useUploadPreviewUrl.ts +20 -0
  159. package/src/crud/filters/BooleanFilter.tsx +38 -38
  160. package/src/crud/filters/DateFilter.tsx +35 -35
  161. package/src/crud/filters/NumberFilter.tsx +33 -33
  162. package/src/crud/filters/ReferenceFilter.tsx +192 -173
  163. package/src/crud/filters/SelectFilter.tsx +44 -44
  164. package/src/crud/filters/TextFilter.tsx +36 -36
  165. package/src/crud/index.ts +99 -90
  166. package/src/crud/types.ts +146 -132
  167. package/src/crud/utils/buildFormPayload.ts +24 -0
  168. package/src/crud/utils/buildInlineRowsPayload.ts +37 -0
  169. package/src/crud/utils/buildResourceFormSubmitBody.ts +29 -0
  170. package/src/crud/utils/choiceSelectionUtils.test.ts +45 -0
  171. package/src/crud/utils/choiceSelectionUtils.ts +116 -0
  172. package/src/crud/utils/formErrors.test.ts +168 -0
  173. package/src/crud/utils/formErrors.ts +138 -38
  174. package/src/crud/utils/formSectionErrors.ts +85 -85
  175. package/src/crud/utils/{getByPath.ts → getFormValue.ts} +13 -13
  176. package/src/crud/utils/hasUploadValues.ts +9 -0
  177. package/src/crud/utils/nestedFieldPath.ts +8 -0
  178. package/src/crud/utils/prepareFormSubmitBody.ts +22 -0
  179. package/src/crud/utils/referenceSelectNotFoundContent.tsx +7 -0
  180. package/src/crud/utils/{setByPath.ts → setFormValue.ts} +18 -18
  181. package/src/crud/utils/sortQueryParam.ts +37 -37
  182. package/src/crud/utils/toFormData.ts +79 -0
  183. package/src/crud/utils/uploadReferenceUtils.ts +44 -0
  184. package/src/crud/utils/useAbortableEffect.ts +17 -17
  185. package/src/crud/utils/useChoices.ts +330 -182
  186. package/src/crud/utils/useFormRecord.ts +179 -0
  187. package/src/crud/utils/useListQueryState.ts +191 -191
  188. package/src/data/abortError.test.ts +19 -19
  189. package/src/data/abortError.ts +10 -10
  190. package/src/data/createMemoryResourceHandlers.ts +78 -78
  191. package/src/data/createRestResourceHandlers.ts +63 -57
  192. package/src/data/dataProviderTypes.ts +7 -5
  193. package/src/data/dataUtils.test.ts +228 -108
  194. package/src/data/inMemoryList.ts +97 -97
  195. package/src/data/parseFormErrorHelpers.test.ts +201 -84
  196. package/src/data/parseFormErrorHelpers.ts +376 -224
  197. package/src/data/resourceHandlers.ts +1 -1
  198. package/src/data/sortHelpers.ts +48 -48
  199. package/src/index.ts +211 -195
  200. package/src/layouts/AdminLayout.tsx +530 -381
  201. package/src/layouts/AuthPageLayout.tsx +87 -81
  202. package/src/layouts/navFilter.ts +49 -0
  203. package/src/layouts/navMenuItems.tsx +44 -0
  204. package/src/pages/LoginPage.tsx +80 -80
  205. package/src/pages/PlaceholderPage.tsx +9 -9
  206. package/src/permissions/resourcePermissions.ts +47 -47
  207. package/src/router/createAdminRouter.tsx +64 -64
  208. package/src/router/guards.tsx +63 -63
  209. package/src/router/routeAccess.ts +89 -89
  210. package/src/types.ts +10 -0
  211. package/src/vite-env.d.ts +1 -1
  212. package/dist/src/crud/context/SubmitFieldsContext.d.ts +0 -18
  213. package/dist/src/crud/context/SubmitFieldsContext.d.ts.map +0 -1
  214. package/dist/src/crud/utils/getByPath.d.ts +0 -3
  215. package/dist/src/crud/utils/getByPath.d.ts.map +0 -1
  216. package/dist/src/crud/utils/inlineArrayName.d.ts +0 -3
  217. package/dist/src/crud/utils/inlineArrayName.d.ts.map +0 -1
  218. package/dist/src/crud/utils/inlineFieldName.d.ts +0 -3
  219. package/dist/src/crud/utils/inlineFieldName.d.ts.map +0 -1
  220. package/dist/src/crud/utils/pickBySources.d.ts +0 -6
  221. package/dist/src/crud/utils/pickBySources.d.ts.map +0 -1
  222. package/dist/src/crud/utils/setByPath.d.ts +0 -3
  223. package/dist/src/crud/utils/setByPath.d.ts.map +0 -1
  224. package/dist/src/crud/utils/useResourceFormData.d.ts +0 -47
  225. package/dist/src/crud/utils/useResourceFormData.d.ts.map +0 -1
  226. package/src/crud/context/SubmitFieldsContext.tsx +0 -62
  227. package/src/crud/utils/inlineArrayName.ts +0 -4
  228. package/src/crud/utils/inlineFieldName.ts +0 -8
  229. package/src/crud/utils/pickBySources.ts +0 -24
  230. package/src/crud/utils/useResourceFormData.ts +0 -231
@@ -1,67 +1,67 @@
1
- import { useMemo } from "react";
2
- import type { BaseSourceProps, DisplayProps, ReferenceProps } from "../types";
3
- import { useRegisterColumn } from "../context/ListContext";
4
- import { useChoices } from "../utils/useChoices";
5
-
6
- export type ReferenceManyColumnProps = BaseSourceProps &
7
- ReferenceProps &
8
- DisplayProps & {
9
- sortable?: boolean;
10
- };
11
-
12
- function ReferenceManyColumnCell({
13
- record,
14
- source,
15
- reference,
16
- choices,
17
- optionLabel,
18
- optionValue,
19
- }: ReferenceManyColumnProps & { record: Record<string, unknown> }) {
20
- const { labelsForValues } = useChoices(
21
- choices,
22
- reference,
23
- optionLabel,
24
- optionValue,
25
- );
26
- const raw = record[source];
27
- const values = Array.isArray(raw) ? raw : [];
28
- return <>{labelsForValues(values)}</>;
29
- }
30
-
31
- export function ReferenceManyColumn({
32
- source,
33
- label,
34
- reference,
35
- choices,
36
- optionLabel = "name",
37
- optionValue = "id",
38
- sortable = false,
39
- }: ReferenceManyColumnProps) {
40
- const def = useMemo(
41
- () => ({
42
- key: source,
43
- source,
44
- label,
45
- sortable,
46
- buildColumn: () => ({
47
- title: label ?? source,
48
- dataIndex: source,
49
- key: source,
50
- sorter: sortable ? true : undefined,
51
- render: (_: unknown, record: Record<string, unknown>) => (
52
- <ReferenceManyColumnCell
53
- record={record}
54
- source={source}
55
- reference={reference}
56
- choices={choices}
57
- optionLabel={optionLabel}
58
- optionValue={optionValue}
59
- />
60
- ),
61
- }),
62
- }),
63
- [source, label, sortable, reference, choices, optionLabel, optionValue],
64
- );
65
- useRegisterColumn(def);
66
- return null;
67
- }
1
+ import { useMemo } from "react";
2
+ import type { BaseSourceProps, DisplayProps, ReferenceProps } from "../types";
3
+ import { useRegisterColumn } from "../context/ListContext";
4
+ import { useChoices } from "../utils/useChoices";
5
+
6
+ export type ReferenceManyColumnProps = BaseSourceProps &
7
+ ReferenceProps &
8
+ DisplayProps & {
9
+ sortable?: boolean;
10
+ };
11
+
12
+ function ReferenceManyColumnCell({
13
+ record,
14
+ source,
15
+ reference,
16
+ choices,
17
+ optionLabel,
18
+ optionValue,
19
+ }: ReferenceManyColumnProps & { record: Record<string, unknown> }) {
20
+ const { labelsForValues } = useChoices(
21
+ choices,
22
+ reference,
23
+ optionLabel,
24
+ optionValue,
25
+ );
26
+ const raw = record[source];
27
+ const values = Array.isArray(raw) ? raw : [];
28
+ return <>{labelsForValues(values)}</>;
29
+ }
30
+
31
+ export function ReferenceManyColumn({
32
+ source,
33
+ label,
34
+ reference,
35
+ choices,
36
+ optionLabel = "name",
37
+ optionValue = "id",
38
+ sortable = false,
39
+ }: ReferenceManyColumnProps) {
40
+ const def = useMemo(
41
+ () => ({
42
+ key: source,
43
+ source,
44
+ label,
45
+ sortable,
46
+ buildColumn: () => ({
47
+ title: label ?? source,
48
+ dataIndex: source,
49
+ key: source,
50
+ sorter: sortable ? true : undefined,
51
+ render: (_: unknown, record: Record<string, unknown>) => (
52
+ <ReferenceManyColumnCell
53
+ record={record}
54
+ source={source}
55
+ reference={reference}
56
+ choices={choices}
57
+ optionLabel={optionLabel}
58
+ optionValue={optionValue}
59
+ />
60
+ ),
61
+ }),
62
+ }),
63
+ [source, label, sortable, reference, choices, optionLabel, optionValue],
64
+ );
65
+ useRegisterColumn(def);
66
+ return null;
67
+ }
@@ -1,38 +1,38 @@
1
- import { useMemo } from "react";
2
- import type { BaseSourceProps } from "../types";
3
- import { getByPath } from "../utils/getByPath";
4
- import { useRegisterColumn } from "../context/ListContext";
5
-
6
- export type TextColumnProps = BaseSourceProps & {
7
- sortable?: boolean;
8
- };
9
-
10
- export function TextColumn({ source, label, sortable = true }: TextColumnProps) {
11
- const def = useMemo(
12
- () => ({
13
- key: source,
14
- source,
15
- label,
16
- sortable,
17
- buildColumn: () => ({
18
- title: label ?? source,
19
- dataIndex: source,
20
- key: source,
21
- sorter: sortable ? true : undefined,
22
- }),
23
- }),
24
- [source, label, sortable],
25
- );
26
- useRegisterColumn(def);
27
- return null;
28
- }
29
-
30
- export function renderDisplayValue(
31
- record: Record<string, unknown>,
32
- source: string,
33
- display?: string | ((record: Record<string, unknown>) => unknown),
34
- ): unknown {
35
- if (typeof display === "function") return display(record);
36
- if (display) return getByPath(record, display);
37
- return record[source];
38
- }
1
+ import { useMemo } from "react";
2
+ import type { BaseSourceProps } from "../types";
3
+ import { getFormValue } from "../utils/getFormValue";
4
+ import { useRegisterColumn } from "../context/ListContext";
5
+
6
+ export type TextColumnProps = BaseSourceProps & {
7
+ sortable?: boolean;
8
+ };
9
+
10
+ export function TextColumn({ source, label, sortable = true }: TextColumnProps) {
11
+ const def = useMemo(
12
+ () => ({
13
+ key: source,
14
+ source,
15
+ label,
16
+ sortable,
17
+ buildColumn: () => ({
18
+ title: label ?? source,
19
+ dataIndex: source,
20
+ key: source,
21
+ sorter: sortable ? true : undefined,
22
+ }),
23
+ }),
24
+ [source, label, sortable],
25
+ );
26
+ useRegisterColumn(def);
27
+ return null;
28
+ }
29
+
30
+ export function renderDisplayValue(
31
+ record: Record<string, unknown>,
32
+ source: string,
33
+ display?: string | ((record: Record<string, unknown>) => unknown),
34
+ ): unknown {
35
+ if (typeof display === "function") return display(record);
36
+ if (display) return getFormValue(record, display);
37
+ return record[source];
38
+ }
@@ -1,71 +1,71 @@
1
- import {
2
- createContext,
3
- useCallback,
4
- useContext,
5
- useEffect,
6
- useMemo,
7
- useState,
8
- type ReactNode,
9
- } from "react";
10
- import type { FilterDefinition } from "../types";
11
-
12
- type FilterContextValue = {
13
- registerFilter: (def: FilterDefinition) => () => void;
14
- filters: FilterDefinition[];
15
- values: Record<string, unknown>;
16
- setFilterValue: (source: string, value: unknown) => void;
17
- };
18
-
19
- const FilterContext = createContext<FilterContextValue | null>(null);
20
-
21
- export function FilterContextProvider({
22
- children,
23
- values,
24
- setFilterValue,
25
- }: {
26
- children: ReactNode;
27
- values: Record<string, unknown>;
28
- setFilterValue: (source: string, value: unknown) => void;
29
- }) {
30
- const [filters, setFilters] = useState<FilterDefinition[]>([]);
31
-
32
- const registerFilter = useCallback((def: FilterDefinition) => {
33
- setFilters((prev) => {
34
- const idx = prev.findIndex((f) => f.key === def.key);
35
- if (idx < 0) return [...prev, def];
36
- if (prev[idx] === def) return prev;
37
- const next = [...prev];
38
- next[idx] = def;
39
- return next;
40
- });
41
- return () => {
42
- setFilters((prev) => prev.filter((f) => f.key !== def.key));
43
- };
44
- }, []);
45
-
46
- const value = useMemo(
47
- (): FilterContextValue => ({
48
- filters,
49
- values,
50
- setFilterValue,
51
- registerFilter,
52
- }),
53
- [filters, values, setFilterValue, registerFilter],
54
- );
55
-
56
- return (
57
- <FilterContext.Provider value={value}>{children}</FilterContext.Provider>
58
- );
59
- }
60
-
61
- export function useFilterContext() {
62
- return useContext(FilterContext);
63
- }
64
-
65
- export function useRegisterFilter(def: FilterDefinition) {
66
- const registerFilter = useFilterContext()?.registerFilter;
67
- useEffect(() => {
68
- if (!registerFilter) return;
69
- return registerFilter(def);
70
- }, [registerFilter, def]);
71
- }
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useEffect,
6
+ useMemo,
7
+ useState,
8
+ type ReactNode,
9
+ } from "react";
10
+ import type { FilterDefinition } from "../types";
11
+
12
+ type FilterContextValue = {
13
+ registerFilter: (def: FilterDefinition) => () => void;
14
+ filters: FilterDefinition[];
15
+ values: Record<string, unknown>;
16
+ setFilterValue: (source: string, value: unknown) => void;
17
+ };
18
+
19
+ const FilterContext = createContext<FilterContextValue | null>(null);
20
+
21
+ export function FilterContextProvider({
22
+ children,
23
+ values,
24
+ setFilterValue,
25
+ }: {
26
+ children: ReactNode;
27
+ values: Record<string, unknown>;
28
+ setFilterValue: (source: string, value: unknown) => void;
29
+ }) {
30
+ const [filters, setFilters] = useState<FilterDefinition[]>([]);
31
+
32
+ const registerFilter = useCallback((def: FilterDefinition) => {
33
+ setFilters((prev) => {
34
+ const idx = prev.findIndex((f) => f.key === def.key);
35
+ if (idx < 0) return [...prev, def];
36
+ if (prev[idx] === def) return prev;
37
+ const next = [...prev];
38
+ next[idx] = def;
39
+ return next;
40
+ });
41
+ return () => {
42
+ setFilters((prev) => prev.filter((f) => f.key !== def.key));
43
+ };
44
+ }, []);
45
+
46
+ const value = useMemo(
47
+ (): FilterContextValue => ({
48
+ filters,
49
+ values,
50
+ setFilterValue,
51
+ registerFilter,
52
+ }),
53
+ [filters, values, setFilterValue, registerFilter],
54
+ );
55
+
56
+ return (
57
+ <FilterContext.Provider value={value}>{children}</FilterContext.Provider>
58
+ );
59
+ }
60
+
61
+ export function useFilterContext() {
62
+ return useContext(FilterContext);
63
+ }
64
+
65
+ export function useRegisterFilter(def: FilterDefinition) {
66
+ const registerFilter = useFilterContext()?.registerFilter;
67
+ useEffect(() => {
68
+ if (!registerFilter) return;
69
+ return registerFilter(def);
70
+ }, [registerFilter, def]);
71
+ }
@@ -1,39 +1,39 @@
1
- import { createContext, useContext, type ReactNode } from "react";
2
-
3
- type FormMetaContextValue = {
4
- resource: string;
5
- isNew: boolean;
6
- disabled?: boolean;
7
- };
8
-
9
- const FormMetaContext = createContext<FormMetaContextValue | null>(null);
10
-
11
- export function FormMetaProvider({
12
- children,
13
- resource,
14
- isNew,
15
- disabled,
16
- }: {
17
- children: ReactNode;
18
- resource: string;
19
- isNew: boolean;
20
- disabled?: boolean;
21
- }) {
22
- return (
23
- <FormMetaContext.Provider value={{ resource, isNew, disabled }}>
24
- {children}
25
- </FormMetaContext.Provider>
26
- );
27
- }
28
-
29
- export function useFormMeta() {
30
- const ctx = useContext(FormMetaContext);
31
- if (!ctx) {
32
- throw new Error("Field components must be used within ResourceForm");
33
- }
34
- return ctx;
35
- }
36
-
37
- export function useFormMetaOptional() {
38
- return useContext(FormMetaContext);
39
- }
1
+ import { createContext, useContext, type ReactNode } from "react";
2
+
3
+ type FormMetaContextValue = {
4
+ resource: string;
5
+ isNew: boolean;
6
+ disabled?: boolean;
7
+ };
8
+
9
+ const FormMetaContext = createContext<FormMetaContextValue | null>(null);
10
+
11
+ export function FormMetaProvider({
12
+ children,
13
+ resource,
14
+ isNew,
15
+ disabled,
16
+ }: {
17
+ children: ReactNode;
18
+ resource: string;
19
+ isNew: boolean;
20
+ disabled?: boolean;
21
+ }) {
22
+ return (
23
+ <FormMetaContext.Provider value={{ resource, isNew, disabled }}>
24
+ {children}
25
+ </FormMetaContext.Provider>
26
+ );
27
+ }
28
+
29
+ export function useFormMeta() {
30
+ const ctx = useContext(FormMetaContext);
31
+ if (!ctx) {
32
+ throw new Error("Field components must be used within a resource form");
33
+ }
34
+ return ctx;
35
+ }
36
+
37
+ export function useFormMetaOptional() {
38
+ return useContext(FormMetaContext);
39
+ }
@@ -1,22 +1,22 @@
1
- import { createContext, useContext, type MutableRefObject, type ReactNode } from "react";
2
-
3
- const FormSectionSourcesContext =
4
- createContext<MutableRefObject<Set<string>> | null>(null);
5
-
6
- export function FormSectionSourcesProvider({
7
- sourcesRef,
8
- children,
9
- }: {
10
- sourcesRef: MutableRefObject<Set<string>>;
11
- children: ReactNode;
12
- }) {
13
- return (
14
- <FormSectionSourcesContext.Provider value={sourcesRef}>
15
- {children}
16
- </FormSectionSourcesContext.Provider>
17
- );
18
- }
19
-
20
- export function useFormSectionSourcesOptional() {
21
- return useContext(FormSectionSourcesContext);
22
- }
1
+ import { createContext, useContext, type MutableRefObject, type ReactNode } from "react";
2
+
3
+ const FormSectionSourcesContext =
4
+ createContext<MutableRefObject<Set<string>> | null>(null);
5
+
6
+ export function FormSectionSourcesProvider({
7
+ sourcesRef,
8
+ children,
9
+ }: {
10
+ sourcesRef: MutableRefObject<Set<string>>;
11
+ children: ReactNode;
12
+ }) {
13
+ return (
14
+ <FormSectionSourcesContext.Provider value={sourcesRef}>
15
+ {children}
16
+ </FormSectionSourcesContext.Provider>
17
+ );
18
+ }
19
+
20
+ export function useFormSectionSourcesOptional() {
21
+ return useContext(FormSectionSourcesContext);
22
+ }
@@ -0,0 +1,61 @@
1
+ import {
2
+ createContext,
3
+ useContext,
4
+ useEffect,
5
+ type RefObject,
6
+ type ReactNode,
7
+ } from "react";
8
+
9
+ export type InlineFieldRegistration = {
10
+ field: string;
11
+ sources: string[];
12
+ payloadKey?: string;
13
+ transformRows?: (rows: Record<string, unknown>[]) => unknown;
14
+ };
15
+
16
+ const InlineFieldsRegistryContext =
17
+ createContext<RefObject<Map<string, InlineFieldRegistration>> | null>(
18
+ null,
19
+ );
20
+
21
+ export function InlineFieldsRegistryProvider({
22
+ children,
23
+ registryRef,
24
+ }: {
25
+ children: ReactNode;
26
+ registryRef: RefObject<Map<string, InlineFieldRegistration>>;
27
+ }) {
28
+ return (
29
+ <InlineFieldsRegistryContext.Provider value={registryRef}>
30
+ {children}
31
+ </InlineFieldsRegistryContext.Provider>
32
+ );
33
+ }
34
+
35
+ export function useInlineFieldsRegistryOptional() {
36
+ return useContext(InlineFieldsRegistryContext);
37
+ }
38
+
39
+ /** Register an inline field array (`useFieldArray` name) for save payload cleaning. */
40
+ export function useRegisterInlineField(
41
+ field: string,
42
+ sources: string[],
43
+ payloadKey?: string,
44
+ transformRows?: (rows: Record<string, unknown>[]) => unknown,
45
+ enabled = true,
46
+ ) {
47
+ const registryRef = useInlineFieldsRegistryOptional();
48
+
49
+ useEffect(() => {
50
+ if (!enabled || !registryRef) return;
51
+ registryRef.current.set(field, {
52
+ field,
53
+ sources,
54
+ payloadKey,
55
+ transformRows,
56
+ });
57
+ return () => {
58
+ registryRef.current.delete(field);
59
+ };
60
+ }, [registryRef, field, sources, payloadKey, transformRows, enabled]);
61
+ }