mirror of
https://github.com/openobserve/openobserve.git
synced 2026-11-04 08:09:27 +00:00
Co-authored-by: ktx-vaidehi <vaidehi.akhani@kiara.tech> Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: ktx-vaidehi <134508096+ktx-vaidehi@users.noreply.github.com>
680 lines
24 KiB
TypeScript
680 lines
24 KiB
TypeScript
import { computed, ref, watch, Ref, ComputedRef } from "vue";
|
|
import { useI18nTyped } from "@/types/i18n";
|
|
import {
|
|
shouldShowLegendsToggle,
|
|
shouldShowLegendPosition,
|
|
shouldShowLegendType,
|
|
shouldShowLegendWidth,
|
|
shouldShowLegendHeight,
|
|
shouldApplyChartAlign,
|
|
shouldShowTopResultsConfig,
|
|
shouldShowAreaLineStyleConfig,
|
|
shouldShowNoValueReplacement,
|
|
shouldShowAxisConfig,
|
|
shouldShowAxisBorderConfig,
|
|
shouldShowCartesianAxisConfig,
|
|
shouldShowGridlines,
|
|
shouldShowAxisLabelConfig,
|
|
shouldShowLineThickness,
|
|
shouldShowDrilldown,
|
|
shouldShowTimeShift,
|
|
} from "@/utils/dashboard/configUtils";
|
|
import {
|
|
SectionId,
|
|
ORDERED_SECTION_IDS,
|
|
DEFAULT_EXPANDED_SECTIONS,
|
|
} from "@/utils/dashboard/searchLabelsConfig";
|
|
import { isExemplarEligible } from "@/utils/dashboard/exemplars/exemplarEligibility";
|
|
import { PROMETHEUS_HISTOGRAM_MODE } from "@/utils/dashboard/promql/convertPromQLHeatmapChart";
|
|
import { isNumericYField } from "@/utils/dashboard/heatmapYAxis";
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Types
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export interface ConfigOption {
|
|
label: string | string[];
|
|
visible?: boolean;
|
|
}
|
|
|
|
export interface ConfigOptions {
|
|
[sectionId: string]: {
|
|
[optionId: string]: ConfigOption;
|
|
};
|
|
}
|
|
|
|
export const THRESHOLD_CHART_TYPES: readonly string[] = [
|
|
"line",
|
|
"area",
|
|
"area-stacked",
|
|
"bar",
|
|
"h-bar",
|
|
"scatter",
|
|
"gauge",
|
|
"metric",
|
|
"state-timeline",
|
|
];
|
|
|
|
export const shouldShowThresholds = (dashboardPanelData: any): boolean =>
|
|
THRESHOLD_CHART_TYPES.includes(dashboardPanelData?.data?.type);
|
|
|
|
// Charts whose series have names an override can match; tables keep Column Formatting.
|
|
export const SERIES_OVERRIDE_CHART_TYPES: readonly string[] = [
|
|
"line",
|
|
"area",
|
|
"area-stacked",
|
|
"bar",
|
|
"h-bar",
|
|
"stacked",
|
|
"h-stacked",
|
|
"scatter",
|
|
"pie",
|
|
"donut",
|
|
"gauge",
|
|
"metric",
|
|
"state-timeline",
|
|
];
|
|
|
|
export const shouldShowSeriesOverrides = (dashboardPanelData: any): boolean =>
|
|
SERIES_OVERRIDE_CHART_TYPES.includes(dashboardPanelData?.data?.type);
|
|
|
|
// Histogram mode always orders rows by le, so the sort control would do nothing there.
|
|
export const shouldShowHeatmapYSort = (dashboardPanelData: any, promqlMode: boolean): boolean =>
|
|
dashboardPanelData?.data?.type === "heatmap" &&
|
|
!(promqlMode && dashboardPanelData?.data?.config?.heatmap_mode === PROMETHEUS_HISTOGRAM_MODE);
|
|
|
|
// Bucketing is generated in the builder's SQL, so custom SQL and PromQL keep the y values they return.
|
|
export const shouldShowHeatmapYBucket = (dashboardPanelData: any, promqlMode: boolean): boolean => {
|
|
const query = dashboardPanelData?.data?.queries?.[dashboardPanelData?.layout?.currentQueryIndex];
|
|
return (
|
|
!promqlMode &&
|
|
dashboardPanelData?.data?.type === "heatmap" &&
|
|
!query?.customQuery &&
|
|
isNumericYField(query?.fields?.y?.[0], dashboardPanelData)
|
|
);
|
|
};
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Pure filter helpers (exported so they can be unit-tested independently)
|
|
// ---------------------------------------------------------------------------
|
|
|
|
export function filterOption(option: ConfigOption | undefined, normalizedQuery: string): boolean {
|
|
if (!option || option.visible === false) return false;
|
|
if (!normalizedQuery) return true;
|
|
const labels = Array.isArray(option.label) ? option.label : [option.label];
|
|
return labels.some((l) => matchesLabel(l.toLowerCase(), normalizedQuery));
|
|
}
|
|
|
|
// A plural query ("limits") also finds the singular, but only as a whole word end, so "axis" never widens to "axi".
|
|
function matchesLabel(label: string, query: string): boolean {
|
|
if (label.includes(query)) return true;
|
|
if (query.length < 4 || !query.endsWith("s")) return false;
|
|
const singular = query.slice(0, -1);
|
|
let at = label.indexOf(singular);
|
|
while (at !== -1) {
|
|
if (!/\p{L}/u.test(label.charAt(at + singular.length))) return true;
|
|
at = label.indexOf(singular, at + 1);
|
|
}
|
|
return false;
|
|
}
|
|
|
|
export function filterSection(
|
|
sectionOptions: Record<string, ConfigOption> | undefined,
|
|
normalizedQuery: string,
|
|
): boolean {
|
|
if (!sectionOptions) return false;
|
|
return Object.values(sectionOptions).some((opt) => filterOption(opt, normalizedQuery));
|
|
}
|
|
|
|
// ---------------------------------------------------------------------------
|
|
// Main composable
|
|
// ---------------------------------------------------------------------------
|
|
|
|
/**
|
|
* Single composable for the config panel.
|
|
*
|
|
* To add a new config section:
|
|
* 1. Add one entry in searchLabelsConfig.ts (SECTION_DEFS)
|
|
* 2. Add its options in the configOptions computed below
|
|
* 3. Add its UI in ConfigPanel.vue
|
|
*/
|
|
export function useConfigPanel(
|
|
dashboardPanelData: any,
|
|
promqlMode: Ref<boolean>,
|
|
dashboardPanelDataPageKey: unknown,
|
|
showTrellisConfig: ComputedRef<boolean>,
|
|
showColorPalette: ComputedRef<boolean>,
|
|
isPivotMode: ComputedRef<boolean>,
|
|
) {
|
|
const { t, tm } = useI18nTyped();
|
|
|
|
const isStateTimeline = computed(() => dashboardPanelData.data.type === "state-timeline");
|
|
|
|
// ── Config options ────────────────────────────────────────────────────────
|
|
|
|
const configOptions = computed<ConfigOptions>(() => ({
|
|
general: {
|
|
description: { label: t("dashboard.description") },
|
|
step: {
|
|
label: t("dashboard.stepValue"),
|
|
visible: !!promqlMode.value,
|
|
},
|
|
"show-exemplars": {
|
|
label: t("dashboard.showExemplarsLabel"),
|
|
visible: !!promqlMode.value && isExemplarEligible(dashboardPanelData.data),
|
|
},
|
|
"panel-default-time": { label: t("dashboard.panelTimeEnabled") },
|
|
"promql-chart-config": {
|
|
label: t("dashboard.promqlChartConfig"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type !== "geomap" &&
|
|
dashboardPanelData.data.type !== "maps" &&
|
|
dashboardPanelData.data.type !== "table",
|
|
},
|
|
},
|
|
promqlTable: {
|
|
"promql-table-mode": {
|
|
label: t("dashboard.promqlTableMode"),
|
|
visible: !!promqlMode.value && dashboardPanelData.data.type === "table",
|
|
},
|
|
"table-aggregations": {
|
|
label: t("dashboard.tableAggregations"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
dashboardPanelData.data.config?.promql_table_mode === "all",
|
|
},
|
|
"visible-columns": {
|
|
label: t("dashboard.visibleColumns"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
(dashboardPanelData.data.config?.promql_table_mode === "all" ||
|
|
dashboardPanelData.data.config?.promql_table_mode === "expanded_timeseries"),
|
|
},
|
|
"hidden-columns": {
|
|
label: t("dashboard.hiddenColumns"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
(dashboardPanelData.data.config?.promql_table_mode === "all" ||
|
|
dashboardPanelData.data.config?.promql_table_mode === "expanded_timeseries"),
|
|
},
|
|
"sticky-first-column": {
|
|
label: t("dashboard.stickyFirstColumn"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
(dashboardPanelData.data.config?.promql_table_mode === "all" ||
|
|
dashboardPanelData.data.config?.promql_table_mode === "expanded_timeseries"),
|
|
},
|
|
"sticky-columns": {
|
|
label: t("dashboard.stickyColumns"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
(dashboardPanelData.data.config?.promql_table_mode === "all" ||
|
|
dashboardPanelData.data.config?.promql_table_mode === "expanded_timeseries"),
|
|
},
|
|
"configure-column-order": {
|
|
label: t("dashboard.configureColumnOrder"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
(dashboardPanelData.data.config?.promql_table_mode === "all" ||
|
|
dashboardPanelData.data.config?.promql_table_mode === "expanded_timeseries"),
|
|
},
|
|
},
|
|
geographic: {
|
|
"geographic-config": {
|
|
label: t("dashboard.configSectionGeographic"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
(dashboardPanelData.data.type === "geomap" || dashboardPanelData.data.type === "maps"),
|
|
},
|
|
},
|
|
legend: {
|
|
"show-legends": {
|
|
label: t("dashboard.showLegendsLabel"),
|
|
visible: shouldShowLegendsToggle(dashboardPanelData),
|
|
},
|
|
"legend-position": {
|
|
label: t("dashboard.legendsPositionLabel"),
|
|
visible: shouldShowLegendPosition(dashboardPanelData),
|
|
},
|
|
"legend-type": {
|
|
label: t("dashboard.legendsType"),
|
|
visible: shouldShowLegendType(dashboardPanelData),
|
|
},
|
|
"legend-size": {
|
|
label: [t("common.legendWidth"), t("dashboard.legendHeight")],
|
|
visible:
|
|
shouldShowLegendWidth(dashboardPanelData) || shouldShowLegendHeight(dashboardPanelData),
|
|
},
|
|
"chart-align": {
|
|
label: t("dashboard.chartAlign"),
|
|
visible: shouldApplyChartAlign(dashboardPanelData),
|
|
},
|
|
"promql-legend": {
|
|
label: t("dashboard.query"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type !== "geomap" &&
|
|
dashboardPanelData.data.type !== "maps",
|
|
},
|
|
"promql-legend-label": {
|
|
label: [t("dashboard.legendLabel"), t("common.legend")],
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type !== "geomap" &&
|
|
dashboardPanelData.data.type !== "maps",
|
|
},
|
|
},
|
|
data: {
|
|
unit: { label: t("dashboard.unitLabel"), visible: !isStateTimeline.value },
|
|
"custom-unit": {
|
|
label: t("dashboard.customunitLabel"),
|
|
visible: !isStateTimeline.value && dashboardPanelData.data.config.unit === "custom",
|
|
},
|
|
decimals: { label: t("dashboard.decimals"), visible: !isStateTimeline.value },
|
|
"heatmap-color-range": {
|
|
label: [t("dashboard.heatmapColorMin"), t("dashboard.heatmapColorMax")],
|
|
visible: dashboardPanelData.data.type === "heatmap",
|
|
},
|
|
"heatmap-palette": {
|
|
label: t("dashboard.heatmapPalette"),
|
|
visible: dashboardPanelData.data.type === "heatmap",
|
|
},
|
|
"heatmap-palette-reverse": {
|
|
label: t("dashboard.heatmapPaletteReverse"),
|
|
visible: dashboardPanelData.data.type === "heatmap",
|
|
},
|
|
"heatmap-color-scale": {
|
|
label: t("dashboard.heatmapColorScale"),
|
|
visible: dashboardPanelData.data.type === "heatmap",
|
|
},
|
|
"heatmap-histogram-mode": {
|
|
label: t("dashboard.heatmapHistogramMode"),
|
|
visible: !!promqlMode.value && dashboardPanelData.data.type === "heatmap",
|
|
},
|
|
"bucket-unit": {
|
|
label: t("dashboard.bucketUnit"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "heatmap" &&
|
|
dashboardPanelData.data.config?.heatmap_mode === PROMETHEUS_HISTOGRAM_MODE,
|
|
},
|
|
"bucket-unit-custom": {
|
|
label: t("dashboard.bucketUnitCustom"),
|
|
visible:
|
|
!!promqlMode.value &&
|
|
dashboardPanelData.data.type === "heatmap" &&
|
|
dashboardPanelData.data.config?.heatmap_mode === PROMETHEUS_HISTOGRAM_MODE &&
|
|
dashboardPanelData.data.config?.bucket_unit === "custom",
|
|
},
|
|
limit: {
|
|
label: t("dashboard.queryLimit"),
|
|
visible:
|
|
!promqlMode.value &&
|
|
!dashboardPanelData.data.queries[dashboardPanelData.layout.currentQueryIndex]
|
|
?.customQuery,
|
|
},
|
|
"top-results": {
|
|
label: t("dashboard.showTopNValues"),
|
|
visible: shouldShowTopResultsConfig(dashboardPanelData, promqlMode.value),
|
|
},
|
|
"top-results-others": {
|
|
label: t("dashboard.addOthersSeries"),
|
|
visible: shouldShowTopResultsConfig(dashboardPanelData, promqlMode.value),
|
|
},
|
|
"connect-nulls": {
|
|
label: t("dashboard.connectNullValues"),
|
|
visible: shouldShowAreaLineStyleConfig(dashboardPanelData),
|
|
},
|
|
"no-value-replacement": {
|
|
label: t("dashboard.noValueReplacement"),
|
|
visible: shouldShowNoValueReplacement(dashboardPanelData, promqlMode.value),
|
|
},
|
|
"query-label": {
|
|
label: t("dashboard.multiSqlQueryLabel"),
|
|
visible:
|
|
!promqlMode.value &&
|
|
dashboardPanelData.data.queries.length > 1 &&
|
|
dashboardPanelData.data.type !== "geomap" &&
|
|
dashboardPanelData.data.type !== "maps",
|
|
},
|
|
},
|
|
stateTimeline: {
|
|
"merge-gaps": {
|
|
label: t("dashboard.stateTimelineMergeGaps"),
|
|
visible: isStateTimeline.value,
|
|
},
|
|
"row-size": {
|
|
label: [t("dashboard.stateTimelineRowHeight"), t("dashboard.stateTimelineRowGap")],
|
|
visible: isStateTimeline.value,
|
|
},
|
|
"show-state-text": {
|
|
label: t("dashboard.stateTimelineShowStateText"),
|
|
visible: isStateTimeline.value,
|
|
},
|
|
"row-sort": {
|
|
label: t("dashboard.stateTimelineRowSort"),
|
|
visible: isStateTimeline.value,
|
|
},
|
|
"tooltip-mode": {
|
|
label: t("dashboard.stateTimelineTooltipMode"),
|
|
visible: isStateTimeline.value,
|
|
},
|
|
},
|
|
axis: {
|
|
"axis-width": {
|
|
label: t("common.axisWidth"),
|
|
visible: shouldShowAxisConfig(dashboardPanelData),
|
|
},
|
|
"heatmap-y-sort": {
|
|
label: t("dashboard.heatmapYSort"),
|
|
visible: shouldShowHeatmapYSort(dashboardPanelData, !!promqlMode.value),
|
|
},
|
|
"heatmap-y-bucket": {
|
|
label: [
|
|
t("dashboard.heatmapYBucket"),
|
|
t("dashboard.heatmapYBucketSize"),
|
|
t("dashboard.heatmapYBucketBase"),
|
|
],
|
|
visible: shouldShowHeatmapYBucket(dashboardPanelData, !!promqlMode.value),
|
|
},
|
|
"heatmap-y-range": {
|
|
label: [t("dashboard.heatmapYMin"), t("dashboard.heatmapYMax")],
|
|
visible:
|
|
shouldShowHeatmapYBucket(dashboardPanelData, !!promqlMode.value) &&
|
|
!!dashboardPanelData.data.config?.heatmap_y_bucket?.mode,
|
|
},
|
|
"axis-border": {
|
|
label: t("dashboard.showBorder"),
|
|
visible: shouldShowAxisBorderConfig(dashboardPanelData),
|
|
},
|
|
"y-axis": {
|
|
label: [t("common.yAxisMin"), t("common.yAxisMax")],
|
|
visible: shouldShowCartesianAxisConfig(dashboardPanelData),
|
|
},
|
|
"axis-label-mode": {
|
|
label: t("dashboard.axisLabelMode"),
|
|
visible: !promqlMode.value && shouldShowCartesianAxisConfig(dashboardPanelData),
|
|
},
|
|
gridlines: {
|
|
label: t("dashboard.showGridlines"),
|
|
visible: shouldShowGridlines(dashboardPanelData),
|
|
},
|
|
},
|
|
labels: {
|
|
"label-position": {
|
|
label: t("dashboard.labelPosition"),
|
|
visible: shouldShowCartesianAxisConfig(dashboardPanelData),
|
|
},
|
|
"label-rotate": {
|
|
label: [t("dashboard.labelRotate")],
|
|
visible: shouldShowCartesianAxisConfig(dashboardPanelData),
|
|
},
|
|
"axis-label": {
|
|
label: [t("dashboard.axisLabelRotate"), t("dashboard.axisLabelTruncate")],
|
|
visible: shouldShowAxisLabelConfig(dashboardPanelData),
|
|
},
|
|
},
|
|
lineStyle: {
|
|
symbol: {
|
|
label: t("dashboard.showSymbol"),
|
|
visible: shouldShowAreaLineStyleConfig(dashboardPanelData),
|
|
},
|
|
interpolation: {
|
|
label: t("dashboard.lineInterpolation"),
|
|
visible: shouldShowAreaLineStyleConfig(dashboardPanelData),
|
|
},
|
|
"line-thickness": {
|
|
label: t("dashboard.lineThickness"),
|
|
visible: shouldShowLineThickness(dashboardPanelData, promqlMode.value),
|
|
},
|
|
},
|
|
table: {
|
|
wrap: {
|
|
label: t("dashboard.wraptext"),
|
|
visible: dashboardPanelData.data.type === "table",
|
|
},
|
|
transpose: {
|
|
label: t("dashboard.tableTranspose"),
|
|
visible: !promqlMode.value && dashboardPanelData.data.type === "table",
|
|
},
|
|
"dynamic-columns": {
|
|
label: t("dashboard.tableDynamicColumns"),
|
|
visible: !promqlMode.value && dashboardPanelData.data.type === "table",
|
|
},
|
|
filtering: {
|
|
label: t("dashboard.tableFiltering"),
|
|
visible: dashboardPanelData.data.type === "table",
|
|
},
|
|
pagination: {
|
|
label: t("dashboard.pagination"),
|
|
visible: dashboardPanelData.data.type === "table",
|
|
},
|
|
"rows-per-page": {
|
|
label: t("dashboard.rowsPerPage"),
|
|
visible:
|
|
!!dashboardPanelData.data.config.table_pagination &&
|
|
dashboardPanelData.data.type === "table",
|
|
},
|
|
},
|
|
pivotTable: {
|
|
"pivot-show-row-totals": {
|
|
label: t("dashboard.pivotShowRowTotals"),
|
|
visible: !promqlMode.value && dashboardPanelData.data.type === "table" && isPivotMode.value,
|
|
},
|
|
"pivot-show-col-totals": {
|
|
label: t("dashboard.pivotShowColTotals"),
|
|
visible: !promqlMode.value && dashboardPanelData.data.type === "table" && isPivotMode.value,
|
|
},
|
|
"pivot-sticky-row-totals": {
|
|
label: t("dashboard.pivotStickyRowTotals"),
|
|
visible:
|
|
!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
isPivotMode.value &&
|
|
!!dashboardPanelData.data.config.table_pivot_show_col_totals,
|
|
},
|
|
"pivot-sticky-col-totals": {
|
|
label: t("dashboard.pivotStickyColTotals"),
|
|
visible:
|
|
!promqlMode.value &&
|
|
dashboardPanelData.data.type === "table" &&
|
|
isPivotMode.value &&
|
|
!!dashboardPanelData.data.config.table_pivot_show_row_totals,
|
|
},
|
|
},
|
|
valueTransformations: {
|
|
"value-transformations": { label: t("dashboard.configSectionValueTransformations") },
|
|
},
|
|
fieldOverrides: {
|
|
"field-overrides": {
|
|
label: [
|
|
t("dashboard.configSectionFieldOverrides"),
|
|
...(typeof tm === "function" &&
|
|
Array.isArray(tm("dashboard.configSectionFieldOverridesAliases"))
|
|
? (tm("dashboard.configSectionFieldOverridesAliases") as string[])
|
|
: []),
|
|
],
|
|
},
|
|
},
|
|
seriesOverrides: {
|
|
"series-overrides": {
|
|
label: [
|
|
t("dashboard.configSectionSeriesOverrides"),
|
|
t("dashboard.addSeriesOverride"),
|
|
t("dashboard.seriesFormattingTitle"),
|
|
...(typeof tm === "function" &&
|
|
Array.isArray(tm("dashboard.configSectionSeriesOverridesAliases"))
|
|
? (tm("dashboard.configSectionSeriesOverridesAliases") as string[])
|
|
: []),
|
|
],
|
|
visible: shouldShowSeriesOverrides(dashboardPanelData),
|
|
},
|
|
},
|
|
map: {
|
|
"map-config": {
|
|
label: t("dashboard.configSectionMap"),
|
|
visible:
|
|
dashboardPanelData.data.type === "geomap" || dashboardPanelData.data.type === "maps",
|
|
},
|
|
},
|
|
gauge: {
|
|
"gauge-min": {
|
|
label: t("dashboard.gaugeMinValue"),
|
|
visible: dashboardPanelData.data.type === "gauge",
|
|
},
|
|
"gauge-max": {
|
|
label: t("dashboard.gaugeMaxValue"),
|
|
visible: dashboardPanelData.data.type === "gauge",
|
|
},
|
|
},
|
|
layout: {
|
|
"trellis-layout": {
|
|
label: t("dashboard.trellisLayout"),
|
|
visible: showTrellisConfig.value,
|
|
},
|
|
"trellis-columns": {
|
|
label: t("dashboard.numOfColumns"),
|
|
visible:
|
|
showTrellisConfig.value && dashboardPanelData.data.config.trellis?.layout === "custom",
|
|
},
|
|
"trellis-group-by": {
|
|
label: t("dashboard.groupMultiYAxisTrellis"),
|
|
visible: showTrellisConfig.value && dashboardPanelData.data.config.trellis?.layout != null,
|
|
},
|
|
},
|
|
colors: {
|
|
colors: {
|
|
label: [t("dashboard.colorPalette"), t("dashboard.colorBySeriesTitle")],
|
|
visible: showColorPalette.value,
|
|
},
|
|
},
|
|
drilldown: {
|
|
drilldown: {
|
|
label: t("dashboard.drilldown"),
|
|
visible: shouldShowDrilldown(dashboardPanelData, dashboardPanelDataPageKey as string),
|
|
},
|
|
},
|
|
comparison: {
|
|
comparison: {
|
|
label: t("dashboard.comparisonAgainst"),
|
|
visible: shouldShowTimeShift(
|
|
dashboardPanelData,
|
|
promqlMode.value,
|
|
dashboardPanelDataPageKey as string,
|
|
),
|
|
},
|
|
},
|
|
markLines: {
|
|
"mark-lines": {
|
|
label: t("dashboard.markLines"),
|
|
visible: shouldShowCartesianAxisConfig(dashboardPanelData),
|
|
},
|
|
},
|
|
thresholds: {
|
|
thresholds: {
|
|
label: [
|
|
t("dashboard.thresholds"),
|
|
t("dashboard.thresholdShowAsLine"),
|
|
t("dashboard.thresholdAddStep"),
|
|
],
|
|
visible: shouldShowThresholds(dashboardPanelData),
|
|
},
|
|
},
|
|
background: {
|
|
background: {
|
|
label: t("dashboard.colorMode"),
|
|
visible: dashboardPanelData.data.type === "metric",
|
|
},
|
|
},
|
|
sparkline: {
|
|
sparkline: {
|
|
label: t("dashboard.configSectionSparkline"),
|
|
visible: dashboardPanelData.data.type === "metric",
|
|
},
|
|
},
|
|
}));
|
|
|
|
// ── Search state ──────────────────────────────────────────────────────────
|
|
|
|
const searchQuery = ref("");
|
|
const expandedSections = ref<Record<string, boolean>>({
|
|
...DEFAULT_EXPANDED_SECTIONS,
|
|
});
|
|
const beforeSearchExpandedSections = ref<Record<string, boolean> | null>(null);
|
|
|
|
const normalizedSearchQuery = computed(() => (searchQuery.value ?? "").trim().toLowerCase());
|
|
|
|
const saveExpansionState = () => {
|
|
beforeSearchExpandedSections.value = { ...expandedSections.value };
|
|
Object.keys(expandedSections.value).forEach((key) => {
|
|
expandedSections.value[key] = true;
|
|
});
|
|
};
|
|
|
|
const restoreExpansionState = () => {
|
|
if (beforeSearchExpandedSections.value) {
|
|
expandedSections.value = { ...beforeSearchExpandedSections.value };
|
|
beforeSearchExpandedSections.value = null;
|
|
}
|
|
};
|
|
|
|
watch(searchQuery, (newQ, oldQ) => {
|
|
if (newQ && !oldQ) saveExpansionState();
|
|
else if (!newQ && oldQ) restoreExpansionState();
|
|
});
|
|
|
|
// ── Bound filter helpers ──────────────────────────────────────────────────
|
|
|
|
const isConfigOptionVisible = (sectionId: SectionId, optionId: string): boolean =>
|
|
filterOption(configOptions.value[sectionId]?.[optionId], normalizedSearchQuery.value);
|
|
|
|
const isSectionVisible = (sectionId: SectionId): boolean =>
|
|
filterSection(configOptions.value[sectionId], normalizedSearchQuery.value);
|
|
|
|
// ── Expand / collapse ─────────────────────────────────────────────────────
|
|
|
|
const isExpanded = (key: string): boolean => expandedSections.value[key] ?? false;
|
|
|
|
const toggleSection = (sectionId: SectionId) => {
|
|
expandedSections.value[sectionId] = !isExpanded(sectionId);
|
|
};
|
|
|
|
const resetSearch = () => {
|
|
searchQuery.value = "";
|
|
restoreExpansionState();
|
|
};
|
|
|
|
const allSectionsExpanded = computed(() =>
|
|
Object.values(expandedSections.value).every((v) => v === true),
|
|
);
|
|
|
|
const toggleAllSections = () => {
|
|
const expand = !allSectionsExpanded.value;
|
|
Object.keys(expandedSections.value).forEach((key) => {
|
|
expandedSections.value[key] = expand;
|
|
});
|
|
};
|
|
|
|
const anySectionVisible = computed(() => ORDERED_SECTION_IDS.some((id) => isSectionVisible(id)));
|
|
|
|
return {
|
|
searchQuery,
|
|
expandedSections,
|
|
isConfigOptionVisible,
|
|
isSectionVisible,
|
|
isExpanded,
|
|
toggleSection,
|
|
resetSearch,
|
|
allSectionsExpanded,
|
|
toggleAllSections,
|
|
anySectionVisible,
|
|
};
|
|
}
|