ding-react-admin 2.0.0 → 2.0.5

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 (169) hide show
  1. package/LICENSE +21 -21
  2. package/README.md +254 -195
  3. package/dist/index.css +1 -0
  4. package/dist/index.js +1773 -1170
  5. package/dist/src/components/NavMenuLabel.d.ts +8 -0
  6. package/dist/src/components/NavMenuLabel.d.ts.map +1 -0
  7. package/dist/src/components/NavMenuSearch.d.ts +9 -0
  8. package/dist/src/components/NavMenuSearch.d.ts.map +1 -0
  9. package/dist/src/components/ScrollableArea.d.ts +10 -0
  10. package/dist/src/components/ScrollableArea.d.ts.map +1 -0
  11. package/dist/src/components/index.d.ts +3 -0
  12. package/dist/src/components/index.d.ts.map +1 -1
  13. package/dist/src/context/AuthProvider.d.ts +2 -0
  14. package/dist/src/context/AuthProvider.d.ts.map +1 -1
  15. package/dist/src/crud/FormGlobalErrorsAlert.d.ts +5 -0
  16. package/dist/src/crud/FormGlobalErrorsAlert.d.ts.map +1 -0
  17. package/dist/src/crud/ResourceForm.d.ts +1 -4
  18. package/dist/src/crud/ResourceForm.d.ts.map +1 -1
  19. package/dist/src/crud/ResourceFormModal.d.ts +7 -1
  20. package/dist/src/crud/ResourceFormModal.d.ts.map +1 -1
  21. package/dist/src/crud/ResourceRecordForm.d.ts +27 -0
  22. package/dist/src/crud/ResourceRecordForm.d.ts.map +1 -0
  23. package/dist/src/crud/fields/ReferenceField.d.ts +12 -2
  24. package/dist/src/crud/fields/ReferenceField.d.ts.map +1 -1
  25. package/dist/src/crud/fields/ReferenceInputActions.d.ts +16 -0
  26. package/dist/src/crud/fields/ReferenceInputActions.d.ts.map +1 -0
  27. package/dist/src/crud/fields/ReferenceManyField.d.ts +10 -1
  28. package/dist/src/crud/fields/ReferenceManyField.d.ts.map +1 -1
  29. package/dist/src/crud/filters/ReferenceFilter.d.ts +1 -1
  30. package/dist/src/crud/filters/ReferenceFilter.d.ts.map +1 -1
  31. package/dist/src/crud/index.d.ts +5 -0
  32. package/dist/src/crud/index.d.ts.map +1 -1
  33. package/dist/src/crud/types.d.ts +12 -0
  34. package/dist/src/crud/types.d.ts.map +1 -1
  35. package/dist/src/crud/utils/choiceSelectionUtils.d.ts +10 -0
  36. package/dist/src/crud/utils/choiceSelectionUtils.d.ts.map +1 -0
  37. package/dist/src/crud/utils/choiceSelectionUtils.test.d.ts +2 -0
  38. package/dist/src/crud/utils/choiceSelectionUtils.test.d.ts.map +1 -0
  39. package/dist/src/crud/utils/formErrors.d.ts +18 -7
  40. package/dist/src/crud/utils/formErrors.d.ts.map +1 -1
  41. package/dist/src/crud/utils/formErrors.test.d.ts +2 -0
  42. package/dist/src/crud/utils/formErrors.test.d.ts.map +1 -0
  43. package/dist/src/crud/utils/prepareFormSubmitBody.d.ts +1 -0
  44. package/dist/src/crud/utils/prepareFormSubmitBody.d.ts.map +1 -1
  45. package/dist/src/crud/utils/referenceSelectNotFoundContent.d.ts +4 -0
  46. package/dist/src/crud/utils/referenceSelectNotFoundContent.d.ts.map +1 -0
  47. package/dist/src/crud/utils/toFormData.d.ts +1 -1
  48. package/dist/src/crud/utils/toFormData.d.ts.map +1 -1
  49. package/dist/src/crud/utils/uploadReferenceUtils.d.ts +5 -0
  50. package/dist/src/crud/utils/uploadReferenceUtils.d.ts.map +1 -0
  51. package/dist/src/crud/utils/useChoices.d.ts +27 -1
  52. package/dist/src/crud/utils/useChoices.d.ts.map +1 -1
  53. package/dist/src/crud/utils/useFormRecord.d.ts +9 -7
  54. package/dist/src/crud/utils/useFormRecord.d.ts.map +1 -1
  55. package/dist/src/data/parseFormErrorHelpers.d.ts +6 -27
  56. package/dist/src/data/parseFormErrorHelpers.d.ts.map +1 -1
  57. package/dist/src/index.d.ts +2 -2
  58. package/dist/src/index.d.ts.map +1 -1
  59. package/dist/src/layouts/AdminLayout.d.ts +1 -1
  60. package/dist/src/layouts/AdminLayout.d.ts.map +1 -1
  61. package/dist/src/layouts/AuthPageLayout.d.ts.map +1 -1
  62. package/dist/src/layouts/navFilter.d.ts +5 -0
  63. package/dist/src/layouts/navFilter.d.ts.map +1 -0
  64. package/dist/src/layouts/navMenuItems.d.ts +6 -0
  65. package/dist/src/layouts/navMenuItems.d.ts.map +1 -0
  66. package/dist/src/types.d.ts +12 -0
  67. package/dist/src/types.d.ts.map +1 -1
  68. package/package.json +85 -59
  69. package/src/app/AdminApp.tsx +34 -34
  70. package/src/components/AuthAlternateLink.tsx +23 -23
  71. package/src/components/DensitySwitch.tsx +31 -31
  72. package/src/components/NavMenuLabel.tsx +26 -0
  73. package/src/components/NavMenuSearch.tsx +76 -0
  74. package/src/components/ScrollableArea.tsx +46 -0
  75. package/src/components/ThemeSwitch.tsx +28 -28
  76. package/src/components/ThemeToolbar.tsx +34 -34
  77. package/src/components/index.ts +7 -4
  78. package/src/components/navMenu.css +21 -0
  79. package/src/components/scrollable.css +28 -0
  80. package/src/context/AppThemeProvider.tsx +128 -128
  81. package/src/context/AuthProvider.tsx +89 -77
  82. package/src/context/DataProvider.tsx +30 -30
  83. package/src/context/PermissionsProvider.tsx +57 -57
  84. package/src/crud/FilterBar.tsx +36 -36
  85. package/src/crud/FormGlobalErrorsAlert.tsx +36 -0
  86. package/src/crud/FormSteps.tsx +135 -135
  87. package/src/crud/FormTabs.tsx +126 -126
  88. package/src/crud/InlineFormSet.tsx +162 -162
  89. package/src/crud/ListActionsBar.tsx +106 -106
  90. package/src/crud/ResourceForm.tsx +83 -160
  91. package/src/crud/ResourceFormModal.tsx +75 -133
  92. package/src/crud/ResourceRecordForm.tsx +166 -0
  93. package/src/crud/columns/BooleanColumn.tsx +32 -32
  94. package/src/crud/columns/CustomColumn.tsx +33 -33
  95. package/src/crud/columns/DateColumn.tsx +28 -28
  96. package/src/crud/columns/ImageColumn.tsx +58 -58
  97. package/src/crud/columns/NumberColumn.tsx +31 -31
  98. package/src/crud/columns/ReferenceColumn.tsx +85 -85
  99. package/src/crud/columns/ReferenceManyColumn.tsx +67 -67
  100. package/src/crud/columns/TextColumn.tsx +38 -38
  101. package/src/crud/context/FilterContext.tsx +71 -71
  102. package/src/crud/context/FormContext.tsx +39 -39
  103. package/src/crud/context/FormSectionContext.tsx +22 -22
  104. package/src/crud/context/InlineFieldsRegistry.tsx +61 -61
  105. package/src/crud/context/ListContext.tsx +93 -93
  106. package/src/crud/fields/BooleanField.tsx +38 -38
  107. package/src/crud/fields/DateField.tsx +53 -53
  108. package/src/crud/fields/FileField.tsx +123 -123
  109. package/src/crud/fields/ImageField.tsx +120 -120
  110. package/src/crud/fields/NumberField.tsx +52 -52
  111. package/src/crud/fields/PasswordField.tsx +99 -99
  112. package/src/crud/fields/ReferenceField.tsx +237 -96
  113. package/src/crud/fields/ReferenceInputActions.tsx +106 -0
  114. package/src/crud/fields/ReferenceManyField.tsx +200 -76
  115. package/src/crud/fields/SelectField.tsx +48 -48
  116. package/src/crud/fields/TextField.tsx +46 -46
  117. package/src/crud/fields/uploadFieldUtils.ts +21 -21
  118. package/src/crud/fields/useUploadPreviewUrl.ts +20 -20
  119. package/src/crud/filters/BooleanFilter.tsx +38 -38
  120. package/src/crud/filters/DateFilter.tsx +35 -35
  121. package/src/crud/filters/NumberFilter.tsx +33 -33
  122. package/src/crud/filters/ReferenceFilter.tsx +192 -173
  123. package/src/crud/filters/SelectFilter.tsx +44 -44
  124. package/src/crud/filters/TextFilter.tsx +36 -36
  125. package/src/crud/index.ts +99 -94
  126. package/src/crud/types.ts +146 -134
  127. package/src/crud/utils/buildFormPayload.ts +24 -24
  128. package/src/crud/utils/buildInlineRowsPayload.ts +37 -37
  129. package/src/crud/utils/buildResourceFormSubmitBody.ts +29 -29
  130. package/src/crud/utils/choiceSelectionUtils.test.ts +45 -0
  131. package/src/crud/utils/choiceSelectionUtils.ts +116 -0
  132. package/src/crud/utils/formErrors.test.ts +168 -0
  133. package/src/crud/utils/formErrors.ts +138 -42
  134. package/src/crud/utils/formSectionErrors.ts +85 -85
  135. package/src/crud/utils/getFormValue.ts +13 -13
  136. package/src/crud/utils/hasUploadValues.ts +9 -9
  137. package/src/crud/utils/nestedFieldPath.ts +8 -8
  138. package/src/crud/utils/prepareFormSubmitBody.ts +22 -17
  139. package/src/crud/utils/referenceSelectNotFoundContent.tsx +7 -0
  140. package/src/crud/utils/setFormValue.ts +18 -18
  141. package/src/crud/utils/sortQueryParam.ts +37 -37
  142. package/src/crud/utils/toFormData.ts +79 -81
  143. package/src/crud/utils/uploadReferenceUtils.ts +44 -0
  144. package/src/crud/utils/useAbortableEffect.ts +17 -17
  145. package/src/crud/utils/useChoices.ts +330 -182
  146. package/src/crud/utils/useFormRecord.ts +179 -156
  147. package/src/crud/utils/useListQueryState.ts +191 -191
  148. package/src/data/abortError.test.ts +19 -19
  149. package/src/data/abortError.ts +10 -10
  150. package/src/data/createMemoryResourceHandlers.ts +78 -78
  151. package/src/data/createRestResourceHandlers.ts +63 -63
  152. package/src/data/dataUtils.test.ts +228 -220
  153. package/src/data/inMemoryList.ts +97 -97
  154. package/src/data/parseFormErrorHelpers.test.ts +201 -138
  155. package/src/data/parseFormErrorHelpers.ts +376 -311
  156. package/src/data/sortHelpers.ts +48 -48
  157. package/src/index.ts +211 -207
  158. package/src/layouts/AdminLayout.tsx +530 -382
  159. package/src/layouts/AuthPageLayout.tsx +87 -81
  160. package/src/layouts/navFilter.ts +49 -0
  161. package/src/layouts/navMenuItems.tsx +44 -0
  162. package/src/pages/LoginPage.tsx +80 -80
  163. package/src/pages/PlaceholderPage.tsx +9 -9
  164. package/src/permissions/resourcePermissions.ts +47 -47
  165. package/src/router/createAdminRouter.tsx +64 -64
  166. package/src/router/guards.tsx +63 -63
  167. package/src/router/routeAccess.ts +89 -89
  168. package/src/types.ts +10 -0
  169. package/src/vite-env.d.ts +1 -1
@@ -1,76 +1,200 @@
1
- import { Select } from "antd";
2
- import { useMemo, useState } from "react";
3
- import type { BaseSourceProps, FieldRules, ReferenceProps } from "../types";
4
- import { useChoices } from "../utils/useChoices";
5
- import { FieldWrapper } from "./FieldWrapper";
6
-
7
- export type ReferenceManyFieldProps = BaseSourceProps &
8
- ReferenceProps & {
9
- name?: string;
10
- required?: boolean;
11
- rules?: FieldRules;
12
- search?: boolean;
13
- allowClear?: boolean;
14
- hideLabel?: boolean;
15
- };
16
-
17
- export function ReferenceManyField({
18
- source,
19
- name,
20
- label,
21
- reference,
22
- choices,
23
- optionLabel = "name",
24
- optionValue = "id",
25
- required,
26
- rules,
27
- search,
28
- allowClear = true,
29
- hideLabel,
30
- }: ReferenceManyFieldProps) {
31
- const [searchText, setSearchText] = useState<string | undefined>();
32
- const { options, loading } = useChoices(
33
- choices,
34
- reference,
35
- optionLabel,
36
- optionValue,
37
- search ? searchText : undefined,
38
- );
39
-
40
- const selectOptions = useMemo(
41
- () =>
42
- options.map((o) => ({
43
- label: o.label,
44
- value: o.value as string | number,
45
- })),
46
- [options],
47
- );
48
-
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>
75
- );
76
- }
1
+ import { Select } from "antd";
2
+ import { useMemo, useState } from "react";
3
+ import { useWatch } from "react-hook-form";
4
+ import type { BaseSourceProps, FieldRules, ReferenceProps } from "../types";
5
+ import { valuesAsIds } from "../utils/choiceSelectionUtils";
6
+ import { referenceSelectNotFoundContent } from "../utils/referenceSelectNotFoundContent";
7
+ import { useChoices } from "../utils/useChoices";
8
+ import { FieldWrapper } from "./FieldWrapper";
9
+ import { ReferenceInputActions } from "./ReferenceInputActions";
10
+ import type { ResourcePermissions } from "../../permissions/resourcePermissions";
11
+ import type { ReactNode } from "react";
12
+
13
+ export type ReferenceManyFieldProps = BaseSourceProps &
14
+ ReferenceProps & {
15
+ name?: string;
16
+ required?: boolean;
17
+ rules?: FieldRules;
18
+ search?: boolean;
19
+ allowClear?: boolean;
20
+ hideLabel?: boolean;
21
+ referenceForm?: ReactNode;
22
+ referencePermissions?: ResourcePermissions;
23
+ referenceTitle?: string;
24
+ referenceDefaultValues?: Record<string, unknown>;
25
+ referenceModalWidth?: number;
26
+ /** When false, hide add button even if catalog defines a form. Default true. */
27
+ referenceActions?: boolean;
28
+ };
29
+
30
+ type ReferenceManyFieldSelectProps = Omit<
31
+ ReferenceManyFieldProps,
32
+ "source" | "label" | "required" | "rules" | "hideLabel" | "name"
33
+ > & {
34
+ value: unknown;
35
+ onChange: (value: unknown) => void;
36
+ disabled?: boolean;
37
+ selectedRecords?: Record<string, unknown> | Record<string, unknown>[];
38
+ };
39
+
40
+ function ReferenceManyFieldSelect({
41
+ reference,
42
+ choices,
43
+ optionLabel = "name",
44
+ optionValue = "id",
45
+ search,
46
+ allowClear = true,
47
+ lazy = true,
48
+ fetchSelected = true,
49
+ value,
50
+ onChange,
51
+ disabled,
52
+ selectedRecords,
53
+ referenceForm,
54
+ referencePermissions,
55
+ referenceTitle,
56
+ referenceDefaultValues,
57
+ referenceModalWidth,
58
+ referenceActions = true,
59
+ }: ReferenceManyFieldSelectProps) {
60
+ const [searchText, setSearchText] = useState<string | undefined>();
61
+ const [dropdownOpen, setDropdownOpen] = useState(false);
62
+ const active = dropdownOpen || Boolean(searchText);
63
+ const selectValue = valuesAsIds(value, optionValue);
64
+
65
+ const { options, loading, reload } = useChoices(
66
+ choices,
67
+ reference,
68
+ optionLabel,
69
+ optionValue,
70
+ search ? searchText : undefined,
71
+ {
72
+ lazy,
73
+ active,
74
+ selectedValues: value,
75
+ selectedRecords,
76
+ fetchSelected,
77
+ },
78
+ );
79
+
80
+ const selectOptions = useMemo(
81
+ () =>
82
+ options.map((o) => ({
83
+ label: o.label,
84
+ value: o.value as string | number,
85
+ })),
86
+ [options],
87
+ );
88
+
89
+ const select = (
90
+ <Select
91
+ mode="multiple"
92
+ value={selectValue}
93
+ onChange={onChange}
94
+ options={selectOptions}
95
+ loading={loading}
96
+ notFoundContent={referenceSelectNotFoundContent(loading)}
97
+ showSearch={search}
98
+ filterOption={search ? false : undefined}
99
+ onSearch={search ? setSearchText : undefined}
100
+ onDropdownVisibleChange={(open) => {
101
+ setDropdownOpen(open);
102
+ if (!open) setSearchText(undefined);
103
+ }}
104
+ allowClear={allowClear}
105
+ disabled={disabled}
106
+ optionFilterProp="label"
107
+ style={{ width: "100%" }}
108
+ />
109
+ );
110
+
111
+ if (!referenceActions) return select;
112
+
113
+ return (
114
+ <div style={{ display: "flex", gap: 8, width: "100%", alignItems: "flex-start" }}>
115
+ <div style={{ flex: 1, minWidth: 0 }}>{select}</div>
116
+ <ReferenceInputActions
117
+ reference={reference}
118
+ referenceForm={referenceForm}
119
+ referencePermissions={referencePermissions}
120
+ referenceTitle={referenceTitle}
121
+ referenceDefaultValues={referenceDefaultValues}
122
+ referenceModalWidth={referenceModalWidth}
123
+ disabled={disabled}
124
+ onCreated={(record) => {
125
+ const id = record[optionValue];
126
+ const current = Array.isArray(selectValue) ? selectValue : [];
127
+ if (current.some((v) => v === id)) {
128
+ void reload();
129
+ return;
130
+ }
131
+ onChange([...current, id]);
132
+ void reload();
133
+ }}
134
+ />
135
+ </div>
136
+ );
137
+ }
138
+
139
+ export function ReferenceManyField({
140
+ source,
141
+ name,
142
+ label,
143
+ reference,
144
+ choices,
145
+ optionLabel = "name",
146
+ optionValue = "id",
147
+ required,
148
+ rules,
149
+ search,
150
+ allowClear = true,
151
+ hideLabel,
152
+ lazy = true,
153
+ recordSource,
154
+ fetchSelected = true,
155
+ referenceForm,
156
+ referencePermissions,
157
+ referenceTitle,
158
+ referenceDefaultValues,
159
+ referenceModalWidth,
160
+ referenceActions = true,
161
+ }: ReferenceManyFieldProps) {
162
+ const embeddedRecords = useWatch({
163
+ name: recordSource ?? "",
164
+ disabled: !recordSource,
165
+ }) as Record<string, unknown> | Record<string, unknown>[] | undefined;
166
+
167
+ return (
168
+ <FieldWrapper
169
+ source={source}
170
+ name={name}
171
+ label={label}
172
+ required={required}
173
+ rules={rules}
174
+ hideLabel={hideLabel}
175
+ >
176
+ {({ value, onChange, disabled }) => (
177
+ <ReferenceManyFieldSelect
178
+ reference={reference}
179
+ choices={choices}
180
+ optionLabel={optionLabel}
181
+ optionValue={optionValue}
182
+ search={search}
183
+ allowClear={allowClear}
184
+ lazy={lazy}
185
+ fetchSelected={fetchSelected}
186
+ value={value}
187
+ onChange={onChange}
188
+ disabled={disabled}
189
+ selectedRecords={recordSource ? embeddedRecords : undefined}
190
+ referenceForm={referenceForm}
191
+ referencePermissions={referencePermissions}
192
+ referenceTitle={referenceTitle}
193
+ referenceDefaultValues={referenceDefaultValues}
194
+ referenceModalWidth={referenceModalWidth}
195
+ referenceActions={referenceActions}
196
+ />
197
+ )}
198
+ </FieldWrapper>
199
+ );
200
+ }
@@ -1,48 +1,48 @@
1
- import { Select } from "antd";
2
- import type { BaseSourceProps, ChoiceOption, FieldRules } from "../types";
3
- import { FieldWrapper } from "./FieldWrapper";
4
-
5
- export type SelectFieldProps = BaseSourceProps & {
6
- name?: string;
7
- required?: boolean;
8
- rules?: FieldRules;
9
- choices: ChoiceOption[];
10
- mode?: "multiple";
11
- allowClear?: boolean;
12
- hideLabel?: boolean;
13
- };
14
-
15
- export function SelectField({
16
- source,
17
- name,
18
- label,
19
- required,
20
- rules,
21
- choices,
22
- mode,
23
- allowClear,
24
- hideLabel,
25
- }: SelectFieldProps) {
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, disabled }) => (
36
- <Select
37
- value={value as string | number | (string | number)[] | undefined}
38
- onChange={onChange}
39
- options={choices}
40
- mode={mode}
41
- allowClear={allowClear}
42
- disabled={disabled}
43
- style={{ width: "100%" }}
44
- />
45
- )}
46
- </FieldWrapper>
47
- );
48
- }
1
+ import { Select } from "antd";
2
+ import type { BaseSourceProps, ChoiceOption, FieldRules } from "../types";
3
+ import { FieldWrapper } from "./FieldWrapper";
4
+
5
+ export type SelectFieldProps = BaseSourceProps & {
6
+ name?: string;
7
+ required?: boolean;
8
+ rules?: FieldRules;
9
+ choices: ChoiceOption[];
10
+ mode?: "multiple";
11
+ allowClear?: boolean;
12
+ hideLabel?: boolean;
13
+ };
14
+
15
+ export function SelectField({
16
+ source,
17
+ name,
18
+ label,
19
+ required,
20
+ rules,
21
+ choices,
22
+ mode,
23
+ allowClear,
24
+ hideLabel,
25
+ }: SelectFieldProps) {
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, disabled }) => (
36
+ <Select
37
+ value={value as string | number | (string | number)[] | undefined}
38
+ onChange={onChange}
39
+ options={choices}
40
+ mode={mode}
41
+ allowClear={allowClear}
42
+ disabled={disabled}
43
+ style={{ width: "100%" }}
44
+ />
45
+ )}
46
+ </FieldWrapper>
47
+ );
48
+ }
@@ -1,46 +1,46 @@
1
- import { Input } from "antd";
2
- import type { CSSProperties } from "react";
3
- import type { BaseSourceProps, FieldRules } from "../types";
4
- import { FieldWrapper } from "./FieldWrapper";
5
-
6
- export type TextFieldProps = BaseSourceProps & {
7
- name?: string;
8
- required?: boolean;
9
- rules?: FieldRules;
10
- placeholder?: string;
11
- inputStyle?: CSSProperties;
12
- hideLabel?: boolean;
13
- };
14
-
15
- export function TextField({
16
- source,
17
- name,
18
- label,
19
- required,
20
- rules,
21
- placeholder,
22
- inputStyle,
23
- hideLabel,
24
- }: TextFieldProps) {
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
- <Input
36
- value={value as string | undefined}
37
- onChange={(e) => onChange(e.target.value)}
38
- onBlur={onBlur}
39
- placeholder={placeholder}
40
- disabled={disabled}
41
- style={inputStyle}
42
- />
43
- )}
44
- </FieldWrapper>
45
- );
46
- }
1
+ import { Input } from "antd";
2
+ import type { CSSProperties } from "react";
3
+ import type { BaseSourceProps, FieldRules } from "../types";
4
+ import { FieldWrapper } from "./FieldWrapper";
5
+
6
+ export type TextFieldProps = BaseSourceProps & {
7
+ name?: string;
8
+ required?: boolean;
9
+ rules?: FieldRules;
10
+ placeholder?: string;
11
+ inputStyle?: CSSProperties;
12
+ hideLabel?: boolean;
13
+ };
14
+
15
+ export function TextField({
16
+ source,
17
+ name,
18
+ label,
19
+ required,
20
+ rules,
21
+ placeholder,
22
+ inputStyle,
23
+ hideLabel,
24
+ }: TextFieldProps) {
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
+ <Input
36
+ value={value as string | undefined}
37
+ onChange={(e) => onChange(e.target.value)}
38
+ onBlur={onBlur}
39
+ placeholder={placeholder}
40
+ disabled={disabled}
41
+ style={inputStyle}
42
+ />
43
+ )}
44
+ </FieldWrapper>
45
+ );
46
+ }
@@ -1,21 +1,21 @@
1
- /** Form value for file/image fields — backend-agnostic. */
2
- export type UploadFieldValue = File | string | null | undefined;
3
-
4
- export function hasUploadValue(value: UploadFieldValue): boolean {
5
- if (value instanceof File) return true;
6
- return typeof value === "string" && value.length > 0;
7
- }
8
-
9
- export function getUploadFileName(value: UploadFieldValue): string | undefined {
10
- if (value instanceof File) return value.name;
11
- if (typeof value === "string" && value.length > 0) {
12
- try {
13
- const pathname = new URL(value, "http://local").pathname;
14
- const name = pathname.split("/").filter(Boolean).pop();
15
- return name || value;
16
- } catch {
17
- return value.split("/").filter(Boolean).pop() || value;
18
- }
19
- }
20
- return undefined;
21
- }
1
+ /** Form value for file/image fields — backend-agnostic. */
2
+ export type UploadFieldValue = File | string | null | undefined;
3
+
4
+ export function hasUploadValue(value: UploadFieldValue): boolean {
5
+ if (value instanceof File) return true;
6
+ return typeof value === "string" && value.length > 0;
7
+ }
8
+
9
+ export function getUploadFileName(value: UploadFieldValue): string | undefined {
10
+ if (value instanceof File) return value.name;
11
+ if (typeof value === "string" && value.length > 0) {
12
+ try {
13
+ const pathname = new URL(value, "http://local").pathname;
14
+ const name = pathname.split("/").filter(Boolean).pop();
15
+ return name || value;
16
+ } catch {
17
+ return value.split("/").filter(Boolean).pop() || value;
18
+ }
19
+ }
20
+ return undefined;
21
+ }
@@ -1,20 +1,20 @@
1
- import { useEffect, useState } from "react";
2
- import type { UploadFieldValue } from "./uploadFieldUtils";
3
-
4
- /** Preview URL for uploads — `URL.createObjectURL` for `File`, direct URL for existing strings. */
5
- export function useUploadPreviewUrl(value: UploadFieldValue): string | undefined {
6
- const [objectUrl, setObjectUrl] = useState<string | undefined>();
7
-
8
- useEffect(() => {
9
- if (value instanceof File) {
10
- const url = URL.createObjectURL(value);
11
- setObjectUrl(url);
12
- return () => URL.revokeObjectURL(url);
13
- }
14
- setObjectUrl(undefined);
15
- }, [value]);
16
-
17
- if (value instanceof File) return objectUrl;
18
- if (typeof value === "string" && value.length > 0) return value;
19
- return undefined;
20
- }
1
+ import { useEffect, useState } from "react";
2
+ import type { UploadFieldValue } from "./uploadFieldUtils";
3
+
4
+ /** Preview URL for uploads — `URL.createObjectURL` for `File`, direct URL for existing strings. */
5
+ export function useUploadPreviewUrl(value: UploadFieldValue): string | undefined {
6
+ const [objectUrl, setObjectUrl] = useState<string | undefined>();
7
+
8
+ useEffect(() => {
9
+ if (value instanceof File) {
10
+ const url = URL.createObjectURL(value);
11
+ setObjectUrl(url);
12
+ return () => URL.revokeObjectURL(url);
13
+ }
14
+ setObjectUrl(undefined);
15
+ }, [value]);
16
+
17
+ if (value instanceof File) return objectUrl;
18
+ if (typeof value === "string" && value.length > 0) return value;
19
+ return undefined;
20
+ }
@@ -1,38 +1,38 @@
1
- import { Select } from "antd";
2
- import { useMemo } from "react";
3
- import type { BaseSourceProps } from "../types";
4
- import { useRegisterFilter } from "../context/FilterContext";
5
-
6
- export type BooleanFilterProps = BaseSourceProps;
7
-
8
- export function BooleanFilter({ source, label }: BooleanFilterProps) {
9
- const def = useMemo(
10
- () => ({
11
- key: source,
12
- source,
13
- label,
14
- render: ({
15
- value,
16
- onChange,
17
- }: {
18
- value: unknown;
19
- onChange: (value: unknown) => void;
20
- }) => (
21
- <Select
22
- allowClear
23
- placeholder={label ?? source}
24
- value={value as boolean | undefined}
25
- onChange={(v) => onChange(v)}
26
- options={[
27
- { label: "Yes", value: true },
28
- { label: "No", value: false },
29
- ]}
30
- style={{ minWidth: 100 }}
31
- />
32
- ),
33
- }),
34
- [source, label],
35
- );
36
- useRegisterFilter(def);
37
- return null;
38
- }
1
+ import { Select } from "antd";
2
+ import { useMemo } from "react";
3
+ import type { BaseSourceProps } from "../types";
4
+ import { useRegisterFilter } from "../context/FilterContext";
5
+
6
+ export type BooleanFilterProps = BaseSourceProps;
7
+
8
+ export function BooleanFilter({ source, label }: BooleanFilterProps) {
9
+ const def = useMemo(
10
+ () => ({
11
+ key: source,
12
+ source,
13
+ label,
14
+ render: ({
15
+ value,
16
+ onChange,
17
+ }: {
18
+ value: unknown;
19
+ onChange: (value: unknown) => void;
20
+ }) => (
21
+ <Select
22
+ allowClear
23
+ placeholder={label ?? source}
24
+ value={value as boolean | undefined}
25
+ onChange={(v) => onChange(v)}
26
+ options={[
27
+ { label: "Yes", value: true },
28
+ { label: "No", value: false },
29
+ ]}
30
+ style={{ minWidth: 100 }}
31
+ />
32
+ ),
33
+ }),
34
+ [source, label],
35
+ );
36
+ useRegisterFilter(def);
37
+ return null;
38
+ }