ydb-embedded-ui 20.4.0 → 21.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +41 -0
- package/dist/components/AutoRefreshControl/i18n/index.d.ts +1 -1
- package/dist/components/FullNodeViewer/i18n/index.d.ts +1 -1
- package/dist/components/Graph/BlockComponents/ConnectionBlockComponent.d.ts +7 -0
- package/dist/components/Graph/BlockComponents/ConnectionBlockComponent.js +30 -0
- package/dist/components/Graph/BlockComponents/ConnectionBlockComponent.js.map +1 -0
- package/dist/components/Graph/BlockComponents/QueryBlockComponent.d.ts +5 -0
- package/dist/components/Graph/BlockComponents/QueryBlockComponent.js +5 -0
- package/dist/components/Graph/BlockComponents/QueryBlockComponent.js.map +1 -0
- package/dist/components/Graph/BlockComponents/ResultBlockComponent.d.ts +7 -0
- package/dist/components/Graph/BlockComponents/ResultBlockComponent.js +5 -0
- package/dist/components/Graph/BlockComponents/ResultBlockComponent.js.map +1 -0
- package/dist/components/Graph/BlockComponents/StageBlockComponent.d.ts +7 -0
- package/dist/components/Graph/BlockComponents/StageBlockComponent.js +17 -0
- package/dist/components/Graph/BlockComponents/StageBlockComponent.js.map +1 -0
- package/dist/components/Graph/FullViewportBackground.d.ts +2 -0
- package/dist/components/Graph/FullViewportBackground.js +67 -0
- package/dist/components/Graph/FullViewportBackground.js.map +1 -0
- package/dist/components/Graph/GraphControls.d.ts +6 -0
- package/dist/components/Graph/GraphControls.js +24 -0
- package/dist/components/Graph/GraphControls.js.map +1 -0
- package/dist/components/Graph/GravityGraph.d.ts +9 -0
- package/dist/components/Graph/GravityGraph.js +122 -0
- package/dist/components/Graph/GravityGraph.js.map +1 -0
- package/dist/components/Graph/GravityGraph.scss +131 -0
- package/dist/components/Graph/NonSelectableConnection.d.ts +9 -0
- package/dist/components/Graph/NonSelectableConnection.js +16 -0
- package/dist/components/Graph/NonSelectableConnection.js.map +1 -0
- package/dist/components/Graph/TooltipComponent.d.ts +8 -0
- package/dist/components/Graph/TooltipComponent.js +63 -0
- package/dist/components/Graph/TooltipComponent.js.map +1 -0
- package/dist/components/Graph/colorsConfig.d.ts +19 -0
- package/dist/components/Graph/colorsConfig.js +20 -0
- package/dist/components/Graph/colorsConfig.js.map +1 -0
- package/dist/components/Graph/i18n/en.json +5 -0
- package/dist/components/Graph/i18n/index.d.ts +2 -0
- package/dist/components/Graph/i18n/index.js +5 -0
- package/dist/components/Graph/i18n/index.js.map +1 -0
- package/dist/components/Graph/runTreeLayout.d.ts +12 -0
- package/dist/components/Graph/runTreeLayout.js +75 -0
- package/dist/components/Graph/runTreeLayout.js.map +1 -0
- package/dist/components/Graph/treeLayout.d.ts +2 -0
- package/dist/components/Graph/treeLayout.js +284 -0
- package/dist/components/Graph/treeLayout.js.map +1 -0
- package/dist/components/Graph/types.d.ts +52 -0
- package/dist/components/Graph/types.js +2 -0
- package/dist/components/Graph/types.js.map +1 -0
- package/dist/components/Graph/utils.d.ts +6 -0
- package/dist/components/Graph/utils.js +88 -0
- package/dist/components/Graph/utils.js.map +1 -0
- package/dist/components/InfoViewer/formatters/pqGroup.d.ts +5 -1
- package/dist/components/InfoViewer/formatters/pqGroup.js +24 -2
- package/dist/components/InfoViewer/formatters/pqGroup.js.map +1 -1
- package/dist/components/InfoViewer/i18n/en.json +8 -1
- package/dist/components/InfoViewer/i18n/index.d.ts +1 -1
- package/dist/components/PDiskInfo/i18n/index.d.ts +1 -1
- package/dist/components/VDiskInfo/i18n/index.d.ts +1 -1
- package/dist/containers/App/Content.js +9 -2
- package/dist/containers/App/Content.js.map +1 -1
- package/dist/containers/App/clusterRouteGuard.d.ts +5 -0
- package/dist/containers/App/clusterRouteGuard.js +13 -0
- package/dist/containers/App/clusterRouteGuard.js.map +1 -0
- package/dist/containers/Clusters/constants.d.ts +1 -1
- package/dist/containers/Nodes/i18n/index.d.ts +1 -1
- package/dist/containers/Operations/constants.d.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TableInfo/i18n/index.d.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/TransferInfo/Credentials.d.ts +1 -1
- package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.js +17 -3
- package/dist/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.js.map +1 -1
- package/dist/containers/Tenant/Diagnostics/TenantOverview/TenantStorage/useTenantStorageNewData.d.ts +2 -2
- package/dist/containers/Tenant/Query/QueryResult/components/Graph/Graph.js +9 -4
- package/dist/containers/Tenant/Query/QueryResult/components/Graph/Graph.js.map +1 -1
- package/dist/containers/Tenant/Query/QueryResult/components/Graph/Graph.scss +2 -0
- package/dist/containers/Tenant/TenantNavigation/useTenantNavigation.d.ts +1 -1
- package/dist/graphLayout.worker.d.ts +1 -0
- package/dist/graphLayout.worker.js +17 -0
- package/dist/graphLayout.worker.js.map +1 -0
- package/dist/lib.d.ts +1 -1
- package/dist/lib.js.map +1 -1
- package/dist/store/reducers/query/parsers/preparePlanData.js.map +1 -1
- package/dist/store/reducers/query/types.d.ts +38 -1
- package/dist/store/reducers/settings/utils.d.ts +2 -2
- package/dist/types/api/schema/persQueueGroup.d.ts +16 -0
- package/dist/types/api/schema/persQueueGroup.js +7 -0
- package/dist/types/api/schema/persQueueGroup.js.map +1 -1
- package/dist/uiFactory/types.d.ts +3 -0
- package/dist/utils/disks/helpers.d.ts +2 -2
- package/dist/utils/hooks/useChangedQuerySettings.d.ts +2 -2
- package/dist/utils/hooks/useLastQueryExecutionSettings.d.ts +2 -2
- package/dist/utils/hooks/useQueryExecutionSettings.d.ts +2 -2
- package/dist/utils/prepareQueryExplain.d.ts +1 -2
- package/dist/utils/prepareQueryExplain.js +10 -2
- package/dist/utils/prepareQueryExplain.js.map +1 -1
- package/dist/utils/query.d.ts +1 -1
- package/package.json +3 -3
- package/src/components/Graph/BlockComponents/ConnectionBlockComponent.tsx +49 -0
- package/src/components/Graph/BlockComponents/QueryBlockComponent.tsx +7 -0
- package/src/components/Graph/BlockComponents/ResultBlockComponent.tsx +10 -0
- package/src/components/Graph/BlockComponents/StageBlockComponent.tsx +35 -0
- package/src/components/Graph/FullViewportBackground.ts +122 -0
- package/src/components/Graph/GraphControls.tsx +49 -0
- package/src/components/Graph/GravityGraph.scss +131 -0
- package/src/components/Graph/GravityGraph.tsx +174 -0
- package/src/components/Graph/NonSelectableConnection.tsx +14 -0
- package/src/components/Graph/TooltipComponent.tsx +114 -0
- package/src/components/Graph/colorsConfig.ts +21 -0
- package/src/components/Graph/i18n/en.json +5 -0
- package/src/components/Graph/i18n/index.ts +7 -0
- package/src/components/Graph/runTreeLayout.ts +105 -0
- package/src/components/Graph/treeLayout.ts +378 -0
- package/src/components/Graph/types.ts +56 -0
- package/src/components/Graph/utils.ts +112 -0
- package/src/components/InfoViewer/formatters/pqGroup.ts +30 -2
- package/src/components/InfoViewer/i18n/en.json +8 -1
- package/src/containers/App/Content.tsx +9 -1
- package/src/containers/App/clusterRouteGuard.ts +24 -0
- package/src/containers/Tenant/Diagnostics/Overview/utils/prepareTopicSchemaInfo.ts +23 -2
- package/src/containers/Tenant/Query/QueryResult/components/Graph/Graph.scss +2 -0
- package/src/containers/Tenant/Query/QueryResult/components/Graph/Graph.tsx +13 -7
- package/src/graphLayout.worker.ts +21 -0
- package/src/lib.ts +1 -0
- package/src/store/reducers/query/parsers/preparePlanData.ts +1 -3
- package/src/store/reducers/query/types.ts +42 -2
- package/src/types/api/schema/persQueueGroup.ts +19 -0
- package/src/uiFactory/types.ts +4 -0
- package/src/utils/prepareQueryExplain.ts +13 -5
- package/dist/components/Graph/Graph.d.ts +0 -12
- package/dist/components/Graph/Graph.js +0 -44
- package/dist/components/Graph/Graph.js.map +0 -1
- package/dist/components/Graph/colors.d.ts +0 -10
- package/dist/components/Graph/colors.js +0 -53
- package/dist/components/Graph/colors.js.map +0 -1
- package/src/components/Graph/Graph.tsx +0 -70
- package/src/components/Graph/colors.ts +0 -75
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {GraphState} from '@gravity-ui/graph';
|
|
4
|
+
import type {TBlockId} from '@gravity-ui/graph';
|
|
5
|
+
import type {HookGraphParams} from '@gravity-ui/graph/react';
|
|
6
|
+
import {GraphBlock, GraphCanvas, useGraph, useGraphEvent} from '@gravity-ui/graph/react';
|
|
7
|
+
|
|
8
|
+
import {uiFactory} from '../../uiFactory/uiFactory';
|
|
9
|
+
import {cn} from '../../utils/cn';
|
|
10
|
+
|
|
11
|
+
const b = cn('ydb-gravity-graph');
|
|
12
|
+
const GRAPH_FIT_PADDING = 40;
|
|
13
|
+
|
|
14
|
+
import {ConnectionBlockComponent} from './BlockComponents/ConnectionBlockComponent';
|
|
15
|
+
import {QueryBlockComponent} from './BlockComponents/QueryBlockComponent';
|
|
16
|
+
import {ResultBlockComponent} from './BlockComponents/ResultBlockComponent';
|
|
17
|
+
import {StageBlockComponent} from './BlockComponents/StageBlockComponent';
|
|
18
|
+
import {FullViewportBackground} from './FullViewportBackground';
|
|
19
|
+
import {GraphControls} from './GraphControls';
|
|
20
|
+
import {NonSelectableConnection} from './NonSelectableConnection';
|
|
21
|
+
import {graphColorsConfig} from './colorsConfig';
|
|
22
|
+
import {runTreeLayout} from './runTreeLayout';
|
|
23
|
+
import type {Data} from './types';
|
|
24
|
+
import {parseCustomPropertyValue} from './utils';
|
|
25
|
+
|
|
26
|
+
import './GravityGraph.scss';
|
|
27
|
+
|
|
28
|
+
interface Props<T> {
|
|
29
|
+
data: Data<T>;
|
|
30
|
+
onError: (error: Error) => void;
|
|
31
|
+
theme?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
const config: HookGraphParams = {
|
|
35
|
+
settings: {
|
|
36
|
+
background: FullViewportBackground,
|
|
37
|
+
connection: NonSelectableConnection,
|
|
38
|
+
showConnectionArrows: false,
|
|
39
|
+
},
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const createGraphLayoutWorker = () => {
|
|
43
|
+
if (uiFactory.createGraphLayoutWorker) {
|
|
44
|
+
return uiFactory.createGraphLayoutWorker();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
return new Worker(new URL('../../graphLayout.worker', import.meta.url));
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
const renderBlockFn = (graph: any, block: any) => {
|
|
51
|
+
const map: Record<string, React.ComponentType<any>> = {
|
|
52
|
+
query: QueryBlockComponent,
|
|
53
|
+
result: ResultBlockComponent,
|
|
54
|
+
stage: StageBlockComponent,
|
|
55
|
+
connection: ConnectionBlockComponent,
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const Component = map[block.is as keyof typeof map];
|
|
59
|
+
|
|
60
|
+
return (
|
|
61
|
+
<GraphBlock graph={graph} block={block} className={b('block')}>
|
|
62
|
+
{Component ? (
|
|
63
|
+
<React.Fragment>
|
|
64
|
+
<Component
|
|
65
|
+
graph={graph}
|
|
66
|
+
block={block}
|
|
67
|
+
className={b('block-content', block.is)}
|
|
68
|
+
/>
|
|
69
|
+
{block.planNodeId !== undefined && block.is !== 'result' && (
|
|
70
|
+
<div className={b('block-id')}>#{block.planNodeId}</div>
|
|
71
|
+
)}
|
|
72
|
+
</React.Fragment>
|
|
73
|
+
) : (
|
|
74
|
+
block.id
|
|
75
|
+
)}
|
|
76
|
+
</GraphBlock>
|
|
77
|
+
);
|
|
78
|
+
};
|
|
79
|
+
|
|
80
|
+
export function GravityGraph<T>({data, onError, theme}: Props<T>) {
|
|
81
|
+
const {graph, start} = useGraph(config);
|
|
82
|
+
const cameraSizeRef = React.useRef({width: 0, height: 0});
|
|
83
|
+
const graphBlockIdsRef = React.useRef<TBlockId[]>([]);
|
|
84
|
+
const fitFrameRef = React.useRef<number>();
|
|
85
|
+
const fitGraphToViewport = React.useCallback(() => {
|
|
86
|
+
const {width, height} = graph.cameraService.getCameraState();
|
|
87
|
+
|
|
88
|
+
if (
|
|
89
|
+
graph.state !== GraphState.READY ||
|
|
90
|
+
width <= 0 ||
|
|
91
|
+
height <= 0 ||
|
|
92
|
+
graphBlockIdsRef.current.length === 0
|
|
93
|
+
) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
graph.zoomTo(graphBlockIdsRef.current, {padding: GRAPH_FIT_PADDING});
|
|
98
|
+
}, [graph]);
|
|
99
|
+
const scheduleGraphFit = React.useCallback(() => {
|
|
100
|
+
if (fitFrameRef.current !== undefined) {
|
|
101
|
+
globalThis.cancelAnimationFrame(fitFrameRef.current);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// camera-change is emitted before its new size is committed.
|
|
105
|
+
fitFrameRef.current = globalThis.requestAnimationFrame(() => {
|
|
106
|
+
fitFrameRef.current = undefined;
|
|
107
|
+
fitGraphToViewport();
|
|
108
|
+
});
|
|
109
|
+
}, [fitGraphToViewport]);
|
|
110
|
+
|
|
111
|
+
React.useEffect(() => {
|
|
112
|
+
return () => {
|
|
113
|
+
if (fitFrameRef.current !== undefined) {
|
|
114
|
+
globalThis.cancelAnimationFrame(fitFrameRef.current);
|
|
115
|
+
}
|
|
116
|
+
};
|
|
117
|
+
}, []);
|
|
118
|
+
|
|
119
|
+
React.useEffect(() => {
|
|
120
|
+
return runTreeLayout({
|
|
121
|
+
request: {
|
|
122
|
+
nodes: data.nodes,
|
|
123
|
+
links: data.links,
|
|
124
|
+
},
|
|
125
|
+
createWorker: createGraphLayoutWorker,
|
|
126
|
+
onResult: ({layout, edges}) => {
|
|
127
|
+
graphBlockIdsRef.current = layout.map(({id}) => id);
|
|
128
|
+
graph.setEntities({
|
|
129
|
+
blocks: layout,
|
|
130
|
+
connections: edges,
|
|
131
|
+
});
|
|
132
|
+
scheduleGraphFit();
|
|
133
|
+
},
|
|
134
|
+
onError,
|
|
135
|
+
});
|
|
136
|
+
}, [data.nodes, data.links, graph, onError, scheduleGraphFit]);
|
|
137
|
+
|
|
138
|
+
React.useEffect(() => {
|
|
139
|
+
graph.setColors(parseCustomPropertyValue(graphColorsConfig));
|
|
140
|
+
}, [graph, theme]);
|
|
141
|
+
|
|
142
|
+
useGraphEvent(graph, 'state-change', ({state}) => {
|
|
143
|
+
if (state === GraphState.ATTACHED) {
|
|
144
|
+
graph.cameraService.set({
|
|
145
|
+
scale: 1,
|
|
146
|
+
scaleMax: 1.5,
|
|
147
|
+
});
|
|
148
|
+
graph.setConstants({
|
|
149
|
+
block: {
|
|
150
|
+
SCALES: [0.125, 0.225, 0.5], // Detailed view stays until zoom = 0.5
|
|
151
|
+
},
|
|
152
|
+
});
|
|
153
|
+
start();
|
|
154
|
+
} else if (state === GraphState.READY) {
|
|
155
|
+
scheduleGraphFit();
|
|
156
|
+
}
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
useGraphEvent(graph, 'camera-change', ({width, height}) => {
|
|
160
|
+
const previousSize = cameraSizeRef.current;
|
|
161
|
+
cameraSizeRef.current = {width, height};
|
|
162
|
+
|
|
163
|
+
if (previousSize.width !== width || previousSize.height !== height) {
|
|
164
|
+
scheduleGraphFit();
|
|
165
|
+
}
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<React.Fragment>
|
|
170
|
+
<GraphCanvas graph={graph} renderBlock={renderBlockFn} />
|
|
171
|
+
<GraphControls graph={graph} />
|
|
172
|
+
</React.Fragment>
|
|
173
|
+
);
|
|
174
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import {MultipointConnection} from '@gravity-ui/graph/react';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Кастомный класс соединения, который отключает визуальное выделение
|
|
5
|
+
* Наследуется от MultipointConnection и переопределяет поведение
|
|
6
|
+
*/
|
|
7
|
+
export class NonSelectableConnection extends MultipointConnection {
|
|
8
|
+
override cursor: 'pointer' = 'pointer';
|
|
9
|
+
|
|
10
|
+
// Переопределяем метод для предотвращения выделения при клике
|
|
11
|
+
protected override handleEvent(event: Event) {
|
|
12
|
+
event.stopPropagation();
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
|
|
3
|
+
import {Popover, Tab, TabList, TabPanel, TabProvider} from '@gravity-ui/uikit';
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
TopologyNodeDataStatsItem,
|
|
7
|
+
TopologyNodeDataStatsSection,
|
|
8
|
+
} from '../../store/reducers/query/types';
|
|
9
|
+
import {cn} from '../../utils/cn';
|
|
10
|
+
|
|
11
|
+
import type {ExtendedTBlock} from './types';
|
|
12
|
+
|
|
13
|
+
const b = cn('ydb-gravity-graph');
|
|
14
|
+
type Props = {
|
|
15
|
+
block: ExtendedTBlock;
|
|
16
|
+
children: React.ReactElement<React.HTMLAttributes<HTMLElement>>;
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const getStatsContent = (stat: TopologyNodeDataStatsItem | TopologyNodeDataStatsSection) => {
|
|
20
|
+
if ('value' in stat) {
|
|
21
|
+
return (
|
|
22
|
+
<p className={b('tooltip-stat-row')} key={stat.name}>
|
|
23
|
+
<span>{stat.name}:</span>
|
|
24
|
+
<span>{stat.value}</span>
|
|
25
|
+
</p>
|
|
26
|
+
);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<section className={b('tooltip-stat-group')} key={stat.name}>
|
|
31
|
+
<div className={b('tooltip-stat-group-name')}>{stat.name}:</div>
|
|
32
|
+
{stat.items?.map(({name, value}: TopologyNodeDataStatsItem) => (
|
|
33
|
+
<p className={b('tooltip-stat-row')} key={name}>
|
|
34
|
+
<span>{name}:</span>
|
|
35
|
+
<span>{value}</span>
|
|
36
|
+
</p>
|
|
37
|
+
))}
|
|
38
|
+
</section>
|
|
39
|
+
);
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const useTooltipContent = (block: ExtendedTBlock) => {
|
|
43
|
+
const firstTab = block?.stats?.[0]?.group || '';
|
|
44
|
+
const [activeTab, setActiveTab] = React.useState(firstTab);
|
|
45
|
+
const validatedActiveTab = block.stats?.some(({group}) => group === activeTab)
|
|
46
|
+
? activeTab
|
|
47
|
+
: firstTab;
|
|
48
|
+
|
|
49
|
+
React.useEffect(() => {
|
|
50
|
+
if (activeTab !== validatedActiveTab) {
|
|
51
|
+
setActiveTab(validatedActiveTab);
|
|
52
|
+
}
|
|
53
|
+
}, [activeTab, validatedActiveTab]);
|
|
54
|
+
|
|
55
|
+
return React.useMemo(
|
|
56
|
+
() => (
|
|
57
|
+
<TabProvider value={validatedActiveTab} onUpdate={setActiveTab}>
|
|
58
|
+
<TabList className={b('tooltip-tabs')}>
|
|
59
|
+
{block?.stats?.map((item) => (
|
|
60
|
+
<Tab value={item.group} key={item.group}>
|
|
61
|
+
{item.group}
|
|
62
|
+
</Tab>
|
|
63
|
+
))}
|
|
64
|
+
</TabList>
|
|
65
|
+
{block?.stats?.map((item) => (
|
|
66
|
+
<TabPanel value={item.group} key={item.group}>
|
|
67
|
+
{item.stats?.map(getStatsContent)}
|
|
68
|
+
</TabPanel>
|
|
69
|
+
))}
|
|
70
|
+
</TabProvider>
|
|
71
|
+
),
|
|
72
|
+
[block?.stats, validatedActiveTab],
|
|
73
|
+
);
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
export const TooltipComponent = ({block, children}: Props) => {
|
|
77
|
+
const content = useTooltipContent(block);
|
|
78
|
+
const trigger = React.cloneElement(children, {
|
|
79
|
+
role: children.props.role ?? 'button',
|
|
80
|
+
tabIndex: children.props.tabIndex ?? 0,
|
|
81
|
+
onKeyDown: (event: React.KeyboardEvent<HTMLElement>) => {
|
|
82
|
+
children.props.onKeyDown?.(event);
|
|
83
|
+
if (event.defaultPrevented) {
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
if (event.key === 'Enter') {
|
|
88
|
+
event.preventDefault();
|
|
89
|
+
event.currentTarget.click();
|
|
90
|
+
} else if (event.key === ' ') {
|
|
91
|
+
event.preventDefault();
|
|
92
|
+
}
|
|
93
|
+
},
|
|
94
|
+
onKeyUp: (event: React.KeyboardEvent<HTMLElement>) => {
|
|
95
|
+
children.props.onKeyUp?.(event);
|
|
96
|
+
if (!event.defaultPrevented && event.key === ' ') {
|
|
97
|
+
event.currentTarget.click();
|
|
98
|
+
}
|
|
99
|
+
},
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
return (
|
|
103
|
+
<Popover
|
|
104
|
+
content={content}
|
|
105
|
+
hasArrow
|
|
106
|
+
trigger="click"
|
|
107
|
+
placement="right-start"
|
|
108
|
+
className="ydb-gravity-graph__tooltip-content"
|
|
109
|
+
strategy="fixed"
|
|
110
|
+
>
|
|
111
|
+
{trigger}
|
|
112
|
+
</Popover>
|
|
113
|
+
);
|
|
114
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
export type AbstractGraphColorsConfig = Partial<Record<string, Partial<Record<string, string>>>>;
|
|
2
|
+
|
|
3
|
+
export const graphColorsConfig = {
|
|
4
|
+
// Default @gravity-ui/graph colors
|
|
5
|
+
canvas: {
|
|
6
|
+
belowLayerBackground: '#0000',
|
|
7
|
+
border: '#0000',
|
|
8
|
+
dots: 'var(--g-color-line-generic)',
|
|
9
|
+
layerBackground: 'var(--g-color-base-background)',
|
|
10
|
+
},
|
|
11
|
+
block: {
|
|
12
|
+
text: 'var(--g-color-text-primary)',
|
|
13
|
+
background: 'var(--g-color-base-float)',
|
|
14
|
+
border: 'var(--g-color-line-generic-solid)',
|
|
15
|
+
},
|
|
16
|
+
connection: {
|
|
17
|
+
background: 'var(--g-color-line-generic-solid)',
|
|
18
|
+
hoverBackground: 'var(--g-color-line-generic-solid)',
|
|
19
|
+
selectedBackground: 'var(--g-color-line-generic-solid)',
|
|
20
|
+
},
|
|
21
|
+
} as const satisfies AbstractGraphColorsConfig;
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
import {prepareTreeLayout} from './treeLayout';
|
|
2
|
+
import type {TreeLayoutRequest, TreeLayoutResult, TreeLayoutWorkerResponse} from './types';
|
|
3
|
+
|
|
4
|
+
type Schedule = (callback: () => void) => () => void;
|
|
5
|
+
|
|
6
|
+
interface RunTreeLayoutOptions {
|
|
7
|
+
request: TreeLayoutRequest;
|
|
8
|
+
createWorker: () => Worker;
|
|
9
|
+
calculateLayout?: () => TreeLayoutResult;
|
|
10
|
+
onResult: (result: TreeLayoutResult) => void;
|
|
11
|
+
onError: (error: Error) => void;
|
|
12
|
+
schedule?: Schedule;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
const scheduleOnMainThread: Schedule = (callback) => {
|
|
16
|
+
const timeoutId = globalThis.setTimeout(callback, 0);
|
|
17
|
+
|
|
18
|
+
return () => globalThis.clearTimeout(timeoutId);
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
const normalizeError = (error: unknown) => {
|
|
22
|
+
return error instanceof Error ? error : new Error(String(error));
|
|
23
|
+
};
|
|
24
|
+
|
|
25
|
+
export function runTreeLayout({
|
|
26
|
+
request,
|
|
27
|
+
createWorker,
|
|
28
|
+
calculateLayout = () => prepareTreeLayout(request),
|
|
29
|
+
onResult,
|
|
30
|
+
onError,
|
|
31
|
+
schedule = scheduleOnMainThread,
|
|
32
|
+
}: RunTreeLayoutOptions) {
|
|
33
|
+
let worker: Worker | undefined;
|
|
34
|
+
let cancelFallback: (() => void) | undefined;
|
|
35
|
+
let cancelled = false;
|
|
36
|
+
let settled = false;
|
|
37
|
+
|
|
38
|
+
const terminateWorker = () => {
|
|
39
|
+
worker?.terminate();
|
|
40
|
+
worker = undefined;
|
|
41
|
+
};
|
|
42
|
+
|
|
43
|
+
const reportError = (error: unknown) => {
|
|
44
|
+
if (cancelled || settled) {
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
settled = true;
|
|
49
|
+
terminateWorker();
|
|
50
|
+
onError(normalizeError(error));
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
const complete = (result: TreeLayoutResult) => {
|
|
54
|
+
if (cancelled || settled) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
settled = true;
|
|
59
|
+
terminateWorker();
|
|
60
|
+
onResult(result);
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const scheduleFallback = () => {
|
|
64
|
+
if (cancelled || settled || cancelFallback) {
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
terminateWorker();
|
|
69
|
+
cancelFallback = schedule(() => {
|
|
70
|
+
cancelFallback = undefined;
|
|
71
|
+
|
|
72
|
+
if (cancelled || settled) {
|
|
73
|
+
return;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
try {
|
|
77
|
+
complete(calculateLayout());
|
|
78
|
+
} catch (error) {
|
|
79
|
+
reportError(error);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
try {
|
|
85
|
+
worker = createWorker();
|
|
86
|
+
worker.onmessage = ({data}: MessageEvent<TreeLayoutWorkerResponse>) => {
|
|
87
|
+
if (data.type === 'success') {
|
|
88
|
+
complete(data.result);
|
|
89
|
+
} else {
|
|
90
|
+
reportError(new Error(data.error));
|
|
91
|
+
}
|
|
92
|
+
};
|
|
93
|
+
worker.onerror = scheduleFallback;
|
|
94
|
+
worker.onmessageerror = scheduleFallback;
|
|
95
|
+
worker.postMessage(request);
|
|
96
|
+
} catch {
|
|
97
|
+
scheduleFallback();
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return () => {
|
|
101
|
+
cancelled = true;
|
|
102
|
+
cancelFallback?.();
|
|
103
|
+
terminateWorker();
|
|
104
|
+
};
|
|
105
|
+
}
|