@snack-uikit/chips 0.8.5-preview-85c5f47b.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/CHANGELOG.md +483 -0
  2. package/LICENSE +201 -0
  3. package/README.md +226 -0
  4. package/dist/components/ChipAssist/ChipAssist.d.ts +15 -0
  5. package/dist/components/ChipAssist/ChipAssist.js +31 -0
  6. package/dist/components/ChipAssist/index.d.ts +1 -0
  7. package/dist/components/ChipAssist/index.js +1 -0
  8. package/dist/components/ChipAssist/styles.module.css +159 -0
  9. package/dist/components/ChipChoice/components/ChipChoiceCustom/ChipChoiceCustom.d.ts +23 -0
  10. package/dist/components/ChipChoice/components/ChipChoiceCustom/ChipChoiceCustom.js +75 -0
  11. package/dist/components/ChipChoice/components/ChipChoiceCustom/index.d.ts +1 -0
  12. package/dist/components/ChipChoice/components/ChipChoiceCustom/index.js +1 -0
  13. package/dist/components/ChipChoice/components/ChipChoiceCustom/styles.module.css +402 -0
  14. package/dist/components/ChipChoice/components/ChipChoiceDate.d.ts +12 -0
  15. package/dist/components/ChipChoice/components/ChipChoiceDate.js +29 -0
  16. package/dist/components/ChipChoice/components/ChipChoiceDateRange.d.ts +14 -0
  17. package/dist/components/ChipChoice/components/ChipChoiceDateRange.js +36 -0
  18. package/dist/components/ChipChoice/components/ChipChoiceMulti.d.ts +14 -0
  19. package/dist/components/ChipChoice/components/ChipChoiceMulti.js +42 -0
  20. package/dist/components/ChipChoice/components/ChipChoiceSingle.d.ts +14 -0
  21. package/dist/components/ChipChoice/components/ChipChoiceSingle.js +34 -0
  22. package/dist/components/ChipChoice/components/index.d.ts +5 -0
  23. package/dist/components/ChipChoice/components/index.js +5 -0
  24. package/dist/components/ChipChoice/constants.d.ts +25 -0
  25. package/dist/components/ChipChoice/constants.js +28 -0
  26. package/dist/components/ChipChoice/index.d.ts +17 -0
  27. package/dist/components/ChipChoice/index.js +17 -0
  28. package/dist/components/ChipChoice/styles.module.css +199 -0
  29. package/dist/components/ChipChoice/types.d.ts +27 -0
  30. package/dist/components/ChipChoice/types.js +1 -0
  31. package/dist/components/ChipChoice/utils.d.ts +4 -0
  32. package/dist/components/ChipChoice/utils.js +14 -0
  33. package/dist/components/ChipChoiceRow/ChipChoiceRow.d.ts +26 -0
  34. package/dist/components/ChipChoiceRow/ChipChoiceRow.js +45 -0
  35. package/dist/components/ChipChoiceRow/components/ForwardedChipChoice.d.ts +2 -0
  36. package/dist/components/ChipChoiceRow/components/ForwardedChipChoice.js +6 -0
  37. package/dist/components/ChipChoiceRow/components/constants.d.ts +6 -0
  38. package/dist/components/ChipChoiceRow/components/constants.js +7 -0
  39. package/dist/components/ChipChoiceRow/components/index.d.ts +1 -0
  40. package/dist/components/ChipChoiceRow/components/index.js +1 -0
  41. package/dist/components/ChipChoiceRow/constants.d.ts +15 -0
  42. package/dist/components/ChipChoiceRow/constants.js +18 -0
  43. package/dist/components/ChipChoiceRow/index.d.ts +1 -0
  44. package/dist/components/ChipChoiceRow/index.js +1 -0
  45. package/dist/components/ChipChoiceRow/styles.module.css +6 -0
  46. package/dist/components/ChipChoiceRow/types.d.ts +20 -0
  47. package/dist/components/ChipChoiceRow/types.js +1 -0
  48. package/dist/components/ChipToggle/ChipToggle.d.ts +17 -0
  49. package/dist/components/ChipToggle/ChipToggle.js +31 -0
  50. package/dist/components/ChipToggle/index.d.ts +1 -0
  51. package/dist/components/ChipToggle/index.js +1 -0
  52. package/dist/components/ChipToggle/styles.module.css +219 -0
  53. package/dist/components/index.d.ts +4 -0
  54. package/dist/components/index.js +4 -0
  55. package/dist/constants.d.ts +37 -0
  56. package/dist/constants.js +40 -0
  57. package/dist/helperComponents/ButtonClearValue/ButtonClearValue.d.ts +11 -0
  58. package/dist/helperComponents/ButtonClearValue/ButtonClearValue.js +12 -0
  59. package/dist/helperComponents/ButtonClearValue/index.d.ts +1 -0
  60. package/dist/helperComponents/ButtonClearValue/index.js +1 -0
  61. package/dist/helperComponents/ButtonClearValue/styles.module.css +43 -0
  62. package/dist/helperComponents/index.d.ts +1 -0
  63. package/dist/helperComponents/index.js +1 -0
  64. package/dist/index.d.ts +1 -0
  65. package/dist/index.js +1 -0
  66. package/dist/styles.module.css +0 -0
  67. package/dist/types.d.ts +15 -0
  68. package/dist/types.js +1 -0
  69. package/package.json +45 -0
  70. package/src/components/ChipAssist/ChipAssist.tsx +73 -0
  71. package/src/components/ChipAssist/index.ts +1 -0
  72. package/src/components/ChipAssist/styles.module.scss +60 -0
  73. package/src/components/ChipChoice/components/ChipChoiceCustom/ChipChoiceCustom.tsx +188 -0
  74. package/src/components/ChipChoice/components/ChipChoiceCustom/index.ts +1 -0
  75. package/src/components/ChipChoice/components/ChipChoiceCustom/styles.module.scss +90 -0
  76. package/src/components/ChipChoice/components/ChipChoiceDate.tsx +60 -0
  77. package/src/components/ChipChoice/components/ChipChoiceDateRange.tsx +69 -0
  78. package/src/components/ChipChoice/components/ChipChoiceMulti.tsx +87 -0
  79. package/src/components/ChipChoice/components/ChipChoiceSingle.tsx +77 -0
  80. package/src/components/ChipChoice/components/index.ts +5 -0
  81. package/src/components/ChipChoice/constants.ts +32 -0
  82. package/src/components/ChipChoice/index.ts +39 -0
  83. package/src/components/ChipChoice/styles.module.scss +90 -0
  84. package/src/components/ChipChoice/types.ts +33 -0
  85. package/src/components/ChipChoice/utils.ts +20 -0
  86. package/src/components/ChipChoiceRow/ChipChoiceRow.tsx +100 -0
  87. package/src/components/ChipChoiceRow/components/ForwardedChipChoice.tsx +10 -0
  88. package/src/components/ChipChoiceRow/components/constants.ts +8 -0
  89. package/src/components/ChipChoiceRow/components/index.ts +1 -0
  90. package/src/components/ChipChoiceRow/constants.ts +21 -0
  91. package/src/components/ChipChoiceRow/index.ts +1 -0
  92. package/src/components/ChipChoiceRow/styles.module.scss +8 -0
  93. package/src/components/ChipChoiceRow/types.ts +34 -0
  94. package/src/components/ChipToggle/ChipToggle.tsx +87 -0
  95. package/src/components/ChipToggle/index.ts +1 -0
  96. package/src/components/ChipToggle/styles.module.scss +153 -0
  97. package/src/components/index.ts +4 -0
  98. package/src/constants.ts +44 -0
  99. package/src/helperComponents/ButtonClearValue/ButtonClearValue.tsx +39 -0
  100. package/src/helperComponents/ButtonClearValue/index.ts +1 -0
  101. package/src/helperComponents/ButtonClearValue/styles.module.scss +50 -0
  102. package/src/helperComponents/index.ts +1 -0
  103. package/src/index.ts +1 -0
  104. package/src/styles.module.scss +114 -0
  105. package/src/types.ts +16 -0
@@ -0,0 +1,188 @@
1
+ import cn from 'classnames';
2
+ import { KeyboardEventHandler, MouseEventHandler, ReactNode, useCallback, useRef, useState } from 'react';
3
+
4
+ import { Droplist } from '@snack-uikit/droplist';
5
+ import { Sun } from '@snack-uikit/loaders';
6
+ import { extractSupportProps } from '@snack-uikit/utils';
7
+
8
+ import { CHIP_CHOICE_TEST_IDS, Size, Variant } from '../../../../constants';
9
+ import { ButtonClearValue } from '../../../../helperComponents';
10
+ import { BUTTON_CLEAR_VALUE_SIZE_MAP, DROPLIST_SIZE_MAP } from '../../constants';
11
+ import { ChipChoiceCommonProps } from '../../types';
12
+ import styles from './styles.module.scss';
13
+
14
+ export type ChipChoiceCustomProps = ChipChoiceCommonProps & {
15
+ /** Отображаемое значение */
16
+ valueToRender?: ReactNode;
17
+ /** Фактическое значение. Используется для отображения кнопки очистки, если свойство <strong>showClearButton=true</strong> */
18
+ value?: string | Date | string[] | [Date, Date];
19
+ /** Колбек для клика по кнопке очистки */
20
+ onClearButtonClick?: MouseEventHandler<HTMLButtonElement>;
21
+ /** Контент выпадающего меню
22
+ <br>
23
+ <br> Принимает <strong>ReactNode</strong>
24
+ <br> Или функцию с аргументами:
25
+ <br> <strong>handleDroplistItemKeyDown</strong>: Обработчик нажатия клавиши на элемент выпадающего меню
26
+ <br> <strong>closeDroplist</strong>: Метод для закрытия выпадающего меню
27
+ */
28
+ children?:
29
+ | ReactNode
30
+ | ((props: {
31
+ handleDroplistItemKeyDown: ReturnType<typeof Droplist.useKeyboardNavigation>['handleDroplistItemKeyDown'];
32
+ closeDroplist(): void;
33
+ }) => ReactNode);
34
+ };
35
+
36
+ export function ChipChoiceCustom({
37
+ size = Size.S,
38
+ disabled,
39
+ loading,
40
+ icon,
41
+ label,
42
+ valueToRender,
43
+ value,
44
+ onClick,
45
+ className,
46
+ tabIndex = 0,
47
+ placement = Droplist.placements.BottomStart,
48
+ widthStrategy = Droplist.widthStrategies.Gte,
49
+ onClearButtonClick,
50
+ showClearButton: showClearButtonProp = true,
51
+ children,
52
+ 'data-test-id': testId,
53
+ ...rest
54
+ }: ChipChoiceCustomProps) {
55
+ const variant = icon && size !== Size.Xs ? Variant.IconBefore : Variant.LabelOnly;
56
+ const spinnerSize = size === Size.Xs ? Sun.sizes.XS : Sun.sizes.S;
57
+
58
+ const clearButtonRef = useRef<HTMLButtonElement>(null);
59
+
60
+ const [isDroplistOpened, setIsDroplistOpened] = useState(false);
61
+
62
+ const {
63
+ firstElementRefCallback,
64
+ triggerElementRef,
65
+ handleDroplistFocusLeave,
66
+ handleTriggerKeyDown,
67
+ handleDroplistItemKeyDown,
68
+ } = Droplist.useKeyboardNavigation<HTMLDivElement>({
69
+ setDroplistOpen: setIsDroplistOpened,
70
+ });
71
+
72
+ const handleChipClick: MouseEventHandler<HTMLDivElement> = e => {
73
+ if (loading || disabled) return;
74
+
75
+ onClick?.(e);
76
+
77
+ !isDroplistOpened && setIsDroplistOpened(true);
78
+ };
79
+
80
+ const handleChipKeyDown: KeyboardEventHandler<HTMLDivElement> = e => {
81
+ if (e.key === 'ArrowRight') {
82
+ clearButtonRef.current?.focus();
83
+ }
84
+
85
+ handleTriggerKeyDown(e);
86
+ };
87
+
88
+ const onOpenChangeHandler = (opened: boolean) => !opened && setIsDroplistOpened(false);
89
+
90
+ const closeDroplist = useCallback(() => {
91
+ setIsDroplistOpened(false);
92
+
93
+ // TODO: same bug as in FieldDate
94
+ setTimeout(() => {
95
+ triggerElementRef.current?.focus();
96
+ }, 0);
97
+ }, [triggerElementRef]);
98
+
99
+ const handleClearButtonClick: MouseEventHandler<HTMLButtonElement> = e => {
100
+ onClearButtonClick?.(e);
101
+ closeDroplist();
102
+ };
103
+
104
+ const handleClearButtonKeyDown: KeyboardEventHandler<HTMLButtonElement> = e => {
105
+ switch (e.key) {
106
+ case 'ArrowLeft': {
107
+ triggerElementRef.current?.focus();
108
+ return;
109
+ }
110
+ case 'Enter':
111
+ case ' ': {
112
+ e.stopPropagation();
113
+ return;
114
+ }
115
+ default:
116
+ break;
117
+ }
118
+ };
119
+
120
+ const showClearButton = showClearButtonProp && (value instanceof Date || Boolean(value?.length));
121
+
122
+ return (
123
+ <Droplist
124
+ trigger={Droplist.triggers.Click}
125
+ open={isDroplistOpened}
126
+ firstElementRefCallback={firstElementRefCallback}
127
+ onOpenChange={onOpenChangeHandler}
128
+ onFocusLeave={handleDroplistFocusLeave}
129
+ triggerClassName={styles.triggerClassName}
130
+ widthStrategy={widthStrategy}
131
+ placement={placement}
132
+ size={DROPLIST_SIZE_MAP[size]}
133
+ data-test-id={CHIP_CHOICE_TEST_IDS.droplist}
134
+ triggerElement={
135
+ <div
136
+ {...extractSupportProps(rest)}
137
+ role='button'
138
+ ref={triggerElementRef}
139
+ className={cn(styles.choiceChip, className)}
140
+ data-size={size}
141
+ data-variant={variant}
142
+ data-loading={loading || undefined}
143
+ data-test-id={testId || undefined}
144
+ data-disabled={(!loading && disabled) || undefined}
145
+ onClick={handleChipClick}
146
+ onKeyDown={handleChipKeyDown}
147
+ tabIndex={tabIndex}
148
+ >
149
+ {variant === Variant.IconBefore && (
150
+ <span className={styles.icon} data-test-id={CHIP_CHOICE_TEST_IDS.icon}>
151
+ {icon}
152
+ </span>
153
+ )}
154
+
155
+ <span className={styles.labelLayout}>
156
+ {label && (
157
+ <span className={styles.label} data-test-id={CHIP_CHOICE_TEST_IDS.label}>
158
+ {label}
159
+ </span>
160
+ )}
161
+
162
+ {loading ? (
163
+ <span className={styles.spinner} data-test-id={CHIP_CHOICE_TEST_IDS.spinner}>
164
+ <Sun size={spinnerSize} />
165
+ </span>
166
+ ) : (
167
+ <span className={styles.value} data-test-id={CHIP_CHOICE_TEST_IDS.value}>
168
+ {valueToRender}
169
+ </span>
170
+ )}
171
+ </span>
172
+
173
+ {!disabled && !loading && showClearButton && (
174
+ <ButtonClearValue
175
+ size={BUTTON_CLEAR_VALUE_SIZE_MAP[size]}
176
+ onClick={handleClearButtonClick}
177
+ data-test-id={CHIP_CHOICE_TEST_IDS.clearButton}
178
+ onKeyDown={handleClearButtonKeyDown}
179
+ ref={clearButtonRef}
180
+ />
181
+ )}
182
+ </div>
183
+ }
184
+ >
185
+ {typeof children === 'function' ? children({ handleDroplistItemKeyDown, closeDroplist }) : children}
186
+ </Droplist>
187
+ );
188
+ }
@@ -0,0 +1 @@
1
+ export * from './ChipChoiceCustom';
@@ -0,0 +1,90 @@
1
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-chips-chipChoice';
2
+ @import '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-element';
3
+ @import '../../styles.module';
4
+
5
+ $sizes: 'xs', 's', 'm', 'l';
6
+
7
+ $labelTypography: (
8
+ 'xs': $light-label-s,
9
+ 's': $light-label-m,
10
+ 'm': $light-label-m,
11
+ 'l': $light-label-l,
12
+ );
13
+
14
+ $valueTypography: (
15
+ 'xs': $sans-label-s,
16
+ 's': $sans-label-m,
17
+ 'm': $sans-label-m,
18
+ 'l': $sans-label-l,
19
+ );
20
+
21
+ .label, .value {
22
+ display: inline-flex;
23
+ align-items: center;
24
+ }
25
+
26
+ .choiceChip {
27
+ @include chip-defaults;
28
+ @include chip-anatomy-styles($chip-choice, $sizes, $labelTypography);
29
+
30
+ background-color: $sys-neutral-background1-level;
31
+ border-color: $sys-neutral-decor-default;
32
+
33
+ .label, .value {
34
+ color: $sys-neutral-text-support;
35
+ }
36
+
37
+ .icon {
38
+ color: $sys-neutral-text-light;
39
+ }
40
+
41
+ @each $size in $sizes {
42
+ &[data-size='#{$size}'] {
43
+ .value {
44
+ @include composite-var($valueTypography, $size);
45
+ }
46
+ }
47
+ }
48
+
49
+ &:hover, &:active, &:focus-visible {
50
+ background-color: $sys-neutral-background2-level;
51
+ border-color: $sys-neutral-decor-hovered;
52
+
53
+ .label {
54
+ color: $sys-neutral-text-support;
55
+ }
56
+
57
+ .value {
58
+ color: $sys-neutral-text-main;
59
+ }
60
+ }
61
+
62
+ &:focus-visible {
63
+ @include chip-outline;
64
+ }
65
+
66
+ &[data-disabled] {
67
+ cursor: not-allowed;
68
+ background-color: $sys-neutral-background;
69
+ border-color: $sys-neutral-decor-disabled;
70
+
71
+ .label, .value {
72
+ color: $sys-neutral-text-light;
73
+ }
74
+ }
75
+
76
+ &[data-loading] {
77
+ @include chip-loading-state($chip-choice, '&:not([data-label])', value, null, true);
78
+
79
+ background-color: $sys-neutral-background;
80
+ border-color: $sys-neutral-decor-activated;
81
+
82
+ .label, .value {
83
+ color: $sys-neutral-text-support;
84
+ }
85
+ }
86
+ }
87
+
88
+ .triggerClassName {
89
+ --offset: #{calc($border-state-focus-s-border-width + $space-drop-list-drop-offset)};
90
+ }
@@ -0,0 +1,60 @@
1
+ import { useUncontrolledProp } from 'uncontrollable';
2
+
3
+ import { Calendar } from '@snack-uikit/calendar';
4
+
5
+ import { DEFAULT_EMPTY_VALUE, Size } from '../../../constants';
6
+ import { CALENDAR_SIZE_MAP } from '../constants';
7
+ import { ChipChoiceCommonProps } from '../types';
8
+ import { ChipChoiceCustom } from './ChipChoiceCustom';
9
+
10
+ export type ChipChoiceDateProps = ChipChoiceCommonProps & {
11
+ /** Значение компонента */
12
+ value?: Date;
13
+ /** Значение компонента по-умолчанию */
14
+ defaultValue?: Date;
15
+ /** Колбек смены значения */
16
+ onChange?(value: Date): void;
17
+ /** Колбек формирующий строковое представление выбранного значения. Принимает выбранное значение */
18
+ valueFormatter?(value?: Date): string;
19
+ };
20
+
21
+ export function ChipChoiceDate({
22
+ size = Size.S,
23
+ value,
24
+ defaultValue,
25
+ onChange,
26
+ valueFormatter,
27
+ ...rest
28
+ }: ChipChoiceDateProps) {
29
+ const [selectedValue, setSelectedValue] = useUncontrolledProp<Date>(value, defaultValue, onChange);
30
+
31
+ const valueToRender = valueFormatter
32
+ ? valueFormatter(selectedValue)
33
+ : (selectedValue && new Date(selectedValue).toLocaleDateString()) || DEFAULT_EMPTY_VALUE;
34
+
35
+ const clearValue = () => setSelectedValue(undefined);
36
+
37
+ return (
38
+ <ChipChoiceCustom
39
+ onClearButtonClick={clearValue}
40
+ value={selectedValue}
41
+ valueToRender={valueToRender}
42
+ size={size}
43
+ {...rest}
44
+ >
45
+ {({ closeDroplist }) => (
46
+ <Calendar
47
+ mode={Calendar.modes.Date}
48
+ size={CALENDAR_SIZE_MAP[size]}
49
+ value={selectedValue}
50
+ onChangeValue={value => {
51
+ setSelectedValue(value);
52
+ closeDroplist();
53
+ }}
54
+ navigationStartRef={element => element?.focus()}
55
+ onFocusLeave={closeDroplist}
56
+ />
57
+ )}
58
+ </ChipChoiceCustom>
59
+ );
60
+ }
@@ -0,0 +1,69 @@
1
+ import { useUncontrolledProp } from 'uncontrollable';
2
+
3
+ import { Calendar } from '@snack-uikit/calendar';
4
+
5
+ import { DEFAULT_EMPTY_VALUE, Size } from '../../../constants';
6
+ import { CALENDAR_SIZE_MAP } from '../constants';
7
+ import { ChipChoiceCommonProps } from '../types';
8
+ import { ChipChoiceCustom } from './ChipChoiceCustom';
9
+
10
+ type Range = [Date, Date];
11
+
12
+ export type ChipChoiceDateRangeProps = ChipChoiceCommonProps & {
13
+ /** Значение компонента */
14
+ value?: Range;
15
+ /** Значение компонента по умолчанию */
16
+ defaultValue?: Range;
17
+ /** Колбек смены значения */
18
+ onChange?(value: Range): void;
19
+ /** Колбек формирующий строковое представление выбранного значения. Принимает массив выбранных значений */
20
+ valueFormatter?(value?: Range): string;
21
+ };
22
+
23
+ function defaultRangeFormatter(value?: Range) {
24
+ if (!value || !value.length) return DEFAULT_EMPTY_VALUE;
25
+
26
+ const [from, to] = value;
27
+
28
+ return `${from.toLocaleDateString()} ${DEFAULT_EMPTY_VALUE} ${to.toLocaleDateString()}`;
29
+ }
30
+
31
+ export function ChipChoiceDateRange({
32
+ size = Size.S,
33
+ value,
34
+ defaultValue,
35
+ onChange,
36
+ valueFormatter,
37
+ ...rest
38
+ }: ChipChoiceDateRangeProps) {
39
+ const [selectedValue, setSelectedValue] = useUncontrolledProp<Range>(value, defaultValue, onChange);
40
+
41
+ const valueToRender = valueFormatter ? valueFormatter(selectedValue) : defaultRangeFormatter(selectedValue);
42
+
43
+ const clearValue = () => setSelectedValue(undefined);
44
+
45
+ return (
46
+ <ChipChoiceCustom
47
+ value={selectedValue}
48
+ valueToRender={valueToRender}
49
+ onClearButtonClick={clearValue}
50
+ size={size}
51
+ {...rest}
52
+ >
53
+ {({ closeDroplist }) => (
54
+ <Calendar
55
+ mode={Calendar.modes.Range}
56
+ size={CALENDAR_SIZE_MAP[size]}
57
+ value={selectedValue}
58
+ onChangeValue={range => {
59
+ setSelectedValue(range);
60
+ closeDroplist();
61
+ }}
62
+ // bug with focus
63
+ // navigationStartRef={element => element?.focus()}
64
+ onFocusLeave={closeDroplist}
65
+ />
66
+ )}
67
+ </ChipChoiceCustom>
68
+ );
69
+ }
@@ -0,0 +1,87 @@
1
+ import { useMemo } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
3
+
4
+ import { Droplist } from '@snack-uikit/droplist';
5
+
6
+ import { Size } from '../../../constants';
7
+ import { ChipChoiceCommonProps, FilterOption } from '../types';
8
+ import { defaultMultiValueLabelFormatter, normalizeValueForMultiChoice } from '../utils';
9
+ import { ChipChoiceCustom } from './ChipChoiceCustom';
10
+
11
+ export type ChipChoiceMultiProps = ChipChoiceCommonProps & {
12
+ /** Массив опций */
13
+ options: FilterOption[];
14
+ /** Значение компонента */
15
+ value?: string[];
16
+ /** Значение компонента по умолчанию */
17
+ defaultValue?: string[];
18
+ /** Колбек смены значения */
19
+ onChange?(value: string[]): void;
20
+ /** Колбек формирующий строковое представление выбранного значения. Принимает массив выбранных значений. По умолчанию для отображения используется кол-во выбранных значений. */
21
+ valueFormatter?(options: FilterOption[]): string;
22
+ };
23
+
24
+ export function ChipChoiceMulti({
25
+ value,
26
+ defaultValue,
27
+ options,
28
+ onChange,
29
+ valueFormatter,
30
+ size = Size.S,
31
+ 'data-test-id': dataTestId,
32
+ ...rest
33
+ }: ChipChoiceMultiProps) {
34
+ const [selectedValue, setSelectedValue] = useUncontrolledProp<string[]>(
35
+ normalizeValueForMultiChoice(options, value),
36
+ normalizeValueForMultiChoice(options, defaultValue) || [],
37
+ onChange,
38
+ );
39
+
40
+ const selectedOptions = useMemo(
41
+ () => options.filter(({ value }) => selectedValue?.includes(value)),
42
+ [options, selectedValue],
43
+ );
44
+
45
+ const valueToRender = valueFormatter
46
+ ? valueFormatter(selectedOptions)
47
+ : defaultMultiValueLabelFormatter(selectedOptions, options.length);
48
+
49
+ const clearValue = () => setSelectedValue([]);
50
+
51
+ return (
52
+ <ChipChoiceCustom
53
+ size={size}
54
+ onClearButtonClick={clearValue}
55
+ valueToRender={valueToRender}
56
+ value={selectedValue}
57
+ data-test-id={dataTestId}
58
+ {...rest}
59
+ >
60
+ {({ handleDroplistItemKeyDown }) =>
61
+ options.map(({ label, value, ...rest }) => {
62
+ const selected = selectedValue || [];
63
+ const checked = selected.includes(value);
64
+ const onChangeHandler = () => {
65
+ if (checked) {
66
+ setSelectedValue(selected.filter(selected => selected !== value));
67
+ } else {
68
+ setSelectedValue([...selected, value]);
69
+ }
70
+ };
71
+
72
+ return (
73
+ <Droplist.ItemMultiple
74
+ {...rest}
75
+ key={value}
76
+ option={label}
77
+ checked={checked}
78
+ onChange={onChangeHandler}
79
+ onKeyDown={handleDroplistItemKeyDown}
80
+ data-test-id={dataTestId && `${dataTestId}__option`}
81
+ />
82
+ );
83
+ })
84
+ }
85
+ </ChipChoiceCustom>
86
+ );
87
+ }
@@ -0,0 +1,77 @@
1
+ import { useMemo } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
3
+
4
+ import { Droplist } from '@snack-uikit/droplist';
5
+
6
+ import { DEFAULT_EMPTY_VALUE, Size } from '../../../constants';
7
+ import { ChipChoiceCommonProps, FilterOption } from '../types';
8
+ import { normalizeValueForSingleChoice } from '../utils';
9
+ import { ChipChoiceCustom } from './ChipChoiceCustom';
10
+
11
+ export type ChipChoiceSingleProps = ChipChoiceCommonProps & {
12
+ /** Массив опций */
13
+ options: FilterOption[];
14
+ /** Значение компонента */
15
+ value?: string;
16
+ /** Значение компонента по умолчанию */
17
+ defaultValue?: string;
18
+ /** Колбек смены значения */
19
+ onChange?(value: string): void;
20
+ /** Колбек формирующий строковое представление выбранного значения. Принимает выбранное значение. По умолчанию для отображения используется FilterOption.label */
21
+ valueFormatter?(option?: FilterOption): string;
22
+ };
23
+
24
+ export function ChipChoiceSingle({
25
+ value,
26
+ defaultValue,
27
+ options,
28
+ onChange,
29
+ valueFormatter,
30
+ size = Size.S,
31
+ 'data-test-id': dataTestId,
32
+ ...rest
33
+ }: ChipChoiceSingleProps) {
34
+ const [selectedValue, setSelectedValue] = useUncontrolledProp<string>(
35
+ normalizeValueForSingleChoice(options, value),
36
+ normalizeValueForSingleChoice(options, defaultValue),
37
+ onChange,
38
+ );
39
+
40
+ const selectedOption = useMemo(() => options.find(({ value }) => value === selectedValue), [options, selectedValue]);
41
+
42
+ const valueToRender = valueFormatter ? valueFormatter(selectedOption) : selectedOption?.label || DEFAULT_EMPTY_VALUE;
43
+
44
+ const clearValue = () => setSelectedValue(undefined);
45
+
46
+ return (
47
+ <ChipChoiceCustom
48
+ onClearButtonClick={clearValue}
49
+ value={selectedValue}
50
+ valueToRender={valueToRender}
51
+ data-test-id={dataTestId}
52
+ size={size}
53
+ {...rest}
54
+ >
55
+ {({ handleDroplistItemKeyDown, closeDroplist }) =>
56
+ options.map(({ label, value, ...rest }) => {
57
+ const onChangeHandler = () => {
58
+ setSelectedValue(value);
59
+ closeDroplist();
60
+ };
61
+
62
+ return (
63
+ <Droplist.ItemSingle
64
+ {...rest}
65
+ key={value}
66
+ option={label}
67
+ checked={selectedValue === value}
68
+ onChange={onChangeHandler}
69
+ onKeyDown={handleDroplistItemKeyDown}
70
+ data-test-id={dataTestId && `${dataTestId}__option`}
71
+ />
72
+ );
73
+ })
74
+ }
75
+ </ChipChoiceCustom>
76
+ );
77
+ }
@@ -0,0 +1,5 @@
1
+ export * from './ChipChoiceCustom';
2
+ export * from './ChipChoiceSingle';
3
+ export * from './ChipChoiceMulti';
4
+ export * from './ChipChoiceDate';
5
+ export * from './ChipChoiceDateRange';
@@ -0,0 +1,32 @@
1
+ import { Calendar } from '@snack-uikit/calendar';
2
+ import { Droplist } from '@snack-uikit/droplist';
3
+
4
+ import { ButtonSize, Size } from '../../constants';
5
+
6
+ export const BUTTON_CLEAR_VALUE_SIZE_MAP = {
7
+ [Size.Xs]: ButtonSize.Xxs,
8
+ [Size.S]: ButtonSize.Xs,
9
+ [Size.M]: ButtonSize.Xs,
10
+ [Size.L]: ButtonSize.Xs,
11
+ };
12
+
13
+ export const CALENDAR_SIZE_MAP = {
14
+ [Size.Xs]: Calendar.sizes.S,
15
+ [Size.S]: Calendar.sizes.S,
16
+ [Size.M]: Calendar.sizes.M,
17
+ [Size.L]: Calendar.sizes.M,
18
+ };
19
+
20
+ export const DROPLIST_SIZE_MAP = {
21
+ [Size.Xs]: Droplist.sizes.S,
22
+ [Size.S]: Droplist.sizes.S,
23
+ [Size.M]: Droplist.sizes.M,
24
+ [Size.L]: Droplist.sizes.L,
25
+ };
26
+
27
+ export enum ChipChoiceType {
28
+ Multi = 'multi',
29
+ Date = 'date',
30
+ DateRange = 'date-range',
31
+ Single = 'single',
32
+ }
@@ -0,0 +1,39 @@
1
+ import { Droplist } from '@snack-uikit/droplist';
2
+
3
+ import { Size, Variant } from '../../constants';
4
+ import {
5
+ ChipChoiceCustom,
6
+ ChipChoiceCustomProps,
7
+ ChipChoiceDate,
8
+ ChipChoiceDateProps,
9
+ ChipChoiceDateRange,
10
+ ChipChoiceDateRangeProps,
11
+ ChipChoiceMulti,
12
+ ChipChoiceMultiProps,
13
+ ChipChoiceSingle,
14
+ ChipChoiceSingleProps,
15
+ } from './components';
16
+ import { ChipChoiceType } from './constants';
17
+
18
+ export type { FilterOption } from './types';
19
+
20
+ export type {
21
+ ChipChoiceCustomProps,
22
+ ChipChoiceMultiProps,
23
+ ChipChoiceSingleProps,
24
+ ChipChoiceDateProps,
25
+ ChipChoiceDateRangeProps,
26
+ };
27
+
28
+ export namespace ChipChoice {
29
+ export const Custom = ChipChoiceCustom;
30
+ export const Single = ChipChoiceSingle;
31
+ export const Multi = ChipChoiceMulti;
32
+ export const Date = ChipChoiceDate;
33
+ export const DateRange = ChipChoiceDateRange;
34
+ export const sizes = Size;
35
+ export const variants = Variant;
36
+ export const placements = Droplist.placements;
37
+ export const widthStrategies = Droplist.widthStrategies;
38
+ export const types = ChipChoiceType;
39
+ }