@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
110 lines (109 loc) • 6.21 kB
JavaScript
import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
/*
* Copyright 2025 The Kubernetes Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import { Icon } from '@iconify/react';
import Badge from '@mui/material/Badge';
import Box from '@mui/material/Box';
import Checkbox from '@mui/material/Checkbox';
import Chip from '@mui/material/Chip';
import CircularProgress from '@mui/material/CircularProgress';
import Popover from '@mui/material/Popover';
import Stack from '@mui/material/Stack';
import { styled } from '@mui/material/styles';
import Typography from '@mui/material/Typography';
import { alpha } from '@mui/system/colorManipulator';
import { memo, useState } from 'react';
const Node = styled('div')(() => ({
display: 'flex',
flexDirection: 'column',
}));
const NodeHeader = styled('div')(({ theme }) => ({
display: 'flex',
flexDirection: 'row',
alignItems: 'center',
borderRadius: theme.spacing(1),
paddingLeft: theme.spacing(0.5),
paddingRight: theme.spacing(0.5),
paddingTop: theme.spacing(0.5),
paddingBottom: theme.spacing(0.5),
':hover': {
background: theme.palette.action.hover,
},
':active': {
background: alpha(theme.palette.action.active, theme.palette.action.activatedOpacity),
},
}));
/**
* Component that displays a Source and allows to check or uncheck it
* and its' descendants
*
* @returns
*/
function GraphSourceView({ source, sourceData, selection, activeItemId, setActiveItemId, toggleSelection, }) {
const hasChildren = 'sources' in source;
const isSelected = (source) => 'sources' in source ? source.sources.every(s => isSelected(s)) : selection.has(source.id);
const isChecked = isSelected(source);
const intermediate = 'sources' in source && source.sources.some(s => isSelected(s)) && !isChecked;
const data = sourceData.get(source.id);
const check = (_jsxs(_Fragment, { children: [_jsx(Box, { mr: 1, display: "flex", children: _jsx(Badge, { badgeContent: isChecked ? data?.nodes?.length : undefined, overlap: "circular", children: _jsx(Box, { width: hasChildren ? '24px' : '24px', height: hasChildren ? '24px' : '24px', children: source.icon }) }) }), _jsx(Typography, { variant: "subtitle2", children: source.label }), !('sources' in source) && isChecked && !data && _jsx(CircularProgress, {}), _jsx(Checkbox, { sx: () => ({ marginLeft: 'auto' }), checked: isChecked, indeterminate: intermediate, onClick: e => {
e.stopPropagation();
toggleSelection(source);
}, onKeyDown: e => {
if (e.key === 'Enter' || e.key === ' ') {
e.stopPropagation();
e.preventDefault();
toggleSelection(source);
}
} })] }));
if (!('sources' in source)) {
return (_jsx(Node, { onClick: () => {
toggleSelection(source);
}, children: _jsx(NodeHeader, { children: check }) }));
}
const isActive = source.id === activeItemId;
return (_jsxs(Node, { children: [_jsxs(NodeHeader, { role: "button", tabIndex: 0, onClick: () => setActiveItemId(isActive ? undefined : source.id), onKeyDown: e => {
if (e.key === 'Enter' || e.key === ' ') {
e.preventDefault();
setActiveItemId(isActive ? undefined : source.id);
}
}, children: [_jsx(Icon, { icon: isActive ? 'mdi:chevron-down' : 'mdi:chevron-right', width: 18, height: 18, style: { flexShrink: 0 } }), check] }), _jsx(Stack, { ml: 3, children: source.id === activeItemId &&
source.sources?.map(source => (_jsx(GraphSourceView, { source: source, selection: selection, toggleSelection: toggleSelection, sourceData: sourceData, activeItemId: activeItemId, setActiveItemId: setActiveItemId }, source.id))) })] }));
}
export const GraphSourcesView = memo(({ sources, sourceData, selectedSources, toggleSource }) => {
const [anchorEl, setAnchorEl] = useState(null);
const [activeItemId, setActiveItemId] = useState(undefined);
const selected = sources.filter(source => {
const isSelected = selectedSources.has(source.id);
return 'sources' in source
? source.sources.some(it => selectedSources.has(it.id))
: isSelected;
});
const selectedText = selected.length > 2
? `${selected[0].label}, ${selected[1].label}, +${selected.length - 2}`
: selected.map(it => it.label).join(', ');
return (_jsxs(_Fragment, { children: [_jsx(Chip, { label: _jsxs(Stack, { direction: "row", gap: 1, alignItems: "center", children: [_jsx(Icon, { icon: "mdi:filter" }), " ", selectedText, ' '] }), color: "primary", variant: 'filled', onClick: e => setAnchorEl(e.currentTarget), sx: {
lineHeight: '1',
} }), _jsx(Popover, { elevation: 4, anchorOrigin: {
vertical: 'bottom',
horizontal: 'left',
}, onClose: () => setAnchorEl(null), anchorEl: anchorEl, open: Boolean(anchorEl), children: _jsx(Box, { sx: {
display: 'flex',
flexDirection: 'column',
width: 'fit-content',
minWidth: '300px',
padding: 1.5,
}, children: sources.map((source, index) => (_jsx(GraphSourceView, { source: source, selection: selectedSources, toggleSelection: toggleSource, sourceData: sourceData, activeItemId: activeItemId, setActiveItemId: id => setActiveItemId(id) }, index))) }) })] }));
});