[Resolver] Refactor resolver assets (#77795)

This PR splits the `assets` module into several other modules/hooks:
* `calculateResolverFontSize` is now `fontSize`
* `colorMap` is now `useColors`
* `cubeAssetsForNow` is now `useCubeAssets`
* `nodeAssets` is gone (inlined into `useCubeAssets`)

The PaintServer and Symbol IDs no longer use random IDs. They are now based on the `resolverComponentInstanceID`. This sets us up to use a provider that can allow multiple resolver instances to share these assets.
This commit is contained in:
Robert Austin 2020-09-18 12:29:38 -04:00 committed by GitHub
parent 3e5ae012c5
commit cdb3c30ab9
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
16 changed files with 634 additions and 565 deletions

View file

@ -1,521 +0,0 @@
/*
* 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.
*/
/* eslint-disable react/display-name */
import React, { memo } from 'react';
import euiThemeAmsterdamDark from '@elastic/eui/dist/eui_theme_amsterdam_dark.json';
import euiThemeAmsterdamLight from '@elastic/eui/dist/eui_theme_amsterdam_light.json';
import { htmlIdGenerator, ButtonColor } from '@elastic/eui';
import styled from 'styled-components';
import { i18n } from '@kbn/i18n';
import { ResolverProcessType } from '../types';
import { useUiSetting } from '../../../../../../src/plugins/kibana_react/public';
type ResolverColorNames =
| 'descriptionText'
| 'full'
| 'graphControls'
| 'graphControlsBackground'
| 'resolverBackground'
| 'resolverEdge'
| 'resolverEdgeText'
| 'resolverBreadcrumbBackground'
| 'pillStroke';
type ColorMap = Record<ResolverColorNames, string>;
interface NodeStyleConfig {
backingFill: string;
cubeSymbol: string;
descriptionFill: string;
descriptionText: string;
isLabelFilled: boolean;
labelButtonFill: ButtonColor;
strokeColor: string;
}
interface NodeStyleMap {
runningProcessCube: NodeStyleConfig;
runningTriggerCube: NodeStyleConfig;
terminatedProcessCube: NodeStyleConfig;
terminatedTriggerCube: NodeStyleConfig;
}
const idGenerator = htmlIdGenerator();
/**
* Ids of paint servers to be referenced by fill and stroke attributes
*/
const PaintServerIds = {
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(({ isDarkMode }: { isDarkMode: boolean }) => (
<>
<linearGradient
id={PaintServerIds.runningProcessCube}
x1="-381.23556"
y1="264.73802"
x2="-380.48514"
y2="263.8816"
gradientTransform="matrix(70.05179, 0, 0, -79.94774, 26724.01618, 21181.09848)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.00087" stopColor="#006bb4" />
<stop offset="1" stopColor="#54b399" />
</linearGradient>
<linearGradient
id={PaintServerIds.runningTriggerCube}
x1="-381.18643"
y1="264.68195"
x2="-380.48514"
y2="263.8816"
gradientTransform="matrix(70.05179, 0, 0, -79.94774, 26724.01618, 21181.09848)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#dd0a73" />
<stop offset="1" stopColor="#f66" />
</linearGradient>
{isDarkMode ? (
<>
<linearGradient
id={PaintServerIds.terminatedProcessCube}
x1="-381.23752"
y1="264.24026"
x2="-380.48514"
y2="263.3816"
gradientTransform="matrix(70.05178, 0, 0, -79.94771, 26724.01313, 21140.72096)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#4c82c3" />
<stop offset="1" stopColor="#8bd1c7" />
</linearGradient>
<linearGradient
id={PaintServerIds.terminatedTriggerCube}
x1="-381.18658"
y1="264.68187"
x2="-380.48546"
y2="263.8817"
gradientTransform="matrix(70.05179, 0, 0, -79.94774, 26724.01618, 21181.09848)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#dd0a73" />
<stop offset="1" stopColor="#f66" />
</linearGradient>
</>
) : (
<>
<linearGradient
id={PaintServerIds.terminatedProcessCube}
x1="10.5206"
y1="9.49068"
x2="46.8141"
y2="45.7844"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#2F6EB6" />
<stop offset="1" stopColor="#00B4AC" />
</linearGradient>
<linearGradient
id={PaintServerIds.terminatedTriggerCube}
x1="15.4848"
y1="12.0468"
x2="43.1049"
y2="47.2331"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#DD0A73" />
<stop offset="1" stopColor="#FF6666" />
</linearGradient>
</>
)}
</>
));
/**
* Ids of symbols to be linked by <use> elements
*/
export const SymbolIds = {
processNodeLabel: idGenerator('nodeSymbol'),
runningProcessCube: idGenerator('runningCube'),
runningTriggerCube: idGenerator('runningTriggerCube'),
terminatedProcessCube: idGenerator('terminatedCube'),
terminatedTriggerCube: idGenerator('terminatedTriggerCube'),
processCubeActiveBacking: idGenerator('activeBacking'),
};
/**
* Defs entries that define shapes, masks and other spatial elements
*/
const SymbolsAndShapes = memo(({ isDarkMode }: { isDarkMode: boolean }) => (
<>
<symbol
id={SymbolIds.processNodeLabel}
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.runningProcessCube} viewBox="0 0 88 100">
<title>{'Running Process'}</title>
<path
d="M87.52127,25.129a3.79536,3.79536,0,0,0-1.43184-1.47165L45.91025.57471a3.83652,3.83652,0,0,0-3.8205,0L1.91039,23.65739A3.86308,3.86308,0,0,0,0,26.95V73.11541a3.79835,3.79835,0,0,0,1.9104,3.2925L42.08975,99.49067a3.83691,3.83691,0,0,0,3.8205,0L86.08943,76.40791A3.79852,3.79852,0,0,0,88,73.11541V26.95A3.77641,3.77641,0,0,0,87.52127,25.129Z"
transform="translate(0.00001 0)"
fill={`url(#${PaintServerIds.runningProcessCube})`}
/>
<g opacity="0.5">
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</g>
<path
d="M.57144,24.91834a3.79909,3.79909,0,0,1,1.34825-1.32625L42.0989.50939a3.837,3.837,0,0,1,3.82081,0L86.09892,23.59206a3.79782,3.79782,0,0,1,1.4318,1.47169L44.00915,49.96733Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.3"
style={{ isolation: 'isolate' }}
/>
<path
d="M43.99984,50.03265V100a3.83392,3.83392,0,0,1-1.91024-.50933L1.91039,76.40791A3.79835,3.79835,0,0,1,0,73.11541V26.95a3.77423,3.77423,0,0,1,.56216-1.96625Z"
transform="translate(0.00001 0)"
fill="#353944"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</symbol>
<symbol id={SymbolIds.runningTriggerCube} viewBox="0 0 88 100">
<title>{'resolver_dark process running'}</title>
<path
d="M87.52127,25.129a3.79536,3.79536,0,0,0-1.43184-1.47165L45.91025.57471a3.83652,3.83652,0,0,0-3.8205,0L1.91039,23.65739A3.86308,3.86308,0,0,0,0,26.95V73.11541a3.79835,3.79835,0,0,0,1.9104,3.2925L42.08975,99.49067a3.83691,3.83691,0,0,0,3.8205,0L86.08943,76.40791A3.79852,3.79852,0,0,0,88,73.11541V26.95A3.77641,3.77641,0,0,0,87.52127,25.129Z"
transform="translate(0.00001 0)"
fill={`url(#${PaintServerIds.runningTriggerCube})`}
/>
<g opacity="0.5">
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</g>
<path
d="M.57144,24.91834a3.79909,3.79909,0,0,1,1.34825-1.32625L42.0989.50939a3.837,3.837,0,0,1,3.82081,0L86.09892,23.59206a3.79782,3.79782,0,0,1,1.4318,1.47169L44.00915,49.96733Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.3"
style={{ isolation: 'isolate' }}
/>
<path
d="M43.99984,50.03265V100a3.83392,3.83392,0,0,1-1.91024-.50933L1.91039,76.40791A3.79835,3.79835,0,0,1,0,73.11541V26.95a3.77423,3.77423,0,0,1,.56216-1.96625Z"
transform="translate(0.00001 0)"
fill="#353944"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</symbol>
<symbol viewBox="0 0 88 100" id={SymbolIds.terminatedProcessCube}>
<title>{'Terminated Process'}</title>
<path
d="M87.52113,24.73352a3.7956,3.7956,0,0,0-1.43182-1.47166L45.91012.17918a3.8365,3.8365,0,0,0-3.82049,0L1.91029,23.26186A3.86312,3.86312,0,0,0-.00009,26.55445V72.7199a3.79834,3.79834,0,0,0,1.91041,3.29249L42.08963,99.09514a3.83689,3.83689,0,0,0,3.82049,0L86.08931,76.01239a3.79852,3.79852,0,0,0,1.91056-3.29249V26.55445A3.77643,3.77643,0,0,0,87.52113,24.73352Z"
transform="translate(0.00013 0.39551)"
fill={isDarkMode ? '#010101' : '#fff'}
/>
<g opacity="0.7">
<path
opacity={isDarkMode ? 1 : 0.6}
d="M87.52113,24.73352a3.7956,3.7956,0,0,0-1.43182-1.47166L45.91012.17918a3.8365,3.8365,0,0,0-3.82049,0L1.91029,23.26186A3.86312,3.86312,0,0,0-.00009,26.55445V72.7199a3.79834,3.79834,0,0,0,1.91041,3.29249L42.08963,99.09514a3.83689,3.83689,0,0,0,3.82049,0L86.08931,76.01239a3.79852,3.79852,0,0,0,1.91056-3.29249V26.55445A3.77643,3.77643,0,0,0,87.52113,24.73352Z"
transform="translate(0.00013 0.39551)"
fill={`url(#${PaintServerIds.terminatedProcessCube})`}
/>
<path
opacity={isDarkMode ? 0.3 : 0.4}
d="M.57134,24.52282a3.79906,3.79906,0,0,1,1.34824-1.32625L42.09878.11387a3.83708,3.83708,0,0,1,3.8208,0L86.09877,23.19655a3.79771,3.79771,0,0,1,1.43182,1.47165L44.00909,49.57182Z"
transform="translate(0.00013 0.39551)"
fill="#fff"
style={{ isolation: 'isolate' }}
/>
<path
opacity={isDarkMode ? 0.2 : 0.4}
d="M43.99972,49.63713V99.60449a3.83406,3.83406,0,0,1-1.91025-.50932L1.91029,76.01239A3.79835,3.79835,0,0,1-.00013,72.7199V26.55445A3.77431,3.77431,0,0,1,.562,24.5882Z"
transform="translate(0.00013 0.39551)"
fill="#353944"
style={{ isolation: 'isolate' }}
/>
</g>
</symbol>
<symbol id={SymbolIds.terminatedTriggerCube} viewBox="0 0 88 100">
<title>{'Terminated Trigger Process'}</title>
{isDarkMode && (
<path
opacity="1"
d="M87.52143,25.06372a3.795,3.795,0,0,0-1.43129-1.47166L45.92578.50939a3.83384,3.83384,0,0,0-3.81907,0L1.94219,23.59206A3.8634,3.8634,0,0,0,.03252,26.88465V73.05008a3.7986,3.7986,0,0,0,1.90971,3.2925L42.10671,99.42532a3.83423,3.83423,0,0,0,3.81907,0L86.09014,76.34258A3.79881,3.79881,0,0,0,88,73.05008V26.88465A3.77748,3.77748,0,0,0,87.52143,25.06372Z"
transform="translate(0)"
fill="#010101"
/>
)}
<g opacity="0.6">
{!isDarkMode && (
<path
opacity="0.6"
d="M87.52143,25.06372a3.795,3.795,0,0,0-1.43129-1.47166L45.92578.50939a3.83384,3.83384,0,0,0-3.81907,0L1.94219,23.59206A3.8634,3.8634,0,0,0,.03252,26.88465V73.05008a3.7986,3.7986,0,0,0,1.90971,3.2925L42.10671,99.42532a3.83423,3.83423,0,0,0,3.81907,0L86.09014,76.34258A3.79881,3.79881,0,0,0,88,73.05008V26.88465A3.77748,3.77748,0,0,0,87.52143,25.06372Z"
transform="translate(0)"
fill="#010101"
/>
)}
<path
opacity={isDarkMode ? 1 : 0.604}
d="M87.48893,25.129a3.79468,3.79468,0,0,0-1.4313-1.47165L45.89329.57472a3.83381,3.83381,0,0,0-3.81908,0L1.90969,23.65739A3.86331,3.86331,0,0,0,0,26.95V73.11541a3.79859,3.79859,0,0,0,1.90969,3.2925L42.07421,99.49067a3.83425,3.83425,0,0,0,3.81908,0L86.05763,76.40791a3.79876,3.79876,0,0,0,1.90985-3.2925V26.95A3.77746,3.77746,0,0,0,87.48893,25.129Z"
transform="translate(0)"
fill={`url(#${PaintServerIds.terminatedTriggerCube})`}
/>
<path
d="M.57124,24.91834A3.79833,3.79833,0,0,1,1.919,23.59209L42.08335.50939a3.83441,3.83441,0,0,1,3.8194,0L86.06711,23.59206a3.7972,3.7972,0,0,1,1.43128,1.47169L43.99289,49.96733Z"
transform="translate(0)"
fill="#fff"
opacity="0.3"
style={{ isolation: 'isolate' }}
/>
<path
d="M43.98359,50.03265V100a3.83139,3.83139,0,0,1-1.90953-.50933L1.90969,76.40791A3.79859,3.79859,0,0,1,0,73.11541V26.95a3.77523,3.77523,0,0,1,.56195-1.96625Z"
transform="translate(0)"
fill="#353944"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</g>
</symbol>
<symbol viewBox="0 -3 88 106" id={SymbolIds.processCubeActiveBacking}>
<title>{'resolver active backing'}</title>
<path
d="m87.521 25.064a3.795 3.795 0 0 0-1.4313-1.4717l-40.164-23.083a3.8338 3.8338 0 0 0-3.8191 0l-40.165 23.083a3.8634 3.8634 0 0 0-1.9097 3.2926v46.165a3.7986 3.7986 0 0 0 1.9097 3.2925l40.164 23.083a3.8342 3.8342 0 0 0 3.8191 0l40.164-23.083a3.7988 3.7988 0 0 0 1.9099-3.2925v-46.165a3.7775 3.7775 0 0 0-0.47857-1.8209z"
strokeWidth="2"
/>
</symbol>
</>
));
/**
* This `<defs>` element is used to define the reusable assets for the Resolver
* It confers several 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)
*/
const SymbolDefinitionsComponent = memo(({ className }: { className?: string }) => {
const isDarkMode = useUiSetting<boolean>('theme:darkMode');
return (
<svg className={className}>
<defs>
<PaintServers isDarkMode={isDarkMode} />
<SymbolsAndShapes isDarkMode={isDarkMode} />
</defs>
</svg>
);
});
export const SymbolDefinitions = styled(SymbolDefinitionsComponent)`
position: absolute;
left: 100%;
top: 100%;
width: 0;
height: 0;
`;
const processTypeToCube: Record<ResolverProcessType, keyof NodeStyleMap> = {
processCreated: 'runningProcessCube',
processRan: 'runningProcessCube',
processTerminated: 'terminatedProcessCube',
unknownProcessEvent: 'runningProcessCube',
processCausedAlert: 'runningTriggerCube',
unknownEvent: 'runningProcessCube',
};
/**
* A hook to bring Resolver theming information into components.
*/
export const useResolverTheme = (): {
colorMap: ColorMap;
nodeAssets: NodeStyleMap;
cubeAssetsForNode: (isProcessTerimnated: boolean, isProcessTrigger: boolean) => NodeStyleConfig;
} => {
const isDarkMode = useUiSetting('theme:darkMode');
const theme = isDarkMode ? euiThemeAmsterdamDark : euiThemeAmsterdamLight;
const getThemedOption = (lightOption: string, darkOption: string): string => {
return isDarkMode ? darkOption : lightOption;
};
const colorMap = {
descriptionText: theme.euiTextColor,
full: theme.euiColorFullShade,
graphControls: theme.euiColorDarkestShade,
graphControlsBackground: theme.euiColorEmptyShade,
processBackingFill: `${theme.euiColorPrimary}${getThemedOption('0F', '1F')}`, // Add opacity 0F = 6% , 1F = 12%
resolverBackground: theme.euiColorEmptyShade,
resolverEdge: getThemedOption(theme.euiColorLightestShade, theme.euiColorLightShade),
resolverBreadcrumbBackground: theme.euiColorLightestShade,
resolverEdgeText: getThemedOption(theme.euiColorDarkShade, theme.euiColorFullShade),
triggerBackingFill: `${theme.euiColorDanger}${getThemedOption('0F', '1F')}`,
pillStroke: theme.euiColorLightShade,
};
const nodeAssets: NodeStyleMap = {
runningProcessCube: {
backingFill: colorMap.processBackingFill,
cubeSymbol: `#${SymbolIds.runningProcessCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate('xpack.securitySolution.endpoint.resolver.runningProcess', {
defaultMessage: 'Running Process',
}),
isLabelFilled: true,
labelButtonFill: 'primary',
strokeColor: theme.euiColorPrimary,
},
runningTriggerCube: {
backingFill: colorMap.triggerBackingFill,
cubeSymbol: `#${SymbolIds.runningTriggerCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate('xpack.securitySolution.endpoint.resolver.runningTrigger', {
defaultMessage: 'Running Trigger',
}),
isLabelFilled: true,
labelButtonFill: 'danger',
strokeColor: theme.euiColorDanger,
},
terminatedProcessCube: {
backingFill: colorMap.processBackingFill,
cubeSymbol: `#${SymbolIds.terminatedProcessCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate(
'xpack.securitySolution.endpoint.resolver.terminatedProcess',
{
defaultMessage: 'Terminated Process',
}
),
isLabelFilled: false,
labelButtonFill: 'primary',
strokeColor: theme.euiColorPrimary,
},
terminatedTriggerCube: {
backingFill: colorMap.triggerBackingFill,
cubeSymbol: `#${SymbolIds.terminatedTriggerCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate(
'xpack.securitySolution.endpoint.resolver.terminatedTrigger',
{
defaultMessage: 'Terminated Trigger',
}
),
isLabelFilled: false,
labelButtonFill: 'danger',
strokeColor: theme.euiColorDanger,
},
};
function cubeAssetsForNode(isProcessTerminated: boolean, isProcessTrigger: boolean) {
if (isProcessTerminated) {
if (isProcessTrigger) {
return nodeAssets.terminatedTriggerCube;
} else {
return nodeAssets[processTypeToCube.processTerminated];
}
} else if (isProcessTrigger) {
return nodeAssets[processTypeToCube.processCausedAlert];
} else {
return nodeAssets[processTypeToCube.processRan];
}
}
return { colorMap, nodeAssets, cubeAssetsForNode };
};
export const calculateResolverFontSize = (
magFactorX: number,
minFontSize: number,
slopeOfFontScale: number
): number => {
const fontSizeAdjustmentForScale = magFactorX > 1 ? slopeOfFontScale * (magFactorX - 1) : 0;
return minFontSize + fontSizeAdjustmentForScale;
};

View file

@ -9,7 +9,8 @@ import styled from 'styled-components';
import { FormattedMessage } from '@kbn/i18n/react';
import { applyMatrix3, distance, angle } from '../models/vector2';
import { Vector2, Matrix3, EdgeLineMetadata } from '../types';
import { useResolverTheme, calculateResolverFontSize } from './assets';
import { fontSize } from './font_size';
import { useColors } from './use_colors';
interface StyledEdgeLine {
readonly resolverEdgeColor: string;
@ -19,7 +20,7 @@ interface StyledEdgeLine {
const StyledEdgeLine = styled.div<StyledEdgeLine>`
position: absolute;
height: ${(props) => {
return `${calculateResolverFontSize(props.magFactorX, 12, 8.5)}px`;
return `${fontSize(props.magFactorX, 12, 8.5)}px`;
}};
background-color: ${(props) => props.resolverEdgeColor};
`;
@ -87,8 +88,8 @@ const EdgeLineComponent = React.memo(
*/
const screenStart = applyMatrix3(startPosition, projectionMatrix);
const screenEnd = applyMatrix3(endPosition, projectionMatrix);
const [magFactorX] = projectionMatrix;
const { colorMap } = useResolverTheme();
const [xScale] = projectionMatrix;
const colorMap = useColors();
const elapsedTime = edgeLineMetadata?.elapsedTime;
/**
@ -96,7 +97,7 @@ const EdgeLineComponent = React.memo(
* should be the same as the distance between the start and end points.
*/
const length = distance(screenStart, screenEnd);
const scaledTypeSize = calculateResolverFontSize(magFactorX, 10, 7.5);
const scaledTypeSize = fontSize(xScale, 10, 7.5);
const style = {
left: `${screenStart[0]}px`,
@ -120,8 +121,8 @@ const EdgeLineComponent = React.memo(
/**
* Calculates a fractional offset from 0 -> 5% as magFactorX decreases from 1 to a min of .5
*/
if (magFactorX < 1) {
const fractionalOffset = (1 / magFactorX) * ((1 - magFactorX) * 10);
if (xScale < 1) {
const fractionalOffset = (1 / xScale) * ((1 - xScale) * 10);
elapsedTimeLeftPosPct += fractionalOffset;
}
@ -130,7 +131,7 @@ const EdgeLineComponent = React.memo(
className={className}
style={style}
resolverEdgeColor={colorMap.resolverEdge}
magFactorX={magFactorX}
magFactorX={xScale}
data-test-subj="resolver:graph:edgeline"
>
{elapsedTime && (

View file

@ -0,0 +1,12 @@
/*
* 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.
*/
/**
* Return a font-size based on a scale, minimum size, and a coefficient.
*/
export function fontSize(scale: number, minimum: number, slope: number): number {
return minimum + (scale > 1 ? slope * (scale - 1) : 0);
}

View file

@ -13,8 +13,8 @@ import { useSelector, useDispatch } from 'react-redux';
import { SideEffectContext } from './side_effect_context';
import { Vector2 } from '../types';
import * as selectors from '../store/selectors';
import { useResolverTheme } from './assets';
import { ResolverAction } from '../store/actions';
import { useColors } from './use_colors';
interface StyledGraphControls {
graphControlsBackground: string;
@ -66,7 +66,7 @@ const GraphControlsComponent = React.memo(
const dispatch: (action: ResolverAction) => unknown = useDispatch();
const scalingFactor = useSelector(selectors.scalingFactor);
const { timestamp } = useContext(SideEffectContext);
const { colorMap } = useResolverTheme();
const colorMap = useColors();
const handleZoomAmountChange = useCallback(
(event: React.ChangeEvent<HTMLInputElement> | React.MouseEvent<HTMLButtonElement>) => {

View file

@ -11,11 +11,12 @@ import { i18n } from '@kbn/i18n';
/* eslint-disable react/display-name */
import React, { memo } from 'react';
import { useResolverTheme, SymbolIds } from '../assets';
interface StyledSVGCube {
readonly isOrigin?: boolean;
}
import { useCubeAssets } from '../use_cube_assets';
import { useSymbolIDs } from '../use_symbol_ids';
/**
* Icon representing a process node.
@ -34,8 +35,8 @@ export const CubeForProcess = memo(function ({
isOrigin?: boolean;
className?: string;
}) {
const { cubeAssetsForNode } = useResolverTheme();
const { cubeSymbol, strokeColor } = cubeAssetsForNode(!running, false);
const { cubeSymbol, strokeColor } = useCubeAssets(!running, false);
const { processCubeActiveBacking } = useSymbolIDs();
return (
<StyledSVG
@ -54,7 +55,7 @@ export const CubeForProcess = memo(function ({
</desc>
{isOrigin && (
<use
xlinkHref={`#${SymbolIds.processCubeActiveBacking}`}
xlinkHref={`#${processCubeActiveBacking}`}
fill="transparent"
x={0}
y={-1}

View file

@ -27,7 +27,7 @@ import {
} from '../../models/process_event';
import { CubeForProcess } from './cube_for_process';
import { ResolverEvent } from '../../../../common/endpoint/types';
import { useResolverTheme } from '../assets';
import { useCubeAssets } from '../use_cube_assets';
import { ResolverState } from '../../types';
import { PanelLoading } from './panel_loading';
import { StyledPanel } from '../styles';
@ -166,13 +166,7 @@ const NodeDetailView = memo(function NodeDetailView({
},
];
}, [processName, nodesLinkNavProps]);
const { cubeAssetsForNode } = useResolverTheme();
const { descriptionText } = useMemo(() => {
if (!processEvent) {
return { descriptionText: '' };
}
return cubeAssetsForNode(isProcessTerminated, false);
}, [processEvent, cubeAssetsForNode, isProcessTerminated]);
const { descriptionText } = useCubeAssets(isProcessTerminated, false);
const nodeDetailHref = useSelector((state: ResolverState) =>
selectors.relativeHref(state)({

View file

@ -26,7 +26,7 @@ import { SafeResolverEvent } from '../../../../common/endpoint/types';
import { LimitWarning } from '../limit_warnings';
import { ResolverState } from '../../types';
import { useNavigateOrReplace } from '../use_navigate_or_replace';
import { useResolverTheme } from '../assets';
import { useColors } from '../use_colors';
const StyledLimitWarning = styled(LimitWarning)`
flex-flow: row wrap;
@ -208,9 +208,7 @@ function NodeDetailLink({ name, item }: { name: string; item: ProcessTableView }
const isTerminated = useSelector((state: ResolverState) =>
entityID === undefined ? false : selectors.isProcessTerminated(state)(entityID)
);
const {
colorMap: { descriptionText },
} = useResolverTheme();
const { descriptionText } = useColors();
return (
<EuiButtonEmpty {...useNavigateOrReplace({ search: item.href })}>
{name === '' ? (

View file

@ -4,11 +4,13 @@
* you may not use this file except in compliance with the Elastic License.
*/
/* eslint-disable react/display-name */
import { i18n } from '@kbn/i18n';
import { EuiBreadcrumbs, EuiCode, EuiBetaBadge } from '@elastic/eui';
import styled from 'styled-components';
import React, { memo } from 'react';
import { useResolverTheme } from '../assets';
import { useColors } from '../use_colors';
/**
* A bold version of EuiCode to display certain titles with
@ -63,7 +65,7 @@ export const GeneratedText = React.memo(function ({ children }) {
valueSplitByWordBoundaries[0],
...valueSplitByWordBoundaries
.splice(1)
.reduce(function (generatedTextMemo: Array<string | JSX.Element>, value, index) {
.reduce(function (generatedTextMemo: Array<string | JSX.Element>, value) {
return [...generatedTextMemo, value, <wbr />];
}, []),
];
@ -73,7 +75,6 @@ export const GeneratedText = React.memo(function ({ children }) {
});
}
});
GeneratedText.displayName = 'GeneratedText';
/**
* A component to keep time representations in blocks so they don't wrap
@ -93,9 +94,7 @@ export const StyledBreadcrumbs = memo(function StyledBreadcrumbs({
}: {
breadcrumbs: Breadcrumbs;
}) {
const {
colorMap: { resolverBreadcrumbBackground, resolverEdgeText },
} = useResolverTheme();
const { resolverBreadcrumbBackground, resolverEdgeText } = useColors();
return (
<>
<BetaHeader>

View file

@ -12,12 +12,15 @@ import { FormattedMessage } from '@kbn/i18n/react';
import { NodeSubMenu } from './submenu';
import { applyMatrix3 } from '../models/vector2';
import { Vector2, Matrix3, ResolverState } from '../types';
import { SymbolIds, useResolverTheme, calculateResolverFontSize } from './assets';
import { ResolverEvent, SafeResolverEvent } from '../../../common/endpoint/types';
import { useResolverDispatch } from './use_resolver_dispatch';
import * as eventModel from '../../../common/endpoint/models/event';
import * as selectors from '../store/selectors';
import { useNavigateOrReplace } from './use_navigate_or_replace';
import { fontSize } from './font_size';
import { useCubeAssets } from './use_cube_assets';
import { useSymbolIDs } from './use_symbol_ids';
import { useColors } from './use_colors';
interface StyledActionsContainer {
readonly color: string;
@ -108,6 +111,8 @@ const UnstyledProcessEventDot = React.memo(
// This should be unique to each instance of Resolver
const htmlIDPrefix = `resolver:${resolverComponentInstanceID}`;
const symbolIDs = useSymbolIDs();
/**
* Convert the position, which is in 'world' coordinates, to screen coordinates.
*/
@ -191,7 +196,7 @@ const UnstyledProcessEventDot = React.memo(
* 18.75 : The smallest readable font size at which labels/descriptions can be read. Font size will not scale below this.
* 12.5 : A 'slope' at which the font size will scale w.r.t. to zoom level otherwise
*/
const scaledTypeSize = calculateResolverFontSize(xScale, 18.75, 12.5);
const scaledTypeSize = fontSize(xScale, 18.75, 12.5);
const markerBaseSize = 15;
const markerSize = markerBaseSize;
@ -212,7 +217,7 @@ const UnstyledProcessEventDot = React.memo(
})
| null;
} = React.createRef();
const { colorMap, cubeAssetsForNode } = useResolverTheme();
const colorMap = useColors();
const {
backingFill,
cubeSymbol,
@ -220,7 +225,7 @@ const UnstyledProcessEventDot = React.memo(
isLabelFilled,
labelButtonFill,
strokeColor,
} = cubeAssetsForNode(
} = useCubeAssets(
isProcessTerminated,
/**
* There is no definition for 'trigger process' yet. return false.
@ -323,7 +328,7 @@ const UnstyledProcessEventDot = React.memo(
>
<StyledOuterGroup>
<use
xlinkHref={`#${SymbolIds.processCubeActiveBacking}`}
xlinkHref={`#${symbolIDs.processCubeActiveBacking}`}
fill={backingFill} // Only visible on hover
x={-15.35}
y={-15.35}
@ -334,7 +339,7 @@ const UnstyledProcessEventDot = React.memo(
/>
{isOrigin && (
<use
xlinkHref={`#${SymbolIds.processCubeActiveBacking}`}
xlinkHref={`#${symbolIDs.processCubeActiveBacking}`}
fill="transparent" // Transparent so we don't double up on the default hover
x={-15.35}
y={-15.35}

View file

@ -16,13 +16,14 @@ import { EdgeLine } from './edge_line';
import { GraphControls } from './graph_controls';
import { ProcessEventDot } from './process_event_dot';
import { useCamera } from './use_camera';
import { SymbolDefinitions, useResolverTheme } from './assets';
import { SymbolDefinitions } from './symbol_definitions';
import { useStateSyncingActions } from './use_state_syncing_actions';
import { StyledMapContainer, GraphContainer } from './styles';
import { entityIDSafeVersion } from '../../../common/endpoint/models/event';
import { SideEffectContext } from './side_effect_context';
import { ResolverProps, ResolverState } from '../types';
import { PanelRouter } from './panels';
import { useColors } from './use_colors';
/**
* The highest level connected Resolver component. Needs a `Provider` in its ancestry to work.
@ -73,7 +74,7 @@ export const ResolverWithoutProviders = React.memo(
const isLoading = useSelector(selectors.isTreeLoading);
const hasError = useSelector(selectors.hadErrorLoadingTree);
const activeDescendantId = useSelector(selectors.ariaActiveDescendant);
const { colorMap } = useResolverTheme();
const colorMap = useColors();
return (
<StyledMapContainer className={className} backgroundColor={colorMap.resolverBackground}>

View file

@ -13,7 +13,7 @@ import styled from 'styled-components';
import { ResolverNodeStats } from '../../../common/endpoint/types';
import { useRelatedEventByCategoryNavigation } from './use_related_event_by_category_navigation';
import { Matrix3 } from '../types';
import { useResolverTheme } from './assets';
import { useColors } from './use_colors';
/**
* i18n-translated titles for submenus and identifiers for display of states:
@ -182,9 +182,7 @@ const NodeSubMenuComponents = React.memo(
// no matter what, keep track of the last project matrix that was used to size the popover
projectionMatrixAtLastRender.current = projectionMatrix;
}, [projectionMatrixAtLastRender, projectionMatrix]);
const {
colorMap: { pillStroke: pillBorderStroke, resolverBackground: pillFill },
} = useResolverTheme();
const { pillStroke: pillBorderStroke, resolverBackground: pillFill } = useColors();
const listStylesFromTheme = useMemo(() => {
return {
border: `1.5px solid ${pillBorderStroke}`,
@ -239,6 +237,7 @@ const NodeSubMenuComponents = React.memo(
className="item"
data-test-subj="resolver:map:node-submenu-item"
style={listStylesFromTheme}
key={opt.optionTitle}
>
<button type="button" className="kbn-resetFocusState" onClick={opt.action}>
{opt.prefix} {opt.optionTitle}

View file

@ -0,0 +1,354 @@
/*
* 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.
*/
/* eslint-disable react/display-name */
import React, { memo } from 'react';
import styled from 'styled-components';
import { useUiSetting } from '../../../../../../src/plugins/kibana_react/public';
import { useSymbolIDs } from './use_symbol_ids';
import { usePaintServerIDs } from './use_paint_server_ids';
/**
* PaintServers: Where color palettes, gradients, patterns and other similar concerns
* are exposed to the component
*/
const PaintServers = memo(({ isDarkMode }: { isDarkMode: boolean }) => {
const paintServerIDs = usePaintServerIDs();
return (
<>
<linearGradient
id={paintServerIDs.runningProcessCube}
x1="-381.23556"
y1="264.73802"
x2="-380.48514"
y2="263.8816"
gradientTransform="matrix(70.05179, 0, 0, -79.94774, 26724.01618, 21181.09848)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0.00087" stopColor="#006bb4" />
<stop offset="1" stopColor="#54b399" />
</linearGradient>
<linearGradient
id={paintServerIDs.runningTriggerCube}
x1="-381.18643"
y1="264.68195"
x2="-380.48514"
y2="263.8816"
gradientTransform="matrix(70.05179, 0, 0, -79.94774, 26724.01618, 21181.09848)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#dd0a73" />
<stop offset="1" stopColor="#f66" />
</linearGradient>
{isDarkMode ? (
<>
<linearGradient
id={paintServerIDs.terminatedProcessCube}
x1="-381.23752"
y1="264.24026"
x2="-380.48514"
y2="263.3816"
gradientTransform="matrix(70.05178, 0, 0, -79.94771, 26724.01313, 21140.72096)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#4c82c3" />
<stop offset="1" stopColor="#8bd1c7" />
</linearGradient>
<linearGradient
id={paintServerIDs.terminatedTriggerCube}
x1="-381.18658"
y1="264.68187"
x2="-380.48546"
y2="263.8817"
gradientTransform="matrix(70.05179, 0, 0, -79.94774, 26724.01618, 21181.09848)"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#dd0a73" />
<stop offset="1" stopColor="#f66" />
</linearGradient>
</>
) : (
<>
<linearGradient
id={paintServerIDs.terminatedProcessCube}
x1="10.5206"
y1="9.49068"
x2="46.8141"
y2="45.7844"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stopColor="#2F6EB6" />
<stop offset="1" stopColor="#00B4AC" />
</linearGradient>
<linearGradient
id={paintServerIDs.terminatedTriggerCube}
x1="15.4848"
y1="12.0468"
x2="43.1049"
y2="47.2331"
gradientUnits="userSpaceOnUse"
>
<stop stopColor="#DD0A73" />
<stop offset="1" stopColor="#FF6666" />
</linearGradient>
</>
)}
</>
);
});
/**
* Defs entries that define shapes, masks and other spatial elements
*/
const SymbolsAndShapes = memo(({ isDarkMode }: { isDarkMode: boolean }) => {
const symbolIDs = useSymbolIDs();
const paintServerIDs = usePaintServerIDs();
return (
<>
<symbol
id={symbolIDs.processNodeLabel}
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.runningProcessCube} viewBox="0 0 88 100">
<title>{'Running Process'}</title>
<path
d="M87.52127,25.129a3.79536,3.79536,0,0,0-1.43184-1.47165L45.91025.57471a3.83652,3.83652,0,0,0-3.8205,0L1.91039,23.65739A3.86308,3.86308,0,0,0,0,26.95V73.11541a3.79835,3.79835,0,0,0,1.9104,3.2925L42.08975,99.49067a3.83691,3.83691,0,0,0,3.8205,0L86.08943,76.40791A3.79852,3.79852,0,0,0,88,73.11541V26.95A3.77641,3.77641,0,0,0,87.52127,25.129Z"
transform="translate(0.00001 0)"
fill={`url(#${paintServerIDs.runningProcessCube})`}
/>
<g opacity="0.5">
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</g>
<path
d="M.57144,24.91834a3.79909,3.79909,0,0,1,1.34825-1.32625L42.0989.50939a3.837,3.837,0,0,1,3.82081,0L86.09892,23.59206a3.79782,3.79782,0,0,1,1.4318,1.47169L44.00915,49.96733Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.3"
style={{ isolation: 'isolate' }}
/>
<path
d="M43.99984,50.03265V100a3.83392,3.83392,0,0,1-1.91024-.50933L1.91039,76.40791A3.79835,3.79835,0,0,1,0,73.11541V26.95a3.77423,3.77423,0,0,1,.56216-1.96625Z"
transform="translate(0.00001 0)"
fill="#353944"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</symbol>
<symbol id={symbolIDs.runningTriggerCube} viewBox="0 0 88 100">
<title>{'resolver_dark process running'}</title>
<path
d="M87.52127,25.129a3.79536,3.79536,0,0,0-1.43184-1.47165L45.91025.57471a3.83652,3.83652,0,0,0-3.8205,0L1.91039,23.65739A3.86308,3.86308,0,0,0,0,26.95V73.11541a3.79835,3.79835,0,0,0,1.9104,3.2925L42.08975,99.49067a3.83691,3.83691,0,0,0,3.8205,0L86.08943,76.40791A3.79852,3.79852,0,0,0,88,73.11541V26.95A3.77641,3.77641,0,0,0,87.52127,25.129Z"
transform="translate(0.00001 0)"
fill={`url(#${paintServerIDs.runningTriggerCube})`}
/>
<g opacity="0.5">
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M59.18326,40.255,44.93983,32.07224a1.7853,1.7853,0,0,0-1.77779,0L28.91861,40.255a1.77022,1.77022,0,0,0-.64527.64058L44.0088,49.96977,59.838,40.91219a1.77,1.77,0,0,0-.65469-.65719Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M28.27334,40.89555a1.75837,1.75837,0,0,0-.24347.89149V58.1525a1.76751,1.76751,0,0,0,.88874,1.532L43.162,67.86729a1.77951,1.77951,0,0,0,.84679.2316V49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
/>
<path
d="M44.0088,68.0989a1.7772,1.7772,0,0,0,.931-.2316l14.24344-8.18274a1.76754,1.76754,0,0,0,.889-1.532V41.787a1.76037,1.76037,0,0,0-.23432-.87485L44.0088,49.96977Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</g>
<path
d="M.57144,24.91834a3.79909,3.79909,0,0,1,1.34825-1.32625L42.0989.50939a3.837,3.837,0,0,1,3.82081,0L86.09892,23.59206a3.79782,3.79782,0,0,1,1.4318,1.47169L44.00915,49.96733Z"
transform="translate(0.00001 0)"
fill="#fff"
opacity="0.3"
style={{ isolation: 'isolate' }}
/>
<path
d="M43.99984,50.03265V100a3.83392,3.83392,0,0,1-1.91024-.50933L1.91039,76.40791A3.79835,3.79835,0,0,1,0,73.11541V26.95a3.77423,3.77423,0,0,1,.56216-1.96625Z"
transform="translate(0.00001 0)"
fill="#353944"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</symbol>
<symbol viewBox="0 0 88 100" id={symbolIDs.terminatedProcessCube}>
<title>{'Terminated Process'}</title>
<path
d="M87.52113,24.73352a3.7956,3.7956,0,0,0-1.43182-1.47166L45.91012.17918a3.8365,3.8365,0,0,0-3.82049,0L1.91029,23.26186A3.86312,3.86312,0,0,0-.00009,26.55445V72.7199a3.79834,3.79834,0,0,0,1.91041,3.29249L42.08963,99.09514a3.83689,3.83689,0,0,0,3.82049,0L86.08931,76.01239a3.79852,3.79852,0,0,0,1.91056-3.29249V26.55445A3.77643,3.77643,0,0,0,87.52113,24.73352Z"
transform="translate(0.00013 0.39551)"
fill={isDarkMode ? '#010101' : '#fff'}
/>
<g opacity="0.7">
<path
opacity={isDarkMode ? 1 : 0.6}
d="M87.52113,24.73352a3.7956,3.7956,0,0,0-1.43182-1.47166L45.91012.17918a3.8365,3.8365,0,0,0-3.82049,0L1.91029,23.26186A3.86312,3.86312,0,0,0-.00009,26.55445V72.7199a3.79834,3.79834,0,0,0,1.91041,3.29249L42.08963,99.09514a3.83689,3.83689,0,0,0,3.82049,0L86.08931,76.01239a3.79852,3.79852,0,0,0,1.91056-3.29249V26.55445A3.77643,3.77643,0,0,0,87.52113,24.73352Z"
transform="translate(0.00013 0.39551)"
fill={`url(#${paintServerIDs.terminatedProcessCube})`}
/>
<path
opacity={isDarkMode ? 0.3 : 0.4}
d="M.57134,24.52282a3.79906,3.79906,0,0,1,1.34824-1.32625L42.09878.11387a3.83708,3.83708,0,0,1,3.8208,0L86.09877,23.19655a3.79771,3.79771,0,0,1,1.43182,1.47165L44.00909,49.57182Z"
transform="translate(0.00013 0.39551)"
fill="#fff"
style={{ isolation: 'isolate' }}
/>
<path
opacity={isDarkMode ? 0.2 : 0.4}
d="M43.99972,49.63713V99.60449a3.83406,3.83406,0,0,1-1.91025-.50932L1.91029,76.01239A3.79835,3.79835,0,0,1-.00013,72.7199V26.55445A3.77431,3.77431,0,0,1,.562,24.5882Z"
transform="translate(0.00013 0.39551)"
fill="#353944"
style={{ isolation: 'isolate' }}
/>
</g>
</symbol>
<symbol id={symbolIDs.terminatedTriggerCube} viewBox="0 0 88 100">
<title>{'Terminated Trigger Process'}</title>
{isDarkMode && (
<path
opacity="1"
d="M87.52143,25.06372a3.795,3.795,0,0,0-1.43129-1.47166L45.92578.50939a3.83384,3.83384,0,0,0-3.81907,0L1.94219,23.59206A3.8634,3.8634,0,0,0,.03252,26.88465V73.05008a3.7986,3.7986,0,0,0,1.90971,3.2925L42.10671,99.42532a3.83423,3.83423,0,0,0,3.81907,0L86.09014,76.34258A3.79881,3.79881,0,0,0,88,73.05008V26.88465A3.77748,3.77748,0,0,0,87.52143,25.06372Z"
transform="translate(0)"
fill="#010101"
/>
)}
<g opacity="0.6">
{!isDarkMode && (
<path
opacity="0.6"
d="M87.52143,25.06372a3.795,3.795,0,0,0-1.43129-1.47166L45.92578.50939a3.83384,3.83384,0,0,0-3.81907,0L1.94219,23.59206A3.8634,3.8634,0,0,0,.03252,26.88465V73.05008a3.7986,3.7986,0,0,0,1.90971,3.2925L42.10671,99.42532a3.83423,3.83423,0,0,0,3.81907,0L86.09014,76.34258A3.79881,3.79881,0,0,0,88,73.05008V26.88465A3.77748,3.77748,0,0,0,87.52143,25.06372Z"
transform="translate(0)"
fill="#010101"
/>
)}
<path
opacity={isDarkMode ? 1 : 0.604}
d="M87.48893,25.129a3.79468,3.79468,0,0,0-1.4313-1.47165L45.89329.57472a3.83381,3.83381,0,0,0-3.81908,0L1.90969,23.65739A3.86331,3.86331,0,0,0,0,26.95V73.11541a3.79859,3.79859,0,0,0,1.90969,3.2925L42.07421,99.49067a3.83425,3.83425,0,0,0,3.81908,0L86.05763,76.40791a3.79876,3.79876,0,0,0,1.90985-3.2925V26.95A3.77746,3.77746,0,0,0,87.48893,25.129Z"
transform="translate(0)"
fill={`url(#${paintServerIDs.terminatedTriggerCube})`}
/>
<path
d="M.57124,24.91834A3.79833,3.79833,0,0,1,1.919,23.59209L42.08335.50939a3.83441,3.83441,0,0,1,3.8194,0L86.06711,23.59206a3.7972,3.7972,0,0,1,1.43128,1.47169L43.99289,49.96733Z"
transform="translate(0)"
fill="#fff"
opacity="0.3"
style={{ isolation: 'isolate' }}
/>
<path
d="M43.98359,50.03265V100a3.83139,3.83139,0,0,1-1.90953-.50933L1.90969,76.40791A3.79859,3.79859,0,0,1,0,73.11541V26.95a3.77523,3.77523,0,0,1,.56195-1.96625Z"
transform="translate(0)"
fill="#353944"
opacity="0.2"
style={{ isolation: 'isolate' }}
/>
</g>
</symbol>
<symbol viewBox="0 -3 88 106" id={symbolIDs.processCubeActiveBacking}>
<title>{'resolver active backing'}</title>
<path
d="m87.521 25.064a3.795 3.795 0 0 0-1.4313-1.4717l-40.164-23.083a3.8338 3.8338 0 0 0-3.8191 0l-40.165 23.083a3.8634 3.8634 0 0 0-1.9097 3.2926v46.165a3.7986 3.7986 0 0 0 1.9097 3.2925l40.164 23.083a3.8342 3.8342 0 0 0 3.8191 0l40.164-23.083a3.7988 3.7988 0 0 0 1.9099-3.2925v-46.165a3.7775 3.7775 0 0 0-0.47857-1.8209z"
strokeWidth="2"
/>
</symbol>
</>
);
});
/**
* This `<defs>` element is used to define the reusable assets for the Resolver
* It confers several 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(() => {
const isDarkMode = useUiSetting<boolean>('theme:darkMode');
return (
<HiddenSVG>
<defs>
<PaintServers isDarkMode={isDarkMode} />
<SymbolsAndShapes isDarkMode={isDarkMode} />
</defs>
</HiddenSVG>
);
});
const HiddenSVG = styled('svg')`
position: absolute;
left: 100%;
top: 100%;
width: 0;
height: 0;
`;

View file

@ -0,0 +1,47 @@
/*
* 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 euiThemeAmsterdamDark from '@elastic/eui/dist/eui_theme_amsterdam_dark.json';
import euiThemeAmsterdamLight from '@elastic/eui/dist/eui_theme_amsterdam_light.json';
import { useMemo } from 'react';
import { useUiSetting } from '../../../../../../src/plugins/kibana_react/public';
type ResolverColorNames =
| 'descriptionText'
| 'full'
| 'graphControls'
| 'graphControlsBackground'
| 'resolverBackground'
| 'resolverEdge'
| 'resolverEdgeText'
| 'resolverBreadcrumbBackground'
| 'pillStroke'
| 'triggerBackingFill'
| 'processBackingFill';
type ColorMap = Record<ResolverColorNames, string>;
/**
* Get access to Kibana-theme based colors.
*/
export function useColors(): ColorMap {
const isDarkMode = useUiSetting('theme:darkMode');
const theme = isDarkMode ? euiThemeAmsterdamDark : euiThemeAmsterdamLight;
return useMemo(() => {
return {
descriptionText: theme.euiTextColor,
full: theme.euiColorFullShade,
graphControls: theme.euiColorDarkestShade,
graphControlsBackground: theme.euiColorEmptyShade,
processBackingFill: `${theme.euiColorPrimary}${isDarkMode ? '1F' : '0F'}`, // Add opacity 0F = 6% , 1F = 12%
resolverBackground: theme.euiColorEmptyShade,
resolverEdge: isDarkMode ? theme.euiColorLightShade : theme.euiColorLightestShade,
resolverBreadcrumbBackground: theme.euiColorLightestShade,
resolverEdgeText: isDarkMode ? theme.euiColorFullShade : theme.euiColorDarkShade,
triggerBackingFill: `${theme.euiColorDanger}${isDarkMode ? '1F' : '0F'}`,
pillStroke: theme.euiColorLightShade,
};
}, [isDarkMode, theme]);
}

View file

@ -0,0 +1,121 @@
/*
* 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 { i18n } from '@kbn/i18n';
import { ButtonColor } from '@elastic/eui';
import euiThemeAmsterdamDark from '@elastic/eui/dist/eui_theme_amsterdam_dark.json';
import euiThemeAmsterdamLight from '@elastic/eui/dist/eui_theme_amsterdam_light.json';
import { useMemo } from 'react';
import { ResolverProcessType } from '../types';
import { useUiSetting } from '../../../../../../src/plugins/kibana_react/public';
import { useSymbolIDs } from './use_symbol_ids';
import { useColors } from './use_colors';
/**
* Provides colors and HTML IDs used to render the 'cube' graphic that accompanies nodes.
*/
export function useCubeAssets(
isProcessTerminated: boolean,
isProcessTrigger: boolean
): NodeStyleConfig {
const SymbolIds = useSymbolIDs();
const isDarkMode = useUiSetting('theme:darkMode');
const theme = isDarkMode ? euiThemeAmsterdamDark : euiThemeAmsterdamLight;
const colorMap = useColors();
const nodeAssets: NodeStyleMap = useMemo(
() => ({
runningProcessCube: {
backingFill: colorMap.processBackingFill,
cubeSymbol: `#${SymbolIds.runningProcessCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate('xpack.securitySolution.endpoint.resolver.runningProcess', {
defaultMessage: 'Running Process',
}),
isLabelFilled: true,
labelButtonFill: 'primary',
strokeColor: theme.euiColorPrimary,
},
runningTriggerCube: {
backingFill: colorMap.triggerBackingFill,
cubeSymbol: `#${SymbolIds.runningTriggerCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate('xpack.securitySolution.endpoint.resolver.runningTrigger', {
defaultMessage: 'Running Trigger',
}),
isLabelFilled: true,
labelButtonFill: 'danger',
strokeColor: theme.euiColorDanger,
},
terminatedProcessCube: {
backingFill: colorMap.processBackingFill,
cubeSymbol: `#${SymbolIds.terminatedProcessCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate(
'xpack.securitySolution.endpoint.resolver.terminatedProcess',
{
defaultMessage: 'Terminated Process',
}
),
isLabelFilled: false,
labelButtonFill: 'primary',
strokeColor: theme.euiColorPrimary,
},
terminatedTriggerCube: {
backingFill: colorMap.triggerBackingFill,
cubeSymbol: `#${SymbolIds.terminatedTriggerCube}`,
descriptionFill: colorMap.descriptionText,
descriptionText: i18n.translate(
'xpack.securitySolution.endpoint.resolver.terminatedTrigger',
{
defaultMessage: 'Terminated Trigger',
}
),
isLabelFilled: false,
labelButtonFill: 'danger',
strokeColor: theme.euiColorDanger,
},
}),
[SymbolIds, colorMap, theme]
);
if (isProcessTerminated) {
if (isProcessTrigger) {
return nodeAssets.terminatedTriggerCube;
} else {
return nodeAssets[processTypeToCube.processTerminated];
}
} else if (isProcessTrigger) {
return nodeAssets[processTypeToCube.processCausedAlert];
} else {
return nodeAssets[processTypeToCube.processRan];
}
}
const processTypeToCube: Record<ResolverProcessType, keyof NodeStyleMap> = {
processCreated: 'runningProcessCube',
processRan: 'runningProcessCube',
processTerminated: 'terminatedProcessCube',
unknownProcessEvent: 'runningProcessCube',
processCausedAlert: 'runningTriggerCube',
unknownEvent: 'runningProcessCube',
};
interface NodeStyleMap {
runningProcessCube: NodeStyleConfig;
runningTriggerCube: NodeStyleConfig;
terminatedProcessCube: NodeStyleConfig;
terminatedTriggerCube: NodeStyleConfig;
}
interface NodeStyleConfig {
backingFill: string;
cubeSymbol: string;
descriptionFill: string;
descriptionText: string;
isLabelFilled: boolean;
labelButtonFill: ButtonColor;
strokeColor: string;
}

View file

@ -0,0 +1,28 @@
/*
* 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 { useMemo } from 'react';
import { useSelector } from 'react-redux';
import * as selectors from '../store/selectors';
/**
* Access the HTML IDs for this Resolver's reusable SVG 'paint servers'.
* In the future these IDs may come from an outside provider (and may be shared by multiple Resolver instances.)
*/
export function usePaintServerIDs() {
const resolverComponentInstanceID = useSelector(selectors.resolverComponentInstanceID);
return useMemo(() => {
const prefix = `${resolverComponentInstanceID}-symbols`;
return {
runningProcessCube: `${prefix}-psRunningProcessCube`,
runningTriggerCube: `${prefix}-psRunningTriggerCube`,
terminatedProcessCube: `${prefix}-psTerminatedProcessCube`,
terminatedTriggerCube: `${prefix}-psTerminatedTriggerCube`,
};
}, [resolverComponentInstanceID]);
}

View file

@ -0,0 +1,30 @@
/*
* 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 { useMemo } from 'react';
import { useSelector } from 'react-redux';
import * as selectors from '../store/selectors';
/**
* Access the HTML IDs for this Resolver's reusable SVG symbols.
* In the future these IDs may come from an outside provider (and may be shared by multiple Resolver instances.)
*/
export function useSymbolIDs() {
const resolverComponentInstanceID = useSelector(selectors.resolverComponentInstanceID);
return useMemo(() => {
const prefix = `${resolverComponentInstanceID}-symbols`;
return {
processNodeLabel: `${prefix}-nodeSymbol`,
runningProcessCube: `${prefix}-runningCube`,
runningTriggerCube: `${prefix}-runningTriggerCube`,
terminatedProcessCube: `${prefix}-terminatedCube`,
terminatedTriggerCube: `${prefix}-terminatedTriggerCube`,
processCubeActiveBacking: `${prefix}-activeBacking`,
};
}, [resolverComponentInstanceID]);
}