ydb-embedded-ui 3.2.0 → 3.2.1
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +11 -0
- package/dist/components/EntitiesCount/EntitiesCount.tsx +34 -0
- package/dist/components/EntitiesCount/i18n/en.json +3 -0
- package/dist/components/{AsideNavigation/Settings → EntitiesCount}/i18n/index.ts +2 -2
- package/dist/components/EntitiesCount/i18n/ru.json +3 -0
- package/dist/components/EntitiesCount/index.ts +1 -0
- package/dist/components/Fullscreen/Fullscreen.scss +7 -5
- package/dist/components/TabletsOverall/TabletsOverall.tsx +4 -4
- package/dist/components/TabletsStatistic/TabletsStatistic.tsx +56 -0
- package/dist/components/TabletsStatistic/index.ts +1 -0
- package/dist/containers/App/App.scss +4 -12
- package/dist/containers/AsideNavigation/AsideNavigation.scss +0 -18
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +95 -33
- package/dist/containers/Heatmap/Heatmap.scss +0 -7
- package/dist/containers/Heatmap/Heatmap.tsx +203 -0
- package/dist/containers/Heatmap/HeatmapCanvas/HeatmapCanvas.js +2 -1
- package/dist/containers/Heatmap/index.ts +1 -0
- package/dist/containers/Node/Node.tsx +1 -1
- package/dist/containers/Storage/Storage.js +12 -19
- package/dist/containers/Tablets/Tablets.scss +0 -5
- package/dist/containers/Tablets/Tablets.tsx +172 -0
- package/dist/containers/Tablets/i18n/en.json +6 -0
- package/dist/{components/AsideNavigation → containers/Tablets}/i18n/index.ts +1 -1
- package/dist/containers/Tablets/i18n/ru.json +6 -0
- package/dist/containers/Tablets/index.ts +1 -0
- package/dist/containers/TabletsFilters/TabletsFilters.js +4 -8
- package/dist/containers/TabletsFilters/TabletsFilters.scss +6 -2
- package/dist/containers/Tenant/Diagnostics/Diagnostics.tsx +4 -8
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantOverview.js +7 -7
- package/dist/containers/Tenants/Tenants.js +1 -1
- package/dist/containers/UserSettings/UserSettings.tsx +4 -3
- package/dist/routes.ts +1 -1
- package/dist/store/reducers/{heatmap.js → heatmap.ts} +33 -18
- package/dist/store/reducers/settings.js +12 -2
- package/dist/types/api/compute.ts +1 -1
- package/dist/types/api/schema.ts +16 -3
- package/dist/types/store/heatmap.ts +51 -0
- package/dist/utils/constants.ts +1 -37
- package/dist/utils/getNodesColumns.js +7 -2
- package/dist/utils/tablet.ts +53 -0
- package/package.json +2 -1
- package/dist/components/AsideNavigation/AsideHeader.scss +0 -147
- package/dist/components/AsideNavigation/AsideHeader.tsx +0 -389
- package/dist/components/AsideNavigation/AsideHeaderFooterItem/AsideHeaderFooterItem.scss +0 -82
- package/dist/components/AsideNavigation/AsideHeaderFooterItem/AsideHeaderFooterItem.tsx +0 -138
- package/dist/components/AsideNavigation/AsideHeaderFooterSlot/AsideHeaderFooterSlot.tsx +0 -33
- package/dist/components/AsideNavigation/AsideHeaderFooterSlot/SlotsContext.tsx +0 -49
- package/dist/components/AsideNavigation/AsideHeaderTooltip/AsideHeaderTooltip.scss +0 -16
- package/dist/components/AsideNavigation/AsideHeaderTooltip/AsideHeaderTooltip.tsx +0 -37
- package/dist/components/AsideNavigation/CompositeBar/CompositeBar.scss +0 -108
- package/dist/components/AsideNavigation/CompositeBar/CompositeBar.tsx +0 -282
- package/dist/components/AsideNavigation/Content/Content.tsx +0 -35
- package/dist/components/AsideNavigation/Drawer/Drawer.scss +0 -76
- package/dist/components/AsideNavigation/Drawer/Drawer.tsx +0 -134
- package/dist/components/AsideNavigation/Drawer/index.ts +0 -1
- package/dist/components/AsideNavigation/Logo/Logo.scss +0 -43
- package/dist/components/AsideNavigation/Logo/Logo.tsx +0 -82
- package/dist/components/AsideNavigation/Settings/README.md +0 -92
- package/dist/components/AsideNavigation/Settings/Settings.scss +0 -128
- package/dist/components/AsideNavigation/Settings/Settings.tsx +0 -270
- package/dist/components/AsideNavigation/Settings/SettingsMenu/SettingsMenu.scss +0 -78
- package/dist/components/AsideNavigation/Settings/SettingsMenu/SettingsMenu.tsx +0 -141
- package/dist/components/AsideNavigation/Settings/SettingsSearch/SettingsSearch.tsx +0 -57
- package/dist/components/AsideNavigation/Settings/collect-settings.ts +0 -156
- package/dist/components/AsideNavigation/Settings/filter-settings.ts +0 -38
- package/dist/components/AsideNavigation/Settings/helpers.ts +0 -39
- package/dist/components/AsideNavigation/Settings/i18n/en.json +0 -5
- package/dist/components/AsideNavigation/Settings/i18n/ru.json +0 -5
- package/dist/components/AsideNavigation/Settings/index.ts +0 -1
- package/dist/components/AsideNavigation/constants.ts +0 -28
- package/dist/components/AsideNavigation/helpers.ts +0 -34
- package/dist/components/AsideNavigation/i18n/en.json +0 -4
- package/dist/components/AsideNavigation/i18n/ru.json +0 -4
- package/dist/components/AsideNavigation/icons.ts +0 -32
- package/dist/components/AsideNavigation/types.ts +0 -23
- package/dist/components/TabletsStatistic/TabletsStatistic.js +0 -58
- package/dist/containers/Heatmap/Heatmap.js +0 -244
- package/dist/containers/Tablets/Tablets.js +0 -228
@@ -1,43 +0,0 @@
|
|
1
|
-
.nv-aside-header-logo {
|
2
|
-
display: flex;
|
3
|
-
flex-shrink: 0;
|
4
|
-
align-items: center;
|
5
|
-
|
6
|
-
height: 32px;
|
7
|
-
margin: 12px 0;
|
8
|
-
|
9
|
-
font-family: 'Rubik', sans-serif;
|
10
|
-
|
11
|
-
&__logo-btn-place {
|
12
|
-
display: flex;
|
13
|
-
flex-shrink: 0;
|
14
|
-
justify-content: center;
|
15
|
-
align-items: center;
|
16
|
-
|
17
|
-
width: var(--nv-aside-header-min-width);
|
18
|
-
}
|
19
|
-
|
20
|
-
&__logo {
|
21
|
-
font-family: 'Rubik', sans-serif;
|
22
|
-
font-size: 16px;
|
23
|
-
line-height: 20px;
|
24
|
-
vertical-align: middle;
|
25
|
-
}
|
26
|
-
|
27
|
-
&__logo-link {
|
28
|
-
&,
|
29
|
-
&:hover,
|
30
|
-
&:active,
|
31
|
-
&:visited,
|
32
|
-
&:focus {
|
33
|
-
text-decoration: none;
|
34
|
-
|
35
|
-
color: inherit;
|
36
|
-
outline: none;
|
37
|
-
}
|
38
|
-
}
|
39
|
-
|
40
|
-
.yc-root &__btn-logo.button2_theme_flat.button2_hovered_yes::before {
|
41
|
-
background-color: transparent;
|
42
|
-
}
|
43
|
-
}
|
@@ -1,82 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import block from 'bem-cn-lite';
|
3
|
-
import {Button, Icon} from '@gravity-ui/uikit';
|
4
|
-
|
5
|
-
import './Logo.scss';
|
6
|
-
|
7
|
-
const b = block('nv-aside-header-logo');
|
8
|
-
|
9
|
-
export interface LogoProps {
|
10
|
-
onLogoIconClick?: (event: React.MouseEvent<HTMLElement, MouseEvent>) => void;
|
11
|
-
logoText: (() => React.ReactNode) | string;
|
12
|
-
logoIcon: SVGIconData;
|
13
|
-
logoIconClassName?: string;
|
14
|
-
logoIconSize?: string | number;
|
15
|
-
logoTextSize?: string | number;
|
16
|
-
logoHref?: string;
|
17
|
-
isCompact: boolean;
|
18
|
-
logoWrapper?: (node: React.ReactNode, isCompact: boolean) => React.ReactNode;
|
19
|
-
}
|
20
|
-
|
21
|
-
export const Logo: React.FC<LogoProps> = ({
|
22
|
-
onLogoIconClick,
|
23
|
-
logoText,
|
24
|
-
logoIcon,
|
25
|
-
logoIconSize = 24,
|
26
|
-
logoTextSize = 16,
|
27
|
-
logoHref = '/',
|
28
|
-
logoIconClassName,
|
29
|
-
isCompact,
|
30
|
-
logoWrapper,
|
31
|
-
}) => {
|
32
|
-
const hasClickHandler = typeof onLogoIconClick === 'function';
|
33
|
-
const hasLogoWrapper = typeof logoWrapper === 'function';
|
34
|
-
|
35
|
-
const linkProps = hasClickHandler
|
36
|
-
? {}
|
37
|
-
: {
|
38
|
-
target: '_self',
|
39
|
-
href: logoHref,
|
40
|
-
};
|
41
|
-
|
42
|
-
const button = (
|
43
|
-
<Button
|
44
|
-
view="flat"
|
45
|
-
size="l"
|
46
|
-
className={b('btn-logo')}
|
47
|
-
component={hasLogoWrapper ? 'span' : undefined}
|
48
|
-
onClick={onLogoIconClick}
|
49
|
-
{...linkProps}
|
50
|
-
>
|
51
|
-
<Icon data={logoIcon} size={logoIconSize} className={logoIconClassName} />
|
52
|
-
</Button>
|
53
|
-
);
|
54
|
-
|
55
|
-
let logo: React.ReactNode;
|
56
|
-
|
57
|
-
if (typeof logoText === 'function') {
|
58
|
-
logo = logoText();
|
59
|
-
} else {
|
60
|
-
logo = (
|
61
|
-
<div className={b('logo')} style={{fontSize: logoTextSize}}>
|
62
|
-
{logoText}
|
63
|
-
</div>
|
64
|
-
);
|
65
|
-
}
|
66
|
-
|
67
|
-
return (
|
68
|
-
<div className={b()}>
|
69
|
-
<div className={b('logo-btn-place')}>
|
70
|
-
{typeof logoWrapper === 'function' ? logoWrapper(button, isCompact) : button}
|
71
|
-
</div>
|
72
|
-
{!isCompact &&
|
73
|
-
(typeof logoWrapper === 'function' ? (
|
74
|
-
logoWrapper(logo, isCompact)
|
75
|
-
) : (
|
76
|
-
<a {...linkProps} className={b('logo-link')} onClick={onLogoIconClick}>
|
77
|
-
{logo}
|
78
|
-
</a>
|
79
|
-
))}
|
80
|
-
</div>
|
81
|
-
);
|
82
|
-
};
|
@@ -1,92 +0,0 @@
|
|
1
|
-
### Settings
|
2
|
-
|
3
|
-
Компонент для отображения настроек сервиса. Поддерживает
|
4
|
-
|
5
|
-
- одно-уровневую и двух-уровневую группировки настроек;
|
6
|
-
- поиск по заголовкам настроек;
|
7
|
-
- отображение состояния загрузки;
|
8
|
-
|
9
|
-
### PropTypes
|
10
|
-
|
11
|
-
#### Settings
|
12
|
-
|
13
|
-
| Property | Type | Required | Default | Description |
|
14
|
-
| :------------- | :------- | :------: | :------ | :----------------------------------------------------------------------- |
|
15
|
-
| loading | boolean | | | флаг неготовности настроек для отображения |
|
16
|
-
| renderLoading | Function | | | Содержимое компонента на время загрузки |
|
17
|
-
| renderNotFound | Function | | | Содержимое страницы при отсутствии найденных элементов |
|
18
|
-
| initialPage | string | | | Активный раздел настроек по умолчанию в формате "/ид группы/ид страницы" |
|
19
|
-
| onPageChange | Function | | | Обработчик события изменения активной страницы настроек |
|
20
|
-
|
21
|
-
#### Settings.Group
|
22
|
-
|
23
|
-
| Property | Type | Required | Default | Description |
|
24
|
-
| :--------- | :----- | :------: | :------ | :--------------------------------------- |
|
25
|
-
| id | string | | | Уникальный идентификатор группы настроек |
|
26
|
-
| groupTitle | string | true | | заголовок группы страниц настроек |
|
27
|
-
|
28
|
-
#### Settings.Page
|
29
|
-
|
30
|
-
| Property | Type | Required | Default | Description |
|
31
|
-
| :------- | :-------- | :------: | :------ | :--------------------------------------------------------- |
|
32
|
-
| id | string | | | Уникальный идентификатор страницы настроек в рамках группы |
|
33
|
-
| title | string | true | | заголовок страницы настроек |
|
34
|
-
| icon | IconProps | true | | данные иконки, отображаемой в меню |
|
35
|
-
|
36
|
-
#### Settings.Section
|
37
|
-
|
38
|
-
| Property | Type | Required | Default | Description |
|
39
|
-
| :-------- | :-------- | :------: | :------ | :------------------------------------- |
|
40
|
-
| title | string | true | | заголовок раздела настроек на странице |
|
41
|
-
| header | ReactNode | | | Шапка секции |
|
42
|
-
| withBadge | boolean | | | Рисует бэйдж у секции и меню |
|
43
|
-
|
44
|
-
#### Settings.Item
|
45
|
-
|
46
|
-
| Property | Type | Required | Default | Description |
|
47
|
-
| :------------------- | :-------------- | :------: | :------- | :------------------------------------------ |
|
48
|
-
| title | string | true | | заголовок настройки |
|
49
|
-
| renderTitleComponent | Function | | | Произвольное содержимое заголовка настройки |
|
50
|
-
| align | 'top', 'center' | | 'center' | выравнивание заголовка и контрола |
|
51
|
-
|
52
|
-
### Использование
|
53
|
-
|
54
|
-
Смотрите в storybook пример `src/strories/demo/SettingsDemo`.
|
55
|
-
|
56
|
-
### Описание
|
57
|
-
|
58
|
-
Меню настроек может быть одно-уровневым:
|
59
|
-
|
60
|
-
```jsx
|
61
|
-
<Settings>
|
62
|
-
<Settings.Page title="Оформление">...</Settings.Page>
|
63
|
-
<Settings.Page title="Коммуникации">...</Settings.Page>
|
64
|
-
</Settings>
|
65
|
-
```
|
66
|
-
|
67
|
-
или двух-уровневым:
|
68
|
-
|
69
|
-
```jsx
|
70
|
-
<Settings>
|
71
|
-
<Settings.Group groupTitle="Arcanum">
|
72
|
-
<Settings.Page title="Оформление">...</Settings.Page>
|
73
|
-
<Settings.Page title="Коммуникации">...</Settings.Page>
|
74
|
-
</Settings.Group>
|
75
|
-
<Settings.Group groupTitle="General">
|
76
|
-
<Settings.Page title="Оформление">...</Settings.Page>
|
77
|
-
<Settings.Page title="Коммуникации">...</Settings.Page>
|
78
|
-
</Settings.Group>
|
79
|
-
</Settings>
|
80
|
-
```
|
81
|
-
|
82
|
-
Страницы настроек делятся на секции с наборами настроек:
|
83
|
-
|
84
|
-
```jsx
|
85
|
-
<Settings.Page title="Features" icon={'...'}>
|
86
|
-
<Settings.Section title="Common">
|
87
|
-
<Settings.Item title="Default VCS">...</Settings.Item>
|
88
|
-
<Settings.Item title="Search root">...</Settings.Item>
|
89
|
-
</Settings.Section>
|
90
|
-
<Settings.Section title="Beta functionality">...</Settings.Section>
|
91
|
-
</Settings.Page>
|
92
|
-
```
|
@@ -1,128 +0,0 @@
|
|
1
|
-
.nv-settings {
|
2
|
-
display: grid;
|
3
|
-
grid-template-columns: 216px 1fr;
|
4
|
-
|
5
|
-
width: 834px;
|
6
|
-
height: 100%;
|
7
|
-
|
8
|
-
&_loading {
|
9
|
-
grid-template-columns: auto;
|
10
|
-
}
|
11
|
-
|
12
|
-
&__loader {
|
13
|
-
align-self: center;
|
14
|
-
justify-self: center;
|
15
|
-
}
|
16
|
-
|
17
|
-
&__not-found {
|
18
|
-
display: grid;
|
19
|
-
align-items: center;
|
20
|
-
justify-items: center;
|
21
|
-
|
22
|
-
height: 100%;
|
23
|
-
}
|
24
|
-
|
25
|
-
&__menu {
|
26
|
-
border-right: 1px solid var(--yc-color-line-generic);
|
27
|
-
}
|
28
|
-
|
29
|
-
&__heading {
|
30
|
-
margin: 20px 20px 0;
|
31
|
-
|
32
|
-
font-size: var(--yc-text-body-2-font-size);
|
33
|
-
font-weight: 500;
|
34
|
-
line-height: var(--yc-text-body-2-line-height);
|
35
|
-
}
|
36
|
-
|
37
|
-
&__search {
|
38
|
-
margin: 12px 20px 16px;
|
39
|
-
}
|
40
|
-
|
41
|
-
&__page {
|
42
|
-
overflow-y: auto;
|
43
|
-
|
44
|
-
padding: 20px;
|
45
|
-
}
|
46
|
-
|
47
|
-
&__section {
|
48
|
-
&-heading {
|
49
|
-
margin: 0;
|
50
|
-
|
51
|
-
font-size: var(--yc-text-body-2-font-size);
|
52
|
-
font-weight: 500;
|
53
|
-
line-height: var(--yc-text-body-2-line-height);
|
54
|
-
|
55
|
-
&_badge {
|
56
|
-
position: relative;
|
57
|
-
|
58
|
-
display: inline-block;
|
59
|
-
|
60
|
-
&::after {
|
61
|
-
position: absolute;
|
62
|
-
top: 1px;
|
63
|
-
right: -8px;
|
64
|
-
|
65
|
-
display: block;
|
66
|
-
|
67
|
-
width: 6px;
|
68
|
-
height: 6px;
|
69
|
-
|
70
|
-
content: '';
|
71
|
-
|
72
|
-
border-radius: 50%;
|
73
|
-
background-color: var(--yc-color-text-danger);
|
74
|
-
}
|
75
|
-
}
|
76
|
-
}
|
77
|
-
|
78
|
-
&-item {
|
79
|
-
margin-top: 24px;
|
80
|
-
}
|
81
|
-
|
82
|
-
& + & {
|
83
|
-
margin-top: 32px;
|
84
|
-
}
|
85
|
-
}
|
86
|
-
|
87
|
-
&__item {
|
88
|
-
display: grid;
|
89
|
-
grid-template-columns: 216px 1fr;
|
90
|
-
justify-items: start;
|
91
|
-
|
92
|
-
&_align_top {
|
93
|
-
align-items: start;
|
94
|
-
}
|
95
|
-
|
96
|
-
&_align_center {
|
97
|
-
align-items: center;
|
98
|
-
}
|
99
|
-
}
|
100
|
-
|
101
|
-
&__item-heading {
|
102
|
-
&_badge {
|
103
|
-
position: relative;
|
104
|
-
|
105
|
-
&::after {
|
106
|
-
position: absolute;
|
107
|
-
top: 1px;
|
108
|
-
right: -10px;
|
109
|
-
|
110
|
-
display: block;
|
111
|
-
|
112
|
-
width: 4px;
|
113
|
-
height: 4px;
|
114
|
-
|
115
|
-
content: '';
|
116
|
-
|
117
|
-
border-radius: 50%;
|
118
|
-
background-color: var(--yc-color-text-danger);
|
119
|
-
}
|
120
|
-
}
|
121
|
-
}
|
122
|
-
|
123
|
-
&__found {
|
124
|
-
font-weight: 500;
|
125
|
-
|
126
|
-
background: var(--yc-color-base-selection);
|
127
|
-
}
|
128
|
-
}
|
@@ -1,270 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import block from 'bem-cn-lite';
|
3
|
-
import i18n from './i18n';
|
4
|
-
|
5
|
-
import {IconProps, Loader} from '@gravity-ui/uikit';
|
6
|
-
import {SettingsSearch} from './SettingsSearch/SettingsSearch';
|
7
|
-
import {SettingsMenu, SettingsMenuItems, SettingsMenuInstance} from './SettingsMenu/SettingsMenu';
|
8
|
-
|
9
|
-
import {getSettingsFromChildren} from './collect-settings';
|
10
|
-
import {filterSettings} from './filter-settings';
|
11
|
-
import {escapeStringForRegExp} from './helpers';
|
12
|
-
|
13
|
-
import './Settings.scss';
|
14
|
-
|
15
|
-
const b = block('nv-settings');
|
16
|
-
|
17
|
-
interface SettingsProps {
|
18
|
-
initialPage?: string;
|
19
|
-
onPageChange?: (page: string | undefined) => void;
|
20
|
-
children: React.ReactNode;
|
21
|
-
renderNotFound?: () => React.ReactNode;
|
22
|
-
renderLoading?: () => React.ReactNode;
|
23
|
-
loading?: boolean;
|
24
|
-
}
|
25
|
-
|
26
|
-
interface SettingsGroupProps {
|
27
|
-
id?: string;
|
28
|
-
groupTitle: string;
|
29
|
-
children: React.ReactNode;
|
30
|
-
}
|
31
|
-
|
32
|
-
interface SettingsPageProps {
|
33
|
-
id?: string;
|
34
|
-
title: string;
|
35
|
-
icon: IconProps;
|
36
|
-
children: React.ReactNode;
|
37
|
-
}
|
38
|
-
|
39
|
-
interface SettingsSectionProps {
|
40
|
-
title: string;
|
41
|
-
header?: React.ReactNode;
|
42
|
-
children: React.ReactNode;
|
43
|
-
withBadge?: boolean;
|
44
|
-
}
|
45
|
-
|
46
|
-
interface SettingsItemProps {
|
47
|
-
title: string;
|
48
|
-
renderTitleComponent?: (highlightedTitle: React.ReactNode | null) => React.ReactNode;
|
49
|
-
align?: 'top' | 'center';
|
50
|
-
children: React.ReactNode;
|
51
|
-
withBadge?: boolean;
|
52
|
-
}
|
53
|
-
|
54
|
-
const ItemContext = React.createContext<React.ReactNode>(undefined);
|
55
|
-
|
56
|
-
export function Settings({
|
57
|
-
initialPage,
|
58
|
-
onPageChange,
|
59
|
-
children,
|
60
|
-
renderNotFound,
|
61
|
-
loading,
|
62
|
-
renderLoading,
|
63
|
-
}: SettingsProps) {
|
64
|
-
const [search, setSearch] = React.useState('');
|
65
|
-
const [selectedPage, setCurrentPage] = React.useState<string | undefined>(initialPage);
|
66
|
-
const prevSelectedPageRef = React.useRef(initialPage);
|
67
|
-
const searchInputRef = React.useRef<HTMLInputElement>(null);
|
68
|
-
const menuRef = React.useRef<SettingsMenuInstance>(null);
|
69
|
-
|
70
|
-
React.useEffect(() => {
|
71
|
-
menuRef.current?.clearFocus();
|
72
|
-
}, [search]);
|
73
|
-
|
74
|
-
React.useEffect(() => {
|
75
|
-
const handler = () => {
|
76
|
-
menuRef.current?.clearFocus();
|
77
|
-
};
|
78
|
-
window.addEventListener('click', handler);
|
79
|
-
return () => {
|
80
|
-
window.removeEventListener('click', handler);
|
81
|
-
};
|
82
|
-
}, []);
|
83
|
-
|
84
|
-
if (selectedPage !== prevSelectedPageRef.current) {
|
85
|
-
onPageChange?.(selectedPage);
|
86
|
-
prevSelectedPageRef.current = selectedPage;
|
87
|
-
}
|
88
|
-
|
89
|
-
if (loading) {
|
90
|
-
return (
|
91
|
-
<div className={b({loading: true})}>
|
92
|
-
{typeof renderLoading === 'function' ? (
|
93
|
-
renderLoading()
|
94
|
-
) : (
|
95
|
-
<Loader className={b('loader')} size="m" />
|
96
|
-
)}
|
97
|
-
</div>
|
98
|
-
);
|
99
|
-
}
|
100
|
-
|
101
|
-
const {menu, pages} = getSettingsFromChildren(children);
|
102
|
-
filterSettings(pages, search, prepareTitle);
|
103
|
-
|
104
|
-
const settingsMenu: SettingsMenuItems = [];
|
105
|
-
for (const fistLevel of menu) {
|
106
|
-
if ('groupTitle' in fistLevel) {
|
107
|
-
settingsMenu.push({
|
108
|
-
groupTitle: fistLevel.groupTitle,
|
109
|
-
items: fistLevel.items.map((item) => ({
|
110
|
-
id: item.pageId,
|
111
|
-
title: item.title,
|
112
|
-
icon: item.icon,
|
113
|
-
disabled: pages[item.pageId].hide,
|
114
|
-
withBadge: item.withBadge,
|
115
|
-
})),
|
116
|
-
});
|
117
|
-
} else {
|
118
|
-
settingsMenu.push({
|
119
|
-
id: fistLevel.pageId,
|
120
|
-
title: fistLevel.title,
|
121
|
-
icon: fistLevel.icon,
|
122
|
-
disabled: pages[fistLevel.pageId].hide,
|
123
|
-
withBadge: fistLevel.withBadge,
|
124
|
-
});
|
125
|
-
}
|
126
|
-
}
|
127
|
-
|
128
|
-
let activePage = selectedPage;
|
129
|
-
if (!activePage || pages[activePage]?.hide) {
|
130
|
-
activePage = undefined;
|
131
|
-
for (const firstLevel of settingsMenu) {
|
132
|
-
if ('groupTitle' in firstLevel) {
|
133
|
-
for (const item of firstLevel.items) {
|
134
|
-
if (!item.disabled) {
|
135
|
-
activePage = item.id;
|
136
|
-
break;
|
137
|
-
}
|
138
|
-
}
|
139
|
-
if (activePage) {
|
140
|
-
break;
|
141
|
-
}
|
142
|
-
} else if (!firstLevel.disabled) {
|
143
|
-
activePage = firstLevel.id;
|
144
|
-
break;
|
145
|
-
}
|
146
|
-
}
|
147
|
-
}
|
148
|
-
|
149
|
-
if (activePage !== selectedPage) {
|
150
|
-
setCurrentPage(activePage);
|
151
|
-
}
|
152
|
-
|
153
|
-
return (
|
154
|
-
<div className={b()}>
|
155
|
-
<div
|
156
|
-
className={b('menu')}
|
157
|
-
onClick={() => {
|
158
|
-
if (searchInputRef.current) {
|
159
|
-
searchInputRef.current.focus();
|
160
|
-
}
|
161
|
-
}}
|
162
|
-
onKeyDown={(event) => {
|
163
|
-
if (menuRef.current) {
|
164
|
-
if (menuRef.current.handleKeyDown(event)) {
|
165
|
-
event.preventDefault();
|
166
|
-
}
|
167
|
-
}
|
168
|
-
}}
|
169
|
-
>
|
170
|
-
<h2 className={b('heading')}>{i18n('heading_settings')}</h2>
|
171
|
-
<SettingsSearch
|
172
|
-
inputRef={searchInputRef}
|
173
|
-
className={b('search')}
|
174
|
-
onChange={setSearch}
|
175
|
-
/>
|
176
|
-
<SettingsMenu
|
177
|
-
ref={menuRef}
|
178
|
-
items={settingsMenu}
|
179
|
-
onChange={setCurrentPage}
|
180
|
-
activeItem={activePage}
|
181
|
-
/>
|
182
|
-
</div>
|
183
|
-
<div className={b('page')}>
|
184
|
-
{activePage ? (
|
185
|
-
pages[activePage].sections.map((section) => {
|
186
|
-
if (section.hide) {
|
187
|
-
return null;
|
188
|
-
}
|
189
|
-
return (
|
190
|
-
<div key={section.title} className={b('section')}>
|
191
|
-
<h3 className={b('section-heading', {badge: section.withBadge})}>
|
192
|
-
{section.title}
|
193
|
-
</h3>
|
194
|
-
{section.header ? section.header : null}
|
195
|
-
{section.items.map(({hide, title, children, titleComponent}) =>
|
196
|
-
hide ? null : (
|
197
|
-
<div key={title} className={b('section-item')}>
|
198
|
-
<ItemContext.Provider value={titleComponent}>
|
199
|
-
{children}
|
200
|
-
</ItemContext.Provider>
|
201
|
-
</div>
|
202
|
-
),
|
203
|
-
)}
|
204
|
-
</div>
|
205
|
-
);
|
206
|
-
})
|
207
|
-
) : typeof renderNotFound === 'function' ? (
|
208
|
-
renderNotFound()
|
209
|
-
) : (
|
210
|
-
<div className={b('not-found')}>{i18n('not-found')}</div>
|
211
|
-
)}
|
212
|
-
</div>
|
213
|
-
</div>
|
214
|
-
);
|
215
|
-
}
|
216
|
-
|
217
|
-
Settings.Group = function SettingsGroup({children}: SettingsGroupProps) {
|
218
|
-
return <React.Fragment>{children}</React.Fragment>;
|
219
|
-
};
|
220
|
-
|
221
|
-
Settings.Page = function SettingsPage({children}: SettingsPageProps) {
|
222
|
-
return <React.Fragment>{children}</React.Fragment>;
|
223
|
-
};
|
224
|
-
|
225
|
-
Settings.Section = function SettingsSection({children}: SettingsSectionProps) {
|
226
|
-
return <React.Fragment>{children}</React.Fragment>;
|
227
|
-
};
|
228
|
-
|
229
|
-
Settings.Item = function SettingsItem({
|
230
|
-
title,
|
231
|
-
children,
|
232
|
-
align = 'center',
|
233
|
-
withBadge,
|
234
|
-
}: SettingsItemProps) {
|
235
|
-
const contextTitle = React.useContext(ItemContext);
|
236
|
-
return (
|
237
|
-
<div className={b('item', {align})}>
|
238
|
-
<label className={b('item-heading', {badge: withBadge})}>{contextTitle ?? title}</label>
|
239
|
-
<div>{children}</div>
|
240
|
-
</div>
|
241
|
-
);
|
242
|
-
};
|
243
|
-
|
244
|
-
function prepareTitle(string: string, search: string) {
|
245
|
-
let temp = string.slice(0);
|
246
|
-
const title: React.ReactNode[] = [];
|
247
|
-
const parts = escapeStringForRegExp(search).split(' ').filter(Boolean);
|
248
|
-
let key = 0;
|
249
|
-
for (const part of parts) {
|
250
|
-
const regex = new RegExp(part, 'ig');
|
251
|
-
const match = regex.exec(temp);
|
252
|
-
if (match) {
|
253
|
-
const m = match[0];
|
254
|
-
const i = match.index;
|
255
|
-
if (i > 0) {
|
256
|
-
title.push(temp.slice(0, i));
|
257
|
-
}
|
258
|
-
title.push(
|
259
|
-
<strong key={key++} className={b('found')}>
|
260
|
-
{m}
|
261
|
-
</strong>,
|
262
|
-
);
|
263
|
-
temp = temp.slice(i + m.length);
|
264
|
-
}
|
265
|
-
}
|
266
|
-
if (temp) {
|
267
|
-
title.push(temp);
|
268
|
-
}
|
269
|
-
return title;
|
270
|
-
}
|
@@ -1,78 +0,0 @@
|
|
1
|
-
.nv-settings-menu {
|
2
|
-
&__group {
|
3
|
-
&-heading {
|
4
|
-
display: inline-block;
|
5
|
-
|
6
|
-
margin-bottom: 12px;
|
7
|
-
padding: 0 20px;
|
8
|
-
|
9
|
-
font-weight: 500;
|
10
|
-
line-height: 18px;
|
11
|
-
}
|
12
|
-
|
13
|
-
& + & {
|
14
|
-
margin-top: 24px;
|
15
|
-
}
|
16
|
-
}
|
17
|
-
|
18
|
-
&__item {
|
19
|
-
$item: &;
|
20
|
-
display: flex;
|
21
|
-
align-items: center;
|
22
|
-
|
23
|
-
height: 40px;
|
24
|
-
padding: 0 20px;
|
25
|
-
|
26
|
-
&[class][class] {
|
27
|
-
color: var(--yc-color-text-primary);
|
28
|
-
|
29
|
-
&#{$item}_disabled {
|
30
|
-
color: var(--yc-color-text-secondary);
|
31
|
-
|
32
|
-
#{$item}-icon {
|
33
|
-
color: var(--yc-color-base-misc-heavy);
|
34
|
-
}
|
35
|
-
}
|
36
|
-
}
|
37
|
-
|
38
|
-
&-icon {
|
39
|
-
margin-right: 5px;
|
40
|
-
|
41
|
-
color: var(--yc-color-text-misc);
|
42
|
-
}
|
43
|
-
|
44
|
-
&:hover,
|
45
|
-
&_focused {
|
46
|
-
background: var(--yc-color-base-simple-hover);
|
47
|
-
}
|
48
|
-
|
49
|
-
&_selected {
|
50
|
-
background: var(--yc-color-base-selection);
|
51
|
-
}
|
52
|
-
|
53
|
-
&_selected:hover,
|
54
|
-
&_selected#{$item}_focused {
|
55
|
-
background: var(--yc-color-base-selection-hover);
|
56
|
-
}
|
57
|
-
|
58
|
-
&_badge {
|
59
|
-
position: relative;
|
60
|
-
|
61
|
-
&::after {
|
62
|
-
position: absolute;
|
63
|
-
top: calc(50% - 3px);
|
64
|
-
right: 9px;
|
65
|
-
|
66
|
-
display: block;
|
67
|
-
|
68
|
-
width: 6px;
|
69
|
-
height: 6px;
|
70
|
-
|
71
|
-
content: '';
|
72
|
-
|
73
|
-
border-radius: 50%;
|
74
|
-
background-color: var(--yc-color-text-danger);
|
75
|
-
}
|
76
|
-
}
|
77
|
-
}
|
78
|
-
}
|