@grafana/ui
Version:
Grafana Components Library
240 lines (235 loc) • 9.42 kB
JavaScript
'use strict';
Object.defineProperty(exports, '__esModule', { value: true });
var jsxRuntime = require('react/jsx-runtime');
var css = require('@emotion/css');
var React = require('react');
var data = require('@grafana/data');
var i18n = require('@grafana/i18n');
var schema = require('@grafana/schema');
var types = require('../PanelChrome/types.cjs');
var ThemeContext = require('../../themes/ThemeContext.cjs');
var Button = require('../Button/Button.cjs');
var IconButton = require('../IconButton/IconButton.cjs');
require('../PanelChrome/index.cjs');
var Toggletip = require('../Toggletip/Toggletip.cjs');
var VizLayout = require('../VizLayout/VizLayout.cjs');
var FacetedLabelsFilter = require('../VizLegend/FacetedLabelsFilter.cjs');
var VizLegend = require('../VizLegend/VizLegend.cjs');
var utils = require('./utils.cjs');
var PanelContext = require('../PanelChrome/PanelContext.cjs');
"use strict";
function hasVisibleLegendSeries(config, data) {
return config.getSeries().some((s) => {
var _a, _b, _c;
const fieldIndex = s.props.dataFrameFieldIndex;
if (!fieldIndex) {
return false;
}
const field = (_a = data[fieldIndex.frameIndex]) == null ? void 0 : _a.fields[fieldIndex.fieldIndex];
if (!field || ((_c = (_b = field.config.custom) == null ? void 0 : _b.hideFrom) == null ? void 0 : _c.legend)) {
return false;
}
return true;
});
}
const PlotLegend = React.memo(function PlotLegend2({
data: data$1,
config,
placement,
calcs,
displayMode,
limit,
enableFacetedFilter = false,
facetedFilterPinned = false,
onPinnedToSidebarChange,
...vizLayoutLegendProps
}) {
const theme = ThemeContext.useTheme2();
const styles = ThemeContext.useStyles2(getPlotLegendStyles);
const { onToggleSeriesVisibility } = PanelContext.usePanelContext();
const [selectedLabels, setSelectedLabels] = React.useState({});
const facetedLabels = React.useMemo(
() => enableFacetedFilter && onToggleSeriesVisibility ? data.extractFacetedLabels(data$1) : {},
[enableFacetedFilter, onToggleSeriesVisibility, data$1]
);
const hasFacetedLabels = Object.keys(facetedLabels).length > 0;
const hasActiveFilters = Object.values(selectedLabels).some((v) => v.length > 0);
const legendItems = config.getSeries().map((s) => {
var _a, _b, _c, _d;
const seriesConfig = s.props;
const fieldIndex = seriesConfig.dataFrameFieldIndex;
const axisPlacement = config.getAxisPlacement(s.props.scaleKey);
if (!fieldIndex) {
return void 0;
}
const field = (_a = data$1[fieldIndex.frameIndex]) == null ? void 0 : _a.fields[fieldIndex.fieldIndex];
if (!field || ((_c = (_b = field.config.custom) == null ? void 0 : _b.hideFrom) == null ? void 0 : _c.legend)) {
return void 0;
}
const label = data.getFieldDisplayName(field, data$1[fieldIndex.frameIndex], data$1);
const scaleColor = data.getFieldSeriesColor(field, theme);
const seriesColor = scaleColor.color;
return {
disabled: !((_d = seriesConfig.show) != null ? _d : true),
fieldIndex,
color: seriesColor,
label,
yAxis: axisPlacement === schema.AxisPlacement.Left || axisPlacement === schema.AxisPlacement.Bottom ? 1 : 2,
getDisplayValues: () => utils.getDisplayValuesForCalcs(calcs, field, theme),
getItemKey: () => `${label}-${fieldIndex.frameIndex}-${fieldIndex.fieldIndex}`,
lineStyle: seriesConfig.lineStyle
};
}).filter((i) => i !== void 0);
const legendHasPrecedence = React.useMemo(() => {
if (!hasActiveFilters) {
return false;
}
const expectedVisible = data.resolveFacetedFilterNames(data$1, selectedLabels, data.getFieldDisplayName);
if (!expectedVisible) {
return false;
}
const expectedSet = new Set(expectedVisible);
const actuallyVisible = new Set(legendItems.filter((item) => !item.disabled).map((item) => item.label));
if (expectedSet.size !== actuallyVisible.size) {
return true;
}
return !Array.from(expectedSet).every((name) => actuallyVisible.has(name));
}, [hasActiveFilters, data$1, selectedLabels, legendItems]);
const handleLabelsChange = React.useCallback(
(selected) => {
setSelectedLabels(selected);
const visibleNames = data.resolveFacetedFilterNames(data$1, selected, data.getFieldDisplayName);
onToggleSeriesVisibility == null ? void 0 : onToggleSeriesVisibility(visibleNames, types.SeriesVisibilityChangeMode.SetExactly);
},
[data$1, onToggleSeriesVisibility]
);
const handleClearFilters = React.useCallback(() => {
setSelectedLabels({});
onToggleSeriesVisibility == null ? void 0 : onToggleSeriesVisibility(null, types.SeriesVisibilityChangeMode.SetExactly);
}, [onToggleSeriesVisibility]);
const handleToggleFilterDock = React.useCallback(() => {
onPinnedToSidebarChange == null ? void 0 : onPinnedToSidebarChange(!facetedFilterPinned);
}, [onPinnedToSidebarChange, facetedFilterPinned]);
const facetedFilter = hasFacetedLabels ? /* @__PURE__ */ jsxRuntime.jsx(
FacetedLabelsFilter.FacetedLabelsFilter,
{
labels: facetedLabels,
selected: selectedLabels,
onChange: handleLabelsChange,
dimmed: legendHasPrecedence
}
) : null;
const filterToggle = facetedFilter ? /* @__PURE__ */ jsxRuntime.jsx(
Toggletip.Toggletip,
{
content: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.filterPopoverContent, children: [
facetedFilter,
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.filterPopoverFooter, children: [
/* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "secondary", size: "sm", icon: "gf-pin", onClick: handleToggleFilterDock, children: i18n.t("grafana-ui.viz-legend.pin-filter", "Pin to sidebar") }),
hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(Button.Button, { variant: "secondary", size: "sm", icon: "times", onClick: handleClearFilters, children: i18n.t("grafana-ui.viz-legend.clear-filters", "Clear all") })
] })
] }),
placement: "bottom-start",
fitContent: true,
dismissOnScroll: true,
children: /* @__PURE__ */ jsxRuntime.jsx(
IconButton.IconButton,
{
name: "eye",
size: "md",
tooltip: i18n.t("grafana-ui.viz-legend.series-visibility", "Series visibility"),
variant: hasActiveFilters ? "primary" : void 0,
"data-testid": "faceted-labels-filter-toggle"
}
)
}
) : null;
const legend = /* @__PURE__ */ jsxRuntime.jsx(
VizLegend.VizLegend,
{
placement,
items: legendItems,
displayMode,
sortBy: vizLayoutLegendProps.sortBy,
sortDesc: vizLayoutLegendProps.sortDesc,
isSortable: true,
limit,
filterAction: !facetedFilterPinned ? filterToggle : void 0,
overflow: vizLayoutLegendProps.overflow
}
);
if (facetedFilterPinned && facetedFilter) {
return /* @__PURE__ */ jsxRuntime.jsx(VizLayout.VizLayout.Legend, { placement, ...vizLayoutLegendProps, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.legendWithFilter, children: [
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.filterContent, children: [
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: styles.filterDockedActions, children: [
hasActiveFilters && /* @__PURE__ */ jsxRuntime.jsx(
IconButton.IconButton,
{
name: "filter-minus",
size: "sm",
tooltip: i18n.t("grafana-ui.viz-legend.clear-filters", "Clear all"),
onClick: handleClearFilters
}
),
/* @__PURE__ */ jsxRuntime.jsx(
IconButton.IconButton,
{
name: "times",
size: "sm",
tooltip: i18n.t("grafana-ui.viz-legend.unpin-sidebar", "Unpin"),
onClick: handleToggleFilterDock
}
)
] }),
facetedFilter
] }),
/* @__PURE__ */ jsxRuntime.jsx("div", { className: styles.legendContent, children: legend })
] }) });
}
return /* @__PURE__ */ jsxRuntime.jsx(VizLayout.VizLayout.Legend, { placement, ...vizLayoutLegendProps, children: legend });
});
PlotLegend.displayName = "PlotLegend";
const getPlotLegendStyles = (theme) => ({
legendWithFilter: css.css({
display: "flex",
width: "100%",
height: "100%",
gap: theme.spacing(1),
overflow: "hidden"
}),
legendContent: css.css({
flex: 1,
minWidth: 0,
overflow: "auto"
}),
filterContent: css.css({
position: "relative",
maxWidth: "50%",
overflow: "auto",
borderRight: `1px solid ${theme.colors.border.weak}`,
paddingRight: theme.spacing(1)
}),
filterDockedActions: css.css({
position: "absolute",
zIndex: 1,
right: theme.spacing(0.5),
top: theme.spacing(0.5),
display: "flex",
gap: theme.spacing(0.25),
color: theme.colors.text.secondary
}),
filterPopoverContent: css.css({
margin: theme.spacing(-3, -2),
maxHeight: 400,
overflow: "auto"
}),
filterPopoverFooter: css.css({
display: "flex",
gap: theme.spacing(1),
borderTop: `1px solid ${theme.colors.border.medium}`,
padding: theme.spacing(1)
})
});
exports.PlotLegend = PlotLegend;
exports.hasVisibleLegendSeries = hasVisibleLegendSeries;
//# sourceMappingURL=PlotLegend.cjs.map