ding-react-admin 1.0.2 → 2.0.0
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.
- package/README.md +38 -13
- package/dist/index.js +1317 -1060
- package/dist/src/crud/InlineFormSet.d.ts +12 -25
- package/dist/src/crud/InlineFormSet.d.ts.map +1 -1
- package/dist/src/crud/ResourceForm.d.ts +7 -4
- package/dist/src/crud/ResourceForm.d.ts.map +1 -1
- package/dist/src/crud/ResourceFormModal.d.ts.map +1 -1
- package/dist/src/crud/ResourceList.d.ts.map +1 -1
- package/dist/src/crud/columns/ImageColumn.d.ts +14 -0
- package/dist/src/crud/columns/ImageColumn.d.ts.map +1 -0
- package/dist/src/crud/context/InlineFieldsRegistry.d.ts +15 -0
- package/dist/src/crud/context/InlineFieldsRegistry.d.ts.map +1 -0
- package/dist/src/crud/context/PayloadFieldsContext.d.ts +18 -0
- package/dist/src/crud/context/PayloadFieldsContext.d.ts.map +1 -0
- package/dist/src/crud/fields/BooleanField.d.ts +3 -1
- package/dist/src/crud/fields/BooleanField.d.ts.map +1 -1
- package/dist/src/crud/fields/DateField.d.ts +3 -1
- package/dist/src/crud/fields/DateField.d.ts.map +1 -1
- package/dist/src/crud/fields/FieldWrapper.d.ts +7 -14
- package/dist/src/crud/fields/FieldWrapper.d.ts.map +1 -1
- package/dist/src/crud/fields/FileField.d.ts +12 -0
- package/dist/src/crud/fields/FileField.d.ts.map +1 -0
- package/dist/src/crud/fields/ImageField.d.ts +14 -0
- package/dist/src/crud/fields/ImageField.d.ts.map +1 -0
- package/dist/src/crud/fields/NumberField.d.ts +5 -3
- package/dist/src/crud/fields/NumberField.d.ts.map +1 -1
- package/dist/src/crud/fields/PasswordField.d.ts +5 -3
- package/dist/src/crud/fields/PasswordField.d.ts.map +1 -1
- package/dist/src/crud/fields/ReferenceField.d.ts +6 -5
- package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -1
- package/dist/src/crud/fields/ReferenceManyField.d.ts +3 -1
- package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -1
- package/dist/src/crud/fields/SelectField.d.ts +3 -1
- package/dist/src/crud/fields/SelectField.d.ts.map +1 -1
- package/dist/src/crud/fields/TextField.d.ts +5 -3
- package/dist/src/crud/fields/TextField.d.ts.map +1 -1
- package/dist/src/crud/fields/uploadFieldUtils.d.ts +5 -0
- package/dist/src/crud/fields/uploadFieldUtils.d.ts.map +1 -0
- package/dist/src/crud/fields/useUploadPreviewUrl.d.ts +4 -0
- package/dist/src/crud/fields/useUploadPreviewUrl.d.ts.map +1 -0
- package/dist/src/crud/index.d.ts +26 -9
- package/dist/src/crud/index.d.ts.map +1 -1
- package/dist/src/crud/types.d.ts +23 -20
- package/dist/src/crud/types.d.ts.map +1 -1
- package/dist/src/crud/utils/buildFormPayload.d.ts +6 -0
- package/dist/src/crud/utils/buildFormPayload.d.ts.map +1 -0
- package/dist/src/crud/utils/buildInlineRowsPayload.d.ts +6 -0
- package/dist/src/crud/utils/buildInlineRowsPayload.d.ts.map +1 -0
- package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts +5 -0
- package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts.map +1 -0
- package/dist/src/crud/utils/formErrors.d.ts +1 -1
- package/dist/src/crud/utils/formErrors.d.ts.map +1 -1
- package/dist/src/crud/utils/getFormValue.d.ts +3 -0
- package/dist/src/crud/utils/getFormValue.d.ts.map +1 -0
- package/dist/src/crud/utils/hasUploadValues.d.ts +3 -0
- package/dist/src/crud/utils/hasUploadValues.d.ts.map +1 -0
- package/dist/src/crud/utils/nestedFieldPath.d.ts +3 -0
- package/dist/src/crud/utils/nestedFieldPath.d.ts.map +1 -0
- package/dist/src/crud/utils/prepareFormSubmitBody.d.ts +7 -0
- package/dist/src/crud/utils/prepareFormSubmitBody.d.ts.map +1 -0
- package/dist/src/crud/utils/setFormValue.d.ts +3 -0
- package/dist/src/crud/utils/setFormValue.d.ts.map +1 -0
- package/dist/src/crud/utils/toFormData.d.ts +13 -0
- package/dist/src/crud/utils/toFormData.d.ts.map +1 -0
- package/dist/src/crud/utils/useAbortableEffect.d.ts +7 -0
- package/dist/src/crud/utils/useAbortableEffect.d.ts.map +1 -0
- package/dist/src/crud/utils/useFormRecord.d.ts +42 -0
- package/dist/src/crud/utils/useFormRecord.d.ts.map +1 -0
- package/dist/src/data/abortError.d.ts +3 -0
- package/dist/src/data/abortError.d.ts.map +1 -0
- package/dist/src/data/abortError.test.d.ts +2 -0
- package/dist/src/data/abortError.test.d.ts.map +1 -0
- package/dist/src/data/createRestResourceHandlers.d.ts +6 -6
- package/dist/src/data/createRestResourceHandlers.d.ts.map +1 -1
- package/dist/src/data/dataProviderTypes.d.ts +13 -6
- package/dist/src/data/dataProviderTypes.d.ts.map +1 -1
- package/dist/src/data/parseFormErrorHelpers.d.ts +44 -6
- package/dist/src/data/parseFormErrorHelpers.d.ts.map +1 -1
- package/dist/src/data/resourceHandlers.d.ts +3 -3
- package/dist/src/data/resourceHandlers.d.ts.map +1 -1
- package/dist/src/index.d.ts +5 -4
- package/dist/src/index.d.ts.map +1 -1
- package/dist/src/layouts/AdminLayout.d.ts.map +1 -1
- package/package.json +59 -59
- package/src/crud/InlineFormSet.tsx +90 -159
- package/src/crud/ResourceForm.tsx +83 -204
- package/src/crud/ResourceFormModal.tsx +57 -45
- package/src/crud/ResourceList.tsx +36 -26
- package/src/crud/columns/ImageColumn.tsx +58 -0
- package/src/crud/columns/TextColumn.tsx +2 -2
- package/src/crud/context/InlineFieldsRegistry.tsx +61 -0
- package/src/crud/context/PayloadFieldsContext.tsx +62 -0
- package/src/crud/fields/BooleanField.tsx +22 -16
- package/src/crud/fields/DateField.tsx +32 -26
- package/src/crud/fields/FieldWrapper.tsx +87 -109
- package/src/crud/fields/FileField.tsx +123 -0
- package/src/crud/fields/ImageField.tsx +120 -0
- package/src/crud/fields/NumberField.tsx +36 -34
- package/src/crud/fields/PasswordField.tsx +41 -40
- package/src/crud/fields/ReferenceField.tsx +35 -130
- package/src/crud/fields/ReferenceManyField.tsx +31 -25
- package/src/crud/fields/SelectField.tsx +6 -0
- package/src/crud/fields/TextField.tsx +32 -30
- package/src/crud/fields/uploadFieldUtils.ts +21 -0
- package/src/crud/fields/useUploadPreviewUrl.ts +20 -0
- package/src/crud/index.ts +35 -14
- package/src/crud/types.ts +134 -130
- package/src/crud/utils/buildFormPayload.ts +24 -0
- package/src/crud/utils/buildInlineRowsPayload.ts +37 -0
- package/src/crud/utils/buildResourceFormSubmitBody.ts +29 -0
- package/src/crud/utils/formErrors.ts +42 -38
- package/src/crud/utils/{getByPath.ts → getFormValue.ts} +2 -2
- package/src/crud/utils/hasUploadValues.ts +9 -0
- package/src/crud/utils/nestedFieldPath.ts +8 -0
- package/src/crud/utils/prepareFormSubmitBody.ts +17 -0
- package/src/crud/utils/{setByPath.ts → setFormValue.ts} +2 -2
- package/src/crud/utils/toFormData.ts +81 -0
- package/src/crud/utils/useAbortableEffect.ts +17 -0
- package/src/crud/utils/useFormRecord.ts +156 -0
- package/src/data/abortError.test.ts +19 -0
- package/src/data/abortError.ts +10 -0
- package/src/data/createMemoryResourceHandlers.ts +1 -1
- package/src/data/createRestResourceHandlers.ts +22 -16
- package/src/data/dataProviderTypes.ts +15 -5
- package/src/data/dataUtils.test.ts +118 -6
- package/src/data/parseFormErrorHelpers.test.ts +138 -84
- package/src/data/parseFormErrorHelpers.ts +311 -224
- package/src/data/resourceHandlers.ts +5 -4
- package/src/index.ts +34 -9
- package/src/layouts/AdminLayout.tsx +2 -1
- package/dist/src/crud/context/FormFieldsContext.d.ts +0 -14
- package/dist/src/crud/context/FormFieldsContext.d.ts.map +0 -1
- package/dist/src/crud/context/InlineFormContext.d.ts +0 -16
- package/dist/src/crud/context/InlineFormContext.d.ts.map +0 -1
- package/dist/src/crud/fields/useInlineOrFormField.d.ts +0 -10
- package/dist/src/crud/fields/useInlineOrFormField.d.ts.map +0 -1
- package/dist/src/crud/utils/getByPath.d.ts +0 -3
- package/dist/src/crud/utils/getByPath.d.ts.map +0 -1
- package/dist/src/crud/utils/inlineArrayName.d.ts +0 -3
- package/dist/src/crud/utils/inlineArrayName.d.ts.map +0 -1
- package/dist/src/crud/utils/pickBySources.d.ts +0 -6
- package/dist/src/crud/utils/pickBySources.d.ts.map +0 -1
- package/dist/src/crud/utils/setByPath.d.ts +0 -3
- package/dist/src/crud/utils/setByPath.d.ts.map +0 -1
- package/src/crud/context/FormFieldsContext.tsx +0 -76
- package/src/crud/context/InlineFormContext.tsx +0 -71
- package/src/crud/fields/useInlineOrFormField.tsx +0 -91
- package/src/crud/utils/inlineArrayName.ts +0 -4
- package/src/crud/utils/pickBySources.ts +0 -24
package/package.json
CHANGED
|
@@ -1,59 +1,59 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "ding-react-admin",
|
|
3
|
-
"version": "
|
|
4
|
-
"type": "module",
|
|
5
|
-
"license": "MIT",
|
|
6
|
-
"description": "Composable admin shell (Ant Design + React Router): quick-start AdminApp or build your own layout.",
|
|
7
|
-
"repository": {
|
|
8
|
-
"type": "git",
|
|
9
|
-
"url": "https://github.com/Muhammadinaam/ding-react-admin.git"
|
|
10
|
-
},
|
|
11
|
-
"main": "./dist/index.js",
|
|
12
|
-
"module": "./dist/index.js",
|
|
13
|
-
"types": "./dist/index.d.ts",
|
|
14
|
-
"exports": {
|
|
15
|
-
".": {
|
|
16
|
-
"types": "./dist/index.d.ts",
|
|
17
|
-
"import": "./dist/index.js"
|
|
18
|
-
}
|
|
19
|
-
},
|
|
20
|
-
"files": [
|
|
21
|
-
"dist",
|
|
22
|
-
"src",
|
|
23
|
-
"docs/assets"
|
|
24
|
-
],
|
|
25
|
-
"scripts": {
|
|
26
|
-
"build": "vite build && node -e \"require('fs').writeFileSync('dist/index.d.ts','export * from \\\"./src/index\\\";\\n')\"",
|
|
27
|
-
"dev:example": "yarn --cwd examples/playground dev",
|
|
28
|
-
"build:example": "yarn --cwd examples/playground build",
|
|
29
|
-
"build:example:gh-pages": "yarn --cwd examples/playground build:gh-pages",
|
|
30
|
-
"test": "vitest run",
|
|
31
|
-
"prepublishOnly": "yarn build"
|
|
32
|
-
},
|
|
33
|
-
"peerDependencies": {
|
|
34
|
-
"@ant-design/icons": ">=6",
|
|
35
|
-
"antd": ">=6",
|
|
36
|
-
"dayjs": ">=1",
|
|
37
|
-
"react": ">=18",
|
|
38
|
-
"react-dom": ">=18",
|
|
39
|
-
"react-hook-form": ">=7",
|
|
40
|
-
"react-router-dom": ">=6"
|
|
41
|
-
},
|
|
42
|
-
"devDependencies": {
|
|
43
|
-
"@ant-design/icons": "^6.0.0",
|
|
44
|
-
"@types/react": "^19.2.14",
|
|
45
|
-
"@types/react-dom": "^19.2.3",
|
|
46
|
-
"@vitejs/plugin-react": "^6.0.1",
|
|
47
|
-
"antd": "^6.0.0",
|
|
48
|
-
"dayjs": "^1.11.13",
|
|
49
|
-
"react": "^19.2.5",
|
|
50
|
-
"react-dom": "^19.2.5",
|
|
51
|
-
"react-hook-form": "^7.56.4",
|
|
52
|
-
"react-router-dom": "^7.14.2",
|
|
53
|
-
"typescript": "~6.0.2",
|
|
54
|
-
"vite": "^8.0.9",
|
|
55
|
-
"vite-plugin-dts": "^4.5.4",
|
|
56
|
-
"vitest": "^3.2.4"
|
|
57
|
-
},
|
|
58
|
-
"dependencies": {}
|
|
59
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "ding-react-admin",
|
|
3
|
+
"version": "2.0.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"license": "MIT",
|
|
6
|
+
"description": "Composable admin shell (Ant Design + React Router): quick-start AdminApp or build your own layout.",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/Muhammadinaam/ding-react-admin.git"
|
|
10
|
+
},
|
|
11
|
+
"main": "./dist/index.js",
|
|
12
|
+
"module": "./dist/index.js",
|
|
13
|
+
"types": "./dist/index.d.ts",
|
|
14
|
+
"exports": {
|
|
15
|
+
".": {
|
|
16
|
+
"types": "./dist/index.d.ts",
|
|
17
|
+
"import": "./dist/index.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
"files": [
|
|
21
|
+
"dist",
|
|
22
|
+
"src",
|
|
23
|
+
"docs/assets"
|
|
24
|
+
],
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "vite build && node -e \"require('fs').writeFileSync('dist/index.d.ts','export * from \\\"./src/index\\\";\\n')\"",
|
|
27
|
+
"dev:example": "yarn --cwd examples/playground dev",
|
|
28
|
+
"build:example": "yarn --cwd examples/playground build",
|
|
29
|
+
"build:example:gh-pages": "yarn --cwd examples/playground build:gh-pages",
|
|
30
|
+
"test": "vitest run",
|
|
31
|
+
"prepublishOnly": "yarn build"
|
|
32
|
+
},
|
|
33
|
+
"peerDependencies": {
|
|
34
|
+
"@ant-design/icons": ">=6",
|
|
35
|
+
"antd": ">=6",
|
|
36
|
+
"dayjs": ">=1",
|
|
37
|
+
"react": ">=18",
|
|
38
|
+
"react-dom": ">=18",
|
|
39
|
+
"react-hook-form": ">=7",
|
|
40
|
+
"react-router-dom": ">=6"
|
|
41
|
+
},
|
|
42
|
+
"devDependencies": {
|
|
43
|
+
"@ant-design/icons": "^6.0.0",
|
|
44
|
+
"@types/react": "^19.2.14",
|
|
45
|
+
"@types/react-dom": "^19.2.3",
|
|
46
|
+
"@vitejs/plugin-react": "^6.0.1",
|
|
47
|
+
"antd": "^6.0.0",
|
|
48
|
+
"dayjs": "^1.11.13",
|
|
49
|
+
"react": "^19.2.5",
|
|
50
|
+
"react-dom": "^19.2.5",
|
|
51
|
+
"react-hook-form": "^7.56.4",
|
|
52
|
+
"react-router-dom": "^7.14.2",
|
|
53
|
+
"typescript": "~6.0.2",
|
|
54
|
+
"vite": "^8.0.9",
|
|
55
|
+
"vite-plugin-dts": "^4.5.4",
|
|
56
|
+
"vitest": "^3.2.4"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {}
|
|
59
|
+
}
|
|
@@ -1,72 +1,82 @@
|
|
|
1
1
|
import { Button, Card, Space, Table, Typography } from "antd";
|
|
2
2
|
import { useFieldArray, useFormContext, type FieldValues } from "react-hook-form";
|
|
3
3
|
import { useMemo, type ReactNode } from "react";
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
} from "./
|
|
9
|
-
import
|
|
10
|
-
import
|
|
11
|
-
import {
|
|
12
|
-
|
|
13
|
-
export type
|
|
4
|
+
import type {
|
|
5
|
+
InlineColumnDef,
|
|
6
|
+
InlineFormSetBaseProps,
|
|
7
|
+
InlineRowContext,
|
|
8
|
+
} from "./types";
|
|
9
|
+
import { nestedFieldPath } from "./utils/nestedFieldPath";
|
|
10
|
+
import { useRegisterInlineField } from "./context/InlineFieldsRegistry";
|
|
11
|
+
import { useRegisterPayloadField } from "./context/PayloadFieldsContext";
|
|
12
|
+
|
|
13
|
+
export type InlineFormSetLayout = "tabular" | "stacked";
|
|
14
|
+
|
|
15
|
+
export type InlineFormSetProps = InlineFormSetBaseProps & {
|
|
16
|
+
columns: InlineColumnDef[];
|
|
17
|
+
};
|
|
14
18
|
|
|
15
|
-
export type
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
children: ReactNode;
|
|
20
|
-
name?: string;
|
|
21
|
-
/** `tabular` (default) — Django TabularInline; `stacked` — Django StackedInline. */
|
|
22
|
-
layout?: InlineFormSetLayout;
|
|
19
|
+
export type InlineFormSetStackedProps = InlineFormSetBaseProps & {
|
|
20
|
+
/** Field sources in each row — used when appending an empty row. */
|
|
21
|
+
sources: string[];
|
|
22
|
+
renderRow: (ctx: InlineRowContext) => ReactNode;
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
function
|
|
26
|
-
const
|
|
25
|
+
function emptyRow(sources: string[]) {
|
|
26
|
+
const row: Record<string, unknown> = {};
|
|
27
|
+
for (const source of sources) row[source] = undefined;
|
|
28
|
+
return row;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function useInlineRows(field: string, sources: string[]) {
|
|
27
32
|
const { control } = useFormContext<FieldValues>();
|
|
28
33
|
const { fields, append, remove } = useFieldArray({
|
|
29
34
|
control,
|
|
30
|
-
name:
|
|
35
|
+
name: field,
|
|
36
|
+
keyName: "rowKey",
|
|
31
37
|
});
|
|
32
38
|
|
|
33
|
-
const appendEmpty = () =>
|
|
34
|
-
if (!ctx) return;
|
|
35
|
-
const empty: Record<string, unknown> = {};
|
|
36
|
-
for (const f of ctx.fields) empty[f.source] = undefined;
|
|
37
|
-
append(empty);
|
|
38
|
-
};
|
|
39
|
+
const appendEmpty = () => append(emptyRow(sources));
|
|
39
40
|
|
|
40
|
-
return {
|
|
41
|
+
return { fields, remove, appendEmpty };
|
|
41
42
|
}
|
|
42
43
|
|
|
43
|
-
|
|
44
|
-
|
|
44
|
+
/** Tabular inline (Django TabularInline) — table with column headers. */
|
|
45
|
+
export function InlineFormSet({
|
|
46
|
+
field,
|
|
45
47
|
label,
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}) {
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
48
|
+
payloadKey,
|
|
49
|
+
transformRows,
|
|
50
|
+
columns,
|
|
51
|
+
}: InlineFormSetProps) {
|
|
52
|
+
const sources = useMemo(
|
|
53
|
+
() => columns.map((col) => col.source),
|
|
54
|
+
[columns],
|
|
55
|
+
);
|
|
56
|
+
const { fields, remove, appendEmpty } = useInlineRows(field, sources);
|
|
57
|
+
|
|
58
|
+
useRegisterPayloadField(field);
|
|
59
|
+
useRegisterInlineField(field, sources, payloadKey, transformRows);
|
|
60
|
+
|
|
61
|
+
const tableColumns = useMemo(
|
|
62
|
+
() =>
|
|
63
|
+
columns.map((col) => ({
|
|
64
|
+
title: col.label ?? col.source,
|
|
65
|
+
key: col.source,
|
|
66
|
+
width: col.width,
|
|
67
|
+
onHeaderCell: () =>
|
|
68
|
+
col.minWidth != null ? { style: { minWidth: col.minWidth } } : {},
|
|
69
|
+
onCell: () =>
|
|
70
|
+
col.minWidth != null ? { style: { minWidth: col.minWidth } } : {},
|
|
71
|
+
render: (_: unknown, __: unknown, index: number) =>
|
|
72
|
+
col.cell({
|
|
73
|
+
name: nestedFieldPath(field, index, col.source),
|
|
74
|
+
index,
|
|
75
|
+
field,
|
|
76
|
+
}),
|
|
77
|
+
})),
|
|
78
|
+
[columns, field],
|
|
79
|
+
);
|
|
70
80
|
|
|
71
81
|
return (
|
|
72
82
|
<div style={{ marginTop: 24 }}>
|
|
@@ -75,9 +85,9 @@ function InlineFormSetTable({
|
|
|
75
85
|
size="small"
|
|
76
86
|
pagination={false}
|
|
77
87
|
scroll={{ x: "max-content" }}
|
|
78
|
-
dataSource={fields.map((f) => ({ ...f, key: f.
|
|
88
|
+
dataSource={fields.map((f) => ({ ...f, key: f.rowKey }))}
|
|
79
89
|
columns={[
|
|
80
|
-
...
|
|
90
|
+
...tableColumns,
|
|
81
91
|
{
|
|
82
92
|
title: "",
|
|
83
93
|
key: "__remove",
|
|
@@ -102,40 +112,45 @@ function InlineFormSetTable({
|
|
|
102
112
|
);
|
|
103
113
|
}
|
|
104
114
|
|
|
105
|
-
|
|
106
|
-
|
|
115
|
+
/** Stacked inline (Django StackedInline) — each row in a card with field labels. */
|
|
116
|
+
export function InlineFormSetStacked({
|
|
117
|
+
field,
|
|
107
118
|
label,
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
119
|
+
payloadKey,
|
|
120
|
+
transformRows,
|
|
121
|
+
sources,
|
|
122
|
+
renderRow,
|
|
123
|
+
}: InlineFormSetStackedProps) {
|
|
124
|
+
const { fields, remove, appendEmpty } = useInlineRows(field, sources);
|
|
113
125
|
|
|
114
|
-
|
|
126
|
+
useRegisterPayloadField(field);
|
|
127
|
+
useRegisterInlineField(field, sources, payloadKey, transformRows);
|
|
115
128
|
|
|
116
129
|
return (
|
|
117
130
|
<div style={{ marginTop: 24 }}>
|
|
118
131
|
<Typography.Title level={5}>{label ?? "Related items"}</Typography.Title>
|
|
119
132
|
<Space orientation="vertical" size="middle" style={{ width: "100%" }}>
|
|
120
|
-
{fields.map((
|
|
133
|
+
{fields.map((rowField, index) => (
|
|
121
134
|
<Card
|
|
122
|
-
key={
|
|
135
|
+
key={rowField.rowKey}
|
|
123
136
|
size="small"
|
|
124
137
|
title={`Item ${index + 1}`}
|
|
125
138
|
extra={
|
|
126
|
-
<Button
|
|
139
|
+
<Button
|
|
140
|
+
type="link"
|
|
141
|
+
danger
|
|
142
|
+
size="small"
|
|
143
|
+
onClick={() => remove(index)}
|
|
144
|
+
>
|
|
127
145
|
Remove
|
|
128
146
|
</Button>
|
|
129
147
|
}
|
|
130
148
|
>
|
|
131
|
-
{
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
})}
|
|
137
|
-
</div>
|
|
138
|
-
))}
|
|
149
|
+
{renderRow({
|
|
150
|
+
field,
|
|
151
|
+
index,
|
|
152
|
+
name: (source) => nestedFieldPath(field, index, source),
|
|
153
|
+
})}
|
|
139
154
|
</Card>
|
|
140
155
|
))}
|
|
141
156
|
</Space>
|
|
@@ -145,87 +160,3 @@ function InlineFormSetStacked({
|
|
|
145
160
|
</div>
|
|
146
161
|
);
|
|
147
162
|
}
|
|
148
|
-
|
|
149
|
-
export function InlineFormSet({
|
|
150
|
-
resource,
|
|
151
|
-
label,
|
|
152
|
-
children,
|
|
153
|
-
name,
|
|
154
|
-
layout = "tabular",
|
|
155
|
-
}: InlineFormSetProps) {
|
|
156
|
-
const arrayName = buildInlineArrayName(resource, name);
|
|
157
|
-
|
|
158
|
-
return (
|
|
159
|
-
<InlineFormSetProvider arrayName={arrayName} layout={layout}>
|
|
160
|
-
{children}
|
|
161
|
-
{layout === "stacked" ? (
|
|
162
|
-
<InlineFormSetStacked arrayName={arrayName} label={label} />
|
|
163
|
-
) : (
|
|
164
|
-
<InlineFormSetTable arrayName={arrayName} label={label} />
|
|
165
|
-
)}
|
|
166
|
-
</InlineFormSetProvider>
|
|
167
|
-
);
|
|
168
|
-
}
|
|
169
|
-
|
|
170
|
-
export type SaveInlineOptions = {
|
|
171
|
-
resource: string;
|
|
172
|
-
foreignKey: string;
|
|
173
|
-
parentId: Identifier;
|
|
174
|
-
rows: Record<string, unknown>[];
|
|
175
|
-
existingIds?: Identifier[];
|
|
176
|
-
/** Return true when the error was handled (field errors applied). Stops saving further rows. */
|
|
177
|
-
onRowError?: (error: unknown, index: number) => boolean;
|
|
178
|
-
};
|
|
179
|
-
|
|
180
|
-
export async function saveInlineRows(
|
|
181
|
-
dp: DataProvider,
|
|
182
|
-
opts: SaveInlineOptions,
|
|
183
|
-
): Promise<boolean> {
|
|
184
|
-
const { resource, foreignKey, parentId, rows, existingIds = [], onRowError } =
|
|
185
|
-
opts;
|
|
186
|
-
const keptIds: Identifier[] = [];
|
|
187
|
-
|
|
188
|
-
for (let index = 0; index < rows.length; index++) {
|
|
189
|
-
const row = rows[index];
|
|
190
|
-
try {
|
|
191
|
-
const { id: _id, ...rest } = row;
|
|
192
|
-
const data = { ...rest, [foreignKey]: parentId };
|
|
193
|
-
const rowId = row.id as Identifier | undefined;
|
|
194
|
-
if (rowId != null && existingIds.some((e) => e === rowId)) {
|
|
195
|
-
await dp.update(resource, { id: rowId, data });
|
|
196
|
-
keptIds.push(rowId);
|
|
197
|
-
} else {
|
|
198
|
-
const res = await dp.create(resource, data);
|
|
199
|
-
const created = res.data as { id: Identifier };
|
|
200
|
-
keptIds.push(created.id);
|
|
201
|
-
}
|
|
202
|
-
} catch (e) {
|
|
203
|
-
if (onRowError?.(e, index)) return false;
|
|
204
|
-
throw e;
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
for (const oldId of existingIds) {
|
|
209
|
-
if (!keptIds.some((k) => k === oldId)) {
|
|
210
|
-
await dp.delete(resource, oldId);
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
|
|
214
|
-
return true;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
export async function loadInlineRows(
|
|
218
|
-
dp: DataProvider,
|
|
219
|
-
resource: string,
|
|
220
|
-
foreignKey: string,
|
|
221
|
-
parentId: Identifier,
|
|
222
|
-
) {
|
|
223
|
-
const res = await dp.getList(resource, {
|
|
224
|
-
filter: { [foreignKey]: Number(parentId) || parentId },
|
|
225
|
-
pagination: { page: 1, perPage: 500 },
|
|
226
|
-
});
|
|
227
|
-
return {
|
|
228
|
-
rows: res.data as Record<string, unknown>[],
|
|
229
|
-
ids: (res.data as { id: Identifier }[]).map((r) => r.id),
|
|
230
|
-
};
|
|
231
|
-
}
|