@true-engineering/true-react-common-ui-kit 4.0.0-alpha5 → 4.0.0-alpha7

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 (94) hide show
  1. package/README.md +117 -4
  2. package/dist/components/Button/Button.styles.d.ts +1 -1
  3. package/dist/components/Checkbox/Checkbox.styles.d.ts +1 -1
  4. package/dist/components/DatePicker/DatePicker.d.ts +2 -2
  5. package/dist/components/DatePicker/helpers.d.ts +3 -0
  6. package/dist/components/DatePicker/types.d.ts +3 -1
  7. package/dist/components/FiltersPane/FiltersPane.d.ts +4 -2
  8. package/dist/components/FiltersPane/FiltersPane.stories.d.ts +3 -3
  9. package/dist/components/FiltersPane/components/Filter/Filter.d.ts +2 -2
  10. package/dist/components/FiltersPane/components/Filter/helpers.d.ts +4 -0
  11. package/dist/components/FiltersPane/components/FilterInterval/FilterInterval.styles.d.ts +1 -1
  12. package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.styles.d.ts +1 -1
  13. package/dist/components/FiltersPane/components/FilterValueView/FilterValueView.d.ts +3 -1
  14. package/dist/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.d.ts +2 -2
  15. package/dist/components/FiltersPane/components/FilterWrapper/FilterWrapper.d.ts +2 -2
  16. package/dist/components/FiltersPane/types.d.ts +15 -5
  17. package/dist/components/Flag/customFlags/customFlags.d.ts +10 -0
  18. package/dist/components/Flag/customFlags/index.d.ts +1 -0
  19. package/dist/components/FlexibleTable/FlexibleTable.d.ts +4 -2
  20. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +6 -6
  21. package/dist/components/FlexibleTable/constants.d.ts +18 -2
  22. package/dist/components/FlexibleTable/types.d.ts +1 -1
  23. package/dist/components/SearchInput/SearchInput.d.ts +2 -2
  24. package/dist/components/SearchInput/SearchInput.stories.d.ts +3 -2
  25. package/dist/components/Select/Select.d.ts +5 -3
  26. package/dist/components/Select/Select.styles.d.ts +2 -2
  27. package/dist/components/Tooltip/Tooltip.d.ts +1 -1
  28. package/dist/components/Tooltip/Tooltip.styles.d.ts +1 -1
  29. package/dist/components/WithMessages/WithMessages.styles.d.ts +1 -1
  30. package/dist/hooks/index.d.ts +1 -0
  31. package/dist/hooks/use-merge.d.ts +1 -0
  32. package/dist/hooks/use-mixed-styles.d.ts +3 -1
  33. package/dist/hooks/use-tweak-styles.d.ts +5 -5
  34. package/dist/theme/Provider.d.ts +6 -3
  35. package/dist/theme/create-themed-styles.d.ts +2 -0
  36. package/dist/theme/helpers.d.ts +9 -3
  37. package/dist/theme/index.d.ts +2 -0
  38. package/dist/theme/true-jss/ThemedStylesManager.d.ts +18 -0
  39. package/dist/theme/true-jss/TweakStylesManager.d.ts +34 -0
  40. package/dist/theme/true-jss/index.d.ts +2 -0
  41. package/dist/theme/true-jss/jss-context.d.ts +9 -0
  42. package/dist/theme/types.d.ts +4 -2
  43. package/dist/true-react-common-ui-kit.js +6736 -5801
  44. package/dist/true-react-common-ui-kit.js.map +1 -1
  45. package/dist/true-react-common-ui-kit.umd.cjs +6866 -5932
  46. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  47. package/dist/types.d.ts +2 -1
  48. package/package.json +1 -1
  49. package/src/components/DatePicker/DatePicker.tsx +9 -4
  50. package/src/components/DatePicker/helpers.ts +13 -1
  51. package/src/components/DatePicker/types.ts +4 -1
  52. package/src/components/FiltersPane/FiltersPane.stories.tsx +4 -2
  53. package/src/components/FiltersPane/FiltersPane.tsx +14 -9
  54. package/src/components/FiltersPane/components/Filter/Filter.tsx +24 -17
  55. package/src/components/FiltersPane/components/Filter/helpers.ts +18 -0
  56. package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +27 -22
  57. package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.ts +1 -0
  58. package/src/components/FiltersPane/components/FilterWithPeriod/FilterWithPeriod.tsx +1 -1
  59. package/src/components/FiltersPane/components/FilterWrapper/FilterWrapper.tsx +7 -5
  60. package/src/components/FiltersPane/types.ts +23 -5
  61. package/src/components/Flag/Flag.stories.tsx +2 -1
  62. package/src/components/Flag/Flag.styles.ts +4 -0
  63. package/src/components/Flag/Flag.tsx +23 -9
  64. package/src/components/Flag/customFlags/AB.svg +1 -0
  65. package/src/components/Flag/customFlags/OS.svg +1 -0
  66. package/src/components/Flag/customFlags/augment.d.ts +1 -0
  67. package/src/components/Flag/customFlags/customFlags.ts +13 -0
  68. package/src/components/Flag/customFlags/index.ts +1 -0
  69. package/src/components/FlexibleTable/FlexibleTable.tsx +13 -12
  70. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +9 -8
  71. package/src/components/FlexibleTable/constants.ts +6 -3
  72. package/src/components/FlexibleTable/types.ts +1 -5
  73. package/src/components/PhoneInput/PhoneInput.stories.tsx +2 -1
  74. package/src/components/PhoneInput/PhoneInput.tsx +5 -2
  75. package/src/components/SearchInput/SearchInput.tsx +23 -28
  76. package/src/components/Select/Select.tsx +11 -2
  77. package/src/components/Tooltip/Tooltip.styles.ts +2 -0
  78. package/src/components/Tooltip/Tooltip.tsx +1 -1
  79. package/src/constants/phone-info.ts +20 -33
  80. package/src/helpers/phone.ts +19 -15
  81. package/src/hooks/index.ts +1 -0
  82. package/src/hooks/use-merge.ts +8 -0
  83. package/src/hooks/use-mixed-styles.ts +9 -11
  84. package/src/hooks/use-tweak-styles.ts +49 -27
  85. package/src/theme/Provider.tsx +10 -5
  86. package/src/theme/create-themed-styles.ts +78 -0
  87. package/src/theme/helpers.ts +39 -39
  88. package/src/theme/index.ts +2 -0
  89. package/src/theme/true-jss/ThemedStylesManager.ts +92 -0
  90. package/src/theme/true-jss/TweakStylesManager.ts +157 -0
  91. package/src/theme/true-jss/index.ts +2 -0
  92. package/src/theme/true-jss/jss-context.tsx +34 -0
  93. package/src/theme/types.ts +4 -2
  94. package/src/types.ts +2 -1
package/dist/types.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  import { FocusEventHandler, type KeyboardEvent, KeyboardEventHandler, MouseEvent, MouseEventHandler, PointerEventHandler, ReactNode } from 'react';
2
2
  import { Modifier, Placement } from 'react-overlays/usePopper';
3
+ import type { IMaybeArray } from './theme';
3
4
  export interface ITestIdProps {
4
5
  testId?: string;
5
6
  }
@@ -10,7 +11,7 @@ export interface IDataAttributesProps extends ITestIdProps {
10
11
  data?: IDataAttributes;
11
12
  }
12
13
  export interface ITweakStylesProps<TweakStyles> {
13
- tweakStyles?: TweakStyles;
14
+ tweakStyles?: IMaybeArray<TweakStyles>;
14
15
  }
15
16
  export type ICommonProps<TweakStyles> = IDataAttributesProps & ITweakStylesProps<TweakStyles>;
16
17
  export interface IDropdownWithPopperOptions {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@true-engineering/true-react-common-ui-kit",
3
- "version": "4.0.0-alpha5",
3
+ "version": "4.0.0-alpha7",
4
4
  "description": "True Engineering React UI Kit with theming support",
5
5
  "author": "True Engineering (https://trueengineering.ru)",
6
6
  "keywords": [
@@ -14,13 +14,18 @@ import { ICommonProps } from '../../types';
14
14
  import { DateInput, EMPTY_DATE_INPUT_VALUE, IDateInputProps } from '../DateInput';
15
15
  import { DatePickerHeader, PopperContainer } from './components';
16
16
  import { DatePickerComponent, DEFAULT_DATE_FORMAT } from './constants';
17
- import { areDatesEquals, getDateFormatter, getDateValueParser } from './helpers';
18
- import { IDatePickerBaseProps, IRange } from './types';
17
+ import {
18
+ areDatesEquals,
19
+ getDateFormatter,
20
+ getDateValueParser,
21
+ preparateDatePickerLocale,
22
+ } from './helpers';
23
+ import { IDatePickerBaseProps, IDatePickerLocale, IRange } from './types';
19
24
  import { IDatePickerStyles, useStyles } from './DatePicker.styles';
20
25
 
21
26
  export interface IDatePickerProps extends IDatePickerBaseProps, ICommonProps<IDatePickerStyles> {
22
27
  selectedDate?: Date | null;
23
- locale: Locale;
28
+ locale: IDatePickerLocale;
24
29
  months?: string[];
25
30
  /** @default 'dd.MM.yyyy' */
26
31
  dateFormat?: string;
@@ -242,7 +247,7 @@ export const DatePicker = forwardRef<ReactDatePicker, IDatePickerProps>(
242
247
  ref={ref}
243
248
  minDate={minDate}
244
249
  maxDate={maxDate}
245
- locale={locale}
250
+ locale={preparateDatePickerLocale(locale)}
246
251
  dateFormat={dateFormat}
247
252
  placeholderText={placeholder}
248
253
  calendarStartDay={calendarStartDay}
@@ -1,10 +1,12 @@
1
- import { parse, format, isSameDay } from 'date-fns';
1
+ import { parse, format, isSameDay, type Locale } from 'date-fns';
2
+ import { ru as ruLocale, enUS as enLocale } from 'date-fns/locale';
2
3
  import {
3
4
  isEmpty,
4
5
  isNotEmpty,
5
6
  isStringNotEmpty,
6
7
  } from '@true-engineering/true-react-platform-helpers';
7
8
  import { EMPTY_DATE_INPUT_VALUE } from '../DateInput';
9
+ import { IDatePickerLocale } from './types';
8
10
 
9
11
  export const getDateFormatter =
10
12
  (dateFormat: string) =>
@@ -21,3 +23,13 @@ export const getDateValueParser =
21
23
  export const areDatesEquals = (date1?: Date | null, date2?: Date | null): boolean =>
22
24
  (isEmpty(date1) && isEmpty(date2)) ||
23
25
  (isNotEmpty(date1) && isNotEmpty(date2) && isSameDay(date1, date2));
26
+
27
+ export const preparateDatePickerLocale = (locale: IDatePickerLocale): Locale => {
28
+ if (locale === 'ru') {
29
+ return ruLocale;
30
+ }
31
+ if (locale === 'en') {
32
+ return enLocale;
33
+ }
34
+ return locale;
35
+ };
@@ -1,8 +1,11 @@
1
1
  import { ReactDatePickerProps } from 'react-datepicker';
2
- import { IDateInputProps } from '../DateInput';
2
+ import { type Locale } from 'date-fns';
3
+ import { type IDateInputProps } from '../DateInput';
3
4
 
4
5
  export type IRange = [Date | null, Date | null] | null;
5
6
 
7
+ export type IDatePickerLocale = 'ru' | 'en' | Locale;
8
+
6
9
  export type IDatePickerBaseProps = Pick<
7
10
  ReactDatePickerProps,
8
11
  | 'startDate'
@@ -70,7 +70,8 @@ type ConfigValues = {
70
70
  custom: string;
71
71
  };
72
72
 
73
- interface IFiltersPaneWithCustomProps<Values, Content> extends IFiltersPaneProps<Values, Content> {
73
+ interface IFiltersPaneWithCustomProps<Values extends Record<string, unknown>, Content>
74
+ extends IFiltersPaneProps<Values, Content> {
74
75
  containerWidth: number;
75
76
  isSearchDisabled: boolean;
76
77
  shouldShowSettingsButton: boolean;
@@ -80,7 +81,7 @@ interface IFiltersPaneWithCustomProps<Values, Content> extends IFiltersPaneProps
80
81
  isClearableFields: boolean;
81
82
  }
82
83
 
83
- function FiltersPaneWithCustomProps<Values, Content>({
84
+ function FiltersPaneWithCustomProps<Values extends Record<string, unknown>, Content>({
84
85
  containerWidth,
85
86
  isSearchDisabled,
86
87
  shouldShowSettingsButton,
@@ -288,6 +289,7 @@ Default.args = {
288
289
  hasClearButton: true,
289
290
  isClearableFields: false,
290
291
  isDisabled: false,
292
+ shouldRenderDataId: false,
291
293
  containerWidth: 400,
292
294
  withFieldNameInLabel: true,
293
295
  isGroupingEnabled: true,
@@ -10,7 +10,7 @@ import { getLocale } from './helpers';
10
10
  import { ConfigType, IFilterLocaleKey, IPartialFilterLocale } from './types';
11
11
  import { useStyles, IFiltersPaneStyles, clearButtonStyles } from './FiltersPane.styles';
12
12
 
13
- export interface IFiltersPaneProps<Values, Content = Values>
13
+ export interface IFiltersPaneProps<Values extends Record<string, unknown>, Content = Values>
14
14
  extends ICommonProps<IFiltersPaneStyles> {
15
15
  filtersConfig: ConfigType<Values>;
16
16
  enabledFilters?: Array<keyof ConfigType<Values>>;
@@ -23,6 +23,8 @@ export interface IFiltersPaneProps<Values, Content = Values>
23
23
  isDisabled?: boolean;
24
24
  /** @default true */
25
25
  hasClearButton?: boolean;
26
+ /** @default false */
27
+ shouldRenderDataId?: boolean;
26
28
  onChangeFilters: (values: Partial<Values>) => void;
27
29
  onSettingsButtonClick?: () => void;
28
30
  onClear?: () => void;
@@ -39,6 +41,7 @@ export function FiltersPane<Values extends Record<string, unknown>, Content = Va
39
41
  search,
40
42
  isDisabled = false,
41
43
  hasClearButton = true,
44
+ shouldRenderDataId = false,
42
45
  testId,
43
46
  onChangeFilters,
44
47
  onSettingsButtonClick,
@@ -61,7 +64,7 @@ export function FiltersPane<Values extends Record<string, unknown>, Content = Va
61
64
 
62
65
  const translates = useMemo(() => getLocale(localeKey, locale), [localeKey, locale]);
63
66
 
64
- const filtersKeys = enabledFilters || Object.keys(filtersConfig);
67
+ const filtersKeys = enabledFilters ?? Object.keys(filtersConfig);
65
68
 
66
69
  const handleClear = () => {
67
70
  if (onClear !== undefined) {
@@ -122,10 +125,11 @@ export function FiltersPane<Values extends Record<string, unknown>, Content = Va
122
125
  {/* Filters */}
123
126
  {filtersKeys.map((key, index) => {
124
127
  const isLast = index === filtersKeys.length - 1;
125
- const currentValue = values[key];
126
- const filter = filtersConfig[key];
128
+ const filterKey = String(key);
129
+ const currentValue = values[filterKey];
130
+ const filter = filtersConfig[filterKey];
127
131
  if (filter === undefined) {
128
- console.error(`enabledFilters содержит фильтр ${String(key)}, не описанный в конфиге`);
132
+ console.error(`enabledFilters содержит фильтр ${filterKey}, не описанный в конфиге`);
129
133
  if (isLast) {
130
134
  return clearButton;
131
135
  }
@@ -137,18 +141,19 @@ export function FiltersPane<Values extends Record<string, unknown>, Content = Va
137
141
  filter={filter}
138
142
  locale={locale}
139
143
  localeKey={localeKey}
140
- onChange={(value) => onChangeFilters({ ...values, [key]: value })}
144
+ onChange={(value) => onChangeFilters({ ...values, [filterKey]: value })}
141
145
  value={currentValue}
142
- key={key as string}
146
+ key={filterKey}
143
147
  isDisabled={isDisabled || filter?.requiredFilledFilters?.some((item) => !values[item])}
144
148
  tweakStyles={tweakFilterWrapperStyles}
145
- testId={getTestId(testId, `filter-${String(key)}`)}
149
+ data={shouldRenderDataId ? { id: filterKey } : undefined}
150
+ testId={getTestId(testId, `filter-${filterKey}`)}
146
151
  />
147
152
  );
148
153
 
149
154
  if (isLast) {
150
155
  return (
151
- <div className={classes.filterWithClearButton} key={key as string}>
156
+ <div className={classes.filterWithClearButton} key={filterKey}>
152
157
  {filterWrapper}
153
158
  {shouldShowClearButton && <>{clearButton}</>}
154
159
  </div>
@@ -1,20 +1,20 @@
1
1
  import { useMemo } from 'react';
2
2
  import { getLocale } from '../../helpers';
3
- import { IFilterMultiSelectValues, IFilterWithDatesValue, IPeriod } from '../../types';
4
3
  import { FilterInterval } from '../FilterInterval';
5
4
  import { FilterMultiSelect } from '../FilterMultiSelect';
6
5
  import { FilterSelect } from '../FilterSelect';
7
6
  import { FilterWithDates } from '../FilterWithDates';
8
7
  import { FilterWithPeriod } from '../FilterWithPeriod';
9
8
  import type { IFilterWrapperProps } from '../FilterWrapper';
9
+ import { isDatePeriodValue, isMultiSelectValue, isPeriodValue } from './helpers';
10
10
 
11
- export interface IFilterProps<Values, Key extends keyof Values>
11
+ export interface IFilterProps<Values extends Record<string, unknown>, Key extends keyof Values>
12
12
  extends IFilterWrapperProps<Values, Key> {
13
13
  onChange: <V>(v: V) => void;
14
14
  onClose?: () => void;
15
15
  }
16
16
 
17
- export function Filter<Values, Key extends keyof Values>(
17
+ export function Filter<Values extends Record<string, unknown>, Key extends keyof Values>(
18
18
  props: IFilterProps<Values, Key>,
19
19
  ): JSX.Element | null {
20
20
  const { filter, value, onChange, onClose, localeKey, locale, testId } = props;
@@ -39,9 +39,11 @@ export function Filter<Values, Key extends keyof Values>(
39
39
  }
40
40
 
41
41
  if (filter.type === 'dateRange') {
42
+ const preparedValue = isPeriodValue(value) ? { ...value } : undefined;
43
+
42
44
  return (
43
45
  <FilterWithPeriod
44
- value={{ ...value } as IPeriod}
46
+ value={preparedValue}
45
47
  onChange={onChange}
46
48
  onClose={onClose}
47
49
  localeKey={translatesLocaleKey}
@@ -53,25 +55,27 @@ export function Filter<Values, Key extends keyof Values>(
53
55
  }
54
56
 
55
57
  if (filter.type === 'dateRangeWithoutPeriod') {
58
+ const preparedValue = isDatePeriodValue(value) ? value : undefined;
59
+
56
60
  return (
57
- <div style={{ width: 320 }}>
58
- <FilterWithDates
59
- value={value as unknown as IFilterWithDatesValue}
60
- onChange={(v) => onChange({ ...v, periodType: 'CUSTOM' })}
61
- onEndBtnSubmit={() => onChange(undefined)}
62
- localeKey={translatesLocaleKey}
63
- locale={translates}
64
- testId={testId !== undefined ? `${testId}-dates` : undefined}
65
- {...filter}
66
- />
67
- </div>
61
+ <FilterWithDates
62
+ value={preparedValue}
63
+ onChange={(v) => onChange({ ...v, periodType: 'CUSTOM' })}
64
+ onEndBtnSubmit={() => onChange(undefined)}
65
+ localeKey={translatesLocaleKey}
66
+ locale={translates}
67
+ testId={testId !== undefined ? `${testId}-dates` : undefined}
68
+ {...filter}
69
+ />
68
70
  );
69
71
  }
70
72
 
71
73
  if (filter.type === 'multiSelect') {
74
+ const preparedValue = isMultiSelectValue<any>(value) ? value : undefined;
75
+
72
76
  return (
73
77
  <FilterMultiSelect
74
- value={value as unknown as IFilterMultiSelectValues<any>}
78
+ value={preparedValue}
75
79
  onChange={onChange}
76
80
  onClose={onClose}
77
81
  localeKey={translatesLocaleKey}
@@ -83,9 +87,11 @@ export function Filter<Values, Key extends keyof Values>(
83
87
  }
84
88
 
85
89
  if (filter.type === 'interval') {
90
+ const preparedValue = Array.isArray(value) ? (value as number[]) : undefined;
91
+
86
92
  return (
87
93
  <FilterInterval
88
- value={value as unknown as number[]}
94
+ value={preparedValue}
89
95
  onChange={onChange}
90
96
  localeKey={translatesLocaleKey}
91
97
  locale={translates}
@@ -102,6 +108,7 @@ export function Filter<Values, Key extends keyof Values>(
102
108
 
103
109
  if (filter.type === 'custom' && filter.component) {
104
110
  const Component = filter.component;
111
+
105
112
  return <Component {...props} filter={filter} />;
106
113
  }
107
114
 
@@ -0,0 +1,18 @@
1
+ import { isEmpty, isString } from '@true-engineering/true-react-platform-helpers';
2
+ import { IDatePeriod, IFilterMultiSelectValues, IPeriod } from '../../types';
3
+
4
+ const isDateOrEmpty = (value: unknown): value is Date | null | undefined =>
5
+ isEmpty(value) || value instanceof Date;
6
+
7
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
8
+ export const isDatePeriodValue = (value: any): value is IDatePeriod =>
9
+ isDateOrEmpty(value?.from) && isDateOrEmpty(value?.to);
10
+
11
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
12
+ export const isPeriodValue = (value: any): value is IPeriod =>
13
+ isString(value?.periodType) && isDatePeriodValue(value);
14
+
15
+ export const isMultiSelectValue = <T extends string>(
16
+ // eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types, @typescript-eslint/no-explicit-any
17
+ value: any,
18
+ ): value is IFilterMultiSelectValues<T> => Array.isArray(value?.include);
@@ -9,14 +9,17 @@ import { IDateRangeConfigItem, IFilterWithDatesValue, IPeriod } from '../../type
9
9
  import type { IFilterWrapperProps } from '../FilterWrapper';
10
10
  import { IFilterValueViewStyles, useStyles } from './FilterValueView.styles';
11
11
 
12
- export function FilterValueView<Values, Key extends keyof Values>({
12
+ export interface IFilterValueView<Values extends Record<string, unknown>, Key extends keyof Values>
13
+ extends Omit<IFilterWrapperProps<Values, Key>, 'filtersPaneRef' | 'tweakStyles' | 'onChange'>,
14
+ ICommonProps<IFilterValueViewStyles> {}
15
+
16
+ export function FilterValueView<Values extends Record<string, unknown>, Key extends keyof Values>({
17
+ value,
18
+ filter,
13
19
  locale,
14
20
  localeKey,
15
- filter,
16
- value,
17
21
  tweakStyles,
18
- }: Omit<IFilterWrapperProps<Values, Key>, 'onChange' | 'filtersPaneRef' | 'tweakStyles'> &
19
- ICommonProps<IFilterValueViewStyles>): JSX.Element {
22
+ }: IFilterValueView<Values, Key>): JSX.Element {
20
23
  const classes = useStyles({ theme: tweakStyles });
21
24
 
22
25
  const translatesLocaleKey = filter.localeKey ?? localeKey;
@@ -29,7 +32,7 @@ export function FilterValueView<Values, Key extends keyof Values>({
29
32
  return <></>;
30
33
  }
31
34
 
32
- if (filter.getSelectedValueView !== undefined) {
35
+ if (isNotEmpty(filter.getSelectedValueView)) {
33
36
  return <span className={classes.text}>{filter.getSelectedValueView(value)}</span>;
34
37
  }
35
38
 
@@ -94,7 +97,7 @@ export function FilterValueView<Values, Key extends keyof Values>({
94
97
  const intervalValueFrom = intervalValue[0];
95
98
  const intervalValueTo = intervalValue[1];
96
99
 
97
- const intervals = [];
100
+ const intervals: string[] = [];
98
101
  if (intervalValueFrom !== undefined) {
99
102
  intervals.push(`${translates.from.toLowerCase()} ${String(intervalValueFrom)}`);
100
103
  }
@@ -105,19 +108,6 @@ export function FilterValueView<Values, Key extends keyof Values>({
105
108
  return <span className={classes.text}>{intervals.join(' ')}</span>;
106
109
  }
107
110
 
108
- if (isMultiple) {
109
- return (
110
- <>
111
- {Array.isArray(value) && value.length > 0 && (
112
- <>
113
- <span className={classes.text}>{displayValue(value[0])}</span>
114
- <span className={classes.count}>{value.length > 1 && ` (+${value.length - 1})`}</span>
115
- </>
116
- )}
117
- </>
118
- );
119
- }
120
-
121
111
  if (isDate) {
122
112
  const { from, to, periodType, label } = value as unknown as IPeriod;
123
113
  const hasFrom = from !== undefined && from !== null;
@@ -127,7 +117,7 @@ export function FilterValueView<Values, Key extends keyof Values>({
127
117
  return <span className={classes.text}>{displayValue(label)}</span>;
128
118
  }
129
119
 
130
- const range = [];
120
+ const range: string[] = [];
131
121
  if (hasFrom) {
132
122
  if (!hasTo) {
133
123
  range.push(translates.from.toLowerCase());
@@ -146,12 +136,27 @@ export function FilterValueView<Values, Key extends keyof Values>({
146
136
  return <span className={classes.text}>{range.join(' ')}</span>;
147
137
  }
148
138
 
139
+ if (isMultiple) {
140
+ const convertValue = filter.getSelectedValue ?? displayValue;
141
+
142
+ return (
143
+ <>
144
+ {Array.isArray(value) && value.length > 0 && (
145
+ <>
146
+ <span className={classes.text}>{convertValue(value[0])}</span>
147
+ <span className={classes.count}>{value.length > 1 && ` (+${value.length - 1})`}</span>
148
+ </>
149
+ )}
150
+ </>
151
+ );
152
+ }
153
+
149
154
  if (isRange && Array.isArray(value)) {
150
155
  const rangeValue = value as unknown as number[];
151
156
  const rangeValueFrom = rangeValue[0];
152
157
  const rangeValueTo = rangeValue[1];
153
158
 
154
- const range = [];
159
+ const range: string[] = [];
155
160
  if (rangeValueFrom !== undefined) {
156
161
  range.push(`${translates.from.toLowerCase()} ${String(rangeValueFrom)}`);
157
162
  }
@@ -6,6 +6,7 @@ import { innerTextButtonStyles } from '../../FiltersPane.styles';
6
6
 
7
7
  export const useStyles = createThemedStyles('FilterWithDates', {
8
8
  root: {
9
+ width: 320,
9
10
  background: colors.CLASSIC_WHITE,
10
11
  position: 'relative',
11
12
  zIndex: 20,
@@ -151,7 +151,7 @@ export const FilterWithPeriod: FC<IFilterWithPeriodProps> = ({
151
151
  </div>
152
152
  )}
153
153
  {isDatePickerShown && (
154
- <div className={classes.picker} style={{ width: 320 }} ref={refDatePicker}>
154
+ <div className={classes.picker} ref={refDatePicker}>
155
155
  <FilterWithDates
156
156
  onStartBtnSubmit={() => {
157
157
  setIsDatePickerShown(false);
@@ -12,8 +12,10 @@ import { FilterValueView } from '../FilterValueView';
12
12
  import { isContentNotEmpty } from './helpers';
13
13
  import { useStyles, IFilterWrapperStyles } from './FilterWrapper.styles';
14
14
 
15
- export interface IFilterWrapperProps<Values, Key extends keyof Values>
16
- extends ICommonProps<IFilterWrapperStyles> {
15
+ export interface IFilterWrapperProps<
16
+ Values extends Record<string, unknown>,
17
+ Key extends keyof Values,
18
+ > extends ICommonProps<IFilterWrapperStyles> {
17
19
  filter: ConfigItem<Values[Key]>;
18
20
  value?: Values[Key];
19
21
  isDisabled?: boolean;
@@ -22,7 +24,7 @@ export interface IFilterWrapperProps<Values, Key extends keyof Values>
22
24
  onChange: <V>(value: V) => void;
23
25
  }
24
26
 
25
- export function FilterWrapper<Values, Key extends keyof Values>({
27
+ export function FilterWrapper<Values extends Record<string, unknown>, Key extends keyof Values>({
26
28
  filter,
27
29
  value,
28
30
  isDisabled,
@@ -105,8 +107,8 @@ export function FilterWrapper<Values, Key extends keyof Values>({
105
107
  <FilterValueView
106
108
  value={value}
107
109
  filter={filter}
108
- localeKey={localeKey}
109
110
  locale={locale}
111
+ localeKey={localeKey}
110
112
  testId={getTestId(testId, 'value')}
111
113
  tweakStyles={tweakFilterValueViewStyles}
112
114
  />
@@ -137,8 +139,8 @@ export function FilterWrapper<Values, Key extends keyof Values>({
137
139
  <Filter
138
140
  value={value}
139
141
  filter={filter}
140
- localeKey={localeKey}
141
142
  locale={locale}
143
+ localeKey={localeKey}
142
144
  onClose={onClose}
143
145
  onChange={onChange}
144
146
  testId={testId}
@@ -84,22 +84,40 @@ export type IDateRangeConfigItem<Value> = IConfigItemBasicBase<Value> & {
84
84
  dateFormat?: string;
85
85
  } & Omit<IFilterWithPeriodProps, 'value' | 'onChange' | 'setIsOpen'>;
86
86
 
87
- export type CustomComponent<Value> = FC<{
87
+ export interface ICustomComponentProps<Value> {
88
88
  value?: Value;
89
89
  onChange: (v?: Value) => void;
90
90
  onClose?: () => void;
91
91
  filter: ICustomConfigItem<Value>;
92
92
  localeKey?: IFilterLocaleKey;
93
93
  locale?: IPartialFilterLocale;
94
- }>;
94
+ }
95
+
96
+ export type CustomComponent<Value> = FC<ICustomComponentProps<Value>>;
97
+
98
+ export type ICustomValue<V> = V extends Array<infer T> ? T : never;
99
+
100
+ export interface ICustomRangeConfigItem<Value> extends IConfigItemBasicBase<Value> {
101
+ // eslint-disable-next-line @typescript-eslint/ban-types
102
+ [key: string & {}]: any;
103
+ type: 'custom';
104
+ component: CustomComponent<Value>;
105
+ valueViewType?: 'range';
106
+ }
95
107
 
96
- export interface ICustomConfigItem<Value> extends IConfigItemBasicBase<Value> {
97
- [key: string]: any;
108
+ export interface ICustomMultipleConfigItem<Value> extends IConfigItemBasicBase<Value> {
109
+ // eslint-disable-next-line @typescript-eslint/ban-types
110
+ [key: string & {}]: any;
98
111
  type: 'custom';
99
112
  component: CustomComponent<Value>;
100
- valueViewType?: 'range' | 'multiple';
113
+ valueViewType?: 'multiple';
114
+ getSelectedValue?: (v: ICustomValue<Value>) => ReactNode;
101
115
  }
102
116
 
117
+ export type ICustomConfigItem<Value> =
118
+ | ICustomRangeConfigItem<Value>
119
+ | ICustomMultipleConfigItem<Value>;
120
+
103
121
  export type ConfigItem<Value> =
104
122
  | ISelectConfigItem<Value>
105
123
  | IMultiSelectConfigItem<Value>
@@ -1,12 +1,13 @@
1
1
  import { countries } from 'country-flag-icons';
2
2
  import { ComponentStory } from '@storybook/react';
3
3
  import { Flag } from './Flag';
4
+ import { customFlags } from './customFlags';
4
5
 
5
6
  export default {
6
7
  title: 'Data Display/Flag',
7
8
  component: Flag,
8
9
  argTypes: {
9
- countryCode: { control: 'select', options: countries },
10
+ countryCode: { control: 'select', options: [...countries, ...Object.keys(customFlags)] },
10
11
  },
11
12
  };
12
13
 
@@ -2,6 +2,10 @@ import { colors, ITweakStyles, createThemedStyles } from '../../theme';
2
2
 
3
3
  export const useStyles = createThemedStyles('Flag', {
4
4
  root: {
5
+ display: 'flex',
6
+ width: '100%',
7
+ height: '100%',
8
+ boxSizing: 'border-box',
5
9
  // приходится хардкодить в компоненте, тк либа Flags выдает флаги с 2-3 пиксельным отступом снизу
6
10
  // если будет нужно, то можно вынести border на уровень пропсов
7
11
  border: [1, 'solid', colors.BORDER_MAIN],
@@ -3,6 +3,7 @@ import { hasFlag } from 'country-flag-icons';
3
3
  import Flags from 'country-flag-icons/react/3x2';
4
4
  import { ICommonProps } from '../../types';
5
5
  import { Icon } from '../Icon';
6
+ import { customFlags } from './customFlags';
6
7
  import { useStyles, IFlagStyles } from './Flag.styles';
7
8
 
8
9
  export interface IFlagProps extends Pick<ICommonProps<IFlagStyles>, 'tweakStyles'> {
@@ -12,15 +13,28 @@ export interface IFlagProps extends Pick<ICommonProps<IFlagStyles>, 'tweakStyles
12
13
 
13
14
  export const Flag: FC<IFlagProps> = ({ countryCode = '', tweakStyles }) => {
14
15
  const classes = useStyles({ theme: tweakStyles });
15
- const CC = countryCode.toUpperCase();
16
+ const countryFlagKey = countryCode.toUpperCase();
16
17
 
17
- const TheFlag = hasFlag(CC)
18
- ? Flags[CC as keyof typeof Flags]
19
- : () => (
20
- <div className={classes.noFlag}>
21
- <Icon type="minus" />
22
- </div>
23
- );
18
+ const hasFlagInLibrary = hasFlag(countryFlagKey);
24
19
 
25
- return <TheFlag className={classes.root} />;
20
+ if (hasFlagInLibrary) {
21
+ const FlagComponent = Flags[countryFlagKey as keyof typeof Flags];
22
+ return (
23
+ <div className={classes.root}>
24
+ <FlagComponent />
25
+ </div>
26
+ );
27
+ }
28
+
29
+ const CustomFlag = customFlags[countryFlagKey as keyof typeof customFlags];
30
+
31
+ if (CustomFlag !== undefined) {
32
+ return <div className={classes.root} dangerouslySetInnerHTML={{ __html: CustomFlag }} />;
33
+ }
34
+
35
+ return (
36
+ <div className={classes.noFlag}>
37
+ <Icon type="minus" />
38
+ </div>
39
+ );
26
40
  };
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 512 341.3"><path fill="#FFF" d="M0 0h512v341.3H0z"/><g fill="#6DA544"><path d="M0 0h512v48.8H0zM0 97.5h512v48.8H0zM0 195h512v48.8H0zM0 292.6h512v48.8H0z"/></g><path fill="#D80027" d="M0 0h256v146.3H0z"/><path fill="#FFF" d="m116.9 114.4-7.5-14.8V69.9L128 59l18.6 10.9v22.3l7.4-7.4 4.2 3-4.2 11.8-14.9 14.8z"/><circle fill="#FFF" cx="82" cy="82.8" r="5.4"/><circle fill="#FFF" cx="90.8" cy="61.7" r="5.4"/><circle fill="#FFF" cx="106.6" cy="46.2" r="5.4"/><circle fill="#FFF" cx="128" cy="40.8" r="5.4"/><circle fill="#FFF" cx="149.4" cy="46.2" r="5.4"/><circle fill="#FFF" cx="165.2" cy="61.7" r="5.4"/><circle fill="#FFF" cx="174" cy="82.8" r="5.4"/></svg>
@@ -0,0 +1 @@
1
+ <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 450 300"><path fill="#FFF" d="M0 0h450v300H0z"/><path fill="red" d="M0 100h450v200H0z"/><path fill="#FFDF00" d="M0 200h450v100H0z"/></svg>
@@ -0,0 +1 @@
1
+ declare module '*.svg?raw';
@@ -0,0 +1,13 @@
1
+ import AB from './AB.svg?raw';
2
+ import OS from './OS.svg?raw';
3
+
4
+ export const customFlags = {
5
+ /**
6
+ * Абхазия
7
+ */
8
+ AB,
9
+ /**
10
+ * Южная осетия
11
+ */
12
+ OS,
13
+ };
@@ -0,0 +1 @@
1
+ export * from './customFlags';