@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.
- package/CHANGELOG.md +483 -0
- package/LICENSE +201 -0
- package/README.md +226 -0
- package/dist/components/ChipAssist/ChipAssist.d.ts +15 -0
- package/dist/components/ChipAssist/ChipAssist.js +31 -0
- package/dist/components/ChipAssist/index.d.ts +1 -0
- package/dist/components/ChipAssist/index.js +1 -0
- package/dist/components/ChipAssist/styles.module.css +159 -0
- package/dist/components/ChipChoice/components/ChipChoiceCustom/ChipChoiceCustom.d.ts +23 -0
- package/dist/components/ChipChoice/components/ChipChoiceCustom/ChipChoiceCustom.js +75 -0
- package/dist/components/ChipChoice/components/ChipChoiceCustom/index.d.ts +1 -0
- package/dist/components/ChipChoice/components/ChipChoiceCustom/index.js +1 -0
- package/dist/components/ChipChoice/components/ChipChoiceCustom/styles.module.css +402 -0
- package/dist/components/ChipChoice/components/ChipChoiceDate.d.ts +12 -0
- package/dist/components/ChipChoice/components/ChipChoiceDate.js +29 -0
- package/dist/components/ChipChoice/components/ChipChoiceDateRange.d.ts +14 -0
- package/dist/components/ChipChoice/components/ChipChoiceDateRange.js +36 -0
- package/dist/components/ChipChoice/components/ChipChoiceMulti.d.ts +14 -0
- package/dist/components/ChipChoice/components/ChipChoiceMulti.js +42 -0
- package/dist/components/ChipChoice/components/ChipChoiceSingle.d.ts +14 -0
- package/dist/components/ChipChoice/components/ChipChoiceSingle.js +34 -0
- package/dist/components/ChipChoice/components/index.d.ts +5 -0
- package/dist/components/ChipChoice/components/index.js +5 -0
- package/dist/components/ChipChoice/constants.d.ts +25 -0
- package/dist/components/ChipChoice/constants.js +28 -0
- package/dist/components/ChipChoice/index.d.ts +17 -0
- package/dist/components/ChipChoice/index.js +17 -0
- package/dist/components/ChipChoice/styles.module.css +199 -0
- package/dist/components/ChipChoice/types.d.ts +27 -0
- package/dist/components/ChipChoice/types.js +1 -0
- package/dist/components/ChipChoice/utils.d.ts +4 -0
- package/dist/components/ChipChoice/utils.js +14 -0
- package/dist/components/ChipChoiceRow/ChipChoiceRow.d.ts +26 -0
- package/dist/components/ChipChoiceRow/ChipChoiceRow.js +45 -0
- package/dist/components/ChipChoiceRow/components/ForwardedChipChoice.d.ts +2 -0
- package/dist/components/ChipChoiceRow/components/ForwardedChipChoice.js +6 -0
- package/dist/components/ChipChoiceRow/components/constants.d.ts +6 -0
- package/dist/components/ChipChoiceRow/components/constants.js +7 -0
- package/dist/components/ChipChoiceRow/components/index.d.ts +1 -0
- package/dist/components/ChipChoiceRow/components/index.js +1 -0
- package/dist/components/ChipChoiceRow/constants.d.ts +15 -0
- package/dist/components/ChipChoiceRow/constants.js +18 -0
- package/dist/components/ChipChoiceRow/index.d.ts +1 -0
- package/dist/components/ChipChoiceRow/index.js +1 -0
- package/dist/components/ChipChoiceRow/styles.module.css +6 -0
- package/dist/components/ChipChoiceRow/types.d.ts +20 -0
- package/dist/components/ChipChoiceRow/types.js +1 -0
- package/dist/components/ChipToggle/ChipToggle.d.ts +17 -0
- package/dist/components/ChipToggle/ChipToggle.js +31 -0
- package/dist/components/ChipToggle/index.d.ts +1 -0
- package/dist/components/ChipToggle/index.js +1 -0
- package/dist/components/ChipToggle/styles.module.css +219 -0
- package/dist/components/index.d.ts +4 -0
- package/dist/components/index.js +4 -0
- package/dist/constants.d.ts +37 -0
- package/dist/constants.js +40 -0
- package/dist/helperComponents/ButtonClearValue/ButtonClearValue.d.ts +11 -0
- package/dist/helperComponents/ButtonClearValue/ButtonClearValue.js +12 -0
- package/dist/helperComponents/ButtonClearValue/index.d.ts +1 -0
- package/dist/helperComponents/ButtonClearValue/index.js +1 -0
- package/dist/helperComponents/ButtonClearValue/styles.module.css +43 -0
- package/dist/helperComponents/index.d.ts +1 -0
- package/dist/helperComponents/index.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/styles.module.css +0 -0
- package/dist/types.d.ts +15 -0
- package/dist/types.js +1 -0
- package/package.json +45 -0
- package/src/components/ChipAssist/ChipAssist.tsx +73 -0
- package/src/components/ChipAssist/index.ts +1 -0
- package/src/components/ChipAssist/styles.module.scss +60 -0
- package/src/components/ChipChoice/components/ChipChoiceCustom/ChipChoiceCustom.tsx +188 -0
- package/src/components/ChipChoice/components/ChipChoiceCustom/index.ts +1 -0
- package/src/components/ChipChoice/components/ChipChoiceCustom/styles.module.scss +90 -0
- package/src/components/ChipChoice/components/ChipChoiceDate.tsx +60 -0
- package/src/components/ChipChoice/components/ChipChoiceDateRange.tsx +69 -0
- package/src/components/ChipChoice/components/ChipChoiceMulti.tsx +87 -0
- package/src/components/ChipChoice/components/ChipChoiceSingle.tsx +77 -0
- package/src/components/ChipChoice/components/index.ts +5 -0
- package/src/components/ChipChoice/constants.ts +32 -0
- package/src/components/ChipChoice/index.ts +39 -0
- package/src/components/ChipChoice/styles.module.scss +90 -0
- package/src/components/ChipChoice/types.ts +33 -0
- package/src/components/ChipChoice/utils.ts +20 -0
- package/src/components/ChipChoiceRow/ChipChoiceRow.tsx +100 -0
- package/src/components/ChipChoiceRow/components/ForwardedChipChoice.tsx +10 -0
- package/src/components/ChipChoiceRow/components/constants.ts +8 -0
- package/src/components/ChipChoiceRow/components/index.ts +1 -0
- package/src/components/ChipChoiceRow/constants.ts +21 -0
- package/src/components/ChipChoiceRow/index.ts +1 -0
- package/src/components/ChipChoiceRow/styles.module.scss +8 -0
- package/src/components/ChipChoiceRow/types.ts +34 -0
- package/src/components/ChipToggle/ChipToggle.tsx +87 -0
- package/src/components/ChipToggle/index.ts +1 -0
- package/src/components/ChipToggle/styles.module.scss +153 -0
- package/src/components/index.ts +4 -0
- package/src/constants.ts +44 -0
- package/src/helperComponents/ButtonClearValue/ButtonClearValue.tsx +39 -0
- package/src/helperComponents/ButtonClearValue/index.ts +1 -0
- package/src/helperComponents/ButtonClearValue/styles.module.scss +50 -0
- package/src/helperComponents/index.ts +1 -0
- package/src/index.ts +1 -0
- package/src/styles.module.scss +114 -0
- 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,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
|
+
}
|