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,141 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import block from 'bem-cn-lite';
|
3
|
-
|
4
|
-
import {Icon, IconProps, Link} from '@gravity-ui/uikit';
|
5
|
-
|
6
|
-
import {useCurrent, useStableCallback} from '../helpers';
|
7
|
-
|
8
|
-
import './SettingsMenu.scss';
|
9
|
-
|
10
|
-
const b = block('nv-settings-menu');
|
11
|
-
|
12
|
-
interface GroupItem {
|
13
|
-
groupTitle: string;
|
14
|
-
items: Item[];
|
15
|
-
}
|
16
|
-
|
17
|
-
interface Item {
|
18
|
-
id: string;
|
19
|
-
title: string;
|
20
|
-
icon?: IconProps;
|
21
|
-
disabled?: boolean;
|
22
|
-
withBadge?: boolean;
|
23
|
-
}
|
24
|
-
|
25
|
-
export type SettingsMenuItems = (GroupItem | Item)[];
|
26
|
-
|
27
|
-
interface SettingsMenuProps {
|
28
|
-
items: SettingsMenuItems;
|
29
|
-
onChange: (id: string) => void;
|
30
|
-
activeItem?: string;
|
31
|
-
focusItem?: string;
|
32
|
-
}
|
33
|
-
|
34
|
-
export interface SettingsMenuInstance {
|
35
|
-
handleKeyDown(event: React.KeyboardEvent): boolean;
|
36
|
-
clearFocus(): void;
|
37
|
-
}
|
38
|
-
|
39
|
-
export const SettingsMenu = React.forwardRef<SettingsMenuInstance, SettingsMenuProps>(
|
40
|
-
function SettingsMenu({items, onChange, activeItem}, ref) {
|
41
|
-
const [focusItem, setFocus] = React.useState<string>();
|
42
|
-
const containerRef = React.useRef<HTMLDivElement>(null);
|
43
|
-
const handleChange = useStableCallback(onChange);
|
44
|
-
const getFocused = useCurrent(focusItem);
|
45
|
-
|
46
|
-
React.useImperativeHandle(
|
47
|
-
ref,
|
48
|
-
() => ({
|
49
|
-
handleKeyDown(event) {
|
50
|
-
if (!containerRef.current) {
|
51
|
-
return false;
|
52
|
-
}
|
53
|
-
const focused = getFocused();
|
54
|
-
if (focused && event.key === 'Enter') {
|
55
|
-
handleChange(focused);
|
56
|
-
return true;
|
57
|
-
} else if (event.key === 'ArrowDown') {
|
58
|
-
setFocus(focusNext(containerRef.current, focused, 1));
|
59
|
-
return true;
|
60
|
-
} else if (event.key === 'ArrowUp') {
|
61
|
-
setFocus(focusNext(containerRef.current, focused, -1));
|
62
|
-
return true;
|
63
|
-
}
|
64
|
-
return false;
|
65
|
-
},
|
66
|
-
clearFocus() {
|
67
|
-
setFocus(undefined);
|
68
|
-
},
|
69
|
-
}),
|
70
|
-
[getFocused, handleChange],
|
71
|
-
);
|
72
|
-
|
73
|
-
return (
|
74
|
-
<div ref={containerRef} className={b()}>
|
75
|
-
{items.map((firstLevelItem) => {
|
76
|
-
if ('groupTitle' in firstLevelItem) {
|
77
|
-
return (
|
78
|
-
<div key={firstLevelItem.groupTitle} className={b('group')}>
|
79
|
-
<span className={b('group-heading')}>
|
80
|
-
{firstLevelItem.groupTitle}
|
81
|
-
</span>
|
82
|
-
{firstLevelItem.items.map((item) => {
|
83
|
-
return renderMenuItem(item, onChange, activeItem, focusItem);
|
84
|
-
})}
|
85
|
-
</div>
|
86
|
-
);
|
87
|
-
}
|
88
|
-
return renderMenuItem(firstLevelItem, onChange, activeItem, focusItem);
|
89
|
-
})}
|
90
|
-
</div>
|
91
|
-
);
|
92
|
-
},
|
93
|
-
);
|
94
|
-
|
95
|
-
function renderMenuItem(
|
96
|
-
item: Item,
|
97
|
-
onChange: (id: string) => void,
|
98
|
-
activeItem: string | undefined,
|
99
|
-
focusItem: string | undefined,
|
100
|
-
) {
|
101
|
-
return (
|
102
|
-
<Link
|
103
|
-
key={item.title}
|
104
|
-
// @ts-ignore
|
105
|
-
extraProps={{'data-id': item.id, tabIndex: -1, disabled: item.disabled}}
|
106
|
-
className={b('item', {
|
107
|
-
selected: activeItem === item.id,
|
108
|
-
disabled: item.disabled,
|
109
|
-
focused: focusItem === item.id,
|
110
|
-
badge: item.withBadge,
|
111
|
-
})}
|
112
|
-
iconLeft={
|
113
|
-
item.icon ? <Icon size={16} {...item.icon} className={b('item-icon')} /> : undefined
|
114
|
-
}
|
115
|
-
onClick={() => {
|
116
|
-
onChange(item.id);
|
117
|
-
}}
|
118
|
-
>
|
119
|
-
{item.icon ? <Icon size={16} {...item.icon} className={b('item-icon')} /> : undefined}
|
120
|
-
<span>{item.title}</span>
|
121
|
-
</Link>
|
122
|
-
);
|
123
|
-
}
|
124
|
-
|
125
|
-
function focusNext(container: HTMLElement, focused: string | undefined, direction: number) {
|
126
|
-
const elements = container.querySelectorAll(`.${b('item')}:not(.${b('item')}_disabled)`);
|
127
|
-
if (elements.length === 0) {
|
128
|
-
return undefined;
|
129
|
-
}
|
130
|
-
|
131
|
-
let currentIndex = direction > 0 ? -1 : 0;
|
132
|
-
if (focused) {
|
133
|
-
currentIndex = Array.prototype.findIndex.call(
|
134
|
-
elements,
|
135
|
-
(element) => element.getAttribute('data-id') === focused,
|
136
|
-
);
|
137
|
-
}
|
138
|
-
|
139
|
-
currentIndex = (elements.length + currentIndex + direction) % elements.length;
|
140
|
-
return elements[currentIndex].getAttribute('data-id') ?? undefined;
|
141
|
-
}
|
@@ -1,57 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import block from 'bem-cn-lite';
|
3
|
-
import {TextInput} from '@gravity-ui/uikit';
|
4
|
-
|
5
|
-
import i18n from '../i18n';
|
6
|
-
|
7
|
-
const b = block('nv-settings-search');
|
8
|
-
|
9
|
-
interface SettingsSearchProps {
|
10
|
-
className?: string;
|
11
|
-
onChange: (search: string) => void;
|
12
|
-
debounce?: number;
|
13
|
-
inputRef?: React.Ref<HTMLInputElement>;
|
14
|
-
}
|
15
|
-
|
16
|
-
export function SettingsSearch({
|
17
|
-
className,
|
18
|
-
onChange,
|
19
|
-
debounce = 200,
|
20
|
-
inputRef,
|
21
|
-
}: SettingsSearchProps) {
|
22
|
-
const [search, setSearch] = React.useState<string>();
|
23
|
-
const onChangeRef = React.useRef(onChange);
|
24
|
-
onChangeRef.current = onChange;
|
25
|
-
const debounceRef = React.useRef(debounce);
|
26
|
-
debounceRef.current = debounce;
|
27
|
-
|
28
|
-
React.useEffect(() => {
|
29
|
-
let timerId: number;
|
30
|
-
if (search !== undefined) {
|
31
|
-
timerId = window.setTimeout(() => {
|
32
|
-
onChangeRef.current(search);
|
33
|
-
}, debounceRef.current);
|
34
|
-
}
|
35
|
-
return () => {
|
36
|
-
clearTimeout(timerId);
|
37
|
-
};
|
38
|
-
}, [search]);
|
39
|
-
return (
|
40
|
-
<div className={b(null, className)}>
|
41
|
-
<TextInput
|
42
|
-
controlRef={(node) => {
|
43
|
-
if (typeof inputRef === 'function') {
|
44
|
-
inputRef(node as HTMLInputElement);
|
45
|
-
} else if (inputRef) {
|
46
|
-
(inputRef.current as HTMLInputElement) = node as HTMLInputElement;
|
47
|
-
}
|
48
|
-
}}
|
49
|
-
hasClear
|
50
|
-
autoFocus
|
51
|
-
placeholder={i18n('placeholder_search')}
|
52
|
-
value={search}
|
53
|
-
onUpdate={setSearch}
|
54
|
-
/>
|
55
|
-
</div>
|
56
|
-
);
|
57
|
-
}
|
@@ -1,156 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import {IconProps} from '@gravity-ui/uikit';
|
3
|
-
import {invariant} from './helpers';
|
4
|
-
|
5
|
-
type SettingsMenu = (SettingsMenuGroup | SettingsMenuItem)[];
|
6
|
-
|
7
|
-
interface SettingsMenuGroup {
|
8
|
-
groupTitle: string;
|
9
|
-
items: SettingsMenuItem[];
|
10
|
-
}
|
11
|
-
|
12
|
-
interface SettingsMenuItem {
|
13
|
-
title: string;
|
14
|
-
icon?: IconProps;
|
15
|
-
pageId: string;
|
16
|
-
withBadge?: boolean;
|
17
|
-
}
|
18
|
-
|
19
|
-
export interface SettingsPage {
|
20
|
-
sections: SettingsPageSection[];
|
21
|
-
hide?: boolean;
|
22
|
-
withBadge?: boolean;
|
23
|
-
}
|
24
|
-
|
25
|
-
interface SettingsPageSection {
|
26
|
-
title: string;
|
27
|
-
header?: React.ReactNode;
|
28
|
-
items: SettingsItem[];
|
29
|
-
hide?: boolean;
|
30
|
-
withBadge?: boolean;
|
31
|
-
}
|
32
|
-
|
33
|
-
interface SettingsItem {
|
34
|
-
title: string;
|
35
|
-
children: React.ReactNode;
|
36
|
-
hide?: boolean;
|
37
|
-
titleComponent?: React.ReactNode;
|
38
|
-
renderTitleComponent?: (highlightedTitle: React.ReactNode | null) => React.ReactNode;
|
39
|
-
}
|
40
|
-
|
41
|
-
export function getSettingsFromChildren(
|
42
|
-
children: React.ReactNode,
|
43
|
-
basepath = '',
|
44
|
-
): {menu: SettingsMenu; pages: Record<string, SettingsPage>} {
|
45
|
-
const menu: SettingsMenu = [];
|
46
|
-
const pages: Record<string, SettingsPage> = {};
|
47
|
-
let hasGroup = false;
|
48
|
-
let hasItems = false;
|
49
|
-
React.Children.forEach(children, (element) => {
|
50
|
-
if (!React.isValidElement(element)) {
|
51
|
-
// Ignore non-elements.
|
52
|
-
return;
|
53
|
-
}
|
54
|
-
if (element.type === React.Fragment) {
|
55
|
-
// Transparently support React.Fragment and its children.
|
56
|
-
const {menu: menuFragment, pages: pagesFragment} = getSettingsFromChildren(
|
57
|
-
element.props.children,
|
58
|
-
basepath,
|
59
|
-
);
|
60
|
-
menu.push(...menuFragment);
|
61
|
-
Object.assign(pages, pagesFragment);
|
62
|
-
} else if (element.props.groupTitle) {
|
63
|
-
if (process.env.NODE_ENV === 'development') {
|
64
|
-
invariant(!hasItems, 'Setting menu must not mix groups and pages on one level');
|
65
|
-
}
|
66
|
-
|
67
|
-
const pageId = `${basepath}/${element.props.id ?? element.props.groupTitle}`;
|
68
|
-
hasGroup = true;
|
69
|
-
|
70
|
-
const {menu: menuFragment, pages: pagesFragment} = getSettingsFromChildren(
|
71
|
-
element.props.children,
|
72
|
-
pageId,
|
73
|
-
);
|
74
|
-
|
75
|
-
if (process.env.NODE_ENV === 'development') {
|
76
|
-
const hasInnerGroup = menuFragment.some((item) => 'groupTitle' in item);
|
77
|
-
invariant(
|
78
|
-
!hasInnerGroup,
|
79
|
-
`Group ${element.props.groupTitle} should not include groups`,
|
80
|
-
);
|
81
|
-
}
|
82
|
-
|
83
|
-
menu.push({
|
84
|
-
groupTitle: element.props.groupTitle,
|
85
|
-
// @ts-ignore
|
86
|
-
items: menuFragment,
|
87
|
-
});
|
88
|
-
Object.assign(pages, pagesFragment);
|
89
|
-
} else {
|
90
|
-
hasItems = true;
|
91
|
-
const pageId = `${basepath}/${element.props.id ?? element.props.title}`;
|
92
|
-
|
93
|
-
if (process.env.NODE_ENV === 'development') {
|
94
|
-
invariant(Boolean(element.props.title), 'Component must include title prop');
|
95
|
-
invariant(!hasGroup, 'Setting menu must not mix groups and pages on one level');
|
96
|
-
invariant(!pages[pageId], `Setting menu page id must be uniq (${pageId})`);
|
97
|
-
}
|
98
|
-
|
99
|
-
pages[pageId] = getSettingsPageFromChildren(element.props.children);
|
100
|
-
menu.push({
|
101
|
-
pageId,
|
102
|
-
title: element.props.title,
|
103
|
-
icon: element.props.icon,
|
104
|
-
withBadge: pages[pageId].withBadge,
|
105
|
-
});
|
106
|
-
}
|
107
|
-
});
|
108
|
-
return {menu, pages};
|
109
|
-
}
|
110
|
-
|
111
|
-
function getSettingsPageFromChildren(children: React.ReactNode): SettingsPage {
|
112
|
-
const page: SettingsPage = {sections: []};
|
113
|
-
React.Children.forEach(children, (element) => {
|
114
|
-
if (!React.isValidElement(element)) {
|
115
|
-
// Ignore non-elements.
|
116
|
-
return;
|
117
|
-
}
|
118
|
-
if (element.type === React.Fragment) {
|
119
|
-
// Transparently support React.Fragment and its children.
|
120
|
-
const {sections, withBadge} = getSettingsPageFromChildren(element.props.children);
|
121
|
-
page.sections.push(...sections);
|
122
|
-
page.withBadge = withBadge || page.withBadge;
|
123
|
-
} else {
|
124
|
-
const {title, header} = element.props;
|
125
|
-
page.withBadge = element.props.withBadge || page.withBadge;
|
126
|
-
page.sections.push({
|
127
|
-
title,
|
128
|
-
header,
|
129
|
-
withBadge: element.props.withBadge,
|
130
|
-
items: getSettingsItemsFromChildren(element.props.children),
|
131
|
-
});
|
132
|
-
}
|
133
|
-
});
|
134
|
-
return page;
|
135
|
-
}
|
136
|
-
|
137
|
-
function getSettingsItemsFromChildren(children: React.ReactNode): SettingsItem[] {
|
138
|
-
const items: SettingsItem[] = [];
|
139
|
-
React.Children.forEach(children, (element) => {
|
140
|
-
if (!React.isValidElement(element)) {
|
141
|
-
// Ignore non-elements.
|
142
|
-
return;
|
143
|
-
}
|
144
|
-
if (element.type === React.Fragment) {
|
145
|
-
// Transparently support React.Fragment and its children.
|
146
|
-
items.push(...getSettingsItemsFromChildren(element.props.children));
|
147
|
-
} else {
|
148
|
-
items.push({
|
149
|
-
title: element.props.title,
|
150
|
-
renderTitleComponent: element.props.renderTitleComponent,
|
151
|
-
children: element,
|
152
|
-
});
|
153
|
-
}
|
154
|
-
});
|
155
|
-
return items;
|
156
|
-
}
|
@@ -1,38 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import {escapeStringForRegExp} from './helpers';
|
3
|
-
import {SettingsPage} from './collect-settings';
|
4
|
-
|
5
|
-
function identity(x: any) {
|
6
|
-
return x;
|
7
|
-
}
|
8
|
-
|
9
|
-
export function filterSettings(
|
10
|
-
pages: Record<string, SettingsPage>,
|
11
|
-
search = '',
|
12
|
-
wrapFoundTitle: (title: string, search: string) => React.ReactNode = identity,
|
13
|
-
) {
|
14
|
-
// 'abc def fg' -> abc.*?cde.*?fg
|
15
|
-
const preparedFilter = escapeStringForRegExp(search).replace(/\s+/g, '.*?');
|
16
|
-
const filterRe = new RegExp(preparedFilter, 'i');
|
17
|
-
for (const page of Object.values(pages)) {
|
18
|
-
let hidePage = true;
|
19
|
-
for (const section of page.sections) {
|
20
|
-
let hideSection = true;
|
21
|
-
for (const item of section.items) {
|
22
|
-
item.hide = Boolean(search) && !filterRe.test(item.title);
|
23
|
-
if (item.renderTitleComponent) {
|
24
|
-
item.titleComponent = item.renderTitleComponent(
|
25
|
-
search && !item.hide ? wrapFoundTitle(item.title, search) : null,
|
26
|
-
);
|
27
|
-
} else {
|
28
|
-
item.titleComponent =
|
29
|
-
search && !item.hide ? wrapFoundTitle(item.title, search) : item.title;
|
30
|
-
}
|
31
|
-
hideSection = hideSection && item.hide;
|
32
|
-
}
|
33
|
-
section.hide = hideSection;
|
34
|
-
hidePage = hidePage && hideSection;
|
35
|
-
}
|
36
|
-
page.hide = hidePage;
|
37
|
-
}
|
38
|
-
}
|
@@ -1,39 +0,0 @@
|
|
1
|
-
import * as React from 'react';
|
2
|
-
|
3
|
-
type AnyFunc = (...args: any[]) => any;
|
4
|
-
|
5
|
-
export function useStableCallback<T extends AnyFunc>(
|
6
|
-
func: T,
|
7
|
-
): (...args: Parameters<T>) => ReturnType<T> | undefined {
|
8
|
-
const funcRef = React.useRef<T>();
|
9
|
-
|
10
|
-
React.useEffect(() => {
|
11
|
-
funcRef.current = func;
|
12
|
-
return () => {
|
13
|
-
funcRef.current = undefined;
|
14
|
-
};
|
15
|
-
}, [func]);
|
16
|
-
|
17
|
-
return React.useCallback((...args: Parameters<T>) => {
|
18
|
-
if (typeof funcRef.current === 'function') {
|
19
|
-
return funcRef.current(...args);
|
20
|
-
}
|
21
|
-
return undefined;
|
22
|
-
}, []);
|
23
|
-
}
|
24
|
-
|
25
|
-
export function useCurrent<T>(value: T) {
|
26
|
-
const ref = React.useRef(value);
|
27
|
-
ref.current = value;
|
28
|
-
return React.useCallback(() => ref.current, []);
|
29
|
-
}
|
30
|
-
|
31
|
-
export function invariant(cond: boolean, message: string): void {
|
32
|
-
if (!cond) {
|
33
|
-
throw new Error(message);
|
34
|
-
}
|
35
|
-
}
|
36
|
-
|
37
|
-
export function escapeStringForRegExp(input: string) {
|
38
|
-
return input.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
39
|
-
}
|
@@ -1 +0,0 @@
|
|
1
|
-
export * from './Settings';
|
@@ -1,28 +0,0 @@
|
|
1
|
-
export const ASIDE_HEADER_COMPACT_WIDTH = 56;
|
2
|
-
export const ASIDE_HEADER_EXPANDED_WIDTH = 236;
|
3
|
-
export const ASIDE_HEADER_COLLAPSE_BUTTON_SIZE = 28;
|
4
|
-
|
5
|
-
export const ASIDE_HEADER_ICON_SIZE = 24;
|
6
|
-
|
7
|
-
export const ASIDE_HEADER_STORE_KEY = 'nvAsideHeader';
|
8
|
-
|
9
|
-
export enum AsideHeaderEvent {
|
10
|
-
SETTINGS_OPEN = 'SETTINGS_OPEN',
|
11
|
-
SETTINGS_CLOSE = 'SETTINGS_CLOSE',
|
12
|
-
}
|
13
|
-
|
14
|
-
export enum AsideHeaderVisibleItem {
|
15
|
-
Settings = 'settings',
|
16
|
-
}
|
17
|
-
|
18
|
-
export enum FooterItemIcon {
|
19
|
-
Bug = 'bug',
|
20
|
-
Support = 'support',
|
21
|
-
Settings = 'settings',
|
22
|
-
SettingsWithDot = 'settings-with-dot',
|
23
|
-
}
|
24
|
-
|
25
|
-
export enum FooterItemIconView {
|
26
|
-
Normal = 'normal',
|
27
|
-
WithDot = 'with-dot',
|
28
|
-
}
|
@@ -1,34 +0,0 @@
|
|
1
|
-
import {ASIDE_HEADER_STORE_KEY} from './constants';
|
2
|
-
import {AsideHeaderLocalStorage} from './types';
|
3
|
-
|
4
|
-
function store<T = unknown>(key: string, data: T) {
|
5
|
-
try {
|
6
|
-
window.localStorage.setItem(key, JSON.stringify(data));
|
7
|
-
} catch (err) {
|
8
|
-
console.error(`data not saved in localeStorage: ${err}`);
|
9
|
-
}
|
10
|
-
}
|
11
|
-
|
12
|
-
function restore<T = any>(key: string): T | null {
|
13
|
-
try {
|
14
|
-
const data = window.localStorage.getItem(key);
|
15
|
-
if (data === null) {
|
16
|
-
return null;
|
17
|
-
}
|
18
|
-
return JSON.parse(data) as T;
|
19
|
-
} catch (err) {
|
20
|
-
return null;
|
21
|
-
}
|
22
|
-
}
|
23
|
-
|
24
|
-
export function getLocalData() {
|
25
|
-
return restore<AsideHeaderLocalStorage>(ASIDE_HEADER_STORE_KEY);
|
26
|
-
}
|
27
|
-
|
28
|
-
export function setLocalData(data: Partial<AsideHeaderLocalStorage> | null) {
|
29
|
-
const storeData = getLocalData();
|
30
|
-
store<AsideHeaderLocalStorage>(ASIDE_HEADER_STORE_KEY, {
|
31
|
-
...storeData,
|
32
|
-
...data,
|
33
|
-
});
|
34
|
-
}
|
@@ -1,32 +0,0 @@
|
|
1
|
-
import {FooterItemIcon, FooterItemIconView} from './constants';
|
2
|
-
import {SlotName} from './AsideHeaderFooterSlot/AsideHeaderFooterSlot';
|
3
|
-
|
4
|
-
import bugIcon from '../../assets/icons/bug.svg';
|
5
|
-
import supportIcon from '../../assets/icons/support.svg';
|
6
|
-
import settingsIcon from '../../assets/icons/settings.svg';
|
7
|
-
import settingsWithDotIcon from '../../assets/icons/settings-with-dot.svg';
|
8
|
-
|
9
|
-
export const footerItemIconMap = {
|
10
|
-
[FooterItemIcon.Bug]: bugIcon,
|
11
|
-
[FooterItemIcon.Support]: supportIcon,
|
12
|
-
[FooterItemIcon.Settings]: settingsIcon,
|
13
|
-
[FooterItemIcon.SettingsWithDot]: settingsWithDotIcon,
|
14
|
-
};
|
15
|
-
|
16
|
-
export function getFooterItemIcon(slot: SlotName, view?: FooterItemIconView) {
|
17
|
-
switch (slot) {
|
18
|
-
case SlotName.BugReport:
|
19
|
-
return footerItemIconMap[FooterItemIcon.Bug];
|
20
|
-
case SlotName.Support:
|
21
|
-
return footerItemIconMap[FooterItemIcon.Support];
|
22
|
-
case SlotName.Settings:
|
23
|
-
switch (view) {
|
24
|
-
case FooterItemIconView.WithDot:
|
25
|
-
return footerItemIconMap[FooterItemIcon.SettingsWithDot];
|
26
|
-
default:
|
27
|
-
return footerItemIconMap[FooterItemIcon.Settings];
|
28
|
-
}
|
29
|
-
default:
|
30
|
-
return undefined;
|
31
|
-
}
|
32
|
-
}
|
@@ -1,23 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
|
3
|
-
export interface AsideHeaderMenuItem {
|
4
|
-
id: string;
|
5
|
-
title: string;
|
6
|
-
tooltipText?: string;
|
7
|
-
icon?: SVGIconData;
|
8
|
-
iconSize?: number | string;
|
9
|
-
link?: string;
|
10
|
-
current?: boolean;
|
11
|
-
pinned?: boolean;
|
12
|
-
onItemClick?: (item: AsideHeaderMenuItem, isCollapsed: boolean) => void;
|
13
|
-
itemWrapper?: (
|
14
|
-
node: React.ReactNode,
|
15
|
-
item: AsideHeaderMenuItem,
|
16
|
-
isCollapsed: boolean,
|
17
|
-
isCompact: boolean,
|
18
|
-
) => React.ReactNode;
|
19
|
-
}
|
20
|
-
|
21
|
-
export interface AsideHeaderLocalStorage {
|
22
|
-
isCompact?: boolean;
|
23
|
-
}
|
@@ -1,58 +0,0 @@
|
|
1
|
-
import React from 'react';
|
2
|
-
import cn from 'bem-cn-lite';
|
3
|
-
import PropTypes from 'prop-types';
|
4
|
-
import {Link} from 'react-router-dom';
|
5
|
-
|
6
|
-
import {getTabletLabel} from '../../utils/constants';
|
7
|
-
import routes, {createHref} from '../../routes';
|
8
|
-
|
9
|
-
import './TabletsStatistic.scss';
|
10
|
-
|
11
|
-
const b = cn('tablets-statistic');
|
12
|
-
|
13
|
-
const prepareTablets = (tablets) => {
|
14
|
-
const res = tablets.map((tablet) => {
|
15
|
-
return {
|
16
|
-
label: getTabletLabel(tablet.Type),
|
17
|
-
type: tablet.Type,
|
18
|
-
count: tablet.Count,
|
19
|
-
state: tablet.State,
|
20
|
-
};
|
21
|
-
});
|
22
|
-
|
23
|
-
return res.sort((a, b) => a.label.localeCompare(b.label));
|
24
|
-
};
|
25
|
-
|
26
|
-
class TabletStatistic extends React.Component {
|
27
|
-
static propTypes = {
|
28
|
-
tablets: PropTypes.array,
|
29
|
-
path: PropTypes.string,
|
30
|
-
nodeIds: PropTypes.array,
|
31
|
-
};
|
32
|
-
renderTabletInfo = (item, index) => {
|
33
|
-
const {path, nodeIds} = this.props;
|
34
|
-
|
35
|
-
return (
|
36
|
-
<Link
|
37
|
-
to={createHref(routes.tabletsFilters, null, {
|
38
|
-
nodeIds,
|
39
|
-
state: item.state,
|
40
|
-
type: item.type,
|
41
|
-
path,
|
42
|
-
})}
|
43
|
-
key={index}
|
44
|
-
className={b('tablet', {state: item.state?.toLowerCase()})}
|
45
|
-
>
|
46
|
-
{item.label}: {item.count}
|
47
|
-
</Link>
|
48
|
-
);
|
49
|
-
};
|
50
|
-
render() {
|
51
|
-
const {tablets = []} = this.props;
|
52
|
-
const preparedTablets = prepareTablets(tablets);
|
53
|
-
|
54
|
-
return <div className={b()}>{preparedTablets.map(this.renderTabletInfo)}</div>;
|
55
|
-
}
|
56
|
-
}
|
57
|
-
|
58
|
-
export default TabletStatistic;
|