).target.value
+ );
+ if (isNaN(valueAsNumber) === false) {
+ dispatch(
+ userSetZoomLevel({
+ id,
+ zoomLevel: valueAsNumber,
+ })
+ );
+ }
+ },
+ [dispatch, id]
+ );
+
+ const handleCenterClick = useCallback(() => {
+ dispatch(userSetPositionOfCamera({ id, cameraView: [0, 0] }));
+ }, [dispatch, id]);
+
+ const handleZoomOutClick = useCallback(() => {
+ dispatch(userClickedZoomOut({ id }));
+ }, [dispatch, id]);
+
+ const handleZoomInClick = useCallback(() => {
+ dispatch(userClickedZoomIn({ id }));
+ }, [dispatch, id]);
+
+ const [handleNorth, handleEast, handleSouth, handleWest] = useMemo(() => {
+ const directionVectors: readonly Vector2[] = [
+ [0, 1],
+ [1, 0],
+ [0, -1],
+ [-1, 0],
+ ];
+ return directionVectors.map((direction) => {
+ return () => {
+ dispatch(userNudgedCamera({ id, direction, time: timestamp() }));
+ };
+ });
+ }, [dispatch, timestamp, id]);
+
+ /* eslint-disable react/button-has-type */
+ return (
+
+
+
+
+ {!isDatePickerAndSourcererDisabled ? (
+ <>
+
+
+ >
+ ) : null}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+ }
+);
+
+GraphControls.displayName = 'GraphControls';
diff --git a/x-pack/plugins/security_solution/public/resolver/view/controls/legend.tsx b/x-pack/plugins/security_solution/public/resolver/view/controls/legend.tsx
new file mode 100644
index 0000000000000..0798050239e5b
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/resolver/view/controls/legend.tsx
@@ -0,0 +1,155 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useCallback } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiPopover, EuiPopoverTitle } from '@elastic/eui';
+import { useColors } from '../use_colors';
+import { StyledDescriptionList } from '../panels/styles';
+import { CubeForProcess } from '../panels/cube_for_process';
+import { GeneratedText } from '../generated_text';
+import {
+ StyledEuiDescriptionListTitle,
+ StyledEuiDescriptionListDescription,
+ StyledEuiButtonIcon,
+ COLUMN_WIDTH,
+} from './styles';
+
+// This component defines the cube legend that allows users to identify the meaning of the cubes
+// Should be updated to be dynamic if and when non process based resolvers are possible
+export const NodeLegend = ({
+ id,
+ closePopover,
+ setActivePopover,
+ isOpen,
+}: {
+ id: string;
+ closePopover: () => void;
+ setActivePopover: (value: 'nodeLegend') => void;
+ isOpen: boolean;
+}) => {
+ const setAsActivePopover = useCallback(() => setActivePopover('nodeLegend'), [setActivePopover]);
+ const colorMap = useColors();
+
+ const nodeLegendButtonTitle = i18n.translate(
+ 'xpack.securitySolution.resolver.graphControls.nodeLegendButtonTitle',
+ {
+ defaultMessage: 'Node Legend',
+ }
+ );
+
+ return (
+
+ }
+ isOpen={isOpen}
+ closePopover={closePopover}
+ anchorPosition="leftCenter"
+ >
+
+ {i18n.translate('xpack.securitySolution.resolver.graphControls.nodeLegend', {
+ defaultMessage: 'legend',
+ })}
+
+
+
+ <>
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.securitySolution.resolver.graphControls.runningProcessCube',
+ {
+ defaultMessage: 'Running Process',
+ }
+ )}
+
+
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.securitySolution.resolver.graphControls.terminatedProcessCube',
+ {
+ defaultMessage: 'Terminated Process',
+ }
+ )}
+
+
+
+
+
+
+
+ {i18n.translate(
+ 'xpack.securitySolution.resolver.graphControls.currentlyLoadingCube',
+ {
+ defaultMessage: 'Loading Process',
+ }
+ )}
+
+
+
+
+
+
+
+ {i18n.translate('xpack.securitySolution.resolver.graphControls.errorCube', {
+ defaultMessage: 'Error Process',
+ })}
+
+
+ >
+
+
+
+ );
+};
diff --git a/x-pack/plugins/security_solution/public/resolver/view/controls/schema.tsx b/x-pack/plugins/security_solution/public/resolver/view/controls/schema.tsx
new file mode 100644
index 0000000000000..4d118887b3931
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/resolver/view/controls/schema.tsx
@@ -0,0 +1,129 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+
+import React, { useCallback } from 'react';
+import { i18n } from '@kbn/i18n';
+import {
+ EuiPopover,
+ EuiPopoverTitle,
+ EuiIconTip,
+ EuiDescriptionListDescription,
+} from '@elastic/eui';
+import { useSelector } from 'react-redux';
+import * as selectors from '../../store/selectors';
+import { useColors } from '../use_colors';
+import { StyledDescriptionList } from '../panels/styles';
+import { GeneratedText } from '../generated_text';
+import type { State } from '../../../common/store/types';
+import { StyledEuiDescriptionListTitle, StyledEuiButtonIcon, COLUMN_WIDTH } from './styles';
+
+export const SchemaInformation = ({
+ id,
+ closePopover,
+ setActivePopover,
+ isOpen,
+}: {
+ id: string;
+ closePopover: () => void;
+ setActivePopover: (value: 'schemaInfo' | null) => void;
+ isOpen: boolean;
+}) => {
+ const colorMap = useColors();
+ const sourceAndSchema = useSelector((state: State) =>
+ selectors.resolverTreeSourceAndSchema(state.analyzer[id])
+ );
+ const setAsActivePopover = useCallback(() => setActivePopover('schemaInfo'), [setActivePopover]);
+
+ const schemaInfoButtonTitle = i18n.translate(
+ 'xpack.securitySolution.resolver.graphControls.schemaInfoButtonTitle',
+ {
+ defaultMessage: 'Schema Information',
+ }
+ );
+
+ const unknownSchemaValue = i18n.translate(
+ 'xpack.securitySolution.resolver.graphControls.unknownSchemaValue',
+ {
+ defaultMessage: 'Unknown',
+ }
+ );
+
+ return (
+
+ }
+ isOpen={isOpen}
+ closePopover={closePopover}
+ anchorPosition="leftCenter"
+ >
+
+ {i18n.translate('xpack.securitySolution.resolver.graphControls.schemaInfoTitle', {
+ defaultMessage: 'process tree',
+ })}
+
+
+
+
+ <>
+
+ {i18n.translate('xpack.securitySolution.resolver.graphControls.schemaSource', {
+ defaultMessage: 'source',
+ })}
+
+
+ {sourceAndSchema?.dataSource ?? unknownSchemaValue}
+
+
+ {i18n.translate('xpack.securitySolution.resolver.graphControls.schemaID', {
+ defaultMessage: 'id',
+ })}
+
+
+ {sourceAndSchema?.schema.id ?? unknownSchemaValue}
+
+
+ {i18n.translate('xpack.securitySolution.resolver.graphControls.schemaEdge', {
+ defaultMessage: 'edge',
+ })}
+
+
+ {sourceAndSchema?.schema.parent ?? unknownSchemaValue}
+
+ >
+
+
+
+ );
+};
diff --git a/x-pack/plugins/security_solution/public/resolver/view/controls/sourcerer_selection.tsx b/x-pack/plugins/security_solution/public/resolver/view/controls/sourcerer_selection.tsx
new file mode 100644
index 0000000000000..ae6a7eb4cb62d
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/resolver/view/controls/sourcerer_selection.tsx
@@ -0,0 +1,65 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import React, { useCallback, memo } from 'react';
+import { i18n } from '@kbn/i18n';
+import { EuiPopover } from '@elastic/eui';
+import { StyledEuiButtonIcon } from './styles';
+import { useColors } from '../use_colors';
+import { Sourcerer } from '../../../common/components/sourcerer';
+import { SourcererScopeName } from '../../../common/store/sourcerer/model';
+
+const nodeLegendButtonTitle = i18n.translate(
+ 'xpack.securitySolution.resolver.graphControls.sourcererButtonTitle',
+ {
+ defaultMessage: 'Data View Selection',
+ }
+);
+
+export const SourcererButton = memo(
+ ({
+ id,
+ closePopover,
+ setActivePopover,
+ isOpen,
+ }: {
+ id: string;
+ closePopover: () => void;
+ setActivePopover: (value: 'sourcererSelection') => void;
+ isOpen: boolean;
+ }) => {
+ const setAsActivePopover = useCallback(
+ () => setActivePopover('sourcererSelection'),
+ [setActivePopover]
+ );
+ const colorMap = useColors();
+
+ return (
+
+ }
+ isOpen={isOpen}
+ closePopover={closePopover}
+ anchorPosition="leftCenter"
+ >
+
+
+ );
+ }
+);
+
+SourcererButton.displayName = 'SourcererButton';
diff --git a/x-pack/plugins/security_solution/public/resolver/view/controls/styles.tsx b/x-pack/plugins/security_solution/public/resolver/view/controls/styles.tsx
new file mode 100644
index 0000000000000..5758297c644b0
--- /dev/null
+++ b/x-pack/plugins/security_solution/public/resolver/view/controls/styles.tsx
@@ -0,0 +1,94 @@
+/*
+ * Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one
+ * or more contributor license agreements. Licensed under the Elastic License
+ * 2.0; you may not use this file except in compliance with the Elastic License
+ * 2.0.
+ */
+import type { EuiRangeProps } from '@elastic/eui';
+import {
+ EuiRange,
+ EuiButtonIcon,
+ EuiDescriptionListTitle,
+ EuiDescriptionListDescription,
+} from '@elastic/eui';
+import styled from 'styled-components';
+
+export const COLUMN_WIDTH = ['fit-content(10em)', 'auto'];
+
+// EuiRange is currently only horizontally positioned. This reorients the track to a vertical position
+export const StyledEuiRange = styled(EuiRange)`
+ & .euiRangeTrack:after {
+ left: -65px;
+ transform: rotate(90deg);
+ }
+`;
+export interface StyledGraphControlProps {
+ $backgroundColor: string;
+ $iconColor: string;
+ $borderColor: string;
+}
+
+export const StyledGraphControlsColumn = styled.div`
+ display: flex;
+ flex-direction: column;
+
+ &:not(last-of-type) {
+ margin-right: 5px;
+ }
+`;
+
+export const StyledEuiDescriptionListTitle = styled(EuiDescriptionListTitle)`
+ text-transform: uppercase;
+`;
+
+export const StyledEuiDescriptionListDescription = styled(EuiDescriptionListDescription)`
+ lineheight: '2.2em'; // lineHeight to align center vertically
+`;
+
+export const StyledEuiButtonIcon = styled(EuiButtonIcon)`
+ background-color: ${(props) => props.$backgroundColor};
+ color: ${(props) => props.$iconColor};
+ border-color: ${(props) => props.$borderColor};
+ border-width: 1px;
+ border-style: solid;
+ border-radius: 4px;
+ width: 40px;
+ height: 40px;
+
+ &:not(last-of-type) {
+ margin-bottom: 7px;
+ }
+`;
+
+export const StyledGraphControls = styled.div>`
+ display: flex;
+ flex-direction: row;
+ position: absolute;
+ top: 5px;
+ right: 5px;
+ background-color: transparent;
+ color: ${(props) => props.$iconColor};
+
+ .zoom-controls {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 5px 0px;
+
+ .zoom-slider {
+ width: 20px;
+ height: 150px;
+ margin: 5px 0px 2px 0px;
+
+ input[type='range'] {
+ width: 150px;
+ height: 20px;
+ transform-origin: 75px 75px;
+ transform: rotate(-90deg);
+ }
+ }
+ }
+ .panning-controls {
+ text-align: center;
+ }
+`;
diff --git a/x-pack/plugins/security_solution/public/resolver/view/graph_controls.tsx b/x-pack/plugins/security_solution/public/resolver/view/graph_controls.tsx
deleted file mode 100644
index ebd09f7839fb4..0000000000000
--- a/x-pack/plugins/security_solution/public/resolver/view/graph_controls.tsx
+++ /dev/null
@@ -1,558 +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
- * 2.0; you may not use this file except in compliance with the Elastic License
- * 2.0.
- */
-
-import React, { useCallback, useMemo, useContext, useState } from 'react';
-import styled from 'styled-components';
-import { i18n } from '@kbn/i18n';
-import type { EuiRangeProps } from '@elastic/eui';
-import {
- EuiRange,
- EuiPanel,
- EuiIcon,
- EuiButtonIcon,
- EuiPopover,
- EuiPopoverTitle,
- EuiIconTip,
- EuiDescriptionListTitle,
- EuiDescriptionListDescription,
-} from '@elastic/eui';
-import { useSelector, useDispatch } from 'react-redux';
-import { SideEffectContext } from './side_effect_context';
-import type { Vector2 } from '../types';
-import * as selectors from '../store/selectors';
-import { useColors } from './use_colors';
-import { StyledDescriptionList } from './panels/styles';
-import { CubeForProcess } from './panels/cube_for_process';
-import { GeneratedText } from './generated_text';
-import {
- userClickedZoomIn,
- userClickedZoomOut,
- userSetZoomLevel,
- userNudgedCamera,
- userSetPositionOfCamera,
-} from '../store/camera/action';
-import type { State } from '../../common/store/types';
-
-// EuiRange is currently only horizontally positioned. This reorients the track to a vertical position
-const StyledEuiRange = styled(EuiRange)`
- & .euiRangeTrack:after {
- left: -65px;
- transform: rotate(90deg);
- }
-`;
-interface StyledGraphControlProps {
- $backgroundColor: string;
- $iconColor: string;
- $borderColor: string;
-}
-
-const StyledGraphControlsColumn = styled.div`
- display: flex;
- flex-direction: column;
-
- &:not(last-of-type) {
- margin-right: 5px;
- }
-`;
-
-const COLUMN_WIDTH = ['fit-content(10em)', 'auto'];
-
-const StyledEuiDescriptionListTitle = styled(EuiDescriptionListTitle)`
- text-transform: uppercase;
-`;
-
-const StyledEuiDescriptionListDescription = styled(EuiDescriptionListDescription)`
- lineheight: '2.2em'; // lineHeight to align center vertically
-`;
-
-const StyledEuiButtonIcon = styled(EuiButtonIcon)`
- background-color: ${(props) => props.$backgroundColor};
- color: ${(props) => props.$iconColor};
- border-color: ${(props) => props.$borderColor};
- border-width: 1px;
- border-style: solid;
- border-radius: 4px;
- width: 40px;
- height: 40px;
-
- &:not(last-of-type) {
- margin-bottom: 7px;
- }
-`;
-
-const StyledGraphControls = styled.div>`
- display: flex;
- flex-direction: row;
- position: absolute;
- top: 5px;
- right: 5px;
- background-color: transparent;
- color: ${(props) => props.$iconColor};
-
- .zoom-controls {
- display: flex;
- flex-direction: column;
- align-items: center;
- padding: 5px 0px;
-
- .zoom-slider {
- width: 20px;
- height: 150px;
- margin: 5px 0px 2px 0px;
-
- input[type='range'] {
- width: 150px;
- height: 20px;
- transform-origin: 75px 75px;
- transform: rotate(-90deg);
- }
- }
- }
- .panning-controls {
- text-align: center;
- }
-`;
-/**
- * Controls for zooming, panning, and centering in Resolver
- */
-
-// eslint-disable-next-line react/display-name
-export const GraphControls = React.memo(
- ({
- id,
- className,
- }: {
- /**
- * Id that identify the scope of analyzer
- */
- id: string;
- /**
- * A className string provided by `styled`
- */
- className?: string;
- }) => {
- const dispatch = useDispatch();
- const scalingFactor = useSelector((state: State) =>
- selectors.scalingFactor(state.analyzer[id])
- );
- const { timestamp } = useContext(SideEffectContext);
- const [activePopover, setPopover] = useState(null);
- const colorMap = useColors();
-
- const setActivePopover = useCallback(
- (value) => {
- if (value === activePopover) {
- setPopover(null);
- } else {
- setPopover(value);
- }
- },
- [setPopover, activePopover]
- );
-
- const closePopover = useCallback(() => setPopover(null), []);
-
- const handleZoomAmountChange: EuiRangeProps['onChange'] = useCallback(
- (event) => {
- const valueAsNumber = parseFloat(
- (event as React.ChangeEvent