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.
Files changed (149) hide show
  1. package/README.md +38 -13
  2. package/dist/index.js +1317 -1060
  3. package/dist/src/crud/InlineFormSet.d.ts +12 -25
  4. package/dist/src/crud/InlineFormSet.d.ts.map +1 -1
  5. package/dist/src/crud/ResourceForm.d.ts +7 -4
  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/columns/ImageColumn.d.ts +14 -0
  10. package/dist/src/crud/columns/ImageColumn.d.ts.map +1 -0
  11. package/dist/src/crud/context/InlineFieldsRegistry.d.ts +15 -0
  12. package/dist/src/crud/context/InlineFieldsRegistry.d.ts.map +1 -0
  13. package/dist/src/crud/context/PayloadFieldsContext.d.ts +18 -0
  14. package/dist/src/crud/context/PayloadFieldsContext.d.ts.map +1 -0
  15. package/dist/src/crud/fields/BooleanField.d.ts +3 -1
  16. package/dist/src/crud/fields/BooleanField.d.ts.map +1 -1
  17. package/dist/src/crud/fields/DateField.d.ts +3 -1
  18. package/dist/src/crud/fields/DateField.d.ts.map +1 -1
  19. package/dist/src/crud/fields/FieldWrapper.d.ts +7 -14
  20. package/dist/src/crud/fields/FieldWrapper.d.ts.map +1 -1
  21. package/dist/src/crud/fields/FileField.d.ts +12 -0
  22. package/dist/src/crud/fields/FileField.d.ts.map +1 -0
  23. package/dist/src/crud/fields/ImageField.d.ts +14 -0
  24. package/dist/src/crud/fields/ImageField.d.ts.map +1 -0
  25. package/dist/src/crud/fields/NumberField.d.ts +5 -3
  26. package/dist/src/crud/fields/NumberField.d.ts.map +1 -1
  27. package/dist/src/crud/fields/PasswordField.d.ts +5 -3
  28. package/dist/src/crud/fields/PasswordField.d.ts.map +1 -1
  29. package/dist/src/crud/fields/ReferenceField.d.ts +6 -5
  30. package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -1
  31. package/dist/src/crud/fields/ReferenceManyField.d.ts +3 -1
  32. package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -1
  33. package/dist/src/crud/fields/SelectField.d.ts +3 -1
  34. package/dist/src/crud/fields/SelectField.d.ts.map +1 -1
  35. package/dist/src/crud/fields/TextField.d.ts +5 -3
  36. package/dist/src/crud/fields/TextField.d.ts.map +1 -1
  37. package/dist/src/crud/fields/uploadFieldUtils.d.ts +5 -0
  38. package/dist/src/crud/fields/uploadFieldUtils.d.ts.map +1 -0
  39. package/dist/src/crud/fields/useUploadPreviewUrl.d.ts +4 -0
  40. package/dist/src/crud/fields/useUploadPreviewUrl.d.ts.map +1 -0
  41. package/dist/src/crud/index.d.ts +26 -9
  42. package/dist/src/crud/index.d.ts.map +1 -1
  43. package/dist/src/crud/types.d.ts +23 -20
  44. package/dist/src/crud/types.d.ts.map +1 -1
  45. package/dist/src/crud/utils/buildFormPayload.d.ts +6 -0
  46. package/dist/src/crud/utils/buildFormPayload.d.ts.map +1 -0
  47. package/dist/src/crud/utils/buildInlineRowsPayload.d.ts +6 -0
  48. package/dist/src/crud/utils/buildInlineRowsPayload.d.ts.map +1 -0
  49. package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts +5 -0
  50. package/dist/src/crud/utils/buildResourceFormSubmitBody.d.ts.map +1 -0
  51. package/dist/src/crud/utils/formErrors.d.ts +1 -1
  52. package/dist/src/crud/utils/formErrors.d.ts.map +1 -1
  53. package/dist/src/crud/utils/getFormValue.d.ts +3 -0
  54. package/dist/src/crud/utils/getFormValue.d.ts.map +1 -0
  55. package/dist/src/crud/utils/hasUploadValues.d.ts +3 -0
  56. package/dist/src/crud/utils/hasUploadValues.d.ts.map +1 -0
  57. package/dist/src/crud/utils/nestedFieldPath.d.ts +3 -0
  58. package/dist/src/crud/utils/nestedFieldPath.d.ts.map +1 -0
  59. package/dist/src/crud/utils/prepareFormSubmitBody.d.ts +7 -0
  60. package/dist/src/crud/utils/prepareFormSubmitBody.d.ts.map +1 -0
  61. package/dist/src/crud/utils/setFormValue.d.ts +3 -0
  62. package/dist/src/crud/utils/setFormValue.d.ts.map +1 -0
  63. package/dist/src/crud/utils/toFormData.d.ts +13 -0
  64. package/dist/src/crud/utils/toFormData.d.ts.map +1 -0
  65. package/dist/src/crud/utils/useAbortableEffect.d.ts +7 -0
  66. package/dist/src/crud/utils/useAbortableEffect.d.ts.map +1 -0
  67. package/dist/src/crud/utils/useFormRecord.d.ts +42 -0
  68. package/dist/src/crud/utils/useFormRecord.d.ts.map +1 -0
  69. package/dist/src/data/abortError.d.ts +3 -0
  70. package/dist/src/data/abortError.d.ts.map +1 -0
  71. package/dist/src/data/abortError.test.d.ts +2 -0
  72. package/dist/src/data/abortError.test.d.ts.map +1 -0
  73. package/dist/src/data/createRestResourceHandlers.d.ts +6 -6
  74. package/dist/src/data/createRestResourceHandlers.d.ts.map +1 -1
  75. package/dist/src/data/dataProviderTypes.d.ts +13 -6
  76. package/dist/src/data/dataProviderTypes.d.ts.map +1 -1
  77. package/dist/src/data/parseFormErrorHelpers.d.ts +44 -6
  78. package/dist/src/data/parseFormErrorHelpers.d.ts.map +1 -1
  79. package/dist/src/data/resourceHandlers.d.ts +3 -3
  80. package/dist/src/data/resourceHandlers.d.ts.map +1 -1
  81. package/dist/src/index.d.ts +5 -4
  82. package/dist/src/index.d.ts.map +1 -1
  83. package/dist/src/layouts/AdminLayout.d.ts.map +1 -1
  84. package/package.json +59 -59
  85. package/src/crud/InlineFormSet.tsx +90 -159
  86. package/src/crud/ResourceForm.tsx +83 -204
  87. package/src/crud/ResourceFormModal.tsx +57 -45
  88. package/src/crud/ResourceList.tsx +36 -26
  89. package/src/crud/columns/ImageColumn.tsx +58 -0
  90. package/src/crud/columns/TextColumn.tsx +2 -2
  91. package/src/crud/context/InlineFieldsRegistry.tsx +61 -0
  92. package/src/crud/context/PayloadFieldsContext.tsx +62 -0
  93. package/src/crud/fields/BooleanField.tsx +22 -16
  94. package/src/crud/fields/DateField.tsx +32 -26
  95. package/src/crud/fields/FieldWrapper.tsx +87 -109
  96. package/src/crud/fields/FileField.tsx +123 -0
  97. package/src/crud/fields/ImageField.tsx +120 -0
  98. package/src/crud/fields/NumberField.tsx +36 -34
  99. package/src/crud/fields/PasswordField.tsx +41 -40
  100. package/src/crud/fields/ReferenceField.tsx +35 -130
  101. package/src/crud/fields/ReferenceManyField.tsx +31 -25
  102. package/src/crud/fields/SelectField.tsx +6 -0
  103. package/src/crud/fields/TextField.tsx +32 -30
  104. package/src/crud/fields/uploadFieldUtils.ts +21 -0
  105. package/src/crud/fields/useUploadPreviewUrl.ts +20 -0
  106. package/src/crud/index.ts +35 -14
  107. package/src/crud/types.ts +134 -130
  108. package/src/crud/utils/buildFormPayload.ts +24 -0
  109. package/src/crud/utils/buildInlineRowsPayload.ts +37 -0
  110. package/src/crud/utils/buildResourceFormSubmitBody.ts +29 -0
  111. package/src/crud/utils/formErrors.ts +42 -38
  112. package/src/crud/utils/{getByPath.ts → getFormValue.ts} +2 -2
  113. package/src/crud/utils/hasUploadValues.ts +9 -0
  114. package/src/crud/utils/nestedFieldPath.ts +8 -0
  115. package/src/crud/utils/prepareFormSubmitBody.ts +17 -0
  116. package/src/crud/utils/{setByPath.ts → setFormValue.ts} +2 -2
  117. package/src/crud/utils/toFormData.ts +81 -0
  118. package/src/crud/utils/useAbortableEffect.ts +17 -0
  119. package/src/crud/utils/useFormRecord.ts +156 -0
  120. package/src/data/abortError.test.ts +19 -0
  121. package/src/data/abortError.ts +10 -0
  122. package/src/data/createMemoryResourceHandlers.ts +1 -1
  123. package/src/data/createRestResourceHandlers.ts +22 -16
  124. package/src/data/dataProviderTypes.ts +15 -5
  125. package/src/data/dataUtils.test.ts +118 -6
  126. package/src/data/parseFormErrorHelpers.test.ts +138 -84
  127. package/src/data/parseFormErrorHelpers.ts +311 -224
  128. package/src/data/resourceHandlers.ts +5 -4
  129. package/src/index.ts +34 -9
  130. package/src/layouts/AdminLayout.tsx +2 -1
  131. package/dist/src/crud/context/FormFieldsContext.d.ts +0 -14
  132. package/dist/src/crud/context/FormFieldsContext.d.ts.map +0 -1
  133. package/dist/src/crud/context/InlineFormContext.d.ts +0 -16
  134. package/dist/src/crud/context/InlineFormContext.d.ts.map +0 -1
  135. package/dist/src/crud/fields/useInlineOrFormField.d.ts +0 -10
  136. package/dist/src/crud/fields/useInlineOrFormField.d.ts.map +0 -1
  137. package/dist/src/crud/utils/getByPath.d.ts +0 -3
  138. package/dist/src/crud/utils/getByPath.d.ts.map +0 -1
  139. package/dist/src/crud/utils/inlineArrayName.d.ts +0 -3
  140. package/dist/src/crud/utils/inlineArrayName.d.ts.map +0 -1
  141. package/dist/src/crud/utils/pickBySources.d.ts +0 -6
  142. package/dist/src/crud/utils/pickBySources.d.ts.map +0 -1
  143. package/dist/src/crud/utils/setByPath.d.ts +0 -3
  144. package/dist/src/crud/utils/setByPath.d.ts.map +0 -1
  145. package/src/crud/context/FormFieldsContext.tsx +0 -76
  146. package/src/crud/context/InlineFormContext.tsx +0 -71
  147. package/src/crud/fields/useInlineOrFormField.tsx +0 -91
  148. package/src/crud/utils/inlineArrayName.ts +0 -4
  149. package/src/crud/utils/pickBySources.ts +0 -24
@@ -0,0 +1,120 @@
1
+ import { DeleteOutlined, UploadOutlined } from "@ant-design/icons";
2
+ import { Button, Image, Space } from "antd";
3
+ import { useRef } from "react";
4
+ import type { BaseSourceProps, FieldRules } from "../types";
5
+ import { FieldWrapper } from "./FieldWrapper";
6
+ import { hasUploadValue, type UploadFieldValue } from "./uploadFieldUtils";
7
+ import { useUploadPreviewUrl } from "./useUploadPreviewUrl";
8
+
9
+ export type ImageFieldProps = BaseSourceProps & {
10
+ name?: string;
11
+ required?: boolean;
12
+ rules?: FieldRules;
13
+ hideLabel?: boolean;
14
+ /** Allow clearing an existing or newly selected image. */
15
+ clearable?: boolean;
16
+ accept?: string;
17
+ /** Max preview width in pixels. */
18
+ previewWidth?: number;
19
+ };
20
+
21
+ function ImageFieldInput({
22
+ value,
23
+ onChange,
24
+ disabled,
25
+ clearable,
26
+ accept = "image/*",
27
+ previewWidth = 200,
28
+ }: {
29
+ value: UploadFieldValue;
30
+ onChange: (value: UploadFieldValue) => void;
31
+ disabled?: boolean;
32
+ clearable?: boolean;
33
+ accept?: string;
34
+ previewWidth?: number;
35
+ }) {
36
+ const inputRef = useRef<HTMLInputElement>(null);
37
+ const previewUrl = useUploadPreviewUrl(value);
38
+ const showClear = clearable && hasUploadValue(value);
39
+
40
+ return (
41
+ <Space direction="vertical" size="middle" style={{ width: "100%" }}>
42
+ {previewUrl ? (
43
+ <Image
44
+ src={previewUrl}
45
+ alt=""
46
+ style={{ maxWidth: previewWidth, maxHeight: previewWidth, objectFit: "contain" }}
47
+ />
48
+ ) : null}
49
+ <Space wrap>
50
+ <Button
51
+ icon={<UploadOutlined />}
52
+ disabled={disabled}
53
+ onClick={() => inputRef.current?.click()}
54
+ >
55
+ Choose image
56
+ </Button>
57
+ {showClear ? (
58
+ <Button
59
+ icon={<DeleteOutlined />}
60
+ disabled={disabled}
61
+ onClick={() => {
62
+ onChange(null);
63
+ if (inputRef.current) inputRef.current.value = "";
64
+ }}
65
+ >
66
+ Clear
67
+ </Button>
68
+ ) : null}
69
+ </Space>
70
+ <input
71
+ ref={inputRef}
72
+ type="file"
73
+ accept={accept}
74
+ disabled={disabled}
75
+ tabIndex={-1}
76
+ aria-hidden
77
+ style={{ display: "none" }}
78
+ onChange={(e) => {
79
+ const file = e.target.files?.[0];
80
+ onChange(file ?? null);
81
+ e.target.value = "";
82
+ }}
83
+ />
84
+ </Space>
85
+ );
86
+ }
87
+
88
+ export function ImageField({
89
+ source,
90
+ name,
91
+ label,
92
+ required,
93
+ rules,
94
+ hideLabel,
95
+ clearable,
96
+ accept,
97
+ previewWidth,
98
+ }: ImageFieldProps) {
99
+ return (
100
+ <FieldWrapper
101
+ source={source}
102
+ name={name}
103
+ label={label}
104
+ required={required}
105
+ rules={rules}
106
+ hideLabel={hideLabel}
107
+ >
108
+ {({ value, onChange, disabled }) => (
109
+ <ImageFieldInput
110
+ value={value as UploadFieldValue}
111
+ onChange={onChange}
112
+ disabled={disabled}
113
+ clearable={clearable}
114
+ accept={accept}
115
+ previewWidth={previewWidth}
116
+ />
117
+ )}
118
+ </FieldWrapper>
119
+ );
120
+ }
@@ -1,50 +1,52 @@
1
1
  import { InputNumber } from "antd";
2
2
  import type { CSSProperties } from "react";
3
- import type { BaseSourceProps, FieldRules, InlineFieldOptions } from "../types";
4
- import { useInlineOrFormField } from "./useInlineOrFormField";
3
+ import type { BaseSourceProps, FieldRules } from "../types";
4
+ import { FieldWrapper } from "./FieldWrapper";
5
5
 
6
- export type NumberFieldProps = BaseSourceProps &
7
- InlineFieldOptions & {
8
- required?: boolean;
9
- rules?: FieldRules;
10
- min?: number;
11
- max?: number;
12
- step?: number;
13
- inputStyle?: CSSProperties;
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
- const field = useInlineOrFormField(
29
- source,
30
- label,
31
- required,
32
- rules,
33
- ({ value, onChange, onBlur, disabled }) => (
34
- <InputNumber
35
- value={value as number | undefined}
36
- onChange={(v) => onChange(v)}
37
- onBlur={onBlur}
38
- min={min}
39
- max={max}
40
- step={step}
41
- disabled={disabled}
42
- style={{ width: "100%", ...inputStyle }}
43
- />
44
- ),
45
- { width: inlineWidth, minWidth: inlineMinWidth },
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, InlineFieldOptions } from "../types";
4
- import { useInlineOrFormField } from "./useInlineOrFormField";
3
+ import type { BaseSourceProps, FieldRules } from "../types";
4
+ import { FieldWrapper } from "./FieldWrapper";
5
5
 
6
- export type PasswordFieldProps = BaseSourceProps &
7
- InlineFieldOptions & {
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
- };
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
- width: inlineWidth,
23
- minWidth: inlineMinWidth,
24
+ hideLabel,
24
25
  }: PasswordFieldProps) {
25
- const field = useInlineOrFormField(
26
- source,
27
- label,
28
- required,
29
- rules,
30
- ({ value, onChange, onBlur, disabled }) => (
31
- <Input.Password
32
- value={value as string | undefined}
33
- onChange={(e) => onChange(e.target.value)}
34
- onBlur={onBlur}
35
- disabled={disabled}
36
- autoComplete={autoComplete}
37
- />
38
- ),
39
- { width: inlineWidth, minWidth: inlineMinWidth },
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
- width,
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
- width={width}
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
- width={width}
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
- width={width}
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
- ReferenceProps &
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
- const field = useInlineOrFormField(
134
-
135
- source,
136
-
137
- label,
138
-
139
- required,
140
-
141
- rules,
142
-
143
- ({ value, onChange, disabled, name, index }) => (
144
-
145
- <Select
146
-
147
- value={value as string | number | undefined}
148
-
149
- onChange={(next) => {
150
-
151
- onChange(next);
152
-
153
- onValueChange?.(next, optionForValue(next), { name, index });
154
-
155
- }}
156
-
157
- options={selectOptions}
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
-
@@ -2,18 +2,21 @@ import { Select } from "antd";
2
2
  import { useMemo, useState } from "react";
3
3
  import type { BaseSourceProps, FieldRules, ReferenceProps } from "../types";
4
4
  import { useChoices } from "../utils/useChoices";
5
- import { useInlineOrFormField } from "./useInlineOrFormField";
5
+ import { FieldWrapper } from "./FieldWrapper";
6
6
 
7
7
  export type ReferenceManyFieldProps = BaseSourceProps &
8
8
  ReferenceProps & {
9
+ name?: string;
9
10
  required?: boolean;
10
11
  rules?: FieldRules;
11
12
  search?: boolean;
12
13
  allowClear?: boolean;
14
+ hideLabel?: boolean;
13
15
  };
14
16
 
15
17
  export function ReferenceManyField({
16
18
  source,
19
+ name,
17
20
  label,
18
21
  reference,
19
22
  choices,
@@ -23,6 +26,7 @@ export function ReferenceManyField({
23
26
  rules,
24
27
  search,
25
28
  allowClear = true,
29
+ hideLabel,
26
30
  }: ReferenceManyFieldProps) {
27
31
  const [searchText, setSearchText] = useState<string | undefined>();
28
32
  const { options, loading } = useChoices(
@@ -42,29 +46,31 @@ export function ReferenceManyField({
42
46
  [options],
43
47
  );
44
48
 
45
- const field = useInlineOrFormField(
46
- source,
47
- label,
48
- required,
49
- rules,
50
- ({ value, onChange, disabled }) => (
51
- <Select
52
- mode="multiple"
53
- value={(value as (string | number)[] | undefined) ?? []}
54
- onChange={onChange}
55
- options={selectOptions}
56
- loading={loading}
57
- showSearch={search}
58
- filterOption={search ? false : undefined}
59
- onSearch={search ? setSearchText : undefined}
60
- allowClear={allowClear}
61
- disabled={disabled}
62
- optionFilterProp="label"
63
- style={{ width: "100%" }}
64
- />
65
- ),
49
+ return (
50
+ <FieldWrapper
51
+ source={source}
52
+ name={name}
53
+ label={label}
54
+ required={required}
55
+ rules={rules}
56
+ hideLabel={hideLabel}
57
+ >
58
+ {({ value, onChange, disabled }) => (
59
+ <Select
60
+ mode="multiple"
61
+ value={(value as (string | number)[] | undefined) ?? []}
62
+ onChange={onChange}
63
+ options={selectOptions}
64
+ loading={loading}
65
+ showSearch={search}
66
+ filterOption={search ? false : undefined}
67
+ onSearch={search ? setSearchText : undefined}
68
+ allowClear={allowClear}
69
+ disabled={disabled}
70
+ optionFilterProp="label"
71
+ style={{ width: "100%" }}
72
+ />
73
+ )}
74
+ </FieldWrapper>
66
75
  );
67
-
68
- if (field.mode === "inline") return null;
69
- return field.element;
70
76
  }
@@ -3,28 +3,34 @@ import type { BaseSourceProps, ChoiceOption, FieldRules } from "../types";
3
3
  import { FieldWrapper } from "./FieldWrapper";
4
4
 
5
5
  export type SelectFieldProps = BaseSourceProps & {
6
+ name?: string;
6
7
  required?: boolean;
7
8
  rules?: FieldRules;
8
9
  choices: ChoiceOption[];
9
10
  mode?: "multiple";
10
11
  allowClear?: boolean;
12
+ hideLabel?: boolean;
11
13
  };
12
14
 
13
15
  export function SelectField({
14
16
  source,
17
+ name,
15
18
  label,
16
19
  required,
17
20
  rules,
18
21
  choices,
19
22
  mode,
20
23
  allowClear,
24
+ hideLabel,
21
25
  }: SelectFieldProps) {
22
26
  return (
23
27
  <FieldWrapper
24
28
  source={source}
29
+ name={name}
25
30
  label={label}
26
31
  required={required}
27
32
  rules={rules}
33
+ hideLabel={hideLabel}
28
34
  >
29
35
  {({ value, onChange, disabled }) => (
30
36
  <Select