ding-react-admin 1.0.2 → 1.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 (86) hide show
  1. package/README.md +36 -5
  2. package/dist/index.js +712 -723
  3. package/dist/src/crud/InlineFormSet.d.ts +13 -11
  4. package/dist/src/crud/InlineFormSet.d.ts.map +1 -1
  5. package/dist/src/crud/ResourceForm.d.ts +10 -2
  6. package/dist/src/crud/ResourceForm.d.ts.map +1 -1
  7. package/dist/src/crud/ResourceFormModal.d.ts.map +1 -1
  8. package/dist/src/crud/ResourceList.d.ts.map +1 -1
  9. package/dist/src/crud/context/SubmitFieldsContext.d.ts +18 -0
  10. package/dist/src/crud/context/SubmitFieldsContext.d.ts.map +1 -0
  11. package/dist/src/crud/fields/BooleanField.d.ts +3 -1
  12. package/dist/src/crud/fields/BooleanField.d.ts.map +1 -1
  13. package/dist/src/crud/fields/DateField.d.ts +3 -1
  14. package/dist/src/crud/fields/DateField.d.ts.map +1 -1
  15. package/dist/src/crud/fields/FieldWrapper.d.ts +7 -14
  16. package/dist/src/crud/fields/FieldWrapper.d.ts.map +1 -1
  17. package/dist/src/crud/fields/NumberField.d.ts +5 -3
  18. package/dist/src/crud/fields/NumberField.d.ts.map +1 -1
  19. package/dist/src/crud/fields/PasswordField.d.ts +5 -3
  20. package/dist/src/crud/fields/PasswordField.d.ts.map +1 -1
  21. package/dist/src/crud/fields/ReferenceField.d.ts +6 -5
  22. package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -1
  23. package/dist/src/crud/fields/ReferenceManyField.d.ts +3 -1
  24. package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -1
  25. package/dist/src/crud/fields/SelectField.d.ts +3 -1
  26. package/dist/src/crud/fields/SelectField.d.ts.map +1 -1
  27. package/dist/src/crud/fields/TextField.d.ts +5 -3
  28. package/dist/src/crud/fields/TextField.d.ts.map +1 -1
  29. package/dist/src/crud/index.d.ts +8 -4
  30. package/dist/src/crud/index.d.ts.map +1 -1
  31. package/dist/src/crud/types.d.ts +21 -20
  32. package/dist/src/crud/types.d.ts.map +1 -1
  33. package/dist/src/crud/utils/inlineFieldName.d.ts +3 -0
  34. package/dist/src/crud/utils/inlineFieldName.d.ts.map +1 -0
  35. package/dist/src/crud/utils/useAbortableEffect.d.ts +7 -0
  36. package/dist/src/crud/utils/useAbortableEffect.d.ts.map +1 -0
  37. package/dist/src/crud/utils/useResourceFormData.d.ts +47 -0
  38. package/dist/src/crud/utils/useResourceFormData.d.ts.map +1 -0
  39. package/dist/src/data/abortError.d.ts +3 -0
  40. package/dist/src/data/abortError.d.ts.map +1 -0
  41. package/dist/src/data/abortError.test.d.ts +2 -0
  42. package/dist/src/data/abortError.test.d.ts.map +1 -0
  43. package/dist/src/data/createRestResourceHandlers.d.ts +2 -2
  44. package/dist/src/data/createRestResourceHandlers.d.ts.map +1 -1
  45. package/dist/src/data/dataProviderTypes.d.ts +7 -1
  46. package/dist/src/data/dataProviderTypes.d.ts.map +1 -1
  47. package/dist/src/data/resourceHandlers.d.ts +2 -2
  48. package/dist/src/data/resourceHandlers.d.ts.map +1 -1
  49. package/dist/src/index.d.ts +4 -3
  50. package/dist/src/index.d.ts.map +1 -1
  51. package/package.json +59 -59
  52. package/src/crud/InlineFormSet.tsx +77 -89
  53. package/src/crud/ResourceForm.tsx +87 -202
  54. package/src/crud/ResourceFormModal.tsx +37 -25
  55. package/src/crud/ResourceList.tsx +35 -26
  56. package/src/crud/context/SubmitFieldsContext.tsx +62 -0
  57. package/src/crud/fields/BooleanField.tsx +22 -16
  58. package/src/crud/fields/DateField.tsx +32 -26
  59. package/src/crud/fields/FieldWrapper.tsx +32 -54
  60. package/src/crud/fields/NumberField.tsx +36 -34
  61. package/src/crud/fields/PasswordField.tsx +41 -40
  62. package/src/crud/fields/ReferenceField.tsx +35 -130
  63. package/src/crud/fields/ReferenceManyField.tsx +31 -25
  64. package/src/crud/fields/SelectField.tsx +6 -0
  65. package/src/crud/fields/TextField.tsx +32 -30
  66. package/src/crud/index.ts +19 -2
  67. package/src/crud/types.ts +132 -130
  68. package/src/crud/utils/inlineFieldName.ts +8 -0
  69. package/src/crud/utils/useAbortableEffect.ts +17 -0
  70. package/src/crud/utils/useResourceFormData.ts +231 -0
  71. package/src/data/abortError.test.ts +19 -0
  72. package/src/data/abortError.ts +10 -0
  73. package/src/data/createMemoryResourceHandlers.ts +1 -1
  74. package/src/data/createRestResourceHandlers.ts +4 -4
  75. package/src/data/dataProviderTypes.ts +8 -0
  76. package/src/data/resourceHandlers.ts +4 -3
  77. package/src/index.ts +14 -1
  78. package/dist/src/crud/context/FormFieldsContext.d.ts +0 -14
  79. package/dist/src/crud/context/FormFieldsContext.d.ts.map +0 -1
  80. package/dist/src/crud/context/InlineFormContext.d.ts +0 -16
  81. package/dist/src/crud/context/InlineFormContext.d.ts.map +0 -1
  82. package/dist/src/crud/fields/useInlineOrFormField.d.ts +0 -10
  83. package/dist/src/crud/fields/useInlineOrFormField.d.ts.map +0 -1
  84. package/src/crud/context/FormFieldsContext.tsx +0 -76
  85. package/src/crud/context/InlineFormContext.tsx +0 -71
  86. package/src/crud/fields/useInlineOrFormField.tsx +0 -91
@@ -0,0 +1,231 @@
1
+ import type { MessageInstance } from "antd/es/message/interface";
2
+ import {
3
+ useCallback,
4
+ useState,
5
+ type MutableRefObject,
6
+ } from "react";
7
+ import type { NavigateFunction } from "react-router-dom";
8
+ import type { DataProvider } from "../../data/dataProviderTypes";
9
+ import { isAbortError } from "../../data/abortError";
10
+ import {
11
+ type DefaultValues,
12
+ type FieldValues,
13
+ type UseFormReturn,
14
+ } from "react-hook-form";
15
+ import {
16
+ loadInlineRows,
17
+ saveInlineRows,
18
+ type InlineFormSetProps,
19
+ } from "../InlineFormSet";
20
+ import { inlineArrayName } from "./inlineArrayName";
21
+ import { pickBySources } from "./pickBySources";
22
+ import { parseAndApplyFormErrors } from "./formErrors";
23
+ import { useAbortableEffect } from "./useAbortableEffect";
24
+
25
+ export type ResourceFormInlineConfig = Pick<
26
+ InlineFormSetProps,
27
+ "resource" | "foreignKey" | "name"
28
+ >;
29
+
30
+ export function resolveInlineArrayName(config: ResourceFormInlineConfig) {
31
+ return inlineArrayName(config.resource, config.name);
32
+ }
33
+
34
+ type LoadOptions<T extends FieldValues> = {
35
+ dp: DataProvider;
36
+ resource: string;
37
+ id: string | undefined;
38
+ isNew: boolean;
39
+ form: UseFormReturn<T>;
40
+ message: MessageInstance;
41
+ defaultValues?: Partial<T>;
42
+ inlines?: ResourceFormInlineConfig[];
43
+ };
44
+
45
+ /** Edit mode: fetch parent (+ inline rows) → form.reset → bump formVersion for field arrays. */
46
+ export function useResourceFormLoad<T extends FieldValues>({
47
+ dp,
48
+ resource,
49
+ id,
50
+ isNew,
51
+ form,
52
+ message,
53
+ defaultValues,
54
+ inlines,
55
+ }: LoadOptions<T>) {
56
+ const [loading, setLoading] = useState(!isNew);
57
+ const [formVersion, setFormVersion] = useState(0);
58
+ const [existingInlineIds, setExistingInlineIds] = useState<
59
+ Record<string, (string | number)[]>
60
+ >({});
61
+
62
+ const load = useCallback(
63
+ async (signal?: AbortSignal) => {
64
+ if (isNew || !id) {
65
+ if (defaultValues) {
66
+ form.reset({ ...defaultValues } as DefaultValues<T>);
67
+ }
68
+ setLoading(false);
69
+ return;
70
+ }
71
+ setLoading(true);
72
+ try {
73
+ const res = await dp.getOne(resource, id, { signal });
74
+ if (signal?.aborted) return;
75
+ const record = res.data as DefaultValues<T>;
76
+ if (inlines?.length) {
77
+ const merged = { ...record } as DefaultValues<T>;
78
+ const idsMap: Record<string, (string | number)[]> = {};
79
+ for (const cfg of inlines) {
80
+ const arrayName = resolveInlineArrayName(cfg);
81
+ const { rows, ids } = await loadInlineRows(
82
+ dp,
83
+ cfg.resource,
84
+ cfg.foreignKey,
85
+ id,
86
+ );
87
+ (merged as Record<string, unknown>)[arrayName] = rows;
88
+ idsMap[arrayName] = ids;
89
+ }
90
+ if (signal?.aborted) return;
91
+ form.reset(merged);
92
+ setExistingInlineIds(idsMap);
93
+ setFormVersion((v) => v + 1);
94
+ } else {
95
+ form.reset(record);
96
+ setFormVersion((v) => v + 1);
97
+ }
98
+ } catch (e) {
99
+ if (!isAbortError(e)) {
100
+ message.error(e instanceof Error ? e.message : "Load failed");
101
+ }
102
+ } finally {
103
+ if (!signal?.aborted) setLoading(false);
104
+ }
105
+ },
106
+ [dp, resource, id, isNew, form, message, defaultValues, inlines],
107
+ );
108
+
109
+ useAbortableEffect((signal) => load(signal), [load]);
110
+
111
+ return { loading, formVersion, existingInlineIds };
112
+ }
113
+
114
+ type SubmitOptions<T extends FieldValues & { id?: unknown }> = {
115
+ dp: DataProvider;
116
+ resource: string;
117
+ id: string | undefined;
118
+ isNew: boolean;
119
+ form: UseFormReturn<T>;
120
+ message: MessageInstance;
121
+ navigate: NavigateFunction;
122
+ listPath: string;
123
+ submitFieldsRef: MutableRefObject<Set<string>>;
124
+ inlines?: ResourceFormInlineConfig[];
125
+ existingInlineIds: Record<string, (string | number)[]>;
126
+ onSaved?: (record: T) => void;
127
+ stayOnPage?: boolean;
128
+ };
129
+
130
+ /** Save: pickBySources → create/update parent → saveInlineRows for each inline config. */
131
+ export function useResourceFormSubmit<T extends FieldValues & { id?: unknown }>({
132
+ dp,
133
+ resource,
134
+ id,
135
+ isNew,
136
+ form,
137
+ message,
138
+ navigate,
139
+ listPath,
140
+ submitFieldsRef,
141
+ inlines,
142
+ existingInlineIds,
143
+ onSaved,
144
+ stayOnPage,
145
+ }: SubmitOptions<T>) {
146
+ return useCallback(
147
+ async (values: T) => {
148
+ try {
149
+ const raw = values as Record<string, unknown>;
150
+ let payload = pickBySources(raw, Array.from(submitFieldsRef.current));
151
+ if (inlines?.length) {
152
+ for (const cfg of inlines) {
153
+ delete payload[resolveInlineArrayName(cfg)];
154
+ }
155
+ }
156
+
157
+ let saved: T;
158
+ if (isNew) {
159
+ const res = await dp.create(resource, payload);
160
+ saved = res.data as T;
161
+ message.success("Created");
162
+ } else if (id) {
163
+ const res = await dp.update(resource, { id, data: payload });
164
+ saved = res.data as T;
165
+ message.success("Updated");
166
+ } else {
167
+ return;
168
+ }
169
+
170
+ const parentId = saved.id as string | number;
171
+ if (inlines?.length && parentId != null) {
172
+ for (const cfg of inlines) {
173
+ const arrayName = resolveInlineArrayName(cfg);
174
+ const rows =
175
+ ((form.getValues(arrayName as never) as unknown) as
176
+ | Record<string, unknown>[]
177
+ | undefined) ?? [];
178
+ const inlineOk = await saveInlineRows(dp, {
179
+ resource: cfg.resource,
180
+ foreignKey: cfg.foreignKey,
181
+ parentId,
182
+ rows,
183
+ existingIds: existingInlineIds[arrayName] ?? [],
184
+ onRowError: (e, index) => {
185
+ const row = rows[index];
186
+ const rowId = row?.id as string | number | undefined;
187
+ return parseAndApplyFormErrors(dp, form, message, e, {
188
+ resource: cfg.resource,
189
+ mutation:
190
+ rowId != null &&
191
+ (existingInlineIds[arrayName] ?? []).some(
192
+ (existingId) => existingId === rowId,
193
+ )
194
+ ? "update"
195
+ : "create",
196
+ inlineArrayName: arrayName,
197
+ rowIndex: index,
198
+ });
199
+ },
200
+ });
201
+ if (!inlineOk) return;
202
+ }
203
+ }
204
+
205
+ onSaved?.(saved);
206
+ if (!stayOnPage) navigate(listPath);
207
+ } catch (e) {
208
+ parseAndApplyFormErrors(dp, form, message, e, {
209
+ resource,
210
+ mutation: isNew ? "create" : "update",
211
+ });
212
+ message.error(e instanceof Error ? e.message : "Save failed");
213
+ }
214
+ },
215
+ [
216
+ dp,
217
+ resource,
218
+ id,
219
+ isNew,
220
+ form,
221
+ message,
222
+ navigate,
223
+ listPath,
224
+ submitFieldsRef,
225
+ inlines,
226
+ existingInlineIds,
227
+ onSaved,
228
+ stayOnPage,
229
+ ],
230
+ );
231
+ }
@@ -0,0 +1,19 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { isAbortError } from "./abortError";
3
+
4
+ describe("isAbortError", () => {
5
+ it("detects DOM AbortError", () => {
6
+ expect(isAbortError({ name: "AbortError" })).toBe(true);
7
+ });
8
+
9
+ it("detects axios CanceledError", () => {
10
+ expect(isAbortError({ name: "CanceledError", code: "ERR_CANCELED" })).toBe(
11
+ true,
12
+ );
13
+ });
14
+
15
+ it("returns false for ordinary errors", () => {
16
+ expect(isAbortError(new Error("Network Error"))).toBe(false);
17
+ expect(isAbortError(null)).toBe(false);
18
+ });
19
+ });
@@ -0,0 +1,10 @@
1
+ /** True when a fetch/axios call was aborted via `AbortSignal` (not a real failure). */
2
+ export function isAbortError(error: unknown): boolean {
3
+ if (error == null || typeof error !== "object") return false;
4
+ const e = error as { name?: string; code?: string };
5
+ return (
6
+ e.name === "AbortError" ||
7
+ e.name === "CanceledError" ||
8
+ e.code === "ERR_CANCELED"
9
+ );
10
+ }
@@ -48,7 +48,7 @@ export function createMemoryResourceHandlers<
48
48
  return applyInMemoryListParams(asRows(base), params) as GetListResult<T>;
49
49
  },
50
50
 
51
- async getOne(id): Promise<GetOneResult<T>> {
51
+ async getOne(id, _params?): Promise<GetOneResult<T>> {
52
52
  return { data: getById(config.getRows(), id) as T };
53
53
  },
54
54
 
@@ -1,11 +1,11 @@
1
- import type { GetListParams, Identifier } from "./dataProviderTypes";
1
+ import type { GetListParams, GetOneParams, Identifier } from "./dataProviderTypes";
2
2
  import type { ResourceHandlers } from "./resourceHandlers";
3
3
 
4
4
  export type RestResourceHandlersConfig<
5
5
  T extends Record<string, unknown> = Record<string, unknown>,
6
6
  > = {
7
7
  list: (params: GetListParams) => Promise<{ data: T[]; total: number }>;
8
- retrieve: (id: Identifier) => Promise<T>;
8
+ retrieve: (id: Identifier, params?: GetOneParams) => Promise<T>;
9
9
  create: (data: Record<string, unknown>) => Promise<T>;
10
10
  update: (id: Identifier, data: Record<string, unknown>) => Promise<T>;
11
11
  destroy: (id: Identifier) => Promise<void>;
@@ -27,8 +27,8 @@ export function createRestResourceHandlers<
27
27
  return config.list(params);
28
28
  },
29
29
 
30
- async getOne(id) {
31
- return { data: await config.retrieve(id) };
30
+ async getOne(id, params) {
31
+ return { data: await config.retrieve(id, params) };
32
32
  },
33
33
 
34
34
  async create(data) {
@@ -13,12 +13,19 @@ export type PaginationParams = {
13
13
  perPage: number;
14
14
  };
15
15
 
16
+ export type GetOneParams = {
17
+ /** Forward to fetch/axios to cancel in-flight reads when the UI unmounts or deps change. */
18
+ signal?: AbortSignal;
19
+ };
20
+
16
21
  export type GetListParams = {
17
22
  pagination?: PaginationParams;
18
23
  /** Single or multi-column sort. */
19
24
  sort?: SortSpec | SortSpec[];
20
25
  /** Filter values; arrays mean "match any of" for that field. */
21
26
  filter?: Record<string, unknown>;
27
+ /** Forward to fetch/axios to cancel in-flight reads when the UI unmounts or deps change. */
28
+ signal?: AbortSignal;
22
29
  };
23
30
 
24
31
  export type GetListResult<RecordType extends Record<string, unknown>> = {
@@ -83,6 +90,7 @@ export type DataProvider<
83
90
  getOne: (
84
91
  resource: string,
85
92
  id: Identifier,
93
+ params?: GetOneParams,
86
94
  ) => Promise<GetOneResult<RecordType>>;
87
95
  create: (
88
96
  resource: string,
@@ -7,6 +7,7 @@ import type {
7
7
  DeleteResult,
8
8
  GetListParams,
9
9
  GetListResult,
10
+ GetOneParams,
10
11
  GetOneResult,
11
12
  Identifier,
12
13
  ParseFormError,
@@ -27,7 +28,7 @@ export type ResourceHandlers<
27
28
  RecordType extends Record<string, unknown> = Record<string, unknown>,
28
29
  > = {
29
30
  getList: (params: GetListParams) => Promise<GetListResult<RecordType>>;
30
- getOne: (id: Identifier) => Promise<GetOneResult<RecordType>>;
31
+ getOne: (id: Identifier, params?: GetOneParams) => Promise<GetOneResult<RecordType>>;
31
32
  create: (data: Partial<RecordType>) => Promise<CreateResult<RecordType>>;
32
33
  update: (
33
34
  params: UpdateParams<RecordType>,
@@ -95,11 +96,11 @@ export function combineResourceHandlers(
95
96
  assertPermission(can, permissions, "list");
96
97
  return h.getList(params);
97
98
  },
98
- async getOne(resource, id) {
99
+ async getOne(resource, id, params) {
99
100
  const { handlers: h, permissions } = resolve(resource);
100
101
  guard?.(resource, "read");
101
102
  assertPermission(can, permissions, "read");
102
- return h.getOne(id);
103
+ return h.getOne(id, params);
103
104
  },
104
105
  async create(resource, data) {
105
106
  const { handlers: h, permissions } = resolve(resource);
package/src/index.ts CHANGED
@@ -28,6 +28,7 @@ export type {
28
28
  DeleteResult,
29
29
  GetListParams,
30
30
  GetListResult,
31
+ GetOneParams,
31
32
  GetOneResult,
32
33
  Identifier,
33
34
  PaginationParams,
@@ -41,6 +42,7 @@ export type {
41
42
  ParseFormErrorContext,
42
43
  } from "./data/dataProviderTypes";
43
44
  export { combineResourceHandlers } from "./data/resourceHandlers";
45
+ export { isAbortError } from "./data/abortError";
44
46
  export {
45
47
  parseDjangoDRFFormErrors,
46
48
  parseDotNetFormErrors,
@@ -118,6 +120,7 @@ export {
118
120
  ResourceForm,
119
121
  ResourceFormModal,
120
122
  InlineFormSet,
123
+ InlineFormSetStacked,
121
124
  FormTabs,
122
125
  FormTab,
123
126
  FormSteps,
@@ -132,6 +135,7 @@ export {
132
135
  PasswordField,
133
136
  ReferenceField,
134
137
  ReferenceManyField,
138
+ FieldWrapper,
135
139
  TextColumn,
136
140
  NumberColumn,
137
141
  BooleanColumn,
@@ -148,17 +152,21 @@ export {
148
152
  ReferenceManyFilter,
149
153
  useListQueryState,
150
154
  useChoices,
155
+ useAbortableEffect,
151
156
  getByPath,
152
157
  pickBySources,
153
158
  inlineArrayName,
159
+ inlineFieldName,
154
160
  useResourceListContext,
155
- useRegisterFormSource,
161
+ useSubmitField,
162
+ useSectionField,
156
163
  } from "./crud";
157
164
  export type {
158
165
  ResourceFormProps,
159
166
  ResourceFormInlineConfig,
160
167
  ResourceFormModalProps,
161
168
  InlineFormSetProps,
169
+ InlineFormSetStackedProps,
162
170
  FormTabsProps,
163
171
  FormTabProps,
164
172
  FormStepsProps,
@@ -173,6 +181,11 @@ export type {
173
181
  DisplayProps,
174
182
  EditMode,
175
183
  FieldRules,
184
+ InlineCellContext,
185
+ InlineColumnDef,
186
+ InlineFormSetBaseProps,
187
+ InlineRowContext,
188
+ FieldWrapperProps,
176
189
  ResourceListBuiltInActions,
177
190
  ResourceListBulkAction,
178
191
  ResourceListBulkActionHelpers,
@@ -1,14 +0,0 @@
1
- import { MutableRefObject, ReactNode } from 'react';
2
- type FormFieldsContextValue = {
3
- registerSource: (source: string) => () => void;
4
- getSources: () => string[];
5
- };
6
- export declare function FormFieldsProvider({ children, sourcesRef: externalSourcesRef, }: {
7
- children: ReactNode;
8
- /** Optional ref shared with submit handler outside this provider. */
9
- sourcesRef?: MutableRefObject<Set<string>>;
10
- }): import("react/jsx-runtime").JSX.Element;
11
- export declare function useFormFieldsOptional(): FormFieldsContextValue | null;
12
- export declare function useRegisterFormSource(source: string): void;
13
- export {};
14
- //# sourceMappingURL=FormFieldsContext.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"FormFieldsContext.d.ts","sourceRoot":"","sources":["../../../../src/crud/context/FormFieldsContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,gBAAgB,EACrB,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AAGf,KAAK,sBAAsB,GAAG;IAC5B,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,MAAM,IAAI,CAAC;IAC/C,UAAU,EAAE,MAAM,MAAM,EAAE,CAAC;CAC5B,CAAC;AAIF,wBAAgB,kBAAkB,CAAC,EACjC,QAAQ,EACR,UAAU,EAAE,kBAAkB,GAC/B,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,qEAAqE;IACrE,UAAU,CAAC,EAAE,gBAAgB,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,CAAC;CAC5C,2CA6BA;AAED,wBAAgB,qBAAqB,kCAEpC;AAED,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,MAAM,QAcnD"}
@@ -1,16 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { InlineFieldDefinition } from '../types';
3
- export type InlineFormSetLayout = "tabular" | "stacked";
4
- export declare function InlineFormSetProvider({ children, arrayName, layout, }: {
5
- children: ReactNode;
6
- arrayName: string;
7
- layout?: InlineFormSetLayout;
8
- }): import("react/jsx-runtime").JSX.Element;
9
- export declare function useInlineFormSetContext(): {
10
- fields: InlineFieldDefinition[];
11
- registerField: (def: InlineFieldDefinition) => () => void;
12
- arrayName: string;
13
- layout: InlineFormSetLayout;
14
- } | null;
15
- export declare function useRegisterInlineField(def: InlineFieldDefinition): void;
16
- //# sourceMappingURL=InlineFormContext.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"InlineFormContext.d.ts","sourceRoot":"","sources":["../../../../src/crud/context/InlineFormContext.tsx"],"names":[],"mappings":"AAAA,OAAO,EAOL,KAAK,SAAS,EACf,MAAM,OAAO,CAAC;AACf,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,UAAU,CAAC;AAEtD,MAAM,MAAM,mBAAmB,GAAG,SAAS,GAAG,SAAS,CAAC;AAWxD,wBAAgB,qBAAqB,CAAC,EACpC,QAAQ,EACR,SAAS,EACT,MAAkB,GACnB,EAAE;IACD,QAAQ,EAAE,SAAS,CAAC;IACpB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,mBAAmB,CAAC;CAC9B,2CAyBA;AAED,wBAAgB,uBAAuB;;mBA3CtB,CAAC,GAAG,EAAE,qBAAqB,KAAK,MAAM,IAAI;eAC9C,MAAM;YACT,mBAAmB;SA8C5B;AAED,wBAAgB,sBAAsB,CAAC,GAAG,EAAE,qBAAqB,QAMhE"}
@@ -1,10 +0,0 @@
1
- import { ReactNode } from 'react';
2
- import { FieldRenderProps, FieldRules, InlineFieldOptions } from '../types';
3
- export declare function useInlineOrFormField(source: string, label: string | undefined, required: boolean | undefined, rules: FieldRules | undefined, renderInput: (props: FieldRenderProps) => ReactNode, inlineOptions?: InlineFieldOptions): {
4
- mode: "inline";
5
- element?: undefined;
6
- } | {
7
- mode: "form";
8
- element: import("react/jsx-runtime").JSX.Element;
9
- };
10
- //# sourceMappingURL=useInlineOrFormField.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useInlineOrFormField.d.ts","sourceRoot":"","sources":["../../../../src/crud/fields/useInlineOrFormField.tsx"],"names":[],"mappings":"AAAA,OAAO,EAA8B,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEnE,OAAO,KAAK,EACV,gBAAgB,EAChB,UAAU,EAEV,kBAAkB,EACnB,MAAM,UAAU,CAAC;AAGlB,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,KAAK,EAAE,MAAM,GAAG,SAAS,EACzB,QAAQ,EAAE,OAAO,GAAG,SAAS,EAC7B,KAAK,EAAE,UAAU,GAAG,SAAS,EAC7B,WAAW,EAAE,CAAC,KAAK,EAAE,gBAAgB,KAAK,SAAS,EACnD,aAAa,CAAC,EAAE,kBAAkB;;;;;;EA0EnC"}
@@ -1,76 +0,0 @@
1
- import {
2
- createContext,
3
- useCallback,
4
- useContext,
5
- useEffect,
6
- useMemo,
7
- useRef,
8
- type MutableRefObject,
9
- type ReactNode,
10
- } from "react";
11
- import { useFormSectionSourcesOptional } from "./FormSectionContext";
12
-
13
- type FormFieldsContextValue = {
14
- registerSource: (source: string) => () => void;
15
- getSources: () => string[];
16
- };
17
-
18
- const FormFieldsContext = createContext<FormFieldsContextValue | null>(null);
19
-
20
- export function FormFieldsProvider({
21
- children,
22
- sourcesRef: externalSourcesRef,
23
- }: {
24
- children: ReactNode;
25
- /** Optional ref shared with submit handler outside this provider. */
26
- sourcesRef?: MutableRefObject<Set<string>>;
27
- }) {
28
- const internalSourcesRef = useRef(new Set<string>());
29
- const sourcesRef = externalSourcesRef ?? internalSourcesRef;
30
-
31
- const registerSource = useCallback(
32
- (source: string) => {
33
- sourcesRef.current.add(source);
34
- return () => {
35
- sourcesRef.current.delete(source);
36
- };
37
- },
38
- [sourcesRef],
39
- );
40
-
41
- const getSources = useCallback(
42
- () => Array.from(sourcesRef.current),
43
- [sourcesRef],
44
- );
45
-
46
- const value = useMemo(
47
- () => ({ registerSource, getSources }),
48
- [registerSource, getSources],
49
- );
50
-
51
- return (
52
- <FormFieldsContext.Provider value={value}>
53
- {children}
54
- </FormFieldsContext.Provider>
55
- );
56
- }
57
-
58
- export function useFormFieldsOptional() {
59
- return useContext(FormFieldsContext);
60
- }
61
-
62
- export function useRegisterFormSource(source: string) {
63
- const ctx = useFormFieldsOptional();
64
- const registerSource = ctx?.registerSource;
65
- const sectionSourcesRef = useFormSectionSourcesOptional();
66
-
67
- useEffect(() => {
68
- if (!registerSource) return;
69
- const unregister = registerSource(source);
70
- sectionSourcesRef?.current.add(source);
71
- return () => {
72
- unregister();
73
- sectionSourcesRef?.current.delete(source);
74
- };
75
- }, [registerSource, sectionSourcesRef, source]);
76
- }
@@ -1,71 +0,0 @@
1
- import {
2
- createContext,
3
- useCallback,
4
- useContext,
5
- useEffect,
6
- useMemo,
7
- useState,
8
- type ReactNode,
9
- } from "react";
10
- import type { InlineFieldDefinition } from "../types";
11
-
12
- export type InlineFormSetLayout = "tabular" | "stacked";
13
-
14
- type InlineFormSetMeta = {
15
- registerField: (def: InlineFieldDefinition) => () => void;
16
- arrayName: string;
17
- layout: InlineFormSetLayout;
18
- };
19
-
20
- const InlineFormSetMetaContext = createContext<InlineFormSetMeta | null>(null);
21
- const InlineFormSetFieldsContext = createContext<InlineFieldDefinition[]>([]);
22
-
23
- export function InlineFormSetProvider({
24
- children,
25
- arrayName,
26
- layout = "tabular",
27
- }: {
28
- children: ReactNode;
29
- arrayName: string;
30
- layout?: InlineFormSetLayout;
31
- }) {
32
- const [fields, setFields] = useState<InlineFieldDefinition[]>([]);
33
-
34
- const registerField = useCallback((def: InlineFieldDefinition) => {
35
- setFields((prev) => {
36
- if (prev.some((f) => f.key === def.key)) return prev;
37
- return [...prev, def];
38
- });
39
- return () => {
40
- setFields((prev) => prev.filter((f) => f.key !== def.key));
41
- };
42
- }, []);
43
-
44
- const meta = useMemo(
45
- (): InlineFormSetMeta => ({ arrayName, registerField, layout }),
46
- [arrayName, registerField, layout],
47
- );
48
-
49
- return (
50
- <InlineFormSetMetaContext.Provider value={meta}>
51
- <InlineFormSetFieldsContext.Provider value={fields}>
52
- {children}
53
- </InlineFormSetFieldsContext.Provider>
54
- </InlineFormSetMetaContext.Provider>
55
- );
56
- }
57
-
58
- export function useInlineFormSetContext() {
59
- const meta = useContext(InlineFormSetMetaContext);
60
- const fields = useContext(InlineFormSetFieldsContext);
61
- if (!meta) return null;
62
- return { ...meta, fields };
63
- }
64
-
65
- export function useRegisterInlineField(def: InlineFieldDefinition) {
66
- const registerField = useContext(InlineFormSetMetaContext)?.registerField;
67
- useEffect(() => {
68
- if (!registerField) return;
69
- return registerField(def);
70
- }, [registerField, def]);
71
- }