@true-engineering/true-react-common-ui-kit 4.0.0-alpha2 → 4.0.0-alpha21

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 (215) hide show
  1. package/README.md +11 -567
  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 +2 -0
  5. package/dist/components/ControlWrapper/ControlWrapper.styles.d.ts +2 -1
  6. package/dist/components/ControlWrapper/index.d.ts +1 -0
  7. package/dist/components/ControlWrapper/types.d.ts +3 -0
  8. package/dist/components/DatePicker/DatePicker.d.ts +3 -3
  9. package/dist/components/DatePicker/DatePicker.styles.d.ts +1 -1
  10. package/dist/components/DatePicker/components/PopperContainer/PopperContainer.d.ts +2 -4
  11. package/dist/components/DatePicker/helpers.d.ts +3 -0
  12. package/dist/components/DatePicker/types.d.ts +5 -3
  13. package/dist/components/FiltersPane/FiltersPane.d.ts +7 -2
  14. package/dist/components/FiltersPane/components/Filter/Filter.d.ts +2 -2
  15. package/dist/components/FiltersPane/components/Filter/helpers.d.ts +4 -0
  16. package/dist/components/FiltersPane/components/FilterInterval/FilterInterval.styles.d.ts +1 -1
  17. package/dist/components/FiltersPane/components/FilterSelect/FilterSelect.styles.d.ts +1 -1
  18. package/dist/components/FiltersPane/components/FilterValueView/FilterValueView.d.ts +3 -1
  19. package/dist/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.d.ts +2 -2
  20. package/dist/components/FiltersPane/components/FilterWrapper/FilterWrapper.d.ts +2 -2
  21. package/dist/components/FiltersPane/types.d.ts +15 -5
  22. package/dist/components/Flag/customFlags/customFlags.d.ts +10 -0
  23. package/dist/components/Flag/customFlags/index.d.ts +1 -0
  24. package/dist/components/FlexibleTable/FlexibleTable.d.ts +4 -2
  25. package/dist/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.d.ts +4 -3
  26. package/dist/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.d.ts +6 -6
  27. package/dist/components/FlexibleTable/constants.d.ts +18 -2
  28. package/dist/components/FlexibleTable/types.d.ts +8 -8
  29. package/dist/components/Icon/icons-list.d.ts +1 -1
  30. package/dist/components/IconButton/IconButton.styles.d.ts +1 -1
  31. package/dist/components/IncrementInput/IncrementInput.styles.d.ts +1 -3
  32. package/dist/components/Input/Input.d.ts +3 -2
  33. package/dist/components/Input/InputBase.d.ts +2 -2
  34. package/dist/components/List/List.d.ts +1 -1
  35. package/dist/components/List/index.d.ts +2 -1
  36. package/dist/components/List/types.d.ts +4 -0
  37. package/dist/components/Modal/Modal.styles.d.ts +1 -1
  38. package/dist/components/Notification/Notification.styles.d.ts +1 -1
  39. package/dist/components/ScrollIntoViewIfNeeded/ScrollIntoViewIfNeeded.d.ts +1 -1
  40. package/dist/components/SearchInput/SearchInput.d.ts +2 -2
  41. package/dist/components/Select/Select.d.ts +5 -3
  42. package/dist/components/Select/Select.styles.d.ts +4 -4
  43. package/dist/components/Select/index.d.ts +1 -1
  44. package/dist/components/Status/Status.styles.d.ts +3 -2
  45. package/dist/components/Status/constants.d.ts +0 -1
  46. package/dist/components/Status/index.d.ts +1 -0
  47. package/dist/components/Status/types.d.ts +5 -2
  48. package/dist/components/TextArea/TextArea.d.ts +2 -5
  49. package/dist/components/TextArea/TextArea.styles.d.ts +1 -2
  50. package/dist/components/TextArea/index.d.ts +0 -1
  51. package/dist/components/TextButton/TextButton.styles.d.ts +1 -1
  52. package/dist/components/Tooltip/Tooltip.d.ts +1 -1
  53. package/dist/components/Tooltip/Tooltip.styles.d.ts +1 -1
  54. package/dist/components/WithMessages/WithMessages.styles.d.ts +1 -1
  55. package/dist/components/WithPopup/WithPopup.d.ts +21 -6
  56. package/dist/components/WithPopup/WithPopup.styles.d.ts +1 -1
  57. package/dist/components/WithPopup/helpers.d.ts +2 -0
  58. package/dist/components/WithPopup/types.d.ts +3 -0
  59. package/dist/components/WithTooltip/WithTooltip.styles.d.ts +1 -0
  60. package/dist/hooks/index.d.ts +1 -0
  61. package/dist/hooks/use-merge.d.ts +1 -0
  62. package/dist/hooks/use-mixed-styles.d.ts +3 -1
  63. package/dist/hooks/use-tweak-styles.d.ts +5 -5
  64. package/dist/style.css +78 -142
  65. package/dist/theme/Provider.d.ts +6 -3
  66. package/dist/theme/common.d.ts +4 -2
  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 +7580 -6523
  76. package/dist/true-react-common-ui-kit.js.map +1 -1
  77. package/dist/true-react-common-ui-kit.umd.cjs +7466 -6410
  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 +1 -1
  82. package/src/components/ControlWrapper/ControlWrapper.styles.ts +7 -6
  83. package/src/components/ControlWrapper/ControlWrapper.tsx +8 -2
  84. package/src/components/ControlWrapper/index.ts +1 -0
  85. package/src/components/ControlWrapper/types.ts +4 -0
  86. package/src/components/DatePicker/DatePicker.stories.tsx +2 -1
  87. package/src/components/DatePicker/DatePicker.styles.ts +3 -1
  88. package/src/components/DatePicker/DatePicker.tsx +12 -7
  89. package/src/components/DatePicker/components/PopperContainer/PopperContainer.tsx +4 -4
  90. package/src/components/DatePicker/helpers.ts +13 -1
  91. package/src/components/DatePicker/types.ts +9 -4
  92. package/src/components/FiltersPane/FiltersPane.stories.tsx +4 -2
  93. package/src/components/FiltersPane/FiltersPane.tsx +19 -13
  94. package/src/components/FiltersPane/components/Filter/Filter.tsx +24 -17
  95. package/src/components/FiltersPane/components/Filter/helpers.ts +18 -0
  96. package/src/components/FiltersPane/components/FilterValueView/FilterValueView.tsx +27 -22
  97. package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.styles.ts +1 -0
  98. package/src/components/FiltersPane/components/FilterWithDates/FilterWithDates.tsx +3 -4
  99. package/src/components/FiltersPane/components/FilterWithPeriod/FilterWithPeriod.tsx +1 -1
  100. package/src/components/FiltersPane/components/FilterWrapper/FilterWrapper.tsx +7 -5
  101. package/src/components/FiltersPane/types.ts +23 -5
  102. package/src/components/Flag/Flag.stories.tsx +2 -1
  103. package/src/components/Flag/Flag.styles.ts +4 -0
  104. package/src/components/Flag/Flag.tsx +23 -9
  105. package/src/components/Flag/customFlags/AB.svg +1 -0
  106. package/src/components/Flag/customFlags/OS.svg +1 -0
  107. package/src/components/Flag/customFlags/augment.d.ts +1 -0
  108. package/src/components/Flag/customFlags/customFlags.ts +13 -0
  109. package/src/components/Flag/customFlags/index.ts +1 -0
  110. package/src/components/FlexibleTable/FlexibleTable.tsx +13 -12
  111. package/src/components/FlexibleTable/components/FlexibleTableCell/FlexibleTableCell.tsx +8 -5
  112. package/src/components/FlexibleTable/components/FlexibleTableRow/FlexibleTableRow.tsx +13 -12
  113. package/src/components/FlexibleTable/constants.ts +6 -3
  114. package/src/components/FlexibleTable/types.ts +8 -12
  115. package/src/components/IncrementInput/IncrementInput.stories.tsx +2 -0
  116. package/src/components/IncrementInput/IncrementInput.styles.ts +31 -39
  117. package/src/components/IncrementInput/IncrementInput.tsx +28 -25
  118. package/src/components/Input/Input.tsx +5 -3
  119. package/src/components/Input/InputBase.tsx +29 -24
  120. package/src/components/List/List.tsx +5 -2
  121. package/src/components/List/index.ts +2 -1
  122. package/src/components/List/types.ts +5 -0
  123. package/src/components/NumberInput/NumberInput.stories.tsx +5 -1
  124. package/src/components/PhoneInput/PhoneInput.stories.tsx +2 -1
  125. package/src/components/PhoneInput/PhoneInput.tsx +5 -2
  126. package/src/components/SearchInput/SearchInput.tsx +20 -29
  127. package/src/components/Select/Select.tsx +12 -2
  128. package/src/components/Select/components/SelectList/SelectList.tsx +1 -1
  129. package/src/components/Select/index.ts +1 -1
  130. package/src/components/Status/Status.stories.tsx +54 -1
  131. package/src/components/Status/Status.styles.ts +2 -37
  132. package/src/components/Status/constants.ts +0 -10
  133. package/src/components/Status/index.ts +1 -1
  134. package/src/components/Status/types.ts +7 -3
  135. package/src/components/TextArea/TextArea.styles.ts +2 -6
  136. package/src/components/TextArea/TextArea.tsx +41 -20
  137. package/src/components/TextArea/index.ts +0 -1
  138. package/src/components/TextButton/TextButton.styles.ts +1 -0
  139. package/src/components/Tooltip/Tooltip.styles.ts +2 -0
  140. package/src/components/Tooltip/Tooltip.tsx +1 -1
  141. package/src/components/WithMessages/WithMessages.stories.tsx +1 -1
  142. package/src/components/WithPopup/WithPopup.stories.tsx +1 -0
  143. package/src/components/WithPopup/WithPopup.styles.ts +2 -0
  144. package/src/components/WithPopup/WithPopup.tsx +64 -16
  145. package/src/components/WithPopup/helpers.ts +9 -0
  146. package/src/components/WithPopup/types.ts +7 -0
  147. package/src/components/WithTooltip/WithTooltip.styles.ts +6 -0
  148. package/src/components/WithTooltip/WithTooltip.tsx +7 -2
  149. package/src/constants/phone-info.ts +20 -33
  150. package/src/helpers/phone.ts +19 -15
  151. package/src/hooks/index.ts +1 -0
  152. package/src/hooks/use-merge.ts +8 -0
  153. package/src/hooks/use-mixed-styles.ts +9 -11
  154. package/src/hooks/use-tweak-styles.ts +49 -27
  155. package/src/theme/Provider.tsx +10 -5
  156. package/src/theme/common.ts +5 -2
  157. package/src/theme/create-themed-styles.ts +78 -0
  158. package/src/theme/helpers.ts +39 -39
  159. package/src/theme/index.ts +2 -0
  160. package/src/theme/true-jss/ThemedStylesManager.ts +92 -0
  161. package/src/theme/true-jss/TweakStylesManager.ts +157 -0
  162. package/src/theme/true-jss/index.ts +2 -0
  163. package/src/theme/true-jss/jss-context.tsx +34 -0
  164. package/src/theme/types.ts +5 -3
  165. package/src/types.ts +17 -4
  166. package/dist/components/AccountInfo/AccountInfo.stories.d.ts +0 -6
  167. package/dist/components/AddButton/AddButton.stories.d.ts +0 -6
  168. package/dist/components/Button/Button.stories.d.ts +0 -6
  169. package/dist/components/Checkbox/Checkbox.stories.d.ts +0 -8
  170. package/dist/components/CloseButton/CloseButton.stories.d.ts +0 -5
  171. package/dist/components/Colors/Colors.stories.d.ts +0 -5
  172. package/dist/components/ControlWrapper/ControlWrapper.stories.d.ts +0 -6
  173. package/dist/components/DateInput/DateInput.stories.d.ts +0 -7
  174. package/dist/components/DatePicker/DatePicker.stories.d.ts +0 -7
  175. package/dist/components/Description/Description.stories.d.ts +0 -16
  176. package/dist/components/FileInput/FileInput.stories.d.ts +0 -7
  177. package/dist/components/FileItem/FileItem.stories.d.ts +0 -8
  178. package/dist/components/FiltersPane/FiltersPane.stories.d.ts +0 -31
  179. package/dist/components/Flag/Flag.stories.d.ts +0 -12
  180. package/dist/components/FlexibleTable/FlexibleTable.stories.d.ts +0 -19
  181. package/dist/components/Icon/Icon.stories.d.ts +0 -6
  182. package/dist/components/IconButton/IconButton.stories.d.ts +0 -6
  183. package/dist/components/IncrementInput/IncrementInput.stories.d.ts +0 -6
  184. package/dist/components/Input/Input.stories.d.ts +0 -25
  185. package/dist/components/List/List.stories.d.ts +0 -5
  186. package/dist/components/Modal/Modal.stories.d.ts +0 -29
  187. package/dist/components/MoreMenu/MoreMenu.stories.d.ts +0 -6
  188. package/dist/components/MultiSelect/MultiSelect.stories.d.ts +0 -13
  189. package/dist/components/NewMoreMenu/NewMoreMenu.stories.d.ts +0 -12
  190. package/dist/components/Notification/Notification.stories.d.ts +0 -8
  191. package/dist/components/NumberInput/NumberInput.stories.d.ts +0 -7
  192. package/dist/components/PhoneInput/PhoneInput.stories.d.ts +0 -28
  193. package/dist/components/PhoneInput/components/PhoneInputCountryList/PhoneInputCountryList.stories.d.ts +0 -5
  194. package/dist/components/RadioButton/RadioButton.stories.d.ts +0 -7
  195. package/dist/components/SearchInput/SearchInput.stories.d.ts +0 -6
  196. package/dist/components/Select/CustomSelect.stories.d.ts +0 -11
  197. package/dist/components/Select/MultiSelect.stories.d.ts +0 -15
  198. package/dist/components/Select/Select.stories.d.ts +0 -15
  199. package/dist/components/Selector/Selector.stories.d.ts +0 -7
  200. package/dist/components/Skeleton/Skeleton.stories.d.ts +0 -6
  201. package/dist/components/SmartInput/SmartInput.stories.d.ts +0 -18
  202. package/dist/components/Status/Status.stories.d.ts +0 -6
  203. package/dist/components/Switch/Switch.stories.d.ts +0 -16
  204. package/dist/components/TextArea/TextArea.stories.d.ts +0 -17
  205. package/dist/components/TextArea/types.d.ts +0 -2
  206. package/dist/components/TextButton/TextButton.stories.d.ts +0 -6
  207. package/dist/components/TextWithInfo/TextWithInfo.stories.d.ts +0 -12
  208. package/dist/components/TextWithTooltip/TextWithTooltip.stories.d.ts +0 -24
  209. package/dist/components/ThemedPreloader/ThemedPreloader.stories.d.ts +0 -17
  210. package/dist/components/Toaster/Toaster.stories.d.ts +0 -5
  211. package/dist/components/Tooltip/Tooltip.stories.d.ts +0 -5
  212. package/dist/components/WithMessages/WithMessages.stories.d.ts +0 -7
  213. package/dist/components/WithPopup/WithPopup.stories.d.ts +0 -16
  214. package/dist/components/WithTooltip/WithTooltip.stories.d.ts +0 -6
  215. package/src/components/TextArea/types.ts +0 -6
@@ -1,4 +1,4 @@
1
- import { isNotEmpty, isEmpty } from '@true-engineering/true-react-platform-helpers';
1
+ import { isNotEmpty, isStringEmpty } from '@true-engineering/true-react-platform-helpers';
2
2
  import type { IPhoneInfo, IPhoneValue } from '../components';
3
3
  import { phoneInfo } from '../constants';
4
4
 
@@ -46,22 +46,26 @@ export const getFullPhone = (phone?: IPhoneValue): string =>
46
46
  (phone?.dialCode ?? '') + (phone?.phoneNumber ?? '');
47
47
 
48
48
  export const getCountryCodeFromPhone = (phoneWithCode: string): string | undefined => {
49
- // попробуем найти уникальный код страны fullCode (dialCode + arealCode)
50
- let countryCode = phoneInfo.find((info) =>
51
- info.fullCodes.some((code) => phoneWithCode.startsWith(code)),
52
- )?.countryCode;
53
-
54
- if (isEmpty(countryCode) && isNotEmpty(phoneWithCode)) {
55
- // если не нашли уникальный fullCode (dialCode + arealCode),
56
- // то пробуем найти dialCode и выбираем с наименьшим Priority
57
- countryCode = phoneInfo
58
- .filter((info) => phoneWithCode.startsWith(info.dialCode))
59
- .sort(
60
- (infoA, infoB) => (infoA.dialCodePriority ?? 1000) - (infoB.dialCodePriority ?? 1000),
61
- )[0]?.countryCode;
49
+ if (isStringEmpty(phoneWithCode)) {
50
+ return;
62
51
  }
63
52
 
64
- return countryCode;
53
+ // ищем страны, для которых phoneWithCode начинается с fullCode (dialCode + areaCode)
54
+ const matchedCountries = phoneInfo.filter((info) =>
55
+ info.fullCodes.some((fullCode) => phoneWithCode.startsWith(fullCode)),
56
+ );
57
+
58
+ // если нашлась всего одна — ок, выдаём её
59
+ if (matchedCountries.length === 1) {
60
+ return matchedCountries[0].countryCode;
61
+ }
62
+
63
+ // если нашлось несколько, выбираем страну с наименьшим dialCodePriority (0 — самая приоритетная)
64
+ const highestPriorityCountries = phoneInfo
65
+ .filter((info) => phoneWithCode.startsWith(info.dialCode))
66
+ .sort((a, b) => (a.dialCodePriority ?? 1000) - (b.dialCodePriority ?? 1000));
67
+
68
+ return highestPriorityCountries.at(0)?.countryCode;
65
69
  };
66
70
 
67
71
  export const getPhoneObjFromString = (fullPhone: string, countryCode?: string): IPhoneValue => {
@@ -5,3 +5,4 @@ export * from './use-tweak-styles';
5
5
  export * from './use-did-mount-effect';
6
6
  export * from './use-mixed-styles';
7
7
  export * from './use-merged-refs';
8
+ export * from './use-merge';
@@ -0,0 +1,8 @@
1
+ import { useMemo } from 'react';
2
+ import { isNotEmpty, mergeStyles } from '@true-engineering/true-react-platform-helpers';
3
+
4
+ export const useMerge = <T>(one?: T, two?: T): T | undefined =>
5
+ useMemo(
6
+ () => (isNotEmpty(one) && isNotEmpty(two) ? mergeStyles(one, two) : one ?? two),
7
+ [one, two],
8
+ );
@@ -1,14 +1,12 @@
1
1
  import { useMemo } from 'react';
2
- import { isNotEmpty, mergeStyles } from '@true-engineering/true-react-platform-helpers';
2
+ import { isObject } from '@true-engineering/true-react-platform-helpers';
3
+ import type { IMixedStyles } from '../theme';
4
+
5
+ export const mixStyles = <T>(...tweakStyles: Array<IMixedStyles<T>>): Array<NonNullable<T>> =>
6
+ tweakStyles.flat().filter(isObject) as Array<NonNullable<T>>;
3
7
 
4
8
  export const useMixedStyles = <StyleSheet>(
5
- baseStyles?: StyleSheet,
6
- tweakStyles?: StyleSheet,
7
- ): StyleSheet | undefined =>
8
- useMemo(
9
- () =>
10
- isNotEmpty(baseStyles) && isNotEmpty(tweakStyles)
11
- ? mergeStyles(baseStyles, tweakStyles)
12
- : baseStyles ?? tweakStyles,
13
- [baseStyles, tweakStyles],
14
- );
9
+ baseStyles?: IMixedStyles<StyleSheet>,
10
+ tweakStyles?: IMixedStyles<StyleSheet>,
11
+ ): Array<NonNullable<StyleSheet>> =>
12
+ useMemo(() => mixStyles(baseStyles, tweakStyles), [baseStyles, tweakStyles]);
@@ -1,11 +1,24 @@
1
- import { useMemo } from 'react';
2
- import { isEmpty, isNotEmpty, mergeStyles } from '@true-engineering/true-react-platform-helpers';
3
- import { areStylesThemed, getTheme, IComponentName, themedStyles } from '../theme';
1
+ import { useContext, useMemo } from 'react';
2
+ import {
3
+ isArrayNotEmpty,
4
+ isNotEmpty,
5
+ mergeStyles,
6
+ } from '@true-engineering/true-react-platform-helpers';
7
+ import {
8
+ areStylesThemed,
9
+ themedStyles,
10
+ IComponentName,
11
+ IMaybeArray,
12
+ useTheme,
13
+ JssContext,
14
+ IMixedStyles,
15
+ } from '../theme';
16
+ import { mixStyles } from './use-mixed-styles';
4
17
 
5
18
  // TODO: Можно усилить типы
6
19
  export const useTweakStyles = <StyleSheet, ClassName extends keyof StyleSheet & `tweak${string}`>({
7
20
  innerStyles,
8
- tweakStyles: currentComponentTweakStyles,
21
+ tweakStyles,
9
22
  className,
10
23
  currentComponentName,
11
24
  }: {
@@ -13,12 +26,12 @@ export const useTweakStyles = <StyleSheet, ClassName extends keyof StyleSheet &
13
26
  * Это tweakStyles, определенные в родительском компоненте
14
27
  * (например, стили для Input, определенные в стилях компонента Select)
15
28
  */
16
- innerStyles?: StyleSheet[ClassName];
29
+ innerStyles?: IMixedStyles<StyleSheet[ClassName]>;
17
30
  /**
18
31
  * Пропса tweakStyles из родительского компонента
19
32
  * (это уже непосредственно ISelectProps.tweakStyles)
20
33
  */
21
- tweakStyles?: StyleSheet;
34
+ tweakStyles?: IMaybeArray<StyleSheet>;
22
35
  /**
23
36
  * Класс для переопределения tweakStyles из-вне. (Например, 'tweakInput')
24
37
  */
@@ -27,31 +40,40 @@ export const useTweakStyles = <StyleSheet, ClassName extends keyof StyleSheet &
27
40
  * Название компонента который вызывает useTweakStyles. (В данном примере 'Select')
28
41
  */
29
42
  currentComponentName?: IComponentName;
30
- }): StyleSheet[ClassName] =>
31
- useMemo(() => {
32
- const theme = getTheme();
33
- // tweakStyles, переопределенные в теме родительского компонента
43
+ }): Array<NonNullable<StyleSheet[ClassName]>> => {
44
+ const theme = useTheme();
45
+ const isMergeDisabled = isNotEmpty(useContext(JssContext).tweakStylesArch);
46
+
47
+ return useMemo(() => {
34
48
  const themeStyles = isNotEmpty(currentComponentName)
35
- ? (theme.components?.[currentComponentName]?.[className as never] as StyleSheet[ClassName])
49
+ ? (theme.components?.[currentComponentName] as StyleSheet)
36
50
  : undefined;
37
- const tweakStyles = currentComponentTweakStyles?.[className];
38
51
 
39
- // Если нечего мержить
40
- const styles = [innerStyles, tweakStyles, themeStyles].filter(isNotEmpty);
41
- if (styles.length <= 1) {
42
- return styles[0];
52
+ const resultStyles = mixStyles(
53
+ innerStyles,
54
+ mixStyles(themeStyles, tweakStyles).map((style) => style[className]),
55
+ );
56
+
57
+ if (isMergeDisabled || resultStyles.length < 2) {
58
+ return resultStyles;
59
+ }
60
+
61
+ const [maybeInnerStyles, maybeThemeStyles, ...rest] = resultStyles;
62
+
63
+ if (
64
+ maybeThemeStyles !== themeStyles?.[className] || // Если нет themeStyles или innerStyles
65
+ isArrayNotEmpty(rest) // Или есть tweakStyles
66
+ ) {
67
+ // Мёржим как есть
68
+ return [mergeStyles(maybeInnerStyles, maybeThemeStyles, ...rest)];
43
69
  }
44
70
 
45
- // Мы можем подмержить тему в innerStyles, чтобы сохранить кэширование
46
- if (isNotEmpty(innerStyles) && isNotEmpty(themeStyles) && isEmpty(tweakStyles)) {
47
- return areStylesThemed(innerStyles) ? innerStyles : themedStyles(innerStyles, themeStyles);
71
+ // Иначе мёржим themeStyles в innerStyles
72
+ if (!areStylesThemed(maybeInnerStyles)) {
73
+ themedStyles(maybeInnerStyles, maybeThemeStyles);
48
74
  }
49
75
 
50
- // Ну тут уже прощай кэш
51
- return mergeStyles(innerStyles, themeStyles, tweakStyles);
52
- }, [
53
- innerStyles,
54
- className,
55
- currentComponentName,
56
- currentComponentTweakStyles,
57
- ]) as StyleSheet[ClassName];
76
+ // И возвращаем только innerStyles
77
+ return [maybeInnerStyles];
78
+ }, [innerStyles, className, currentComponentName, tweakStyles, theme, isMergeDisabled]);
79
+ };
@@ -1,6 +1,6 @@
1
- import { createContext, FC, ReactNode } from 'react';
1
+ import { createContext, FC, ReactNode, useContext, useMemo } from 'react';
2
2
  import { common } from './common';
3
- import { IUiKitTheme } from './types';
3
+ import type { IUiKitTheme } from './types';
4
4
 
5
5
  export interface IThemedProviderProps {
6
6
  theme: IUiKitTheme;
@@ -11,11 +11,16 @@ let globalTheme: IUiKitTheme;
11
11
 
12
12
  export const getTheme = (): IUiKitTheme => globalTheme;
13
13
 
14
- export const ThemeContext = createContext<{
14
+ interface ThemeContextValue {
15
15
  theme: IUiKitTheme;
16
- }>({ theme: common });
16
+ }
17
+
18
+ export const ThemeContext = createContext<ThemeContextValue>({ theme: common });
17
19
 
18
20
  export const ThemeProvider: FC<IThemedProviderProps> = ({ theme, children }) => {
19
21
  globalTheme = theme;
20
- return <ThemeContext.Provider value={{ theme }}>{children}</ThemeContext.Provider>;
22
+ const value: ThemeContextValue = useMemo(() => ({ theme }), [theme]);
23
+ return <ThemeContext.Provider value={value}>{children}</ThemeContext.Provider>;
21
24
  };
25
+
26
+ export const useTheme = (): IUiKitTheme => useContext(ThemeContext).theme;
@@ -91,9 +91,12 @@ export const helpers = {
91
91
 
92
92
  // Chromium
93
93
  '@supports selector(::-webkit-scrollbar)': {
94
+ '--webkit-scrollbar-width': '10px',
95
+ '--webkit-scrollbar-height': '10px',
96
+
94
97
  '&::-webkit-scrollbar': {
95
- height: 10,
96
- width: 10,
98
+ width: 'var(--webkit-scrollbar-width)',
99
+ height: 'var(--webkit-scrollbar-height)',
97
100
 
98
101
  '&-thumb': {
99
102
  width: 6,
@@ -0,0 +1,78 @@
1
+ import { useContext, useInsertionEffect, useMemo } from 'react';
2
+ import { createUseStyles, Styles } from 'react-jss';
3
+ import { isNotEmpty, mergeStyles } from '@true-engineering/true-react-platform-helpers';
4
+ import { mixStyles } from '../hooks/use-mixed-styles';
5
+ import { getTheme, useTheme } from './Provider';
6
+ import {
7
+ checkStyles,
8
+ cleanStyles,
9
+ getTweakStylesCache,
10
+ isStylesNotEmpty,
11
+ mergeTweakStyles,
12
+ } from './helpers';
13
+ import { ThemedStylesManager, JssContext } from './true-jss';
14
+ import { IComponentName, IStyles, IUseStyles } from './types';
15
+
16
+ const DEFAULT_NAME = 'LocalComponent';
17
+
18
+ export function createThemedStyles<C extends string>(
19
+ ...args: [IStyles<C>] | [IComponentName, IStyles<C>]
20
+ ): IUseStyles<C> {
21
+ const [componentName, styles] = args.length === 2 ? args : [undefined, args[0]];
22
+ const name = componentName ?? DEFAULT_NAME;
23
+
24
+ if (process.env.NODE_ENV !== 'production') {
25
+ checkStyles(name, styles);
26
+ }
27
+
28
+ const tweakStylesManager = new ThemedStylesManager({ styles, name });
29
+ const tweakStylesCache = getTweakStylesCache();
30
+
31
+ const useStyles = createUseStyles<C>((tweakStyles) => {
32
+ const theme = getTheme();
33
+ return mergeStyles(
34
+ styles as Styles<C>,
35
+ isNotEmpty(componentName) ? (theme?.components?.[componentName] as Styles<C>) : undefined,
36
+ tweakStyles,
37
+ );
38
+ });
39
+
40
+ return (data) => {
41
+ const jssContext = useContext(JssContext);
42
+
43
+ const tweakStyles = useMemo(
44
+ () => mixStyles(data?.theme).filter(isStylesNotEmpty),
45
+ [data?.theme],
46
+ );
47
+
48
+ if (jssContext.tweakStylesArch !== 'true-jss') {
49
+ const tweakStylesStrategy =
50
+ jssContext.tweakStylesArch === 'react-jss' ? tweakStylesCache : mergeTweakStyles;
51
+
52
+ const theme = useMemo(
53
+ () => cleanStyles(tweakStylesStrategy(tweakStyles)),
54
+ [tweakStylesStrategy, tweakStyles],
55
+ );
56
+
57
+ return useStyles(isNotEmpty(data) ? { ...data, theme } : data);
58
+ }
59
+
60
+ const { components } = useTheme();
61
+
62
+ const themeStyles = isNotEmpty(componentName) ? components?.[componentName] : undefined;
63
+
64
+ const classes = tweakStylesManager.getClasses(themeStyles, tweakStyles, jssContext);
65
+
66
+ useInsertionEffect(() => {
67
+ const unmanage = tweakStylesManager.manage(themeStyles);
68
+ return unmanage;
69
+ }, [themeStyles]);
70
+
71
+ useInsertionEffect(() => {
72
+ const unmanage = tweakStylesManager.manageTweak(themeStyles, tweakStyles);
73
+ return unmanage;
74
+ }, [themeStyles, classes]);
75
+
76
+ return classes;
77
+ };
78
+ }
@@ -1,29 +1,31 @@
1
- import { createUseStyles, Styles } from 'react-jss';
2
- import merge from 'lodash-es/merge';
1
+ import { merge } from 'lodash-es';
3
2
  import {
4
3
  isArrayNotEmpty,
5
4
  isNotEmpty,
6
5
  mergeStyles,
7
6
  } from '@true-engineering/true-react-platform-helpers';
8
- import { getTheme } from './Provider';
9
- import { IComponentName, IStyles, IUseStyles } from './types';
7
+ import { IMaybeArray } from './types';
10
8
 
11
9
  const EMPTY_STYLES = {};
12
10
 
13
- const isTweakStyle = (key: string): boolean => key.startsWith('tweak');
11
+ export const isTweakStyle = (key: string): boolean => key.startsWith('tweak');
14
12
 
15
- const cleanStyles = <T>(tweakStyles?: T): T =>
16
- isNotEmpty(tweakStyles) && Object.keys(tweakStyles).some((key) => !isTweakStyle(key))
17
- ? tweakStyles
18
- : (EMPTY_STYLES as T);
13
+ export const isStylesNotEmpty = <T>(tweakStyles?: T): tweakStyles is NonNullable<T> =>
14
+ isNotEmpty(tweakStyles) && !Object.keys(tweakStyles).every(isTweakStyle);
19
15
 
20
- const checkStyles = (componentName: string, styles: Record<string, unknown>): void => {
21
- const invalidClasses = Object.keys(styles).reduce((acc, key) => {
22
- if (isTweakStyle(key)) {
23
- acc.push(key);
24
- }
25
- return acc;
26
- }, [] as string[]);
16
+ export const cleanStyles = <T>(tweakStyles?: T): T =>
17
+ isStylesNotEmpty(tweakStyles) ? tweakStyles : (EMPTY_STYLES as T);
18
+
19
+ export const mergeTweakStyles = <T>(tweakStyles?: IMaybeArray<T>): T | undefined => {
20
+ if (!Array.isArray(tweakStyles)) {
21
+ return tweakStyles;
22
+ }
23
+ const [first, ...rest] = tweakStyles;
24
+ return isArrayNotEmpty(rest) ? mergeStyles(first, ...rest) : first;
25
+ };
26
+
27
+ export const checkStyles = (componentName: string, styles: Record<string, unknown>): void => {
28
+ const invalidClasses = Object.keys(styles).filter(isTweakStyle);
27
29
 
28
30
  if (isArrayNotEmpty(invalidClasses)) {
29
31
  console.error(
@@ -47,30 +49,28 @@ export const themedStyles = <T>(styles: T, themeComponentStyles: T): T => {
47
49
  return styles;
48
50
  };
49
51
 
50
- export function createThemedStyles<C extends string, P>(styles: IStyles<C, P>): IUseStyles<C, P>;
52
+ type TweakStylesCache = (styles: object[], prev?: object[]) => object | undefined;
53
+ export const getTweakStylesCache = (): TweakStylesCache => {
54
+ let mergedStyles: object | undefined;
55
+ const cache = new WeakMap<object, TweakStylesCache>();
51
56
 
52
- export function createThemedStyles<C extends string, P>(
53
- componentName: IComponentName,
54
- styles: IStyles<C, P>,
55
- ): IUseStyles<C, P>;
57
+ const getNext = (next: object) => {
58
+ if (!cache.has(next)) {
59
+ cache.set(next, getTweakStylesCache());
60
+ }
61
+ return cache.get(next)!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
62
+ };
56
63
 
57
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
58
- export function createThemedStyles<C extends string, P>(...args: any[]): IUseStyles<C, P> {
59
- const shouldUseTheme = args.length > 1;
60
- const styles: IStyles<C, P> = shouldUseTheme ? args[1] : args[0];
61
- const componentName: IComponentName | undefined = shouldUseTheme ? args[0] : undefined;
64
+ return (styles, prev = []) => {
65
+ const [next, ...rest] = styles;
66
+ if (isNotEmpty(next)) {
67
+ return getNext(next)(rest, prev.concat(next));
68
+ }
62
69
 
63
- if (process.env.NODE_ENV !== 'production') {
64
- checkStyles(componentName ?? 'LocalComponent', styles);
65
- }
66
- const useStyles = createUseStyles<C, P>((tweakStyles) => {
67
- const theme = getTheme();
68
- return mergeStyles(
69
- styles as Styles<C, P>,
70
- isNotEmpty(componentName) ? (theme?.components?.[componentName] as Styles<C, P>) : undefined,
71
- tweakStyles,
72
- );
73
- });
70
+ if (isNotEmpty(mergedStyles)) {
71
+ return mergedStyles;
72
+ }
74
73
 
75
- return (data) => useStyles(isNotEmpty(data) ? { ...data, theme: cleanStyles(data.theme) } : data);
76
- }
74
+ return (mergedStyles = mergeTweakStyles(prev));
75
+ };
76
+ };
@@ -1,4 +1,6 @@
1
1
  export * from './Provider';
2
+ export * from './true-jss';
2
3
  export * from './common';
3
4
  export * from './types';
4
5
  export * from './helpers';
6
+ export * from './create-themed-styles';
@@ -0,0 +1,92 @@
1
+ import { jss } from 'react-jss';
2
+ import type { Classes, StyleSheet } from 'jss';
3
+ import { isNotEmpty, mergeStyles } from '@true-engineering/true-react-platform-helpers';
4
+ import { IStyles } from '../types';
5
+ import { IPartialStyle, TweakStylesManager } from './TweakStylesManager';
6
+ import { IJssContext } from './jss-context';
7
+
8
+ interface IStyleSheet<C extends string> {
9
+ manager: TweakStylesManager<C>;
10
+ refs: number;
11
+ sheet: StyleSheet<C>;
12
+ detachTimeout?: number;
13
+ }
14
+
15
+ const NO_THEME_STYLES: IPartialStyle = {};
16
+
17
+ interface IThemedStylesManagerOptions<C extends string> {
18
+ styles: IStyles<C>;
19
+ name: string;
20
+ }
21
+
22
+ export class ThemedStylesManager<C extends string> {
23
+ #styleSheetMap = new Map<IPartialStyle, IStyleSheet<C>>();
24
+ #styles: IStyles<C>;
25
+ #name: string;
26
+
27
+ constructor({ styles, name }: IThemedStylesManagerOptions<C>) {
28
+ this.#styles = styles;
29
+ this.#name = name;
30
+ }
31
+
32
+ public getClasses(
33
+ theme = NO_THEME_STYLES,
34
+ tweak: IPartialStyle[],
35
+ jssContext: IJssContext,
36
+ ): Classes<C> {
37
+ return this.getOrCreate(theme, jssContext).manager.getClasses(tweak);
38
+ }
39
+
40
+ public manageTweak(theme = NO_THEME_STYLES, tweak: IPartialStyle[]): VoidFunction | undefined {
41
+ this.#styleSheetMap.get(theme)?.manager.manage(tweak);
42
+
43
+ return () => this.#styleSheetMap.get(theme)?.manager.unmanage(tweak);
44
+ }
45
+
46
+ public manage(theme = NO_THEME_STYLES): VoidFunction | undefined {
47
+ const value = this.#styleSheetMap.get(theme);
48
+ if (!isNotEmpty(value)) {
49
+ return;
50
+ }
51
+ if (isNotEmpty(value.detachTimeout)) {
52
+ clearTimeout(value.detachTimeout);
53
+ value.detachTimeout = undefined;
54
+ }
55
+ value.refs += 1;
56
+ if (value.refs > 0) {
57
+ value.sheet.attach();
58
+ }
59
+
60
+ return () => this.unmanage(theme);
61
+ }
62
+
63
+ private unmanage(theme = NO_THEME_STYLES): void {
64
+ const value = this.#styleSheetMap.get(theme);
65
+ if (!isNotEmpty(value) || value.refs === 0) {
66
+ return;
67
+ }
68
+ value.refs -= 1;
69
+ if (value.refs === 0 && !isNotEmpty(value.detachTimeout)) {
70
+ value.detachTimeout = window.setTimeout(() => {
71
+ if (value.refs === 0) {
72
+ value.sheet.detach();
73
+ }
74
+ }, 100);
75
+ }
76
+ }
77
+
78
+ private getOrCreate(theme: IPartialStyle, jssContext: IJssContext): IStyleSheet<C> {
79
+ if (!this.#styleSheetMap.has(theme)) {
80
+ const sheet = jss.createStyleSheet(mergeStyles(this.#styles, theme), {
81
+ link: true,
82
+ meta: this.#name,
83
+ generateId: jssContext.generateId,
84
+ });
85
+ const classes = { ...sheet.classes };
86
+ const manager = new TweakStylesManager({ sheet, classes });
87
+ this.#styleSheetMap.set(theme, { sheet, manager, refs: 0 });
88
+ }
89
+
90
+ return this.#styleSheetMap.get(theme)!; // eslint-disable-line @typescript-eslint/no-non-null-assertion
91
+ }
92
+ }
@@ -0,0 +1,157 @@
1
+ import type { StyleSheet, Classes, Rule } from 'jss';
2
+ import {
3
+ isArrayNotEmpty,
4
+ isNotEmpty,
5
+ joinStrings,
6
+ mergeStyles,
7
+ } from '@true-engineering/true-react-platform-helpers';
8
+ import { isTweakStyle } from '../helpers';
9
+
10
+ export type IPartialStyle = object;
11
+
12
+ interface TweakRules<C extends string> {
13
+ classes: Classes<C>;
14
+ refs?: number;
15
+ rules: Map<C, Rule>;
16
+ }
17
+
18
+ interface TweakStylesManagerOptions<C extends string> {
19
+ sheet: StyleSheet<C>;
20
+ classes: Classes<C>;
21
+ tweak?: IPartialStyle[];
22
+ }
23
+
24
+ export class TweakStylesManager<C extends string> {
25
+ #map = new WeakMap<IPartialStyle, TweakStylesManager<C>>();
26
+ #sheet: StyleSheet<C>;
27
+ #classes: Classes<C>;
28
+ #tweak: IPartialStyle[];
29
+
30
+ #tweakRules: TweakRules<C> | undefined;
31
+ constructor({ sheet, classes, tweak = [] }: TweakStylesManagerOptions<C>) {
32
+ this.#sheet = sheet;
33
+ this.#classes = classes;
34
+ this.#tweak = tweak;
35
+ }
36
+
37
+ public getClasses([next, ...rest]: IPartialStyle[]): Classes<C> {
38
+ const nextManager = this.getNextManager(next);
39
+ if (isNotEmpty(nextManager)) {
40
+ return nextManager.getClasses(rest);
41
+ }
42
+ return this.getOrCreate()?.classes ?? this.#classes;
43
+ }
44
+
45
+ public manage([next, ...rest]: IPartialStyle[]): void {
46
+ const nextManager = this.getNextManager(next);
47
+ if (isNotEmpty(nextManager)) {
48
+ return nextManager.manage(rest);
49
+ }
50
+ if (!isNotEmpty(this.#tweakRules)) {
51
+ return;
52
+ }
53
+ if (this.#tweakRules.refs === 0) {
54
+ this.insertRules();
55
+ }
56
+ this.#tweakRules.refs = (this.#tweakRules.refs ?? 0) + 1;
57
+ }
58
+
59
+ public unmanage([next, ...rest]: IPartialStyle[]): void {
60
+ const nextManager = this.getNextManager(next);
61
+ if (isNotEmpty(nextManager)) {
62
+ return nextManager.unmanage(rest);
63
+ }
64
+ if (!isNotEmpty(this.#tweakRules) || this.#tweakRules.refs === 0) {
65
+ return;
66
+ }
67
+ this.#tweakRules.refs = (this.#tweakRules.refs ?? 1) - 1;
68
+ if (this.#tweakRules.refs === 0) {
69
+ this.deleteRules();
70
+ }
71
+ }
72
+
73
+ private getNextManager(next?: IPartialStyle) {
74
+ if (!isNotEmpty(next)) {
75
+ return;
76
+ }
77
+ if (!this.#map.has(next)) {
78
+ const manager = new TweakStylesManager({
79
+ sheet: this.#sheet,
80
+ classes: this.#classes,
81
+ tweak: this.#tweak.concat(next),
82
+ });
83
+ this.#map.set(next, manager);
84
+ }
85
+ return this.#map.get(next);
86
+ }
87
+
88
+ private getOrCreate() {
89
+ if (!isNotEmpty(this.#tweakRules) && isArrayNotEmpty(this.#tweak)) {
90
+ const [first, ...rest] = this.#tweak;
91
+ const styles = isArrayNotEmpty(rest) ? mergeStyles(first, ...rest) : first;
92
+
93
+ const rules = this.addRules(styles);
94
+
95
+ const classes = { ...this.#classes };
96
+ rules.forEach((rule, key) => {
97
+ classes[key] = joinStrings([classes[key], rule.id], ' ');
98
+ });
99
+
100
+ this.#tweakRules = { classes, rules };
101
+ }
102
+ return this.#tweakRules;
103
+ }
104
+
105
+ private addRules(styles: IPartialStyle) {
106
+ return new Map(
107
+ Object.entries(styles)
108
+ .map(([key, style]) => this.addRule(key as C, style))
109
+ .filter(isNotEmpty),
110
+ );
111
+ }
112
+
113
+ private addRule(key: C, style: object) {
114
+ if (isTweakStyle(key)) {
115
+ return;
116
+ }
117
+ const rule = this.#sheet.addRule(key, style, { index: this.getInsertIndex(key) });
118
+ return isNotEmpty(rule) ? ([key, rule] as const) : undefined;
119
+ }
120
+
121
+ private insertRules() {
122
+ this.#tweakRules?.rules.forEach((rule, key) => {
123
+ this.#sheet.rules.register(rule);
124
+ this.#sheet.rules.index.splice(this.getInsertIndex(key), 0, rule);
125
+ this.#sheet.insertRule(rule);
126
+ });
127
+ }
128
+
129
+ private deleteRules() {
130
+ this.#tweakRules?.rules.forEach((rule) => this.#sheet.deleteRule(rule));
131
+ }
132
+
133
+ private getInsertIndex(key: C) {
134
+ return (
135
+ this.#sheet.rules.index.findIndex((rule) => rule.key === key) + 1 ||
136
+ this.#sheet.rules.index.length
137
+ );
138
+ }
139
+ }
140
+
141
+ declare module 'jss' {
142
+ export interface RuleList {
143
+ index: Rule[];
144
+ }
145
+ // eslint-disable-next-line no-shadow
146
+ export interface StyleSheet<RuleName> {
147
+ rules: RuleList;
148
+ // eslint-disable-next-line @typescript-eslint/method-signature-style
149
+ deleteRule(rule: Rule): boolean;
150
+ // eslint-disable-next-line @typescript-eslint/method-signature-style
151
+ addRule(
152
+ name: RuleName,
153
+ style: JssStyle,
154
+ options?: Partial<Rule['options']>,
155
+ ): (Rule & { id: string }) | null;
156
+ }
157
+ }
@@ -0,0 +1,2 @@
1
+ export * from './ThemedStylesManager';
2
+ export * from './jss-context';