@cloud-ru/uikit-product-utils 7.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +671 -0
- package/LICENSE +201 -0
- package/README.md +264 -0
- package/package.json +54 -0
- package/src/components/ConfigProvider/ConfigProvider.tsx +20 -0
- package/src/components/ConfigProvider/index.ts +1 -0
- package/src/components/ForThemeMode/ForThemeMode.tsx +14 -0
- package/src/components/ForThemeMode/hooks.ts +18 -0
- package/src/components/ForThemeMode/index.ts +2 -0
- package/src/components/index.ts +2 -0
- package/src/constants/adaptive.ts +45 -0
- package/src/constants/displayMode.ts +25 -0
- package/src/constants/environment.ts +16 -0
- package/src/helpers/tryParseJson.ts +12 -0
- package/src/hooks/index.ts +13 -0
- package/src/hooks/private/getCustomStore.ts +19 -0
- package/src/hooks/useAdaptive.tsx +8 -0
- package/src/hooks/useBrand.ts +31 -0
- package/src/hooks/useComponentSize.tsx +42 -0
- package/src/hooks/useConfig.ts +170 -0
- package/src/hooks/useDisplayMode.ts +10 -0
- package/src/hooks/useEventHandler.ts +21 -0
- package/src/hooks/useForceUpdate.ts +6 -0
- package/src/hooks/useForceUpdateOnPageLoadedCompletely.ts +13 -0
- package/src/hooks/useLanguage.ts +62 -0
- package/src/hooks/useMatchMedia.ts +52 -0
- package/src/hooks/useTextProvider.ts +14 -0
- package/src/hooks/useTheme.ts +31 -0
- package/src/hooks/useUniqueId.ts +7 -0
- package/src/index.ts +7 -0
- package/src/styles/default.ts +67 -0
- package/src/styles/index.ts +1 -0
- package/src/types/adaptive.ts +9 -0
- package/src/types/general.ts +10 -0
- package/src/types/index.ts +5 -0
- package/src/types/language.ts +227 -0
- package/src/types/theme.ts +19 -0
- package/src/types/withSupportProps.ts +6 -0
- package/src/utils/alert.ts +5 -0
- package/src/utils/createTextProvider.ts +18 -0
- package/src/utils/excludeSupportProps.ts +6 -0
- package/src/utils/extractSupportProps.ts +6 -0
- package/src/utils/getAdaptive.ts +23 -0
- package/src/utils/getDisplayMode.ts +34 -0
- package/src/utils/getMatchMedia.ts +38 -0
- package/src/utils/getUserAgentInfo.ts +36 -0
- package/src/utils/index.ts +10 -0
- package/src/utils/keyboardSelectHandler.ts +8 -0
- package/src/utils/private/constants.ts +1 -0
- package/src/utils/private/excludeProps.ts +11 -0
- package/src/utils/private/extractProps.ts +7 -0
- package/src/utils/uniqueId.ts +18 -0
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export * from './useLanguage';
|
|
2
|
+
export * from './useComponentSize';
|
|
3
|
+
export * from './useTheme';
|
|
4
|
+
export * from './useBrand';
|
|
5
|
+
export * from './useForceUpdate';
|
|
6
|
+
export * from './useForceUpdateOnPageLoadedCompletely';
|
|
7
|
+
export * from './useEventHandler';
|
|
8
|
+
export * from './useUniqueId';
|
|
9
|
+
export * from './useConfig';
|
|
10
|
+
export * from './useTextProvider';
|
|
11
|
+
export * from './useAdaptive';
|
|
12
|
+
export * from './useDisplayMode';
|
|
13
|
+
export * from './useMatchMedia';
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { DEFAULT } from '../../constants/environment';
|
|
2
|
+
import { WindowStore } from '../../types';
|
|
3
|
+
|
|
4
|
+
export function getCustomStore(
|
|
5
|
+
defaultStore: Partial<WindowStore['sbercloudUIKit']> = {},
|
|
6
|
+
): WindowStore['sbercloudUIKit'] {
|
|
7
|
+
const customWindow: WindowStore = globalThis as WindowStore & { sbercloudUIKit: null };
|
|
8
|
+
|
|
9
|
+
if (!customWindow.sbercloudUIKit) {
|
|
10
|
+
customWindow.sbercloudUIKit = {
|
|
11
|
+
brand: DEFAULT.BRAND,
|
|
12
|
+
theme: DEFAULT.THEME,
|
|
13
|
+
languageCode: DEFAULT.LANGUAGE,
|
|
14
|
+
...defaultStore,
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
return customWindow.sbercloudUIKit;
|
|
19
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { LayoutType } from '../types/adaptive';
|
|
2
|
+
import { getAdaptive } from '../utils';
|
|
3
|
+
import { useAdaptiveMatchMedia } from './useMatchMedia';
|
|
4
|
+
|
|
5
|
+
export function useAdaptive(): { layoutType: LayoutType } {
|
|
6
|
+
const matchMedia = useAdaptiveMatchMedia();
|
|
7
|
+
return getAdaptive(matchMedia);
|
|
8
|
+
}
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isBrowser } from '@snack-uikit/utils';
|
|
4
|
+
|
|
5
|
+
import { DEFAULT, POST_MESSAGE_KEY } from '../constants/environment';
|
|
6
|
+
import { tryParseJson } from '../helpers/tryParseJson';
|
|
7
|
+
import { Brand } from '../types/theme';
|
|
8
|
+
import { getCustomStore } from './private/getCustomStore';
|
|
9
|
+
|
|
10
|
+
export const useBrand = () => {
|
|
11
|
+
const store = getCustomStore();
|
|
12
|
+
const [brand, setBrand] = useState(store.brand || DEFAULT.BRAND);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const receiveChangeThemeDoneMessage = (event: MessageEvent) => {
|
|
16
|
+
const eventData = tryParseJson(event.data);
|
|
17
|
+
if (eventData.key !== POST_MESSAGE_KEY.changeBrandDone) return;
|
|
18
|
+
setBrand(eventData.value);
|
|
19
|
+
};
|
|
20
|
+
window.addEventListener('message', receiveChangeThemeDoneMessage, false);
|
|
21
|
+
|
|
22
|
+
return () => window.removeEventListener('message', receiveChangeThemeDoneMessage, false);
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const changeBrand = useCallback((brand: Brand) => {
|
|
26
|
+
isBrowser() &&
|
|
27
|
+
window.postMessage(JSON.stringify({ key: POST_MESSAGE_KEY.changeBrand, value: brand }), location.origin);
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
return { brand, changeBrand };
|
|
31
|
+
};
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { RefObject, useCallback, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useLayoutEffect } from '@snack-uikit/utils';
|
|
4
|
+
|
|
5
|
+
function getSize(el: HTMLElement | null) {
|
|
6
|
+
if (!el) {
|
|
7
|
+
return {
|
|
8
|
+
width: 0,
|
|
9
|
+
height: 0,
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return {
|
|
14
|
+
width: el.offsetWidth,
|
|
15
|
+
height: el.offsetHeight,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export function useComponentSize(ref: RefObject<HTMLElement>) {
|
|
20
|
+
const [size, setSize] = useState(getSize(ref.current));
|
|
21
|
+
|
|
22
|
+
const handleResize = useCallback(() => {
|
|
23
|
+
if (ref.current) {
|
|
24
|
+
setSize(getSize(ref.current));
|
|
25
|
+
}
|
|
26
|
+
}, [ref]);
|
|
27
|
+
|
|
28
|
+
useLayoutEffect(() => {
|
|
29
|
+
if (!ref.current) return;
|
|
30
|
+
|
|
31
|
+
handleResize();
|
|
32
|
+
|
|
33
|
+
const resizeObserver = new ResizeObserver(handleResize);
|
|
34
|
+
resizeObserver.observe(ref.current);
|
|
35
|
+
|
|
36
|
+
return () => {
|
|
37
|
+
resizeObserver.disconnect();
|
|
38
|
+
};
|
|
39
|
+
}, [handleResize, ref]);
|
|
40
|
+
|
|
41
|
+
return size;
|
|
42
|
+
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
import { useCallback, useEffect, useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import AdminBrandThemes from '@sbercloud/figma-tokens-admin/build/css/brand.module.css';
|
|
4
|
+
import CloudBrandThemes from '@sbercloud/figma-tokens-cloud-platform/build/css/brand.module.css';
|
|
5
|
+
import GigaIdBrandThemes from '@sbercloud/figma-tokens-giga-id/build/css/brand.module.css';
|
|
6
|
+
import MLSpaceBrandThemes from '@sbercloud/figma-tokens-mlspace/build/css/brand.module.css';
|
|
7
|
+
import SiteBrandThemes from '@sbercloud/figma-tokens-web/build/css/brand.module.css';
|
|
8
|
+
import { isBrowser, useLayoutEffect } from '@snack-uikit/utils';
|
|
9
|
+
|
|
10
|
+
import { POST_MESSAGE_KEY } from '../constants/environment';
|
|
11
|
+
import { tryParseJson } from '../helpers/tryParseJson';
|
|
12
|
+
import { Brand, LanguageCodeType, Themes } from '../types';
|
|
13
|
+
import { getCustomStore } from './private/getCustomStore';
|
|
14
|
+
|
|
15
|
+
type UseConfigProps = {
|
|
16
|
+
languageCode?: LanguageCodeType;
|
|
17
|
+
theme?: Themes;
|
|
18
|
+
brand?: Brand;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const themeMap = {
|
|
22
|
+
[Themes.Green]: CloudBrandThemes.light,
|
|
23
|
+
[Themes.GreenDark]: CloudBrandThemes.dark,
|
|
24
|
+
[Themes.Purple]: MLSpaceBrandThemes.light,
|
|
25
|
+
[Themes.PurpleDark]: MLSpaceBrandThemes.dark,
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const brandMap = {
|
|
29
|
+
[Brand.Cloud]: CloudBrandThemes.light,
|
|
30
|
+
[Brand.CloudDark]: CloudBrandThemes.dark,
|
|
31
|
+
[Brand.MLSpace]: MLSpaceBrandThemes.light,
|
|
32
|
+
[Brand.MLSpaceDark]: MLSpaceBrandThemes.dark,
|
|
33
|
+
[Brand.Admin]: AdminBrandThemes.light,
|
|
34
|
+
[Brand.AdminDark]: AdminBrandThemes.dark,
|
|
35
|
+
[Brand.Site]: SiteBrandThemes.light,
|
|
36
|
+
[Brand.SiteDark]: SiteBrandThemes.dark,
|
|
37
|
+
[Brand.GigaId]: GigaIdBrandThemes.light,
|
|
38
|
+
[Brand.GigaIdDark]: GigaIdBrandThemes.dark,
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
export function useConfig({ languageCode, theme, brand }: UseConfigProps) {
|
|
42
|
+
const store = getCustomStore({ brand, theme, languageCode });
|
|
43
|
+
const [configLanguageCode, setConfigLanguageCodeTheme] = useState(store.languageCode);
|
|
44
|
+
const previousThemeRef = useRef<Themes>();
|
|
45
|
+
const previousBrandRef = useRef<Brand>();
|
|
46
|
+
|
|
47
|
+
const updateTheme = useCallback(
|
|
48
|
+
(newTheme: Themes) => {
|
|
49
|
+
if (isBrowser()) {
|
|
50
|
+
store.theme = newTheme;
|
|
51
|
+
|
|
52
|
+
const html = document.getElementsByTagName('html')[0];
|
|
53
|
+
|
|
54
|
+
if (previousThemeRef.current) {
|
|
55
|
+
html.classList.remove(themeMap[previousThemeRef.current]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
html.setAttribute('data-theme', newTheme);
|
|
59
|
+
|
|
60
|
+
const body = document.getElementsByTagName('body')[0];
|
|
61
|
+
body.setAttribute('data-theme', newTheme);
|
|
62
|
+
|
|
63
|
+
html.classList.add(themeMap[newTheme]);
|
|
64
|
+
|
|
65
|
+
window.postMessage(JSON.stringify({ key: POST_MESSAGE_KEY.changeThemeDone, value: newTheme }), location.origin);
|
|
66
|
+
|
|
67
|
+
previousThemeRef.current = newTheme;
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
[store],
|
|
71
|
+
);
|
|
72
|
+
|
|
73
|
+
const updateBrand = useCallback(
|
|
74
|
+
(newBrand: Brand) => {
|
|
75
|
+
if (isBrowser()) {
|
|
76
|
+
store.brand = newBrand;
|
|
77
|
+
|
|
78
|
+
const html = document.getElementsByTagName('html')[0];
|
|
79
|
+
|
|
80
|
+
if (previousBrandRef.current) {
|
|
81
|
+
html.classList.remove(brandMap[previousBrandRef.current]);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
html.setAttribute('data-brand', newBrand);
|
|
85
|
+
|
|
86
|
+
const body = document.getElementsByTagName('body')[0];
|
|
87
|
+
body.setAttribute('data-brand', newBrand);
|
|
88
|
+
|
|
89
|
+
html.classList.add(brandMap[newBrand]);
|
|
90
|
+
|
|
91
|
+
window.postMessage(JSON.stringify({ key: POST_MESSAGE_KEY.changeBrandDone, value: newBrand }), location.origin);
|
|
92
|
+
|
|
93
|
+
previousBrandRef.current = newBrand;
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
[store],
|
|
97
|
+
);
|
|
98
|
+
|
|
99
|
+
useEffect(() => {
|
|
100
|
+
/*-----------
|
|
101
|
+
--- THEME/BRAND ---
|
|
102
|
+
-----------*/
|
|
103
|
+
|
|
104
|
+
const receiveChangeThemeMessage = (event: MessageEvent) => {
|
|
105
|
+
const eventData = tryParseJson(event.data);
|
|
106
|
+
|
|
107
|
+
switch (eventData.key) {
|
|
108
|
+
case POST_MESSAGE_KEY.changeTheme:
|
|
109
|
+
updateTheme(eventData.value);
|
|
110
|
+
break;
|
|
111
|
+
|
|
112
|
+
case POST_MESSAGE_KEY.changeBrand:
|
|
113
|
+
updateBrand(eventData.value);
|
|
114
|
+
break;
|
|
115
|
+
|
|
116
|
+
default:
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
};
|
|
120
|
+
|
|
121
|
+
window.addEventListener('message', receiveChangeThemeMessage, false);
|
|
122
|
+
|
|
123
|
+
/*--------------
|
|
124
|
+
--- LANGUAGE ---
|
|
125
|
+
--------------*/
|
|
126
|
+
|
|
127
|
+
const receiveChangeLanguageMessage = (event: MessageEvent) => {
|
|
128
|
+
const eventData = tryParseJson(event.data);
|
|
129
|
+
if (eventData.key !== POST_MESSAGE_KEY.changeLanguage) return;
|
|
130
|
+
setConfigLanguageCodeTheme(eventData.value);
|
|
131
|
+
};
|
|
132
|
+
window.addEventListener('message', receiveChangeLanguageMessage, false);
|
|
133
|
+
|
|
134
|
+
return () => {
|
|
135
|
+
window.removeEventListener('message', receiveChangeThemeMessage, false);
|
|
136
|
+
window.removeEventListener('message', receiveChangeLanguageMessage, false);
|
|
137
|
+
};
|
|
138
|
+
}, [updateBrand, updateTheme]);
|
|
139
|
+
|
|
140
|
+
/*-------------
|
|
141
|
+
---- THEME ----
|
|
142
|
+
-------------*/
|
|
143
|
+
|
|
144
|
+
// need to do it this way to update theme before any other child component render & useEffect/useLayoutEffect
|
|
145
|
+
if (previousThemeRef.current !== theme && theme) {
|
|
146
|
+
updateTheme(theme);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (previousBrandRef.current !== brand && brand) {
|
|
150
|
+
updateBrand(brand);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/*--------------
|
|
154
|
+
--- LANGUAGE ---
|
|
155
|
+
--------------*/
|
|
156
|
+
|
|
157
|
+
useLayoutEffect(() => {
|
|
158
|
+
if (languageCode) {
|
|
159
|
+
store.languageCode = languageCode;
|
|
160
|
+
setConfigLanguageCodeTheme(store.languageCode);
|
|
161
|
+
}
|
|
162
|
+
}, [languageCode, store]);
|
|
163
|
+
|
|
164
|
+
useEffect(() => {
|
|
165
|
+
window.postMessage(
|
|
166
|
+
JSON.stringify({ key: POST_MESSAGE_KEY.changeLanguageDone, value: configLanguageCode }),
|
|
167
|
+
location.origin,
|
|
168
|
+
);
|
|
169
|
+
}, [configLanguageCode]);
|
|
170
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { ValueOf } from '@snack-uikit/utils';
|
|
2
|
+
|
|
3
|
+
import { DISPLAY_MODES } from '../constants/displayMode';
|
|
4
|
+
import { getDisplayMode } from '../utils/getDisplayMode';
|
|
5
|
+
import { useDisplayModeMatchMedia } from './useMatchMedia';
|
|
6
|
+
|
|
7
|
+
export function useDisplayMode(): 'unknown' | 'twa' | ValueOf<typeof DISPLAY_MODES> {
|
|
8
|
+
const matchMedia = useDisplayModeMatchMedia();
|
|
9
|
+
return getDisplayMode(matchMedia);
|
|
10
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { useCallback, useRef } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useLayoutEffect } from '@snack-uikit/utils';
|
|
4
|
+
|
|
5
|
+
import { warning } from '../utils';
|
|
6
|
+
|
|
7
|
+
export function useEventHandler<T extends (...args: never[]) => unknown>(handler: T) {
|
|
8
|
+
const handlerRef = useRef<T | null>(null);
|
|
9
|
+
|
|
10
|
+
useLayoutEffect(() => {
|
|
11
|
+
handlerRef.current = handler;
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
return useCallback((...args: Parameters<T>) => {
|
|
15
|
+
const handler = handlerRef.current;
|
|
16
|
+
|
|
17
|
+
warning('The event handler cannot be called during render', handler === null);
|
|
18
|
+
|
|
19
|
+
return handler?.(...args);
|
|
20
|
+
}, []) as T;
|
|
21
|
+
}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useForceUpdate } from './useForceUpdate';
|
|
4
|
+
|
|
5
|
+
export function useForceUpdateOnPageLoadedCompletely() {
|
|
6
|
+
const rerender = useForceUpdate();
|
|
7
|
+
useEffect(() => {
|
|
8
|
+
window.addEventListener('load', rerender);
|
|
9
|
+
return () => {
|
|
10
|
+
window.removeEventListener('load', rerender);
|
|
11
|
+
};
|
|
12
|
+
}, [rerender]);
|
|
13
|
+
}
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isBrowser, useLayoutEffect } from '@snack-uikit/utils';
|
|
4
|
+
|
|
5
|
+
import { DEFAULT, POST_MESSAGE_KEY } from '../constants/environment';
|
|
6
|
+
import { tryParseJson } from '../helpers/tryParseJson';
|
|
7
|
+
import { LanguageCodeType } from '../types';
|
|
8
|
+
import { getCustomStore } from './private/getCustomStore';
|
|
9
|
+
|
|
10
|
+
type useLanguageProps = {
|
|
11
|
+
onlyEnabledLanguage?: boolean;
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export const useLanguage = (props?: useLanguageProps) => {
|
|
15
|
+
const store = getCustomStore();
|
|
16
|
+
const [languageCode, setLanguageCode] = useState<LanguageCodeType>(store.languageCode || DEFAULT.LANGUAGE);
|
|
17
|
+
const onlyEnabledLanguage = props?.onlyEnabledLanguage;
|
|
18
|
+
|
|
19
|
+
useEffect(() => {
|
|
20
|
+
const receiveChangeLanguageDoneMessage = (event: MessageEvent) => {
|
|
21
|
+
const eventData = tryParseJson(event.data);
|
|
22
|
+
if (eventData.key !== POST_MESSAGE_KEY.changeLanguageDone) return;
|
|
23
|
+
setLanguageCode(eventData.value);
|
|
24
|
+
};
|
|
25
|
+
window.addEventListener('message', receiveChangeLanguageDoneMessage, false);
|
|
26
|
+
|
|
27
|
+
return () => window.removeEventListener('message', receiveChangeLanguageDoneMessage, false);
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
useLayoutEffect(() => {
|
|
31
|
+
if (!onlyEnabledLanguage) {
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
const miniCode = languageCode.split('-')[0];
|
|
35
|
+
const enGroup = miniCode === 'en';
|
|
36
|
+
const ruGroup = ['ru', 'be'].includes(miniCode);
|
|
37
|
+
if (enGroup) {
|
|
38
|
+
setLanguageCode(LanguageCodeType.enGB);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (ruGroup) {
|
|
42
|
+
setLanguageCode(LanguageCodeType.ruRU);
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
if (languageCode === LanguageCodeType.cimode) {
|
|
46
|
+
setLanguageCode(LanguageCodeType.cimode);
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
setLanguageCode(LanguageCodeType.ruRU);
|
|
50
|
+
}, [languageCode, onlyEnabledLanguage]);
|
|
51
|
+
|
|
52
|
+
const changeLanguage = useCallback((languageCode: LanguageCodeType) => {
|
|
53
|
+
if (isBrowser()) {
|
|
54
|
+
window.postMessage(
|
|
55
|
+
JSON.stringify({ key: POST_MESSAGE_KEY.changeLanguage, value: languageCode }),
|
|
56
|
+
location.origin,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
}, []);
|
|
60
|
+
|
|
61
|
+
return { languageCode, changeLanguage };
|
|
62
|
+
};
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { useRef, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { useLayoutEffect } from '@snack-uikit/utils';
|
|
4
|
+
|
|
5
|
+
import { ADAPTIVE_QUERIES, INITIAL_ADAPTIVE_QUERIES_VALUE } from '../constants/adaptive';
|
|
6
|
+
import { DISPLAY_MODE_QUERIES, INITIAL_DISPLAY_MODE_QUERIES_VALUE } from '../constants/displayMode';
|
|
7
|
+
import { MatchMediaGeneric } from '../types';
|
|
8
|
+
import { getMatchMediaGeneric, getMediaQueryListGeneric } from '../utils/getMatchMedia';
|
|
9
|
+
|
|
10
|
+
export const useMatchMediaGeneric = <T extends string>({
|
|
11
|
+
queryValues,
|
|
12
|
+
initialValues,
|
|
13
|
+
}: {
|
|
14
|
+
queryValues: Record<T, string>;
|
|
15
|
+
initialValues: MatchMediaGeneric<T>;
|
|
16
|
+
}): MatchMediaGeneric<T> => {
|
|
17
|
+
const updatedHandlers = {
|
|
18
|
+
getMatchMedia: () => getMatchMediaGeneric({ queryValues, initialValues }),
|
|
19
|
+
getMediaQueryList: () => getMediaQueryListGeneric({ queryValues }),
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const handlersRef = useRef(updatedHandlers);
|
|
23
|
+
handlersRef.current = updatedHandlers;
|
|
24
|
+
|
|
25
|
+
const [value, setValue] = useState(handlersRef.current.getMatchMedia);
|
|
26
|
+
|
|
27
|
+
useLayoutEffect(() => {
|
|
28
|
+
const handler = () => setValue(handlersRef.current.getMatchMedia);
|
|
29
|
+
|
|
30
|
+
const mediaQueryList = handlersRef.current.getMediaQueryList();
|
|
31
|
+
|
|
32
|
+
mediaQueryList.forEach(([, mql]) => mql.addEventListener('change', handler));
|
|
33
|
+
|
|
34
|
+
return (): void => mediaQueryList.forEach(([, mql]) => mql.removeEventListener('change', handler));
|
|
35
|
+
}, []);
|
|
36
|
+
|
|
37
|
+
return value;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
export function useAdaptiveMatchMedia() {
|
|
41
|
+
return useMatchMediaGeneric({
|
|
42
|
+
queryValues: ADAPTIVE_QUERIES,
|
|
43
|
+
initialValues: INITIAL_ADAPTIVE_QUERIES_VALUE,
|
|
44
|
+
});
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export function useDisplayModeMatchMedia() {
|
|
48
|
+
return useMatchMediaGeneric({
|
|
49
|
+
queryValues: DISPLAY_MODE_QUERIES,
|
|
50
|
+
initialValues: INITIAL_DISPLAY_MODE_QUERIES_VALUE,
|
|
51
|
+
});
|
|
52
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { useCallback } from 'react';
|
|
2
|
+
|
|
3
|
+
import { LanguageCodeType } from '../types';
|
|
4
|
+
import { useLanguage } from './useLanguage';
|
|
5
|
+
|
|
6
|
+
type TextProviderFunction<T extends string> = (entity: T) => string;
|
|
7
|
+
|
|
8
|
+
export const useTextProvider = <T extends string>(
|
|
9
|
+
textProvider: (language: LanguageCodeType, entity: T) => string,
|
|
10
|
+
): TextProviderFunction<T> => {
|
|
11
|
+
const { languageCode } = useLanguage({ onlyEnabledLanguage: true });
|
|
12
|
+
|
|
13
|
+
return useCallback((entity: T) => textProvider(languageCode, entity), [languageCode, textProvider]);
|
|
14
|
+
};
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { useCallback, useEffect, useState } from 'react';
|
|
2
|
+
|
|
3
|
+
import { isBrowser } from '@snack-uikit/utils';
|
|
4
|
+
|
|
5
|
+
import { DEFAULT, POST_MESSAGE_KEY } from '../constants/environment';
|
|
6
|
+
import { tryParseJson } from '../helpers/tryParseJson';
|
|
7
|
+
import { Themes } from '../types/theme';
|
|
8
|
+
import { getCustomStore } from './private/getCustomStore';
|
|
9
|
+
|
|
10
|
+
export const useTheme = () => {
|
|
11
|
+
const store = getCustomStore();
|
|
12
|
+
const [theme, setTheme] = useState(store.theme || DEFAULT.THEME);
|
|
13
|
+
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
const receiveChangeThemeDoneMessage = (event: MessageEvent) => {
|
|
16
|
+
const eventData = tryParseJson(event.data);
|
|
17
|
+
if (eventData.key !== POST_MESSAGE_KEY.changeThemeDone) return;
|
|
18
|
+
setTheme(eventData.value);
|
|
19
|
+
};
|
|
20
|
+
window.addEventListener('message', receiveChangeThemeDoneMessage, false);
|
|
21
|
+
|
|
22
|
+
return () => window.removeEventListener('message', receiveChangeThemeDoneMessage, false);
|
|
23
|
+
}, []);
|
|
24
|
+
|
|
25
|
+
const changeTheme = useCallback((theme: Themes) => {
|
|
26
|
+
isBrowser() &&
|
|
27
|
+
window.postMessage(JSON.stringify({ key: POST_MESSAGE_KEY.changeTheme, value: theme }), location.origin);
|
|
28
|
+
}, []);
|
|
29
|
+
|
|
30
|
+
return { theme, changeTheme };
|
|
31
|
+
};
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { themeVars } from '@sbercloud/figma-tokens-cloud-platform';
|
|
2
|
+
|
|
3
|
+
function getLoadingWheelAnimationPerChild(quantity: number, duration: number) {
|
|
4
|
+
function getLoadingWheelAnimation(child: number) {
|
|
5
|
+
return `
|
|
6
|
+
&:nth-child(${child}) {
|
|
7
|
+
animation-delay: ${(-child / quantity) * duration}ms;
|
|
8
|
+
animation-duration: ${duration}ms;
|
|
9
|
+
}
|
|
10
|
+
`;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
return Array.from({ length: quantity }, (_, index) => index + 1)
|
|
14
|
+
.map(getLoadingWheelAnimation)
|
|
15
|
+
.join('');
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export const DEFAULT_STYLES = {
|
|
19
|
+
COMMON: `
|
|
20
|
+
box-sizing: border-box;
|
|
21
|
+
margin: 0;
|
|
22
|
+
padding: 0;
|
|
23
|
+
outline: 0;
|
|
24
|
+
|
|
25
|
+
&::after,
|
|
26
|
+
&::before {
|
|
27
|
+
box-sizing: border-box;
|
|
28
|
+
}
|
|
29
|
+
`,
|
|
30
|
+
BORDERLESS: `
|
|
31
|
+
border: none;
|
|
32
|
+
border-radius: 0;
|
|
33
|
+
`,
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export const ANIMATIONS = {
|
|
37
|
+
TRANSITION: '0.2s ease-in-out',
|
|
38
|
+
LOADING_WHEEL: `
|
|
39
|
+
& > path {
|
|
40
|
+
@keyframes loading-wheel {
|
|
41
|
+
0% {
|
|
42
|
+
opacity: 1;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
90% {
|
|
46
|
+
opacity: 0.125;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
100% {
|
|
50
|
+
opacity: 1;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
animation-iteration-count: infinite;
|
|
55
|
+
animation-name: loading-wheel;
|
|
56
|
+
animation-timing-function: linear;
|
|
57
|
+
|
|
58
|
+
${getLoadingWheelAnimationPerChild(8, 1000)};
|
|
59
|
+
}
|
|
60
|
+
`,
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
export const SHADOW: Record<string, string> = {
|
|
64
|
+
SMALL: themeVars.boxShadow.elevation.level1, // old value: '0 2px 12px rgba(0, 0, 0, 0.04)',
|
|
65
|
+
MEDIUM: themeVars.boxShadow.elevation.level3, // old value: '0 4px 20px rgba(0, 0, 0, 0.08)',
|
|
66
|
+
LARGE: themeVars.boxShadow.elevation.level4, // old value: '0 8px 32px rgba(0, 0, 0, 0.08)',
|
|
67
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './default';
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { ValueOf } from '@snack-uikit/utils';
|
|
2
|
+
|
|
3
|
+
import { LAYOUT_TYPE } from '../constants/adaptive';
|
|
4
|
+
|
|
5
|
+
export type MatchMediaGeneric<T extends string> = Record<T, boolean>;
|
|
6
|
+
|
|
7
|
+
export type LayoutType = ValueOf<typeof LAYOUT_TYPE>;
|
|
8
|
+
|
|
9
|
+
export type WithLayoutType<T = object> = T & { layoutType: LayoutType };
|