mirror of https://github.com/grafana/grafana.git
171 lines
4.8 KiB
TypeScript
171 lines
4.8 KiB
TypeScript
import { css } from '@emotion/css';
|
|
import { memo, forwardRef, useMemo, useState } from 'react';
|
|
|
|
import { GrafanaTheme2, Labels } from '@grafana/data';
|
|
import { t } from '@grafana/i18n';
|
|
import { Button, Icon, Tooltip, useStyles2 } from '@grafana/ui';
|
|
|
|
import { getNormalizedFieldName } from './panel/processing';
|
|
|
|
// Levels are already encoded in color, filename is a Loki-ism
|
|
const HIDDEN_LABELS = ['detected_level', 'level', 'lvl', 'filename'];
|
|
|
|
export interface Props {
|
|
labels: Labels;
|
|
emptyMessage?: string;
|
|
addTooltip?: boolean;
|
|
displayMax?: number;
|
|
displayAll?: boolean;
|
|
onDisplayMaxToggle?(state: boolean): void;
|
|
}
|
|
|
|
export const LogLabels = memo(
|
|
({
|
|
labels,
|
|
emptyMessage,
|
|
addTooltip = true,
|
|
displayMax,
|
|
onDisplayMaxToggle,
|
|
displayAll: initialDisplayAll = false,
|
|
}: Props) => {
|
|
const [displayAll, setDisplayAll] = useState<boolean | undefined>(displayMax ? initialDisplayAll : undefined);
|
|
const styles = useStyles2(getStyles);
|
|
const allLabels = useMemo(
|
|
() =>
|
|
Object.keys(labels)
|
|
.filter((label) => !label.startsWith('_') && !HIDDEN_LABELS.includes(label) && labels[label])
|
|
.map((label) => `${label}=${labels[label]}`),
|
|
[labels]
|
|
);
|
|
const displayLabels = useMemo(
|
|
() => allLabels.slice(0, !displayAll && displayMax ? displayMax : Infinity),
|
|
[allLabels, displayAll, displayMax]
|
|
);
|
|
|
|
if (displayLabels.length === 0 && emptyMessage) {
|
|
return (
|
|
<span className={styles.logsLabels}>
|
|
<span className={styles.logsLabel}>{emptyMessage}</span>
|
|
</span>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<span className={styles.logsLabels}>
|
|
{displayLabels.map((labelValue) => {
|
|
return addTooltip ? (
|
|
<Tooltip content={labelValue} key={labelValue} placement="top">
|
|
<LogLabel styles={styles}>{labelValue}</LogLabel>
|
|
</Tooltip>
|
|
) : (
|
|
<LogLabel styles={styles} tooltip={labelValue} key={labelValue}>
|
|
{labelValue}
|
|
</LogLabel>
|
|
);
|
|
})}
|
|
{displayLabels.length < allLabels.length && !displayAll && (
|
|
<Button
|
|
size="sm"
|
|
fill="outline"
|
|
variant="secondary"
|
|
className={styles.button}
|
|
aria-label={t('logs.log-labels.expand', 'Expand labels')}
|
|
onClick={() => {
|
|
setDisplayAll(true);
|
|
onDisplayMaxToggle?.(true);
|
|
}}
|
|
>
|
|
<Icon name="plus" size="xs" />
|
|
{allLabels.length - displayLabels.length}
|
|
</Button>
|
|
)}
|
|
{displayAll === true && (
|
|
<Button
|
|
size="sm"
|
|
fill="outline"
|
|
variant="secondary"
|
|
className={styles.button}
|
|
aria-label={t('logs.log-labels.collapse', 'Collapse labels')}
|
|
onClick={() => {
|
|
setDisplayAll(false);
|
|
onDisplayMaxToggle?.(false);
|
|
}}
|
|
>
|
|
<Icon name="minus" size="xs" />
|
|
</Button>
|
|
)}
|
|
</span>
|
|
);
|
|
}
|
|
);
|
|
LogLabels.displayName = 'LogLabels';
|
|
|
|
interface LogLabelsArrayProps {
|
|
labels: string[];
|
|
}
|
|
|
|
export const LogLabelsList = memo(({ labels }: LogLabelsArrayProps) => {
|
|
const styles = useStyles2(getStyles);
|
|
|
|
return (
|
|
<span className={styles.logsLabels}>
|
|
{labels.map((label) => (
|
|
<LogLabel key={label} styles={styles} tooltip={label}>
|
|
{getNormalizedFieldName(label)}
|
|
</LogLabel>
|
|
))}
|
|
</span>
|
|
);
|
|
});
|
|
LogLabelsList.displayName = 'LogLabelsList';
|
|
|
|
interface LogLabelProps {
|
|
styles: Record<string, string>;
|
|
tooltip?: string;
|
|
children: JSX.Element | string;
|
|
}
|
|
|
|
const LogLabel = forwardRef<HTMLSpanElement, LogLabelProps>(({ styles, tooltip, children }: LogLabelProps, ref) => {
|
|
return (
|
|
<span className={styles.logsLabel} ref={ref}>
|
|
<span className={styles.logsLabelValue} title={tooltip}>
|
|
{children}
|
|
</span>
|
|
</span>
|
|
);
|
|
});
|
|
LogLabel.displayName = 'LogLabel';
|
|
|
|
const getStyles = (theme: GrafanaTheme2) => {
|
|
return {
|
|
logsLabels: css({
|
|
display: 'inline-flex',
|
|
flexWrap: 'wrap',
|
|
fontSize: theme.typography.size.xs,
|
|
alignItems: 'center',
|
|
}),
|
|
logsLabel: css({
|
|
label: 'logs-label',
|
|
display: 'flex',
|
|
padding: theme.spacing(0, 0.25),
|
|
backgroundColor: theme.colors.background.secondary,
|
|
borderRadius: theme.shape.radius.default,
|
|
margin: theme.spacing(0.125, 0.5, 0, 0),
|
|
textOverflow: 'ellipsis',
|
|
whiteSpace: 'nowrap',
|
|
overflow: 'hidden',
|
|
maxHeight: theme.spacing(2),
|
|
}),
|
|
logsLabelValue: css({
|
|
label: 'logs-label__value',
|
|
display: 'inline-block',
|
|
maxWidth: theme.spacing(25),
|
|
textOverflow: 'ellipsis',
|
|
overflow: 'hidden',
|
|
}),
|
|
button: css({
|
|
height: theme.spacing(2.75),
|
|
}),
|
|
};
|
|
};
|