@snack-uikit/chips 0.24.1 → 0.24.2-preview-08c7f775.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 (76) hide show
  1. package/README.md +28 -8
  2. package/dist/cjs/components/ChipChoice/components/ChipChoiceBase/ChipChoiceBase.d.ts +3 -7
  3. package/dist/cjs/components/ChipChoice/components/ChipChoiceBase/ChipChoiceBase.js +3 -4
  4. package/dist/cjs/components/ChipChoice/components/ChipChoiceCustom.d.ts +1 -1
  5. package/dist/cjs/components/ChipChoice/components/ChipChoiceCustom.js +9 -9
  6. package/dist/cjs/components/ChipChoice/components/ChipChoiceDate.d.ts +1 -1
  7. package/dist/cjs/components/ChipChoice/components/ChipChoiceDate.js +9 -6
  8. package/dist/cjs/components/ChipChoice/components/ChipChoiceDateRange.d.ts +1 -1
  9. package/dist/cjs/components/ChipChoice/components/ChipChoiceDateRange.js +8 -6
  10. package/dist/cjs/components/ChipChoice/components/ChipChoiceMultiple.d.ts +1 -1
  11. package/dist/cjs/components/ChipChoice/components/ChipChoiceMultiple.js +8 -10
  12. package/dist/cjs/components/ChipChoice/components/ChipChoiceSingle.d.ts +1 -1
  13. package/dist/cjs/components/ChipChoice/components/ChipChoiceSingle.js +9 -9
  14. package/dist/cjs/components/ChipChoice/components/ChipChoiceTime.d.ts +1 -1
  15. package/dist/cjs/components/ChipChoice/components/ChipChoiceTime.js +9 -6
  16. package/dist/cjs/components/ChipChoice/constants.d.ts +1 -0
  17. package/dist/cjs/components/ChipChoice/constants.js +1 -0
  18. package/dist/cjs/components/ChipChoice/types.d.ts +7 -3
  19. package/dist/cjs/components/ChipChoiceRow/ChipChoiceRow.d.ts +12 -6
  20. package/dist/cjs/components/ChipChoiceRow/ChipChoiceRow.js +123 -24
  21. package/dist/cjs/components/ChipChoiceRow/components/constants.d.ts +1 -0
  22. package/dist/cjs/components/ChipChoiceRow/components/constants.js +1 -0
  23. package/dist/cjs/components/ChipChoiceRow/helpers.d.ts +5 -0
  24. package/dist/cjs/components/ChipChoiceRow/helpers.js +19 -0
  25. package/dist/cjs/components/ChipChoiceRow/index.d.ts +1 -0
  26. package/dist/cjs/components/ChipChoiceRow/index.js +2 -1
  27. package/dist/cjs/components/ChipChoiceRow/styles.module.css +21 -0
  28. package/dist/cjs/components/ChipChoiceRow/types.d.ts +5 -5
  29. package/dist/cjs/constants.d.ts +4 -1
  30. package/dist/cjs/constants.js +4 -1
  31. package/dist/esm/components/ChipChoice/components/ChipChoiceBase/ChipChoiceBase.d.ts +3 -7
  32. package/dist/esm/components/ChipChoice/components/ChipChoiceBase/ChipChoiceBase.js +2 -2
  33. package/dist/esm/components/ChipChoice/components/ChipChoiceCustom.d.ts +1 -1
  34. package/dist/esm/components/ChipChoice/components/ChipChoiceCustom.js +6 -9
  35. package/dist/esm/components/ChipChoice/components/ChipChoiceDate.d.ts +1 -1
  36. package/dist/esm/components/ChipChoice/components/ChipChoiceDate.js +6 -6
  37. package/dist/esm/components/ChipChoice/components/ChipChoiceDateRange.d.ts +1 -1
  38. package/dist/esm/components/ChipChoice/components/ChipChoiceDateRange.js +5 -6
  39. package/dist/esm/components/ChipChoice/components/ChipChoiceMultiple.d.ts +1 -1
  40. package/dist/esm/components/ChipChoice/components/ChipChoiceMultiple.js +4 -7
  41. package/dist/esm/components/ChipChoice/components/ChipChoiceSingle.d.ts +1 -1
  42. package/dist/esm/components/ChipChoice/components/ChipChoiceSingle.js +5 -8
  43. package/dist/esm/components/ChipChoice/components/ChipChoiceTime.d.ts +1 -1
  44. package/dist/esm/components/ChipChoice/components/ChipChoiceTime.js +6 -6
  45. package/dist/esm/components/ChipChoice/constants.d.ts +1 -0
  46. package/dist/esm/components/ChipChoice/constants.js +1 -0
  47. package/dist/esm/components/ChipChoice/types.d.ts +7 -3
  48. package/dist/esm/components/ChipChoiceRow/ChipChoiceRow.d.ts +12 -6
  49. package/dist/esm/components/ChipChoiceRow/ChipChoiceRow.js +69 -13
  50. package/dist/esm/components/ChipChoiceRow/components/constants.d.ts +1 -0
  51. package/dist/esm/components/ChipChoiceRow/components/constants.js +1 -0
  52. package/dist/esm/components/ChipChoiceRow/helpers.d.ts +5 -0
  53. package/dist/esm/components/ChipChoiceRow/helpers.js +13 -0
  54. package/dist/esm/components/ChipChoiceRow/index.d.ts +1 -0
  55. package/dist/esm/components/ChipChoiceRow/index.js +1 -0
  56. package/dist/esm/components/ChipChoiceRow/styles.module.css +21 -0
  57. package/dist/esm/components/ChipChoiceRow/types.d.ts +5 -5
  58. package/dist/esm/constants.d.ts +4 -1
  59. package/dist/esm/constants.js +4 -1
  60. package/package.json +7 -5
  61. package/src/components/ChipChoice/components/ChipChoiceBase/ChipChoiceBase.tsx +2 -7
  62. package/src/components/ChipChoice/components/ChipChoiceCustom.tsx +8 -9
  63. package/src/components/ChipChoice/components/ChipChoiceDate.tsx +8 -6
  64. package/src/components/ChipChoice/components/ChipChoiceDateRange.tsx +7 -7
  65. package/src/components/ChipChoice/components/ChipChoiceMultiple.tsx +6 -8
  66. package/src/components/ChipChoice/components/ChipChoiceSingle.tsx +7 -7
  67. package/src/components/ChipChoice/components/ChipChoiceTime.tsx +8 -6
  68. package/src/components/ChipChoice/constants.ts +1 -0
  69. package/src/components/ChipChoice/types.ts +7 -2
  70. package/src/components/ChipChoiceRow/ChipChoiceRow.tsx +174 -30
  71. package/src/components/ChipChoiceRow/components/constants.ts +1 -0
  72. package/src/components/ChipChoiceRow/helpers.ts +15 -0
  73. package/src/components/ChipChoiceRow/index.ts +1 -0
  74. package/src/components/ChipChoiceRow/styles.module.scss +27 -2
  75. package/src/components/ChipChoiceRow/types.ts +13 -4
  76. package/src/constants.ts +4 -1
@@ -1,4 +1,4 @@
1
- import { ReactNode, useCallback, useRef, useState } from 'react';
1
+ import { ReactNode, useCallback, useRef } from 'react';
2
2
  import { useUncontrolledProp } from 'uncontrollable';
3
3
 
4
4
  import { Calendar, CalendarProps } from '@snack-uikit/calendar';
@@ -47,6 +47,9 @@ export function ChipChoiceDateRange({
47
47
  valueRender,
48
48
  dropDownClassName,
49
49
  buildCalendarCellProps,
50
+ onClearButtonClick,
51
+ open: openProp,
52
+ onOpenChange,
50
53
  ...rest
51
54
  }: ChipChoiceDateRangeProps) {
52
55
  const [selectedValue, setSelectedValue] = useUncontrolledProp<Range>(value, defaultValue, onChange);
@@ -56,17 +59,14 @@ export function ChipChoiceDateRange({
56
59
  const valueToRender = valueRender
57
60
  ? valueRender(selectedValue)
58
61
  : defaultRangeFormatter({ value: selectedValue, allLabel: t('allLabel') });
59
-
60
- const clearValue = () => setSelectedValue(undefined);
61
-
62
62
  const localRef = useRef<HTMLDivElement>(null);
63
63
 
64
- const [open, setOpen] = useState<boolean>(false);
64
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
65
65
 
66
66
  const closeDroplist = useCallback(() => {
67
67
  setOpen(false);
68
68
  setTimeout(() => localRef.current?.focus(), 0);
69
- }, []);
69
+ }, [setOpen]);
70
70
 
71
71
  const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
72
72
 
@@ -98,7 +98,7 @@ export function ChipChoiceDateRange({
98
98
  <ChipChoiceBase
99
99
  {...rest}
100
100
  ref={localRef}
101
- onClearButtonClick={clearValue}
101
+ onClearButtonClick={onClearButtonClick}
102
102
  value={selectedValue}
103
103
  valueToRender={valueToRender}
104
104
  size={size}
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
2
3
 
3
4
  import { Droplist, ItemId, SelectionSingleValueType } from '@snack-uikit/list';
4
5
  import { useLocale } from '@snack-uikit/locale';
@@ -43,11 +44,13 @@ export function ChipChoiceMultiple<T extends ContentRenderProps = ContentRenderP
43
44
  searchable,
44
45
  contentRender,
45
46
  dropDownClassName,
46
- showClearButton = true,
47
+ onClearButtonClick,
47
48
  autoApply = true,
48
49
  disableFuzzySearch = false,
49
50
  onApprove,
50
51
  onCancel,
52
+ open: openProp,
53
+ onOpenChange,
51
54
  ...rest
52
55
  }: ChipChoiceMultipleProps<T>) {
53
56
  const [value, setValue] = useValueControl<SelectionSingleValueType[]>({
@@ -70,7 +73,7 @@ export function ChipChoiceMultiple<T extends ContentRenderProps = ContentRenderP
70
73
 
71
74
  const { t } = useLocale('Chips');
72
75
 
73
- const [open, setOpen] = useState<boolean>(false);
76
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
74
77
  const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
75
78
 
76
79
  const flatMapOptions = useMemo(() => Object.values(flattenOptions), [flattenOptions]);
@@ -98,10 +101,6 @@ export function ChipChoiceMultiple<T extends ContentRenderProps = ContentRenderP
98
101
  );
99
102
  const items = useMemo(() => transformOptionsToItems<T>(result, contentRender), [contentRender, result]);
100
103
 
101
- const clearValue = () => {
102
- setValue([]);
103
- setDeferredValue([]);
104
- };
105
104
  const chipRef = useRef<HTMLDivElement>(null);
106
105
  const listRef = useRef<HTMLElement>(null);
107
106
 
@@ -192,9 +191,8 @@ export function ChipChoiceMultiple<T extends ContentRenderProps = ContentRenderP
192
191
  <ChipChoiceBase
193
192
  {...rest}
194
193
  ref={chipRef}
195
- onClearButtonClick={clearValue}
194
+ onClearButtonClick={onClearButtonClick}
196
195
  value={value}
197
- showClearButton={showClearButton && !(Array.isArray(value) && [0].includes(value.length))}
198
196
  valueToRender={valueToRender}
199
197
  label={label}
200
198
  loading={rest.loading}
@@ -1,4 +1,5 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
2
3
 
3
4
  import { Droplist, ItemId, SelectionSingleValueType } from '@snack-uikit/list';
4
5
  import { useLocale } from '@snack-uikit/locale';
@@ -36,6 +37,9 @@ export function ChipChoiceSingle<T extends ContentRenderProps = ContentRenderPro
36
37
  autoApply = true,
37
38
  onApprove,
38
39
  onCancel,
40
+ onClearButtonClick,
41
+ open: openProp,
42
+ onOpenChange,
39
43
  ...rest
40
44
  }: ChipChoiceSingleProps<T>) {
41
45
  const [value, setValue] = useValueControl<SelectionSingleValueType>({
@@ -56,7 +60,7 @@ export function ChipChoiceSingle<T extends ContentRenderProps = ContentRenderPro
56
60
 
57
61
  const { t } = useLocale('Chips');
58
62
 
59
- const [open, setOpen] = useState<boolean>(false);
63
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
60
64
  const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
61
65
 
62
66
  const flatMapOptions = useMemo(() => Object.values(flattenOptions), [flattenOptions]);
@@ -82,10 +86,6 @@ export function ChipChoiceSingle<T extends ContentRenderProps = ContentRenderPro
82
86
  );
83
87
  const items = useMemo(() => transformOptionsToItems<T>(result, contentRender), [contentRender, result]);
84
88
 
85
- const clearValue = () => {
86
- setValue(undefined);
87
- setDeferredValue(undefined);
88
- };
89
89
  const chipRef = useRef<HTMLDivElement>(null);
90
90
 
91
91
  const handleSelectionChange = useCallback(
@@ -102,7 +102,7 @@ export function ChipChoiceSingle<T extends ContentRenderProps = ContentRenderPro
102
102
  }
103
103
  }
104
104
  },
105
- [autoApply, setValue, setDeferredValue],
105
+ [autoApply, setOpen, setValue, setDeferredValue],
106
106
  );
107
107
 
108
108
  const handleOnCancelClick = () => {
@@ -175,7 +175,7 @@ export function ChipChoiceSingle<T extends ContentRenderProps = ContentRenderPro
175
175
  <ChipChoiceBase
176
176
  {...rest}
177
177
  ref={chipRef}
178
- onClearButtonClick={clearValue}
178
+ onClearButtonClick={onClearButtonClick}
179
179
  value={value}
180
180
  valueToRender={valueToRender}
181
181
  label={label}
@@ -1,4 +1,5 @@
1
- import { ReactNode, useCallback, useMemo, useRef, useState } from 'react';
1
+ import { ReactNode, useCallback, useMemo, useRef } from 'react';
2
+ import { useUncontrolledProp } from 'uncontrollable';
2
3
 
3
4
  import { TimePicker, TimePickerProps } from '@snack-uikit/calendar';
4
5
  import { Dropdown } from '@snack-uikit/dropdown';
@@ -50,19 +51,22 @@ export function ChipChoiceTime({
50
51
  dropDownClassName,
51
52
  showSeconds = true,
52
53
  placement,
54
+ onClearButtonClick,
55
+ open: openProp,
56
+ onOpenChange,
53
57
  ...rest
54
58
  }: ChipChoiceTimeProps) {
55
59
  const [selectedValue, setSelectedValue] = useValueControl<TimeValue>({ value, defaultValue, onChange });
56
60
 
57
61
  const localRef = useRef<HTMLDivElement>(null);
58
62
 
59
- const [open, setOpen] = useState<boolean>(false);
63
+ const [open, setOpen] = useUncontrolledProp(openProp, false, onOpenChange);
60
64
  const handleOnKeyDown = useHandleOnKeyDown({ setOpen });
61
65
 
62
66
  const closeDroplist = useCallback(() => {
63
67
  setOpen(false);
64
68
  setTimeout(() => localRef.current?.focus(), 0);
65
- }, []);
69
+ }, [setOpen]);
66
70
 
67
71
  const { t } = useLocale('Chips');
68
72
 
@@ -76,8 +80,6 @@ export function ChipChoiceTime({
76
80
  return getStringTimeValue(selectedValue, { showSeconds, locale: DEFAULT_LOCALE });
77
81
  }, [selectedValue, showSeconds, t, valueRender]);
78
82
 
79
- const clearValue = () => setSelectedValue(undefined);
80
-
81
83
  const handleChangeValue = useCallback(
82
84
  (value: TimeValue) => {
83
85
  setSelectedValue(value);
@@ -114,7 +116,7 @@ export function ChipChoiceTime({
114
116
  <ChipChoiceBase
115
117
  {...rest}
116
118
  ref={localRef}
117
- onClearButtonClick={clearValue}
119
+ onClearButtonClick={onClearButtonClick}
118
120
  value={selectedValue}
119
121
  valueToRender={valueToRender}
120
122
  size={size}
@@ -44,6 +44,7 @@ export const CHIP_CHOICE_TYPE = {
44
44
  Multiple: 'multiple',
45
45
  Date: 'date',
46
46
  DateTime: 'date-time',
47
+ Time: 'time',
47
48
  DateRange: 'date-range',
48
49
  Single: 'single',
49
50
  Custom: 'custom',
@@ -79,8 +79,8 @@ export type ChipChoiceCommonProps = WithSupportProps<
79
79
  * @default false
80
80
  */
81
81
  disableFuzzySearch?: boolean;
82
- /** Отображение кнопки очистки значения @default true*/
83
- showClearButton?: boolean;
82
+ /** Колбек для клика по кнопке очистки */
83
+ onClearButtonClick?: MouseEventHandler<HTMLButtonElement>;
84
84
  /** Расположение выпадающего меню */
85
85
  placement?: DropdownProps['placement'];
86
86
  /**
@@ -92,6 +92,10 @@ export type ChipChoiceCommonProps = WithSupportProps<
92
92
  */
93
93
  widthStrategy?: DropdownProps['widthStrategy'];
94
94
  dropDownClassName?: string;
95
+ /** Управляет состоянием показан/не показан. */
96
+ open?: boolean;
97
+ /** Колбек отображения компонента. Срабатывает при изменении состояния open. */
98
+ onOpenChange?: (isOpen: boolean) => void;
95
99
  }
96
100
  >;
97
101
 
@@ -123,6 +127,7 @@ export type ChipChoiceSelectCommonProps<T extends ContentRenderProps = ContentRe
123
127
  | 'noResultsState'
124
128
  | 'loading'
125
129
  | 'scrollToSelectedItem'
130
+ | 'virtualized'
126
131
  >;
127
132
 
128
133
  export type ChipChoiceSingleProps<T extends ContentRenderProps = ContentRenderProps> = ChipChoiceSelectCommonProps<T> &
@@ -1,9 +1,13 @@
1
1
  import cn from 'classnames';
2
+ import React, { useCallback, useEffect, useMemo, useRef, useState } from 'react';
2
3
  import { useUncontrolledProp } from 'uncontrollable';
3
4
 
4
5
  import { ButtonFunction } from '@snack-uikit/button';
5
- import { CrossSVG } from '@snack-uikit/icons';
6
+ import { Divider } from '@snack-uikit/divider';
7
+ import { CrossSVG, PlusSVG } from '@snack-uikit/icons';
8
+ import { Droplist, DroplistProps } from '@snack-uikit/list';
6
9
  import { useLocale } from '@snack-uikit/locale';
10
+ import { Tooltip } from '@snack-uikit/tooltip';
7
11
  import { extractSupportProps, WithSupportProps } from '@snack-uikit/utils';
8
12
 
9
13
  import { CHIP_CHOICE_ROW_IDS } from '../../constants';
@@ -14,7 +18,9 @@ import { ChipChoiceProps, ChipChoiceRowSize, FilterValue, OmitBetter } from './t
14
18
 
15
19
  export type FiltersState = Record<string, unknown>;
16
20
 
17
- export type ChipChoiceRowFilter = OmitBetter<ChipChoiceProps, 'onChange' | 'value' | 'size' | 'defaultValue'>;
21
+ export type ChipChoiceRowFilter = OmitBetter<ChipChoiceProps, 'onChange' | 'value' | 'size' | 'defaultValue'> & {
22
+ pinned?: boolean;
23
+ };
18
24
 
19
25
  export type ChipChoiceRowProps<TState extends FiltersState> = WithSupportProps<{
20
26
  /** Состояние фильтров */
@@ -29,32 +35,51 @@ export type ChipChoiceRowProps<TState extends FiltersState> = WithSupportProps<{
29
35
  size?: ChipChoiceRowSize;
30
36
  /** CSS-класс */
31
37
  className?: string;
32
- /** Скрыть/показать кнопку очиски @default true */
33
- showClearAllButton?: boolean;
34
- /** Текст кнопки очистки @default 'Clear all' */
35
- clearAllButtonLabel?: string;
38
+ /** Скрыть/показать кнопку очиски фильтров @default true */
39
+ showClearButton?: boolean;
40
+ /** Скрыть/показать кнопку добавления фильров @default true */
41
+ showAddButton?: boolean;
42
+ /** Состояние для видимых фильтров */
43
+ visibleFilters?: string[];
44
+ /** Коллбек на изменение видимых фильтров */
45
+ onVisibleFiltersChange?(value: string[]): void;
36
46
  }>;
37
47
 
38
48
  export function ChipChoiceRow<TState extends FiltersState>({
39
49
  filters,
40
50
  onChange,
41
- showClearAllButton = true,
42
- clearAllButtonLabel: clearAllButtonLabelProp,
51
+ showClearButton = true,
52
+ showAddButton = true,
43
53
  className,
44
54
  value,
45
- defaultValue,
55
+ defaultValue: defaultValueProp,
46
56
  size = CHIP_CHOICE_ROW_SIZE.S,
57
+ visibleFilters: visibleFiltersProp,
58
+ onVisibleFiltersChange,
47
59
  ...rest
48
60
  }: ChipChoiceRowProps<TState>) {
49
61
  const { t } = useLocale('Chips');
50
62
 
51
- const clearAllButtonLabel = clearAllButtonLabelProp ?? t('clearAllButton');
63
+ const defaultValue = useMemo(() => (defaultValueProp ?? {}) as TState, [defaultValueProp]);
52
64
 
53
- const [state, setState] = useUncontrolledProp<TState>(value, (defaultValue ?? {}) as TState, newState => {
65
+ const [state, setState] = useUncontrolledProp<TState>(value, defaultValue, newState => {
54
66
  const result = typeof newState === 'function' ? newState(state) : newState;
55
67
  onChange?.(result);
56
68
  });
57
69
 
70
+ const [addListValue, setAddListValue] = useUncontrolledProp<string[]>(
71
+ visibleFiltersProp,
72
+ Object.keys(state),
73
+ newState => {
74
+ const result = typeof newState === 'function' ? newState(addListValue) : newState;
75
+ onVisibleFiltersChange?.(result);
76
+ },
77
+ );
78
+
79
+ const [openedChip, setOpenedChip] = useState<string>('');
80
+
81
+ const [addListOpen, setAddListOpen] = useState(false);
82
+
58
83
  const handleChange = (fieldId: string, value: FilterValue) => {
59
84
  setState((state: TState) => ({
60
85
  ...state,
@@ -62,44 +87,163 @@ export function ChipChoiceRow<TState extends FiltersState>({
62
87
  }));
63
88
  };
64
89
 
90
+ const handleChipOpen = useCallback(
91
+ (filterId: string) => (isOpen: boolean) => {
92
+ setOpenedChip(isOpen ? filterId : '');
93
+ },
94
+ [],
95
+ );
96
+
65
97
  const handleFiltersClear = () => {
66
- setState({} as TState);
98
+ const defaultState = filters.reduce((res, filter) => {
99
+ if (filter.pinned) {
100
+ return { ...res, [filter.id]: defaultValue[filter.id] } as TState;
101
+ }
102
+
103
+ return res;
104
+ }, {} as TState);
105
+
106
+ setState(defaultState);
107
+ setAddListValue([]);
67
108
  };
68
109
 
69
- const hasAnyFilter = Object.values(state).some(filter => {
70
- if (Array.isArray(filter)) {
71
- return filter.length > 0 && Object.values(filter).some(Boolean);
110
+ const pinnedFilters = useMemo(() => filters.filter(filter => filter.pinned), [filters]);
111
+ const nonPinnedFilters = useMemo(() => filters.filter(filter => !filter.pinned), [filters]);
112
+
113
+ const visibleFilters = useMemo(
114
+ () =>
115
+ addListValue
116
+ .map(filterId => nonPinnedFilters.find(filter => filter.id === filterId))
117
+ .filter((item): item is ChipChoiceRowFilter => Boolean(item)),
118
+ [addListValue, nonPinnedFilters],
119
+ );
120
+
121
+ const hasAnyFilter = useMemo(
122
+ () => visibleFilters.length > 0 || pinnedFilters.some(filter => state[filter.id] !== defaultValue[filter.id]),
123
+ [defaultValue, pinnedFilters, state, visibleFilters.length],
124
+ );
125
+
126
+ const handleClearPinnedFilter = (filterId: string) => {
127
+ const defaultFilterValue = defaultValue[filterId];
128
+
129
+ if (state[filterId] === defaultFilterValue) {
130
+ return;
72
131
  }
73
132
 
74
- if (filter && typeof filter === 'object') {
75
- return Object.values(filter).some(Boolean) || filter instanceof Date;
133
+ return () => setState((prevState: TState) => ({ ...prevState, [filterId]: defaultFilterValue }));
134
+ };
135
+
136
+ const handleRemoveVisibleFilter = (filterId: string) => () => {
137
+ setAddListValue((prev?: string[]) => prev?.filter(item => filterId !== item));
138
+ setState((prevState: TState) => ({ ...prevState, [filterId]: undefined }));
139
+ };
140
+
141
+ const addSelectorOptions: DroplistProps['items'] = useMemo(
142
+ () =>
143
+ nonPinnedFilters
144
+ .filter(filter => !addListValue.includes(filter.id))
145
+ .map((filter, index) => ({
146
+ id: filter.id,
147
+ content: { option: filter.label ?? filter.id },
148
+ onClick: () => {
149
+ setAddListValue(function (prevValue?: string[]) {
150
+ return [...(prevValue ?? []), filter.id];
151
+ });
152
+ setAddListOpen(false);
153
+ },
154
+ 'data-test-id': `${CHIP_CHOICE_ROW_IDS.addButtonOption}-${filter['data-test-id'] ?? index}`,
155
+ })),
156
+ [addListValue, nonPinnedFilters, setAddListValue],
157
+ );
158
+
159
+ const canAddChips = addSelectorOptions.length > 0;
160
+
161
+ const addListPrevValue = useRef(addListValue);
162
+
163
+ useEffect(() => {
164
+ const prevValue = addListPrevValue.current;
165
+
166
+ if (addListValue.length > prevValue.length) {
167
+ const newItem = addListValue.find(item => !prevValue.includes(item));
168
+
169
+ if (newItem) {
170
+ handleChipOpen(newItem)(true);
171
+ }
76
172
  }
77
173
 
78
- return Boolean(filter);
79
- });
174
+ addListPrevValue.current = addListValue;
175
+ }, [addListValue, handleChipOpen]);
80
176
 
81
177
  return (
82
178
  <div className={cn(styles.chipChoiceRow, className)} {...extractSupportProps(rest)}>
83
- {filters.map(filter => (
179
+ {pinnedFilters.length > 0 && (
180
+ <div className={styles.pinnedItems}>
181
+ {pinnedFilters.map(filter => (
182
+ <ForwardedChipChoice
183
+ key={filter.id}
184
+ {...filter}
185
+ value={state[filter.id] as never}
186
+ size={MAP_ROW_SIZE_TO_CHOICE_SIZE[size]}
187
+ onChange={(value: FilterValue) => handleChange(filter.id, value)}
188
+ onClearButtonClick={handleClearPinnedFilter(filter.id)}
189
+ />
190
+ ))}
191
+
192
+ <Divider orientation='vertical' className={styles.divider} />
193
+ </div>
194
+ )}
195
+
196
+ {visibleFilters.map(filter => (
84
197
  <ForwardedChipChoice
85
198
  key={filter.id}
86
199
  {...filter}
87
200
  value={state[filter.id] as never}
88
201
  size={MAP_ROW_SIZE_TO_CHOICE_SIZE[size]}
89
202
  onChange={(value: FilterValue) => handleChange(filter.id, value)}
203
+ onClearButtonClick={handleRemoveVisibleFilter(filter.id)}
204
+ open={openedChip === filter.id}
205
+ onOpenChange={handleChipOpen(filter.id)}
90
206
  />
91
207
  ))}
92
208
 
93
- {showClearAllButton && hasAnyFilter && (
94
- <ButtonFunction
95
- onClick={handleFiltersClear}
96
- label={clearAllButtonLabel}
97
- icon={<CrossSVG />}
98
- iconPosition='before'
99
- size={MAP_ROW_SIZE_TO_BUTTON_SIZE[size]}
100
- data-test-id={CHIP_CHOICE_ROW_IDS.clearAllButton}
101
- />
102
- )}
209
+ <div className={styles.controlWrapper}>
210
+ {showAddButton && (
211
+ <Tooltip
212
+ tip={t('addButtonDisabledTip')}
213
+ open={canAddChips ? false : undefined}
214
+ placement='bottom'
215
+ data-test-id={CHIP_CHOICE_ROW_IDS.addButtonTooltip}
216
+ >
217
+ <Droplist
218
+ open={canAddChips && addListOpen}
219
+ onOpenChange={setAddListOpen}
220
+ items={addSelectorOptions}
221
+ triggerClassName={styles.addButtonWrapper}
222
+ trigger='clickAndFocusVisible'
223
+ >
224
+ <ButtonFunction
225
+ disabled={!canAddChips}
226
+ label={t('add')}
227
+ icon={<PlusSVG />}
228
+ iconPosition='before'
229
+ size={MAP_ROW_SIZE_TO_BUTTON_SIZE[size]}
230
+ data-test-id={CHIP_CHOICE_ROW_IDS.addButton}
231
+ />
232
+ </Droplist>
233
+ </Tooltip>
234
+ )}
235
+
236
+ {showClearButton && hasAnyFilter && (
237
+ <ButtonFunction
238
+ onClick={handleFiltersClear}
239
+ label={t('clear')}
240
+ icon={<CrossSVG />}
241
+ iconPosition='before'
242
+ size={MAP_ROW_SIZE_TO_BUTTON_SIZE[size]}
243
+ data-test-id={CHIP_CHOICE_ROW_IDS.clearButton}
244
+ />
245
+ )}
246
+ </div>
103
247
  </div>
104
248
  );
105
249
  }
@@ -6,6 +6,7 @@ export const MAP_CHIP_TYPE_TO_COMPONENT = {
6
6
  [CHIP_CHOICE_TYPE.Multiple]: ChipChoice.Multiple,
7
7
  [CHIP_CHOICE_TYPE.Date]: ChipChoice.Date,
8
8
  [CHIP_CHOICE_TYPE.DateTime]: ChipChoice.Date,
9
+ [CHIP_CHOICE_TYPE.Time]: ChipChoice.Time,
9
10
  [CHIP_CHOICE_TYPE.DateRange]: ChipChoice.DateRange,
10
11
  [CHIP_CHOICE_TYPE.Custom]: ChipChoice.Custom,
11
12
  };
@@ -0,0 +1,15 @@
1
+ /**
2
+ * функция проверки заполненности фильтра
3
+ * @function hasFilterBeenApplied
4
+ */
5
+ export function hasFilterBeenApplied(filter: unknown): boolean {
6
+ if (Array.isArray(filter)) {
7
+ return filter.length > 0 && Object.values(filter).some(Boolean);
8
+ }
9
+
10
+ if (filter && typeof filter === 'object') {
11
+ return Object.values(filter).some(Boolean) || filter instanceof Date;
12
+ }
13
+
14
+ return Boolean(filter);
15
+ }
@@ -1 +1,2 @@
1
1
  export * from './ChipChoiceRow';
2
+ export * from './helpers';
@@ -1,8 +1,33 @@
1
- @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-chips-chipChoice';
1
+ @use '@snack-uikit/figma-tokens/build/scss/components/styles-tokens-chips-chipChoice' as chips;
2
2
 
3
3
  .chipChoiceRow {
4
- @include styles-tokens-chips-chipChoice.composite-var(styles-tokens-chips-chipChoice.$chip-choice-row-container);
4
+ @include chips.composite-var(chips.$chip-choice-row-container);
5
5
 
6
6
  display: flex;
7
7
  flex-wrap: wrap;
8
+ }
9
+
10
+ .pinnedItems {
11
+ @include chips.composite-var(chips.$chip-choice-row-pinned);
12
+
13
+ display: flex;
14
+ flex-wrap: wrap;
15
+ //height: min-content;
16
+ }
17
+
18
+ .controlWrapper {
19
+ @include chips.composite-var(chips.$chip-choice-row-control-wrapper);
20
+
21
+ display: flex;
22
+ flex-wrap: nowrap;
23
+ //height: min-content;
24
+ }
25
+
26
+ .divider {
27
+ align-self: stretch;
28
+ height: auto;
29
+ }
30
+
31
+ .addButtonWrapper {
32
+ display: inline-flex;
8
33
  }
@@ -1,10 +1,14 @@
1
1
  import { ValueOf } from '@snack-uikit/utils';
2
2
 
3
- import { ChipChoiceCustomProps } from '../ChipChoice/components';
4
- import { ChipChoiceDateProps } from '../ChipChoice/components/ChipChoiceDate';
5
- import { ChipChoiceDateRangeProps } from '../ChipChoice/components/ChipChoiceDateRange';
3
+ import {
4
+ ChipChoiceCustomProps,
5
+ ChipChoiceDateProps,
6
+ ChipChoiceDateRangeProps,
7
+ ChipChoiceMultipleProps,
8
+ ChipChoiceSingleProps,
9
+ ChipChoiceTimeProps,
10
+ } from '../ChipChoice';
6
11
  import { CHIP_CHOICE_TYPE } from '../ChipChoice/constants';
7
- import { ChipChoiceMultipleProps, ChipChoiceSingleProps } from '../ChipChoice/types';
8
12
  import { CHIP_CHOICE_ROW_SIZE } from './constants';
9
13
 
10
14
  export type ChipChoiceRowSize = ValueOf<typeof CHIP_CHOICE_ROW_SIZE>;
@@ -25,6 +29,10 @@ type ChipChoiceDateTimeType = {
25
29
  type: typeof CHIP_CHOICE_TYPE.DateTime;
26
30
  } & Omit<ChipChoiceDateProps, 'mode'> & { mode: 'date-time'; showSeconds?: boolean };
27
31
 
32
+ type ChipChoiceTimeType = {
33
+ type: typeof CHIP_CHOICE_TYPE.Time;
34
+ } & ChipChoiceTimeProps;
35
+
28
36
  type ChipChoiceDateRangeType = {
29
37
  type: typeof CHIP_CHOICE_TYPE.DateRange;
30
38
  } & ChipChoiceDateRangeProps;
@@ -40,6 +48,7 @@ export type ChipChoiceProps = {
40
48
  | ChipChoiceSingleType
41
49
  | ChipChoiceDateType
42
50
  | ChipChoiceDateTimeType
51
+ | ChipChoiceTimeType
43
52
  | ChipChoiceDateRangeType
44
53
  | ChipChoiceCustomType
45
54
  );
package/src/constants.ts CHANGED
@@ -34,7 +34,10 @@ export const CHIP_CHOICE_TEST_IDS = {
34
34
  };
35
35
 
36
36
  export const CHIP_CHOICE_ROW_IDS = {
37
- clearAllButton: 'chip-choice-row__clear-all-button',
37
+ clearButton: 'chip-choice-row__clear-button',
38
+ addButton: 'chip-choice-row__add-button',
39
+ addButtonTooltip: 'chip-choice-row__add-button-tooltip',
40
+ addButtonOption: 'chip-choice-row__add-button-option',
38
41
  };
39
42
 
40
43
  export const CHIP_TOGGLE_TEST_IDS = {