import type {TBlock} from '@gravity-ui/graph'; import type {ExplainPlanNodeData} from '../../store/reducers/query/types'; import type {AbstractGraphColorsConfig} from './colorsConfig'; import type {Data, EdgeResult, TreeConnection} from './types'; const BLOCK_TOP_PADDING = 8; const BLOCK_LINE_HEIGHT = 16; const BORDER_HEIGHT = 2; const getBlockSize = (block: ExplainPlanNodeData) => { const ONE_LINE_HEIGHT = BLOCK_TOP_PADDING * 2 + BLOCK_LINE_HEIGHT + BORDER_HEIGHT; const operatorsLength = Math.max(block.operators?.length ?? 0, 1); const tablesLength = block.tables?.length ?? 0; switch (block.type) { case 'query': return { width: 40, height: 40, }; case 'result': return { width: 112, height: ONE_LINE_HEIGHT, }; case 'stage': return { width: 248, height: BORDER_HEIGHT + BLOCK_TOP_PADDING * 2 + (operatorsLength + tablesLength) * BLOCK_LINE_HEIGHT, }; case 'connection': return { width: 122, height: ONE_LINE_HEIGHT, }; case 'materialize': return { width: 190, height: ONE_LINE_HEIGHT, }; default: return { width: 100, height: ONE_LINE_HEIGHT, }; } }; export const prepareBlocks = (nodes: Data['nodes']): TBlock[] => { return nodes.map(({name: id, data: {id: planNodeId, name, type, ...rest}, data}) => ({ id, is: type, name, planNodeId, ...getBlockSize(data), ...rest, })); }; export const prepareConnections = (links: Data['links']): TreeConnection[] => { return links.map(({from, to}) => ({ id: `${from}:${to}`, sourceBlockId: from, targetBlockId: to, })); }; function calculateCurrentCustomPropertyValue( colorSettings: Partial>, computedStyle: CSSStyleDeclaration, ): Partial> { const result: Partial> = {}; for (const nestedKey in colorSettings) { if (Object.prototype.hasOwnProperty.call(colorSettings, nestedKey)) { const value = colorSettings[nestedKey]; if (value !== undefined) { result[nestedKey] = value?.startsWith('var(') ? computedStyle.getPropertyValue(value.substring(4, value.length - 1)).trim() : value; } } } return result; } export function parseCustomPropertyValue( colors: T, block: HTMLElement = globalThis.document.body, ): T { const parsed: AbstractGraphColorsConfig = {}; const computedStyle = window.getComputedStyle(block); for (const topKey in colors) { if (Object.prototype.hasOwnProperty.call(colors, topKey)) { const nestedObj = colors[topKey]; if (nestedObj) { parsed[topKey] = calculateCurrentCustomPropertyValue(nestedObj, computedStyle); } } } return parsed as T; } // Identifies the geometry the camera is aimed at: block placement and the edges between them. export function getLayoutSignature(blocks: TBlock[], edges: EdgeResult[]) { return JSON.stringify([ blocks.map(({id, x, y, width, height}) => [id, x, y, width, height]), edges.map(({sourceBlockId, targetBlockId}) => [sourceBlockId, targetBlockId]), ]); }