ydb-embedded-ui 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- package/CHANGELOG.md +8 -0
- package/README.md +28 -0
- package/dist/HOCS/WithSearch/WithSearch.js +26 -0
- package/dist/HOCS/index.js +1 -0
- package/dist/assets/icons/bug.svg +1 -0
- package/dist/assets/icons/close.svg +1 -0
- package/dist/assets/icons/control-menu-button.svg +1 -0
- package/dist/assets/icons/databases.svg +3 -0
- package/dist/assets/icons/dots.svg +1 -0
- package/dist/assets/icons/server.svg +1 -0
- package/dist/assets/icons/settings-with-dot.svg +1 -0
- package/dist/assets/icons/settings.svg +1 -0
- package/dist/assets/icons/signIn.svg +1 -0
- package/dist/assets/icons/signOut.svg +1 -0
- package/dist/assets/icons/star.svg +1 -0
- package/dist/assets/icons/storage.svg +1 -0
- package/dist/assets/icons/support.svg +1 -0
- package/dist/assets/icons/user-check.svg +1 -0
- package/dist/assets/icons/user-secret.svg +1 -0
- package/dist/assets/icons/ydb.svg +4 -0
- package/dist/components/AsideNavigation/AsideHeader.scss +148 -0
- package/dist/components/AsideNavigation/AsideHeader.tsx +388 -0
- package/dist/components/AsideNavigation/AsideHeaderFooterItem/AsideHeaderFooterItem.scss +82 -0
- package/dist/components/AsideNavigation/AsideHeaderFooterItem/AsideHeaderFooterItem.tsx +138 -0
- package/dist/components/AsideNavigation/AsideHeaderFooterSlot/AsideHeaderFooterSlot.tsx +33 -0
- package/dist/components/AsideNavigation/AsideHeaderFooterSlot/SlotsContext.tsx +49 -0
- package/dist/components/AsideNavigation/AsideHeaderTooltip/AsideHeaderTooltip.scss +16 -0
- package/dist/components/AsideNavigation/AsideHeaderTooltip/AsideHeaderTooltip.tsx +37 -0
- package/dist/components/AsideNavigation/CompositeBar/CompositeBar.scss +108 -0
- package/dist/components/AsideNavigation/CompositeBar/CompositeBar.tsx +282 -0
- package/dist/components/AsideNavigation/Content/Content.tsx +35 -0
- package/dist/components/AsideNavigation/Drawer/Drawer.scss +76 -0
- package/dist/components/AsideNavigation/Drawer/Drawer.tsx +134 -0
- package/dist/components/AsideNavigation/Drawer/index.ts +1 -0
- package/dist/components/AsideNavigation/Logo/Logo.scss +44 -0
- package/dist/components/AsideNavigation/Logo/Logo.tsx +82 -0
- package/dist/components/AsideNavigation/Settings/README.md +92 -0
- package/dist/components/AsideNavigation/Settings/Settings.scss +113 -0
- package/dist/components/AsideNavigation/Settings/Settings.tsx +270 -0
- package/dist/components/AsideNavigation/Settings/SettingsMenu/SettingsMenu.scss +70 -0
- package/dist/components/AsideNavigation/Settings/SettingsMenu/SettingsMenu.tsx +141 -0
- package/dist/components/AsideNavigation/Settings/SettingsSearch/SettingsSearch.tsx +57 -0
- package/dist/components/AsideNavigation/Settings/collect-settings.ts +156 -0
- package/dist/components/AsideNavigation/Settings/filter-settings.ts +38 -0
- package/dist/components/AsideNavigation/Settings/helpers.ts +39 -0
- package/dist/components/AsideNavigation/Settings/i18n/en.json +5 -0
- package/dist/components/AsideNavigation/Settings/i18n/index.ts +11 -0
- package/dist/components/AsideNavigation/Settings/i18n/ru.json +5 -0
- package/dist/components/AsideNavigation/Settings/index.ts +1 -0
- package/dist/components/AsideNavigation/constants.ts +28 -0
- package/dist/components/AsideNavigation/helpers.ts +34 -0
- package/dist/components/AsideNavigation/i18n/en.json +4 -0
- package/dist/components/AsideNavigation/i18n/index.ts +11 -0
- package/dist/components/AsideNavigation/i18n/ru.json +4 -0
- package/dist/components/AsideNavigation/icons.ts +32 -0
- package/dist/components/AsideNavigation/types.ts +23 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.js +25 -0
- package/dist/components/Breadcrumbs/Breadcrumbs.scss +4 -0
- package/dist/components/ClusterInfo/ClusterInfo.scss +65 -0
- package/dist/components/ClusterInfo/ClusterInfo.tsx +157 -0
- package/dist/components/Collapse/Collapse.js +84 -0
- package/dist/components/Collapse/Collapse.scss +70 -0
- package/dist/components/CriticalActionDialog/CriticalActionDialog.js +53 -0
- package/dist/components/CriticalActionDialog/CriticalActionDialog.scss +31 -0
- package/dist/components/EmptyState/EmptyState.js +48 -0
- package/dist/components/EmptyState/EmptyState.scss +70 -0
- package/dist/components/EntityStatus/EntityStatus.js +96 -0
- package/dist/components/EntityStatus/EntityStatus.scss +81 -0
- package/dist/components/FullGroupViewer/FullGroupViewer.js +149 -0
- package/dist/components/FullGroupViewer/FullGroupViewer.scss +32 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.js +108 -0
- package/dist/components/FullNodeViewer/FullNodeViewer.scss +78 -0
- package/dist/components/GroupTreeViewer/GroupTreeViewer.js +86 -0
- package/dist/components/GroupTreeViewer/GroupTreeViewer.scss +17 -0
- package/dist/components/GroupViewer/GroupViewer.js +100 -0
- package/dist/components/GroupViewer/GroupViewer.scss +41 -0
- package/dist/components/Hotkey/Hotkey.js +102 -0
- package/dist/components/Icon/Icon.js +26 -0
- package/dist/components/InfoViewer/InfoViewer.js +47 -0
- package/dist/components/InfoViewer/InfoViewer.scss +48 -0
- package/dist/components/InternalLink/InternalLink.js +15 -0
- package/dist/components/NodesViewer/NodesViewer.js +183 -0
- package/dist/components/NodesViewer/NodesViewer.scss +66 -0
- package/dist/components/PDiskViewer/PDiskViewer.js +80 -0
- package/dist/components/PDiskViewer/PDiskViewer.scss +40 -0
- package/dist/components/Pagination/Pagination.js +63 -0
- package/dist/components/Pagination/Pagination.scss +25 -0
- package/dist/components/PoolBar/PoolBar.js +52 -0
- package/dist/components/PoolBar/PoolBar.scss +40 -0
- package/dist/components/PoolUsage/PoolUsage.js +54 -0
- package/dist/components/PoolUsage/PoolUsage.scss +65 -0
- package/dist/components/PoolsGraph/PoolsGraph.js +33 -0
- package/dist/components/PoolsGraph/PoolsGraph.scss +3 -0
- package/dist/components/ProblemFilter/ProblemFilter.js +24 -0
- package/dist/components/ProgressViewer/ProgressViewer.js +92 -0
- package/dist/components/ProgressViewer/ProgressViewer.scss +84 -0
- package/dist/components/SplitPane/SplitPane.js +368 -0
- package/dist/components/SplitPane/SplitPane.scss +107 -0
- package/dist/components/SplitPane/index.js +3 -0
- package/dist/components/Tablet/Tablet.js +61 -0
- package/dist/components/Tablet/Tablet.scss +49 -0
- package/dist/components/TabletsStatistic/TabletsStatistic.js +58 -0
- package/dist/components/TabletsStatistic/TabletsStatistic.scss +41 -0
- package/dist/components/TabletsViewer/TabletsViewer.js +44 -0
- package/dist/components/TabletsViewer/TabletsViewer.scss +37 -0
- package/dist/components/Tag/Tag.js +14 -0
- package/dist/components/Tag/Tag.scss +17 -0
- package/dist/components/Tags/Tags.js +36 -0
- package/dist/components/Tags/Tags.scss +5 -0
- package/dist/components/TenantOverview/TenantOverview.js +148 -0
- package/dist/components/TenantOverview/TenantOverview.scss +75 -0
- package/dist/components/TreeView/TreeView.js +60 -0
- package/dist/components/TreeView/TreeView.scss +30 -0
- package/dist/components/TruncatedQuery/TruncatedQuery.js +26 -0
- package/dist/components/TruncatedQuery/TruncatedQuery.scss +12 -0
- package/dist/containers/App/App.js +67 -0
- package/dist/containers/App/App.scss +154 -0
- package/dist/containers/App/Content.js +109 -0
- package/dist/containers/App/TipPopup/TipPopup.js +66 -0
- package/dist/containers/App/TipPopup/TipPopup.scss +42 -0
- package/dist/containers/AppIcons/AppIcons.js +477 -0
- package/dist/containers/AsideNavigation/AsideNavigation.scss +43 -0
- package/dist/containers/AsideNavigation/AsideNavigation.tsx +228 -0
- package/dist/containers/Authentication/Authentication.scss +37 -0
- package/dist/containers/Authentication/Authentication.tsx +89 -0
- package/dist/containers/Cluster/Cluster.js +168 -0
- package/dist/containers/Cluster/Cluster.scss +52 -0
- package/dist/containers/Group/Group.js +97 -0
- package/dist/containers/Group/Group.scss +6 -0
- package/dist/containers/Header/Header.js +88 -0
- package/dist/containers/Header/Header.scss +72 -0
- package/dist/containers/Header/Host/Host.js +66 -0
- package/dist/containers/Header/Host/Host.scss +43 -0
- package/dist/containers/Heatmap/Heatmap.js +246 -0
- package/dist/containers/Heatmap/Heatmap.scss +94 -0
- package/dist/containers/Heatmap/HeatmapCanvas/HeatmapCanvas.js +167 -0
- package/dist/containers/Heatmap/Histogram/Histogram.js +108 -0
- package/dist/containers/Heatmap/Histogram/Histogram.scss +49 -0
- package/dist/containers/Heatmap/util.js +110 -0
- package/dist/containers/Node/Node.js +184 -0
- package/dist/containers/Node/Node.scss +34 -0
- package/dist/containers/Node/NodePages.js +13 -0
- package/dist/containers/Nodes/Nodes.js +193 -0
- package/dist/containers/Nodes/Nodes.scss +50 -0
- package/dist/containers/Pdisk/Pdisk.js +159 -0
- package/dist/containers/Pdisk/Pdisk.scss +40 -0
- package/dist/containers/Pool/Pool.js +169 -0
- package/dist/containers/Pool/Pool.scss +32 -0
- package/dist/containers/ReduxTooltip/ReduxTooltip.js +108 -0
- package/dist/containers/ReduxTooltip/ReduxTooltip.scss +67 -0
- package/dist/containers/StorageV2/DiskStateProgressBar/DiskStateProgressBar.scss +81 -0
- package/dist/containers/StorageV2/DiskStateProgressBar/DiskStateProgressBar.tsx +56 -0
- package/dist/containers/StorageV2/Pdisk/Pdisk.scss +32 -0
- package/dist/containers/StorageV2/Pdisk/Pdisk.tsx +167 -0
- package/dist/containers/StorageV2/Storage.js +249 -0
- package/dist/containers/StorageV2/Storage.scss +57 -0
- package/dist/containers/StorageV2/StorageFilter/StorageFilter.js +39 -0
- package/dist/containers/StorageV2/StorageGroups/StorageGroups.scss +26 -0
- package/dist/containers/StorageV2/StorageGroups/StorageGroups.tsx +234 -0
- package/dist/containers/StorageV2/StorageNodes/StorageNodes.scss +30 -0
- package/dist/containers/StorageV2/StorageNodes/StorageNodes.tsx +135 -0
- package/dist/containers/StorageV2/Vdisk/Vdisk.js +250 -0
- package/dist/containers/StorageV2/Vdisk/Vdisk.scss +32 -0
- package/dist/containers/Tablet/Tablet.js +453 -0
- package/dist/containers/Tablet/Tablet.scss +88 -0
- package/dist/containers/Tablets/Tablets.js +306 -0
- package/dist/containers/Tablets/Tablets.scss +77 -0
- package/dist/containers/TabletsFilters/TabletsFilters.js +412 -0
- package/dist/containers/TabletsFilters/TabletsFilters.scss +104 -0
- package/dist/containers/Tenant/Acl/Acl.js +149 -0
- package/dist/containers/Tenant/Acl/Acl.scss +34 -0
- package/dist/containers/Tenant/Compute/Compute.js +110 -0
- package/dist/containers/Tenant/Compute/Compute.scss +6 -0
- package/dist/containers/Tenant/Describe/Describe.js +81 -0
- package/dist/containers/Tenant/Describe/Describe.scss +25 -0
- package/dist/containers/Tenant/Healthcheck/Healthcheck.js +116 -0
- package/dist/containers/Tenant/Healthcheck/Healthcheck.scss +64 -0
- package/dist/containers/Tenant/Healthcheck/IssuesViewer/IssueViewer.scss +164 -0
- package/dist/containers/Tenant/Healthcheck/IssuesViewer/IssuesViewer.js +185 -0
- package/dist/containers/Tenant/Network/Network.js +341 -0
- package/dist/containers/Tenant/Network/Network.scss +145 -0
- package/dist/containers/Tenant/Network/NodeNetwork/NodeNetwork.js +71 -0
- package/dist/containers/Tenant/Network/NodeNetwork/NodeNetwork.scss +52 -0
- package/dist/containers/Tenant/Preview/Preview.js +169 -0
- package/dist/containers/Tenant/Preview/Preview.scss +20 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditor.js +649 -0
- package/dist/containers/Tenant/QueryEditor/QueryEditor.scss +71 -0
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.js +168 -0
- package/dist/containers/Tenant/QueryEditor/QueryExplain/QueryExplain.scss +42 -0
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.js +58 -0
- package/dist/containers/Tenant/QueryEditor/QueryResult/QueryResult.scss +24 -0
- package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.js +171 -0
- package/dist/containers/Tenant/QueryEditor/SaveQuery/SaveQuery.scss +51 -0
- package/dist/containers/Tenant/QueryEditor/SavedQueries/SavedQueries.js +156 -0
- package/dist/containers/Tenant/QueryEditor/SavedQueries/SavedQueries.scss +82 -0
- package/dist/containers/Tenant/Schema/HotKeys/HotKeys.js +149 -0
- package/dist/containers/Tenant/Schema/HotKeys/HotKeys.scss +48 -0
- package/dist/containers/Tenant/Schema/Info/Info.js +84 -0
- package/dist/containers/Tenant/Schema/Info/Info.scss +3 -0
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.js +67 -0
- package/dist/containers/Tenant/Schema/SchemaInfoViewer/SchemaInfoViewer.scss +21 -0
- package/dist/containers/Tenant/Schema/SchemaMain/SchemaMain.js +439 -0
- package/dist/containers/Tenant/Schema/SchemaMain/SchemaMain.scss +90 -0
- package/dist/containers/Tenant/Schema/SchemaNode/SchemaNode.js +150 -0
- package/dist/containers/Tenant/Schema/SchemaNode/SchemaNode.scss +41 -0
- package/dist/containers/Tenant/Schema/SchemaPages.js +56 -0
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.js +115 -0
- package/dist/containers/Tenant/Schema/SchemaTree/SchemaTree.scss +13 -0
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.js +63 -0
- package/dist/containers/Tenant/Schema/SchemaViewer/SchemaViewer.scss +28 -0
- package/dist/containers/Tenant/Tenant.js +199 -0
- package/dist/containers/Tenant/Tenant.scss +94 -0
- package/dist/containers/Tenant/TenantPages.js +35 -0
- package/dist/containers/Tenant/TopQueries/TopQueries.js +184 -0
- package/dist/containers/Tenant/TopQueries/TopQueries.scss +53 -0
- package/dist/containers/Tenant/TopShards/TopShards.js +171 -0
- package/dist/containers/Tenant/TopShards/TopShards.scss +23 -0
- package/dist/containers/Tenants/Tenants.js +375 -0
- package/dist/containers/Tenants/Tenants.scss +73 -0
- package/dist/containers/UserSettings/UserSettings.tsx +57 -0
- package/dist/containers/Vdisk/Vdisk.js +160 -0
- package/dist/containers/Vdisk/Vdisk.scss +40 -0
- package/dist/containers/VdiskPdiskNode/VdiskPdiskNode.js +528 -0
- package/dist/containers/VdiskPdiskNode/VdiskPdiskNode.scss +60 -0
- package/dist/contexts/HistoryContext.ts +6 -0
- package/dist/index.css +11 -0
- package/dist/index.js +28 -0
- package/dist/index.test.js +5 -0
- package/dist/react-app-env.d.ts +1 -0
- package/dist/reportWebVitals.js +13 -0
- package/dist/routes.js +40 -0
- package/dist/services/api.js +224 -0
- package/dist/setupTests.js +5 -0
- package/dist/store/index.js +61 -0
- package/dist/store/reducers/authentication.js +77 -0
- package/dist/store/reducers/cluster.js +52 -0
- package/dist/store/reducers/clusterInfo.js +48 -0
- package/dist/store/reducers/clusterNodes.js +70 -0
- package/dist/store/reducers/describe.js +45 -0
- package/dist/store/reducers/executeQuery.js +158 -0
- package/dist/store/reducers/executeTopQueries.js +69 -0
- package/dist/store/reducers/explainQuery.js +174 -0
- package/dist/store/reducers/group.js +49 -0
- package/dist/store/reducers/healthcheckInfo.js +45 -0
- package/dist/store/reducers/heatmap.js +105 -0
- package/dist/store/reducers/host.js +44 -0
- package/dist/store/reducers/hotKeys.js +57 -0
- package/dist/store/reducers/index.js +78 -0
- package/dist/store/reducers/network.js +45 -0
- package/dist/store/reducers/node.js +42 -0
- package/dist/store/reducers/nodes.js +58 -0
- package/dist/store/reducers/olapStats.js +74 -0
- package/dist/store/reducers/pdisk.js +51 -0
- package/dist/store/reducers/pool.js +42 -0
- package/dist/store/reducers/preview.js +73 -0
- package/dist/store/reducers/schema.js +95 -0
- package/dist/store/reducers/schemaAcl.js +44 -0
- package/dist/store/reducers/settings.js +76 -0
- package/dist/store/reducers/shardsWorkload.js +75 -0
- package/dist/store/reducers/storage.js +280 -0
- package/dist/store/reducers/tablet.js +94 -0
- package/dist/store/reducers/tablets.js +90 -0
- package/dist/store/reducers/tabletsFilters.js +126 -0
- package/dist/store/reducers/tenant.js +76 -0
- package/dist/store/reducers/tenants.js +61 -0
- package/dist/store/reducers/tooltip.js +64 -0
- package/dist/store/reducers/vdisk.js +49 -0
- package/dist/store/state-url-mapping.js +133 -0
- package/dist/store/utils.js +55 -0
- package/dist/styles/mixins.scss +254 -0
- package/dist/styles/react-treeview.scss +45 -0
- package/dist/types/assets.d.ts +12 -0
- package/dist/types/react-list.d.ts +4 -0
- package/dist/types/window.d.ts +33 -0
- package/dist/utils/actionsConstants.js +4 -0
- package/dist/utils/constants.js +126 -0
- package/dist/utils/getNodesColumns.js +156 -0
- package/dist/utils/i18n/i18n.ts +7 -0
- package/dist/utils/i18n/index.ts +1 -0
- package/dist/utils/index.js +136 -0
- package/dist/utils/monaco.js +69 -0
- package/dist/utils/prepareQueryExplain.ts +101 -0
- package/dist/utils/tooltip.js +197 -0
- package/dist/utils/utils.js +75 -0
- package/package.json +89 -0
@@ -0,0 +1,44 @@
|
|
1
|
+
.nv-aside-header-logo {
|
2
|
+
display: flex;
|
3
|
+
align-items: center;
|
4
|
+
flex-shrink: 0;
|
5
|
+
|
6
|
+
height: 32px;
|
7
|
+
margin: 12px 0;
|
8
|
+
|
9
|
+
font-family: 'Rubik', sans-serif;
|
10
|
+
|
11
|
+
&__logo-btn-place {
|
12
|
+
display: flex;
|
13
|
+
align-items: center;
|
14
|
+
flex-shrink: 0;
|
15
|
+
justify-content: center;
|
16
|
+
|
17
|
+
width: var(--nv-aside-header-min-width);
|
18
|
+
}
|
19
|
+
|
20
|
+
&__logo {
|
21
|
+
vertical-align: middle;
|
22
|
+
|
23
|
+
font-family: 'Rubik', sans-serif;
|
24
|
+
font-size: 16px;
|
25
|
+
line-height: 20px;
|
26
|
+
}
|
27
|
+
|
28
|
+
&__logo-link {
|
29
|
+
&,
|
30
|
+
&:hover,
|
31
|
+
&:active,
|
32
|
+
&:visited,
|
33
|
+
&:focus {
|
34
|
+
text-decoration: none;
|
35
|
+
|
36
|
+
color: inherit;
|
37
|
+
outline: none;
|
38
|
+
}
|
39
|
+
}
|
40
|
+
|
41
|
+
.yc-root &__btn-logo.button2_theme_flat.button2_hovered_yes::before {
|
42
|
+
background-color: transparent;
|
43
|
+
}
|
44
|
+
}
|
@@ -0,0 +1,82 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import block from 'bem-cn-lite';
|
3
|
+
import {Button, Icon} from '@yandex-cloud/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
|
+
};
|
@@ -0,0 +1,92 @@
|
|
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
|
+
```
|
@@ -0,0 +1,113 @@
|
|
1
|
+
.nv-settings {
|
2
|
+
display: grid;
|
3
|
+
grid-template-columns: 216px 1fr;
|
4
|
+
width: 834px;
|
5
|
+
height: 100%;
|
6
|
+
|
7
|
+
&_loading {
|
8
|
+
grid-template-columns: auto;
|
9
|
+
}
|
10
|
+
|
11
|
+
&__loader {
|
12
|
+
align-self: center;
|
13
|
+
justify-self: center;
|
14
|
+
}
|
15
|
+
|
16
|
+
&__not-found {
|
17
|
+
display: grid;
|
18
|
+
align-items: center;
|
19
|
+
justify-items: center;
|
20
|
+
height: 100%;
|
21
|
+
}
|
22
|
+
|
23
|
+
&__menu {
|
24
|
+
border-right: 1px solid var(--yc-color-line-generic);
|
25
|
+
}
|
26
|
+
|
27
|
+
&__heading {
|
28
|
+
margin: 20px 20px 0;
|
29
|
+
font-size: var(--yc-text-body2-font-size);
|
30
|
+
line-height: var(--yc-text-body2-line-height);
|
31
|
+
font-weight: 500;
|
32
|
+
}
|
33
|
+
|
34
|
+
&__search {
|
35
|
+
margin: 12px 20px 16px;
|
36
|
+
}
|
37
|
+
|
38
|
+
&__page {
|
39
|
+
padding: 20px;
|
40
|
+
overflow-y: auto;
|
41
|
+
}
|
42
|
+
|
43
|
+
&__section {
|
44
|
+
&-heading {
|
45
|
+
margin: 0;
|
46
|
+
font-size: var(--yc-text-body2-font-size);
|
47
|
+
line-height: var(--yc-text-body2-line-height);
|
48
|
+
font-weight: 500;
|
49
|
+
|
50
|
+
&_badge {
|
51
|
+
display: inline-block;
|
52
|
+
position: relative;
|
53
|
+
|
54
|
+
&::after {
|
55
|
+
content: '';
|
56
|
+
display: block;
|
57
|
+
width: 6px;
|
58
|
+
height: 6px;
|
59
|
+
border-radius: 50%;
|
60
|
+
background-color: var(--yc-color-text-danger);
|
61
|
+
position: absolute;
|
62
|
+
right: -8px;
|
63
|
+
top: 1px;
|
64
|
+
}
|
65
|
+
}
|
66
|
+
}
|
67
|
+
|
68
|
+
&-item {
|
69
|
+
margin-top: 24px;
|
70
|
+
}
|
71
|
+
|
72
|
+
& + & {
|
73
|
+
margin-top: 32px;
|
74
|
+
}
|
75
|
+
}
|
76
|
+
|
77
|
+
&__item {
|
78
|
+
display: grid;
|
79
|
+
grid-template-columns: 216px 1fr;
|
80
|
+
justify-items: start;
|
81
|
+
|
82
|
+
&_align_top {
|
83
|
+
align-items: start;
|
84
|
+
}
|
85
|
+
|
86
|
+
&_align_center {
|
87
|
+
align-items: center;
|
88
|
+
}
|
89
|
+
}
|
90
|
+
|
91
|
+
&__item-heading {
|
92
|
+
&_badge {
|
93
|
+
position: relative;
|
94
|
+
|
95
|
+
&::after {
|
96
|
+
content: '';
|
97
|
+
display: block;
|
98
|
+
width: 4px;
|
99
|
+
height: 4px;
|
100
|
+
border-radius: 50%;
|
101
|
+
background-color: var(--yc-color-text-danger);
|
102
|
+
position: absolute;
|
103
|
+
right: -10px;
|
104
|
+
top: 1px;
|
105
|
+
}
|
106
|
+
}
|
107
|
+
}
|
108
|
+
|
109
|
+
&__found {
|
110
|
+
font-weight: 500;
|
111
|
+
background: var(--yc-color-base-selection);
|
112
|
+
}
|
113
|
+
}
|
@@ -0,0 +1,270 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import block from 'bem-cn-lite';
|
3
|
+
import i18n from './i18n';
|
4
|
+
|
5
|
+
import {IconProps, Loader} from '@yandex-cloud/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
|
+
}
|
@@ -0,0 +1,70 @@
|
|
1
|
+
.nv-settings-menu {
|
2
|
+
&__group {
|
3
|
+
&-heading {
|
4
|
+
display: inline-block;
|
5
|
+
padding: 0 20px;
|
6
|
+
margin-bottom: 12px;
|
7
|
+
font-weight: 500;
|
8
|
+
line-height: 18px;
|
9
|
+
}
|
10
|
+
|
11
|
+
& + & {
|
12
|
+
margin-top: 24px;
|
13
|
+
}
|
14
|
+
}
|
15
|
+
|
16
|
+
&__item {
|
17
|
+
$item: &;
|
18
|
+
display: flex;
|
19
|
+
align-items: center;
|
20
|
+
height: 40px;
|
21
|
+
padding: 0 20px;
|
22
|
+
|
23
|
+
&[class][class] {
|
24
|
+
color: var(--yc-color-text-primary);
|
25
|
+
|
26
|
+
&#{$item}_disabled {
|
27
|
+
color: var(--yc-color-text-secondary);
|
28
|
+
|
29
|
+
#{$item}-icon {
|
30
|
+
color: var(--yc-color-base-misc-heavy);
|
31
|
+
}
|
32
|
+
}
|
33
|
+
}
|
34
|
+
|
35
|
+
&-icon {
|
36
|
+
color: var(--yc-color-text-misc);
|
37
|
+
margin-right: 5px;
|
38
|
+
}
|
39
|
+
|
40
|
+
&:hover,
|
41
|
+
&_focused {
|
42
|
+
background: var(--yc-color-base-simple-hover);
|
43
|
+
}
|
44
|
+
|
45
|
+
&_selected {
|
46
|
+
background: var(--yc-color-base-selection);
|
47
|
+
}
|
48
|
+
|
49
|
+
&_selected:hover,
|
50
|
+
&_selected#{$item}_focused {
|
51
|
+
background: var(--yc-color-base-selection-hover);
|
52
|
+
}
|
53
|
+
|
54
|
+
&_badge {
|
55
|
+
position: relative;
|
56
|
+
|
57
|
+
&::after {
|
58
|
+
content: '';
|
59
|
+
display: block;
|
60
|
+
width: 6px;
|
61
|
+
height: 6px;
|
62
|
+
border-radius: 50%;
|
63
|
+
background-color: var(--yc-color-text-danger);
|
64
|
+
position: absolute;
|
65
|
+
right: 9px;
|
66
|
+
top: calc(50% - 3px);
|
67
|
+
}
|
68
|
+
}
|
69
|
+
}
|
70
|
+
}
|