UNPKG

@grafana/ui

Version:
235 lines (232 loc) • 8.97 kB
import { jsx, jsxs } from 'react/jsx-runtime'; import { css } from '@emotion/css'; import { memo, useState, useMemo, useCallback } from 'react'; import { extractFacetedLabels, getFieldDisplayName, getFieldSeriesColor, resolveFacetedFilterNames } from '@grafana/data'; import { t } from '@grafana/i18n'; import { AxisPlacement } from '@grafana/schema'; import { SeriesVisibilityChangeMode } from '../PanelChrome/types.mjs'; import { useTheme2, useStyles2 } from '../../themes/ThemeContext.mjs'; import { Button } from '../Button/Button.mjs'; import { IconButton } from '../IconButton/IconButton.mjs'; import '../PanelChrome/index.mjs'; import { Toggletip } from '../Toggletip/Toggletip.mjs'; import { VizLayout } from '../VizLayout/VizLayout.mjs'; import { FacetedLabelsFilter } from '../VizLegend/FacetedLabelsFilter.mjs'; import { VizLegend } from '../VizLegend/VizLegend.mjs'; import { getDisplayValuesForCalcs } from './utils.mjs'; import { usePanelContext } from '../PanelChrome/PanelContext.mjs'; "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 = memo(function PlotLegend2({ data, config, placement, calcs, displayMode, limit, enableFacetedFilter = false, facetedFilterPinned = false, onPinnedToSidebarChange, ...vizLayoutLegendProps }) { const theme = useTheme2(); const styles = useStyles2(getPlotLegendStyles); const { onToggleSeriesVisibility } = usePanelContext(); const [selectedLabels, setSelectedLabels] = useState({}); const facetedLabels = useMemo( () => enableFacetedFilter && onToggleSeriesVisibility ? extractFacetedLabels(data) : {}, [enableFacetedFilter, onToggleSeriesVisibility, data] ); 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[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 = getFieldDisplayName(field, data[fieldIndex.frameIndex], data); const scaleColor = getFieldSeriesColor(field, theme); const seriesColor = scaleColor.color; return { disabled: !((_d = seriesConfig.show) != null ? _d : true), fieldIndex, color: seriesColor, label, yAxis: axisPlacement === AxisPlacement.Left || axisPlacement === AxisPlacement.Bottom ? 1 : 2, getDisplayValues: () => getDisplayValuesForCalcs(calcs, field, theme), getItemKey: () => `${label}-${fieldIndex.frameIndex}-${fieldIndex.fieldIndex}`, lineStyle: seriesConfig.lineStyle }; }).filter((i) => i !== void 0); const legendHasPrecedence = useMemo(() => { if (!hasActiveFilters) { return false; } const expectedVisible = resolveFacetedFilterNames(data, selectedLabels, 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, selectedLabels, legendItems]); const handleLabelsChange = useCallback( (selected) => { setSelectedLabels(selected); const visibleNames = resolveFacetedFilterNames(data, selected, getFieldDisplayName); onToggleSeriesVisibility == null ? void 0 : onToggleSeriesVisibility(visibleNames, SeriesVisibilityChangeMode.SetExactly); }, [data, onToggleSeriesVisibility] ); const handleClearFilters = useCallback(() => { setSelectedLabels({}); onToggleSeriesVisibility == null ? void 0 : onToggleSeriesVisibility(null, SeriesVisibilityChangeMode.SetExactly); }, [onToggleSeriesVisibility]); const handleToggleFilterDock = useCallback(() => { onPinnedToSidebarChange == null ? void 0 : onPinnedToSidebarChange(!facetedFilterPinned); }, [onPinnedToSidebarChange, facetedFilterPinned]); const facetedFilter = hasFacetedLabels ? /* @__PURE__ */ jsx( FacetedLabelsFilter, { labels: facetedLabels, selected: selectedLabels, onChange: handleLabelsChange, dimmed: legendHasPrecedence } ) : null; const filterToggle = facetedFilter ? /* @__PURE__ */ jsx( Toggletip, { content: /* @__PURE__ */ jsxs("div", { className: styles.filterPopoverContent, children: [ facetedFilter, /* @__PURE__ */ jsxs("div", { className: styles.filterPopoverFooter, children: [ /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", icon: "gf-pin", onClick: handleToggleFilterDock, children: t("grafana-ui.viz-legend.pin-filter", "Pin to sidebar") }), hasActiveFilters && /* @__PURE__ */ jsx(Button, { variant: "secondary", size: "sm", icon: "times", onClick: handleClearFilters, children: t("grafana-ui.viz-legend.clear-filters", "Clear all") }) ] }) ] }), placement: "bottom-start", fitContent: true, dismissOnScroll: true, children: /* @__PURE__ */ jsx( IconButton, { name: "eye", size: "md", tooltip: t("grafana-ui.viz-legend.series-visibility", "Series visibility"), variant: hasActiveFilters ? "primary" : void 0, "data-testid": "faceted-labels-filter-toggle" } ) } ) : null; const legend = /* @__PURE__ */ jsx( 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__ */ jsx(VizLayout.Legend, { placement, ...vizLayoutLegendProps, children: /* @__PURE__ */ jsxs("div", { className: styles.legendWithFilter, children: [ /* @__PURE__ */ jsxs("div", { className: styles.filterContent, children: [ /* @__PURE__ */ jsxs("div", { className: styles.filterDockedActions, children: [ hasActiveFilters && /* @__PURE__ */ jsx( IconButton, { name: "filter-minus", size: "sm", tooltip: t("grafana-ui.viz-legend.clear-filters", "Clear all"), onClick: handleClearFilters } ), /* @__PURE__ */ jsx( IconButton, { name: "times", size: "sm", tooltip: t("grafana-ui.viz-legend.unpin-sidebar", "Unpin"), onClick: handleToggleFilterDock } ) ] }), facetedFilter ] }), /* @__PURE__ */ jsx("div", { className: styles.legendContent, children: legend }) ] }) }); } return /* @__PURE__ */ jsx(VizLayout.Legend, { placement, ...vizLayoutLegendProps, children: legend }); }); PlotLegend.displayName = "PlotLegend"; const getPlotLegendStyles = (theme) => ({ legendWithFilter: css({ display: "flex", width: "100%", height: "100%", gap: theme.spacing(1), overflow: "hidden" }), legendContent: css({ flex: 1, minWidth: 0, overflow: "auto" }), filterContent: css({ position: "relative", maxWidth: "50%", overflow: "auto", borderRight: `1px solid ${theme.colors.border.weak}`, paddingRight: theme.spacing(1) }), filterDockedActions: 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({ margin: theme.spacing(-3, -2), maxHeight: 400, overflow: "auto" }), filterPopoverFooter: css({ display: "flex", gap: theme.spacing(1), borderTop: `1px solid ${theme.colors.border.medium}`, padding: theme.spacing(1) }) }); export { PlotLegend, hasVisibleLegendSeries }; //# sourceMappingURL=PlotLegend.mjs.map