@kinvolk/headlamp-plugin
Version:
The needed infrastructure for building Headlamp plugins.
174 lines (173 loc) • 7.72 kB
JavaScript
import { jsxs as _jsxs, jsx as _jsx, Fragment as _Fragment } 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 Box from '@mui/material/Box';
import Button from '@mui/material/Button';
import Typography from '@mui/material/Typography';
import React from 'react';
import { useTranslation } from 'react-i18next';
import { ResourceClasses, useSelectedClusters } from '../../../lib/k8s';
import { localeDate } from '../../../lib/util';
import { NameValueTable } from '../../common/SimpleTable';
import Link from '../Link';
import { LightTooltip } from '../Tooltip';
export const metadataStyles = (theme) => ({
color: theme.palette.text.primary,
backgroundColor: theme.palette.metadataBgColor,
fontSize: theme.typography.pxToRem(16),
wordBreak: 'break-word',
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
marginRight: theme.spacing(1),
overflow: 'hidden',
whiteSpace: 'nowrap',
overflowWrap: 'anywhere',
textOverflow: 'ellipsis',
});
export function MetadataDisplay(props) {
const { resource, extraRows } = props;
const { t } = useTranslation();
const shouldShowCluster = useSelectedClusters().length > 1;
let makeExtraRows;
function makeOwnerReferences(ownerReferences) {
if (!resource || ownerReferences === undefined) {
return undefined;
}
const numItems = ownerReferences.length;
if (numItems === 0) {
return undefined;
}
return ownerReferences
.map((ownerRef, i) => {
if (ownerRef.kind in ResourceClasses) {
let routeName;
try {
routeName = ResourceClasses[ownerRef.kind].detailsRoute;
}
catch (e) {
console.error(`Error getting routeName for {ownerRef.kind}`, e);
return null;
}
return (_jsxs(_Fragment, { children: [_jsxs(Link, { routeName: routeName, activeCluster: resource.cluster, params: { name: ownerRef.name, namespace: resource.metadata.namespace }, children: [ownerRef.kind, ": ", ownerRef.name] }), i < numItems - 1 && _jsx("br", {})] }));
}
return (_jsxs(_Fragment, { children: [`${ownerRef.kind}: ${ownerRef.name}`, i < numItems - 1 && _jsx("br", {})] }));
})
.filter(element => element !== null);
}
if (typeof extraRows === 'function') {
makeExtraRows = extraRows;
}
else if (!extraRows) {
makeExtraRows = () => null;
}
else {
makeExtraRows = () => extraRows;
}
const mainRows = [
{
name: t('translation|Name'),
value: resource.metadata.name,
},
{
name: t('glossary|Namespace'),
value: resource.metadata.namespace && (_jsx(Link, { routeName: 'namespace', params: { name: resource.metadata.namespace }, activeCluster: resource.cluster, children: resource.metadata.namespace })),
hide: !resource.metadata.namespace,
},
shouldShowCluster && {
name: t('glossary|Cluster'),
value: resource.cluster,
},
{
name: t('Creation'),
value: localeDate(resource.metadata.creationTimestamp),
},
{
name: t('Labels'),
value: resource.metadata.labels && _jsx(MetadataDictGrid, { dict: resource.metadata.labels }),
hide: !resource.metadata.labels,
},
{
name: t('Annotations'),
value: resource.metadata.annotations && (_jsx(MetadataDictGrid, { dict: resource.metadata.annotations })),
hide: !resource.metadata.annotations,
},
{
name: resource.metadata.ownerReferences && resource.metadata.ownerReferences.length > 1
? t('Owner refs')
: t('Controlled by'),
value: makeOwnerReferences(resource.metadata.ownerReferences || []),
hide: !resource.metadata.ownerReferences || resource.metadata.ownerReferences.length === 0,
},
].filter(Boolean).concat(makeExtraRows(resource) || []);
return (_jsx(Box, { children: _jsx(NameValueTable, { rows: mainRows }) }));
}
export function MetadataDictGrid(props) {
const { dict, showKeys = true, gridProps } = props;
const { t } = useTranslation();
const [expanded, setExpanded] = React.useState(false);
const defaultNumShown = 20;
const keys = Object.keys(dict || []);
const MetadataEntry = React.forwardRef((props, ref) => {
return (_jsx(Typography, { ...props, sx: theme => ({
color: theme.palette.text.primary,
borderRadius: theme.shape.borderRadius + 'px',
backgroundColor: theme.palette.background.muted,
border: '1px solid',
borderColor: theme.palette.divider,
fontSize: theme.typography.pxToRem(14),
wordBreak: 'break-word',
paddingTop: 0.5,
paddingBottom: 0.5,
paddingLeft: 1,
paddingRight: 1,
marginRight: theme.spacing(1),
overflow: 'hidden',
whiteSpace: 'nowrap',
overflowWrap: 'anywhere',
textOverflow: 'ellipsis',
}), ref: ref }));
});
function makeLabel(key) {
let fullText = dict[key];
if (showKeys) {
fullText = key + ': ' + fullText;
}
let shortText = fullText;
// Shorten the label manually because relying on the ellipsing methods
// was not working (it would correctly ellipse the text, but the width of it
// would still extend the area/section where the text is contained).
if (fullText.length > 50) {
shortText = fullText.substr(0, 50) + '…';
}
let labelComponent = _jsx(MetadataEntry, { children: shortText });
// If the full label is not being shown, use a tooltip to show the full text
// to the user (so they select it, etc.).
if (fullText.length !== shortText.length) {
labelComponent = _jsx(LightTooltip, { title: fullText, children: labelComponent, interactive: true });
}
return labelComponent;
}
return (_jsxs(Box, { children: [_jsx(Box, { sx: {
display: 'flex',
flexWrap: 'wrap',
gap: 0.5,
}, ...gridProps, children: keys.slice(0, expanded ? keys.length : defaultNumShown).map(key => (_jsx(React.Fragment, { children: makeLabel(key) }, key))) }), keys.length > defaultNumShown && (_jsx(Button, { onClick: () => setExpanded(!expanded), size: "small", startIcon: _jsx(Icon, { icon: expanded ? 'mdi:menu-up' : 'mdi:menu-down' }), sx: { mt: 1, mb: 1 }, children: !expanded
? t('translation|Show all labels (+{{count}} more)', {
count: keys.length - defaultNumShown,
})
: t('translation|Show fewer') }))] }));
}