Skip to content

Commit

Permalink
CNV-45822: Vm tree view 4
Browse files Browse the repository at this point in the history
Signed-off-by: Aviv Turgeman <[email protected]>
  • Loading branch information
avivtur committed Dec 5, 2024
1 parent eaa7623 commit e3a728d
Show file tree
Hide file tree
Showing 14 changed files with 409 additions and 162 deletions.
2 changes: 2 additions & 0 deletions locales/en/plugin__kubevirt-plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -957,6 +957,7 @@
"Project labels": "Project labels",
"Project name to clone the template to": "Project name to clone the template to",
"Project selector": "Project selector",
"Projects": "Projects",
"Provider": "Provider",
"Provisioning": "Provisioning",
"Public SSH key": "Public SSH key",
Expand Down Expand Up @@ -1132,6 +1133,7 @@
"Show all": "Show all",
"Show all alerts": "Show all alerts",
"Show less": "Show less",
"Show OCP default projects": "Show OCP default projects",
"Show top 10": "Show top 10",
"Show top 5": "Show top 5",
"Show uncategorized VirtualMachines": "Show uncategorized VirtualMachines",
Expand Down
44 changes: 33 additions & 11 deletions src/views/virtualmachines/tree/VirtualMachineTreeView.scss
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,46 @@
overflow-x: hidden;
}

.vms-tree-view-body,
.vms-tree-view-toolbar,
.vms-tree-view-toolbar-content {
.pf-v5-c-drawer__body.vms-tree-view-body,
.pf-v5-c-toolbar.vms-tree-view-toolbar,
.pf-v5-c-toolbar__content.vms-tree-view-toolbar-content,
.pf-v5-c-drawer__body.vms-tree-view-actions {
padding: 0;
}

.pf-v5-c-tree-view__node .pf-v5-c-tree-view__node-count,
.vms-tree-view-toolbar-buttons {
.pf-v5-c-drawer__panel-main {
.pf-v5-c-drawer__body {
padding: 0 var(--pf-global--spacer--sm);
height: fit-content;
}
}
.pf-v5-c-toolbar__content.vms-tree-view-toolbar-content,
.vms-tree-view-toolbar-section,
.vms-tree-view-search-input {
width: 100%;
}
.vms-tree-view-search-input {
margin: var(--pf-global--spacer--sm) var(--pf-global--spacer--sm) 0 var(--pf-global--spacer--sm);
}
.pf-v5-c-toolbar.vms-tree-view-toolbar {
margin-bottom: var(--pf-global--spacer--sm);
}
.pf-v5-c-tree-view__node .pf-v5-c-tree-view__node-count {
display: flex;
flex-grow: 1;
flex-direction: row-reverse;
}

.vms-tree-view-toolbar-buttons {
align-self: center;
margin-right: var(--pf-global--spacer--sm);
.pf-v5-c-tree-view {
padding: 0;
}

&__padding {
padding: var(--pf-global--spacer--sm);
}
.vms-tree-view-toolbar-action {
padding: var(--pf-global--spacer--sm);
}

.vms-tree-view-toolbar-default-project-switch {
margin-left: var(--pf-global--spacer--sm);
margin-bottom: 0;
font-weight: var(--pf-global--FontWeight--light);
}
116 changes: 100 additions & 16 deletions src/views/virtualmachines/tree/VirtualMachineTreeView.tsx
Original file line number Diff line number Diff line change
@@ -1,30 +1,47 @@
import React, { FC, MouseEvent, useMemo } from 'react';
import React, { FC, MouseEvent, useEffect, useMemo } from 'react';
import { useLocation, useNavigate } from 'react-router-dom-v5-compat';

import VirtualMachineModel from '@kubevirt-ui/kubevirt-api/console/models/VirtualMachineModel';
import { useQueryParamsMethods } from '@kubevirt-utils/components/ListPageFilter/hooks/useQueryParamsMethods';
import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import useLocalStorage from '@kubevirt-utils/hooks/useLocalStorage';
import { convertResourceArrayToMap, getResourceUrl } from '@kubevirt-utils/resources/shared';
import { getContentScrollableElement } from '@kubevirt-utils/utils/utils';
import { FilterValue, useActiveNamespace } from '@openshift-console/dynamic-plugin-sdk';
import {
Button,
ButtonVariant,
Drawer,
DrawerActions,
DrawerContent,
DrawerContentBody,
DrawerHead,
DrawerPanelBody,
DrawerPanelContent,
Title,
Tooltip,
TreeView,
TreeViewDataItem,
} from '@patternfly/react-core';
// import { PanelCloseIcon, PanelOpenIcon } from '@patternfly/react-icons';
import { TEXT_FILTER_LABELS_ID } from '@virtualmachines/list/hooks/constants';

import TreeViewToolbar from './components/TreeViewToolbar';
import { useHideNamespaceBar } from './hooks/useHideNamespaceBar';
import { useSyncClicksEffects } from './hooks/useSyncClicksEffects';
import { useTreeViewData } from './hooks/useTreeViewData';
import { useTreeViewSearch } from './hooks/useTreeViewSearch';
import ClosePanelIcon from './icons/ClosePanelIcon';
import CollapseAllIcon from './icons/CollapseAllIcon';
import ExpandAllIcon from './icons/ExpandAllIcon';
import OpenPanelIcon from './icons/OpenPanelIcon';
import {
CLOSED_DRAWER_SIZE,
FOLDER_SELECTOR_PREFIX,
OPEN_DRAWER_SIZE,
PANEL_WIDTH_PROPERTY,
PROJECT_SELECTOR_PREFIX,
TREE_VIEW_LAST_WIDTH,
TREE_VIEW_PANEL_ID,
VM_FOLDER_LABEL,
} from './utils/constants';
Expand All @@ -42,12 +59,23 @@ type VirtualMachineTreeViewProps = {
};

const VirtualMachineTreeView: FC<VirtualMachineTreeViewProps> = ({ children, onFilterChange }) => {
const { t } = useKubevirtTranslation();
const [activeNamespace] = useActiveNamespace();
const navigate = useNavigate();
const location = useLocation();

const { setOrRemoveQueryArgument } = useQueryParamsMethods();
const { isAdmin, loaded, loadError, projectNames, vms } = useTreeViewData();
const [drawerWidth, setDrawerWidth] = useLocalStorage(TREE_VIEW_LAST_WIDTH, OPEN_DRAWER_SIZE);

const {
isAdmin,
loaded,
loadError,
projectNames,
setShowDefaultProjects,
showDefaultProjects,
vms,
} = useTreeViewData();

const vmsMapper = useMemo(() => convertResourceArrayToMap(vms, true), [vms]);

const treeData = useMemo(
Expand All @@ -57,8 +85,13 @@ const VirtualMachineTreeView: FC<VirtualMachineTreeViewProps> = ({ children, onF

const { filteredItems, onSearch, setShowAll, showAll } = useTreeViewSearch(treeData);

const drawerPanel = document.getElementById(TREE_VIEW_PANEL_ID);

useSyncClicksEffects(activeNamespace, loaded, location);
useHideNamespaceBar();
useEffect(() => {
drawerPanel?.style?.setProperty(PANEL_WIDTH_PROPERTY, drawerWidth);
}, [drawerPanel, drawerWidth]);

if (loadError) return <>{children}</>;

Expand Down Expand Up @@ -100,6 +133,32 @@ const VirtualMachineTreeView: FC<VirtualMachineTreeViewProps> = ({ children, onF
);
};

const toggleDrawer = () => {
treeViewOpen.value = !treeViewOpen.value;

const size = treeViewOpen.value ? OPEN_DRAWER_SIZE : CLOSED_DRAWER_SIZE;
drawerPanel.style.setProperty(PANEL_WIDTH_PROPERTY, size);
setDrawerWidth(size);
};

const onResize = (
_e: globalThis.MouseEvent | React.KeyboardEvent | TouchEvent,
width: number,
) => {
setDrawerWidth(`${String(width)}px`);
};
const togglePanelButton = (
<Tooltip content={treeViewOpen.value ? t('Close') : t('Open')}>
<Button
className="vms-tree-view-toolbar-action"
onClick={toggleDrawer}
variant={ButtonVariant.plain}
>
{treeViewOpen.value ? <ClosePanelIcon /> : <OpenPanelIcon />}
</Button>
</Tooltip>
);

return (
<Drawer isExpanded isInline position="start">
<DrawerContent
Expand All @@ -109,22 +168,47 @@ const VirtualMachineTreeView: FC<VirtualMachineTreeViewProps> = ({ children, onF
height: getContentScrollableElement().offsetHeight || 0,
}}
className="vms-tree-view"
defaultSize={drawerWidth}
id={TREE_VIEW_PANEL_ID}
isResizable={treeViewOpen.value}
onResize={onResize}
>
<DrawerPanelBody className="vms-tree-view-body">
<TreeView
toolbar={
<TreeViewToolbar onSearch={onSearch} setShowAll={setShowAll} showAll={showAll} />
}
activeItems={selectedTreeItem.value}
allExpanded={showAll}
data={!treeViewOpen.value ? [] : filteredItems ?? treeData}
hasBadges={loaded}
hasSelectableNodes
onSelect={onSelect}
/>
</DrawerPanelBody>
{!treeViewOpen.value ? (
togglePanelButton
) : (
<>
<TreeViewToolbar
onSearch={onSearch}
setShowDefaultProjects={setShowDefaultProjects}
showDefaultProjects={showDefaultProjects}
/>
<DrawerHead>
<Title headingLevel="h6">{t('Projects')}</Title>
<DrawerActions>
<Tooltip content={showAll ? t('Collapse all') : t('Expand all')}>
<Button
className="vms-tree-view-toolbar-action"
onClick={() => setShowAll((prev) => !prev)}
variant={ButtonVariant.plain}
>
{showAll ? <CollapseAllIcon /> : <ExpandAllIcon />}
</Button>
</Tooltip>
{togglePanelButton}
</DrawerActions>
</DrawerHead>
<DrawerPanelBody className="vms-tree-view-body">
<TreeView
activeItems={selectedTreeItem.value}
allExpanded={showAll}
data={!treeViewOpen.value ? [] : filteredItems ?? treeData}
hasBadges={loaded}
hasSelectableNodes
onSelect={onSelect}
/>
</DrawerPanelBody>
</>
)}
</DrawerPanelContent>
}
>
Expand Down
96 changes: 41 additions & 55 deletions src/views/virtualmachines/tree/components/TreeViewToolbar.tsx
Original file line number Diff line number Diff line change
@@ -1,77 +1,63 @@
import React, { ChangeEvent, Dispatch, FC, SetStateAction } from 'react';
import React, { ChangeEvent, FC } from 'react';

import { useKubevirtTranslation } from '@kubevirt-utils/hooks/useKubevirtTranslation';
import {
Button,
ButtonVariant,
Divider,
Stack,
StackItem,
Switch,
Toolbar,
ToolbarContent,
ToolbarItem,
Tooltip,
TreeViewSearch,
} from '@patternfly/react-core';
import { PanelCloseIcon, PanelOpenIcon } from '@patternfly/react-icons';

import {
CLOSED_DRAWER_SIZE,
OPEN_DRAWER_SIZE,
PANEL_WIDTH_PROPERTY,
TREE_VIEW_PANEL_ID,
TREE_VIEW_SEARCH_ID,
} from '../utils/constants';
import { HIDE, SHOW, TREE_VIEW_SEARCH_ID } from '../utils/constants';
import { treeViewOpen } from '../utils/utils';

type TreeViewToolbarProps = {
onSearch: (event: ChangeEvent<HTMLInputElement>) => void;
setShowAll: Dispatch<SetStateAction<boolean>>;
showAll: boolean;
setShowDefaultProjects: (show: string) => void;
showDefaultProjects: string;
};

const TreeViewToolbar: FC<TreeViewToolbarProps> = ({ onSearch, setShowAll, showAll }) => {
const TreeViewToolbar: FC<TreeViewToolbarProps> = ({
onSearch,
setShowDefaultProjects,
showDefaultProjects,
}) => {
const { t } = useKubevirtTranslation();

const toggleDrawer = () => {
treeViewOpen.value = !treeViewOpen.value;
const panel = document.getElementById(TREE_VIEW_PANEL_ID);
panel.style.setProperty(
PANEL_WIDTH_PROPERTY,
treeViewOpen.value ? OPEN_DRAWER_SIZE : CLOSED_DRAWER_SIZE,
);
};

return (
<Toolbar className="vms-tree-view-toolbar">
<Toolbar className="vms-tree-view-toolbar" isSticky>
<ToolbarContent className="vms-tree-view-toolbar-content">
<ToolbarItem>
{treeViewOpen.value && (
<TreeViewSearch
id={TREE_VIEW_SEARCH_ID}
name={TREE_VIEW_SEARCH_ID}
onSearch={onSearch}
placeholder={t('Search')}
/>
)}
</ToolbarItem>
<ToolbarItem className="vms-tree-view-toolbar-buttons">
<Tooltip content={treeViewOpen.value ? t('Close') : t('Open')}>
<Button
className="vms-tree-view-toolbar-buttons__padding"
onClick={toggleDrawer}
variant={ButtonVariant.plain}
>
{treeViewOpen.value ? <PanelCloseIcon /> : <PanelOpenIcon />}
</Button>
</Tooltip>
{treeViewOpen.value && (
<Button
className="vms-tree-view-toolbar-buttons__padding"
onClick={() => setShowAll((prev) => !prev)}
variant={ButtonVariant.link}
>
{showAll ? t('Collapse all') : t('Expand all')}
</Button>
)}
</ToolbarItem>
<Stack className="vms-tree-view-toolbar-section" hasGutter>
<StackItem>
<ToolbarItem>
{treeViewOpen.value && (
<TreeViewSearch
className="vms-tree-view-search-input"
id={TREE_VIEW_SEARCH_ID}
name={TREE_VIEW_SEARCH_ID}
onSearch={onSearch}
placeholder={t('Search')}
/>
)}
</ToolbarItem>
</StackItem>
<Divider />
<StackItem>
{treeViewOpen.value && (
<Switch
checked={showDefaultProjects === SHOW}
className="vms-tree-view-toolbar-default-project-switch"
label={t('Show OCP default projects')}
onChange={(_, checked) => setShowDefaultProjects(checked ? SHOW : HIDE)}
/>
)}
</StackItem>
<Divider />
</Stack>
</ToolbarContent>
</Toolbar>
);
Expand Down
Loading

0 comments on commit e3a728d

Please sign in to comment.