@true-engineering/true-react-common-ui-kit 4.0.0-alpha3 → 4.0.0-alpha30

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 (221) hide show
  1. package/README.md +11 -607
  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/ControlWrapper/ControlWrapper.d.ts +4 -2
  5. package/dist/components/ControlWrapper/ControlWrapper.styles.d.ts +2 -1
  6. package/dist/components/ControlWrapper/helpers.d.ts +4 -0
  7. package/dist/components/ControlWrapper/index.d.ts +2 -0
  8. package/dist/components/ControlWrapper/types.d.ts +12 -0
  9. package/dist/components/DatePicker/DatePicker.d.ts +3 -3
  10. package/dist/components/DatePicker/DatePicker.styles.d.ts +1 -1
  11. package/dist/components/DatePicker/components/PopperContainer/PopperContainer.d.ts +2 -4
  12. package/dist/components/DatePicker/helpers.d.ts +3 -0
  13. package/dist/components/DatePicker/types.d.ts +5 -3
  14. package/dist/components/FiltersPane/FiltersPane.d.ts +7 -2
  15. package/dist/components/FiltersPane/components/Filter/Filter.d.ts +2 -2
  16. package/dist/components/FiltersPane/components/Filter/helpers.d.ts +4 -0
  17. package/dist/components/FiltersPane/components/FilterInterval/FilterInterval.styles.d.ts +1 -1
  18. package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.styles.d.ts +1 -1
  19. package/dist/components/FiltersPane/components/FilterValueView/FilterValueView.d.ts +3 -1
  20. package/dist/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.d.ts +2 -2
  21. package/dist/components/FiltersPane/components/FilterWrapper/FilterWrapper.d.ts +2 -2
  22. package/dist/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.styles.d.ts +1 -1
  23. package/dist/components/FiltersPane/types.d.ts +16 -5
  24. package/dist/components/Flag/customFlags/customFlags.d.ts +10 -0
  25. package/dist/components/Flag/customFlags/index.d.ts +1 -0
  26. package/dist/components/FlexibleTable/FlexibleTable.d.ts +4 -2
  27. package/dist/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.d.ts +4 -3
  28. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +6 -6
  29. package/dist/components/FlexibleTable/constants.d.ts +18 -2
  30. package/dist/components/FlexibleTable/types.d.ts +20 -12
  31. package/dist/components/Icon/icons-list.d.ts +1 -1
  32. package/dist/components/IconButton/IconButton.styles.d.ts +1 -1
  33. package/dist/components/IncrementInput/IncrementInput.styles.d.ts +1 -3
  34. package/dist/components/Input/Input.d.ts +3 -2
  35. package/dist/components/Input/InputBase.d.ts +2 -2
  36. package/dist/components/List/List.d.ts +1 -1
  37. package/dist/components/List/index.d.ts +2 -1
  38. package/dist/components/List/types.d.ts +4 -0
  39. package/dist/components/Modal/Modal.styles.d.ts +1 -1
  40. package/dist/components/Notification/Notification.styles.d.ts +1 -1
  41. package/dist/components/ScrollIntoViewIfNeeded/ScrollIntoViewIfNeeded.d.ts +1 -1
  42. package/dist/components/SearchInput/SearchInput.d.ts +2 -2
  43. package/dist/components/Select/Select.d.ts +5 -3
  44. package/dist/components/Select/Select.styles.d.ts +5 -5
  45. package/dist/components/Select/components/SelectListItem/SelectListItem.d.ts +2 -1
  46. package/dist/components/Select/index.d.ts +1 -1
  47. package/dist/components/Status/Status.styles.d.ts +3 -2
  48. package/dist/components/Status/constants.d.ts +0 -1
  49. package/dist/components/Status/index.d.ts +1 -0
  50. package/dist/components/Status/types.d.ts +5 -2
  51. package/dist/components/TextArea/TextArea.d.ts +4 -5
  52. package/dist/components/TextArea/TextArea.styles.d.ts +5 -4
  53. package/dist/components/TextArea/index.d.ts +1 -1
  54. package/dist/components/TextArea/types.d.ts +4 -2
  55. package/dist/components/TextButton/TextButton.styles.d.ts +1 -1
  56. package/dist/components/Tooltip/Tooltip.d.ts +1 -1
  57. package/dist/components/Tooltip/Tooltip.styles.d.ts +1 -1
  58. package/dist/components/WithMessages/WithMessages.styles.d.ts +1 -1
  59. package/dist/components/WithPopup/WithPopup.d.ts +11 -3
  60. package/dist/hooks/index.d.ts +2 -0
  61. package/dist/hooks/use-intersection-ref.d.ts +8 -0
  62. package/dist/hooks/use-merge.d.ts +1 -0
  63. package/dist/hooks/use-mixed-styles.d.ts +3 -1
  64. package/dist/hooks/use-tweak-styles.d.ts +5 -5
  65. package/dist/style.css +78 -142
  66. package/dist/theme/Provider.d.ts +6 -3
  67. package/dist/theme/create-themed-styles.d.ts +2 -0
  68. package/dist/theme/helpers.d.ts +9 -3
  69. package/dist/theme/index.d.ts +2 -0
  70. package/dist/theme/true-jss/ThemedStylesManager.d.ts +18 -0
  71. package/dist/theme/true-jss/TweakStylesManager.d.ts +34 -0
  72. package/dist/theme/true-jss/index.d.ts +2 -0
  73. package/dist/theme/true-jss/jss-context.d.ts +9 -0
  74. package/dist/theme/types.d.ts +5 -3
  75. package/dist/true-react-common-ui-kit.js +7593 -6483
  76. package/dist/true-react-common-ui-kit.js.map +1 -1
  77. package/dist/true-react-common-ui-kit.umd.cjs +7618 -6509
  78. package/dist/true-react-common-ui-kit.umd.cjs.map +1 -1
  79. package/dist/types.d.ts +10 -3
  80. package/package.json +4 -5
  81. package/src/components/ControlWrapper/ControlWrapper.stories.tsx +8 -3
  82. package/src/components/ControlWrapper/ControlWrapper.styles.ts +7 -6
  83. package/src/components/ControlWrapper/ControlWrapper.tsx +31 -20
  84. package/src/components/ControlWrapper/helpers.ts +11 -0
  85. package/src/components/ControlWrapper/index.ts +2 -0
  86. package/src/components/ControlWrapper/types.ts +16 -0
  87. package/src/components/DateInput/DateInput.stories.tsx +0 -1
  88. package/src/components/DatePicker/DatePicker.stories.tsx +24 -11
  89. package/src/components/DatePicker/DatePicker.styles.ts +3 -1
  90. package/src/components/DatePicker/DatePicker.tsx +18 -10
  91. package/src/components/DatePicker/components/PopperContainer/PopperContainer.tsx +4 -4
  92. package/src/components/DatePicker/helpers.ts +13 -1
  93. package/src/components/DatePicker/types.ts +9 -4
  94. package/src/components/FiltersPane/FiltersPane.stories.tsx +4 -2
  95. package/src/components/FiltersPane/FiltersPane.tsx +28 -19
  96. package/src/components/FiltersPane/components/Filter/Filter.tsx +36 -30
  97. package/src/components/FiltersPane/components/Filter/helpers.ts +18 -0
  98. package/src/components/FiltersPane/components/FilterSelect/FilterSelect.tsx +22 -23
  99. package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +27 -22
  100. package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.ts +2 -1
  101. package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.tsx +3 -4
  102. package/src/components/FiltersPane/components/FilterWithPeriod/FilterWithPeriod.tsx +4 -3
  103. package/src/components/FiltersPane/components/FilterWrapper/FilterWrapper.tsx +14 -10
  104. package/src/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.styles.ts +5 -0
  105. package/src/components/FiltersPane/components/FiltersPaneSearch/FiltersPaneSearch.tsx +16 -19
  106. package/src/components/FiltersPane/types.ts +24 -5
  107. package/src/components/Flag/Flag.stories.tsx +2 -1
  108. package/src/components/Flag/Flag.styles.ts +4 -0
  109. package/src/components/Flag/Flag.tsx +23 -9
  110. package/src/components/Flag/customFlags/AB.svg +1 -0
  111. package/src/components/Flag/customFlags/OS.svg +1 -0
  112. package/src/components/Flag/customFlags/augment.d.ts +1 -0
  113. package/src/components/Flag/customFlags/customFlags.ts +13 -0
  114. package/src/components/Flag/customFlags/index.ts +1 -0
  115. package/src/components/FlexibleTable/FlexibleTable.tsx +40 -63
  116. package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.tsx +8 -5
  117. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +13 -12
  118. package/src/components/FlexibleTable/constants.ts +6 -3
  119. package/src/components/FlexibleTable/types.ts +20 -16
  120. package/src/components/IncrementInput/IncrementInput.stories.tsx +2 -0
  121. package/src/components/IncrementInput/IncrementInput.styles.ts +31 -39
  122. package/src/components/IncrementInput/IncrementInput.tsx +28 -25
  123. package/src/components/Input/Input.stories.tsx +1 -6
  124. package/src/components/Input/Input.tsx +5 -3
  125. package/src/components/Input/InputBase.tsx +27 -25
  126. package/src/components/List/List.tsx +5 -2
  127. package/src/components/List/index.ts +2 -1
  128. package/src/components/List/types.ts +5 -0
  129. package/src/components/MultiSelectList/MultiSelectList.tsx +15 -11
  130. package/src/components/NumberInput/NumberInput.stories.tsx +5 -1
  131. package/src/components/PhoneInput/PhoneInput.stories.tsx +2 -1
  132. package/src/components/PhoneInput/PhoneInput.tsx +5 -2
  133. package/src/components/SearchInput/SearchInput.tsx +20 -29
  134. package/src/components/Select/CustomSelect.stories.tsx +1 -0
  135. package/src/components/Select/MultiSelect.stories.tsx +5 -0
  136. package/src/components/Select/Select.stories.tsx +6 -0
  137. package/src/components/Select/Select.styles.ts +5 -40
  138. package/src/components/Select/Select.tsx +36 -22
  139. package/src/components/Select/components/SelectList/SelectList.tsx +4 -2
  140. package/src/components/Select/components/SelectListItem/SelectListItem.tsx +5 -2
  141. package/src/components/Select/index.ts +1 -1
  142. package/src/components/Status/Status.stories.tsx +54 -1
  143. package/src/components/Status/Status.styles.ts +2 -37
  144. package/src/components/Status/constants.ts +0 -10
  145. package/src/components/Status/index.ts +1 -1
  146. package/src/components/Status/types.ts +7 -3
  147. package/src/components/TextArea/TextArea.stories.tsx +15 -1
  148. package/src/components/TextArea/TextArea.styles.ts +15 -8
  149. package/src/components/TextArea/TextArea.tsx +96 -62
  150. package/src/components/TextArea/index.ts +1 -1
  151. package/src/components/TextArea/types.ts +5 -5
  152. package/src/components/TextButton/TextButton.styles.ts +1 -0
  153. package/src/components/Tooltip/Tooltip.styles.ts +2 -0
  154. package/src/components/Tooltip/Tooltip.tsx +1 -1
  155. package/src/components/WithMessages/WithMessages.stories.tsx +1 -1
  156. package/src/components/WithPopup/WithPopup.tsx +36 -15
  157. package/src/constants/phone-info.ts +20 -33
  158. package/src/helpers/phone.ts +19 -15
  159. package/src/hooks/index.ts +2 -0
  160. package/src/hooks/use-intersection-ref.ts +30 -0
  161. package/src/hooks/use-merge.ts +8 -0
  162. package/src/hooks/use-mixed-styles.ts +9 -11
  163. package/src/hooks/use-tweak-styles.ts +49 -27
  164. package/src/theme/Provider.tsx +10 -5
  165. package/src/theme/create-themed-styles.ts +78 -0
  166. package/src/theme/helpers.ts +39 -39
  167. package/src/theme/index.ts +2 -0
  168. package/src/theme/true-jss/ThemedStylesManager.ts +92 -0
  169. package/src/theme/true-jss/TweakStylesManager.ts +157 -0
  170. package/src/theme/true-jss/index.ts +2 -0
  171. package/src/theme/true-jss/jss-context.tsx +34 -0
  172. package/src/theme/types.ts +5 -3
  173. package/src/types.ts +17 -4
  174. package/dist/components/AccountInfo/AccountInfo.stories.d.ts +0 -6
  175. package/dist/components/AddButton/AddButton.stories.d.ts +0 -6
  176. package/dist/components/Button/Button.stories.d.ts +0 -6
  177. package/dist/components/Checkbox/Checkbox.stories.d.ts +0 -8
  178. package/dist/components/CloseButton/CloseButton.stories.d.ts +0 -5
  179. package/dist/components/Colors/Colors.stories.d.ts +0 -5
  180. package/dist/components/ControlWrapper/ControlWrapper.stories.d.ts +0 -6
  181. package/dist/components/DateInput/DateInput.stories.d.ts +0 -7
  182. package/dist/components/DatePicker/DatePicker.stories.d.ts +0 -7
  183. package/dist/components/Description/Description.stories.d.ts +0 -16
  184. package/dist/components/FileInput/FileInput.stories.d.ts +0 -7
  185. package/dist/components/FileItem/FileItem.stories.d.ts +0 -8
  186. package/dist/components/FiltersPane/FiltersPane.stories.d.ts +0 -31
  187. package/dist/components/Flag/Flag.stories.d.ts +0 -12
  188. package/dist/components/FlexibleTable/FlexibleTable.stories.d.ts +0 -19
  189. package/dist/components/Icon/Icon.stories.d.ts +0 -6
  190. package/dist/components/IconButton/IconButton.stories.d.ts +0 -6
  191. package/dist/components/IncrementInput/IncrementInput.stories.d.ts +0 -6
  192. package/dist/components/Input/Input.stories.d.ts +0 -25
  193. package/dist/components/List/List.stories.d.ts +0 -5
  194. package/dist/components/Modal/Modal.stories.d.ts +0 -29
  195. package/dist/components/MoreMenu/MoreMenu.stories.d.ts +0 -6
  196. package/dist/components/MultiSelect/MultiSelect.stories.d.ts +0 -13
  197. package/dist/components/NewMoreMenu/NewMoreMenu.stories.d.ts +0 -12
  198. package/dist/components/Notification/Notification.stories.d.ts +0 -8
  199. package/dist/components/NumberInput/NumberInput.stories.d.ts +0 -7
  200. package/dist/components/PhoneInput/PhoneInput.stories.d.ts +0 -28
  201. package/dist/components/PhoneInput/components/PhoneInputCountryList/PhoneInputCountryList.stories.d.ts +0 -5
  202. package/dist/components/RadioButton/RadioButton.stories.d.ts +0 -7
  203. package/dist/components/SearchInput/SearchInput.stories.d.ts +0 -6
  204. package/dist/components/Select/CustomSelect.stories.d.ts +0 -11
  205. package/dist/components/Select/MultiSelect.stories.d.ts +0 -15
  206. package/dist/components/Select/Select.stories.d.ts +0 -15
  207. package/dist/components/Selector/Selector.stories.d.ts +0 -7
  208. package/dist/components/Skeleton/Skeleton.stories.d.ts +0 -6
  209. package/dist/components/SmartInput/SmartInput.stories.d.ts +0 -18
  210. package/dist/components/Status/Status.stories.d.ts +0 -6
  211. package/dist/components/Switch/Switch.stories.d.ts +0 -16
  212. package/dist/components/TextArea/TextArea.stories.d.ts +0 -17
  213. package/dist/components/TextButton/TextButton.stories.d.ts +0 -6
  214. package/dist/components/TextWithInfo/TextWithInfo.stories.d.ts +0 -12
  215. package/dist/components/TextWithTooltip/TextWithTooltip.stories.d.ts +0 -24
  216. package/dist/components/ThemedPreloader/ThemedPreloader.stories.d.ts +0 -17
  217. package/dist/components/Toaster/Toaster.stories.d.ts +0 -5
  218. package/dist/components/Tooltip/Tooltip.stories.d.ts +0 -5
  219. package/dist/components/WithMessages/WithMessages.stories.d.ts +0 -7
  220. package/dist/components/WithPopup/WithPopup.stories.d.ts +0 -16
  221. package/dist/components/WithTooltip/WithTooltip.stories.d.ts +0 -6
@@ -1,6 +1,13 @@
1
1
  import { ReactNode, useMemo, useRef, useState } from 'react';
2
2
  import clsx from 'clsx';
3
- import { addDataAttributes } from '../../../../helpers';
3
+ import {
4
+ addDataAttributes,
5
+ addDataTestId,
6
+ getTestId,
7
+ isArrayLikeNotEmpty,
8
+ isArrayNotEmpty,
9
+ isNotEmpty,
10
+ } from '@true-engineering/true-react-platform-helpers';
4
11
  import { useOnClickOutside, useTweakStyles } from '../../../../hooks';
5
12
  import { ICommonProps } from '../../../../types';
6
13
  import { Icon } from '../../../Icon';
@@ -89,20 +96,15 @@ export function FiltersPaneSearch<Value>({
89
96
  setIsOpen(false);
90
97
  };
91
98
 
92
- const selectedFieldLabel = useMemo(
93
- () => (field !== undefined && getValueView !== undefined ? getValueView(field) : undefined),
94
- [field],
95
- );
96
-
97
99
  return (
98
100
  <div
99
101
  className={clsx(classes.root, {
100
102
  [classes.focused]: isInputFocused,
101
103
  [classes.disabled]: isDisabled,
104
+ [classes.hasValue]: isArrayLikeNotEmpty(value),
102
105
  })}
103
106
  ref={refRoot}
104
- data-testid={testId}
105
- {...addDataAttributes(data)}
107
+ {...addDataAttributes(data, testId)}
106
108
  >
107
109
  <SearchInput
108
110
  value={value}
@@ -111,26 +113,21 @@ export function FiltersPaneSearch<Value>({
111
113
  tweakStyles={tweakSearchInputStyles}
112
114
  onFocus={() => setIsInputFocused(true)}
113
115
  onBlur={() => setIsInputFocused(false)}
114
- testId={testId !== undefined ? `${testId}-input` : undefined}
116
+ testId={getTestId(testId, 'input')}
115
117
  maxLength={maxLength}
116
118
  isDisabled={isDisabled}
117
119
  />
118
120
 
119
121
  <div className={classes.selectWrapper}>
120
122
  {/* Select block */}
121
- {fields.length > 0 && (
123
+ {isArrayNotEmpty(fields) && (
122
124
  <div
123
125
  className={classes.selectBlock}
124
126
  onClick={!isDisabled ? () => setIsOpen(!isOpen) : undefined}
125
- data-testid={testId !== undefined ? `${testId}-select` : undefined}
127
+ {...addDataTestId(testId, 'select')}
126
128
  >
127
- <div
128
- className={clsx(
129
- classes.selectLabel,
130
- selectedFieldLabel !== undefined && classes.active,
131
- )}
132
- >
133
- {selectedFieldLabel ?? translates.displayedFields}
129
+ <div className={clsx(classes.selectLabel, { [classes.active]: isNotEmpty(field) })}>
130
+ {isNotEmpty(field) ? getValueView?.(field) : translates.displayedFields}
134
131
  </div>
135
132
  <div className={clsx(classes.chevronIcon, isOpen && classes.open)}>
136
133
  <Icon type="chevron-down" />
@@ -152,7 +149,7 @@ export function FiltersPaneSearch<Value>({
152
149
  onChange={handleFieldsChange}
153
150
  isSearchEnabled={isSelectSearchEnabled}
154
151
  hasClearButton={hasClearSelectButton}
155
- testId={testId !== undefined ? `${testId}-dropdown` : undefined}
152
+ testId={getTestId(testId, 'dropdown')}
156
153
  />
157
154
  </div>
158
155
  )}
@@ -1,4 +1,5 @@
1
1
  import { FC, ReactNode } from 'react';
2
+ import { ITestIdProps } from '../../types';
2
3
  import { IDatePickerProps } from '../DatePicker';
3
4
  import { IMultiSelectListValues } from '../MultiSelectList';
4
5
  import type {
@@ -84,22 +85,40 @@ export type IDateRangeConfigItem<Value> = IConfigItemBasicBase<Value> & {
84
85
  dateFormat?: string;
85
86
  } & Omit<IFilterWithPeriodProps, 'value' | 'onChange' | 'setIsOpen'>;
86
87
 
87
- export type CustomComponent<Value> = FC<{
88
+ export interface ICustomComponentProps<Value> extends ITestIdProps {
88
89
  value?: Value;
89
90
  onChange: (v?: Value) => void;
90
91
  onClose?: () => void;
91
92
  filter: ICustomConfigItem<Value>;
92
93
  localeKey?: IFilterLocaleKey;
93
94
  locale?: IPartialFilterLocale;
94
- }>;
95
+ }
96
+
97
+ export type CustomComponent<Value> = FC<ICustomComponentProps<Value>>;
98
+
99
+ export type ICustomValue<V> = V extends Array<infer T> ? T : never;
100
+
101
+ export interface ICustomRangeConfigItem<Value> extends IConfigItemBasicBase<Value> {
102
+ // eslint-disable-next-line @typescript-eslint/ban-types
103
+ [key: string & {}]: any;
104
+ type: 'custom';
105
+ component: CustomComponent<Value>;
106
+ valueViewType?: 'range';
107
+ }
95
108
 
96
- export interface ICustomConfigItem<Value> extends IConfigItemBasicBase<Value> {
97
- [key: string]: any;
109
+ export interface ICustomMultipleConfigItem<Value> extends IConfigItemBasicBase<Value> {
110
+ // eslint-disable-next-line @typescript-eslint/ban-types
111
+ [key: string & {}]: any;
98
112
  type: 'custom';
99
113
  component: CustomComponent<Value>;
100
- valueViewType?: 'range' | 'multiple';
114
+ valueViewType?: 'multiple';
115
+ getSelectedValue?: (v: ICustomValue<Value>) => ReactNode;
101
116
  }
102
117
 
118
+ export type ICustomConfigItem<Value> =
119
+ | ICustomRangeConfigItem<Value>
120
+ | ICustomMultipleConfigItem<Value>;
121
+
103
122
  export type ConfigItem<Value> =
104
123
  | ISelectConfigItem<Value>
105
124
  | 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';
@@ -1,15 +1,16 @@
1
1
  import { ReactNode, RefObject, useCallback, useEffect, useMemo, useRef } from 'react';
2
2
  import clsx from 'clsx';
3
3
  import {
4
- addDataTestId,
4
+ addDataAttributes,
5
5
  applyAction,
6
+ getTestId,
6
7
  indexMap,
7
8
  isArrayNotEmpty,
8
9
  isEmpty,
9
10
  isNotEmpty,
11
+ isReactNodeNotEmpty,
10
12
  } from '@true-engineering/true-react-platform-helpers';
11
- import { addDataAttributes } from '../../helpers';
12
- import { useMergedRefs, useTweakStyles } from '../../hooks';
13
+ import { useIntersectionRef, useMergedRefs, useTweakStyles } from '../../hooks';
13
14
  import { ICommonProps } from '../../types';
14
15
  import { ThemedPreloader } from '../ThemedPreloader';
15
16
  import { FlexibleTableRow, IFlexibleTableRowProps } from './components';
@@ -34,10 +35,10 @@ export interface IFlexibleTableProps<
34
35
  | 'uniqueField'
35
36
  | 'rowAttributes'
36
37
  | 'isFirstColumnSticky'
37
- | 'isExpandableRowComponentInitiallyOpen'
38
38
  | 'expandableRowComponent'
39
39
  | 'onRowClick'
40
40
  | 'onRowHover'
41
+ | 'rowRef'
41
42
  > {
42
43
  content: Row[];
43
44
  /** @default 'table' */
@@ -62,6 +63,8 @@ export interface IFlexibleTableProps<
62
63
  nothingFoundContent?: ReactNode;
63
64
  /** @default true */
64
65
  shouldRenderHeader?: boolean;
66
+ /** @default false */
67
+ isExpandableRowComponentInitiallyOpen?: boolean | ((row: Row, index: number) => boolean);
65
68
  }
66
69
 
67
70
  export function FlexibleTable<
@@ -79,7 +82,7 @@ export function FlexibleTable<
79
82
  isFirstColumnSticky = false,
80
83
  isHorizontallyScrollable = false,
81
84
  isRowFocusable = false,
82
- infinityScrollConfig,
85
+ infinityScrollConfig: infinityScrollConfigDeprecated,
83
86
  renderMode = 'table',
84
87
  refForScroll,
85
88
  nothingFoundContent,
@@ -88,6 +91,7 @@ export function FlexibleTable<
88
91
  tweakStyles,
89
92
  shouldRenderHeader = true,
90
93
  onHeadClick,
94
+ isExpandableRowComponentInitiallyOpen: isRowInitiallyOpen,
91
95
  ...restProps
92
96
  }: IFlexibleTableProps<Row, HeaderContent, UniqueField>): JSX.Element {
93
97
  const classes = useStyles({ theme: tweakStyles });
@@ -98,18 +102,16 @@ export function FlexibleTable<
98
102
  currentComponentName: 'FlexibleTable',
99
103
  });
100
104
 
101
- const observer = useRef<IntersectionObserver>();
102
105
  const scrollRef = useRef<HTMLDivElement>(null);
103
106
 
104
107
  const columns = useMemo(() => enabledColumns ?? Object.keys(config), [enabledColumns, config]);
105
108
 
106
- const hasInfiniteScroll = isNotEmpty(infinityScrollConfig);
107
-
108
- const tableRowProps: Omit<
109
- IFlexibleTableRowProps<Row, HeaderContent, UniqueField>,
110
- 'item' | 'index'
111
- > = {
109
+ const getTableRowProps = (
110
+ item: Row,
111
+ index: number,
112
+ ): IFlexibleTableRowProps<Row, HeaderContent, UniqueField> => ({
112
113
  ...restProps,
114
+ item,
113
115
  renderMode,
114
116
  config,
115
117
  columns,
@@ -118,7 +120,8 @@ export function FlexibleTable<
118
120
  isFirstColumnSticky,
119
121
  isFocusable: isRowFocusable,
120
122
  tweakStyles: tweakTableRowStyles,
121
- };
123
+ isExpandableRowComponentInitiallyOpen: applyAction(isRowInitiallyOpen, item, index),
124
+ });
122
125
 
123
126
  const getDataScrollAttributeSetter = useCallback(
124
127
  (key: string, setter: (el: HTMLDivElement) => boolean) => (el?: HTMLDivElement) => {
@@ -143,12 +146,6 @@ export function FlexibleTable<
143
146
  [getDataScrollAttributeSetter],
144
147
  );
145
148
 
146
- const shouldShowNothingFound =
147
- !isArrayNotEmpty(content) &&
148
- nothingFoundContent !== undefined &&
149
- !infinityScrollConfig?.isLoading &&
150
- (infinityScrollConfig?.isLastPage === undefined || infinityScrollConfig.isLastPage);
151
-
152
149
  const ref = useMergedRefs([
153
150
  refForScroll,
154
151
  scrollRef,
@@ -156,37 +153,25 @@ export function FlexibleTable<
156
153
  setIsScrolledAttribute,
157
154
  ]);
158
155
 
159
- const initIntersectionObserver = useCallback(
160
- (node: HTMLDivElement | null) => {
161
- if (
162
- !hasInfiniteScroll ||
163
- infinityScrollConfig.isLoading ||
164
- infinityScrollConfig.activePage >= infinityScrollConfig.totalPages
165
- ) {
166
- return;
167
- }
168
-
169
- if (observer.current) {
170
- observer.current.disconnect();
171
- }
172
-
173
- observer.current = new IntersectionObserver((entries) => {
174
- if (entries[0].isIntersecting) {
175
- infinityScrollConfig.onInfinityScroll(infinityScrollConfig.activePage + 1);
176
- }
177
- });
156
+ const infinityScrollConfig = infinityScrollConfigDeprecated && {
157
+ isLoading: infinityScrollConfigDeprecated.isLoading,
158
+ onInfinityScroll: () =>
159
+ infinityScrollConfigDeprecated.onInfinityScroll(
160
+ (infinityScrollConfigDeprecated.activePage ?? 0) + 1,
161
+ ),
162
+ isEnabled:
163
+ infinityScrollConfigDeprecated.isEnabled ?? !infinityScrollConfigDeprecated.isLastPage,
164
+ };
165
+ const intersectionRef = useIntersectionRef({
166
+ isDisabled: !infinityScrollConfig?.isEnabled || infinityScrollConfig.isLoading,
167
+ onIntersection: infinityScrollConfig?.onInfinityScroll,
168
+ });
178
169
 
179
- if (node) {
180
- observer.current.observe(node);
181
- }
182
- },
183
- [
184
- hasInfiniteScroll,
185
- infinityScrollConfig?.activePage,
186
- infinityScrollConfig?.totalPages,
187
- infinityScrollConfig?.onInfinityScroll,
188
- ],
189
- );
170
+ const shouldShowNothingFound =
171
+ isReactNodeNotEmpty(nothingFoundContent) &&
172
+ !isArrayNotEmpty(content) &&
173
+ !infinityScrollConfig?.isLoading &&
174
+ !infinityScrollConfig?.isEnabled;
190
175
 
191
176
  useEffect(() => {
192
177
  const scrollContainer = scrollRef.current;
@@ -217,11 +202,7 @@ export function FlexibleTable<
217
202
 
218
203
  return (
219
204
  <div ref={ref} className={clsx({ [classes.scroll]: isHorizontallyScrollable })}>
220
- <Table.Root
221
- className={classes.root}
222
- {...addDataTestId(testId)}
223
- {...addDataAttributes({ ...data, isLoading })}
224
- >
205
+ <Table.Root className={classes.root} {...addDataAttributes({ ...data, isLoading }, testId)}>
225
206
  {shouldRenderHeader && (
226
207
  <Table.Head className={classes.head}>
227
208
  <Table.Row className={classes.headerRow}>
@@ -267,9 +248,7 @@ export function FlexibleTable<
267
248
  )}
268
249
  <Table.Body className={classes.body}>
269
250
  {isLoading ? (
270
- indexMap(6, (i) => (
271
- <FlexibleTableRow {...tableRowProps} key={i} item={{} as Row} index={i} />
272
- ))
251
+ indexMap(6, (i) => <FlexibleTableRow {...getTableRowProps({} as Row, i)} key={i} />)
273
252
  ) : (
274
253
  <>
275
254
  {shouldShowNothingFound && (
@@ -282,19 +261,17 @@ export function FlexibleTable<
282
261
 
283
262
  {content.map((item, i) => (
284
263
  <FlexibleTableRow
285
- {...tableRowProps}
264
+ {...getTableRowProps(item, i)}
286
265
  isActive={activeRowsSet.has(i)}
287
266
  key={isNotEmpty(uniqueField) ? item[uniqueField] : i}
288
- item={item}
289
- index={i}
290
267
  />
291
268
  ))}
292
269
 
293
- {hasInfiniteScroll && !infinityScrollConfig.isLastPage && (
270
+ {infinityScrollConfig?.isEnabled && (
294
271
  <Table.Row className={classes.loaderRow}>
295
272
  <Table.Cell className={classes.loaderCell} colSpan={columns.length}>
296
- <div ref={initIntersectionObserver} className={classes.loader}>
297
- <ThemedPreloader type="dots" />
273
+ <div ref={intersectionRef} className={classes.loader}>
274
+ <ThemedPreloader type="dots" testId={getTestId(testId, 'loader')} />
298
275
  </div>
299
276
  </Table.Cell>
300
277
  </Table.Row>
@@ -1,3 +1,4 @@
1
+ import { ReactNode, useCallback } from 'react';
1
2
  import clsx from 'clsx';
2
3
  import { applyAction, isNotEmpty } from '@true-engineering/true-react-platform-helpers';
3
4
  import { addDataAttributes } from '../../../../helpers';
@@ -20,14 +21,11 @@ export interface IFlexibleTableCellProps<
20
21
  > extends Pick<ICommonProps<IFlexibleTableCellStyles>, 'tweakStyles'>,
21
22
  Pick<
22
23
  IValueComponentProps<Row, unknown>,
23
- | 'isFocusedRow'
24
- | 'isActiveRow'
25
- | 'isNestedComponentExpanded'
26
- | 'isRowNestedComponentExpanded'
27
- | 'onSetNestedComponent'
24
+ 'isFocusedRow' | 'isActiveRow' | 'isNestedComponentExpanded' | 'isRowNestedComponentExpanded'
28
25
  > {
29
26
  row: Row;
30
27
  columnName: keyof Row;
28
+ updateNestedComponent: (component: ReactNode, cellKey: keyof Row) => void;
31
29
  config: IFlexibleTableConfigType<Row, HeaderContent>;
32
30
  renderMode: IFlexibleTableRenderMode;
33
31
  isSecond?: boolean;
@@ -46,6 +44,7 @@ export function FlexibleTableCell<
46
44
  isSecond,
47
45
  isSticky: isOldSticky,
48
46
  isLoading,
47
+ updateNestedComponent,
49
48
  tweakStyles,
50
49
  ...valueComponentProps
51
50
  }: IFlexibleTableCellProps<Row, HeaderContent>): JSX.Element {
@@ -73,6 +72,10 @@ export function FlexibleTableCell<
73
72
 
74
73
  const cellComponentProps: IValueComponentProps<Row, Row[keyof Row]> = {
75
74
  ...valueComponentProps,
75
+ onSetNestedComponent: useCallback(
76
+ (node) => updateNestedComponent(node, columnName),
77
+ [columnName, updateNestedComponent],
78
+ ),
76
79
  value,
77
80
  row,
78
81
  };
@@ -1,9 +1,9 @@
1
- import { ReactNode, useState, memo, MouseEvent } from 'react';
1
+ import { ReactNode, useState, memo, MouseEvent, RefCallback, useCallback } from 'react';
2
2
  import clsx from 'clsx';
3
- import { applyAction, isEmpty, isNotEmpty } from '@true-engineering/true-react-platform-helpers';
3
+ import { isEmpty, isNotEmpty } from '@true-engineering/true-react-platform-helpers';
4
4
  import { addDataAttributes } from '../../../../helpers';
5
5
  import { useTweakStyles } from '../../../../hooks';
6
- import { ICommonProps, IDataAttributes } from '../../../../types';
6
+ import { IDataAttributes, ITweakStylesProps } from '../../../../types';
7
7
  import { TableRenders } from '../../constants';
8
8
  import {
9
9
  ITableRow,
@@ -19,9 +19,8 @@ export interface IFlexibleTableRowProps<
19
19
  Row extends ITableRow,
20
20
  HeaderContent extends IHeaderContent<Row>,
21
21
  UniqueField extends keyof Row,
22
- > extends Pick<ICommonProps<IFlexibleTableRowStyles>, 'tweakStyles'> {
22
+ > extends ITweakStylesProps<IFlexibleTableRowStyles> {
23
23
  item: Row;
24
- index: number;
25
24
  uniqueField?: UniqueField;
26
25
  renderMode: IFlexibleTableRenderMode;
27
26
  /** @default false */
@@ -37,7 +36,8 @@ export interface IFlexibleTableRowProps<
37
36
  columns: Array<keyof Row & string>;
38
37
  rowAttributes?: Array<keyof Row>;
39
38
  /** @default false */
40
- isExpandableRowComponentInitiallyOpen?: boolean | ((row: Row, index: number) => boolean);
39
+ isExpandableRowComponentInitiallyOpen?: boolean;
40
+ rowRef?: RefCallback<HTMLTableRowElement>;
41
41
  /** Возвращает React-элемент, который отрисуется под строкой при нажатии на неё */
42
42
  expandableRowComponent?: (item: Row, isOpen: boolean, close: () => void) => ReactNode;
43
43
  onRowHover?: (id?: Row[UniqueField]) => void;
@@ -50,7 +50,6 @@ function FlexibleTableRowInner<
50
50
  UniqueField extends keyof Row,
51
51
  >({
52
52
  item,
53
- index,
54
53
  config,
55
54
  columns,
56
55
  uniqueField,
@@ -61,6 +60,7 @@ function FlexibleTableRowInner<
61
60
  isLoading = false,
62
61
  rowAttributes,
63
62
  isExpandableRowComponentInitiallyOpen = false,
63
+ rowRef,
64
64
  tweakStyles,
65
65
  expandableRowComponent,
66
66
  onRowHover,
@@ -75,8 +75,8 @@ function FlexibleTableRowInner<
75
75
  });
76
76
 
77
77
  const [isFocused, setFocused] = useState(false);
78
- const [nestedComponent, setNestedComponent] = useState<INestedComponent>(() => ({
79
- isOpen: applyAction(isExpandableRowComponentInitiallyOpen, item, index),
78
+ const [nestedComponent, setNestedComponent] = useState<INestedComponent<keyof Row>>(() => ({
79
+ isOpen: isExpandableRowComponentInitiallyOpen,
80
80
  }));
81
81
 
82
82
  const isEditable = !isLoading && (isNotEmpty(onRowClick) || isNotEmpty(onRowHover));
@@ -111,13 +111,13 @@ function FlexibleTableRowInner<
111
111
  setNestedComponent({ isOpen: false });
112
112
  };
113
113
 
114
- const updateNestedComponent = (component: ReactNode, cellKey: string) => {
114
+ const updateNestedComponent = useCallback((component: ReactNode, cellKey: keyof Row) => {
115
115
  if (isEmpty(component)) {
116
116
  closeNestedComponent();
117
117
  } else {
118
118
  setNestedComponent({ isOpen: true, component, cellKey });
119
119
  }
120
- };
120
+ }, []);
121
121
 
122
122
  const handleRowClick = () => {
123
123
  if (isNotEmpty(uniqueField)) {
@@ -139,6 +139,7 @@ function FlexibleTableRowInner<
139
139
  return (
140
140
  <>
141
141
  <Table.Row
142
+ ref={rowRef}
142
143
  className={clsx(classes.root, {
143
144
  [classes.active]: isActive,
144
145
  [classes.editable]: isEditable,
@@ -173,7 +174,7 @@ function FlexibleTableRowInner<
173
174
  isRowNestedComponentExpanded={
174
175
  isNestedComponentExpanded && isEmpty(nestedComponentCellKey)
175
176
  }
176
- onSetNestedComponent={(component) => updateNestedComponent(component, key)}
177
+ updateNestedComponent={updateNestedComponent}
177
178
  />
178
179
  ))}
179
180
  </Table.Row>
@@ -1,8 +1,11 @@
1
- import { IFlexibleTableRenderMode, IFlexibleTableRender } from './types';
1
+ import { IFlexibleTableRenderElement, IFlexibleTableRenderMode } from './types';
2
2
 
3
3
  export const DEFAULT_DATE_FORMAT = 'dd.MM.yyyy';
4
4
 
5
- export const TableRenders: Record<IFlexibleTableRenderMode, IFlexibleTableRender> = {
5
+ export const TableRenders = {
6
6
  table: { Root: 'table', Head: 'thead', Body: 'tbody', Row: 'tr', Header: 'th', Cell: 'td' },
7
7
  divs: { Root: 'div', Head: 'div', Body: 'div', Row: 'div', Header: 'div', Cell: 'div' },
8
- };
8
+ } satisfies Record<
9
+ IFlexibleTableRenderMode,
10
+ Record<IFlexibleTableRenderElement, keyof JSX.IntrinsicElements>
11
+ >;
@@ -2,11 +2,7 @@ import { CSSProperties, MouseEvent, ReactNode } from 'react';
2
2
  import { IRenderNode } from '../../types';
3
3
 
4
4
  export type IFlexibleTableRenderMode = 'table' | 'divs';
5
-
6
- export type IFlexibleTableRender = Record<
7
- 'Root' | 'Head' | 'Body' | 'Row' | 'Header' | 'Cell',
8
- keyof JSX.IntrinsicElements
9
- >;
5
+ export type IFlexibleTableRenderElement = 'Root' | 'Head' | 'Body' | 'Row' | 'Header' | 'Cell';
10
6
 
11
7
  // TODO: Заменить Record<string, any> на Record<string, unknown>
12
8
  export type ITableRow = Record<string, any>;
@@ -40,16 +36,16 @@ export interface IFlexibleTableRowConfig<
40
36
  title?: IRenderNode<ITitleComponentProps<HeaderContent[Key]>>;
41
37
  component?: IRenderNode<IValueComponentProps<Values, NonNullable<Values[Key]>>>;
42
38
  dateFormat?: string;
43
- minWidth?: string | number;
44
- width?: string | number;
45
- maxWidth?: string | number;
39
+ minWidth?: CSSProperties['minWidth'];
40
+ width?: CSSProperties['width'];
41
+ maxWidth?: CSSProperties['maxWidth'];
46
42
  /** @default 'left' */
47
43
  titleAlign?: CSSProperties['textAlign'];
48
44
  cellAlign?: CSSProperties['textAlign'];
49
45
  cellVerticalAlign?: CSSProperties['verticalAlign'];
50
46
  position?: CSSProperties['position'];
51
- right?: number;
52
- left?: number;
47
+ right?: CSSProperties['right'];
48
+ left?: CSSProperties['left'];
53
49
  shouldRenderDataId?: boolean;
54
50
  /**
55
51
  * Проверка, нужно ли отрисовать component
@@ -68,15 +64,23 @@ export type IFlexibleTableConfigType<
68
64
  };
69
65
 
70
66
  export interface IInfinityScrollConfig {
71
- activePage: number;
72
- totalPages: number;
67
+ isEnabled?: boolean;
73
68
  isLoading: boolean;
74
- isLastPage: boolean;
75
- onInfinityScroll: (skip: number) => void;
69
+ onInfinityScroll: {
70
+ (_: never): void;
71
+ /** @deprecated use activePage directly */
72
+ (skip: number): void; // eslint-disable-line @typescript-eslint/unified-signatures
73
+ };
74
+ /** @deprecated use activePage in onInfinityScroll */
75
+ activePage?: number;
76
+ /** @deprecated use isEnabled */
77
+ isLastPage?: boolean;
78
+ /** @deprecated use isEnabled */
79
+ totalPages?: number;
76
80
  }
77
81
 
78
- export interface INestedComponent {
82
+ export interface INestedComponent<T extends PropertyKey = string> {
79
83
  isOpen: boolean;
80
84
  component?: ReactNode;
81
- cellKey?: string;
85
+ cellKey?: T;
82
86
  }
@@ -26,4 +26,6 @@ Default.args = {
26
26
  isDisabled: false,
27
27
  isRequired: false,
28
28
  isReadonly: false,
29
+ icon: 'question',
30
+ isClearable: true,
29
31
  };