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,57 +1,57 @@
1
- import {
2
- createContext,
3
- useCallback,
4
- useContext,
5
- useMemo,
6
- type ReactNode,
7
- } from "react";
8
-
9
- /**
10
- * Check whether the current user has a permission string from your backend.
11
- * Example: `can("users.create")` or `can("main.add_user")`.
12
- */
13
- export type PermissionsChecker = (permission: string) => boolean;
14
-
15
- const PermissionsContext = createContext<PermissionsChecker | null>(null);
16
-
17
- export type PermissionsProviderProps = {
18
- children: ReactNode;
19
- /** Return true if the current user has this permission string. */
20
- can: PermissionsChecker;
21
- };
22
-
23
- export function PermissionsProvider({
24
- children,
25
- can,
26
- }: PermissionsProviderProps) {
27
- const memo = useMemo(() => can, [can]);
28
- return (
29
- <PermissionsContext.Provider value={memo}>
30
- {children}
31
- </PermissionsContext.Provider>
32
- );
33
- }
34
-
35
- export function usePermissions(): PermissionsChecker {
36
- const ctx = useContext(PermissionsContext);
37
- if (!ctx) {
38
- throw new Error(
39
- "usePermissions must be used within PermissionsProvider",
40
- );
41
- }
42
- return ctx;
43
- }
44
-
45
- /** Build a `can(permission)` checker from the user's permission strings (e.g. from login). */
46
- export function createPermissionsChecker(
47
- getPermissions: () => string[] | undefined,
48
- ): PermissionsChecker {
49
- return (permission: string) =>
50
- getPermissions()?.includes(permission) ?? false;
51
- }
52
-
53
- /** Convenience: `const allowed = useCan("users.create")();` */
54
- export function useCan(permission: string) {
55
- const can = usePermissions();
56
- return useCallback(() => can(permission), [can, permission]);
57
- }
1
+ import {
2
+ createContext,
3
+ useCallback,
4
+ useContext,
5
+ useMemo,
6
+ type ReactNode,
7
+ } from "react";
8
+
9
+ /**
10
+ * Check whether the current user has a permission string from your backend.
11
+ * Example: `can("users.create")` or `can("main.add_user")`.
12
+ */
13
+ export type PermissionsChecker = (permission: string) => boolean;
14
+
15
+ const PermissionsContext = createContext<PermissionsChecker | null>(null);
16
+
17
+ export type PermissionsProviderProps = {
18
+ children: ReactNode;
19
+ /** Return true if the current user has this permission string. */
20
+ can: PermissionsChecker;
21
+ };
22
+
23
+ export function PermissionsProvider({
24
+ children,
25
+ can,
26
+ }: PermissionsProviderProps) {
27
+ const memo = useMemo(() => can, [can]);
28
+ return (
29
+ <PermissionsContext.Provider value={memo}>
30
+ {children}
31
+ </PermissionsContext.Provider>
32
+ );
33
+ }
34
+
35
+ export function usePermissions(): PermissionsChecker {
36
+ const ctx = useContext(PermissionsContext);
37
+ if (!ctx) {
38
+ throw new Error(
39
+ "usePermissions must be used within PermissionsProvider",
40
+ );
41
+ }
42
+ return ctx;
43
+ }
44
+
45
+ /** Build a `can(permission)` checker from the user's permission strings (e.g. from login). */
46
+ export function createPermissionsChecker(
47
+ getPermissions: () => string[] | undefined,
48
+ ): PermissionsChecker {
49
+ return (permission: string) =>
50
+ getPermissions()?.includes(permission) ?? false;
51
+ }
52
+
53
+ /** Convenience: `const allowed = useCan("users.create")();` */
54
+ export function useCan(permission: string) {
55
+ const can = usePermissions();
56
+ return useCallback(() => can(permission), [can, permission]);
57
+ }
@@ -1,36 +1,36 @@
1
- import { Space, Typography } from "antd";
2
- import type { ReactNode } from "react";
3
- import { useFilterContext } from "./context/FilterContext";
4
-
5
- function FilterBarUI() {
6
- const ctx = useFilterContext();
7
- if (!ctx || ctx.filters.length === 0) return null;
8
-
9
- return (
10
- <Space wrap size="middle" style={{ marginBottom: 16 }}>
11
- {ctx.filters.map((f) => (
12
- <Space key={f.key} orientation="vertical" size={2}>
13
- {f.label ? (
14
- <Typography.Text type="secondary" style={{ fontSize: 12 }}>
15
- {f.label}
16
- </Typography.Text>
17
- ) : null}
18
- {f.render({
19
- value: ctx.values[f.source],
20
- onChange: (v) => ctx.setFilterValue(f.source, v),
21
- })}
22
- </Space>
23
- ))}
24
- </Space>
25
- );
26
- }
27
-
28
- /** Wrap filter field components; renders filter controls above the table. */
29
- export function FilterBar({ children }: { children: ReactNode }) {
30
- return (
31
- <>
32
- {children}
33
- <FilterBarUI />
34
- </>
35
- );
36
- }
1
+ import { Space, Typography } from "antd";
2
+ import type { ReactNode } from "react";
3
+ import { useFilterContext } from "./context/FilterContext";
4
+
5
+ function FilterBarUI() {
6
+ const ctx = useFilterContext();
7
+ if (!ctx || ctx.filters.length === 0) return null;
8
+
9
+ return (
10
+ <Space wrap size="middle" style={{ marginBottom: 16 }}>
11
+ {ctx.filters.map((f) => (
12
+ <Space key={f.key} orientation="vertical" size={2}>
13
+ {f.label ? (
14
+ <Typography.Text type="secondary" style={{ fontSize: 12 }}>
15
+ {f.label}
16
+ </Typography.Text>
17
+ ) : null}
18
+ {f.render({
19
+ value: ctx.values[f.source],
20
+ onChange: (v) => ctx.setFilterValue(f.source, v),
21
+ })}
22
+ </Space>
23
+ ))}
24
+ </Space>
25
+ );
26
+ }
27
+
28
+ /** Wrap filter field components; renders filter controls above the table. */
29
+ export function FilterBar({ children }: { children: ReactNode }) {
30
+ return (
31
+ <>
32
+ {children}
33
+ <FilterBarUI />
34
+ </>
35
+ );
36
+ }
@@ -0,0 +1,36 @@
1
+ import { Alert } from "antd";
2
+
3
+ export type FormGlobalErrorsAlertProps = {
4
+ errors: string[];
5
+ };
6
+
7
+ export function FormGlobalErrorsAlert({ errors }: FormGlobalErrorsAlertProps) {
8
+ if (!errors.length) return null;
9
+
10
+ if (errors.length === 1) {
11
+ return (
12
+ <Alert
13
+ type="error"
14
+ title={errors[0]}
15
+ showIcon
16
+ style={{ marginBottom: 16 }}
17
+ />
18
+ );
19
+ }
20
+
21
+ return (
22
+ <Alert
23
+ type="error"
24
+ title="Could not save"
25
+ showIcon
26
+ style={{ marginBottom: 16 }}
27
+ description={
28
+ <ul style={{ margin: 0, paddingLeft: 20 }}>
29
+ {errors.map((msg) => (
30
+ <li key={msg}>{msg}</li>
31
+ ))}
32
+ </ul>
33
+ }
34
+ />
35
+ );
36
+ }
@@ -1,135 +1,135 @@
1
- import { Button, Space, Steps, type StepsProps } from "antd";
2
- import {
3
- Children,
4
- isValidElement,
5
- useCallback,
6
- useMemo,
7
- useState,
8
- type CSSProperties,
9
- type ReactElement,
10
- type ReactNode,
11
- } from "react";
12
- import { useFormContext, useFormState } from "react-hook-form";
13
- import { FormSectionSourcesProvider } from "./context/FormSectionContext";
14
- import {
15
- sectionHasError,
16
- useActivateFirstErrorSection,
17
- useSectionSourceRefs,
18
- } from "./utils/formSectionErrors";
19
-
20
- export type FormStepProps = {
21
- title: ReactNode;
22
- description?: ReactNode;
23
- children: ReactNode;
24
- };
25
-
26
- export function FormStep(_props: FormStepProps) {
27
- return null;
28
- }
29
-
30
- function isFormStep(child: ReactNode): child is ReactElement<FormStepProps> {
31
- return isValidElement(child) && child.type === FormStep;
32
- }
33
-
34
- export type FormStepsProps = {
35
- children: ReactNode;
36
- initialStep?: number;
37
- /** Show Previous / Next controls below the active step. Default true. */
38
- showNavigation?: boolean;
39
- /** Allow clicking step headers to change the active step. Default false. */
40
- allowStepSelect?: boolean;
41
- stepsStyle?: CSSProperties;
42
- navigationStyle?: CSSProperties;
43
- } & Partial<Pick<StepsProps, "size" | "direction" | "type" | "status">>;
44
-
45
- export function FormSteps({
46
- children,
47
- initialStep = 0,
48
- showNavigation = true,
49
- allowStepSelect = false,
50
- stepsStyle,
51
- navigationStyle,
52
- size,
53
- direction,
54
- type,
55
- status,
56
- }: FormStepsProps) {
57
- const steps = useMemo(
58
- () => Children.toArray(children).filter(isFormStep),
59
- [children],
60
- );
61
- const sectionRefs = useSectionSourceRefs(steps.length);
62
- const [current, setCurrent] = useState(initialStep);
63
- const lastIndex = steps.length - 1;
64
-
65
- useActivateFirstErrorSection(sectionRefs, setCurrent);
66
-
67
- const { control, getFieldState } = useFormContext();
68
- const formState = useFormState({ control });
69
-
70
- const stepItems = useMemo(
71
- () =>
72
- steps.map((step, index) => {
73
- const hasError = sectionHasError(
74
- sectionRefs[index].current,
75
- getFieldState,
76
- formState,
77
- );
78
-
79
- return {
80
- title: step.props.title,
81
- description: step.props.description,
82
- status: hasError ? ("error" as const) : undefined,
83
- };
84
- }),
85
- [formState, getFieldState, sectionRefs, steps],
86
- );
87
-
88
- const handleStepSelect = useCallback(
89
- (next: number) => {
90
- setCurrent(next);
91
- },
92
- [],
93
- );
94
-
95
- return (
96
- <>
97
- <Steps
98
- current={current}
99
- items={stepItems}
100
- style={{ marginBottom: 24, ...stepsStyle }}
101
- onChange={allowStepSelect ? handleStepSelect : undefined}
102
- size={size}
103
- direction={direction}
104
- type={type}
105
- status={status}
106
- />
107
-
108
- {steps.map((step, index) => (
109
- <div
110
- key={(step.key as string | undefined) ?? String(index)}
111
- style={{ display: current === index ? undefined : "none" }}
112
- >
113
- <FormSectionSourcesProvider sourcesRef={sectionRefs[index]}>
114
- {step.props.children}
115
- </FormSectionSourcesProvider>
116
- </div>
117
- ))}
118
-
119
- {showNavigation && steps.length > 1 ? (
120
- <Space style={{ marginTop: 16, ...navigationStyle }}>
121
- <Button disabled={current === 0} onClick={() => setCurrent((s) => s - 1)}>
122
- Previous
123
- </Button>
124
- <Button
125
- type="primary"
126
- disabled={current === lastIndex}
127
- onClick={() => setCurrent((s) => s + 1)}
128
- >
129
- Next
130
- </Button>
131
- </Space>
132
- ) : null}
133
- </>
134
- );
135
- }
1
+ import { Button, Space, Steps, type StepsProps } from "antd";
2
+ import {
3
+ Children,
4
+ isValidElement,
5
+ useCallback,
6
+ useMemo,
7
+ useState,
8
+ type CSSProperties,
9
+ type ReactElement,
10
+ type ReactNode,
11
+ } from "react";
12
+ import { useFormContext, useFormState } from "react-hook-form";
13
+ import { FormSectionSourcesProvider } from "./context/FormSectionContext";
14
+ import {
15
+ sectionHasError,
16
+ useActivateFirstErrorSection,
17
+ useSectionSourceRefs,
18
+ } from "./utils/formSectionErrors";
19
+
20
+ export type FormStepProps = {
21
+ title: ReactNode;
22
+ description?: ReactNode;
23
+ children: ReactNode;
24
+ };
25
+
26
+ export function FormStep(_props: FormStepProps) {
27
+ return null;
28
+ }
29
+
30
+ function isFormStep(child: ReactNode): child is ReactElement<FormStepProps> {
31
+ return isValidElement(child) && child.type === FormStep;
32
+ }
33
+
34
+ export type FormStepsProps = {
35
+ children: ReactNode;
36
+ initialStep?: number;
37
+ /** Show Previous / Next controls below the active step. Default true. */
38
+ showNavigation?: boolean;
39
+ /** Allow clicking step headers to change the active step. Default false. */
40
+ allowStepSelect?: boolean;
41
+ stepsStyle?: CSSProperties;
42
+ navigationStyle?: CSSProperties;
43
+ } & Partial<Pick<StepsProps, "size" | "direction" | "type" | "status">>;
44
+
45
+ export function FormSteps({
46
+ children,
47
+ initialStep = 0,
48
+ showNavigation = true,
49
+ allowStepSelect = false,
50
+ stepsStyle,
51
+ navigationStyle,
52
+ size,
53
+ direction,
54
+ type,
55
+ status,
56
+ }: FormStepsProps) {
57
+ const steps = useMemo(
58
+ () => Children.toArray(children).filter(isFormStep),
59
+ [children],
60
+ );
61
+ const sectionRefs = useSectionSourceRefs(steps.length);
62
+ const [current, setCurrent] = useState(initialStep);
63
+ const lastIndex = steps.length - 1;
64
+
65
+ useActivateFirstErrorSection(sectionRefs, setCurrent);
66
+
67
+ const { control, getFieldState } = useFormContext();
68
+ const formState = useFormState({ control });
69
+
70
+ const stepItems = useMemo(
71
+ () =>
72
+ steps.map((step, index) => {
73
+ const hasError = sectionHasError(
74
+ sectionRefs[index].current,
75
+ getFieldState,
76
+ formState,
77
+ );
78
+
79
+ return {
80
+ title: step.props.title,
81
+ description: step.props.description,
82
+ status: hasError ? ("error" as const) : undefined,
83
+ };
84
+ }),
85
+ [formState, getFieldState, sectionRefs, steps],
86
+ );
87
+
88
+ const handleStepSelect = useCallback(
89
+ (next: number) => {
90
+ setCurrent(next);
91
+ },
92
+ [],
93
+ );
94
+
95
+ return (
96
+ <>
97
+ <Steps
98
+ current={current}
99
+ items={stepItems}
100
+ style={{ marginBottom: 24, ...stepsStyle }}
101
+ onChange={allowStepSelect ? handleStepSelect : undefined}
102
+ size={size}
103
+ direction={direction}
104
+ type={type}
105
+ status={status}
106
+ />
107
+
108
+ {steps.map((step, index) => (
109
+ <div
110
+ key={(step.key as string | undefined) ?? String(index)}
111
+ style={{ display: current === index ? undefined : "none" }}
112
+ >
113
+ <FormSectionSourcesProvider sourcesRef={sectionRefs[index]}>
114
+ {step.props.children}
115
+ </FormSectionSourcesProvider>
116
+ </div>
117
+ ))}
118
+
119
+ {showNavigation && steps.length > 1 ? (
120
+ <Space style={{ marginTop: 16, ...navigationStyle }}>
121
+ <Button disabled={current === 0} onClick={() => setCurrent((s) => s - 1)}>
122
+ Previous
123
+ </Button>
124
+ <Button
125
+ type="primary"
126
+ disabled={current === lastIndex}
127
+ onClick={() => setCurrent((s) => s + 1)}
128
+ >
129
+ Next
130
+ </Button>
131
+ </Space>
132
+ ) : null}
133
+ </>
134
+ );
135
+ }