Skip to content

Commit

Permalink
feat(Core UI): ✨ Crete a PF legend
Browse files Browse the repository at this point in the history
  • Loading branch information
bartoval committed Jul 26, 2023
1 parent c111ff3 commit 8e254e4
Show file tree
Hide file tree
Showing 12 changed files with 281 additions and 111 deletions.
29 changes: 29 additions & 0 deletions src/App.css
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@
@import '@patternfly/patternfly/components/Toolbar/toolbar.css';
@import '@patternfly/patternfly/components/Tooltip/tooltip.css';
@import '@patternfly/patternfly/components/Truncate/truncate.css';
@import '@patternfly/patternfly/components/Popover/popover.css';
@import '@patternfly/patternfly/patternfly.css';

@import '@patternfly/patternfly/patternfly-addons.css';

.pf-c-nav__link {
Expand All @@ -31,6 +34,24 @@
max-width: 100%;
}

.sk-topology-control-bar__button {
margin-right: var(--pf-global--spacer--xs);
margin-top: var(--pf-global--spacer--xs);
border: none;
border-radius: var(--pf-global--BorderRadius--sm);
box-shadow: var(--pf-global--BoxShadow--sm);
}
.sk-topology-control-bar__button:not(.pf-m-disabled) {
background-color: var(--pf-global--BackgroundColor--100);
}
.sk-topology-control-bar__button:after {
display: none;
}
.sk-topology-control-bar__button:hover {
border: none;
box-shadow: var(--pf-global--BoxShadow--md);
}

body,
html {
font-size: 16px;
Expand Down Expand Up @@ -127,3 +148,11 @@ html {
transform: rotate(360deg);
}
}

.sk-topology-controls {
position: absolute !important;
bottom: 0;
left: 0;
background-color: transparent !important;
padding: 0;
}
3 changes: 3 additions & 0 deletions src/core/components/Graph/Graph.constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,3 +32,6 @@ export const COMBO_BORDER_COLOR_DEFAULT = '#FFFFFF';
export const COMBO_COLOR_DEFAULT_LABEL = '#000000';
export const NODE_COLOR_HOVER_DEFAULT = '#BEE1F4';
export const NODE_COLOR_HOVER_EDGE_DEFAULT = '#0066CC';

export const LEGEND_DEFAULT_BG_COLOR = '#FFFFFF';
export const LEGEND_DEFAULT_STROKE_COLOR = '#000000';
83 changes: 49 additions & 34 deletions src/core/components/Graph/GraphMenuControl.tsx
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { useCallback, useEffect, useRef } from 'react';
import { useRef } from 'react';

import { Graph } from '@antv/g6-pc';
import { Button, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem, Tooltip } from '@patternfly/react-core';
import { ExpandIcon, MapMarkerIcon, SearchMinusIcon, SearchPlusIcon } from '@patternfly/react-icons';
import { Button, Popover, Toolbar, ToolbarContent, ToolbarGroup, ToolbarItem, Tooltip } from '@patternfly/react-core';
import { ExpandArrowsAltIcon, ExpandIcon, SearchMinusIcon, SearchPlusIcon } from '@patternfly/react-icons';
import { useNavigate } from 'react-router-dom';

import ProcessLegend from './Legend';
import { GraphController } from './services';

type ZoomControlsProps = {
Expand All @@ -17,10 +18,10 @@ const ZOOM_DELTA = 0.25;
const FIT_SCREEN_CACHE_KEY_SUFFIX = '-fitScreen';
const ZOOM_CACHE_KEY_SUFFIX = '-graphZoom';
const DURATION_ANIMATION_CONTROL_DEFAULT = 250;
const LEGEND_LABEL_NAME = 'Legend';

const GraphMenuControl = function ({ graphInstance, onGetZoom, onFitScreen }: ZoomControlsProps) {
const showLegendRef = useRef(false);
const legendBtnRef = useRef<HTMLDivElement>();
const popoverRef = useRef<HTMLButtonElement>(null);

const center = graphInstance.getGraphCenterPoint();
const navigate = useNavigate();
Expand Down Expand Up @@ -69,58 +70,72 @@ const GraphMenuControl = function ({ graphInstance, onGetZoom, onFitScreen }: Zo
navigate(0);
};

const handleShowLegend = useCallback(() => {
const $legend = document.querySelector('.g6-legend-container');
const displayValue = showLegendRef.current ? 'none' : '';

$legend?.setAttribute(
'style',
`position:absolute; display:${displayValue}; left:${(legendBtnRef?.current?.offsetLeft || 0) / 2}px; bottom:${
(legendBtnRef?.current?.clientTop || 0) + (legendBtnRef?.current?.clientHeight || 0)
}px;`
);
$legend
?.querySelector('canvas')
?.setAttribute('style', 'box-shadow:0 1rem 2rem 0 rgba(3, 3, 3, 0.16), 0 0 0.5rem 0 rgba(3, 3, 3, 0.1)');
showLegendRef.current = !showLegendRef.current;
}, []);

useEffect(() => {
const $legend = document.querySelector('.g6-legend-container');
$legend?.setAttribute('style', 'display:none; box-shadow: 0 0.5rem 0.5rem -0.375rem rgba(3, 3, 3, 0.18)');
}, []);

return (
<Toolbar style={{ position: 'absolute', bottom: '0', left: '0', background: 'transparent', padding: 0 }}>
<Toolbar className="sk-topology-controls">
<ToolbarContent>
<ToolbarGroup spaceItems={{ default: 'spaceItemsSm' }} className="pf-u-background-color-100">
<ToolbarGroup spaceItems={{ default: 'spaceItemsNone' }}>
<ToolbarItem>
<Tooltip content={'zoom in'}>
<Button isSmall variant="tertiary" onClick={handleIncreaseZoom} icon={<SearchPlusIcon />} />
<Button
isSmall
variant="tertiary"
onClick={handleIncreaseZoom}
icon={<SearchPlusIcon />}
className="pf-u-background-color-100 sk-topology-control-bar__button"
/>
</Tooltip>
</ToolbarItem>

<ToolbarItem>
<Tooltip content={'zoom out'}>
<Button isSmall variant="tertiary" onClick={handleDecreaseZoom} icon={<SearchMinusIcon />} />
<Button
isSmall
variant="tertiary"
onClick={handleDecreaseZoom}
icon={<SearchMinusIcon />}
className="pf-u-background-color-100 sk-topology-control-bar__button"
/>
</Tooltip>
</ToolbarItem>

<ToolbarItem>
<Tooltip content={'fit view'}>
<Button isSmall variant="tertiary" onClick={handleZoomToDefault} icon={<ExpandIcon />} />
<Button
isSmall
variant="tertiary"
onClick={handleZoomToDefault}
icon={<ExpandArrowsAltIcon />}
className="pf-u-background-color-100 sk-topology-control-bar__button"
/>
</Tooltip>
</ToolbarItem>

<ToolbarItem>
<Tooltip content={'reposition'}>
<Button isSmall variant="tertiary" onClick={handleReposition} icon={<MapMarkerIcon />} />
<Button
isSmall
variant="tertiary"
onClick={handleReposition}
icon={<ExpandIcon />}
className="pf-u-background-color-100 sk-topology-control-bar__button"
/>
</Tooltip>
</ToolbarItem>

<ToolbarItem>
<Button isSmall variant="tertiary" onClick={handleShowLegend} ref={legendBtnRef}>
Legend
<Popover
aria-label="Network graph legend"
hasAutoWidth
bodyContent={<ProcessLegend />}
reference={popoverRef}
/>
<Button
ref={popoverRef}
isSmall
variant="tertiary"
className="pf-u-background-color-100 sk-topology-control-bar__button"
>
{LEGEND_LABEL_NAME}
</Button>
</ToolbarItem>
</ToolbarGroup>
Expand Down
6 changes: 2 additions & 4 deletions src/core/components/Graph/GraphReactAdaptor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import {
DEFAULT_NODE_CONFIG,
DEFAULT_NODE_STATE_CONFIG
} from './config';
import { createLegend, registerCustomBehaviours } from './customBehaviours';
import { registerCustomBehaviours } from './customBehaviours';
import GraphMenuControl from './GraphMenuControl';
import { GraphController } from './services';

Expand All @@ -33,7 +33,6 @@ const GraphReactAdaptor: FC<GraphReactAdaptorProps> = memo(
onClickNode,
onClickCombo,
itemSelected,
legendData,
onGetZoom,
onFitScreen,
layout,
Expand Down Expand Up @@ -96,7 +95,6 @@ const GraphReactAdaptor: FC<GraphReactAdaptorProps> = memo(
const graphRef = useCallback(($node: HTMLDivElement) => {
if (nodes.length && !topologyGraphRef.current) {
const data = GraphController.getG6Model({ edges, nodes, combos });
const legend = legendData ? createLegend(legendData) : '';

const width = $node.scrollWidth;
const height = $node.scrollHeight;
Expand All @@ -106,7 +104,7 @@ const GraphReactAdaptor: FC<GraphReactAdaptorProps> = memo(
width,
height,
fitCenter: config?.fitCenter || false,
plugins: [legend],
plugins: [],
modes: DEFAULT_MODE,
defaultNode: DEFAULT_NODE_CONFIG,
defaultCombo: DEFAULT_COMBO_CONFIG,
Expand Down
103 changes: 103 additions & 0 deletions src/core/components/Graph/Legend.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { Divider, Flex, FlexItem, Text, TextContent, TextVariants, Title } from '@patternfly/react-core';

import { EDGE_COLOR_ACTIVE_DEFAULT } from './Graph.constants';
import SvgCircle from './Shapes/Circle';
import SvgDiamond from './Shapes/Diamond';
import SvgHorizontalLine from './Shapes/HorizontalLine';
import SvgSquare from './Shapes/Square';

enum Labels {
Exposed = 'Process, Component or Site exposed',
NoExposed = 'Process/Component',
SiteGroup = 'Site group',
Remote = 'Server process or component',
DataLink = 'Data link',
ActiveDataLink = 'Active data link',
SiteLink = 'Site link'
}

const ProcessLegend = function () {
return (
<>
<Title headingLevel="h3" className="pf-u-my-sm ">
Processes
</Title>
<Flex>
<Flex className="pf-u-mx-md" direction={{ default: 'column' }} alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem className="pf-u-mb-0">
<SvgCircle />
</FlexItem>

<FlexItem className="pf-u-mb-0">
<SvgDiamond />
</FlexItem>

<FlexItem className="pf-u-mb-0">
<SvgSquare />
</FlexItem>

<FlexItem className="pf-u-mb-0">
<SvgCircle dimension={8} />
</FlexItem>
</Flex>
<Flex className="pf-u-mb-md ">
<TextContent>
<FlexItem>
<Text component={TextVariants.small}>{Labels.Exposed}</Text>
</FlexItem>

<FlexItem>
<Text component={TextVariants.small}>{Labels.NoExposed}</Text>
</FlexItem>

<FlexItem>
<Text component={TextVariants.small}>{Labels.SiteGroup}</Text>
</FlexItem>

<FlexItem>
<Text component={TextVariants.small}>{Labels.Remote}</Text>
</FlexItem>
</TextContent>
</Flex>

<Divider />

<Title headingLevel="h3" className="pf-u-mt-md ">
Links
</Title>
</Flex>
<Flex>
<Flex className="pf-u-mx-md" direction={{ default: 'column' }} alignItems={{ default: 'alignItemsCenter' }}>
<FlexItem className="pf-u-mb-0">
<SvgHorizontalLine />
</FlexItem>

<FlexItem className="pf-u-mb-0">
<SvgHorizontalLine color={EDGE_COLOR_ACTIVE_DEFAULT} />
</FlexItem>

<FlexItem className="pf-u-mb-0">
<SvgHorizontalLine dashed />
</FlexItem>
</Flex>
<Flex>
<TextContent>
<FlexItem>
<Text component={TextVariants.small}>{Labels.DataLink}</Text>
</FlexItem>

<FlexItem>
<Text component={TextVariants.small}>{Labels.ActiveDataLink}</Text>
</FlexItem>

<FlexItem>
<Text component={TextVariants.small}>{Labels.SiteLink}</Text>
</FlexItem>
</TextContent>
</Flex>
</Flex>
</>
);
};

export default ProcessLegend;
24 changes: 24 additions & 0 deletions src/core/components/Graph/Shapes/Circle.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';

import { LEGEND_DEFAULT_BG_COLOR, LEGEND_DEFAULT_STROKE_COLOR } from '../Graph.constants';

const SvgCircle = function ({ dimension = 12 }: { dimension?: number }) {
// Dimension of the circle (width and height)
const circleDimension = dimension;
const circleColor = LEGEND_DEFAULT_BG_COLOR;
const circleStroke = LEGEND_DEFAULT_STROKE_COLOR;

return (
<svg width={circleDimension} height={circleDimension}>
<circle
cx={circleDimension / 2}
cy={circleDimension / 2}
r={circleDimension / 2}
fill={circleColor}
stroke={circleStroke}
/>
</svg>
);
};

export default SvgCircle;
24 changes: 24 additions & 0 deletions src/core/components/Graph/Shapes/Diamond.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import React from 'react';

import { LEGEND_DEFAULT_BG_COLOR, LEGEND_DEFAULT_STROKE_COLOR } from '../Graph.constants';

const SvgDiamond = function ({ dimension = 12 }: { dimension?: number }) {
// Dimension of the diamond (width and height)
const diamondDimension = dimension;
const diamondColor = LEGEND_DEFAULT_BG_COLOR;
const diamondStroke = LEGEND_DEFAULT_STROKE_COLOR;

return (
<svg width={diamondDimension} height={diamondDimension}>
<polygon
points={`0,${diamondDimension / 2} ${diamondDimension / 2},0 ${diamondDimension},${diamondDimension / 2} ${
diamondDimension / 2
},${diamondDimension}`}
fill={diamondColor}
stroke={diamondStroke}
/>
</svg>
);
};

export default SvgDiamond;
Loading

0 comments on commit 8e254e4

Please sign in to comment.