@archbase/components 4.0.1 → 4.0.2
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/dist/editors/ArchbaseMultiSelect.d.ts +110 -0
- package/dist/editors/ArchbaseTagInputEdit.d.ts +91 -0
- package/dist/editors/index.d.ts +4 -0
- package/dist/index.js +9957 -9487
- package/package.json +5 -5
- package/src/datagrid/main/archbase-data-grid.tsx +40 -33
- package/src/editors/ArchbaseDatePickerEdit.tsx +19 -11
- package/src/editors/ArchbaseMultiSelect.tsx +642 -0
- package/src/editors/ArchbaseTagInputEdit.tsx +395 -0
- package/src/editors/index.tsx +6 -0
|
@@ -0,0 +1,642 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CheckIcon,
|
|
3
|
+
CloseButton,
|
|
4
|
+
Combobox,
|
|
5
|
+
ComboboxDropdown,
|
|
6
|
+
ComboboxTarget,
|
|
7
|
+
FloatingPosition,
|
|
8
|
+
Group,
|
|
9
|
+
Image,
|
|
10
|
+
MantineSize,
|
|
11
|
+
MantineStyleProp,
|
|
12
|
+
OptionsFilter,
|
|
13
|
+
Pill,
|
|
14
|
+
PillsInput,
|
|
15
|
+
ScrollArea,
|
|
16
|
+
Text,
|
|
17
|
+
useCombobox,
|
|
18
|
+
} from '@mantine/core';
|
|
19
|
+
import { uniqueId } from 'lodash';
|
|
20
|
+
import React, {
|
|
21
|
+
CSSProperties,
|
|
22
|
+
FocusEventHandler,
|
|
23
|
+
forwardRef,
|
|
24
|
+
ReactNode,
|
|
25
|
+
useCallback,
|
|
26
|
+
useEffect,
|
|
27
|
+
useMemo,
|
|
28
|
+
useRef,
|
|
29
|
+
useState
|
|
30
|
+
} from 'react';
|
|
31
|
+
import { ArchbaseDataSource, DataSourceEvent, DataSourceEventNames } from '@archbase/data';
|
|
32
|
+
import { useArchbaseDidMount, useArchbaseDidUpdate, useArchbaseWillUnmount } from '@archbase/core';
|
|
33
|
+
|
|
34
|
+
export interface ArchbaseMultiSelectProps<T, ID, O> {
|
|
35
|
+
/** Permite ou não desselecionar um item */
|
|
36
|
+
allowDeselect?: boolean;
|
|
37
|
+
/** Indicador se permite limpar o select */
|
|
38
|
+
clearable?: boolean;
|
|
39
|
+
/** Fonte de dados onde será atribuido o item selecionado */
|
|
40
|
+
dataSource?: ArchbaseDataSource<T, ID>;
|
|
41
|
+
/** Campo onde deverá ser atribuido o item selecionado na fonte de dados */
|
|
42
|
+
dataField?: string;
|
|
43
|
+
/** Indicador se o select está desabilitado */
|
|
44
|
+
disabled?: boolean;
|
|
45
|
+
/** Indicador se o select é somente leitura. Obs: usado em conjunto com o status da fonte de dados */
|
|
46
|
+
readOnly?: boolean;
|
|
47
|
+
/** Texto explicativo do select */
|
|
48
|
+
placeholder?: string;
|
|
49
|
+
/** Título do select */
|
|
50
|
+
label?: string;
|
|
51
|
+
/** Descrição do select */
|
|
52
|
+
description?: string;
|
|
53
|
+
/** Último erro ocorrido no select */
|
|
54
|
+
error?: string;
|
|
55
|
+
/** Permite pesquisar no select */
|
|
56
|
+
searchable?: boolean;
|
|
57
|
+
/** Icon a esquerda do select */
|
|
58
|
+
icon?: ReactNode;
|
|
59
|
+
/** Largura do icone a esquerda do select */
|
|
60
|
+
iconWidth?: MantineSize;
|
|
61
|
+
/** Valor de entrada controlado */
|
|
62
|
+
value?: any[];
|
|
63
|
+
/** Valor padrão de entrada não controlado */
|
|
64
|
+
defaultValue?: any[];
|
|
65
|
+
/** Função com base em quais itens no menu suspenso são filtrados */
|
|
66
|
+
filter?: OptionsFilter;
|
|
67
|
+
/** Estilo do select */
|
|
68
|
+
style?: MantineStyleProp;
|
|
69
|
+
/** Tamanho do campo */
|
|
70
|
+
size?: MantineSize;
|
|
71
|
+
/** Largura do select */
|
|
72
|
+
width?: number | string | undefined;
|
|
73
|
+
/** Estado aberto do menu suspenso inicial */
|
|
74
|
+
initiallyOpened?: boolean;
|
|
75
|
+
/** Alterar renderizador de item */
|
|
76
|
+
itemComponent?: React.FC<any>;
|
|
77
|
+
/** Alterar renderizador de item selecionado */
|
|
78
|
+
selectedItemComponent?: React.FC<any>;
|
|
79
|
+
/** Chamado quando o menu suspenso é aberto */
|
|
80
|
+
onDropdownOpen?(): void;
|
|
81
|
+
/** Chamado quando o menu suspenso é aberto */
|
|
82
|
+
onDropdownClose?(): void;
|
|
83
|
+
/** Limite a quantidade de itens exibidos por vez para seleção pesquisável */
|
|
84
|
+
limit?: number;
|
|
85
|
+
/** Rótulo nada encontrado */
|
|
86
|
+
nothingFound?: React.ReactNode;
|
|
87
|
+
/** Índice z dropdown */
|
|
88
|
+
zIndex?: React.CSSProperties['zIndex'];
|
|
89
|
+
/** Comportamento de posicionamento dropdown */
|
|
90
|
+
dropdownPosition?: FloatingPosition;
|
|
91
|
+
/** Evento quando os valores selecionados são alterados */
|
|
92
|
+
onChangeValues?: (values: O[]) => void;
|
|
93
|
+
/** Evento quando o foco sai do select */
|
|
94
|
+
onFocusExit?: FocusEventHandler<T> | undefined;
|
|
95
|
+
/** Evento quando o select recebe o foco */
|
|
96
|
+
onFocusEnter?: FocusEventHandler<T> | undefined;
|
|
97
|
+
/** Opções de seleção iniciais */
|
|
98
|
+
initialOptions?: O[];
|
|
99
|
+
/** Function que retorna o label de uma opção */
|
|
100
|
+
getOptionLabel: (option: O) => string;
|
|
101
|
+
/** Function que retorna o valor de uma opção */
|
|
102
|
+
getOptionValue: (option: O) => any;
|
|
103
|
+
/** Function que retorna a imagem de uma opção */
|
|
104
|
+
getOptionImage?: (option: O) => any | undefined | null;
|
|
105
|
+
/** Indica se o select tem o preenchimento obrigatório */
|
|
106
|
+
required?: boolean;
|
|
107
|
+
/** Chamado sempre que o valor da pesquisa muda */
|
|
108
|
+
onSearchChange?(query: string): void;
|
|
109
|
+
/** Converte o valor antes de atribuir ao field do registro atual no datasource */
|
|
110
|
+
converter?: (value: O) => any;
|
|
111
|
+
/** Function que busca o valor original antes de converter pelo valor de retorno do converter */
|
|
112
|
+
getConvertedOption?: (value: any) => Promise<O>;
|
|
113
|
+
/** Coleção de opções do select */
|
|
114
|
+
options?: ReadonlyArray<any> | ArchbaseDataSource<any, any>;
|
|
115
|
+
/** Campo do label quando options é um ArchbaseDataSource */
|
|
116
|
+
optionsLabelField?: string;
|
|
117
|
+
/** Coleção de ReactNode que representam as opções do select */
|
|
118
|
+
children?: ReactNode | ReactNode[];
|
|
119
|
+
/** Máxima altura do dropdown */
|
|
120
|
+
maxDropdownHeight?: number;
|
|
121
|
+
/** Função de ordenação customizada para os valores selecionados */
|
|
122
|
+
sortSelectedValues?: (a: O, b: O) => number;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function buildOptions<O>(
|
|
126
|
+
options?: ReadonlyArray<any> | ArchbaseDataSource<any, any>,
|
|
127
|
+
initialOptions?: O[],
|
|
128
|
+
children?: ReactNode | ReactNode[] | undefined,
|
|
129
|
+
getOptionLabel?: (option: O) => string,
|
|
130
|
+
getOptionValue?: (option: O) => any,
|
|
131
|
+
getOptionImage?: (option: O) => any | undefined | null,
|
|
132
|
+
optionsLabelField?: string
|
|
133
|
+
): any {
|
|
134
|
+
if (!initialOptions && !children && !options) {
|
|
135
|
+
return [];
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
// Se options é um ArchbaseDataSource
|
|
139
|
+
if (
|
|
140
|
+
options &&
|
|
141
|
+
options instanceof ArchbaseDataSource &&
|
|
142
|
+
getOptionLabel &&
|
|
143
|
+
getOptionValue &&
|
|
144
|
+
optionsLabelField
|
|
145
|
+
) {
|
|
146
|
+
const ds = options as ArchbaseDataSource<any, any>;
|
|
147
|
+
ds.first();
|
|
148
|
+
const result: any[] = [];
|
|
149
|
+
while (!ds.isEOF()) {
|
|
150
|
+
const record = ds.getCurrentRecord();
|
|
151
|
+
result.push({
|
|
152
|
+
label: ds.getFieldValue(optionsLabelField),
|
|
153
|
+
value: getOptionValue(record),
|
|
154
|
+
image: getOptionImage ? getOptionImage(record) : undefined,
|
|
155
|
+
origin: record,
|
|
156
|
+
key: uniqueId('select'),
|
|
157
|
+
});
|
|
158
|
+
ds.next();
|
|
159
|
+
}
|
|
160
|
+
ds.first();
|
|
161
|
+
return result;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
// Se options é um array
|
|
165
|
+
if (options && Array.isArray(options)) {
|
|
166
|
+
return options;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
// Se children foi passado
|
|
170
|
+
if (children) {
|
|
171
|
+
return React.Children.toArray(children).map((item: any) => {
|
|
172
|
+
const { label, value, origin, ...others } = item.props;
|
|
173
|
+
return {
|
|
174
|
+
label: label,
|
|
175
|
+
value: value,
|
|
176
|
+
origin: origin !== undefined ? origin : value,
|
|
177
|
+
key: uniqueId('select'),
|
|
178
|
+
...others
|
|
179
|
+
};
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
// Se initialOptions foi passado
|
|
184
|
+
if (getOptionImage) {
|
|
185
|
+
return initialOptions!.map((item: O) => {
|
|
186
|
+
return {
|
|
187
|
+
label: getOptionLabel!(item),
|
|
188
|
+
value: getOptionValue!(item),
|
|
189
|
+
image: getOptionImage(item),
|
|
190
|
+
origin: item,
|
|
191
|
+
key: uniqueId('select'),
|
|
192
|
+
};
|
|
193
|
+
});
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return initialOptions!.map((item: O) => {
|
|
197
|
+
return {
|
|
198
|
+
label: getOptionLabel!(item),
|
|
199
|
+
value: getOptionValue!(item),
|
|
200
|
+
origin: item,
|
|
201
|
+
key: uniqueId('select'),
|
|
202
|
+
};
|
|
203
|
+
});
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const areArraysEqual = (arr1: any[], arr2: any[]) => {
|
|
207
|
+
if (arr1.length !== arr2.length) return false;
|
|
208
|
+
return arr1.every((item, index) => item === arr2[index]);
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
export const SelectItem = ({ image, label, description, values, ...others }) => (
|
|
212
|
+
<div {...others}>
|
|
213
|
+
<Group style={{ flexWrap: "nowrap" }}>
|
|
214
|
+
{image && <Image w={50} src={image} />}
|
|
215
|
+
<div>
|
|
216
|
+
<Text size="sm">{label}</Text>
|
|
217
|
+
{description && (
|
|
218
|
+
<Text size="xs" opacity={0.65}>
|
|
219
|
+
{description}
|
|
220
|
+
</Text>
|
|
221
|
+
)}
|
|
222
|
+
</div>
|
|
223
|
+
{values && values.includes(others.value) ? <CheckIcon size={12} /> : null}
|
|
224
|
+
</Group>
|
|
225
|
+
</div>
|
|
226
|
+
);
|
|
227
|
+
|
|
228
|
+
export const SelectedItem = ({ item, value, onRemove, label }) => (
|
|
229
|
+
<Pill
|
|
230
|
+
key={value}
|
|
231
|
+
withRemoveButton
|
|
232
|
+
onRemove={() => onRemove()}
|
|
233
|
+
>
|
|
234
|
+
{label}
|
|
235
|
+
</Pill>
|
|
236
|
+
);
|
|
237
|
+
|
|
238
|
+
export function ArchbaseMultiSelect<T, ID, O>({
|
|
239
|
+
allowDeselect = true,
|
|
240
|
+
clearable = true,
|
|
241
|
+
dataSource,
|
|
242
|
+
dataField,
|
|
243
|
+
disabled = false,
|
|
244
|
+
readOnly = false,
|
|
245
|
+
placeholder,
|
|
246
|
+
initialOptions = [],
|
|
247
|
+
searchable = true,
|
|
248
|
+
label,
|
|
249
|
+
description,
|
|
250
|
+
error,
|
|
251
|
+
icon,
|
|
252
|
+
iconWidth,
|
|
253
|
+
required,
|
|
254
|
+
getOptionLabel,
|
|
255
|
+
getOptionValue,
|
|
256
|
+
getOptionImage,
|
|
257
|
+
onFocusEnter,
|
|
258
|
+
onFocusExit,
|
|
259
|
+
onChangeValues,
|
|
260
|
+
value,
|
|
261
|
+
defaultValue,
|
|
262
|
+
filter,
|
|
263
|
+
size,
|
|
264
|
+
style,
|
|
265
|
+
width,
|
|
266
|
+
initiallyOpened,
|
|
267
|
+
itemComponent: ItemComponent = SelectItem,
|
|
268
|
+
selectedItemComponent: SelectedItemComponent = SelectedItem,
|
|
269
|
+
onDropdownOpen,
|
|
270
|
+
onDropdownClose,
|
|
271
|
+
limit,
|
|
272
|
+
nothingFound,
|
|
273
|
+
zIndex,
|
|
274
|
+
dropdownPosition,
|
|
275
|
+
onSearchChange,
|
|
276
|
+
converter,
|
|
277
|
+
getConvertedOption,
|
|
278
|
+
options,
|
|
279
|
+
optionsLabelField,
|
|
280
|
+
children,
|
|
281
|
+
maxDropdownHeight = 280,
|
|
282
|
+
sortSelectedValues
|
|
283
|
+
}: ArchbaseMultiSelectProps<T, ID, O>) {
|
|
284
|
+
const combobox = useCombobox({
|
|
285
|
+
onDropdownClose: () => {
|
|
286
|
+
combobox.resetSelectedOption();
|
|
287
|
+
if (onDropdownClose) {
|
|
288
|
+
onDropdownClose();
|
|
289
|
+
}
|
|
290
|
+
},
|
|
291
|
+
onDropdownOpen: () => {
|
|
292
|
+
if (onDropdownOpen) {
|
|
293
|
+
onDropdownOpen();
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
});
|
|
297
|
+
|
|
298
|
+
const [selectedValues, setSelectedValues] = useState<O[]>(defaultValue || []);
|
|
299
|
+
const [updateCounter, setUpdateCounter] = useState(0);
|
|
300
|
+
const [queryValue, setQueryValue] = useState<string>('');
|
|
301
|
+
const [internalError, setInternalError] = useState<string | undefined>(error);
|
|
302
|
+
|
|
303
|
+
const currentOptions: any[] = useMemo(() => {
|
|
304
|
+
return buildOptions<O>(
|
|
305
|
+
options,
|
|
306
|
+
initialOptions,
|
|
307
|
+
children,
|
|
308
|
+
getOptionLabel,
|
|
309
|
+
getOptionValue,
|
|
310
|
+
getOptionImage,
|
|
311
|
+
optionsLabelField
|
|
312
|
+
);
|
|
313
|
+
}, [
|
|
314
|
+
updateCounter,
|
|
315
|
+
options,
|
|
316
|
+
initialOptions,
|
|
317
|
+
children,
|
|
318
|
+
getOptionLabel,
|
|
319
|
+
getOptionValue,
|
|
320
|
+
getOptionImage,
|
|
321
|
+
optionsLabelField
|
|
322
|
+
]);
|
|
323
|
+
|
|
324
|
+
const loadDataSourceFieldValue = async () => {
|
|
325
|
+
let initialValue: any = value;
|
|
326
|
+
|
|
327
|
+
if (dataSource && dataField) {
|
|
328
|
+
initialValue = dataSource.getFieldValue(dataField);
|
|
329
|
+
if (!initialValue) {
|
|
330
|
+
initialValue = [];
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
if (getConvertedOption && converter && initialValue && initialValue.length > 0) {
|
|
334
|
+
// Converte cada item do array
|
|
335
|
+
const convertedPromises = initialValue.map(item => getConvertedOption(item));
|
|
336
|
+
initialValue = await Promise.all(convertedPromises);
|
|
337
|
+
}
|
|
338
|
+
setSelectedValues(initialValue ?? []);
|
|
339
|
+
};
|
|
340
|
+
|
|
341
|
+
const fieldChangedListener = useCallback(() => {
|
|
342
|
+
loadDataSourceFieldValue();
|
|
343
|
+
}, []);
|
|
344
|
+
|
|
345
|
+
const dataSourceEvent = useCallback((event: DataSourceEvent<T>) => {
|
|
346
|
+
if (dataSource && dataField) {
|
|
347
|
+
if (
|
|
348
|
+
event.type === DataSourceEventNames.dataChanged ||
|
|
349
|
+
event.type === DataSourceEventNames.recordChanged ||
|
|
350
|
+
event.type === DataSourceEventNames.afterScroll ||
|
|
351
|
+
event.type === DataSourceEventNames.afterCancel
|
|
352
|
+
) {
|
|
353
|
+
loadDataSourceFieldValue();
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
if (event.type === DataSourceEventNames.onFieldError && event.fieldName === dataField) {
|
|
357
|
+
setInternalError(event.error);
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}, []);
|
|
361
|
+
|
|
362
|
+
const dataSourceOptionsEvent = useCallback((event: DataSourceEvent<T>) => {
|
|
363
|
+
if (event.type === DataSourceEventNames.dataChanged) {
|
|
364
|
+
setUpdateCounter((prevCounter) => prevCounter + 1);
|
|
365
|
+
}
|
|
366
|
+
}, []);
|
|
367
|
+
|
|
368
|
+
useArchbaseDidMount(() => {
|
|
369
|
+
loadDataSourceFieldValue();
|
|
370
|
+
if (dataSource && dataField) {
|
|
371
|
+
dataSource.addListener(dataSourceEvent);
|
|
372
|
+
dataSource.addFieldChangeListener(dataField, fieldChangedListener);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
if (options && options instanceof ArchbaseDataSource) {
|
|
376
|
+
(options as ArchbaseDataSource<T, ID>).addListener(dataSourceOptionsEvent);
|
|
377
|
+
}
|
|
378
|
+
});
|
|
379
|
+
|
|
380
|
+
useArchbaseWillUnmount(() => {
|
|
381
|
+
if (dataSource && dataField) {
|
|
382
|
+
dataSource.removeListener(dataSourceEvent);
|
|
383
|
+
dataSource.removeFieldChangeListener(dataField, fieldChangedListener);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
if (options && options instanceof ArchbaseDataSource) {
|
|
387
|
+
(options as ArchbaseDataSource<T, ID>).removeListener(dataSourceOptionsEvent);
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
|
|
391
|
+
useArchbaseDidUpdate(() => {
|
|
392
|
+
loadDataSourceFieldValue();
|
|
393
|
+
}, []);
|
|
394
|
+
|
|
395
|
+
useEffect(() => {
|
|
396
|
+
setInternalError(undefined);
|
|
397
|
+
}, [value, selectedValues, queryValue]);
|
|
398
|
+
|
|
399
|
+
useEffect(() => {
|
|
400
|
+
if (value !== undefined) {
|
|
401
|
+
setSelectedValues(value);
|
|
402
|
+
}
|
|
403
|
+
}, [value]);
|
|
404
|
+
|
|
405
|
+
const handleConverter = (value) => {
|
|
406
|
+
if (converter && value) {
|
|
407
|
+
return converter(value);
|
|
408
|
+
}
|
|
409
|
+
return value;
|
|
410
|
+
};
|
|
411
|
+
|
|
412
|
+
const handleValueRemove = (val: O) => {
|
|
413
|
+
setSelectedValues((current) => {
|
|
414
|
+
const updatedValues = current.filter((item) => getOptionValue(item) !== getOptionValue(val));
|
|
415
|
+
const convertedValues = updatedValues.map(updatedValue => handleConverter(updatedValue));
|
|
416
|
+
|
|
417
|
+
if (
|
|
418
|
+
dataSource &&
|
|
419
|
+
!dataSource.isBrowsing() &&
|
|
420
|
+
dataField &&
|
|
421
|
+
!areArraysEqual(
|
|
422
|
+
dataSource.getFieldValue(dataField) || [],
|
|
423
|
+
convertedValues
|
|
424
|
+
)
|
|
425
|
+
) {
|
|
426
|
+
dataSource.setFieldValue(dataField, convertedValues);
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
if (onChangeValues) {
|
|
430
|
+
onChangeValues(updatedValues);
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
return updatedValues;
|
|
434
|
+
});
|
|
435
|
+
};
|
|
436
|
+
|
|
437
|
+
const handleChange = (optionValue) => {
|
|
438
|
+
// Encontra o objeto original da opção
|
|
439
|
+
const option = currentOptions.find(opt => opt.value === optionValue);
|
|
440
|
+
const value = option && option.origin ? option.origin : optionValue;
|
|
441
|
+
|
|
442
|
+
setSelectedValues((prevSelected) => {
|
|
443
|
+
const isSelected = prevSelected.some(
|
|
444
|
+
(item) => getOptionValue(item) === getOptionValue(value)
|
|
445
|
+
);
|
|
446
|
+
|
|
447
|
+
// Se o valor já está selecionado, o removemos. Caso contrário, adicionamos.
|
|
448
|
+
const updatedValues = isSelected
|
|
449
|
+
? prevSelected.filter(
|
|
450
|
+
(item) => getOptionValue(item) !== getOptionValue(value)
|
|
451
|
+
)
|
|
452
|
+
: [...prevSelected, value];
|
|
453
|
+
|
|
454
|
+
const convertedValues = updatedValues.map(updatedValue => handleConverter(updatedValue));
|
|
455
|
+
|
|
456
|
+
if (
|
|
457
|
+
dataSource &&
|
|
458
|
+
!dataSource.isBrowsing() &&
|
|
459
|
+
dataField &&
|
|
460
|
+
!areArraysEqual(
|
|
461
|
+
dataSource.getFieldValue(dataField) || [],
|
|
462
|
+
convertedValues
|
|
463
|
+
)
|
|
464
|
+
) {
|
|
465
|
+
dataSource.setFieldValue(dataField, convertedValues);
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (onChangeValues) {
|
|
469
|
+
onChangeValues(updatedValues);
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
return updatedValues;
|
|
473
|
+
});
|
|
474
|
+
};
|
|
475
|
+
|
|
476
|
+
const handleOnFocusExit = (event) => {
|
|
477
|
+
if (onFocusExit) {
|
|
478
|
+
onFocusExit(event);
|
|
479
|
+
}
|
|
480
|
+
};
|
|
481
|
+
|
|
482
|
+
const handleOnFocusEnter = (event) => {
|
|
483
|
+
if (onFocusEnter) {
|
|
484
|
+
onFocusEnter(event);
|
|
485
|
+
}
|
|
486
|
+
};
|
|
487
|
+
|
|
488
|
+
const handleSearchChange = (query: string) => {
|
|
489
|
+
setQueryValue(query);
|
|
490
|
+
if (onSearchChange) {
|
|
491
|
+
onSearchChange(query);
|
|
492
|
+
}
|
|
493
|
+
};
|
|
494
|
+
|
|
495
|
+
const isReadOnly = () => {
|
|
496
|
+
let isReadOnly = readOnly;
|
|
497
|
+
if (dataSource && !readOnly) {
|
|
498
|
+
isReadOnly = dataSource.isBrowsing();
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
return isReadOnly;
|
|
502
|
+
};
|
|
503
|
+
|
|
504
|
+
const selectedValuesLabels = selectedValues.map(selecteValue => getOptionLabel(selecteValue).toLowerCase());
|
|
505
|
+
|
|
506
|
+
const filteredOptions = currentOptions.filter((item) => {
|
|
507
|
+
const matchesSearch = !searchable || item.label.toLowerCase().includes(queryValue.toLowerCase().trim());
|
|
508
|
+
const notSelected = !selectedValuesLabels.includes(item.label.toLowerCase());
|
|
509
|
+
return matchesSearch && notSelected;
|
|
510
|
+
});
|
|
511
|
+
|
|
512
|
+
const sortedSelectedValues = sortSelectedValues
|
|
513
|
+
? [...selectedValues].sort(sortSelectedValues)
|
|
514
|
+
: selectedValues;
|
|
515
|
+
|
|
516
|
+
const values = sortedSelectedValues.map((item) => (
|
|
517
|
+
<SelectedItemComponent
|
|
518
|
+
key={getOptionValue(item)}
|
|
519
|
+
item={item}
|
|
520
|
+
value={getOptionValue(item)}
|
|
521
|
+
onRemove={() => handleValueRemove(item)}
|
|
522
|
+
label={getOptionLabel(item)}
|
|
523
|
+
/>
|
|
524
|
+
));
|
|
525
|
+
|
|
526
|
+
const displayedOptions = limit ? filteredOptions.slice(0, limit) : filteredOptions;
|
|
527
|
+
|
|
528
|
+
return (
|
|
529
|
+
<Combobox
|
|
530
|
+
store={combobox}
|
|
531
|
+
withinPortal={true}
|
|
532
|
+
position={dropdownPosition}
|
|
533
|
+
zIndex={zIndex}
|
|
534
|
+
onOptionSubmit={(val) => {
|
|
535
|
+
handleChange(val);
|
|
536
|
+
setQueryValue('');
|
|
537
|
+
}}
|
|
538
|
+
>
|
|
539
|
+
<ComboboxTarget>
|
|
540
|
+
<PillsInput
|
|
541
|
+
disabled={disabled}
|
|
542
|
+
leftSection={icon}
|
|
543
|
+
leftSectionWidth={iconWidth}
|
|
544
|
+
label={label}
|
|
545
|
+
w={width}
|
|
546
|
+
description={description}
|
|
547
|
+
error={internalError}
|
|
548
|
+
required={required}
|
|
549
|
+
style={style}
|
|
550
|
+
onClick={() => combobox.openDropdown()}
|
|
551
|
+
rightSection={
|
|
552
|
+
<>
|
|
553
|
+
{selectedValues.length > 0 && clearable && !disabled && !isReadOnly() ? (
|
|
554
|
+
<CloseButton
|
|
555
|
+
size="sm"
|
|
556
|
+
onMouseDown={(event) => event.preventDefault()}
|
|
557
|
+
onClick={() => {
|
|
558
|
+
setQueryValue('');
|
|
559
|
+
setSelectedValues([]);
|
|
560
|
+
if (
|
|
561
|
+
dataSource &&
|
|
562
|
+
!dataSource.isBrowsing() &&
|
|
563
|
+
dataField
|
|
564
|
+
) {
|
|
565
|
+
dataSource.setFieldValue(dataField, []);
|
|
566
|
+
}
|
|
567
|
+
if (onChangeValues) {
|
|
568
|
+
onChangeValues([]);
|
|
569
|
+
}
|
|
570
|
+
}}
|
|
571
|
+
aria-label="Clear value"
|
|
572
|
+
/>
|
|
573
|
+
) : (
|
|
574
|
+
<Combobox.Chevron />
|
|
575
|
+
)}
|
|
576
|
+
</>
|
|
577
|
+
}
|
|
578
|
+
rightSectionPointerEvents={selectedValues.length === 0 ? 'none' : 'all'}
|
|
579
|
+
>
|
|
580
|
+
<Pill.Group>
|
|
581
|
+
{values}
|
|
582
|
+
<PillsInput.Field
|
|
583
|
+
readOnly={isReadOnly()}
|
|
584
|
+
value={queryValue}
|
|
585
|
+
onChange={(event) => {
|
|
586
|
+
handleSearchChange(event.currentTarget.value);
|
|
587
|
+
if (filteredOptions.length > 0) {
|
|
588
|
+
combobox.openDropdown();
|
|
589
|
+
}
|
|
590
|
+
combobox.updateSelectedOptionIndex();
|
|
591
|
+
}}
|
|
592
|
+
onBlur={(event) => {
|
|
593
|
+
handleOnFocusExit(event);
|
|
594
|
+
}}
|
|
595
|
+
onFocus={(event) => handleOnFocusEnter(event)}
|
|
596
|
+
onClick={() => {
|
|
597
|
+
if (filteredOptions.length > 0) {
|
|
598
|
+
combobox.openDropdown();
|
|
599
|
+
}
|
|
600
|
+
}}
|
|
601
|
+
placeholder={placeholder}
|
|
602
|
+
onKeyDown={(event) => {
|
|
603
|
+
if (event.key === 'Backspace' && queryValue.length === 0) {
|
|
604
|
+
if (selectedValues.length > 0) {
|
|
605
|
+
handleValueRemove(selectedValues[selectedValues.length - 1]);
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}}
|
|
609
|
+
/>
|
|
610
|
+
</Pill.Group>
|
|
611
|
+
</PillsInput>
|
|
612
|
+
</ComboboxTarget>
|
|
613
|
+
<ComboboxDropdown>
|
|
614
|
+
<Combobox.Options>
|
|
615
|
+
<ScrollArea.Autosize mah={maxDropdownHeight} type="scroll">
|
|
616
|
+
{displayedOptions.length === 0 ? (
|
|
617
|
+
<Combobox.Empty>{nothingFound || 'Nada encontrado'}</Combobox.Empty>
|
|
618
|
+
) : (
|
|
619
|
+
displayedOptions.map((option) => (
|
|
620
|
+
<Combobox.Option
|
|
621
|
+
value={option.value}
|
|
622
|
+
key={option.key}
|
|
623
|
+
>
|
|
624
|
+
{ItemComponent ? (
|
|
625
|
+
<ItemComponent
|
|
626
|
+
values={selectedValues.map(v => getOptionValue(v))}
|
|
627
|
+
{...option}
|
|
628
|
+
/>
|
|
629
|
+
) : (
|
|
630
|
+
option.label
|
|
631
|
+
)}
|
|
632
|
+
</Combobox.Option>
|
|
633
|
+
))
|
|
634
|
+
)}
|
|
635
|
+
</ScrollArea.Autosize>
|
|
636
|
+
</Combobox.Options>
|
|
637
|
+
</ComboboxDropdown>
|
|
638
|
+
</Combobox>
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
ArchbaseMultiSelect.displayName = 'ArchbaseMultiSelect';
|