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.
- package/README.md +36 -5
- package/dist/index.js +712 -723
- package/dist/src/crud/InlineFormSet.d.ts +13 -11
- package/dist/src/crud/InlineFormSet.d.ts.map +1 -1
- package/dist/src/crud/ResourceForm.d.ts +10 -2
- 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/context/SubmitFieldsContext.d.ts +18 -0
- package/dist/src/crud/context/SubmitFieldsContext.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/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/index.d.ts +8 -4
- package/dist/src/crud/index.d.ts.map +1 -1
- package/dist/src/crud/types.d.ts +21 -20
- package/dist/src/crud/types.d.ts.map +1 -1
- package/dist/src/crud/utils/inlineFieldName.d.ts +3 -0
- package/dist/src/crud/utils/inlineFieldName.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/useResourceFormData.d.ts +47 -0
- package/dist/src/crud/utils/useResourceFormData.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 +2 -2
- package/dist/src/data/createRestResourceHandlers.d.ts.map +1 -1
- package/dist/src/data/dataProviderTypes.d.ts +7 -1
- package/dist/src/data/dataProviderTypes.d.ts.map +1 -1
- package/dist/src/data/resourceHandlers.d.ts +2 -2
- package/dist/src/data/resourceHandlers.d.ts.map +1 -1
- package/dist/src/index.d.ts +4 -3
- package/dist/src/index.d.ts.map +1 -1
- package/package.json +59 -59
- package/src/crud/InlineFormSet.tsx +77 -89
- package/src/crud/ResourceForm.tsx +87 -202
- package/src/crud/ResourceFormModal.tsx +37 -25
- package/src/crud/ResourceList.tsx +35 -26
- package/src/crud/context/SubmitFieldsContext.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 +32 -54
- 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/index.ts +19 -2
- package/src/crud/types.ts +132 -130
- package/src/crud/utils/inlineFieldName.ts +8 -0
- package/src/crud/utils/useAbortableEffect.ts +17 -0
- package/src/crud/utils/useResourceFormData.ts +231 -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 +4 -4
- package/src/data/dataProviderTypes.ts +8 -0
- package/src/data/resourceHandlers.ts +4 -3
- package/src/index.ts +14 -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/src/crud/context/FormFieldsContext.tsx +0 -76
- package/src/crud/context/InlineFormContext.tsx +0 -71
- package/src/crud/fields/useInlineOrFormField.tsx +0 -91
|
@@ -1,47 +1,53 @@
|
|
|
1
1
|
import { DatePicker } from "antd";
|
|
2
2
|
import dayjs from "dayjs";
|
|
3
3
|
import type { BaseSourceProps, FieldRules } from "../types";
|
|
4
|
-
import {
|
|
4
|
+
import { FieldWrapper } from "./FieldWrapper";
|
|
5
5
|
|
|
6
6
|
const DATE_FORMAT = "YYYY-MM-DD";
|
|
7
7
|
|
|
8
8
|
export type DateFieldProps = BaseSourceProps & {
|
|
9
|
+
name?: string;
|
|
9
10
|
required?: boolean;
|
|
10
11
|
rules?: FieldRules;
|
|
11
12
|
showTime?: boolean;
|
|
13
|
+
hideLabel?: boolean;
|
|
12
14
|
};
|
|
13
15
|
|
|
14
16
|
export function DateField({
|
|
15
17
|
source,
|
|
18
|
+
name,
|
|
16
19
|
label,
|
|
17
20
|
required,
|
|
18
21
|
rules,
|
|
19
22
|
showTime,
|
|
23
|
+
hideLabel,
|
|
20
24
|
}: DateFieldProps) {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
25
|
+
return (
|
|
26
|
+
<FieldWrapper
|
|
27
|
+
source={source}
|
|
28
|
+
name={name}
|
|
29
|
+
label={label}
|
|
30
|
+
required={required}
|
|
31
|
+
rules={rules}
|
|
32
|
+
hideLabel={hideLabel}
|
|
33
|
+
>
|
|
34
|
+
{({ value, onChange, onBlur, disabled }) => (
|
|
35
|
+
<DatePicker
|
|
36
|
+
value={value ? dayjs(String(value)) : null}
|
|
37
|
+
onChange={(d) =>
|
|
38
|
+
onChange(
|
|
39
|
+
d
|
|
40
|
+
? d.format(showTime ? `${DATE_FORMAT} HH:mm:ss` : DATE_FORMAT)
|
|
41
|
+
: null,
|
|
42
|
+
)
|
|
43
|
+
}
|
|
44
|
+
onBlur={onBlur}
|
|
45
|
+
showTime={showTime}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
format={showTime ? `${DATE_FORMAT} HH:mm:ss` : DATE_FORMAT}
|
|
48
|
+
style={{ width: "100%" }}
|
|
49
|
+
/>
|
|
50
|
+
)}
|
|
51
|
+
</FieldWrapper>
|
|
43
52
|
);
|
|
44
|
-
|
|
45
|
-
if (field.mode === "inline") return null;
|
|
46
|
-
return field.element;
|
|
47
53
|
}
|
|
@@ -3,87 +3,63 @@ import { Controller, useFormContext, type FieldValues } from "react-hook-form";
|
|
|
3
3
|
import type { ReactNode } from "react";
|
|
4
4
|
import type { FieldRules } from "../types";
|
|
5
5
|
import { useFormMetaOptional } from "../context/FormContext";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
useSectionField,
|
|
8
|
+
useSubmitField,
|
|
9
|
+
} from "../context/SubmitFieldsContext";
|
|
7
10
|
|
|
8
11
|
export type FieldWrapperProps = {
|
|
12
|
+
/** Logical field name — used for submit payload tracking on top-level fields. */
|
|
9
13
|
source: string;
|
|
14
|
+
/** Full RHF path. Defaults to `source`. Inline cells pass e.g. `lines.0.label`. */
|
|
15
|
+
name?: string;
|
|
10
16
|
label?: string;
|
|
11
17
|
required?: boolean;
|
|
12
18
|
rules?: FieldRules;
|
|
19
|
+
/** Tabular inline cells: column header replaces the label. */
|
|
20
|
+
hideLabel?: boolean;
|
|
13
21
|
children: (props: {
|
|
14
22
|
value: unknown;
|
|
15
23
|
onChange: (value: unknown) => void;
|
|
16
24
|
onBlur: () => void;
|
|
17
25
|
disabled?: boolean;
|
|
26
|
+
name: string;
|
|
18
27
|
}) => ReactNode;
|
|
19
28
|
};
|
|
20
29
|
|
|
21
30
|
export function FieldWrapper({
|
|
22
31
|
source,
|
|
32
|
+
name,
|
|
23
33
|
label,
|
|
24
34
|
required,
|
|
25
35
|
rules,
|
|
36
|
+
hideLabel,
|
|
26
37
|
children,
|
|
27
38
|
}: FieldWrapperProps) {
|
|
39
|
+
const fieldName = name ?? source;
|
|
40
|
+
const isTopLevel = !fieldName.includes(".");
|
|
41
|
+
|
|
42
|
+
// react-hook-form: connect this field to the form from <ResourceForm>'s FormProvider.
|
|
43
|
+
// `control` is passed to <Controller> below so value, onChange, and validation work.
|
|
28
44
|
const { control } = useFormContext<FieldValues>();
|
|
45
|
+
|
|
46
|
+
// ding-react-admin: optional form-wide flags from <FormMetaProvider> (inside ResourceForm).
|
|
47
|
+
// Used here for `disabled` on inputs (e.g. read-only view mode). Returns null outside a form page.
|
|
29
48
|
const meta = useFormMetaOptional();
|
|
30
|
-
const fieldLabel = label ?? source;
|
|
31
|
-
|
|
49
|
+
const fieldLabel = hideLabel ? undefined : (label ?? source);
|
|
50
|
+
const requiredMessage = label ?? source;
|
|
32
51
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
control={control}
|
|
37
|
-
rules={{
|
|
38
|
-
required: required ? `${fieldLabel} is required` : false,
|
|
39
|
-
...rules,
|
|
40
|
-
}}
|
|
41
|
-
render={({ field, fieldState }) => (
|
|
42
|
-
<Form.Item
|
|
43
|
-
label={fieldLabel}
|
|
44
|
-
validateStatus={fieldState.error ? "error" : undefined}
|
|
45
|
-
help={fieldState.error?.message}
|
|
46
|
-
required={required}
|
|
47
|
-
>
|
|
48
|
-
{children({
|
|
49
|
-
value: field.value,
|
|
50
|
-
onChange: field.onChange,
|
|
51
|
-
onBlur: field.onBlur,
|
|
52
|
-
disabled: meta?.disabled,
|
|
53
|
-
})}
|
|
54
|
-
</Form.Item>
|
|
55
|
-
)}
|
|
56
|
-
/>
|
|
57
|
-
);
|
|
58
|
-
}
|
|
52
|
+
// ding-react-admin: remember this `source` so Save sends only fields you rendered in JSX
|
|
53
|
+
// (see pickBySources in ResourceForm). Skipped for inline row cells — nested `name` paths.
|
|
54
|
+
useSubmitField(source, isTopLevel);
|
|
59
55
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
required,
|
|
64
|
-
rules,
|
|
65
|
-
hideLabel,
|
|
66
|
-
children,
|
|
67
|
-
}: {
|
|
68
|
-
name: string;
|
|
69
|
-
label?: string;
|
|
70
|
-
required?: boolean;
|
|
71
|
-
rules?: FieldRules;
|
|
72
|
-
/** Tabular inlines use column headers — suppress per-cell labels. */
|
|
73
|
-
hideLabel?: boolean;
|
|
74
|
-
children: (props: {
|
|
75
|
-
value: unknown;
|
|
76
|
-
onChange: (value: unknown) => void;
|
|
77
|
-
onBlur: () => void;
|
|
78
|
-
}) => ReactNode;
|
|
79
|
-
}) {
|
|
80
|
-
const { control } = useFormContext<FieldValues>();
|
|
81
|
-
const fieldLabel = hideLabel ? undefined : label;
|
|
82
|
-
const requiredMessage = label ?? "This field";
|
|
56
|
+
// ding-react-admin: group this field with the current FormTab / FormStep for error highlighting
|
|
57
|
+
// and "jump to first invalid tab" on Save. No-op outside tabs/steps.
|
|
58
|
+
useSectionField(source, isTopLevel);
|
|
83
59
|
|
|
84
60
|
return (
|
|
85
61
|
<Controller
|
|
86
|
-
name={
|
|
62
|
+
name={fieldName}
|
|
87
63
|
control={control}
|
|
88
64
|
rules={{
|
|
89
65
|
required: required ? `${requiredMessage} is required` : false,
|
|
@@ -95,12 +71,14 @@ export function InlineFieldWrapper({
|
|
|
95
71
|
validateStatus={fieldState.error ? "error" : undefined}
|
|
96
72
|
help={fieldState.error?.message}
|
|
97
73
|
required={required && !hideLabel}
|
|
98
|
-
style={{ marginBottom: 0 }}
|
|
74
|
+
style={hideLabel ? { marginBottom: 0 } : undefined}
|
|
99
75
|
>
|
|
100
76
|
{children({
|
|
101
77
|
value: field.value,
|
|
102
78
|
onChange: field.onChange,
|
|
103
79
|
onBlur: field.onBlur,
|
|
80
|
+
disabled: meta?.disabled,
|
|
81
|
+
name: fieldName,
|
|
104
82
|
})}
|
|
105
83
|
</Form.Item>
|
|
106
84
|
)}
|
|
@@ -1,50 +1,52 @@
|
|
|
1
1
|
import { InputNumber } from "antd";
|
|
2
2
|
import type { CSSProperties } from "react";
|
|
3
|
-
import type { BaseSourceProps, FieldRules
|
|
4
|
-
import {
|
|
3
|
+
import type { BaseSourceProps, FieldRules } from "../types";
|
|
4
|
+
import { FieldWrapper } from "./FieldWrapper";
|
|
5
5
|
|
|
6
|
-
export type NumberFieldProps = BaseSourceProps &
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
export type NumberFieldProps = BaseSourceProps & {
|
|
7
|
+
name?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
rules?: FieldRules;
|
|
10
|
+
min?: number;
|
|
11
|
+
max?: number;
|
|
12
|
+
step?: number;
|
|
13
|
+
inputStyle?: CSSProperties;
|
|
14
|
+
hideLabel?: boolean;
|
|
15
|
+
};
|
|
15
16
|
|
|
16
17
|
export function NumberField({
|
|
17
18
|
source,
|
|
19
|
+
name,
|
|
18
20
|
label,
|
|
19
21
|
required,
|
|
20
22
|
rules,
|
|
21
23
|
min,
|
|
22
24
|
max,
|
|
23
25
|
step,
|
|
24
|
-
width: inlineWidth,
|
|
25
|
-
minWidth: inlineMinWidth,
|
|
26
26
|
inputStyle,
|
|
27
|
+
hideLabel,
|
|
27
28
|
}: NumberFieldProps) {
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
29
|
+
return (
|
|
30
|
+
<FieldWrapper
|
|
31
|
+
source={source}
|
|
32
|
+
name={name}
|
|
33
|
+
label={label}
|
|
34
|
+
required={required}
|
|
35
|
+
rules={rules}
|
|
36
|
+
hideLabel={hideLabel}
|
|
37
|
+
>
|
|
38
|
+
{({ value, onChange, onBlur, disabled }) => (
|
|
39
|
+
<InputNumber
|
|
40
|
+
value={value as number | undefined}
|
|
41
|
+
onChange={(v) => onChange(v)}
|
|
42
|
+
onBlur={onBlur}
|
|
43
|
+
min={min}
|
|
44
|
+
max={max}
|
|
45
|
+
step={step}
|
|
46
|
+
disabled={disabled}
|
|
47
|
+
style={{ width: "100%", ...inputStyle }}
|
|
48
|
+
/>
|
|
49
|
+
)}
|
|
50
|
+
</FieldWrapper>
|
|
46
51
|
);
|
|
47
|
-
|
|
48
|
-
if (field.mode === "inline") return null;
|
|
49
|
-
return field.element;
|
|
50
52
|
}
|
|
@@ -1,71 +1,73 @@
|
|
|
1
1
|
import { Input } from "antd";
|
|
2
2
|
import { useWatch } from "react-hook-form";
|
|
3
|
-
import type { BaseSourceProps, FieldRules
|
|
4
|
-
import {
|
|
3
|
+
import type { BaseSourceProps, FieldRules } from "../types";
|
|
4
|
+
import { FieldWrapper } from "./FieldWrapper";
|
|
5
5
|
|
|
6
|
-
export type PasswordFieldProps = BaseSourceProps &
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
export type PasswordFieldProps = BaseSourceProps & {
|
|
7
|
+
name?: string;
|
|
8
|
+
required?: boolean;
|
|
9
|
+
rules?: FieldRules;
|
|
10
|
+
/** When set, renders a second field and validates it matches `source`. */
|
|
11
|
+
confirmSource?: string;
|
|
12
|
+
confirmLabel?: string;
|
|
13
|
+
autoComplete?: string;
|
|
14
|
+
hideLabel?: boolean;
|
|
15
|
+
};
|
|
15
16
|
|
|
16
17
|
function PasswordInputField({
|
|
17
18
|
source,
|
|
19
|
+
name,
|
|
18
20
|
label,
|
|
19
21
|
required,
|
|
20
22
|
rules,
|
|
21
23
|
autoComplete,
|
|
22
|
-
|
|
23
|
-
minWidth: inlineMinWidth,
|
|
24
|
+
hideLabel,
|
|
24
25
|
}: PasswordFieldProps) {
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
26
|
+
return (
|
|
27
|
+
<FieldWrapper
|
|
28
|
+
source={source}
|
|
29
|
+
name={name}
|
|
30
|
+
label={label}
|
|
31
|
+
required={required}
|
|
32
|
+
rules={rules}
|
|
33
|
+
hideLabel={hideLabel}
|
|
34
|
+
>
|
|
35
|
+
{({ value, onChange, onBlur, disabled }) => (
|
|
36
|
+
<Input.Password
|
|
37
|
+
value={value as string | undefined}
|
|
38
|
+
onChange={(e) => onChange(e.target.value)}
|
|
39
|
+
onBlur={onBlur}
|
|
40
|
+
disabled={disabled}
|
|
41
|
+
autoComplete={autoComplete}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
</FieldWrapper>
|
|
40
45
|
);
|
|
41
|
-
|
|
42
|
-
if (field.mode === "inline") return null;
|
|
43
|
-
return field.element;
|
|
44
46
|
}
|
|
45
47
|
|
|
46
48
|
export function PasswordField({
|
|
47
49
|
source,
|
|
50
|
+
name,
|
|
48
51
|
label,
|
|
49
52
|
required,
|
|
50
53
|
rules,
|
|
51
54
|
confirmSource,
|
|
52
55
|
confirmLabel = "Confirm password",
|
|
53
56
|
autoComplete = "new-password",
|
|
54
|
-
|
|
55
|
-
minWidth,
|
|
57
|
+
hideLabel,
|
|
56
58
|
}: PasswordFieldProps) {
|
|
57
|
-
const password = useWatch({ name: source, disabled: !confirmSource });
|
|
59
|
+
const password = useWatch({ name: name ?? source, disabled: !confirmSource });
|
|
58
60
|
|
|
59
61
|
if (!confirmSource) {
|
|
60
62
|
return (
|
|
61
63
|
<PasswordInputField
|
|
62
64
|
source={source}
|
|
65
|
+
name={name}
|
|
63
66
|
label={label}
|
|
64
67
|
required={required}
|
|
65
68
|
rules={rules}
|
|
66
69
|
autoComplete={autoComplete}
|
|
67
|
-
|
|
68
|
-
minWidth={minWidth}
|
|
70
|
+
hideLabel={hideLabel}
|
|
69
71
|
/>
|
|
70
72
|
);
|
|
71
73
|
}
|
|
@@ -74,20 +76,19 @@ export function PasswordField({
|
|
|
74
76
|
<>
|
|
75
77
|
<PasswordInputField
|
|
76
78
|
source={source}
|
|
79
|
+
name={name}
|
|
77
80
|
label={label}
|
|
78
81
|
required={required}
|
|
79
82
|
rules={rules}
|
|
80
83
|
autoComplete={autoComplete}
|
|
81
|
-
|
|
82
|
-
minWidth={minWidth}
|
|
84
|
+
hideLabel={hideLabel}
|
|
83
85
|
/>
|
|
84
86
|
<PasswordInputField
|
|
85
87
|
source={confirmSource}
|
|
86
88
|
label={confirmLabel}
|
|
87
89
|
required={required}
|
|
88
90
|
autoComplete={autoComplete}
|
|
89
|
-
|
|
90
|
-
minWidth={minWidth}
|
|
91
|
+
hideLabel={hideLabel}
|
|
91
92
|
rules={{
|
|
92
93
|
validate: (value: string) =>
|
|
93
94
|
!password || value === password || "Passwords do not match",
|
|
@@ -1,191 +1,96 @@
|
|
|
1
1
|
import { Select } from "antd";
|
|
2
|
-
|
|
3
2
|
import type { CSSProperties } from "react";
|
|
4
|
-
|
|
5
3
|
import { useMemo, useState } from "react";
|
|
6
|
-
|
|
7
4
|
import type {
|
|
8
|
-
|
|
9
5
|
BaseSourceProps,
|
|
10
|
-
|
|
11
6
|
ChoiceOption,
|
|
12
|
-
|
|
13
7
|
FieldRules,
|
|
14
|
-
|
|
15
|
-
InlineFieldOptions,
|
|
16
|
-
|
|
17
8
|
ReferenceProps,
|
|
18
|
-
|
|
19
9
|
} from "../types";
|
|
20
|
-
|
|
21
10
|
import { useChoices } from "../utils/useChoices";
|
|
22
|
-
|
|
23
|
-
import { useInlineOrFormField } from "./useInlineOrFormField";
|
|
24
|
-
|
|
25
|
-
|
|
11
|
+
import { FieldWrapper } from "./FieldWrapper";
|
|
26
12
|
|
|
27
13
|
export type ReferenceFieldProps = BaseSourceProps &
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
InlineFieldOptions & {
|
|
32
|
-
|
|
14
|
+
ReferenceProps & {
|
|
15
|
+
name?: string;
|
|
33
16
|
required?: boolean;
|
|
34
|
-
|
|
35
17
|
rules?: FieldRules;
|
|
36
|
-
|
|
37
18
|
search?: boolean;
|
|
38
|
-
|
|
39
19
|
allowClear?: boolean;
|
|
40
|
-
|
|
41
20
|
disabled?: boolean;
|
|
42
|
-
|
|
21
|
+
hideLabel?: boolean;
|
|
43
22
|
/** Applied to the underlying Select / input control. */
|
|
44
|
-
|
|
45
23
|
inputStyle?: CSSProperties;
|
|
46
|
-
|
|
47
24
|
/** Called after the value changes (e.g. to update dependent fields). */
|
|
48
|
-
|
|
49
25
|
onValueChange?: (
|
|
50
|
-
|
|
51
26
|
value: unknown,
|
|
52
|
-
|
|
53
27
|
option: ChoiceOption | undefined,
|
|
54
|
-
|
|
55
|
-
meta: { name?: string; index?: number },
|
|
56
|
-
|
|
28
|
+
meta: { name: string },
|
|
57
29
|
) => void;
|
|
58
|
-
|
|
59
30
|
};
|
|
60
31
|
|
|
61
|
-
|
|
62
|
-
|
|
63
32
|
export function ReferenceField({
|
|
64
|
-
|
|
65
33
|
source,
|
|
66
|
-
|
|
34
|
+
name,
|
|
67
35
|
label,
|
|
68
|
-
|
|
69
36
|
reference,
|
|
70
|
-
|
|
71
37
|
choices,
|
|
72
|
-
|
|
73
38
|
optionLabel = "name",
|
|
74
|
-
|
|
75
39
|
optionValue = "id",
|
|
76
|
-
|
|
77
40
|
required,
|
|
78
|
-
|
|
79
41
|
rules,
|
|
80
|
-
|
|
81
42
|
search,
|
|
82
|
-
|
|
83
43
|
allowClear,
|
|
84
|
-
|
|
85
44
|
disabled: disabledProp,
|
|
86
|
-
|
|
87
|
-
width: inlineWidth,
|
|
88
|
-
|
|
89
|
-
minWidth: inlineMinWidth,
|
|
90
|
-
|
|
45
|
+
hideLabel,
|
|
91
46
|
inputStyle,
|
|
92
|
-
|
|
93
47
|
onValueChange,
|
|
94
|
-
|
|
95
48
|
}: ReferenceFieldProps) {
|
|
96
|
-
|
|
97
49
|
const [searchText, setSearchText] = useState<string | undefined>();
|
|
98
|
-
|
|
99
50
|
const { options, loading, optionForValue } = useChoices(
|
|
100
|
-
|
|
101
51
|
choices,
|
|
102
|
-
|
|
103
52
|
reference,
|
|
104
|
-
|
|
105
53
|
optionLabel,
|
|
106
|
-
|
|
107
54
|
optionValue,
|
|
108
|
-
|
|
109
55
|
search ? searchText : undefined,
|
|
110
|
-
|
|
111
56
|
);
|
|
112
57
|
|
|
113
|
-
|
|
114
|
-
|
|
115
58
|
const selectOptions = useMemo(
|
|
116
|
-
|
|
117
59
|
() =>
|
|
118
|
-
|
|
119
60
|
options.map((o) => ({
|
|
120
|
-
|
|
121
61
|
label: o.label,
|
|
122
|
-
|
|
123
62
|
value: o.value as string | number,
|
|
124
|
-
|
|
125
63
|
})),
|
|
126
|
-
|
|
127
64
|
[options],
|
|
128
|
-
|
|
129
65
|
);
|
|
130
66
|
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
loading={loading && options.length === 0}
|
|
160
|
-
|
|
161
|
-
showSearch={search}
|
|
162
|
-
|
|
163
|
-
filterOption={search ? false : undefined}
|
|
164
|
-
|
|
165
|
-
onSearch={search ? setSearchText : undefined}
|
|
166
|
-
|
|
167
|
-
allowClear={allowClear}
|
|
168
|
-
|
|
169
|
-
disabled={disabled || disabledProp}
|
|
170
|
-
|
|
171
|
-
optionFilterProp="label"
|
|
172
|
-
|
|
173
|
-
style={{ width: "100%", minWidth: 160, ...inputStyle }}
|
|
174
|
-
|
|
175
|
-
/>
|
|
176
|
-
|
|
177
|
-
),
|
|
178
|
-
|
|
179
|
-
{ width: inlineWidth, minWidth: inlineMinWidth },
|
|
180
|
-
|
|
67
|
+
return (
|
|
68
|
+
<FieldWrapper
|
|
69
|
+
source={source}
|
|
70
|
+
name={name}
|
|
71
|
+
label={label}
|
|
72
|
+
required={required}
|
|
73
|
+
rules={rules}
|
|
74
|
+
hideLabel={hideLabel}
|
|
75
|
+
>
|
|
76
|
+
{({ value, onChange, disabled, name: fieldName }) => (
|
|
77
|
+
<Select
|
|
78
|
+
value={value as string | number | undefined}
|
|
79
|
+
onChange={(next) => {
|
|
80
|
+
onChange(next);
|
|
81
|
+
onValueChange?.(next, optionForValue(next), { name: fieldName });
|
|
82
|
+
}}
|
|
83
|
+
options={selectOptions}
|
|
84
|
+
loading={loading && options.length === 0}
|
|
85
|
+
showSearch={search}
|
|
86
|
+
filterOption={search ? false : undefined}
|
|
87
|
+
onSearch={search ? setSearchText : undefined}
|
|
88
|
+
allowClear={allowClear}
|
|
89
|
+
disabled={disabled || disabledProp}
|
|
90
|
+
optionFilterProp="label"
|
|
91
|
+
style={{ width: "100%", minWidth: 160, ...inputStyle }}
|
|
92
|
+
/>
|
|
93
|
+
)}
|
|
94
|
+
</FieldWrapper>
|
|
181
95
|
);
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
if (field.mode === "inline") return null;
|
|
186
|
-
|
|
187
|
-
return field.element;
|
|
188
|
-
|
|
189
96
|
}
|
|
190
|
-
|
|
191
|
-
|