Resolver/nodedesign 25 (#60630)
* PR base * adds designed resolver nodes * adjust distance between nodes * WIP remove stroke * WIP changes to meet mocks * new boxes * remove animation * new box assets * baby resolver running nodes complete * cleanup defs, add running trigger cube * added 2 more defs for process cubes * adding switched for assets on node component * vacuuming defs file * adjusting types and references to new event model * switch background to full shade for contrast * switch background to full shade for contrast * cube, animation and a11y changes to 25% nodes * PR base * adds designed resolver nodes * adjust distance between nodes * WIP remove stroke * WIP changes to meet mocks * new boxes * remove animation * new box assets * baby resolver running nodes complete * cleanup defs, add running trigger cube * added 2 more defs for process cubes * adding switched for assets on node component * vacuuming defs file * adjusting types and references to new event model * switch background to full shade for contrast * cube, animation and a11y changes to 25% nodes * merge upstream * change from Legacy to new Resolver event * cleaning up unused styles * fix adjacency map issues * fix process type to cube mapping * fix typing on selctor * set viewport to strict * remove unused types * fixes ci / testing issues * feedback from Jon Buttner * fix index from Jon Buttner comment * reset focus state on nodes * Robert review: changing adjacency map property names for better semantics * Robert Austin review: changing var name * Robert Austin review: rearrange code for readability * Robert Austin review: change const name * Robert Austin review: rearranging code for readability * Robert Austin review: adjustments to process_event_dot * Robert Austin review: replace level getter * Robert Austin review: removing unnecessary casting * Robert Austin review: adjust selector * Robert Austin review: fix setting parent map * Robert Austin review: replace function with consts * K Qualters review: change return type of function Co-authored-by: Elastic Machine <elasticmachine@users.noreply.github.com>
This commit is contained in:
parent
a0a85dbb90
commit
dd93a14fef
|
@ -5,7 +5,7 @@
|
|||
*/
|
||||
|
||||
import { uniquePidForProcess, uniqueParentPidForProcess } from './process_event';
|
||||
import { IndexedProcessTree } from '../types';
|
||||
import { IndexedProcessTree, AdjacentProcessMap } from '../types';
|
||||
import { ResolverEvent } from '../../../../common/types';
|
||||
import { levelOrder as baseLevelOrder } from '../lib/tree_sequencers';
|
||||
|
||||
|
@ -15,21 +15,89 @@ import { levelOrder as baseLevelOrder } from '../lib/tree_sequencers';
|
|||
export function factory(processes: ResolverEvent[]): IndexedProcessTree {
|
||||
const idToChildren = new Map<string | undefined, ResolverEvent[]>();
|
||||
const idToValue = new Map<string, ResolverEvent>();
|
||||
const idToAdjacent = new Map<string, AdjacentProcessMap>();
|
||||
|
||||
function emptyAdjacencyMap(id: string): AdjacentProcessMap {
|
||||
return {
|
||||
self: id,
|
||||
parent: null,
|
||||
firstChild: null,
|
||||
previousSibling: null,
|
||||
nextSibling: null,
|
||||
level: 1,
|
||||
};
|
||||
}
|
||||
|
||||
const roots: ResolverEvent[] = [];
|
||||
|
||||
for (const process of processes) {
|
||||
idToValue.set(uniquePidForProcess(process), process);
|
||||
const uniqueProcessPid = uniquePidForProcess(process);
|
||||
idToValue.set(uniqueProcessPid, process);
|
||||
|
||||
const currentProcessAdjacencyMap: AdjacentProcessMap =
|
||||
idToAdjacent.get(uniqueProcessPid) || emptyAdjacencyMap(uniqueProcessPid);
|
||||
idToAdjacent.set(uniqueProcessPid, currentProcessAdjacencyMap);
|
||||
|
||||
const uniqueParentPid = uniqueParentPidForProcess(process);
|
||||
const processChildren = idToChildren.get(uniqueParentPid);
|
||||
if (processChildren) {
|
||||
processChildren.push(process);
|
||||
const currentProcessSiblings = idToChildren.get(uniqueParentPid);
|
||||
|
||||
if (currentProcessSiblings) {
|
||||
const previousProcessId = uniquePidForProcess(
|
||||
currentProcessSiblings[currentProcessSiblings.length - 1]
|
||||
);
|
||||
currentProcessSiblings.push(process);
|
||||
/**
|
||||
* Update adjacency maps for current and previous entries
|
||||
*/
|
||||
idToAdjacent.get(previousProcessId)!.nextSibling = uniqueProcessPid;
|
||||
currentProcessAdjacencyMap.previousSibling = previousProcessId;
|
||||
if (uniqueParentPid) {
|
||||
currentProcessAdjacencyMap.parent = uniqueParentPid;
|
||||
}
|
||||
} else {
|
||||
idToChildren.set(uniqueParentPid, [process]);
|
||||
|
||||
if (uniqueParentPid) {
|
||||
/**
|
||||
* Get the parent's map, otherwise set an empty one
|
||||
*/
|
||||
const parentAdjacencyMap =
|
||||
idToAdjacent.get(uniqueParentPid) ||
|
||||
(idToAdjacent.set(uniqueParentPid, emptyAdjacencyMap(uniqueParentPid)),
|
||||
idToAdjacent.get(uniqueParentPid))!;
|
||||
// set firstChild for parent
|
||||
parentAdjacencyMap.firstChild = uniqueProcessPid;
|
||||
// set parent for current
|
||||
currentProcessAdjacencyMap.parent = uniqueParentPid || null;
|
||||
} else {
|
||||
// In this case (no unique parent id), it must be a root
|
||||
roots.push(process);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Scan adjacency maps from the top down and assign levels
|
||||
*/
|
||||
function traverseLevels(currentProcessMap: AdjacentProcessMap, level: number = 1): void {
|
||||
const nextLevel = level + 1;
|
||||
if (currentProcessMap.nextSibling) {
|
||||
traverseLevels(idToAdjacent.get(currentProcessMap.nextSibling)!, level);
|
||||
}
|
||||
if (currentProcessMap.firstChild) {
|
||||
traverseLevels(idToAdjacent.get(currentProcessMap.firstChild)!, nextLevel);
|
||||
}
|
||||
currentProcessMap.level = level;
|
||||
}
|
||||
|
||||
for (const treeRoot of roots) {
|
||||
traverseLevels(idToAdjacent.get(uniquePidForProcess(treeRoot))!);
|
||||
}
|
||||
|
||||
return {
|
||||
idToChildren,
|
||||
idToProcess: idToValue,
|
||||
idToAdjacent,
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -38,8 +106,8 @@ export function factory(processes: ResolverEvent[]): IndexedProcessTree {
|
|||
*/
|
||||
export function children(tree: IndexedProcessTree, process: ResolverEvent): ResolverEvent[] {
|
||||
const id = uniquePidForProcess(process);
|
||||
const processChildren = tree.idToChildren.get(id);
|
||||
return processChildren === undefined ? [] : processChildren;
|
||||
const currentProcessSiblings = tree.idToChildren.get(id);
|
||||
return currentProcessSiblings === undefined ? [] : currentProcessSiblings;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -43,10 +43,23 @@ interface UserChangedSelectedEvent {
|
|||
interface AppRequestedResolverData {
|
||||
readonly type: 'appRequestedResolverData';
|
||||
}
|
||||
/**
|
||||
* When the user switches the active descendent of the Resolver.
|
||||
*/
|
||||
interface UserFocusedOnResolverNode {
|
||||
readonly type: 'userFocusedOnResolverNode';
|
||||
readonly payload: {
|
||||
/**
|
||||
* Used to identify the process node that should be brought into view.
|
||||
*/
|
||||
readonly nodeId: string;
|
||||
};
|
||||
}
|
||||
|
||||
export type ResolverAction =
|
||||
| CameraAction
|
||||
| DataAction
|
||||
| UserBroughtProcessIntoView
|
||||
| UserChangedSelectedEvent
|
||||
| AppRequestedResolverData;
|
||||
| AppRequestedResolverData
|
||||
| UserFocusedOnResolverNode;
|
||||
|
|
|
@ -40,129 +40,129 @@ Object {
|
|||
0,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
Array [
|
||||
35.35533905932738,
|
||||
-21.228911104120876,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
-35.35533905932738,
|
||||
-62.053740150507174,
|
||||
],
|
||||
Array [
|
||||
106.06601717798213,
|
||||
19.595917942265423,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
-35.35533905932738,
|
||||
-62.053740150507174,
|
||||
],
|
||||
Array [
|
||||
0,
|
||||
-82.46615467370032,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
106.06601717798213,
|
||||
19.595917942265423,
|
||||
],
|
||||
Array [
|
||||
141.4213562373095,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
-82.46615467370032,
|
||||
],
|
||||
Array [
|
||||
35.35533905932738,
|
||||
-102.87856919689347,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
-123.2909837200866,
|
||||
],
|
||||
Array [
|
||||
70.71067811865476,
|
||||
-82.46615467370032,
|
||||
-41.641325627314025,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
-123.2909837200866,
|
||||
],
|
||||
Array [
|
||||
35.35533905932738,
|
||||
-143.70339824327976,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
70.71067811865476,
|
||||
-82.46615467370032,
|
||||
],
|
||||
Array [
|
||||
106.06601717798213,
|
||||
-102.87856919689347,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
141.4213562373095,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
Array [
|
||||
176.7766952966369,
|
||||
-21.22891110412087,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
141.4213562373095,
|
||||
-41.64132562731402,
|
||||
-70.71067811865476,
|
||||
-123.29098372008661,
|
||||
],
|
||||
Array [
|
||||
212.13203435596427,
|
||||
-0.8164965809277259,
|
||||
40.00833246545857,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
141.4213562373095,
|
||||
-41.64132562731402,
|
||||
-70.71067811865476,
|
||||
-123.29098372008661,
|
||||
],
|
||||
Array [
|
||||
176.7766952966369,
|
||||
-62.053740150507174,
|
||||
0,
|
||||
-164.1158127664729,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
212.13203435596427,
|
||||
-0.8164965809277259,
|
||||
40.00833246545857,
|
||||
],
|
||||
Array [
|
||||
247.48737341529164,
|
||||
-21.228911104120883,
|
||||
282.842712474619,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
247.48737341529164,
|
||||
-21.228911104120883,
|
||||
0,
|
||||
-164.1158127664729,
|
||||
],
|
||||
Array [
|
||||
318.1980515339464,
|
||||
-62.05374015050717,
|
||||
70.71067811865476,
|
||||
-204.9406418128592,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
-245.76547085924548,
|
||||
],
|
||||
Array [
|
||||
141.4213562373095,
|
||||
-164.1158127664729,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
0,
|
||||
-245.76547085924548,
|
||||
],
|
||||
Array [
|
||||
70.71067811865476,
|
||||
-286.5902999056318,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
141.4213562373095,
|
||||
-164.1158127664729,
|
||||
],
|
||||
Array [
|
||||
212.13203435596427,
|
||||
-204.9406418128592,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
282.842712474619,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
Array [
|
||||
353.5533905932738,
|
||||
-41.64132562731401,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
282.842712474619,
|
||||
-82.4661546737003,
|
||||
],
|
||||
Array [
|
||||
424.26406871192853,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
282.842712474619,
|
||||
-82.4661546737003,
|
||||
],
|
||||
Array [
|
||||
353.5533905932738,
|
||||
-123.29098372008661,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
424.26406871192853,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
Array [
|
||||
494.9747468305833,
|
||||
-41.64132562731404,
|
||||
],
|
||||
],
|
||||
Array [
|
||||
Array [
|
||||
494.9747468305833,
|
||||
-41.64132562731404,
|
||||
],
|
||||
Array [
|
||||
636.3961030678928,
|
||||
-123.2909837200866,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -199,7 +199,7 @@ Object {
|
|||
},
|
||||
} => Array [
|
||||
0,
|
||||
-82.46615467370032,
|
||||
-164.1158127664729,
|
||||
],
|
||||
Object {
|
||||
"@timestamp": 1582233383000,
|
||||
|
@ -215,7 +215,7 @@ Object {
|
|||
"unique_ppid": 0,
|
||||
},
|
||||
} => Array [
|
||||
141.4213562373095,
|
||||
282.842712474619,
|
||||
-0.8164965809277259,
|
||||
],
|
||||
Object {
|
||||
|
@ -232,8 +232,8 @@ Object {
|
|||
"unique_ppid": 1,
|
||||
},
|
||||
} => Array [
|
||||
35.35533905932738,
|
||||
-143.70339824327976,
|
||||
70.71067811865476,
|
||||
-286.5902999056318,
|
||||
],
|
||||
Object {
|
||||
"@timestamp": 1582233383000,
|
||||
|
@ -249,8 +249,8 @@ Object {
|
|||
"unique_ppid": 1,
|
||||
},
|
||||
} => Array [
|
||||
106.06601717798213,
|
||||
-102.87856919689347,
|
||||
212.13203435596427,
|
||||
-204.9406418128592,
|
||||
],
|
||||
Object {
|
||||
"@timestamp": 1582233383000,
|
||||
|
@ -266,8 +266,8 @@ Object {
|
|||
"unique_ppid": 2,
|
||||
},
|
||||
} => Array [
|
||||
176.7766952966369,
|
||||
-62.053740150507174,
|
||||
353.5533905932738,
|
||||
-123.29098372008661,
|
||||
],
|
||||
Object {
|
||||
"@timestamp": 1582233383000,
|
||||
|
@ -283,8 +283,8 @@ Object {
|
|||
"unique_ppid": 2,
|
||||
},
|
||||
} => Array [
|
||||
247.48737341529164,
|
||||
-21.228911104120883,
|
||||
494.9747468305833,
|
||||
-41.64132562731404,
|
||||
],
|
||||
Object {
|
||||
"@timestamp": 1582233383000,
|
||||
|
@ -300,8 +300,8 @@ Object {
|
|||
"unique_ppid": 6,
|
||||
},
|
||||
} => Array [
|
||||
318.1980515339464,
|
||||
-62.05374015050717,
|
||||
636.3961030678928,
|
||||
-123.2909837200866,
|
||||
],
|
||||
},
|
||||
}
|
||||
|
@ -316,8 +316,8 @@ Object {
|
|||
-0.8164965809277259,
|
||||
],
|
||||
Array [
|
||||
70.71067811865476,
|
||||
-41.641325627314025,
|
||||
141.4213562373095,
|
||||
-82.46615467370032,
|
||||
],
|
||||
],
|
||||
],
|
||||
|
@ -353,8 +353,8 @@ Object {
|
|||
"unique_ppid": 0,
|
||||
},
|
||||
} => Array [
|
||||
70.71067811865476,
|
||||
-41.641325627314025,
|
||||
141.4213562373095,
|
||||
-82.46615467370032,
|
||||
],
|
||||
},
|
||||
}
|
||||
|
|
|
@ -13,11 +13,12 @@ import {
|
|||
EdgeLineSegment,
|
||||
ProcessWithWidthMetadata,
|
||||
Matrix3,
|
||||
AdjacentProcessMap,
|
||||
} from '../../types';
|
||||
import { ResolverEvent } from '../../../../../common/types';
|
||||
import { Vector2 } from '../../types';
|
||||
import { add as vector2Add, applyMatrix3 } from '../../lib/vector2';
|
||||
import { isGraphableProcess } from '../../models/process_event';
|
||||
import { isGraphableProcess, uniquePidForProcess } from '../../models/process_event';
|
||||
import {
|
||||
factory as indexedProcessTreeFactory,
|
||||
children as indexedProcessTreeChildren,
|
||||
|
@ -27,7 +28,7 @@ import {
|
|||
} from '../../models/indexed_process_tree';
|
||||
|
||||
const unit = 100;
|
||||
const distanceBetweenNodesInUnits = 1;
|
||||
const distanceBetweenNodesInUnits = 2;
|
||||
|
||||
export function isLoading(state: DataState) {
|
||||
return state.isLoading;
|
||||
|
@ -392,17 +393,42 @@ function processPositions(
|
|||
return positions;
|
||||
}
|
||||
|
||||
export const processNodePositionsAndEdgeLineSegments = createSelector(
|
||||
export const indexedProcessTree = createSelector(graphableProcesses, function indexedTree(
|
||||
/* eslint-disable no-shadow */
|
||||
graphableProcesses
|
||||
/* eslint-enable no-shadow */
|
||||
) {
|
||||
return indexedProcessTreeFactory(graphableProcesses);
|
||||
});
|
||||
|
||||
export const processAdjacencies = createSelector(
|
||||
indexedProcessTree,
|
||||
graphableProcesses,
|
||||
function processNodePositionsAndEdgeLineSegments(
|
||||
function selectProcessAdjacencies(
|
||||
/* eslint-disable no-shadow */
|
||||
indexedProcessTree,
|
||||
graphableProcesses
|
||||
/* eslint-enable no-shadow */
|
||||
) {
|
||||
/**
|
||||
* Index the tree, creating maps from id -> node and id -> children
|
||||
*/
|
||||
const indexedProcessTree = indexedProcessTreeFactory(graphableProcesses);
|
||||
const processToAdjacencyMap = new Map<ResolverEvent, AdjacentProcessMap>();
|
||||
const { idToAdjacent } = indexedProcessTree;
|
||||
|
||||
for (const graphableProcess of graphableProcesses) {
|
||||
const processPid = uniquePidForProcess(graphableProcess);
|
||||
const adjacencyMap = idToAdjacent.get(processPid)!;
|
||||
processToAdjacencyMap.set(graphableProcess, adjacencyMap);
|
||||
}
|
||||
return { processToAdjacencyMap };
|
||||
}
|
||||
);
|
||||
|
||||
export const processNodePositionsAndEdgeLineSegments = createSelector(
|
||||
indexedProcessTree,
|
||||
function processNodePositionsAndEdgeLineSegments(
|
||||
/* eslint-disable no-shadow */
|
||||
indexedProcessTree
|
||||
/* eslint-enable no-shadow */
|
||||
) {
|
||||
/**
|
||||
* Walk the tree in reverse level order, calculating the 'width' of subtrees.
|
||||
*/
|
||||
|
|
|
@ -7,11 +7,25 @@ import { Reducer, combineReducers } from 'redux';
|
|||
import { animateProcessIntoView } from './methods';
|
||||
import { cameraReducer } from './camera/reducer';
|
||||
import { dataReducer } from './data/reducer';
|
||||
import { ResolverState, ResolverAction } from '../types';
|
||||
import { ResolverState, ResolverAction, ResolverUIState } from '../types';
|
||||
|
||||
const uiReducer: Reducer<ResolverUIState, ResolverAction> = (
|
||||
uiState = { activeDescendentId: null },
|
||||
action
|
||||
) => {
|
||||
if (action.type === 'userFocusedOnResolverNode') {
|
||||
return {
|
||||
activeDescendentId: action.payload.nodeId,
|
||||
};
|
||||
} else {
|
||||
return uiState;
|
||||
}
|
||||
};
|
||||
|
||||
const concernReducers = combineReducers({
|
||||
camera: cameraReducer,
|
||||
data: dataReducer,
|
||||
ui: uiReducer,
|
||||
});
|
||||
|
||||
export const resolverReducer: Reducer<ResolverState, ResolverAction> = (state, action) => {
|
||||
|
|
|
@ -54,6 +54,11 @@ export const processNodePositionsAndEdgeLineSegments = composeSelectors(
|
|||
dataSelectors.processNodePositionsAndEdgeLineSegments
|
||||
);
|
||||
|
||||
export const processAdjacencies = composeSelectors(
|
||||
dataStateSelector,
|
||||
dataSelectors.processAdjacencies
|
||||
);
|
||||
|
||||
/**
|
||||
* Returns the camera state from within ResolverState
|
||||
*/
|
||||
|
|
|
@ -23,6 +23,21 @@ export interface ResolverState {
|
|||
* Contains the state associated with event data (process events and possibly other event types).
|
||||
*/
|
||||
readonly data: DataState;
|
||||
|
||||
/**
|
||||
* Contains the state needed to maintain Resolver UI elements.
|
||||
*/
|
||||
readonly ui: ResolverUIState;
|
||||
}
|
||||
|
||||
/**
|
||||
* Piece of redux state that models an animation for the camera.
|
||||
*/
|
||||
export interface ResolverUIState {
|
||||
/**
|
||||
* The ID attribute of the resolver's aria-activedescendent.
|
||||
*/
|
||||
readonly activeDescendentId: string | null;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -174,9 +189,26 @@ export interface ProcessEvent {
|
|||
source_id?: number;
|
||||
process_name: string;
|
||||
process_path: string;
|
||||
signature_status?: string;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* A map of Process Ids that indicate which processes are adjacent to a given process along
|
||||
* directions in two axes: up/down and previous/next.
|
||||
*/
|
||||
export interface AdjacentProcessMap {
|
||||
readonly self: string;
|
||||
parent: string | null;
|
||||
firstChild: string | null;
|
||||
previousSibling: string | null;
|
||||
nextSibling: string | null;
|
||||
/**
|
||||
* To support aria-level, this must be >= 1
|
||||
*/
|
||||
level: number;
|
||||
}
|
||||
|
||||
/**
|
||||
* A represention of a process tree with indices for O(1) access to children and values by id.
|
||||
*/
|
||||
|
@ -189,6 +221,10 @@ export interface IndexedProcessTree {
|
|||
* Map of ID to process
|
||||
*/
|
||||
idToProcess: Map<string, ResolverEvent>;
|
||||
/**
|
||||
* Map of ID to adjacent processes
|
||||
*/
|
||||
idToAdjacent: Map<string, AdjacentProcessMap>;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,381 @@
|
|||
/*
|
||||
* Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
|
||||
* or more contributor license agreements. Licensed under the Elastic License;
|
||||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React, { memo } from 'react';
|
||||
import { saturate, lighten } from 'polished';
|
||||
|
||||
import {
|
||||
htmlIdGenerator,
|
||||
euiPaletteForTemperature,
|
||||
euiPaletteForStatus,
|
||||
colorPalette,
|
||||
} from '@elastic/eui';
|
||||
|
||||
/**
|
||||
* Generating from `colorPalette` function: This could potentially
|
||||
* pick up a palette shift and decouple from raw hex
|
||||
*/
|
||||
const [euiColorEmptyShade, , , , , euiColor85Shade, euiColorFullShade] = colorPalette(
|
||||
['#ffffff', '#000000'],
|
||||
7
|
||||
);
|
||||
|
||||
/**
|
||||
* Base Colors - sourced from EUI
|
||||
*/
|
||||
const resolverPalette: Record<string, string | string[]> = {
|
||||
temperatures: euiPaletteForTemperature(7),
|
||||
statii: euiPaletteForStatus(7),
|
||||
fullShade: euiColorFullShade,
|
||||
emptyShade: euiColorEmptyShade,
|
||||
};
|
||||
|
||||
/**
|
||||
* Defines colors by semantics like so:
|
||||
* `danger`, `attention`, `enabled`, `disabled`
|
||||
* Or by function like:
|
||||
* `colorBlindBackground`, `subMenuForeground`
|
||||
*/
|
||||
type ResolverColorNames =
|
||||
| 'ok'
|
||||
| 'empty'
|
||||
| 'full'
|
||||
| 'warning'
|
||||
| 'strokeBehindEmpty'
|
||||
| 'resolverBackground'
|
||||
| 'runningProcessStart'
|
||||
| 'runningProcessEnd'
|
||||
| 'runningTriggerStart'
|
||||
| 'runningTriggerEnd'
|
||||
| 'activeNoWarning'
|
||||
| 'activeWarning'
|
||||
| 'fullLabelBackground'
|
||||
| 'inertDescription';
|
||||
|
||||
export const NamedColors: Record<ResolverColorNames, string> = {
|
||||
ok: saturate(0.5, resolverPalette.temperatures[0]),
|
||||
empty: euiColorEmptyShade,
|
||||
full: euiColorFullShade,
|
||||
strokeBehindEmpty: euiColor85Shade,
|
||||
warning: resolverPalette.statii[3],
|
||||
resolverBackground: euiColorFullShade,
|
||||
runningProcessStart: '#006BB4',
|
||||
runningProcessEnd: '#017D73',
|
||||
runningTriggerStart: '#BD281E',
|
||||
runningTriggerEnd: '#DD0A73',
|
||||
activeNoWarning: '#0078FF',
|
||||
activeWarning: '#C61F38',
|
||||
fullLabelBackground: '#3B3C41',
|
||||
inertDescription: '#747474',
|
||||
};
|
||||
|
||||
const idGenerator = htmlIdGenerator();
|
||||
|
||||
/**
|
||||
* Ids of paint servers to be referenced by fill and stroke attributes
|
||||
*/
|
||||
export const PaintServerIds = {
|
||||
runningProcess: idGenerator('psRunningProcess'),
|
||||
runningTrigger: idGenerator('psRunningTrigger'),
|
||||
runningProcessCube: idGenerator('psRunningProcessCube'),
|
||||
runningTriggerCube: idGenerator('psRunningTriggerCube'),
|
||||
terminatedProcessCube: idGenerator('psTerminatedProcessCube'),
|
||||
terminatedTriggerCube: idGenerator('psTerminatedTriggerCube'),
|
||||
};
|
||||
|
||||
/**
|
||||
* PaintServers: Where color palettes, grandients, patterns and other similar concerns
|
||||
* are exposed to the component
|
||||
*/
|
||||
const PaintServers = memo(() => (
|
||||
<>
|
||||
<linearGradient
|
||||
id={PaintServerIds.runningProcess}
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
spreadMethod="reflect"
|
||||
gradientUnits="objectBoundingBox"
|
||||
>
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={saturate(0.7, lighten(0.05, NamedColors.runningProcessStart))}
|
||||
stopOpacity="1"
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={saturate(0.7, lighten(0.05, NamedColors.runningProcessEnd))}
|
||||
stopOpacity="1"
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id={PaintServerIds.runningTrigger}
|
||||
x1="0"
|
||||
y1="0"
|
||||
x2="1"
|
||||
y2="0"
|
||||
spreadMethod="reflect"
|
||||
gradientUnits="objectBoundingBox"
|
||||
>
|
||||
<stop
|
||||
offset="0%"
|
||||
stopColor={saturate(0.7, lighten(0.05, NamedColors.runningTriggerStart))}
|
||||
stopOpacity="1"
|
||||
/>
|
||||
<stop
|
||||
offset="100%"
|
||||
stopColor={saturate(0.7, lighten(0.05, NamedColors.runningTriggerEnd))}
|
||||
stopOpacity="1"
|
||||
/>
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id={PaintServerIds.runningProcessCube}
|
||||
x1="-382.33074"
|
||||
y1="265.24689"
|
||||
x2="-381.88086"
|
||||
y2="264.46019"
|
||||
gradientTransform="matrix(88, 0, 0, -100, 33669, 26535)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stopColor={NamedColors.runningProcessStart} />
|
||||
<stop offset="1" stopColor={NamedColors.runningProcessEnd} />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id={PaintServerIds.runningTriggerCube}
|
||||
x1="-382.32713"
|
||||
y1="265.24057"
|
||||
x2="-381.88108"
|
||||
y2="264.46057"
|
||||
gradientTransform="matrix(88, 0, 0, -100, 33669, 26535)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stopColor="#bd281f" />
|
||||
<stop offset="1" stopColor="#dc0b72" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id={PaintServerIds.terminatedProcessCube}
|
||||
x1="-382.33074"
|
||||
y1="265.24689"
|
||||
x2="-381.88086"
|
||||
y2="264.46019"
|
||||
gradientTransform="matrix(88, 0, 0, -100, 33669, 26535)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stopColor="#006bb4" />
|
||||
<stop offset="1" stopColor="#017d73" />
|
||||
</linearGradient>
|
||||
<linearGradient
|
||||
id={PaintServerIds.terminatedTriggerCube}
|
||||
x1="-382.33074"
|
||||
y1="265.24689"
|
||||
x2="-381.88086"
|
||||
y2="264.46019"
|
||||
gradientTransform="matrix(88, 0, 0, -100, 33669, 26535)"
|
||||
gradientUnits="userSpaceOnUse"
|
||||
>
|
||||
<stop offset="0" stopColor="#be2820" />
|
||||
<stop offset="1" stopColor="#dc0b72" />
|
||||
</linearGradient>
|
||||
</>
|
||||
));
|
||||
|
||||
/**
|
||||
* Ids of symbols to be linked by <use> elements
|
||||
*/
|
||||
export const SymbolIds = {
|
||||
processNode: idGenerator('nodeSymbol'),
|
||||
solidHexagon: idGenerator('hexagon'),
|
||||
runningProcessCube: idGenerator('runningCube'),
|
||||
runningTriggerCube: idGenerator('runningTriggerCube'),
|
||||
terminatedProcessCube: idGenerator('terminatedCube'),
|
||||
terminatedTriggerCube: idGenerator('terminatedTriggerCube'),
|
||||
};
|
||||
|
||||
/**
|
||||
* Defs entries that define shapes, masks and other spatial elements
|
||||
*/
|
||||
const SymbolsAndShapes = memo(() => (
|
||||
<>
|
||||
<symbol id={SymbolIds.processNode} viewBox="0 0 144 25" preserveAspectRatio="xMidYMid meet">
|
||||
<rect
|
||||
x="1"
|
||||
y="1"
|
||||
width="142"
|
||||
height="23"
|
||||
fill="inherit"
|
||||
strokeWidth="0"
|
||||
paintOrder="normal"
|
||||
/>
|
||||
</symbol>
|
||||
<symbol id={SymbolIds.solidHexagon} viewBox="0 0 200 200" preserveAspectRatio="xMidYMid meet">
|
||||
<g transform="translate(0,-97)">
|
||||
<path
|
||||
transform="matrix(1.6461 0 0 1.6596 -56.401 -64.183)"
|
||||
d="m95.148 97.617 28.238 16.221 23.609 13.713 0.071 32.566-0.071 27.302-28.167 16.344-23.68 13.59-28.238-16.221-23.609-13.713-0.07098-32.566 0.07098-27.302 28.167-16.344z"
|
||||
fill="inherit"
|
||||
strokeWidth="15"
|
||||
stroke="inherit"
|
||||
/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id={SymbolIds.runningProcessCube} viewBox="0 0 88 100">
|
||||
<title>Running Process</title>
|
||||
<g>
|
||||
<polygon
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
fill="#1d1e24"
|
||||
/>
|
||||
<polygon
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="27 41.077 44.089 31 61 41.077 61 60.128 44.089 70 27 60.128 27 41.077"
|
||||
fill="#fff"
|
||||
/>
|
||||
<polygon points="44 31 61 41.077 61 60.128 44 50 44 31" fill="#d8d8d8" />
|
||||
<polygon points="27 60.128 27 41.077 44 31 44 50 27 60.128" fill="#959595" />
|
||||
<polygon
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
opacity="0.74744"
|
||||
fill={`url(#${PaintServerIds.runningProcessCube})`}
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="88 25.839 44.23 0 0 25.839 44 50 88 25.839"
|
||||
fill="#fff"
|
||||
opacity="0.13893"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol id={SymbolIds.runningTriggerCube} viewBox="0 0 88 100">
|
||||
<title>Running Trigger Process</title>
|
||||
<g>
|
||||
<polygon
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
fill="#1d1e24"
|
||||
/>
|
||||
<polygon
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="27 41.077 44.089 31 61 41.077 61 60.128 44.089 70 27 60.128 27 41.077"
|
||||
fill="#fff"
|
||||
/>
|
||||
<polygon points="44 31 61 41.077 61 60.128 44 50 44 31" fill="#d8d8d8" />
|
||||
<polygon points="27 60.128 27 41.077 44 31 44 50 27 60.128" fill="#959595" />
|
||||
<polygon
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
opacity="0.75"
|
||||
fill={`url(#${PaintServerIds.runningTriggerCube})`}
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="88 25.839 44.23 0 0 25.839 44 50 88 25.839"
|
||||
fill="#fff"
|
||||
opacity="0.13893"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
</g>
|
||||
</symbol>
|
||||
<symbol viewBox="0 0 88 100" id={SymbolIds.terminatedProcessCube}>
|
||||
<title>Terminated Process</title>
|
||||
<g>
|
||||
<polygon
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
fill="#1d1e24"
|
||||
/>
|
||||
<polygon
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
id="Path-4-Copy-15"
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
opacity="0.35"
|
||||
fill={`url(#${PaintServerIds.terminatedProcessCube})`}
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
id="Path-Copy-20"
|
||||
points="88 25.839 44.23 0 0 25.839 44 50 88 25.839"
|
||||
fill="#fff"
|
||||
opacity="0.13893"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
id="Path-Copy-21"
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
</g>
|
||||
</symbol>
|
||||
<svg id={SymbolIds.terminatedTriggerCube} viewBox="0 0 88 100">
|
||||
<title>Terminated Trigger Process</title>
|
||||
<g>
|
||||
<polygon
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
fill="#1d1e24"
|
||||
/>
|
||||
<polygon
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="0 25.839 44.23 0 88 25.839 88 74.688 44.23 100 0 74.688 0 25.839"
|
||||
opacity="0.35"
|
||||
fill={`url(#${PaintServerIds.terminatedTriggerCube})`}
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="88 25.839 44.23 0 0 25.839 44 50 88 25.839"
|
||||
fill="#fff"
|
||||
opacity="0.13893"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
<polygon
|
||||
points="44.23 100 44 50 0 25.839 0 74.664 44.23 100"
|
||||
opacity="0.25179"
|
||||
style={{ isolation: 'isolate' }}
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</>
|
||||
));
|
||||
|
||||
/**
|
||||
* This <defs> element is used to define the reusable assets for the Resolver
|
||||
* It confers sevral advantages, including but not limited to:
|
||||
* 1) Freedom of form for creative assets (beyond box-model constraints)
|
||||
* 2) Separation of concerns between creative assets and more functional areas of the app
|
||||
* 3) <use> elements can be handled by compositor (faster)
|
||||
*/
|
||||
export const SymbolDefinitions = memo(() => (
|
||||
<svg>
|
||||
<defs>
|
||||
<PaintServers />
|
||||
<SymbolsAndShapes />
|
||||
</defs>
|
||||
</svg>
|
||||
));
|
|
@ -66,7 +66,7 @@ export const EdgeLine = styled(
|
|||
*/
|
||||
transform: `translateY(-50%) rotateZ(${angle(screenStart, screenEnd)}rad)`,
|
||||
};
|
||||
return <div className={className} style={style} />;
|
||||
return <div role="presentation" className={className} style={style} />;
|
||||
}
|
||||
)
|
||||
)`
|
||||
|
@ -74,4 +74,5 @@ export const EdgeLine = styled(
|
|||
height: 3px;
|
||||
background-color: #d4d4d4;
|
||||
color: #333333;
|
||||
contain: strict;
|
||||
`;
|
||||
|
|
|
@ -14,6 +14,7 @@ import { Panel } from './panel';
|
|||
import { GraphControls } from './graph_controls';
|
||||
import { ProcessEventDot } from './process_event_dot';
|
||||
import { useCamera } from './use_camera';
|
||||
import { SymbolDefinitions, NamedColors } from './defs';
|
||||
import { ResolverAction } from '../types';
|
||||
import { ResolverEvent } from '../../../../common/types';
|
||||
|
||||
|
@ -33,6 +34,14 @@ const StyledGraphControls = styled(GraphControls)`
|
|||
right: 5px;
|
||||
`;
|
||||
|
||||
const StyledResolverContainer = styled.div`
|
||||
display: flex;
|
||||
flex-grow: 1;
|
||||
contain: layout;
|
||||
`;
|
||||
|
||||
const bgColor = NamedColors.resolverBackground;
|
||||
|
||||
export const Resolver = styled(
|
||||
React.memo(function Resolver({
|
||||
className,
|
||||
|
@ -46,6 +55,8 @@ export const Resolver = styled(
|
|||
);
|
||||
|
||||
const dispatch: (action: ResolverAction) => unknown = useDispatch();
|
||||
const { processToAdjacencyMap } = useSelector(selectors.processAdjacencies);
|
||||
|
||||
const { projectionMatrix, ref, onMouseDown } = useCamera();
|
||||
const isLoading = useSelector(selectors.isLoading);
|
||||
|
||||
|
@ -62,29 +73,35 @@ export const Resolver = styled(
|
|||
<EuiLoadingSpinner size="xl" />
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<div className="resolver-graph" onMouseDown={onMouseDown} ref={ref}>
|
||||
{Array.from(processNodePositions).map(([processEvent, position], index) => (
|
||||
<ProcessEventDot
|
||||
key={index}
|
||||
position={position}
|
||||
projectionMatrix={projectionMatrix}
|
||||
event={processEvent}
|
||||
/>
|
||||
))}
|
||||
{edgeLineSegments.map(([startPosition, endPosition], index) => (
|
||||
<EdgeLine
|
||||
key={index}
|
||||
startPosition={startPosition}
|
||||
endPosition={endPosition}
|
||||
projectionMatrix={projectionMatrix}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
<StyledPanel />
|
||||
<StyledGraphControls />
|
||||
</>
|
||||
<StyledResolverContainer
|
||||
className="resolver-graph kbn-resetFocusState"
|
||||
onMouseDown={onMouseDown}
|
||||
ref={ref}
|
||||
role="tree"
|
||||
tabIndex={0}
|
||||
>
|
||||
{edgeLineSegments.map(([startPosition, endPosition], index) => (
|
||||
<EdgeLine
|
||||
key={index}
|
||||
startPosition={startPosition}
|
||||
endPosition={endPosition}
|
||||
projectionMatrix={projectionMatrix}
|
||||
/>
|
||||
))}
|
||||
{Array.from(processNodePositions).map(([processEvent, position], index) => (
|
||||
<ProcessEventDot
|
||||
key={index}
|
||||
position={position}
|
||||
projectionMatrix={projectionMatrix}
|
||||
event={processEvent}
|
||||
adjacentNodeMap={processToAdjacencyMap.get(processEvent)}
|
||||
/>
|
||||
))}
|
||||
</StyledResolverContainer>
|
||||
)}
|
||||
<StyledPanel />
|
||||
<StyledGraphControls />
|
||||
<SymbolDefinitions />
|
||||
</div>
|
||||
);
|
||||
})
|
||||
|
@ -111,4 +128,6 @@ export const Resolver = styled(
|
|||
* Prevent partially visible components from showing up outside the bounds of Resolver.
|
||||
*/
|
||||
overflow: hidden;
|
||||
contain: strict;
|
||||
background-color: ${bgColor};
|
||||
`;
|
||||
|
|
|
@ -4,12 +4,52 @@
|
|||
* you may not use this file except in compliance with the Elastic License.
|
||||
*/
|
||||
|
||||
import React from 'react';
|
||||
import React, { useCallback, useMemo } from 'react';
|
||||
import styled from 'styled-components';
|
||||
import { i18n } from '@kbn/i18n';
|
||||
import { htmlIdGenerator, EuiKeyboardAccessible } from '@elastic/eui';
|
||||
import { applyMatrix3 } from '../lib/vector2';
|
||||
import { Vector2, Matrix3 } from '../types';
|
||||
import { Vector2, Matrix3, AdjacentProcessMap, ResolverProcessType } from '../types';
|
||||
import { SymbolIds, NamedColors, PaintServerIds } from './defs';
|
||||
import { ResolverEvent } from '../../../../common/types';
|
||||
import { useResolverDispatch } from './use_resolver_dispatch';
|
||||
import * as eventModel from '../../../../common/models/event';
|
||||
import * as processModel from '../models/process_event';
|
||||
|
||||
const nodeAssets = {
|
||||
runningProcessCube: {
|
||||
cubeSymbol: `#${SymbolIds.runningProcessCube}`,
|
||||
labelFill: `url(#${PaintServerIds.runningProcess})`,
|
||||
descriptionFill: NamedColors.activeNoWarning,
|
||||
descriptionText: i18n.translate('xpack.endpoint.resolver.runningProcess', {
|
||||
defaultMessage: 'Running Process',
|
||||
}),
|
||||
},
|
||||
runningTriggerCube: {
|
||||
cubeSymbol: `#${SymbolIds.runningTriggerCube}`,
|
||||
labelFill: `url(#${PaintServerIds.runningTrigger})`,
|
||||
descriptionFill: NamedColors.activeWarning,
|
||||
descriptionText: i18n.translate('xpack.endpoint.resolver.runningTrigger', {
|
||||
defaultMessage: 'Running Trigger',
|
||||
}),
|
||||
},
|
||||
terminatedProcessCube: {
|
||||
cubeSymbol: `#${SymbolIds.terminatedProcessCube}`,
|
||||
labelFill: NamedColors.fullLabelBackground,
|
||||
descriptionFill: NamedColors.inertDescription,
|
||||
descriptionText: i18n.translate('xpack.endpoint.resolver.terminatedProcess', {
|
||||
defaultMessage: 'Terminated Process',
|
||||
}),
|
||||
},
|
||||
terminatedTriggerCube: {
|
||||
cubeSymbol: `#${SymbolIds.terminatedTriggerCube}`,
|
||||
labelFill: NamedColors.fullLabelBackground,
|
||||
descriptionFill: NamedColors.inertDescription,
|
||||
descriptionText: i18n.translate('xpack.endpoint.resolver.terminatedTrigger', {
|
||||
defaultMessage: 'Terminated Trigger',
|
||||
}),
|
||||
},
|
||||
};
|
||||
|
||||
/**
|
||||
* A placeholder view for a process node.
|
||||
|
@ -21,6 +61,7 @@ export const ProcessEventDot = styled(
|
|||
position,
|
||||
event,
|
||||
projectionMatrix,
|
||||
adjacentNodeMap,
|
||||
}: {
|
||||
/**
|
||||
* A `className` string provided by `styled`
|
||||
|
@ -38,39 +79,205 @@ export const ProcessEventDot = styled(
|
|||
* projectionMatrix which can be used to convert `position` to screen coordinates.
|
||||
*/
|
||||
projectionMatrix: Matrix3;
|
||||
/**
|
||||
* map of what nodes are "adjacent" to this one in "up, down, previous, next" directions
|
||||
*/
|
||||
adjacentNodeMap?: AdjacentProcessMap;
|
||||
}) => {
|
||||
/**
|
||||
* Convert the position, which is in 'world' coordinates, to screen coordinates.
|
||||
*/
|
||||
const [left, top] = applyMatrix3(position, projectionMatrix);
|
||||
const style = {
|
||||
left: (left - 20).toString() + 'px',
|
||||
top: (top - 20).toString() + 'px',
|
||||
};
|
||||
|
||||
const [magFactorX] = projectionMatrix;
|
||||
|
||||
const selfId = adjacentNodeMap?.self;
|
||||
|
||||
const nodeViewportStyle = useMemo(
|
||||
() => ({
|
||||
left: `${left}px`,
|
||||
top: `${top}px`,
|
||||
// Width of symbol viewport scaled to fit
|
||||
width: `${360 * magFactorX}px`,
|
||||
// Height according to symbol viewbox AR
|
||||
height: `${120 * magFactorX}px`,
|
||||
// Adjusted to position/scale with camera
|
||||
transform: `translateX(-${0.172413 * 360 * magFactorX + 10}px) translateY(-${0.73684 *
|
||||
120 *
|
||||
magFactorX}px)`,
|
||||
}),
|
||||
[left, magFactorX, top]
|
||||
);
|
||||
|
||||
const markerBaseSize = 15;
|
||||
const markerSize = markerBaseSize;
|
||||
const markerPositionOffset = -markerBaseSize / 2;
|
||||
|
||||
const labelYOffset = markerPositionOffset + 0.25 * markerSize - 0.5;
|
||||
|
||||
const labelYHeight = markerSize / 1.7647;
|
||||
|
||||
const levelAttribute = adjacentNodeMap?.level
|
||||
? {
|
||||
'aria-level': adjacentNodeMap.level,
|
||||
}
|
||||
: {};
|
||||
|
||||
const flowToAttribute = adjacentNodeMap?.nextSibling
|
||||
? {
|
||||
'aria-flowto': adjacentNodeMap.nextSibling,
|
||||
}
|
||||
: {};
|
||||
|
||||
const nodeType = getNodeType(event);
|
||||
const clickTargetRef: { current: SVGAnimationElement | null } = React.createRef();
|
||||
const { cubeSymbol, labelFill, descriptionFill, descriptionText } = nodeAssets[nodeType];
|
||||
const resolverNodeIdGenerator = htmlIdGenerator('resolverNode');
|
||||
const [nodeId, labelId, descriptionId] = [
|
||||
!!selfId ? resolverNodeIdGenerator(String(selfId)) : resolverNodeIdGenerator(),
|
||||
resolverNodeIdGenerator(),
|
||||
resolverNodeIdGenerator(),
|
||||
] as string[];
|
||||
|
||||
const dispatch = useResolverDispatch();
|
||||
|
||||
const handleFocus = useCallback(
|
||||
(focusEvent: React.FocusEvent<SVGSVGElement>) => {
|
||||
dispatch({
|
||||
type: 'userFocusedOnResolverNode',
|
||||
payload: {
|
||||
nodeId,
|
||||
},
|
||||
});
|
||||
focusEvent.currentTarget.setAttribute('aria-current', 'true');
|
||||
},
|
||||
[dispatch, nodeId]
|
||||
);
|
||||
|
||||
const handleClick = useCallback(
|
||||
(clickEvent: React.MouseEvent<SVGSVGElement, MouseEvent>) => {
|
||||
if (clickTargetRef.current !== null) {
|
||||
(clickTargetRef.current as any).beginElement();
|
||||
}
|
||||
},
|
||||
[clickTargetRef]
|
||||
);
|
||||
|
||||
return (
|
||||
<span className={className} style={style} data-test-subj={'resolverNode'}>
|
||||
name: {eventModel.eventName(event)}
|
||||
<br />
|
||||
x: {position[0]}
|
||||
<br />
|
||||
y: {position[1]}
|
||||
</span>
|
||||
<EuiKeyboardAccessible>
|
||||
<svg
|
||||
data-test-subj={'resolverNode'}
|
||||
className={className + ' kbn-resetFocusState'}
|
||||
viewBox="-15 -15 90 30"
|
||||
preserveAspectRatio="xMidYMid meet"
|
||||
role="treeitem"
|
||||
{...levelAttribute}
|
||||
{...flowToAttribute}
|
||||
aria-labelledby={labelId}
|
||||
aria-describedby={descriptionId}
|
||||
aria-haspopup={'true'}
|
||||
style={nodeViewportStyle}
|
||||
id={nodeId}
|
||||
onClick={handleClick}
|
||||
onFocus={handleFocus}
|
||||
tabIndex={-1}
|
||||
>
|
||||
<g>
|
||||
<use
|
||||
role="presentation"
|
||||
xlinkHref={cubeSymbol}
|
||||
x={markerPositionOffset}
|
||||
y={markerPositionOffset}
|
||||
width={markerSize}
|
||||
height={markerSize}
|
||||
opacity="1"
|
||||
className="cube"
|
||||
>
|
||||
<animateTransform
|
||||
attributeType="XML"
|
||||
attributeName="transform"
|
||||
type="scale"
|
||||
values="1 1; 1 .83; 1 .8; 1 .83; 1 1"
|
||||
dur="0.2s"
|
||||
begin="click"
|
||||
repeatCount="1"
|
||||
className="squish"
|
||||
ref={clickTargetRef}
|
||||
/>
|
||||
</use>
|
||||
<use
|
||||
role="presentation"
|
||||
xlinkHref={`#${SymbolIds.processNode}`}
|
||||
x={markerPositionOffset + markerSize - 0.5}
|
||||
y={labelYOffset}
|
||||
width={(markerSize / 1.7647) * 5}
|
||||
height={markerSize / 1.7647}
|
||||
opacity="1"
|
||||
fill={labelFill}
|
||||
/>
|
||||
<text
|
||||
x={markerPositionOffset + 0.7 * markerSize + 50 / 2}
|
||||
y={labelYOffset + labelYHeight / 2}
|
||||
textAnchor="middle"
|
||||
dominantBaseline="middle"
|
||||
fontSize="3.75"
|
||||
fontWeight="bold"
|
||||
fill={NamedColors.empty}
|
||||
paintOrder="stroke"
|
||||
tabIndex={-1}
|
||||
style={{ letterSpacing: '-0.02px' }}
|
||||
id={labelId}
|
||||
>
|
||||
{eventModel.eventName(event)}
|
||||
</text>
|
||||
<text
|
||||
x={markerPositionOffset + markerSize}
|
||||
y={labelYOffset - 1}
|
||||
textAnchor="start"
|
||||
dominantBaseline="middle"
|
||||
fontSize="2.67"
|
||||
fill={descriptionFill}
|
||||
id={descriptionId}
|
||||
paintOrder="stroke"
|
||||
fontWeight="bold"
|
||||
style={{ textTransform: 'uppercase', letterSpacing: '-0.01px' }}
|
||||
>
|
||||
{descriptionText}
|
||||
</text>
|
||||
</g>
|
||||
</svg>
|
||||
</EuiKeyboardAccessible>
|
||||
);
|
||||
}
|
||||
)
|
||||
)`
|
||||
position: absolute;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
display: block;
|
||||
text-align: left;
|
||||
font-size: 10px;
|
||||
/**
|
||||
* Give the element a button-like appearance.
|
||||
*/
|
||||
user-select: none;
|
||||
border: 1px solid black;
|
||||
box-sizing: border-box;
|
||||
border-radius: 10%;
|
||||
padding: 4px;
|
||||
white-space: nowrap;
|
||||
will-change: left, top, width, height;
|
||||
contain: strict;
|
||||
`;
|
||||
|
||||
const processTypeToCube: Record<ResolverProcessType, keyof typeof nodeAssets> = {
|
||||
processCreated: 'terminatedProcessCube',
|
||||
processRan: 'runningProcessCube',
|
||||
processTerminated: 'terminatedProcessCube',
|
||||
unknownProcessEvent: 'runningProcessCube',
|
||||
processCausedAlert: 'runningTriggerCube',
|
||||
unknownEvent: 'runningProcessCube',
|
||||
};
|
||||
|
||||
function getNodeType(processEvent: ResolverEvent): keyof typeof nodeAssets {
|
||||
const processType = processModel.eventType(processEvent);
|
||||
|
||||
if (processType in processTypeToCube) {
|
||||
return processTypeToCube[processType];
|
||||
}
|
||||
return 'runningProcessCube';
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue