@cloud-ru/uikit-product-mobile-chips 0.8.36

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 (46) hide show
  1. package/CHANGELOG.md +1178 -0
  2. package/LICENSE +201 -0
  3. package/README.md +8 -0
  4. package/package.json +60 -0
  5. package/src/components/AdaptiveChips/AdaptiveChips.tsx +21 -0
  6. package/src/components/AdaptiveChips/index.ts +1 -0
  7. package/src/components/MobileChipChoice/components/ChipChoiceBase/ChipChoiceBase.tsx +148 -0
  8. package/src/components/MobileChipChoice/components/ChipChoiceBase/index.ts +1 -0
  9. package/src/components/MobileChipChoice/components/ChipChoiceBase/styles.module.scss +89 -0
  10. package/src/components/MobileChipChoice/components/MobileChipChoiceCustom.tsx +81 -0
  11. package/src/components/MobileChipChoice/components/MobileChipChoiceDate.tsx +135 -0
  12. package/src/components/MobileChipChoice/components/MobileChipChoiceDateRange.tsx +101 -0
  13. package/src/components/MobileChipChoice/components/MobileChipChoiceMultiple.tsx +218 -0
  14. package/src/components/MobileChipChoice/components/MobileChipChoiceSingle.tsx +176 -0
  15. package/src/components/MobileChipChoice/components/MobileChipChoiceTime.tsx +122 -0
  16. package/src/components/MobileChipChoice/components/index.ts +6 -0
  17. package/src/components/MobileChipChoice/components/styles.module.scss +36 -0
  18. package/src/components/MobileChipChoice/constants.ts +20 -0
  19. package/src/components/MobileChipChoice/hooks.tsx +127 -0
  20. package/src/components/MobileChipChoice/index.ts +38 -0
  21. package/src/components/MobileChipChoice/styles.module.scss +103 -0
  22. package/src/components/MobileChipChoice/types.ts +139 -0
  23. package/src/components/MobileChipChoice/utils/index.ts +3 -0
  24. package/src/components/MobileChipChoice/utils/options.tsx +61 -0
  25. package/src/components/MobileChipChoice/utils/typeGuards.ts +35 -0
  26. package/src/components/MobileChipChoice/utils/utils.ts +62 -0
  27. package/src/components/MobileChipChoiceRow/MobileChipChoiceRow.tsx +275 -0
  28. package/src/components/MobileChipChoiceRow/components/ForwardedChipChoice.tsx +10 -0
  29. package/src/components/MobileChipChoiceRow/components/constants.ts +12 -0
  30. package/src/components/MobileChipChoiceRow/components/index.ts +1 -0
  31. package/src/components/MobileChipChoiceRow/constants.ts +21 -0
  32. package/src/components/MobileChipChoiceRow/index.ts +2 -0
  33. package/src/components/MobileChipChoiceRow/styles.module.scss +32 -0
  34. package/src/components/MobileChipChoiceRow/types.ts +60 -0
  35. package/src/components/index.ts +3 -0
  36. package/src/constants.ts +50 -0
  37. package/src/helperComponents/ButtonClearValue/ButtonClearValue.tsx +40 -0
  38. package/src/helperComponents/ButtonClearValue/index.ts +1 -0
  39. package/src/helperComponents/ButtonClearValue/styles.module.scss +50 -0
  40. package/src/helperComponents/ItemContent/ItemContent.tsx +37 -0
  41. package/src/helperComponents/ItemContent/index.ts +1 -0
  42. package/src/helperComponents/ItemContent/styles.module.scss +80 -0
  43. package/src/helperComponents/index.ts +2 -0
  44. package/src/index.ts +1 -0
  45. package/src/styles.module.scss +113 -0
  46. package/src/types.ts +26 -0
@@ -0,0 +1,135 @@
1
+ import { ReactNode, useCallback, useMemo, useRef } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
3
+
4
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
5
+ import { MobileDropdown } from '@cloud-ru/uikit-product-mobile-dropdown';
6
+ import { Calendar, CalendarProps } from '@snack-uikit/calendar';
7
+ import { Scroll } from '@snack-uikit/scroll';
8
+ import { useValueControl } from '@snack-uikit/utils';
9
+
10
+ import { CHIP_CHOICE_TEST_IDS, SIZE } from '../../../constants';
11
+ import { DEFAULT_LOCALE } from '../constants';
12
+ import { useHandleOnKeyDown } from '../hooks';
13
+ import { ChipChoiceCommonProps } from '../types';
14
+ import { ChipChoiceBase } from './ChipChoiceBase';
15
+ import styles from './styles.module.scss';
16
+
17
+ type ChipChoiceDateWithSeconds = {
18
+ mode?: 'date-time';
19
+ showSeconds?: boolean;
20
+ };
21
+
22
+ export type MobileChipChoiceDateProps = ChipChoiceCommonProps & {
23
+ /** Значение компонента */
24
+ value?: Date;
25
+ /** Значение компонента по-умолчанию */
26
+ defaultValue?: Date;
27
+ /** Колбек смены значения */
28
+ onChange?(value: Date): void;
29
+ /** Колбек формирующий строковое представление выбранного значения. Принимает выбранное значение */
30
+ valueRender?(value?: Date): ReactNode;
31
+ mode?: Exclude<CalendarProps['mode'], 'range'>;
32
+ } & (
33
+ | ChipChoiceDateWithSeconds
34
+ | {
35
+ mode?: 'date' | 'month' | 'year';
36
+ }
37
+ );
38
+
39
+ export function MobileChipChoiceDate({
40
+ size = SIZE.S,
41
+ value,
42
+ defaultValue,
43
+ onChange,
44
+ valueRender,
45
+ mode = 'date',
46
+ onClearButtonClick,
47
+ open: openProp,
48
+ onOpenChange,
49
+ ...rest
50
+ }: MobileChipChoiceDateProps) {
51
+ const [selectedValue, setSelectedValue] = useValueControl<Date>({ value, defaultValue, onChange });
52
+
53
+ const showSeconds = mode === 'date-time' ? ((rest as ChipChoiceDateWithSeconds).showSeconds ?? true) : undefined;
54
+
55
+ const localRef = useRef<HTMLDivElement>(null);
56
+
57
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
58
+ const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
59
+
60
+ const closeDroplist = useCallback(() => {
61
+ setOpen(false);
62
+ setTimeout(() => localRef.current?.focus(), 0);
63
+ }, [setOpen]);
64
+
65
+ const { t } = useLocale('Chips');
66
+
67
+ const valueToRender = useMemo(() => {
68
+ if (valueRender) {
69
+ return valueRender(selectedValue);
70
+ }
71
+
72
+ if (!selectedValue) return t('allLabel');
73
+
74
+ const date = new Date(selectedValue);
75
+
76
+ if (mode === 'date-time') {
77
+ return date.toLocaleString(DEFAULT_LOCALE, {
78
+ year: 'numeric',
79
+ month: 'numeric',
80
+ day: 'numeric',
81
+ hour: '2-digit',
82
+ minute: '2-digit',
83
+ second: showSeconds ? '2-digit' : undefined,
84
+ });
85
+ }
86
+
87
+ return date.toLocaleDateString(DEFAULT_LOCALE, {
88
+ year: 'numeric',
89
+ month: 'numeric',
90
+ day: mode === 'date' ? 'numeric' : undefined,
91
+ });
92
+ }, [mode, selectedValue, showSeconds, t, valueRender]);
93
+
94
+ const handleChangeValue = useCallback(
95
+ (value: Date) => {
96
+ setSelectedValue(value);
97
+ closeDroplist();
98
+ },
99
+ [closeDroplist, setSelectedValue],
100
+ );
101
+
102
+ const navigationStartRef = useRef<HTMLButtonElement>(null);
103
+
104
+ return (
105
+ <MobileDropdown
106
+ content={
107
+ <Scroll className={mode === 'date-time' ? styles.dateTimeWrapper : styles.dateWrapper} barHideStrategy='never'>
108
+ <Calendar
109
+ mode={mode}
110
+ size='l'
111
+ value={selectedValue}
112
+ onChangeValue={handleChangeValue}
113
+ navigationStartRef={navigationStartRef}
114
+ onFocusLeave={closeDroplist}
115
+ showSeconds={showSeconds}
116
+ locale={DEFAULT_LOCALE}
117
+ />
118
+ </Scroll>
119
+ }
120
+ open={open}
121
+ onOpenChange={setOpen}
122
+ data-test-id={CHIP_CHOICE_TEST_IDS.droplist}
123
+ >
124
+ <ChipChoiceBase
125
+ {...rest}
126
+ ref={localRef}
127
+ onClearButtonClick={onClearButtonClick}
128
+ value={selectedValue}
129
+ valueToRender={valueToRender}
130
+ size={size}
131
+ onKeyDown={handleOnKeyDown()}
132
+ />
133
+ </MobileDropdown>
134
+ );
135
+ }
@@ -0,0 +1,101 @@
1
+ import { ReactNode, useCallback, useRef } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
3
+
4
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
5
+ import { MobileDropdown } from '@cloud-ru/uikit-product-mobile-dropdown';
6
+ import { Calendar } from '@snack-uikit/calendar';
7
+
8
+ import { CHIP_CHOICE_TEST_IDS, DEFAULT_EMPTY_VALUE, SIZE } from '../../../constants';
9
+ import { useHandleOnKeyDown } from '../hooks';
10
+ import { ChipChoiceCommonProps } from '../types';
11
+ import { ChipChoiceBase } from './ChipChoiceBase';
12
+ import styles from './styles.module.scss';
13
+
14
+ type Range = [Date, Date];
15
+
16
+ export type MobileChipChoiceDateRangeProps = ChipChoiceCommonProps & {
17
+ /** Значение компонента */
18
+ value?: Range;
19
+ /** Значение компонента по умолчанию */
20
+ defaultValue?: Range;
21
+ /** Колбек смены значения */
22
+ onChange?(value: Range): void;
23
+ /** Колбек формирующий строковое представление выбранного значения. Принимает массив выбранных значений */
24
+ valueRender?(value?: Range): ReactNode;
25
+ };
26
+
27
+ type DefaultRangeFormatterProps = {
28
+ value?: Range;
29
+ allLabel?: string;
30
+ };
31
+
32
+ function defaultRangeFormatter({ value, allLabel }: DefaultRangeFormatterProps) {
33
+ if (!value || !value.length) return allLabel;
34
+
35
+ const [from, to] = value;
36
+
37
+ return `${from.toLocaleDateString()} ${DEFAULT_EMPTY_VALUE} ${to.toLocaleDateString()}`;
38
+ }
39
+
40
+ export function MobileChipChoiceDateRange({
41
+ size = SIZE.S,
42
+ value,
43
+ defaultValue,
44
+ onChange,
45
+ valueRender,
46
+ onClearButtonClick,
47
+ open: openProp,
48
+ onOpenChange,
49
+ ...rest
50
+ }: MobileChipChoiceDateRangeProps) {
51
+ const [selectedValue, setSelectedValue] = useUncontrolledProp<Range>(value, defaultValue, onChange);
52
+
53
+ const { t } = useLocale('Chips');
54
+
55
+ const valueToRender = valueRender
56
+ ? valueRender(selectedValue)
57
+ : defaultRangeFormatter({ value: selectedValue, allLabel: t('allLabel') });
58
+
59
+ const localRef = useRef<HTMLDivElement>(null);
60
+
61
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
62
+
63
+ const closeDroplist = useCallback(() => {
64
+ setOpen(false);
65
+ setTimeout(() => localRef.current?.focus(), 0);
66
+ }, [setOpen]);
67
+
68
+ const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
69
+
70
+ return (
71
+ <MobileDropdown
72
+ content={
73
+ <div className={styles.dateWrapper}>
74
+ <Calendar
75
+ mode='range'
76
+ size='l'
77
+ value={selectedValue}
78
+ onChangeValue={value => {
79
+ setSelectedValue(value);
80
+ closeDroplist();
81
+ }}
82
+ onFocusLeave={closeDroplist}
83
+ />
84
+ </div>
85
+ }
86
+ open={open}
87
+ data-test-id={CHIP_CHOICE_TEST_IDS.droplist}
88
+ onOpenChange={setOpen}
89
+ >
90
+ <ChipChoiceBase
91
+ {...rest}
92
+ ref={localRef}
93
+ onClearButtonClick={onClearButtonClick}
94
+ value={selectedValue}
95
+ valueToRender={valueToRender}
96
+ size={size}
97
+ onKeyDown={handleOnKeyDown()}
98
+ />
99
+ </MobileDropdown>
100
+ );
101
+ }
@@ -0,0 +1,218 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
3
+
4
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
5
+ import { ItemId, MobileDroplist, SelectionSingleValueType } from '@cloud-ru/uikit-product-mobile-dropdown';
6
+ import { ButtonFilled, ButtonFunction } from '@snack-uikit/button';
7
+ import { useValueControl } from '@snack-uikit/utils';
8
+
9
+ import { CHIP_CHOICE_TEST_IDS, SIZE } from '../../../constants';
10
+ import { useAutoApplyFooter, useHandleOnKeyDown, useOptionSearch } from '../hooks';
11
+ import { ContentRenderProps, MobileChipChoiceMultipleProps } from '../types';
12
+ import { FlattenOption, kindFlattenOptions } from '../utils';
13
+ import { transformOptionsToItems } from '../utils/options';
14
+ import { ChipChoiceBase } from './ChipChoiceBase';
15
+ import styles from './styles.module.scss';
16
+
17
+ export type ChipChoiceMultipleValueFormatterProps<T extends ContentRenderProps = ContentRenderProps> = {
18
+ value: FlattenOption<T>[];
19
+ total: number;
20
+ allLabel: string;
21
+ };
22
+
23
+ const defaultMultiValueLabelFormatter = ({ value, total, allLabel }: ChipChoiceMultipleValueFormatterProps): ItemId => {
24
+ const len = value.length;
25
+
26
+ if ([0, total].includes(len) && total !== len) {
27
+ return allLabel;
28
+ }
29
+
30
+ if (len === 1) {
31
+ return value[0].label;
32
+ }
33
+
34
+ return `${len.toString()}/${total}`;
35
+ };
36
+
37
+ export function MobileChipChoiceMultiple<T extends ContentRenderProps = ContentRenderProps>({
38
+ value: valueProp,
39
+ defaultValue,
40
+ options,
41
+ onChange: onChangeProp,
42
+ valueRender,
43
+ size = SIZE.S,
44
+ label,
45
+ searchable: searchableProp,
46
+ contentRender,
47
+ onClearButtonClick,
48
+ open: openProp,
49
+ onOpenChange,
50
+ virtualized,
51
+ disableFuzzySearch = false,
52
+ autoApply = true,
53
+ onApprove,
54
+ onCancel,
55
+ ...rest
56
+ }: MobileChipChoiceMultipleProps<T>) {
57
+ const { t } = useLocale();
58
+
59
+ const [value, setValue] = useValueControl<SelectionSingleValueType[]>({
60
+ value: valueProp,
61
+ defaultValue,
62
+ onChange: onChangeProp,
63
+ });
64
+
65
+ const [deferredValue, setDeferredValue] = useValueControl<SelectionSingleValueType[]>({
66
+ defaultValue,
67
+ });
68
+
69
+ const flattenOptions = useMemo(() => {
70
+ const { flattenOptions } = kindFlattenOptions<T>({ options });
71
+
72
+ return flattenOptions;
73
+ }, [options]);
74
+
75
+ const searchable = searchableProp && Object.values(flattenOptions).length > 5;
76
+
77
+ const [searchValue = '', setSearchValue] = useState<string>('');
78
+
79
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
80
+ const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
81
+
82
+ const flatMapOptions = useMemo(() => Object.values(flattenOptions), [flattenOptions]);
83
+
84
+ const dropListSelection = useMemo(() => (autoApply ? value : deferredValue), [autoApply, deferredValue, value]);
85
+
86
+ const selectedOptions = useMemo(
87
+ () => (value && value.length ? value.map(id => flattenOptions[id]).filter(Boolean) : ([] as FlattenOption<T>[])),
88
+ [flattenOptions, value],
89
+ );
90
+
91
+ const valueToRender = valueRender
92
+ ? valueRender(selectedOptions)
93
+ : defaultMultiValueLabelFormatter({
94
+ value: selectedOptions ?? [],
95
+ total: Object.keys(flattenOptions).length,
96
+ allLabel: t('Chips.allLabel'),
97
+ });
98
+
99
+ const optionSearch = useOptionSearch({ options, flatMapOptions, disableFuzzySearch });
100
+
101
+ const result = useMemo(
102
+ () => (!searchable || valueToRender === searchValue ? options : optionSearch(searchValue)),
103
+ [optionSearch, options, searchValue, searchable, valueToRender],
104
+ );
105
+ const items = useMemo(() => transformOptionsToItems<T>(result, contentRender), [contentRender, result]);
106
+
107
+ const chipRef = useRef<HTMLDivElement>(null);
108
+ const listRef = useRef<HTMLElement>(null);
109
+
110
+ const handleSelectionChange = useCallback(
111
+ (newValue?: SelectionSingleValueType[]) => {
112
+ if (newValue !== undefined) {
113
+ if (autoApply) {
114
+ setValue(newValue);
115
+ } else {
116
+ setDeferredValue(newValue);
117
+ }
118
+
119
+ if (searchValue) {
120
+ listRef.current?.focus();
121
+ }
122
+ }
123
+ },
124
+ [autoApply, searchValue, setValue, setDeferredValue],
125
+ );
126
+
127
+ const handleOnCancelClick = () => {
128
+ onCancel?.();
129
+
130
+ setDeferredValue(value);
131
+ setOpen(false);
132
+ };
133
+
134
+ const handleOnApproveClick = () => {
135
+ onApprove?.();
136
+
137
+ setValue(deferredValue);
138
+ setOpen(false);
139
+ };
140
+
141
+ const autoApplyFooter = useAutoApplyFooter({
142
+ autoApply,
143
+ onApprove: handleOnApproveClick,
144
+ onCancel: handleOnCancelClick,
145
+ });
146
+
147
+ useEffect(() => {
148
+ if (searchValue && !open) {
149
+ setSearchValue('');
150
+ }
151
+ }, [searchable, open, searchValue]);
152
+
153
+ useEffect(() => {
154
+ setDeferredValue(value);
155
+ // eslint-disable-next-line react-hooks/exhaustive-deps
156
+ }, [value]);
157
+
158
+ return (
159
+ <MobileDroplist
160
+ items={items}
161
+ selection={{
162
+ value: dropListSelection,
163
+ onChange: handleSelectionChange,
164
+ mode: 'multiple',
165
+ }}
166
+ data-test-id={CHIP_CHOICE_TEST_IDS.droplist}
167
+ open={open}
168
+ onOpenChange={open => {
169
+ if (!open) {
170
+ setSearchValue('');
171
+ handleOnCancelClick();
172
+ }
173
+ setOpen(open);
174
+ }}
175
+ label={label}
176
+ virtualized={virtualized}
177
+ footer={
178
+ <div className={styles.footer}>
179
+ <div className={styles.footerTopLine}>
180
+ <span className={styles.counter}>{`${t('MobileChips.selectedN')}${value?.length || 0}`}</span>
181
+ <ButtonFunction
182
+ label={t('MobileChips.resetAll')}
183
+ onClick={() => {
184
+ handleSelectionChange([]);
185
+ }}
186
+ size='m'
187
+ />
188
+ </div>
189
+
190
+ {autoApply ? (
191
+ <ButtonFilled
192
+ fullWidth
193
+ label={t('MobileChips.select')}
194
+ onClick={() => {
195
+ setOpen(false);
196
+ }}
197
+ size='l'
198
+ />
199
+ ) : (
200
+ autoApplyFooter
201
+ )}
202
+ </div>
203
+ }
204
+ >
205
+ <ChipChoiceBase
206
+ {...rest}
207
+ ref={chipRef}
208
+ onClearButtonClick={onClearButtonClick}
209
+ value={value}
210
+ valueToRender={valueToRender}
211
+ label={label}
212
+ loading={rest.loading}
213
+ size={size}
214
+ onKeyDown={handleOnKeyDown()}
215
+ />
216
+ </MobileDroplist>
217
+ );
218
+ }
@@ -0,0 +1,176 @@
1
+ import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
3
+
4
+ import { useLocale } from '@cloud-ru/uikit-product-locale';
5
+ import { ItemId, MobileDroplist, SelectionSingleValueType } from '@cloud-ru/uikit-product-mobile-dropdown';
6
+ import { useValueControl } from '@snack-uikit/utils';
7
+
8
+ import { CHIP_CHOICE_TEST_IDS, SIZE } from '../../../constants';
9
+ import { useAutoApplyFooter, useHandleOnKeyDown, useOptionSearch } from '../hooks';
10
+ import { ContentRenderProps, MobileChipChoiceSingleProps } from '../types';
11
+ import { FlattenOption, kindFlattenOptions } from '../utils';
12
+ import { transformOptionsToItems } from '../utils/options';
13
+ import { ChipChoiceBase } from './ChipChoiceBase';
14
+ import styles from './styles.module.scss';
15
+
16
+ export type ChipChoiceSingleValueFormatterProps = {
17
+ label?: ItemId;
18
+ allLabel?: string;
19
+ };
20
+
21
+ export function defaultSingleValueFormatter({ label, allLabel }: ChipChoiceSingleValueFormatterProps) {
22
+ return label ?? allLabel;
23
+ }
24
+
25
+ export function MobileChipChoiceSingle<T extends ContentRenderProps = ContentRenderProps>({
26
+ value: valueProp,
27
+ defaultValue,
28
+ options,
29
+ onChange: onChangeProp,
30
+ valueRender,
31
+ size = SIZE.S,
32
+ label,
33
+ searchable: searchableProp,
34
+ contentRender,
35
+ onClearButtonClick,
36
+ open: openProp,
37
+ onOpenChange,
38
+ virtualized,
39
+ disableFuzzySearch = false,
40
+ autoApply = true,
41
+ onApprove,
42
+ onCancel,
43
+ ...rest
44
+ }: MobileChipChoiceSingleProps<T>) {
45
+ const [value, setValue] = useValueControl<SelectionSingleValueType>({
46
+ value: valueProp,
47
+ defaultValue,
48
+ onChange: onChangeProp,
49
+ });
50
+
51
+ const [deferredValue, setDeferredValue] = useValueControl<SelectionSingleValueType>({
52
+ defaultValue,
53
+ });
54
+
55
+ const flattenOptions = useMemo(() => {
56
+ const { flattenOptions } = kindFlattenOptions<T>({ options });
57
+
58
+ return flattenOptions;
59
+ }, [options]);
60
+
61
+ const searchable = searchableProp && Object.values(flattenOptions).length > 5;
62
+
63
+ const { t } = useLocale('Chips');
64
+
65
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
66
+ const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
67
+
68
+ const flatMapOptions = useMemo(() => Object.values(flattenOptions), [flattenOptions]);
69
+
70
+ const dropListSelection = useMemo(() => (autoApply ? value : deferredValue), [autoApply, deferredValue, value]);
71
+
72
+ const selectedOption = useMemo(
73
+ () => (value ? flattenOptions[value] : ({} as FlattenOption<T>)),
74
+ [flattenOptions, value],
75
+ );
76
+
77
+ const [searchValue, setSearchValue] = useState<string>('');
78
+
79
+ const valueToRender = valueRender
80
+ ? valueRender(selectedOption)
81
+ : defaultSingleValueFormatter({ label: selectedOption?.label, allLabel: t('allLabel') });
82
+
83
+ const optionSearch = useOptionSearch({ options, flatMapOptions, disableFuzzySearch });
84
+
85
+ const result = useMemo(
86
+ () => (!searchable || valueToRender === searchValue ? options : optionSearch(searchValue)),
87
+ [optionSearch, options, searchValue, searchable, valueToRender],
88
+ );
89
+ const items = useMemo(() => transformOptionsToItems<T>(result, contentRender), [contentRender, result]);
90
+
91
+ const chipRef = useRef<HTMLDivElement>(null);
92
+
93
+ const handleSelectionChange = useCallback(
94
+ (newValue?: SelectionSingleValueType) => {
95
+ if (newValue !== undefined) {
96
+ chipRef.current?.focus();
97
+
98
+ if (autoApply) {
99
+ setOpen(false);
100
+ setSearchValue('');
101
+ setValue(newValue);
102
+ } else {
103
+ setDeferredValue(newValue);
104
+ }
105
+
106
+ return;
107
+ }
108
+
109
+ if (autoApply) {
110
+ setOpen(false);
111
+ }
112
+ },
113
+ [autoApply, setOpen, setValue, setDeferredValue],
114
+ );
115
+
116
+ const handleOnCancelClick = useCallback(() => {
117
+ onCancel?.();
118
+
119
+ setDeferredValue(value);
120
+ setOpen(false);
121
+ }, [onCancel, setDeferredValue, setOpen, value]);
122
+
123
+ const handleOnApproveClick = useCallback(() => {
124
+ onApprove?.();
125
+
126
+ setValue(deferredValue);
127
+ setOpen(false);
128
+ }, [deferredValue, onApprove, setOpen, setValue]);
129
+
130
+ const autoApplyFooter = useAutoApplyFooter({
131
+ autoApply,
132
+ onApprove: handleOnApproveClick,
133
+ onCancel: handleOnCancelClick,
134
+ });
135
+
136
+ useEffect(() => {
137
+ if (searchValue && !open) {
138
+ setSearchValue('');
139
+ }
140
+ }, [searchable, open, searchValue]);
141
+
142
+ return (
143
+ <MobileDroplist
144
+ items={items}
145
+ selection={{
146
+ value: dropListSelection,
147
+ onChange: handleSelectionChange,
148
+ mode: 'single',
149
+ }}
150
+ data-test-id={CHIP_CHOICE_TEST_IDS.droplist}
151
+ open={open}
152
+ onOpenChange={open => {
153
+ if (!open) {
154
+ setSearchValue('');
155
+ handleOnCancelClick();
156
+ }
157
+ setOpen(open);
158
+ }}
159
+ label={label}
160
+ virtualized={virtualized}
161
+ footer={!autoApply && autoApplyFooter ? <div className={styles.footer}>{autoApplyFooter}</div> : undefined}
162
+ >
163
+ <ChipChoiceBase
164
+ {...rest}
165
+ ref={chipRef}
166
+ onClearButtonClick={onClearButtonClick}
167
+ value={value}
168
+ valueToRender={valueToRender}
169
+ label={label}
170
+ loading={rest.loading}
171
+ size={size}
172
+ onKeyDown={handleOnKeyDown()}
173
+ />
174
+ </MobileDroplist>
175
+ );
176
+ }