@dashadmin/dash-components 1.3.25 → 1.3.28
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 +35 -0
- package/dist/components/AuditLog/AuditLog.js +345 -1
- package/dist/components/AuditLog/index.js +4 -1
- package/dist/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.js +630 -3
- package/dist/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.js +53 -1
- package/dist/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.js +647 -3
- package/dist/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.js +53 -1
- package/dist/components/Json/Json.js +235 -1
- package/dist/components/JsonColorSelector/JsonColorSelector.js +1172 -1
- package/dist/components/JsonColorSelector/JsonColorSelectorEnhanced.js +863 -2
- package/dist/components/JsonColorSelector/JsonCssVarValues.js +738 -1
- package/dist/components/JsonColorSelector/components/ColorEditDialog.js +426 -1
- package/dist/components/JsonColorSelector/components/ColorPaletteItem.js +75 -1
- package/dist/components/JsonColorSelector/components/ImageColorExtractor.js +283 -1
- package/dist/components/JsonColorSelector/helpers/functions.js +101 -1
- package/dist/components/JsonColorSelector/useAsyncColorThief.js +105 -1
- package/dist/components/ListActive/ListActive.js +126 -1
- package/dist/components/ListActive/ListBoolean.js +124 -1
- package/dist/components/NotificationPreferences/NotificationPreferences.js +317 -1
- package/dist/components/NotificationPreferences/index.js +7 -1
- package/dist/components/SearchableSelects/RASearchableSelect.js +236 -1
- package/dist/components/SearchableSelects/RASearchableSelectCheckboxes.js +479 -1
- package/dist/components/SearchableSelects/RASearchableSelectChips.js +465 -1
- package/dist/components/SearchableSelects/RASearchableSelectChipsRecordContext.js +295 -1
- package/dist/components/SearchableSelects/SearchableSelect.js +202 -1
- package/dist/components/SearchableSelects/SearchableSelectCheckboxes.js +290 -1
- package/dist/components/SortableDataGrid/SortableDataGrid.js +72 -1
- package/dist/components/SortableDataGrid/SortableDataGridBody.js +167 -1
- package/dist/components/SortableDataGrid/SortableDataGridHeader.js +109 -1
- package/dist/components/SortableDataGrid/SortableDataGridRow.js +283 -1
- package/dist/components/Upload/SingleImageUploader.js +40 -1
- package/dist/components/custom/PackageCopyMethod.js +78 -1
- package/dist/components/custom/PackageRadioButtonGroup.js +45 -1
- package/dist/components/dialog/Basic.js +8 -1
- package/dist/components/dialog/Correct.js +14 -1
- package/dist/components/dialog/Error.js +14 -1
- package/dist/components/dialog/Info.js +14 -1
- package/dist/components/dialog/InfoV2.js +21 -1
- package/dist/components/dialog/NotFound.js +14 -1
- package/dist/components/dialog/QuickSearch.js +117 -1
- package/dist/components/notifications/NotificationsCenter.js +597 -1
- package/dist/components/notifications/index.js +24 -1
- package/dist/components/tables/PaginationComponent.js +36 -1
- package/dist/components/theme/AppLoadingFallback.js +38 -2
- package/dist/components/theme/ErrorBoundary.js +52 -1
- package/dist/components/theme/InitialLoader.js +13 -1
- package/dist/components/theme/NotFound.js +136 -1
- package/dist/components/theme/NotResults.js +38 -1
- package/dist/hooks/useDraggable.js +6 -1
- package/dist/hooks/useNotifications.js +8 -1
- package/dist/hooks/useQuickSearch.js +26 -1
- package/dist/index.js +66 -5567
- package/dist/utils/setNativeValue.js +17 -1
- package/package.json +148 -132
- package/src/components/AuditLog/AuditLog.tsx +476 -0
- package/src/components/AuditLog/index.ts +2 -0
- package/src/components/AutoCompleteArrayInput/AutoCompleteCheckBoxInput.tsx +917 -0
- package/src/components/AutoCompleteArrayInput/AutocompleteCheckBoxArrayInput.tsx +39 -0
- package/src/components/AutoCompleteArrayInputAutoSearch/AutoCompleteCheckBoxInput.tsx +916 -0
- package/src/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayInput.tsx +44 -0
- package/src/components/Json/Json.tsx +312 -0
- package/src/components/JsonColorSelector/JsonColorSelector.tsx +1435 -0
- package/src/components/JsonColorSelector/JsonColorSelectorEnhanced.tsx +1029 -0
- package/src/components/JsonColorSelector/JsonCssVarValues.tsx +863 -0
- package/src/components/JsonColorSelector/components/ColorEditDialog.tsx +491 -0
- package/src/components/JsonColorSelector/components/ColorPaletteItem.tsx +80 -0
- package/src/components/JsonColorSelector/components/ImageColorExtractor.tsx +328 -0
- package/src/components/JsonColorSelector/helpers/functions.tsx +123 -0
- package/src/components/JsonColorSelector/interfaces/interfaces.tsx +22 -0
- package/src/components/JsonColorSelector/useAsyncColorThief.tsx +97 -0
- package/src/components/ListActive/ListActive.tsx +106 -0
- package/src/components/ListActive/ListBoolean.tsx +96 -0
- package/src/components/NotificationPreferences/NotificationPreferences.tsx +463 -0
- package/src/components/NotificationPreferences/index.tsx +2 -0
- package/src/components/SearchableSelects/RASearchableSelect.tsx +300 -0
- package/src/components/SearchableSelects/RASearchableSelectCheckboxes.tsx +604 -0
- package/src/components/SearchableSelects/RASearchableSelectChips.tsx +558 -0
- package/src/components/SearchableSelects/RASearchableSelectChipsRecordContext.tsx +362 -0
- package/src/components/SearchableSelects/SearchableSelect.tsx +321 -0
- package/src/components/SearchableSelects/SearchableSelectCheckboxes.tsx +355 -0
- package/src/components/SortableDataGrid/SortableDataGrid.tsx +56 -0
- package/src/components/SortableDataGrid/SortableDataGridBody.tsx +175 -0
- package/src/components/SortableDataGrid/SortableDataGridHeader.tsx +171 -0
- package/src/components/SortableDataGrid/SortableDataGridRow.tsx +247 -0
- package/src/components/Upload/SingleImageUploader.tsx +55 -0
- package/src/components/custom/PackageCopyMethod.tsx +109 -0
- package/src/components/custom/PackageRadioButtonGroup.tsx +47 -0
- package/src/components/dialog/Basic.tsx +11 -0
- package/src/components/dialog/Correct.tsx +16 -0
- package/src/components/dialog/Error.tsx +15 -0
- package/src/components/dialog/Info.tsx +15 -0
- package/src/components/dialog/InfoV2.tsx +35 -0
- package/src/components/dialog/NotFound.tsx +18 -0
- package/src/components/dialog/QuickSearch.tsx +115 -0
- package/src/components/notifications/NotificationsCenter.tsx +792 -0
- package/src/components/notifications/index.ts +42 -0
- package/src/components/tables/PaginationComponent.tsx +17 -0
- package/src/components/theme/AppLoadingFallback.tsx +42 -0
- package/src/components/theme/ErrorBoundary.tsx +57 -0
- package/src/components/theme/InitialLoader.tsx +16 -0
- package/src/components/theme/NotFound.tsx +124 -0
- package/src/components/theme/NotResults.tsx +54 -0
- package/src/hooks/useDraggable.ts +11 -0
- package/src/hooks/useNotifications.ts +14 -0
- package/src/hooks/useQuickSearch.tsx +29 -0
- package/src/index.tsx +77 -0
- package/src/utils/setNativeValue.tsx +23 -0
- package/dist/color-thief-CTwGFhOB.js +0 -272
- /package/{dist → src}/components/AutoCompleteArrayInputAutoSearch/AutocompleteCheckBoxArrayAutoSearch.tsx.wip +0 -0
- /package/{dist → src}/components/JsonColorSelector/JsonColorSelector.tsx.bkup +0 -0
- /package/{dist → src}/components/SortableDataGrid/README.md +0 -0
- /package/{dist → src}/styles/index.less +0 -0
|
@@ -0,0 +1,916 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {
|
|
3
|
+
isValidElement,
|
|
4
|
+
useCallback,
|
|
5
|
+
useEffect,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
ReactNode,
|
|
10
|
+
} from 'react';
|
|
11
|
+
import { isEqual , get, debounce} from 'lodash';
|
|
12
|
+
import clsx from 'clsx';
|
|
13
|
+
import {
|
|
14
|
+
Autocomplete,
|
|
15
|
+
AutocompleteProps,
|
|
16
|
+
Checkbox,
|
|
17
|
+
Chip,
|
|
18
|
+
TextField,
|
|
19
|
+
TextFieldProps,
|
|
20
|
+
Tooltip,
|
|
21
|
+
createFilterOptions,
|
|
22
|
+
} from '@mui/material';
|
|
23
|
+
import { styled } from '@mui/material/styles';
|
|
24
|
+
import {
|
|
25
|
+
ChoicesProps,
|
|
26
|
+
FieldTitle,
|
|
27
|
+
RaRecord,
|
|
28
|
+
useChoicesContext,
|
|
29
|
+
useInput,
|
|
30
|
+
useSuggestions,
|
|
31
|
+
UseSuggestionsOptions,
|
|
32
|
+
useTimeout,
|
|
33
|
+
useTranslate,
|
|
34
|
+
warning,
|
|
35
|
+
useGetRecordRepresentation,
|
|
36
|
+
useEvent,
|
|
37
|
+
sanitizeInputRestProps,
|
|
38
|
+
} from 'react-admin';
|
|
39
|
+
|
|
40
|
+
import {
|
|
41
|
+
CommonInputProps,
|
|
42
|
+
InputHelperText,
|
|
43
|
+
SupportCreateSuggestionOptions,
|
|
44
|
+
useSupportCreateSuggestion,
|
|
45
|
+
} from 'react-admin';
|
|
46
|
+
|
|
47
|
+
import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
|
|
48
|
+
import CheckBoxIcon from '@mui/icons-material/CheckBox';
|
|
49
|
+
//import { OptionType } from 'dayjs';
|
|
50
|
+
|
|
51
|
+
const icon = <CheckBoxOutlineBlankIcon fontSize='small' />;
|
|
52
|
+
const checkedIcon = <CheckBoxIcon fontSize='small' />;
|
|
53
|
+
const DefaultFilterToQuery = (searchText) => ({ q: searchText });
|
|
54
|
+
const PREFIX = 'RaAutocompleteInput';
|
|
55
|
+
|
|
56
|
+
const defaultFilterOptions = createFilterOptions();
|
|
57
|
+
|
|
58
|
+
export const AutocompleteInputClasses = {
|
|
59
|
+
textField: `${PREFIX}-textField`,
|
|
60
|
+
};
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
const StyledAutocomplete = styled(Autocomplete, {
|
|
64
|
+
name: PREFIX,
|
|
65
|
+
overridesResolver: (props, styles) => styles.root,
|
|
66
|
+
})(({ theme }) => ({
|
|
67
|
+
[`& .${AutocompleteInputClasses.textField}`]: {
|
|
68
|
+
minWidth: theme.spacing(20),
|
|
69
|
+
},
|
|
70
|
+
}));
|
|
71
|
+
|
|
72
|
+
// @ts-ignore Expected.
|
|
73
|
+
export interface AutocompleteInputProps<
|
|
74
|
+
OptionType = RaRecord,
|
|
75
|
+
Multiple extends boolean | undefined = false,
|
|
76
|
+
DisableClearable extends boolean | undefined = false,
|
|
77
|
+
SupportCreate extends boolean | undefined = false,
|
|
78
|
+
> extends Omit<CommonInputProps, 'source'>,
|
|
79
|
+
ChoicesProps,
|
|
80
|
+
UseSuggestionsOptions,
|
|
81
|
+
Omit<SupportCreateSuggestionOptions, 'handleChange' | 'optionText'>,
|
|
82
|
+
Omit<
|
|
83
|
+
AutocompleteProps<OptionType, Multiple, DisableClearable, SupportCreate>,
|
|
84
|
+
'onChange' | 'options' | 'renderInput'
|
|
85
|
+
> {
|
|
86
|
+
children?: ReactNode;
|
|
87
|
+
debounce?: number;
|
|
88
|
+
emptyText?: string;
|
|
89
|
+
emptyValue?: any;
|
|
90
|
+
filterToQuery?: (searchText: string) => any;
|
|
91
|
+
inputText?: (option: any) => string;
|
|
92
|
+
setFilter?: (value: string) => void;
|
|
93
|
+
shouldRenderSuggestions?: any;
|
|
94
|
+
// Source is optional as AutocompleteInput can be used inside a ReferenceInput that already defines the source
|
|
95
|
+
source?: string;
|
|
96
|
+
TextFieldProps?: TextFieldProps;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
const getSelectedItems = (
|
|
101
|
+
// eslint-disable-next-line @typescript-eslint/default-param-last
|
|
102
|
+
choices = [],
|
|
103
|
+
value,
|
|
104
|
+
// eslint-disable-next-line @typescript-eslint/default-param-last
|
|
105
|
+
optionValue = 'id',
|
|
106
|
+
multiple,
|
|
107
|
+
) => {
|
|
108
|
+
if (multiple) {
|
|
109
|
+
return (Array.isArray(value ?? []) ? value : [value])
|
|
110
|
+
.map((item) =>
|
|
111
|
+
choices.find(
|
|
112
|
+
(choice) => String(item) === String(get(choice, optionValue)),
|
|
113
|
+
),
|
|
114
|
+
)
|
|
115
|
+
.filter((item) => !!item);
|
|
116
|
+
}
|
|
117
|
+
return (
|
|
118
|
+
choices.find(
|
|
119
|
+
(choice) => String(get(choice, optionValue)) === String(value),
|
|
120
|
+
) || ''
|
|
121
|
+
);
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
const areSelectedItemsEqual = (
|
|
126
|
+
selectedChoice: RaRecord | RaRecord[],
|
|
127
|
+
newSelectedChoice: RaRecord | RaRecord[],
|
|
128
|
+
// eslint-disable-next-line @typescript-eslint/default-param-last
|
|
129
|
+
optionValue = 'id',
|
|
130
|
+
multiple: boolean,
|
|
131
|
+
) => {
|
|
132
|
+
if (multiple) {
|
|
133
|
+
const selectedChoiceArray = (selectedChoice as RaRecord[]) ?? [];
|
|
134
|
+
const newSelectedChoiceArray = (newSelectedChoice as RaRecord[]) ?? [];
|
|
135
|
+
if (selectedChoiceArray.length !== newSelectedChoiceArray.length) {
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
const equalityArray = selectedChoiceArray.map((choice) =>
|
|
139
|
+
newSelectedChoiceArray.some(
|
|
140
|
+
(newChoice) => get(newChoice, optionValue) === get(choice, optionValue),
|
|
141
|
+
),
|
|
142
|
+
);
|
|
143
|
+
return !equalityArray.some((item) => item === false);
|
|
144
|
+
}
|
|
145
|
+
return (
|
|
146
|
+
get(selectedChoice, optionValue) === get(newSelectedChoice, optionValue)
|
|
147
|
+
);
|
|
148
|
+
};
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* UseSelectedChoice.
|
|
152
|
+
*
|
|
153
|
+
* @returns The selected choice (or choices if multiple) by matching the input value with the choices.
|
|
154
|
+
*/
|
|
155
|
+
const useSelectedChoice = <
|
|
156
|
+
OptionType = RaRecord,
|
|
157
|
+
Multiple extends boolean | undefined = false,
|
|
158
|
+
DisableClearable extends boolean | undefined = false,
|
|
159
|
+
SupportCreate extends boolean | undefined = false,
|
|
160
|
+
>(
|
|
161
|
+
value: any,
|
|
162
|
+
{
|
|
163
|
+
choices,
|
|
164
|
+
multiple,
|
|
165
|
+
optionValue,
|
|
166
|
+
}: AutocompleteInputProps<
|
|
167
|
+
OptionType,
|
|
168
|
+
Multiple,
|
|
169
|
+
DisableClearable,
|
|
170
|
+
SupportCreate
|
|
171
|
+
>,
|
|
172
|
+
) => {
|
|
173
|
+
const selectedChoiceRef = useRef(
|
|
174
|
+
getSelectedItems(choices, value, optionValue, multiple),
|
|
175
|
+
);
|
|
176
|
+
const [selectedChoice, setSelectedChoice] = useState<RaRecord | RaRecord[]>(
|
|
177
|
+
() => getSelectedItems(choices, value, optionValue, multiple),
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
// As the selected choices are objects, we want to ensure we pass the same
|
|
181
|
+
// reference to the Autocomplete as it would reset its filter value otherwise.
|
|
182
|
+
useEffect(() => {
|
|
183
|
+
const newSelectedItems = getSelectedItems(
|
|
184
|
+
choices,
|
|
185
|
+
value,
|
|
186
|
+
optionValue,
|
|
187
|
+
multiple,
|
|
188
|
+
);
|
|
189
|
+
|
|
190
|
+
if (
|
|
191
|
+
!areSelectedItemsEqual(
|
|
192
|
+
selectedChoiceRef.current,
|
|
193
|
+
newSelectedItems,
|
|
194
|
+
optionValue,
|
|
195
|
+
multiple,
|
|
196
|
+
)
|
|
197
|
+
) {
|
|
198
|
+
selectedChoiceRef.current = newSelectedItems;
|
|
199
|
+
setSelectedChoice(newSelectedItems);
|
|
200
|
+
}
|
|
201
|
+
}, [choices, value, multiple, optionValue]);
|
|
202
|
+
return selectedChoice || null;
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* An Input component for an autocomplete field, using an array of objects for the options
|
|
208
|
+
*
|
|
209
|
+
* Pass possible options as an array of objects in the 'choices' attribute.
|
|
210
|
+
*
|
|
211
|
+
* By default, the options are built from:
|
|
212
|
+
* the 'id' property as the option value,
|
|
213
|
+
* the 'name' property as the option text
|
|
214
|
+
*
|
|
215
|
+
* @example
|
|
216
|
+
* const choices = [
|
|
217
|
+
* { id: 'M', name: 'Male' },
|
|
218
|
+
* { id: 'F', name: 'Female' },
|
|
219
|
+
* ];
|
|
220
|
+
* <AutocompleteInput source="gender" choices={choices} />
|
|
221
|
+
*
|
|
222
|
+
* You can also customize the properties to use for the option name and value,
|
|
223
|
+
* thanks to the 'optionText' and 'optionValue' attributes.
|
|
224
|
+
* @example
|
|
225
|
+
* const choices = [
|
|
226
|
+
* { _id: 123, full_name: 'Leo Tolstoi', sex: 'M' },
|
|
227
|
+
* { _id: 456, full_name: 'Jane Austen', sex: 'F' },
|
|
228
|
+
* ];
|
|
229
|
+
* <AutocompleteInput source="author_id" choices={choices} optionText="full_name" optionValue="_id" />
|
|
230
|
+
*
|
|
231
|
+
* `optionText` also accepts a function, so you can shape the option text at will:
|
|
232
|
+
* @example
|
|
233
|
+
* const choices = [
|
|
234
|
+
* { id: 123, first_name: 'Leo', last_name: 'Tolstoi' },
|
|
235
|
+
* { id: 456, first_name: 'Jane', last_name: 'Austen' },
|
|
236
|
+
* ];
|
|
237
|
+
* const optionRenderer = choice => `${choice.first_name} ${choice.last_name}`;
|
|
238
|
+
* <AutocompleteInput source="author_id" choices={choices} optionText={optionRenderer} />
|
|
239
|
+
*
|
|
240
|
+
* `optionText` also accepts a React Element, that can access
|
|
241
|
+
* the related choice through the `useRecordContext` hook. You can use Field components there.
|
|
242
|
+
* Note that you must also specify the `matchSuggestion` and `inputText` props
|
|
243
|
+
* @example
|
|
244
|
+
* const choices = [
|
|
245
|
+
* { id: 123, first_name: 'Leo', last_name: 'Tolstoi' },
|
|
246
|
+
* { id: 456, first_name: 'Jane', last_name: 'Austen' },
|
|
247
|
+
* ];
|
|
248
|
+
* const matchSuggestion = (filterValue, choice) => choice.first_name.match(filterValue) || choice.last_name.match(filterValue)
|
|
249
|
+
* const inputText = (record) => `${record.fullName} (${record.language})`;
|
|
250
|
+
*
|
|
251
|
+
* const FullNameField = () => {
|
|
252
|
+
* const record = useRecordContext();
|
|
253
|
+
* return <span>{record.first_name} {record.last_name}</span>;
|
|
254
|
+
* }
|
|
255
|
+
* <AutocompleteInput source="author" choices={choices} optionText={<FullNameField />} matchSuggestion={matchSuggestion} inputText={inputText} />
|
|
256
|
+
*
|
|
257
|
+
* The choices are translated by default, so you can use translation identifiers as choices:
|
|
258
|
+
* @example
|
|
259
|
+
* const choices = [
|
|
260
|
+
* { id: 'M', name: 'myroot.gender.male' },
|
|
261
|
+
* { id: 'F', name: 'myroot.gender.female' },
|
|
262
|
+
* ];
|
|
263
|
+
*
|
|
264
|
+
* However, in some cases (e.g. inside a `<ReferenceInput>`), you may not want
|
|
265
|
+
* the choice to be translated. In that case, set the `translateChoice` prop to false.
|
|
266
|
+
* @example
|
|
267
|
+
* <AutocompleteInput source="gender" choices={choices} translateChoice={false}/>
|
|
268
|
+
*
|
|
269
|
+
* The object passed as `options` props is passed to the Material UI <TextField> component
|
|
270
|
+
* @example
|
|
271
|
+
* <AutocompleteInput source="author_id" options={{ color: 'secondary', InputLabelProps: { shrink: true } }} />
|
|
272
|
+
*/
|
|
273
|
+
|
|
274
|
+
const AutocompleteCheckBoxInput = <
|
|
275
|
+
OptionType extends RaRecord = RaRecord,
|
|
276
|
+
Multiple extends boolean | undefined = false,
|
|
277
|
+
DisableClearable extends boolean | undefined = false,
|
|
278
|
+
SupportCreate extends boolean | undefined = false,
|
|
279
|
+
>(
|
|
280
|
+
props: AutocompleteInputProps<
|
|
281
|
+
OptionType,
|
|
282
|
+
Multiple,
|
|
283
|
+
DisableClearable,
|
|
284
|
+
SupportCreate
|
|
285
|
+
>,
|
|
286
|
+
) => {
|
|
287
|
+
const {
|
|
288
|
+
choices: choicesProp,
|
|
289
|
+
className,
|
|
290
|
+
blurOnSelect = false,
|
|
291
|
+
clearOnBlur = false,
|
|
292
|
+
selectOnFocus = true,
|
|
293
|
+
openOnFocus = true,
|
|
294
|
+
renderTags,
|
|
295
|
+
disableCloseOnSelect = true,
|
|
296
|
+
filterSelectedOptions = false,
|
|
297
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
|
|
298
|
+
renderOption = null,
|
|
299
|
+
clearText = 'ra.action.clear_input_value',
|
|
300
|
+
closeText = 'ra.action.close',
|
|
301
|
+
create,
|
|
302
|
+
createLabel,
|
|
303
|
+
createItemLabel,
|
|
304
|
+
createValue,
|
|
305
|
+
debounce: debounceDelay = 250,
|
|
306
|
+
defaultValue,
|
|
307
|
+
emptyText,
|
|
308
|
+
emptyValue = '',
|
|
309
|
+
field: fieldOverride,
|
|
310
|
+
format,
|
|
311
|
+
helperText,
|
|
312
|
+
id: idOverride,
|
|
313
|
+
inputText,
|
|
314
|
+
isFetching: isFetchingProp,
|
|
315
|
+
isLoading: isLoadingProp,
|
|
316
|
+
isRequired: isRequiredOverride,
|
|
317
|
+
label,
|
|
318
|
+
limitChoicesToValue,
|
|
319
|
+
matchSuggestion,
|
|
320
|
+
margin,
|
|
321
|
+
fieldState: fieldStateOverride,
|
|
322
|
+
filterToQuery: filterToQueryProp = DefaultFilterToQuery,
|
|
323
|
+
formState: formStateOverride,
|
|
324
|
+
multiple = false,
|
|
325
|
+
noOptionsText,
|
|
326
|
+
onBlur,
|
|
327
|
+
onChange,
|
|
328
|
+
onCreate,
|
|
329
|
+
openText = 'ra.action.open',
|
|
330
|
+
optionText,
|
|
331
|
+
optionValue,
|
|
332
|
+
parse,
|
|
333
|
+
resource: resourceProp,
|
|
334
|
+
shouldRenderSuggestions,
|
|
335
|
+
setFilter,
|
|
336
|
+
size,
|
|
337
|
+
source: sourceProp,
|
|
338
|
+
suggestionLimit = Infinity,
|
|
339
|
+
// eslint-disable-next-line @typescript-eslint/no-shadow
|
|
340
|
+
TextFieldProps,
|
|
341
|
+
translateChoice,
|
|
342
|
+
validate,
|
|
343
|
+
variant,
|
|
344
|
+
...rest
|
|
345
|
+
} = props;
|
|
346
|
+
|
|
347
|
+
const filterToQuery = useEvent(filterToQueryProp);
|
|
348
|
+
|
|
349
|
+
// const pendingStateRef = useRef<any[]>([]);
|
|
350
|
+
const lastOnChangeEvent = useRef<any>(null);
|
|
351
|
+
|
|
352
|
+
const {
|
|
353
|
+
allChoices,
|
|
354
|
+
isLoading,
|
|
355
|
+
error: fetchError,
|
|
356
|
+
resource,
|
|
357
|
+
source,
|
|
358
|
+
setFilters,
|
|
359
|
+
isFromReference,
|
|
360
|
+
} = useChoicesContext({
|
|
361
|
+
choices: choicesProp,
|
|
362
|
+
isFetching: isFetchingProp,
|
|
363
|
+
isLoading: isLoadingProp,
|
|
364
|
+
resource: resourceProp,
|
|
365
|
+
source: sourceProp,
|
|
366
|
+
});
|
|
367
|
+
|
|
368
|
+
const translate = useTranslate();
|
|
369
|
+
|
|
370
|
+
const {
|
|
371
|
+
id,
|
|
372
|
+
field,
|
|
373
|
+
isRequired,
|
|
374
|
+
fieldState: { error, invalid, isTouched },
|
|
375
|
+
formState: { isSubmitted },
|
|
376
|
+
} = useInput({
|
|
377
|
+
defaultValue,
|
|
378
|
+
id: idOverride,
|
|
379
|
+
field: fieldOverride,
|
|
380
|
+
fieldState: fieldStateOverride,
|
|
381
|
+
formState: formStateOverride,
|
|
382
|
+
isRequired: isRequiredOverride,
|
|
383
|
+
onBlur,
|
|
384
|
+
onChange,
|
|
385
|
+
parse,
|
|
386
|
+
format,
|
|
387
|
+
resource,
|
|
388
|
+
source,
|
|
389
|
+
validate,
|
|
390
|
+
...rest,
|
|
391
|
+
});
|
|
392
|
+
|
|
393
|
+
const finalChoices = useMemo(
|
|
394
|
+
() =>
|
|
395
|
+
// eslint-disable-next-line eqeqeq
|
|
396
|
+
emptyText == undefined || isRequired || multiple
|
|
397
|
+
? allChoices
|
|
398
|
+
: [
|
|
399
|
+
{
|
|
400
|
+
[optionValue || 'id']: emptyValue,
|
|
401
|
+
[typeof optionText === 'string' ? optionText : 'name']: translate(
|
|
402
|
+
emptyText,
|
|
403
|
+
{
|
|
404
|
+
_: emptyText,
|
|
405
|
+
},
|
|
406
|
+
),
|
|
407
|
+
}].concat(allChoices),
|
|
408
|
+
[
|
|
409
|
+
allChoices,
|
|
410
|
+
emptyValue,
|
|
411
|
+
emptyText,
|
|
412
|
+
isRequired,
|
|
413
|
+
multiple,
|
|
414
|
+
optionText,
|
|
415
|
+
optionValue,
|
|
416
|
+
translate,
|
|
417
|
+
],
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
const selectedChoice = useSelectedChoice<
|
|
421
|
+
OptionType,
|
|
422
|
+
Multiple,
|
|
423
|
+
DisableClearable,
|
|
424
|
+
SupportCreate
|
|
425
|
+
>(field.value, {
|
|
426
|
+
choices: finalChoices,
|
|
427
|
+
// @ts-ignore Expected.
|
|
428
|
+
multiple,
|
|
429
|
+
optionValue,
|
|
430
|
+
});
|
|
431
|
+
|
|
432
|
+
useEffect(() => {
|
|
433
|
+
// eslint-disable-next-line eqeqeq
|
|
434
|
+
if (emptyValue == null) {
|
|
435
|
+
throw new Error(
|
|
436
|
+
'emptyValue being set to null or undefined is not supported. Use parse to turn the empty string into null.',
|
|
437
|
+
);
|
|
438
|
+
}
|
|
439
|
+
}, [emptyValue]);
|
|
440
|
+
|
|
441
|
+
useEffect(() => {
|
|
442
|
+
// eslint-disable-next-line eqeqeq
|
|
443
|
+
if (isValidElement(optionText) && emptyText != undefined) {
|
|
444
|
+
throw new Error(
|
|
445
|
+
'optionText of type React element is not supported when setting emptyText',
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
// eslint-disable-next-line eqeqeq
|
|
449
|
+
if (isValidElement(optionText) && inputText == undefined) {
|
|
450
|
+
throw new Error(`
|
|
451
|
+
If you provided a React element for the optionText prop, you must also provide the inputText prop (used for the text input)`);
|
|
452
|
+
}
|
|
453
|
+
if (
|
|
454
|
+
isValidElement(optionText) &&
|
|
455
|
+
!isFromReference &&
|
|
456
|
+
// eslint-disable-next-line eqeqeq
|
|
457
|
+
matchSuggestion == undefined
|
|
458
|
+
) {
|
|
459
|
+
throw new Error(`
|
|
460
|
+
If you provided a React element for the optionText prop, you must also provide the matchSuggestion prop (used to match the user input with a choice)`);
|
|
461
|
+
}
|
|
462
|
+
}, [optionText, inputText, matchSuggestion, emptyText, isFromReference]);
|
|
463
|
+
|
|
464
|
+
useEffect(() => {
|
|
465
|
+
warning(
|
|
466
|
+
/* eslint-disable eqeqeq */
|
|
467
|
+
shouldRenderSuggestions != undefined && noOptionsText == undefined,
|
|
468
|
+
'When providing a shouldRenderSuggestions function, we recommend you also provide the noOptionsText prop and set it to a text explaining users why no options are displayed. It supports translation keys.',
|
|
469
|
+
);
|
|
470
|
+
/* eslint-enable eqeqeq */
|
|
471
|
+
}, [shouldRenderSuggestions, noOptionsText]);
|
|
472
|
+
|
|
473
|
+
const getRecordRepresentation = useGetRecordRepresentation(resource);
|
|
474
|
+
|
|
475
|
+
const { getChoiceText, getChoiceValue, getSuggestions } = useSuggestions({
|
|
476
|
+
choices: finalChoices,
|
|
477
|
+
limitChoicesToValue,
|
|
478
|
+
matchSuggestion,
|
|
479
|
+
optionText:
|
|
480
|
+
optionText ?? (isFromReference ? getRecordRepresentation : undefined),
|
|
481
|
+
optionValue,
|
|
482
|
+
selectedItem: selectedChoice,
|
|
483
|
+
suggestionLimit,
|
|
484
|
+
translateChoice: translateChoice ?? !isFromReference,
|
|
485
|
+
});
|
|
486
|
+
|
|
487
|
+
const [filterValue, setFilterValue] = useState('');
|
|
488
|
+
|
|
489
|
+
const [pendingState, setPendingState] = useState<any[]>(null);
|
|
490
|
+
|
|
491
|
+
const handleChange = (newValue: any) => {
|
|
492
|
+
let processedValue;
|
|
493
|
+
|
|
494
|
+
if (multiple) {
|
|
495
|
+
if (Array.isArray(newValue)) {
|
|
496
|
+
processedValue = newValue.map(getChoiceValue);
|
|
497
|
+
} else {
|
|
498
|
+
processedValue = [...(field.value ?? []), getChoiceValue(newValue)];
|
|
499
|
+
}
|
|
500
|
+
} else {
|
|
501
|
+
processedValue = getChoiceValue(newValue) ?? emptyValue;
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
if (lastOnChangeEvent.current[1] === 'clear') {
|
|
505
|
+
field.onChange(processedValue);
|
|
506
|
+
return;
|
|
507
|
+
}
|
|
508
|
+
|
|
509
|
+
if (lastOnChangeEvent.current[0].currentTarget.nodeName === 'svg') {
|
|
510
|
+
field.onChange(processedValue);
|
|
511
|
+
return;
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
if (lastOnChangeEvent.current[1] === 'removeOption') {
|
|
515
|
+
setPendingState((_) =>
|
|
516
|
+
processedValue.filter((x) => !new Set(pendingState).has(x)),
|
|
517
|
+
);
|
|
518
|
+
|
|
519
|
+
return;
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
setPendingState((prevState) =>
|
|
523
|
+
prevState
|
|
524
|
+
? Array.from(new Set([...prevState, ...processedValue]))
|
|
525
|
+
: Array.from(new Set([...processedValue])),
|
|
526
|
+
);
|
|
527
|
+
};
|
|
528
|
+
|
|
529
|
+
/* useEffect(()=>{
|
|
530
|
+
if(pendingState && pendingState.length === 0) {
|
|
531
|
+
field.onChange([]);
|
|
532
|
+
}
|
|
533
|
+
},[pendingState])*/
|
|
534
|
+
|
|
535
|
+
// eslint-disable-next-line
|
|
536
|
+
const debouncedSetFilter = useCallback(
|
|
537
|
+
debounce((filter) => {
|
|
538
|
+
if (setFilter) {
|
|
539
|
+
return setFilter(filter);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
if (choicesProp) {
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
setFilters(filterToQuery(filter), [], true);
|
|
547
|
+
}, debounceDelay),
|
|
548
|
+
[debounceDelay, setFilters, setFilter],
|
|
549
|
+
);
|
|
550
|
+
|
|
551
|
+
// We must reset the filter every time the value changes to ensure we
|
|
552
|
+
// display at least some choices even if the input has a value.
|
|
553
|
+
// Otherwise, it would only display the currently selected one and the user
|
|
554
|
+
// would have to first clear the input before seeing any other choices
|
|
555
|
+
const currentValue = useRef(field.value);
|
|
556
|
+
useEffect(() => {
|
|
557
|
+
if (!isEqual(currentValue.current, field.value)) {
|
|
558
|
+
currentValue.current = field.value;
|
|
559
|
+
debouncedSetFilter('');
|
|
560
|
+
}
|
|
561
|
+
}, [field.value]); // eslint-disable-line
|
|
562
|
+
|
|
563
|
+
const {
|
|
564
|
+
getCreateItem,
|
|
565
|
+
handleChange: handleChangeWithCreateSupport,
|
|
566
|
+
createElement,
|
|
567
|
+
createId,
|
|
568
|
+
} = useSupportCreateSuggestion({
|
|
569
|
+
create,
|
|
570
|
+
createLabel,
|
|
571
|
+
createItemLabel,
|
|
572
|
+
createValue,
|
|
573
|
+
handleChange,
|
|
574
|
+
filter: filterValue,
|
|
575
|
+
onCreate,
|
|
576
|
+
optionText,
|
|
577
|
+
});
|
|
578
|
+
|
|
579
|
+
const getOptionLabel = useCallback(
|
|
580
|
+
(option: any, isListItem = false) => {
|
|
581
|
+
// eslint-disable-next-line eqeqeq
|
|
582
|
+
if (option == undefined) {
|
|
583
|
+
return '';
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
// Value selected with enter, right from the input
|
|
587
|
+
if (typeof option === 'string') {
|
|
588
|
+
return option;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
if (option?.id === createId) {
|
|
592
|
+
return get(
|
|
593
|
+
option,
|
|
594
|
+
typeof optionText === 'string' ? optionText : 'name',
|
|
595
|
+
);
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
if (!isListItem && option[optionValue || 'id'] === emptyValue) {
|
|
599
|
+
return get(
|
|
600
|
+
option,
|
|
601
|
+
typeof optionText === 'string' ? optionText : 'name',
|
|
602
|
+
);
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
if (!isListItem && inputText !== undefined) {
|
|
606
|
+
return inputText(option);
|
|
607
|
+
}
|
|
608
|
+
|
|
609
|
+
return getChoiceText(option);
|
|
610
|
+
},
|
|
611
|
+
[getChoiceText, inputText, createId, optionText, optionValue, emptyValue],
|
|
612
|
+
);
|
|
613
|
+
|
|
614
|
+
/*const finalOnBlur = useCallback((): void => {
|
|
615
|
+
if (clearOnBlur && !multiple) {
|
|
616
|
+
const optionLabel = getOptionLabel(selectedChoice);
|
|
617
|
+
if (!isEqual(optionLabel, filterValue)) {
|
|
618
|
+
setFilterValue(optionLabel);
|
|
619
|
+
debouncedSetFilter('');
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
|
|
623
|
+
field.onBlur();
|
|
624
|
+
}, [
|
|
625
|
+
clearOnBlur,
|
|
626
|
+
field,
|
|
627
|
+
getOptionLabel,
|
|
628
|
+
selectedChoice,
|
|
629
|
+
filterValue,
|
|
630
|
+
debouncedSetFilter,
|
|
631
|
+
multiple,
|
|
632
|
+
//pendingStateRef
|
|
633
|
+
]);*/
|
|
634
|
+
|
|
635
|
+
useEffect(() => {
|
|
636
|
+
if (!multiple) {
|
|
637
|
+
const optionLabel = getOptionLabel(selectedChoice);
|
|
638
|
+
if (typeof optionLabel === 'string') {
|
|
639
|
+
setFilterValue(optionLabel);
|
|
640
|
+
} else {
|
|
641
|
+
throw new Error(
|
|
642
|
+
'When optionText returns a React element, you must also provide the inputText prop',
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
}, [getOptionLabel, multiple, selectedChoice]);
|
|
647
|
+
|
|
648
|
+
|
|
649
|
+
const doesQueryMatchSelection = useCallback(
|
|
650
|
+
(filter: string) => {
|
|
651
|
+
let selectedItemTexts;
|
|
652
|
+
|
|
653
|
+
if (multiple) {
|
|
654
|
+
selectedItemTexts = selectedChoice.map((item) => getOptionLabel(item));
|
|
655
|
+
} else {
|
|
656
|
+
selectedItemTexts = [getOptionLabel(selectedChoice)];
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
return selectedItemTexts.includes(filter);
|
|
660
|
+
},
|
|
661
|
+
[getOptionLabel, multiple, selectedChoice],
|
|
662
|
+
);
|
|
663
|
+
const doesQueryMatchSuggestion = useCallback(
|
|
664
|
+
(filter) => {
|
|
665
|
+
const hasOption = finalChoices
|
|
666
|
+
? finalChoices.some((choice) => getOptionLabel(choice) === filter)
|
|
667
|
+
: false;
|
|
668
|
+
|
|
669
|
+
return doesQueryMatchSelection(filter) || hasOption;
|
|
670
|
+
},
|
|
671
|
+
[finalChoices, getOptionLabel, doesQueryMatchSelection],
|
|
672
|
+
);
|
|
673
|
+
|
|
674
|
+
const handleInputChange = (
|
|
675
|
+
event: any,
|
|
676
|
+
newInputValue: string,
|
|
677
|
+
_reason: string,
|
|
678
|
+
) => {
|
|
679
|
+
if (event?.type === 'change' || !doesQueryMatchSelection(newInputValue)) {
|
|
680
|
+
setFilterValue(newInputValue);
|
|
681
|
+
debouncedSetFilter(newInputValue);
|
|
682
|
+
}
|
|
683
|
+
};
|
|
684
|
+
|
|
685
|
+
const filterOptions = (options, params) => {
|
|
686
|
+
let filteredOptions =
|
|
687
|
+
isFromReference || // When used inside a reference, AutocompleteInput shouldn't do the filtering as it's done by the reference input
|
|
688
|
+
matchSuggestion || // When using element as optionText (and matchSuggestion), options are filtered by getSuggestions, so they shouldn't be filtered here
|
|
689
|
+
limitChoicesToValue // When limiting choices to values (why? it's legacy!), options are also filtered by getSuggestions, so they shouldn't be filtered here
|
|
690
|
+
? options
|
|
691
|
+
: defaultFilterOptions(options, params); // Otherwise, we let Material UI's Autocomplete do the filtering
|
|
692
|
+
|
|
693
|
+
// add create option if necessary
|
|
694
|
+
const { inputValue } = params;
|
|
695
|
+
if (
|
|
696
|
+
(onCreate || create) &&
|
|
697
|
+
inputValue !== '' &&
|
|
698
|
+
!doesQueryMatchSuggestion(filterValue)
|
|
699
|
+
) {
|
|
700
|
+
filteredOptions = filteredOptions.concat(getCreateItem(inputValue));
|
|
701
|
+
}
|
|
702
|
+
|
|
703
|
+
return filteredOptions;
|
|
704
|
+
};
|
|
705
|
+
|
|
706
|
+
const handleAutocompleteChange = (
|
|
707
|
+
event: any,
|
|
708
|
+
newValue: any,
|
|
709
|
+
_reason: string,
|
|
710
|
+
) => {
|
|
711
|
+
lastOnChangeEvent.current = [event, _reason];
|
|
712
|
+
handleChangeWithCreateSupport(newValue !== null ? newValue : emptyValue);
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
const oneSecondHasPassed = useTimeout(1000, filterValue);
|
|
716
|
+
|
|
717
|
+
const suggestions = useMemo(() => {
|
|
718
|
+
if (!isFromReference && (matchSuggestion || limitChoicesToValue)) {
|
|
719
|
+
return getSuggestions(filterValue);
|
|
720
|
+
}
|
|
721
|
+
return finalChoices?.slice(0, suggestionLimit) || [];
|
|
722
|
+
}, [
|
|
723
|
+
finalChoices,
|
|
724
|
+
filterValue,
|
|
725
|
+
getSuggestions,
|
|
726
|
+
limitChoicesToValue,
|
|
727
|
+
matchSuggestion,
|
|
728
|
+
suggestionLimit,
|
|
729
|
+
isFromReference,
|
|
730
|
+
]);
|
|
731
|
+
|
|
732
|
+
const isOptionEqualToValue = (option, value) => {
|
|
733
|
+
return String(getChoiceValue(option)) === String(getChoiceValue(value));
|
|
734
|
+
};
|
|
735
|
+
const renderHelperText =
|
|
736
|
+
!!fetchError ||
|
|
737
|
+
helperText !== false ||
|
|
738
|
+
((isTouched || isSubmitted) && invalid);
|
|
739
|
+
|
|
740
|
+
return (
|
|
741
|
+
<>
|
|
742
|
+
<StyledAutocomplete
|
|
743
|
+
className={clsx('ra-input', `ra-input-${source}`, className)}
|
|
744
|
+
clearText={translate(clearText, { _: clearText })}
|
|
745
|
+
closeText={translate(closeText, { _: closeText })}
|
|
746
|
+
openText={translate(openText, { _: openText })}
|
|
747
|
+
id={id}
|
|
748
|
+
isOptionEqualToValue={isOptionEqualToValue}
|
|
749
|
+
//disableClearable={false}
|
|
750
|
+
blurOnSelect={blurOnSelect}
|
|
751
|
+
clearOnBlur={clearOnBlur}
|
|
752
|
+
selectOnFocus={selectOnFocus}
|
|
753
|
+
openOnFocus={openOnFocus}
|
|
754
|
+
disableCloseOnSelect={disableCloseOnSelect}
|
|
755
|
+
filterSelectedOptions={filterSelectedOptions}
|
|
756
|
+
|
|
757
|
+
renderInput={(params) => (
|
|
758
|
+
<TextField
|
|
759
|
+
name={field.name}
|
|
760
|
+
label={
|
|
761
|
+
<FieldTitle
|
|
762
|
+
label={label}
|
|
763
|
+
source={source}
|
|
764
|
+
resource={resourceProp}
|
|
765
|
+
isRequired={isRequired}
|
|
766
|
+
/>
|
|
767
|
+
}
|
|
768
|
+
error={!!fetchError || ((isTouched || isSubmitted) && invalid)}
|
|
769
|
+
helperText={
|
|
770
|
+
renderHelperText ? (
|
|
771
|
+
<InputHelperText
|
|
772
|
+
//touched={isTouched || isSubmitted || fetchError}
|
|
773
|
+
error={error?.message || fetchError?.message}
|
|
774
|
+
helperText={helperText}
|
|
775
|
+
/>
|
|
776
|
+
) : null
|
|
777
|
+
}
|
|
778
|
+
margin={margin}
|
|
779
|
+
variant={variant}
|
|
780
|
+
className={AutocompleteInputClasses.textField}
|
|
781
|
+
{...TextFieldProps}
|
|
782
|
+
{...params}
|
|
783
|
+
size={size}
|
|
784
|
+
/>
|
|
785
|
+
)}
|
|
786
|
+
multiple={multiple}
|
|
787
|
+
renderTags={
|
|
788
|
+
renderTags
|
|
789
|
+
? renderTags
|
|
790
|
+
// eslint-disable-next-line @typescript-eslint/no-unused-vars, no-unused-vars
|
|
791
|
+
: (value, getTagProps) => {
|
|
792
|
+
/*return value.map((option, index) => (
|
|
793
|
+
<Chip {...getTagProps({ index })} label={option.name} />
|
|
794
|
+
));*/
|
|
795
|
+
const attribute = optionText as string;
|
|
796
|
+
|
|
797
|
+
return (
|
|
798
|
+
<Tooltip
|
|
799
|
+
title={value.map((item) => item[attribute]).join(' , ')}
|
|
800
|
+
>
|
|
801
|
+
<Chip
|
|
802
|
+
label={
|
|
803
|
+
value[0][attribute] +
|
|
804
|
+
(value.length > 1 ? ' +' + (value.length - 1) : '')
|
|
805
|
+
}
|
|
806
|
+
sx={{
|
|
807
|
+
'.MuiSvgIcon-root': {
|
|
808
|
+
// FIXME: Workaround to allow choices deletion
|
|
809
|
+
// Maybe related to storybook and mui using different versions of emotion
|
|
810
|
+
zIndex: 100,
|
|
811
|
+
},
|
|
812
|
+
}}
|
|
813
|
+
variant='filled'
|
|
814
|
+
size='small'
|
|
815
|
+
/>
|
|
816
|
+
</Tooltip>
|
|
817
|
+
);
|
|
818
|
+
}}
|
|
819
|
+
|
|
820
|
+
/*value.map((option, index) => (
|
|
821
|
+
<Chip
|
|
822
|
+
label={
|
|
823
|
+
isValidElement(optionText)
|
|
824
|
+
? inputText(option)
|
|
825
|
+
: getChoiceText(option)
|
|
826
|
+
}
|
|
827
|
+
sx={{
|
|
828
|
+
'.MuiSvgIcon-root': {
|
|
829
|
+
// FIXME: Workaround to allow choices deletion
|
|
830
|
+
// Maybe related to storybook and mui using different versions of emotion
|
|
831
|
+
zIndex: 100,
|
|
832
|
+
},
|
|
833
|
+
}}
|
|
834
|
+
variant="outlined"
|
|
835
|
+
size="small"
|
|
836
|
+
{...getTagProps({ index })}
|
|
837
|
+
|
|
838
|
+
/>
|
|
839
|
+
|
|
840
|
+
))*/
|
|
841
|
+
noOptionsText={
|
|
842
|
+
typeof noOptionsText === 'string'
|
|
843
|
+
? translate(noOptionsText, { _: noOptionsText })
|
|
844
|
+
: noOptionsText
|
|
845
|
+
}
|
|
846
|
+
{...sanitizeInputRestProps(rest)}
|
|
847
|
+
freeSolo={!!create || !!onCreate}
|
|
848
|
+
handleHomeEndKeys={!!create || !!onCreate}
|
|
849
|
+
filterOptions={filterOptions}
|
|
850
|
+
options={
|
|
851
|
+
shouldRenderSuggestions == undefined || // eslint-disable-line eqeqeq
|
|
852
|
+
shouldRenderSuggestions(filterValue)
|
|
853
|
+
? suggestions
|
|
854
|
+
: []
|
|
855
|
+
}
|
|
856
|
+
getOptionLabel={getOptionLabel}
|
|
857
|
+
inputValue={filterValue}
|
|
858
|
+
loading={
|
|
859
|
+
isLoading &&
|
|
860
|
+
(!finalChoices || finalChoices.length === 0) &&
|
|
861
|
+
oneSecondHasPassed
|
|
862
|
+
}
|
|
863
|
+
value={selectedChoice}
|
|
864
|
+
onChange={(e, val, reason) => {
|
|
865
|
+
handleAutocompleteChange(e, val, reason);
|
|
866
|
+
}}
|
|
867
|
+
//onBlur={finalOnBlur}
|
|
868
|
+
onInputChange={handleInputChange}
|
|
869
|
+
clearOnEscape={false}
|
|
870
|
+
/*renderOption={renderOption? renderOption: (props, record: RaRecord) => {
|
|
871
|
+
(props as {
|
|
872
|
+
key: string;
|
|
873
|
+
}).key = getChoiceValue(record);
|
|
874
|
+
|
|
875
|
+
const optionLabel = getOptionLabel(record, true);
|
|
876
|
+
|
|
877
|
+
return (
|
|
878
|
+
<li {...props}>
|
|
879
|
+
{optionLabel === '' ? '' : optionLabel}
|
|
880
|
+
</li>
|
|
881
|
+
);
|
|
882
|
+
}}*/
|
|
883
|
+
|
|
884
|
+
renderOption={(p, option: RaRecord, { selected }) => {
|
|
885
|
+
const sel: boolean =
|
|
886
|
+
selectedChoice && selectedChoice.find((ele) => ele.id === option.id)
|
|
887
|
+
? true
|
|
888
|
+
: selected;
|
|
889
|
+
|
|
890
|
+
return (
|
|
891
|
+
<li {...p} /*onClick={(e)=> e.stopPropagation()}*/>
|
|
892
|
+
<Checkbox
|
|
893
|
+
icon={icon}
|
|
894
|
+
checkedIcon={checkedIcon}
|
|
895
|
+
style={{ marginRight: 8 }}
|
|
896
|
+
//checked={sel}
|
|
897
|
+
defaultChecked={sel}
|
|
898
|
+
/>
|
|
899
|
+
{option[optionText as string]}
|
|
900
|
+
</li>
|
|
901
|
+
);
|
|
902
|
+
}}
|
|
903
|
+
onClose={() => {
|
|
904
|
+
// BUG, cannot delete the last element!
|
|
905
|
+
if (pendingState) {
|
|
906
|
+
field.onChange(pendingState);
|
|
907
|
+
//setPendingState([]);
|
|
908
|
+
}
|
|
909
|
+
}}
|
|
910
|
+
/>
|
|
911
|
+
{createElement}
|
|
912
|
+
</>
|
|
913
|
+
);
|
|
914
|
+
};
|
|
915
|
+
|
|
916
|
+
export default AutocompleteCheckBoxInput;
|