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,37 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import block from 'bem-cn-lite';
|
3
|
+
import {Popup, PopupPlacement} from '@yandex-cloud/uikit';
|
4
|
+
import './AsideHeaderTooltip.scss';
|
5
|
+
|
6
|
+
const b = block('nv-aside-header-tooltip');
|
7
|
+
const popupPlacement: PopupPlacement = ['right'];
|
8
|
+
|
9
|
+
export interface AsideHeaderTooltipProps {
|
10
|
+
anchor: HTMLElement | null;
|
11
|
+
text: React.ReactNode;
|
12
|
+
}
|
13
|
+
|
14
|
+
export const AsideHeaderTooltip: React.FC<AsideHeaderTooltipProps> = ({anchor, text}) => {
|
15
|
+
const anchorRef = React.useRef(anchor);
|
16
|
+
|
17
|
+
React.useEffect(() => {
|
18
|
+
anchorRef.current = anchor;
|
19
|
+
}, [anchor]);
|
20
|
+
if (!anchor) {
|
21
|
+
return null;
|
22
|
+
}
|
23
|
+
|
24
|
+
return (
|
25
|
+
<Popup
|
26
|
+
className={b()}
|
27
|
+
open={true}
|
28
|
+
anchorRef={anchorRef}
|
29
|
+
placement={popupPlacement}
|
30
|
+
disableEscapeKeyDown={true}
|
31
|
+
disableOutsideClick={true}
|
32
|
+
disableLayer={true}
|
33
|
+
>
|
34
|
+
<div className={b('text')}>{text}</div>
|
35
|
+
</Popup>
|
36
|
+
);
|
37
|
+
};
|
@@ -0,0 +1,108 @@
|
|
1
|
+
.nv-composite-bar {
|
2
|
+
$class: &;
|
3
|
+
|
4
|
+
flex: 1 0 auto;
|
5
|
+
|
6
|
+
width: 100%;
|
7
|
+
min-height: 40px;
|
8
|
+
|
9
|
+
&__root-menu-item {
|
10
|
+
cursor: pointer;
|
11
|
+
}
|
12
|
+
|
13
|
+
&__root-menu-item.yc-list__item_selected:not(&__root-menu-item_compact) {
|
14
|
+
background-color: var(--yc-color-base-selection);
|
15
|
+
}
|
16
|
+
|
17
|
+
& &__root-menu-item_compact#{&}__root-menu-item {
|
18
|
+
background-color: transparent;
|
19
|
+
}
|
20
|
+
|
21
|
+
&__menu-item {
|
22
|
+
display: flex;
|
23
|
+
align-items: center;
|
24
|
+
|
25
|
+
width: 100%;
|
26
|
+
height: 100%;
|
27
|
+
}
|
28
|
+
|
29
|
+
&__menu-icon-place {
|
30
|
+
display: flex;
|
31
|
+
align-items: center;
|
32
|
+
flex-shrink: 0;
|
33
|
+
justify-content: center;
|
34
|
+
|
35
|
+
width: var(--nv-aside-header-min-width);
|
36
|
+
height: 100%;
|
37
|
+
}
|
38
|
+
|
39
|
+
#{$class} &__menu-icon {
|
40
|
+
color: var(--yc-color-text-misc);
|
41
|
+
}
|
42
|
+
|
43
|
+
&__menu-title {
|
44
|
+
overflow: hidden;
|
45
|
+
|
46
|
+
width: 100%;
|
47
|
+
|
48
|
+
white-space: nowrap;
|
49
|
+
text-overflow: ellipsis;
|
50
|
+
}
|
51
|
+
|
52
|
+
&__root-collapse-item {
|
53
|
+
cursor: pointer;
|
54
|
+
}
|
55
|
+
|
56
|
+
&__collapse-item {
|
57
|
+
display: flex;
|
58
|
+
align-items: center;
|
59
|
+
|
60
|
+
width: 100%;
|
61
|
+
height: 100%;
|
62
|
+
padding: 0 16px;
|
63
|
+
}
|
64
|
+
|
65
|
+
&__collapse-items-popup-content {
|
66
|
+
padding: 4px 0;
|
67
|
+
}
|
68
|
+
|
69
|
+
&__link {
|
70
|
+
display: flex;
|
71
|
+
align-items: center;
|
72
|
+
|
73
|
+
width: 100%;
|
74
|
+
height: 100%;
|
75
|
+
|
76
|
+
&,
|
77
|
+
&:hover,
|
78
|
+
&:active,
|
79
|
+
&:visited,
|
80
|
+
&:focus {
|
81
|
+
text-decoration: none;
|
82
|
+
|
83
|
+
color: inherit;
|
84
|
+
outline: none;
|
85
|
+
}
|
86
|
+
}
|
87
|
+
|
88
|
+
&__btn-icon {
|
89
|
+
display: flex;
|
90
|
+
align-items: center;
|
91
|
+
justify-content: center;
|
92
|
+
|
93
|
+
width: 100%;
|
94
|
+
height: 100%;
|
95
|
+
|
96
|
+
&::before {
|
97
|
+
border-radius: 0;
|
98
|
+
}
|
99
|
+
|
100
|
+
&_current {
|
101
|
+
background-color: var(--yc-color-base-selection);
|
102
|
+
}
|
103
|
+
|
104
|
+
@at-root .yc-list__item_active &:not(&_current) {
|
105
|
+
background-color: var(--yc-color-base-simple-hover);
|
106
|
+
}
|
107
|
+
}
|
108
|
+
}
|
@@ -0,0 +1,282 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import block from 'bem-cn-lite';
|
3
|
+
import AutoSizer from 'react-virtualized-auto-sizer';
|
4
|
+
import {List, Icon, Popup, PopupPlacement} from '@yandex-cloud/uikit';
|
5
|
+
|
6
|
+
import {AsideHeaderMenuItem} from '../types';
|
7
|
+
import i18n from '../i18n';
|
8
|
+
import {AsideHeaderTooltip} from '../AsideHeaderTooltip/AsideHeaderTooltip';
|
9
|
+
|
10
|
+
import dotsIcon from '../../../assets/icons/dots.svg';
|
11
|
+
|
12
|
+
import './CompositeBar.scss';
|
13
|
+
|
14
|
+
const b = block('nv-composite-bar');
|
15
|
+
export const ITEM_HEIGHT = 40;
|
16
|
+
const POPUP_ITEM_HEIGHT = 28;
|
17
|
+
const COLLAPSE_ITEM_ID = 'nv-collapse-item-id';
|
18
|
+
const popupPlacement: PopupPlacement = ['right-start', 'right-end', 'right'];
|
19
|
+
|
20
|
+
const getSelectedItemIndex = (items: AsideHeaderMenuItem[]) => {
|
21
|
+
const index = items.findIndex(({current}) => Boolean(current));
|
22
|
+
return index === -1 ? undefined : index;
|
23
|
+
};
|
24
|
+
|
25
|
+
interface ItemProps {
|
26
|
+
item: AsideHeaderMenuItem;
|
27
|
+
isCompact: boolean;
|
28
|
+
collapseItems: AsideHeaderMenuItem[] | null;
|
29
|
+
onMouseEnter?: () => void;
|
30
|
+
onMouseLeave?: () => void;
|
31
|
+
onClick?: () => void;
|
32
|
+
}
|
33
|
+
|
34
|
+
const Item: React.FC<ItemProps> = ({item, isCompact, collapseItems, onClick}) => {
|
35
|
+
const [tooltipAnchor, setTooltipAnchor] = React.useState<HTMLDivElement | null>(null);
|
36
|
+
const [open, toggleOpen] = React.useState<boolean>(false);
|
37
|
+
const popupAnchor = React.useRef<HTMLDivElement>(null);
|
38
|
+
|
39
|
+
const tooltipText = item.tooltipText || item.title;
|
40
|
+
const iconSize = item.iconSize || 24;
|
41
|
+
const isCollapseItem = item.id === COLLAPSE_ITEM_ID;
|
42
|
+
|
43
|
+
const node = (
|
44
|
+
<div
|
45
|
+
className={b('menu-item')}
|
46
|
+
ref={popupAnchor}
|
47
|
+
onClick={() => {
|
48
|
+
if (typeof item.onItemClick === 'function') {
|
49
|
+
item.onItemClick(item, false);
|
50
|
+
}
|
51
|
+
if (isCollapseItem) {
|
52
|
+
toggleOpen(!open);
|
53
|
+
setTooltipAnchor(null);
|
54
|
+
}
|
55
|
+
onClick?.();
|
56
|
+
}}
|
57
|
+
>
|
58
|
+
<div className={b('menu-icon-place')}>
|
59
|
+
{isCompact ? (
|
60
|
+
<React.Fragment>
|
61
|
+
<div
|
62
|
+
onMouseEnter={(event) => !open && setTooltipAnchor(event.currentTarget)}
|
63
|
+
onMouseLeave={() => setTooltipAnchor(null)}
|
64
|
+
className={b('btn-icon', {current: Boolean(item.current)})}
|
65
|
+
>
|
66
|
+
{item.icon && (
|
67
|
+
<Icon data={item.icon} size={iconSize} className={b('menu-icon')} />
|
68
|
+
)}
|
69
|
+
</div>
|
70
|
+
<AsideHeaderTooltip anchor={tooltipAnchor} text={tooltipText} />
|
71
|
+
</React.Fragment>
|
72
|
+
) : (
|
73
|
+
item.icon && (
|
74
|
+
<Icon data={item.icon} size={iconSize} className={b('menu-icon')} />
|
75
|
+
)
|
76
|
+
)}
|
77
|
+
</div>
|
78
|
+
<div className={b('menu-title')} title={item.title}>
|
79
|
+
{item.title}
|
80
|
+
</div>
|
81
|
+
{isCollapseItem && Array.isArray(collapseItems) && Boolean(popupAnchor.current) && (
|
82
|
+
<Popup
|
83
|
+
placement={popupPlacement}
|
84
|
+
open={open}
|
85
|
+
anchorRef={popupAnchor}
|
86
|
+
onClose={() => toggleOpen(false)}
|
87
|
+
>
|
88
|
+
<div className={b('collapse-items-popup-content')}>
|
89
|
+
<List
|
90
|
+
itemClassName={b('root-collapse-item')}
|
91
|
+
items={collapseItems}
|
92
|
+
selectedItemIndex={getSelectedItemIndex(collapseItems)}
|
93
|
+
itemHeight={POPUP_ITEM_HEIGHT}
|
94
|
+
itemsHeight={collapseItems.length * POPUP_ITEM_HEIGHT}
|
95
|
+
virtualized={false}
|
96
|
+
filterable={false}
|
97
|
+
sortable={false}
|
98
|
+
renderItem={(collapseItem) => {
|
99
|
+
const collapseNode = (
|
100
|
+
<div
|
101
|
+
className={b('collapse-item')}
|
102
|
+
onClick={() => {
|
103
|
+
if (typeof collapseItem.onItemClick === 'function') {
|
104
|
+
collapseItem.onItemClick(collapseItem, true);
|
105
|
+
}
|
106
|
+
}}
|
107
|
+
>
|
108
|
+
{collapseItem.title}
|
109
|
+
</div>
|
110
|
+
);
|
111
|
+
if (typeof collapseItem.itemWrapper === 'function') {
|
112
|
+
return collapseItem.itemWrapper(
|
113
|
+
collapseNode,
|
114
|
+
collapseItem,
|
115
|
+
true,
|
116
|
+
isCompact,
|
117
|
+
);
|
118
|
+
}
|
119
|
+
|
120
|
+
return collapseItem.link ? (
|
121
|
+
<a href={collapseItem.link} className={b('link')}>
|
122
|
+
{collapseNode}
|
123
|
+
</a>
|
124
|
+
) : (
|
125
|
+
collapseNode
|
126
|
+
);
|
127
|
+
}}
|
128
|
+
/>
|
129
|
+
</div>
|
130
|
+
</Popup>
|
131
|
+
)}
|
132
|
+
</div>
|
133
|
+
);
|
134
|
+
|
135
|
+
if (typeof item.itemWrapper === 'function') {
|
136
|
+
return item.itemWrapper(node, item, false, isCompact) as React.ReactElement;
|
137
|
+
}
|
138
|
+
|
139
|
+
return item.link ? (
|
140
|
+
<a href={item.link} className={b('link')}>
|
141
|
+
{node}
|
142
|
+
</a>
|
143
|
+
) : (
|
144
|
+
node
|
145
|
+
);
|
146
|
+
};
|
147
|
+
Item.displayName = 'Item';
|
148
|
+
|
149
|
+
interface CompositeBarProps {
|
150
|
+
items: AsideHeaderMenuItem[];
|
151
|
+
isCompact: boolean;
|
152
|
+
onClickItem?: (item: AsideHeaderMenuItem) => void;
|
153
|
+
}
|
154
|
+
|
155
|
+
interface CompositeBarState {
|
156
|
+
height: string | number;
|
157
|
+
activeItemIndex?: number;
|
158
|
+
}
|
159
|
+
|
160
|
+
interface OnResizeArgs {
|
161
|
+
width: number;
|
162
|
+
height: number;
|
163
|
+
}
|
164
|
+
|
165
|
+
export class CompositeBar extends React.Component<CompositeBarProps> {
|
166
|
+
render() {
|
167
|
+
return (
|
168
|
+
<React.Fragment>
|
169
|
+
<div className={b()} style={{height: this.state.height}}>
|
170
|
+
{this.props.items.length !== 0 && (
|
171
|
+
<AutoSizer onResize={this.onResize}>
|
172
|
+
{({width, height}) => {
|
173
|
+
const style = {
|
174
|
+
width,
|
175
|
+
height,
|
176
|
+
};
|
177
|
+
return <div style={style}>{this.renderMenu(height)}</div>;
|
178
|
+
}}
|
179
|
+
</AutoSizer>
|
180
|
+
)}
|
181
|
+
</div>
|
182
|
+
</React.Fragment>
|
183
|
+
);
|
184
|
+
}
|
185
|
+
|
186
|
+
state: CompositeBarState = {
|
187
|
+
height: 'auto',
|
188
|
+
activeItemIndex: undefined,
|
189
|
+
};
|
190
|
+
|
191
|
+
private currentItemsCount = 0;
|
192
|
+
private skipCheckResize = false;
|
193
|
+
|
194
|
+
private onResize = ({height}: OnResizeArgs) => {
|
195
|
+
if (this.skipCheckResize) {
|
196
|
+
this.skipCheckResize = false;
|
197
|
+
return;
|
198
|
+
}
|
199
|
+
|
200
|
+
const desiredHeight = this.currentItemsCount * ITEM_HEIGHT;
|
201
|
+
if (height < desiredHeight) {
|
202
|
+
if (this.state.height !== desiredHeight) {
|
203
|
+
this.skipCheckResize = true;
|
204
|
+
this.setState({height: desiredHeight});
|
205
|
+
}
|
206
|
+
} else if (this.state.height !== 'auto') {
|
207
|
+
this.skipCheckResize = true;
|
208
|
+
this.setState({height: 'auto'});
|
209
|
+
}
|
210
|
+
};
|
211
|
+
|
212
|
+
private renderMenu(height: number) {
|
213
|
+
const {items, isCompact, onClickItem} = this.props;
|
214
|
+
const capacity = Math.max(1, Math.floor(height / ITEM_HEIGHT));
|
215
|
+
let listItems: AsideHeaderMenuItem[] | null;
|
216
|
+
let collapseItems: AsideHeaderMenuItem[] | null = null;
|
217
|
+
if (capacity === 1) {
|
218
|
+
listItems = items.filter((item) => item.pinned);
|
219
|
+
collapseItems = [...items.filter((item) => !item.pinned)];
|
220
|
+
if (collapseItems.length > 0) {
|
221
|
+
listItems.push(this.getCollapseItem());
|
222
|
+
}
|
223
|
+
} else if (capacity < items.length) {
|
224
|
+
const extraCount = items.filter(
|
225
|
+
(item, idx) => item.pinned && idx >= capacity - 1,
|
226
|
+
).length;
|
227
|
+
const pinnedFlag = items.reduceRight(
|
228
|
+
(acc, curr, idx) => {
|
229
|
+
const useExtraCount = !curr.pinned && idx < capacity - 1 && acc.extraCount > 0;
|
230
|
+
acc.flags.unshift(curr.pinned || useExtraCount);
|
231
|
+
return {
|
232
|
+
flags: acc.flags,
|
233
|
+
extraCount: acc.extraCount - Number(useExtraCount),
|
234
|
+
};
|
235
|
+
},
|
236
|
+
{flags: [] as boolean[], extraCount},
|
237
|
+
).flags;
|
238
|
+
listItems = items.filter(
|
239
|
+
(item, idx) => item.pinned || (idx < capacity - 1 && !pinnedFlag[idx]),
|
240
|
+
);
|
241
|
+
collapseItems = items.filter(
|
242
|
+
(item, idx) => !item.pinned && (idx >= capacity - 1 || pinnedFlag[idx]),
|
243
|
+
);
|
244
|
+
if (collapseItems.length > 0) {
|
245
|
+
listItems.push(this.getCollapseItem());
|
246
|
+
}
|
247
|
+
} else {
|
248
|
+
listItems = [...items];
|
249
|
+
}
|
250
|
+
this.currentItemsCount = listItems.length;
|
251
|
+
|
252
|
+
return (
|
253
|
+
<List
|
254
|
+
items={listItems}
|
255
|
+
selectedItemIndex={isCompact ? undefined : getSelectedItemIndex(listItems)}
|
256
|
+
itemHeight={ITEM_HEIGHT}
|
257
|
+
itemClassName={b('root-menu-item', {compact: isCompact})}
|
258
|
+
itemsHeight={listItems.length * ITEM_HEIGHT}
|
259
|
+
virtualized={false}
|
260
|
+
filterable={false}
|
261
|
+
sortable={false}
|
262
|
+
renderItem={(item) => (
|
263
|
+
<Item
|
264
|
+
item={item}
|
265
|
+
isCompact={isCompact}
|
266
|
+
collapseItems={collapseItems}
|
267
|
+
onClick={() => onClickItem?.(item)}
|
268
|
+
/>
|
269
|
+
)}
|
270
|
+
/>
|
271
|
+
);
|
272
|
+
}
|
273
|
+
|
274
|
+
private getCollapseItem(): AsideHeaderMenuItem {
|
275
|
+
return {
|
276
|
+
id: COLLAPSE_ITEM_ID,
|
277
|
+
title: i18n('label_more'),
|
278
|
+
icon: dotsIcon,
|
279
|
+
iconSize: 16,
|
280
|
+
};
|
281
|
+
}
|
282
|
+
}
|
@@ -0,0 +1,35 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
|
3
|
+
export type RenderContentType = (data: {size: number}) => React.ReactNode;
|
4
|
+
|
5
|
+
interface ContentProps {
|
6
|
+
renderContent?: RenderContentType;
|
7
|
+
className?: string;
|
8
|
+
size: number;
|
9
|
+
}
|
10
|
+
|
11
|
+
interface RenderContentProps {
|
12
|
+
renderContent: RenderContentType;
|
13
|
+
size: number;
|
14
|
+
}
|
15
|
+
|
16
|
+
const RenderContent: React.FC<RenderContentProps> = React.memo(({renderContent, size}) => {
|
17
|
+
return <React.Fragment>{renderContent({size})}</React.Fragment>;
|
18
|
+
});
|
19
|
+
|
20
|
+
RenderContent.displayName = 'RenderContent';
|
21
|
+
|
22
|
+
export const Content: React.FC<ContentProps> = ({size, className, renderContent}) => {
|
23
|
+
return (
|
24
|
+
<div
|
25
|
+
className={className}
|
26
|
+
style={{
|
27
|
+
...({'--nv-aside-header-size': `${size}px`} as React.CSSProperties),
|
28
|
+
}}
|
29
|
+
>
|
30
|
+
{typeof renderContent === 'function' && (
|
31
|
+
<RenderContent size={size} renderContent={renderContent} />
|
32
|
+
)}
|
33
|
+
</div>
|
34
|
+
);
|
35
|
+
};
|
@@ -0,0 +1,76 @@
|
|
1
|
+
.nv-drawer {
|
2
|
+
&__item {
|
3
|
+
position: absolute;
|
4
|
+
top: 0;
|
5
|
+
bottom: 0;
|
6
|
+
left: 0;
|
7
|
+
|
8
|
+
height: 100%;
|
9
|
+
|
10
|
+
background-color: var(--yc-color-base-background);
|
11
|
+
will-change: transform;
|
12
|
+
}
|
13
|
+
|
14
|
+
&__item-transition-enter {
|
15
|
+
transform: translate(-100%, 0);
|
16
|
+
}
|
17
|
+
|
18
|
+
&__item-transition-enter-active {
|
19
|
+
transition: transform 300ms;
|
20
|
+
transform: translate(0, 0);
|
21
|
+
}
|
22
|
+
|
23
|
+
&__item-transition-enter-done {
|
24
|
+
// Хак, чтобы не размывался текст после анимации в некоторых браузерах
|
25
|
+
filter: blur(0px);
|
26
|
+
|
27
|
+
transform: translateZ(0);
|
28
|
+
}
|
29
|
+
|
30
|
+
&__item-transition-exit {
|
31
|
+
transform: translate(0, 0);
|
32
|
+
}
|
33
|
+
|
34
|
+
&__item-transition-exit-active {
|
35
|
+
transition: transform 300ms;
|
36
|
+
transform: translate(-100%, 0);
|
37
|
+
}
|
38
|
+
|
39
|
+
&__item-transition-exit-done {
|
40
|
+
visibility: hidden;
|
41
|
+
}
|
42
|
+
|
43
|
+
&__veil {
|
44
|
+
position: absolute;
|
45
|
+
top: 0;
|
46
|
+
right: 0;
|
47
|
+
bottom: 0;
|
48
|
+
left: 0;
|
49
|
+
|
50
|
+
background-color: var(--yc-color-sfx-veil);
|
51
|
+
}
|
52
|
+
|
53
|
+
&__veil-transition-enter {
|
54
|
+
opacity: 0;
|
55
|
+
}
|
56
|
+
|
57
|
+
&__veil-transition-enter-active {
|
58
|
+
transition: opacity 300ms;
|
59
|
+
|
60
|
+
opacity: 1;
|
61
|
+
}
|
62
|
+
|
63
|
+
&__veil-transition-exit {
|
64
|
+
opacity: 1;
|
65
|
+
}
|
66
|
+
|
67
|
+
&__veil-transition-exit-active {
|
68
|
+
transition: opacity 300ms;
|
69
|
+
|
70
|
+
opacity: 0;
|
71
|
+
}
|
72
|
+
|
73
|
+
&__veil-transition-exit-done {
|
74
|
+
visibility: hidden;
|
75
|
+
}
|
76
|
+
}
|
@@ -0,0 +1,134 @@
|
|
1
|
+
import React from 'react';
|
2
|
+
import block from 'bem-cn-lite';
|
3
|
+
import {CSSTransition, Transition} from 'react-transition-group';
|
4
|
+
import './Drawer.scss';
|
5
|
+
|
6
|
+
const b = block('nv-drawer');
|
7
|
+
const TIMEOUT = 300;
|
8
|
+
|
9
|
+
export interface DrawerItemProps {
|
10
|
+
visible: boolean;
|
11
|
+
className?: string;
|
12
|
+
}
|
13
|
+
|
14
|
+
export const DrawerItem: React.FC<DrawerItemProps> = ({className, visible, children}) => {
|
15
|
+
return (
|
16
|
+
<CSSTransition
|
17
|
+
in={visible}
|
18
|
+
timeout={TIMEOUT}
|
19
|
+
unmountOnExit={true}
|
20
|
+
classNames={b('item-transition')}
|
21
|
+
>
|
22
|
+
<div className={b('item', className)}>{children}</div>
|
23
|
+
</CSSTransition>
|
24
|
+
);
|
25
|
+
};
|
26
|
+
|
27
|
+
type DrawerChild = React.ReactElement<DrawerItemProps>;
|
28
|
+
|
29
|
+
export interface DrawerProps {
|
30
|
+
children: DrawerChild | DrawerChild[];
|
31
|
+
preventSrcollBody?: boolean;
|
32
|
+
className?: string;
|
33
|
+
style?: React.CSSProperties;
|
34
|
+
onVeilClick?: (event: React.MouseEvent<HTMLDivElement, MouseEvent>) => void;
|
35
|
+
onEscape?: () => void;
|
36
|
+
}
|
37
|
+
|
38
|
+
export const Drawer: React.FC<DrawerProps> = ({
|
39
|
+
className,
|
40
|
+
children,
|
41
|
+
style,
|
42
|
+
onVeilClick,
|
43
|
+
onEscape,
|
44
|
+
preventSrcollBody = true,
|
45
|
+
}) => {
|
46
|
+
let someItemVisible = false;
|
47
|
+
React.Children.forEach(children, (child) => {
|
48
|
+
const childElem = child as DrawerChild;
|
49
|
+
if (childElem.type === DrawerItem) {
|
50
|
+
const childVisible = Boolean(childElem.props.visible);
|
51
|
+
if (childVisible) {
|
52
|
+
someItemVisible = true;
|
53
|
+
}
|
54
|
+
}
|
55
|
+
});
|
56
|
+
|
57
|
+
React.useEffect(() => {
|
58
|
+
function onKeyDown(event: KeyboardEvent) {
|
59
|
+
if (event.key === 'Escape') {
|
60
|
+
onEscape?.();
|
61
|
+
}
|
62
|
+
}
|
63
|
+
if (someItemVisible) {
|
64
|
+
window.addEventListener('keydown', onKeyDown);
|
65
|
+
}
|
66
|
+
return () => {
|
67
|
+
window.removeEventListener('keydown', onKeyDown);
|
68
|
+
};
|
69
|
+
}, [onEscape, someItemVisible]);
|
70
|
+
|
71
|
+
React.useEffect(() => {
|
72
|
+
const prevPreventSrcollBody = preventSrcollBody;
|
73
|
+
const cleanupPreventScrollBody = () => {
|
74
|
+
document.body.style.paddingRight = '';
|
75
|
+
document.body.style.paddingBottom = '';
|
76
|
+
document.body.style.overflow = '';
|
77
|
+
};
|
78
|
+
const setPreventScrollBody = () => {
|
79
|
+
const vw = window.innerWidth - document.documentElement.clientWidth;
|
80
|
+
const hw = window.innerHeight - document.documentElement.clientHeight;
|
81
|
+
document.body.style.paddingRight = vw + 'px';
|
82
|
+
document.body.style.paddingBottom = hw + 'px';
|
83
|
+
document.body.style.overflow = 'hidden';
|
84
|
+
};
|
85
|
+
if (prevPreventSrcollBody) {
|
86
|
+
if (someItemVisible) {
|
87
|
+
setPreventScrollBody();
|
88
|
+
} else {
|
89
|
+
cleanupPreventScrollBody();
|
90
|
+
}
|
91
|
+
}
|
92
|
+
return () => {
|
93
|
+
if (prevPreventSrcollBody) {
|
94
|
+
cleanupPreventScrollBody();
|
95
|
+
}
|
96
|
+
};
|
97
|
+
}, [someItemVisible, preventSrcollBody]);
|
98
|
+
|
99
|
+
return (
|
100
|
+
<Transition
|
101
|
+
in={someItemVisible}
|
102
|
+
timeout={{enter: 0, exit: TIMEOUT}}
|
103
|
+
mountOnEnter
|
104
|
+
unmountOnExit
|
105
|
+
>
|
106
|
+
{(state) => {
|
107
|
+
const childrenVisible = someItemVisible && state === 'entered';
|
108
|
+
return (
|
109
|
+
<div className={b(null, className)} style={style}>
|
110
|
+
<CSSTransition
|
111
|
+
in={childrenVisible}
|
112
|
+
timeout={TIMEOUT}
|
113
|
+
unmountOnExit={true}
|
114
|
+
classNames={b('veil-transition')}
|
115
|
+
>
|
116
|
+
<div className={b('veil')} onClick={onVeilClick} />
|
117
|
+
</CSSTransition>
|
118
|
+
{React.Children.map(children, (child) => {
|
119
|
+
const childElem = child as DrawerChild;
|
120
|
+
if (childElem.type === DrawerItem) {
|
121
|
+
const childVisible = Boolean(childElem.props.visible);
|
122
|
+
return React.cloneElement(childElem, {
|
123
|
+
...childElem.props,
|
124
|
+
visible: childVisible && childrenVisible,
|
125
|
+
});
|
126
|
+
}
|
127
|
+
return child;
|
128
|
+
})}
|
129
|
+
</div>
|
130
|
+
);
|
131
|
+
}}
|
132
|
+
</Transition>
|
133
|
+
);
|
134
|
+
};
|
@@ -0,0 +1 @@
|
|
1
|
+
export * from './Drawer';
|