@pulzr/perses-timeseries-panel 0.1.0 → 0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/README.md +3 -2
  2. package/__mf/js/{PulzrTimeSeriesChart.3d9d57bbf3.js → PulzrTimeSeriesChart.8f5010ba5c.js} +4 -4
  3. package/__mf/js/async/{0.a921d52b8b.js → 0.1f67a94360.js} +2 -2
  4. package/__mf/js/async/07.36ac3671c5.js +2 -0
  5. package/__mf/js/async/{0p.e5f4580631.js → 0p.58ba51c3d8.js} +2 -2
  6. package/__mf/js/async/{1.fe54a278c1.js → 1.73bde7147c.js} +7 -7
  7. package/__mf/js/async/{2.659f419ef1.js → 2.965b5a081a.js} +2 -2
  8. package/__mf/js/async/{24.59697df786.js → 24.c20b9c5a17.js} +1 -1
  9. package/__mf/js/async/3.364f359293.js +1 -0
  10. package/__mf/js/async/4.21994aea94.js +1 -0
  11. package/__mf/js/async/5.f4b8ab81b7.js +1 -0
  12. package/__mf/js/async/{6.eaeef8b4c8.js → 6.1c072abe87.js} +1 -1
  13. package/__mf/js/async/6x.3c32d29a25.js +3 -0
  14. package/__mf/js/async/{7.fdcaf8075f.js → 7.15f564c6f3.js} +4 -4
  15. package/__mf/js/async/{9.172fcda3a7.js → 9.f03162afcc.js} +1 -1
  16. package/__mf/js/async/__federation_expose_PulzrTimeSeriesChart.872d907273.js +1 -0
  17. package/__mf/js/async/aw.c62047d59e.js +1 -0
  18. package/__mf/js/async/b.d9e335c512.js +1 -0
  19. package/__mf/js/async/{c.58ce24efaf.js → c.db5f03155c.js} +1 -1
  20. package/__mf/js/async/{f.f76467156b.js → f.629bfc34cd.js} +2 -2
  21. package/__mf/js/async/{f6.448348018e.js → f6.a297b4c3b5.js} +1 -1
  22. package/__mf/js/async/g.8f186fee46.js +1 -0
  23. package/__mf/js/async/i.20d8c95685.js +1 -0
  24. package/__mf/js/async/k.9353f82963.js +2 -0
  25. package/__mf/js/async/kk.54ddbdccb1.js +1 -0
  26. package/__mf/js/async/{m.201f79637c.js → m.8322f15461.js} +3 -3
  27. package/__mf/js/async/{n.a9f767e43e.js → n.07b91c8f3b.js} +1 -1
  28. package/__mf/js/async/{o.f440fb5535.js → o.f00502c3dc.js} +1 -1
  29. package/__mf/js/async/o8.0d0f7ae8dd.js +2 -0
  30. package/__mf/js/async/{os.fc957c451d.js → os.48ffc14283.js} +2 -2
  31. package/__mf/js/async/{p.a24c534e4c.js → p.f622a2635a.js} +2 -2
  32. package/__mf/js/async/{pj.7f6006dd9b.js → pj.dd52375d06.js} +1 -1
  33. package/__mf/js/async/{s.6a069b1205.js → s.4d1c08522b.js} +1 -1
  34. package/__mf/js/async/{s9.ee54b7db5d.js → s9.14445ad382.js} +1 -1
  35. package/__mf/js/async/se.3e1054cb2e.js +110 -0
  36. package/__mf/js/async/sp.823f3e93e8.js +1 -0
  37. package/__mf/js/async/sr.52e9bae874.js +1 -0
  38. package/__mf/js/async/t.94a8ce332d.js +1 -0
  39. package/__mf/js/async/tl.d9749e7d1e.js +1 -0
  40. package/__mf/js/async/uz.53202eb429.js +1 -0
  41. package/__mf/js/async/{x.41fc8a24ba.js → x.57044cf1d5.js} +1 -1
  42. package/__mf/js/async/xr.6ee20d6a04.js +1 -0
  43. package/__mf/js/async/y.2d4e8c2b2e.js +1 -0
  44. package/__mf/js/async/{yk.623d1e6e67.js → yk.d357453890.js} +2 -2
  45. package/__mf/js/{main.b7f0427e6f.js → main.5302f40bb7.js} +4 -4
  46. package/lib/TimeSeriesChartPanel.d.ts.map +1 -1
  47. package/lib/TimeSeriesChartPanel.js +2 -2
  48. package/lib/TimeSeriesChartPanel.js.map +1 -1
  49. package/lib/utils/data-transform.d.ts +13 -0
  50. package/lib/utils/data-transform.d.ts.map +1 -1
  51. package/lib/utils/data-transform.js +40 -0
  52. package/lib/utils/data-transform.js.map +1 -1
  53. package/lib/utils/timezone-formatter.d.ts +5 -1
  54. package/lib/utils/timezone-formatter.d.ts.map +1 -1
  55. package/lib/utils/timezone-formatter.js +15 -9
  56. package/lib/utils/timezone-formatter.js.map +1 -1
  57. package/mf-manifest.json +42 -42
  58. package/mf-stats.json +42 -42
  59. package/package.json +1 -1
  60. package/__mf/js/async/07.7092d9d54f.js +0 -2
  61. package/__mf/js/async/31.217831ad7d.js +0 -1
  62. package/__mf/js/async/49.ce17b0b7df.js +0 -1
  63. package/__mf/js/async/5.3d5e90410c.js +0 -1
  64. package/__mf/js/async/6x.c223ac1732.js +0 -3
  65. package/__mf/js/async/__federation_expose_PulzrTimeSeriesChart.9598d44d58.js +0 -1
  66. package/__mf/js/async/a.436ea5e76a.js +0 -1
  67. package/__mf/js/async/bb.3cbe8733b4.js +0 -1
  68. package/__mf/js/async/g.96a6e47a3d.js +0 -1
  69. package/__mf/js/async/i.ca63476c51.js +0 -1
  70. package/__mf/js/async/k.3d144e3240.js +0 -2
  71. package/__mf/js/async/kk.e2851ee742.js +0 -1
  72. package/__mf/js/async/o8.02af3e606c.js +0 -2
  73. package/__mf/js/async/se.70d92643a1.js +0 -110
  74. package/__mf/js/async/sp.96a839c02d.js +0 -1
  75. package/__mf/js/async/sr.60755905c7.js +0 -1
  76. package/__mf/js/async/t.ab701c63f2.js +0 -1
  77. package/__mf/js/async/tl.c115139e26.js +0 -1
  78. package/__mf/js/async/u.3634c64cf1.js +0 -1
  79. package/__mf/js/async/xr.b4f1367f01.js +0 -1
  80. package/__mf/js/async/y.3029ca8d48.js +0 -1
  81. /package/__mf/js/async/{0.a921d52b8b.js.LICENSE.txt → 0.1f67a94360.js.LICENSE.txt} +0 -0
  82. /package/__mf/js/async/{07.7092d9d54f.js.LICENSE.txt → 07.36ac3671c5.js.LICENSE.txt} +0 -0
  83. /package/__mf/js/async/{0p.e5f4580631.js.LICENSE.txt → 0p.58ba51c3d8.js.LICENSE.txt} +0 -0
  84. /package/__mf/js/async/{1.fe54a278c1.js.LICENSE.txt → 1.73bde7147c.js.LICENSE.txt} +0 -0
  85. /package/__mf/js/async/{2.659f419ef1.js.LICENSE.txt → 2.965b5a081a.js.LICENSE.txt} +0 -0
  86. /package/__mf/js/async/{7.fdcaf8075f.js.LICENSE.txt → 7.15f564c6f3.js.LICENSE.txt} +0 -0
  87. /package/__mf/js/async/{f.f76467156b.js.LICENSE.txt → f.629bfc34cd.js.LICENSE.txt} +0 -0
  88. /package/__mf/js/async/{k.3d144e3240.js.LICENSE.txt → k.9353f82963.js.LICENSE.txt} +0 -0
  89. /package/__mf/js/async/{m.201f79637c.js.LICENSE.txt → m.8322f15461.js.LICENSE.txt} +0 -0
  90. /package/__mf/js/async/{os.fc957c451d.js.LICENSE.txt → os.48ffc14283.js.LICENSE.txt} +0 -0
  91. /package/__mf/js/async/{p.a24c534e4c.js.LICENSE.txt → p.f622a2635a.js.LICENSE.txt} +0 -0
  92. /package/__mf/js/async/{se.70d92643a1.js.LICENSE.txt → se.3e1054cb2e.js.LICENSE.txt} +0 -0
  93. /package/__mf/js/async/{yk.623d1e6e67.js.LICENSE.txt → yk.d357453890.js.LICENSE.txt} +0 -0
@@ -1 +1 @@
1
- {"version":3,"file":"TimeSeriesChartPanel.d.ts","sourceRoot":"","sources":["../../src/TimeSeriesChartPanel.tsx"],"names":[],"mappings":"AAoCA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,2BAA2B,CAAC;AAQ7E,OAAO,KAAK,EAAsB,cAAc,EAAwB,MAAM,kBAAkB,CAAC;AAGjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAY,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAsBlF,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,sBAAsB,EAAE,cAAc,CAAC,CAAC;AAuBtF,iBAAS,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CA6hBvF;AAKD,eAAO,MAAM,oBAAoB,2EAYhC,CAAC"}
1
+ {"version":3,"file":"TimeSeriesChartPanel.d.ts","sourceRoot":"","sources":["../../src/TimeSeriesChartPanel.tsx"],"names":[],"mappings":"AAmCA,OAAO,KAAK,EAAE,UAAU,EAAmB,MAAM,2BAA2B,CAAC;AAQ7E,OAAO,KAAK,EAAsB,cAAc,EAAwB,MAAM,kBAAkB,CAAC;AAGjG,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,OAAO,KAAK,EAAY,sBAAsB,EAAE,MAAM,2BAA2B,CAAC;AAuBlF,MAAM,MAAM,oBAAoB,GAAG,UAAU,CAAC,sBAAsB,EAAE,cAAc,CAAC,CAAC;AAuBtF,iBAAS,6BAA6B,CAAC,KAAK,EAAE,oBAAoB,GAAG,YAAY,GAAG,IAAI,CA6hBvF;AAKD,eAAO,MAAM,oBAAoB,2EAYhC,CAAC"}
@@ -12,7 +12,7 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
  import { Box, useTheme } from "@mui/material";
15
- import { YAxisLabel, useChartsTheme, ContentWithLegend, DEFAULT_TOOLTIP_CONFIG, getFormattedMultipleYAxes, DEFAULT_LEGEND, formatValue, getTimeSeriesValues } from "@perses-dev/components";
15
+ import { YAxisLabel, useChartsTheme, ContentWithLegend, DEFAULT_TOOLTIP_CONFIG, getFormattedMultipleYAxes, DEFAULT_LEGEND, formatValue } from "@perses-dev/components";
16
16
  import { usePanelAnnotationsWithData } from "@perses-dev/dashboards";
17
17
  import { LEGEND_VALUE_CONFIG, useTimeRange, validateLegendSpec, legendValues, getCalculations } from "@perses-dev/plugin-system";
18
18
  import merge from "lodash/merge";
@@ -20,7 +20,7 @@ import { memo, useCallback, useId, useMemo, useRef, useState } from "react";
20
20
  import { BAND_LABEL, DEFAULT_BAND_OPACITY, DEFAULT_FORMAT, DEFAULT_VISUAL, THRESHOLD_PLOT_INTERVAL } from "./time-series-chart-model.js";
21
21
  import { TimeSeriesChartBase } from "./TimeSeriesChartBase.js";
22
22
  import { convertAnnotationToTimeSeriesAnnotation } from "./utils/annotation.js";
23
- import { getBandSeries, getTimeSeries, getCommonTimeScaleForQueries, convertPanelYAxis, getThresholdSeries, convertPercentThreshold } from "./utils/data-transform.js";
23
+ import { getBandSeries, getTimeSeries, getTimeSeriesValues, getCommonTimeScaleForQueries, convertPanelYAxis, getThresholdSeries, convertPercentThreshold } from "./utils/data-transform.js";
24
24
  import { getSeriesColor } from "./utils/palette-gen.js";
25
25
  /**
26
26
  * Stable string key for a labels record, so exemplars can be matched to their
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/TimeSeriesChartPanel.tsx"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Box, useTheme } from '@mui/material';\nimport type {\n ChartInstance,\n ZoomEventData,\n SelectedLegendItemState,\n TableColumnConfig,\n LegendItem,\n LegendProps,\n TooltipConfig,\n TimeChartSeriesMapping,\n StepOptions,\n} from '@perses-dev/components';\nimport {\n YAxisLabel,\n useChartsTheme,\n ContentWithLegend,\n DEFAULT_TOOLTIP_CONFIG,\n getFormattedMultipleYAxes,\n DEFAULT_LEGEND,\n formatValue,\n getTimeSeriesValues,\n} from '@perses-dev/components';\nimport { usePanelAnnotationsWithData } from '@perses-dev/dashboards';\nimport type { PanelProps, CalculationType } from '@perses-dev/plugin-system';\nimport {\n LEGEND_VALUE_CONFIG,\n useTimeRange,\n validateLegendSpec,\n legendValues,\n getCalculations,\n} from '@perses-dev/plugin-system';\nimport type { Labels, TimeSeries, TimeSeriesData, TimeSeriesValueTuple } from '@perses-dev/spec';\nimport type { GridComponentOption } from 'echarts';\nimport merge from 'lodash/merge';\nimport type { ReactElement } from 'react';\nimport { memo, useCallback, useId, useMemo, useRef, useState } from 'react';\n\nimport type { BandEdge, TimeSeriesChartOptions } from './time-series-chart-model';\nimport {\n BAND_LABEL,\n DEFAULT_BAND_OPACITY,\n DEFAULT_FORMAT,\n DEFAULT_VISUAL,\n THRESHOLD_PLOT_INTERVAL,\n} from './time-series-chart-model';\nimport { TimeSeriesChartBase } from './TimeSeriesChartBase';\nimport type { TimeSeriesAnnotation } from './utils/annotation';\nimport { convertAnnotationToTimeSeriesAnnotation } from './utils/annotation';\nimport type { ExemplarChartData } from './utils/data-transform';\nimport {\n getBandSeries,\n getTimeSeries,\n getCommonTimeScaleForQueries,\n convertPanelYAxis,\n getThresholdSeries,\n convertPercentThreshold,\n} from './utils/data-transform';\nimport { getSeriesColor } from './utils/palette-gen';\n\nexport type TimeSeriesChartProps = PanelProps<TimeSeriesChartOptions, TimeSeriesData>;\n\n/**\n * Stable string key for a labels record, so exemplars can be matched to their\n * series regardless of the labels order.\n */\nfunction labelsKey(labels: Labels): string {\n return JSON.stringify(\n Object.keys(labels)\n .toSorted()\n .map((labelName) => [labelName, labels[labelName]]),\n );\n}\n\n// Using an \"ALL\" value to handle the case on first loading the chart where we\n// want to select all, but do not want all of the legend items to be visually highlighted.\n// This helps us differentiate those cases more clearly instead of inferring it\n// based on the state of the data. This also helps us avoid some coding\n// complexity around initializing a full record for the initial load that would\n// currently require significantly more refactoring of this component.\n// TODO: simplify this if we switch the list-based legend UI to use checkboxes,\n// where we *would* want to visually select all items in this case.\n\nfunction TimeSeriesChartPanelComponent(props: TimeSeriesChartProps): ReactElement | null {\n const {\n spec: { thresholds, yAxis, tooltip, querySettings: querySettingsList },\n contentDimensions,\n queryResults,\n } = props;\n const chartsTheme = useChartsTheme();\n const muiTheme = useTheme();\n const chartId = useId();\n\n const chartRef = useRef<ChartInstance>(null);\n\n // ECharts theme comes from ChartsProvider, more info: https://echarts.apache.org/en/option.html#color\n // Colors are manually applied since our legend and tooltip are built custom with React.\n const categoricalPalette = chartsTheme.echartsTheme.color;\n\n // TODO: consider refactoring how the layout/spacing/alignment are calculated\n // the next time significant changes are made to the time series panel (e.g.\n // when making improvements to the legend to more closely match designs).\n // This may also want to include moving some of this logic down to the shared,\n // embeddable components.\n const contentPadding = chartsTheme.container.padding.default;\n const adjustedContentDimensions: typeof contentDimensions = contentDimensions\n ? {\n width: contentDimensions.width - contentPadding * 2,\n height: contentDimensions.height - contentPadding * 2,\n }\n : undefined;\n\n // populate default 'position' and other future properties\n const legend = useMemo(() => {\n return props.spec.legend && validateLegendSpec(props.spec.legend)\n ? merge({}, DEFAULT_LEGEND, props.spec.legend)\n : undefined;\n }, [props.spec.legend]);\n\n // TODO: add support for y_axis_alt.format\n const format = props.spec.yAxis?.format ?? DEFAULT_FORMAT;\n\n // ensures there are fallbacks for unset properties since most\n // users should not need to customize visual display\n const visual = useMemo(() => {\n return merge({}, DEFAULT_VISUAL, props.spec.visual);\n }, [props.spec.visual]);\n\n // convert Perses dashboard format to be ECharts compatible\n const echartsYAxis = useMemo(() => {\n return convertPanelYAxis(yAxis);\n }, [yAxis]);\n\n // Collect unique formats from query settings that differ from the base format\n // These will create additional Y axes on the right side\n const { additionalFormats, formatToYAxisIndex } = useMemo(() => {\n const baseUnit = format?.unit ?? 'decimal';\n const additionalFormats: Array<typeof format> = [];\n const formatToYAxisIndex = new Map<string, number>();\n\n // Index 0 is reserved for the base Y axis\n formatToYAxisIndex.set(baseUnit, 0);\n\n // Collect unique formats from query settings\n for (const qs of querySettingsList ?? []) {\n if (qs.format?.unit && qs.format.unit !== baseUnit) {\n const unitKey = qs.format.unit;\n if (!formatToYAxisIndex.has(unitKey)) {\n // Add new format - index is 1 + position in additionalFormats array\n formatToYAxisIndex.set(unitKey, 1 + additionalFormats.length);\n additionalFormats.push(qs.format);\n }\n }\n }\n\n return { additionalFormats, formatToYAxisIndex };\n }, [format, querySettingsList]);\n\n const [selectedLegendItems, setSelectedLegendItems] = useState<SelectedLegendItemState>('ALL');\n const [legendSorting, setLegendSorting] = useState<NonNullable<LegendProps['tableProps']>['sorting']>();\n\n const { setTimeRange } = useTimeRange();\n\n const annotationsWithData = usePanelAnnotationsWithData(props.definition?.spec.annotations);\n\n const annotations: TimeSeriesAnnotation[] = useMemo(\n () => convertAnnotationToTimeSeriesAnnotation(annotationsWithData),\n [annotationsWithData],\n );\n\n const calculationTypes = legend?.values;\n\n // Point processing and legend statistics depend on query data, not legend selection.\n // Reuse these arrays when toggling series instead of rescanning every sample.\n const { timeScale, preparedQueries } = useMemo(() => {\n const commonScale = getCommonTimeScaleForQueries(queryResults);\n const settingsByQuery = new Map(querySettingsList?.map((settings) => [settings.queryIndex, settings]));\n let seriesIndex = 0;\n const queries = commonScale\n ? queryResults.map((result, queryIndex) => {\n const querySettings = settingsByQuery.get(queryIndex);\n const series = result.data.series.map((timeSeries) => {\n const baseValues = getTimeSeriesValues(timeSeries, commonScale);\n const values = querySettings?.negativeY\n ? baseValues.map(([t, v]): TimeSeriesValueTuple => [t, v === null ? null : -v])\n : baseValues;\n const calculations = calculationTypes\n ? getCalculations(timeSeries.values, calculationTypes as CalculationType[])\n : undefined;\n let maxValue = 0;\n if (querySettings?.format?.unit) {\n for (const [, value] of timeSeries.values) {\n maxValue = Math.max(maxValue, Math.abs(value ?? 0));\n }\n }\n const seriesId = `${chartId}${timeSeries.name}${seriesIndex}`;\n seriesIndex += 1;\n return { timeSeries, seriesId, values, calculations, maxValue };\n });\n return { querySettings, series, exemplars: result.data.exemplars };\n })\n : [];\n return { timeScale: commonScale, preparedQueries: queries };\n }, [queryResults, querySettingsList, calculationTypes, chartId]);\n\n // Prepare all exemplar groups independently of legend selection. The base chart\n // filters their scatter series without rebuilding marker data for visible groups.\n const chartExemplars = useMemo(() => {\n const groups: ExemplarChartData[] = [];\n let seriesIndex = 0;\n for (const { querySettings, series, exemplars } of preparedQueries) {\n // Labels identify a series within a query, not across different queries.\n const seriesByLabels = new Map<string, ExemplarChartData>();\n for (const { timeSeries, seriesId } of series) {\n const seriesName = timeSeries.formattedName ?? timeSeries.name;\n if (exemplars?.length && timeSeries.labels) {\n seriesByLabels.set(labelsKey(timeSeries.labels), {\n seriesId,\n seriesName,\n color: getSeriesColor({\n defaultCategoricalPalette: categoricalPalette as string[],\n visual,\n muiPrimaryColor: muiTheme.palette.primary.main,\n seriesName,\n seriesIndex,\n querySettings,\n queryHasMultipleResults: series.length > 1,\n }),\n seriesLabels: timeSeries.labels,\n yAxisIndex: formatToYAxisIndex.get(querySettings?.format?.unit ?? '') ?? 0,\n negativeY: querySettings?.negativeY,\n exemplars: [],\n });\n }\n seriesIndex += 1;\n }\n for (const group of exemplars ?? []) {\n if (group.exemplars.length === 0) continue;\n const matched = seriesByLabels.get(labelsKey(group.seriesLabels));\n if (matched) groups.push({ ...matched, exemplars: group.exemplars });\n }\n }\n return groups;\n }, [preparedQueries, categoricalPalette, visual, muiTheme.palette.primary.main, formatToYAxisIndex]);\n\n // Populate series data based on query results\n const {\n timeChartData,\n timeSeriesMapping,\n legendItems,\n seriesFormatMap: computedSeriesFormatMap,\n maxValuesByFormat,\n } = useMemo(() => {\n if (timeScale === undefined) {\n return {\n timeChartData: [] as TimeSeries[],\n timeSeriesMapping: [] as TimeChartSeriesMapping,\n legendItems: [] as LegendItem[],\n seriesFormatMap: new Map(),\n maxValuesByFormat: new Map<string, number>(),\n };\n }\n\n const legendItems: LegendItem[] = [];\n\n // Utilizes ECharts dataset so raw data is separate from series option style properties\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/\n const timeChartData: TimeSeries[] = [];\n const timeSeriesMapping: TimeChartSeriesMapping = [];\n\n // Track max values for each format unit (used for dynamic Y axis offset calculation)\n const maxValuesByFormat = new Map<string, number>();\n const seriesFormatMap = new Map<string, typeof format>();\n\n let visibleSeriesCount = 0;\n for (const { series } of preparedQueries) {\n for (const { seriesId } of series) {\n if (selectedLegendItems === 'ALL' || selectedLegendItems[seriesId]) visibleSeriesCount++;\n }\n }\n\n // Index is counted across multiple queries which ensures the categorical color palette does not reset for every query\n let seriesIndex = 0;\n\n // A query's band edges, drawn after every other series (see getBandSeries)\n const bands: Array<{\n id: string;\n color?: string;\n yAxisIndex: number;\n querySettings?: (typeof preparedQueries)[number]['querySettings'];\n edges: Partial<Record<BandEdge, TimeSeriesValueTuple[]>>;\n }> = [];\n\n for (const [queryIndex, { querySettings, series }] of preparedQueries.entries()) {\n const band: (typeof bands)[number] = {\n id: `${chartId}band${queryIndex}`,\n yAxisIndex: querySettings?.format?.unit ? (formatToYAxisIndex.get(querySettings.format.unit) ?? 0) : 0,\n querySettings,\n edges: {},\n };\n bands.push(band);\n for (const { timeSeries, seriesId, values, calculations, maxValue } of series) {\n const edge = timeSeries.labels?.[BAND_LABEL];\n if (edge === 'lo' || edge === 'hi') {\n band.edges[edge] = values;\n continue;\n }\n // Format is determined by seriesNameFormat in query spec\n const formattedSeriesName = timeSeries.formattedName ?? timeSeries.name;\n\n // Color is used for line, tooltip, and legend\n const seriesColor = getSeriesColor({\n // ECharts type for color is not always an array but it is always an array in ChartsProvider\n defaultCategoricalPalette: categoricalPalette as string[],\n visual,\n muiPrimaryColor: muiTheme.palette.primary.main,\n seriesName: formattedSeriesName,\n seriesIndex,\n querySettings: querySettings,\n queryHasMultipleResults: series.length > 1,\n });\n if (band.color === undefined) band.color = seriesColor;\n\n // When we initially load the chart, we want to show all series, but\n // DO NOT want to visualy highlight all the items in the legend.\n const isSelectAll = selectedLegendItems === 'ALL';\n const isSelected = !isSelectAll && !!selectedLegendItems[seriesId];\n const showTimeSeries = isSelected || isSelectAll;\n\n if (showTimeSeries) {\n // Use timeChartData.length to ensure the data that is passed into the tooltip accounts for\n // which legend items are selected. This must happen before timeChartData.push to avoid an\n // off-by-one error, seriesIndex cannot be used since it's needed to cycle through palette\n const datasetIndex = timeChartData.length;\n\n // Determine yAxisIndex based on the query's format setting\n const queryFormat = querySettings?.format;\n const yAxisIndex = queryFormat?.unit ? (formatToYAxisIndex.get(queryFormat.unit) ?? 0) : 0;\n\n // Each series is stored as a separate dataset source.\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/#how-to-reference-several-datasets\n timeSeriesMapping.push(\n getTimeSeries(\n seriesId,\n datasetIndex,\n formattedSeriesName,\n visual,\n timeScale,\n seriesColor,\n querySettings,\n yAxisIndex,\n visibleSeriesCount,\n ),\n );\n\n // Store the format for this series for tooltip formatting\n if (queryFormat) {\n seriesFormatMap.set(seriesId, queryFormat);\n\n // Track max value for this format unit (used for dynamic Y axis offset calculation)\n const unitKey = queryFormat.unit;\n if (unitKey) {\n const currentMax = maxValuesByFormat.get(unitKey) ?? 0;\n if (maxValue > currentMax) {\n maxValuesByFormat.set(unitKey, maxValue);\n }\n }\n }\n\n timeChartData.push({\n name: formattedSeriesName,\n values,\n });\n }\n\n if (legend && legendItems) {\n legendItems.push({\n id: seriesId, // Avoids duplicate key console errors when there are duplicate series names\n label: formattedSeriesName,\n color: seriesColor,\n data: calculations,\n });\n }\n\n // Used for repeating colors in Categorical palette\n seriesIndex += 1;\n }\n }\n\n // map thresholds only if there is at least one time series to avoid displaying thresholds without any data\n if (thresholds && thresholds.steps && timeChartData.length > 0) {\n // Convert how thresholds are defined in the panel spec to valid ECharts 'line' series.\n // These are styled with predefined colors and a dashed style to look different than series from query results.\n // Regular series are used instead of markLines since thresholds currently show in our React TimeSeriesTooltip.\n const thresholdsColors = chartsTheme.thresholds;\n const defaultThresholdColor = thresholds.defaultColor ?? thresholdsColors.defaultColor;\n thresholds.steps.forEach((step: StepOptions, index: number) => {\n const stepPaletteColor = thresholdsColors.palette[index] ?? defaultThresholdColor;\n const thresholdLineColor = step.color ?? stepPaletteColor;\n const stepOption: StepOptions = {\n color: thresholdLineColor,\n value:\n // yAxis is passed here since it corresponds to dashboard JSON instead of the already converted ECharts yAxis\n thresholds.mode === 'percent'\n ? convertPercentThreshold(step.value, timeChartData, yAxis?.max, yAxis?.min)\n : step.value,\n };\n const thresholdName = step.name ?? `Threshold ${index + 1}`;\n\n // Generates array of [time, step.value] where time ranges from timescale.startMs to timescale.endMs with an interval of 15s\n const thresholdTimeValueTuple: TimeSeriesValueTuple[] = [];\n let currentTimestamp = timeScale.startMs;\n while (currentTimestamp <= timeScale.endMs) {\n thresholdTimeValueTuple.push([currentTimestamp, stepOption.value]);\n // Used to plot fake thresholds datapoints so correct nearby threshold series shows in tooltip without flicker\n currentTimestamp += 1000 * THRESHOLD_PLOT_INTERVAL;\n }\n\n timeChartData.push({\n name: thresholdName,\n values: thresholdTimeValueTuple,\n });\n timeSeriesMapping.push(getThresholdSeries(thresholdName, stepOption, seriesIndex));\n seriesIndex += 1;\n });\n }\n\n for (const { id, color, yAxisIndex, querySettings, edges } of bands) {\n if (!edges.lo || !edges.hi) continue;\n timeSeriesMapping.push(\n ...getBandSeries(\n id,\n edges.lo,\n edges.hi,\n color ?? muiTheme.palette.primary.main,\n visual.bandOpacity ?? DEFAULT_BAND_OPACITY,\n yAxisIndex,\n querySettings?.lineInterpolation ?? visual.lineInterpolation,\n ),\n );\n }\n\n return {\n timeScale,\n timeChartData,\n timeSeriesMapping,\n legendItems,\n seriesFormatMap,\n maxValuesByFormat,\n };\n }, [\n preparedQueries,\n timeScale,\n thresholds,\n selectedLegendItems,\n legend,\n visual,\n yAxis?.max,\n yAxis?.min,\n categoricalPalette,\n chartsTheme.thresholds,\n muiTheme.palette.primary.main,\n formatToYAxisIndex,\n chartId,\n ]);\n\n // Create multiple Y axes if there are additional formats\n // Uses max values from data to compute dynamic offsets that adapt to label widths\n const multipleYAxes = useMemo(() => {\n if (additionalFormats.length === 0) {\n return undefined; // Use single Y axis (default behavior)\n }\n // Build array of max values for each additional format (in order)\n const maxValues = additionalFormats.map((fmt) => {\n const unitKey = fmt.unit;\n return unitKey ? (maxValuesByFormat?.get(unitKey) ?? 1000) : 1000;\n });\n return getFormattedMultipleYAxes(echartsYAxis, format, additionalFormats, maxValues);\n }, [echartsYAxis, format, additionalFormats, maxValuesByFormat]);\n\n // Translate the legend values into columns for the table legend.\n const legendColumns = useMemo(() => {\n if (!legend?.values) {\n return [];\n }\n\n // Iterating the predefined list of possible values to retain a specific\n // intended order of values.\n return legendValues.reduce(\n (columns, legendValue) => {\n const legendConfig = LEGEND_VALUE_CONFIG[legendValue];\n\n if (legendConfig && legend?.values?.includes(legendValue)) {\n columns.push({\n accessorKey: `data.${legendValue}`,\n header: legendConfig.label,\n headerDescription: legendConfig.description,\n // Intentionally hardcoding a column width to start based on discussions\n // with design around keeping this simple to start. This may need\n // revisiting in the future to handle edge cases with very large values.\n width: 72,\n align: 'right',\n cell: ({ getValue }) => {\n const cellValue = getValue();\n return typeof cellValue === 'number' && format ? formatValue(cellValue, format) : cellValue;\n },\n cellDescription: true,\n enableSorting: true,\n });\n }\n\n return columns;\n },\n [] as Array<TableColumnConfig<LegendItem>>,\n );\n }, [legend?.values, format]);\n\n const gridOverrides: GridComponentOption = useMemo(() => {\n // When Y axes are hidden, disable containLabel to prevent auto-spacing, but add bottom padding for X axis\n return echartsYAxis.show === false\n ? {\n left: 0,\n right: 0,\n bottom: 30,\n containLabel: false,\n }\n : {\n left: yAxis && yAxis.label ? 30 : 20,\n // With containLabel: true in theme, ECharts auto-reserves space for axis labels.\n // For multiple right axes, add extra padding for the last axis labels that extend beyond the grid.\n right: additionalFormats.length > 0 ? 10 : 20,\n bottom: 0,\n containLabel: true,\n };\n }, [echartsYAxis.show, yAxis, additionalFormats.length]);\n\n const handleDataZoom = useCallback(\n (event: ZoomEventData): void => {\n setTimeRange({ start: new Date(event.start), end: new Date(event.end) });\n },\n [setTimeRange],\n );\n\n if (adjustedContentDimensions === undefined) {\n return null;\n }\n\n // Used to opt in to ECharts trigger item which show subgroup data accurately.\n // Derived from the actual series mapping rather than `visual.stack` alone so that\n // bar charts stacked only via per-query overrides also use the right tooltip mode.\n const isStackedBar =\n visual.display === 'bar' && timeSeriesMapping.some((s) => s?.type === 'bar' && s.stack === 'all');\n\n // Turn on tooltip pinning by default but opt out for stacked bar or if explicitly set in tooltip panel spec\n let enablePinning = true;\n if (isStackedBar) {\n enablePinning = false;\n } else if (tooltip?.enablePinning !== undefined) {\n enablePinning = tooltip.enablePinning;\n }\n const tooltipConfig: TooltipConfig = {\n ...DEFAULT_TOOLTIP_CONFIG,\n enablePinning,\n };\n\n return (\n <Box sx={{ padding: `${contentPadding}px` }}>\n <ContentWithLegend\n width={adjustedContentDimensions.width}\n height={adjustedContentDimensions.height}\n // Making this small enough that the medium size doesn't get\n // responsive-handling-ed away when in the panel options editor.\n minChildrenHeight={50}\n legendSize={legend?.size}\n legendProps={\n legend && {\n options: legend,\n data: legendItems || [],\n selectedItems: selectedLegendItems,\n onSelectedItemsChange: setSelectedLegendItems,\n tableProps: {\n columns: legendColumns,\n sorting: legendSorting,\n onSortingChange: setLegendSorting,\n },\n onItemMouseOver: (e, { id }): void => {\n chartRef.current?.highlightSeries({ name: id });\n },\n onItemMouseOut: (): void => {\n chartRef.current?.clearHighlightedSeries();\n },\n }\n }\n >\n {({ height, width }) => {\n return (\n <Box style={{ height, width }}>\n {yAxis && yAxis.show && yAxis.label && <YAxisLabel name={yAxis.label} height={height} />}\n <TimeSeriesChartBase\n ref={chartRef}\n height={height}\n data={timeChartData}\n seriesMapping={timeSeriesMapping}\n exemplars={chartExemplars}\n annotations={annotations}\n timeScale={timeScale}\n yAxis={multipleYAxes ?? echartsYAxis}\n format={format}\n seriesFormatMap={computedSeriesFormatMap}\n grid={gridOverrides}\n isStackedBar={isStackedBar}\n tooltipConfig={tooltipConfig}\n syncGroup=\"default-panel-group\" // TODO: make configurable from dashboard settings and per panel-group overrides\n onDataZoom={handleDataZoom}\n // Show an empty chart when there is no data because the user unselected all items in\n // the legend. Otherwise, show a \"no data\" message.\n noDataVariant={!timeChartData.length && legendItems && legendItems.length > 0 ? 'chart' : 'message'}\n />\n </Box>\n );\n }}\n </ContentWithLegend>\n </Box>\n );\n}\n\n// PanelContent recreates the queryResults array and its wrappers on dashboard\n// hover/focus updates. Compare the underlying references in query order so those\n// updates do not invalidate data preparation or scan the samples and exemplars.\nexport const TimeSeriesChartPanel = memo(\n TimeSeriesChartPanelComponent,\n (previous, next): boolean =>\n previous.spec === next.spec &&\n previous.definition === next.definition &&\n previous.contentDimensions?.width === next.contentDimensions?.width &&\n previous.contentDimensions?.height === next.contentDimensions?.height &&\n previous.queryResults.length === next.queryResults.length &&\n previous.queryResults.every(\n (query, index) =>\n query.data === next.queryResults[index]?.data && query.definition === next.queryResults[index]?.definition,\n ),\n);\n"],"names":["Box","useTheme","YAxisLabel","useChartsTheme","ContentWithLegend","DEFAULT_TOOLTIP_CONFIG","getFormattedMultipleYAxes","DEFAULT_LEGEND","formatValue","getTimeSeriesValues","usePanelAnnotationsWithData","LEGEND_VALUE_CONFIG","useTimeRange","validateLegendSpec","legendValues","getCalculations","merge","memo","useCallback","useId","useMemo","useRef","useState","BAND_LABEL","DEFAULT_BAND_OPACITY","DEFAULT_FORMAT","DEFAULT_VISUAL","THRESHOLD_PLOT_INTERVAL","TimeSeriesChartBase","convertAnnotationToTimeSeriesAnnotation","getBandSeries","getTimeSeries","getCommonTimeScaleForQueries","convertPanelYAxis","getThresholdSeries","convertPercentThreshold","getSeriesColor","labelsKey","labels","JSON","stringify","Object","keys","toSorted","map","labelName","TimeSeriesChartPanelComponent","props","spec","thresholds","yAxis","tooltip","querySettings","querySettingsList","contentDimensions","queryResults","chartsTheme","muiTheme","chartId","chartRef","categoricalPalette","echartsTheme","color","contentPadding","container","padding","default","adjustedContentDimensions","width","height","undefined","legend","format","visual","echartsYAxis","additionalFormats","formatToYAxisIndex","baseUnit","unit","Map","set","qs","unitKey","has","length","push","selectedLegendItems","setSelectedLegendItems","legendSorting","setLegendSorting","setTimeRange","annotationsWithData","definition","annotations","calculationTypes","values","timeScale","preparedQueries","commonScale","settingsByQuery","settings","queryIndex","seriesIndex","queries","result","get","series","data","timeSeries","baseValues","negativeY","t","v","calculations","maxValue","value","Math","max","abs","seriesId","name","exemplars","chartExemplars","groups","seriesByLabels","seriesName","formattedName","defaultCategoricalPalette","muiPrimaryColor","palette","primary","main","queryHasMultipleResults","seriesLabels","yAxisIndex","group","matched","timeChartData","timeSeriesMapping","legendItems","seriesFormatMap","computedSeriesFormatMap","maxValuesByFormat","visibleSeriesCount","bands","entries","band","id","edges","edge","formattedSeriesName","seriesColor","isSelectAll","isSelected","showTimeSeries","datasetIndex","queryFormat","currentMax","label","steps","thresholdsColors","defaultThresholdColor","defaultColor","forEach","step","index","stepPaletteColor","thresholdLineColor","stepOption","mode","min","thresholdName","thresholdTimeValueTuple","currentTimestamp","startMs","endMs","lo","hi","bandOpacity","lineInterpolation","multipleYAxes","maxValues","fmt","legendColumns","reduce","columns","legendValue","legendConfig","includes","accessorKey","header","headerDescription","description","align","cell","getValue","cellValue","cellDescription","enableSorting","gridOverrides","show","left","right","bottom","containLabel","handleDataZoom","event","start","Date","end","isStackedBar","display","some","s","type","stack","enablePinning","tooltipConfig","sx","minChildrenHeight","legendSize","size","legendProps","options","selectedItems","onSelectedItemsChange","tableProps","sorting","onSortingChange","onItemMouseOver","e","current","highlightSeries","onItemMouseOut","clearHighlightedSeries","style","ref","seriesMapping","grid","syncGroup","onDataZoom","noDataVariant","TimeSeriesChartPanel","previous","next","every","query"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/TimeSeriesChartPanel.tsx"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { Box, useTheme } from '@mui/material';\nimport type {\n ChartInstance,\n ZoomEventData,\n SelectedLegendItemState,\n TableColumnConfig,\n LegendItem,\n LegendProps,\n TooltipConfig,\n TimeChartSeriesMapping,\n StepOptions,\n} from '@perses-dev/components';\nimport {\n YAxisLabel,\n useChartsTheme,\n ContentWithLegend,\n DEFAULT_TOOLTIP_CONFIG,\n getFormattedMultipleYAxes,\n DEFAULT_LEGEND,\n formatValue,\n} from '@perses-dev/components';\nimport { usePanelAnnotationsWithData } from '@perses-dev/dashboards';\nimport type { PanelProps, CalculationType } from '@perses-dev/plugin-system';\nimport {\n LEGEND_VALUE_CONFIG,\n useTimeRange,\n validateLegendSpec,\n legendValues,\n getCalculations,\n} from '@perses-dev/plugin-system';\nimport type { Labels, TimeSeries, TimeSeriesData, TimeSeriesValueTuple } from '@perses-dev/spec';\nimport type { GridComponentOption } from 'echarts';\nimport merge from 'lodash/merge';\nimport type { ReactElement } from 'react';\nimport { memo, useCallback, useId, useMemo, useRef, useState } from 'react';\n\nimport type { BandEdge, TimeSeriesChartOptions } from './time-series-chart-model';\nimport {\n BAND_LABEL,\n DEFAULT_BAND_OPACITY,\n DEFAULT_FORMAT,\n DEFAULT_VISUAL,\n THRESHOLD_PLOT_INTERVAL,\n} from './time-series-chart-model';\nimport { TimeSeriesChartBase } from './TimeSeriesChartBase';\nimport type { TimeSeriesAnnotation } from './utils/annotation';\nimport { convertAnnotationToTimeSeriesAnnotation } from './utils/annotation';\nimport type { ExemplarChartData } from './utils/data-transform';\nimport {\n getBandSeries,\n getTimeSeries,\n getTimeSeriesValues,\n getCommonTimeScaleForQueries,\n convertPanelYAxis,\n getThresholdSeries,\n convertPercentThreshold,\n} from './utils/data-transform';\nimport { getSeriesColor } from './utils/palette-gen';\n\nexport type TimeSeriesChartProps = PanelProps<TimeSeriesChartOptions, TimeSeriesData>;\n\n/**\n * Stable string key for a labels record, so exemplars can be matched to their\n * series regardless of the labels order.\n */\nfunction labelsKey(labels: Labels): string {\n return JSON.stringify(\n Object.keys(labels)\n .toSorted()\n .map((labelName) => [labelName, labels[labelName]]),\n );\n}\n\n// Using an \"ALL\" value to handle the case on first loading the chart where we\n// want to select all, but do not want all of the legend items to be visually highlighted.\n// This helps us differentiate those cases more clearly instead of inferring it\n// based on the state of the data. This also helps us avoid some coding\n// complexity around initializing a full record for the initial load that would\n// currently require significantly more refactoring of this component.\n// TODO: simplify this if we switch the list-based legend UI to use checkboxes,\n// where we *would* want to visually select all items in this case.\n\nfunction TimeSeriesChartPanelComponent(props: TimeSeriesChartProps): ReactElement | null {\n const {\n spec: { thresholds, yAxis, tooltip, querySettings: querySettingsList },\n contentDimensions,\n queryResults,\n } = props;\n const chartsTheme = useChartsTheme();\n const muiTheme = useTheme();\n const chartId = useId();\n\n const chartRef = useRef<ChartInstance>(null);\n\n // ECharts theme comes from ChartsProvider, more info: https://echarts.apache.org/en/option.html#color\n // Colors are manually applied since our legend and tooltip are built custom with React.\n const categoricalPalette = chartsTheme.echartsTheme.color;\n\n // TODO: consider refactoring how the layout/spacing/alignment are calculated\n // the next time significant changes are made to the time series panel (e.g.\n // when making improvements to the legend to more closely match designs).\n // This may also want to include moving some of this logic down to the shared,\n // embeddable components.\n const contentPadding = chartsTheme.container.padding.default;\n const adjustedContentDimensions: typeof contentDimensions = contentDimensions\n ? {\n width: contentDimensions.width - contentPadding * 2,\n height: contentDimensions.height - contentPadding * 2,\n }\n : undefined;\n\n // populate default 'position' and other future properties\n const legend = useMemo(() => {\n return props.spec.legend && validateLegendSpec(props.spec.legend)\n ? merge({}, DEFAULT_LEGEND, props.spec.legend)\n : undefined;\n }, [props.spec.legend]);\n\n // TODO: add support for y_axis_alt.format\n const format = props.spec.yAxis?.format ?? DEFAULT_FORMAT;\n\n // ensures there are fallbacks for unset properties since most\n // users should not need to customize visual display\n const visual = useMemo(() => {\n return merge({}, DEFAULT_VISUAL, props.spec.visual);\n }, [props.spec.visual]);\n\n // convert Perses dashboard format to be ECharts compatible\n const echartsYAxis = useMemo(() => {\n return convertPanelYAxis(yAxis);\n }, [yAxis]);\n\n // Collect unique formats from query settings that differ from the base format\n // These will create additional Y axes on the right side\n const { additionalFormats, formatToYAxisIndex } = useMemo(() => {\n const baseUnit = format?.unit ?? 'decimal';\n const additionalFormats: Array<typeof format> = [];\n const formatToYAxisIndex = new Map<string, number>();\n\n // Index 0 is reserved for the base Y axis\n formatToYAxisIndex.set(baseUnit, 0);\n\n // Collect unique formats from query settings\n for (const qs of querySettingsList ?? []) {\n if (qs.format?.unit && qs.format.unit !== baseUnit) {\n const unitKey = qs.format.unit;\n if (!formatToYAxisIndex.has(unitKey)) {\n // Add new format - index is 1 + position in additionalFormats array\n formatToYAxisIndex.set(unitKey, 1 + additionalFormats.length);\n additionalFormats.push(qs.format);\n }\n }\n }\n\n return { additionalFormats, formatToYAxisIndex };\n }, [format, querySettingsList]);\n\n const [selectedLegendItems, setSelectedLegendItems] = useState<SelectedLegendItemState>('ALL');\n const [legendSorting, setLegendSorting] = useState<NonNullable<LegendProps['tableProps']>['sorting']>();\n\n const { setTimeRange } = useTimeRange();\n\n const annotationsWithData = usePanelAnnotationsWithData(props.definition?.spec.annotations);\n\n const annotations: TimeSeriesAnnotation[] = useMemo(\n () => convertAnnotationToTimeSeriesAnnotation(annotationsWithData),\n [annotationsWithData],\n );\n\n const calculationTypes = legend?.values;\n\n // Point processing and legend statistics depend on query data, not legend selection.\n // Reuse these arrays when toggling series instead of rescanning every sample.\n const { timeScale, preparedQueries } = useMemo(() => {\n const commonScale = getCommonTimeScaleForQueries(queryResults);\n const settingsByQuery = new Map(querySettingsList?.map((settings) => [settings.queryIndex, settings]));\n let seriesIndex = 0;\n const queries = commonScale\n ? queryResults.map((result, queryIndex) => {\n const querySettings = settingsByQuery.get(queryIndex);\n const series = result.data.series.map((timeSeries) => {\n const baseValues = getTimeSeriesValues(timeSeries, commonScale);\n const values = querySettings?.negativeY\n ? baseValues.map(([t, v]): TimeSeriesValueTuple => [t, v === null ? null : -v])\n : baseValues;\n const calculations = calculationTypes\n ? getCalculations(timeSeries.values, calculationTypes as CalculationType[])\n : undefined;\n let maxValue = 0;\n if (querySettings?.format?.unit) {\n for (const [, value] of timeSeries.values) {\n maxValue = Math.max(maxValue, Math.abs(value ?? 0));\n }\n }\n const seriesId = `${chartId}${timeSeries.name}${seriesIndex}`;\n seriesIndex += 1;\n return { timeSeries, seriesId, values, calculations, maxValue };\n });\n return { querySettings, series, exemplars: result.data.exemplars };\n })\n : [];\n return { timeScale: commonScale, preparedQueries: queries };\n }, [queryResults, querySettingsList, calculationTypes, chartId]);\n\n // Prepare all exemplar groups independently of legend selection. The base chart\n // filters their scatter series without rebuilding marker data for visible groups.\n const chartExemplars = useMemo(() => {\n const groups: ExemplarChartData[] = [];\n let seriesIndex = 0;\n for (const { querySettings, series, exemplars } of preparedQueries) {\n // Labels identify a series within a query, not across different queries.\n const seriesByLabels = new Map<string, ExemplarChartData>();\n for (const { timeSeries, seriesId } of series) {\n const seriesName = timeSeries.formattedName ?? timeSeries.name;\n if (exemplars?.length && timeSeries.labels) {\n seriesByLabels.set(labelsKey(timeSeries.labels), {\n seriesId,\n seriesName,\n color: getSeriesColor({\n defaultCategoricalPalette: categoricalPalette as string[],\n visual,\n muiPrimaryColor: muiTheme.palette.primary.main,\n seriesName,\n seriesIndex,\n querySettings,\n queryHasMultipleResults: series.length > 1,\n }),\n seriesLabels: timeSeries.labels,\n yAxisIndex: formatToYAxisIndex.get(querySettings?.format?.unit ?? '') ?? 0,\n negativeY: querySettings?.negativeY,\n exemplars: [],\n });\n }\n seriesIndex += 1;\n }\n for (const group of exemplars ?? []) {\n if (group.exemplars.length === 0) continue;\n const matched = seriesByLabels.get(labelsKey(group.seriesLabels));\n if (matched) groups.push({ ...matched, exemplars: group.exemplars });\n }\n }\n return groups;\n }, [preparedQueries, categoricalPalette, visual, muiTheme.palette.primary.main, formatToYAxisIndex]);\n\n // Populate series data based on query results\n const {\n timeChartData,\n timeSeriesMapping,\n legendItems,\n seriesFormatMap: computedSeriesFormatMap,\n maxValuesByFormat,\n } = useMemo(() => {\n if (timeScale === undefined) {\n return {\n timeChartData: [] as TimeSeries[],\n timeSeriesMapping: [] as TimeChartSeriesMapping,\n legendItems: [] as LegendItem[],\n seriesFormatMap: new Map(),\n maxValuesByFormat: new Map<string, number>(),\n };\n }\n\n const legendItems: LegendItem[] = [];\n\n // Utilizes ECharts dataset so raw data is separate from series option style properties\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/\n const timeChartData: TimeSeries[] = [];\n const timeSeriesMapping: TimeChartSeriesMapping = [];\n\n // Track max values for each format unit (used for dynamic Y axis offset calculation)\n const maxValuesByFormat = new Map<string, number>();\n const seriesFormatMap = new Map<string, typeof format>();\n\n let visibleSeriesCount = 0;\n for (const { series } of preparedQueries) {\n for (const { seriesId } of series) {\n if (selectedLegendItems === 'ALL' || selectedLegendItems[seriesId]) visibleSeriesCount++;\n }\n }\n\n // Index is counted across multiple queries which ensures the categorical color palette does not reset for every query\n let seriesIndex = 0;\n\n // A query's band edges, drawn after every other series (see getBandSeries)\n const bands: Array<{\n id: string;\n color?: string;\n yAxisIndex: number;\n querySettings?: (typeof preparedQueries)[number]['querySettings'];\n edges: Partial<Record<BandEdge, TimeSeriesValueTuple[]>>;\n }> = [];\n\n for (const [queryIndex, { querySettings, series }] of preparedQueries.entries()) {\n const band: (typeof bands)[number] = {\n id: `${chartId}band${queryIndex}`,\n yAxisIndex: querySettings?.format?.unit ? (formatToYAxisIndex.get(querySettings.format.unit) ?? 0) : 0,\n querySettings,\n edges: {},\n };\n bands.push(band);\n for (const { timeSeries, seriesId, values, calculations, maxValue } of series) {\n const edge = timeSeries.labels?.[BAND_LABEL];\n if (edge === 'lo' || edge === 'hi') {\n band.edges[edge] = values;\n continue;\n }\n // Format is determined by seriesNameFormat in query spec\n const formattedSeriesName = timeSeries.formattedName ?? timeSeries.name;\n\n // Color is used for line, tooltip, and legend\n const seriesColor = getSeriesColor({\n // ECharts type for color is not always an array but it is always an array in ChartsProvider\n defaultCategoricalPalette: categoricalPalette as string[],\n visual,\n muiPrimaryColor: muiTheme.palette.primary.main,\n seriesName: formattedSeriesName,\n seriesIndex,\n querySettings: querySettings,\n queryHasMultipleResults: series.length > 1,\n });\n if (band.color === undefined) band.color = seriesColor;\n\n // When we initially load the chart, we want to show all series, but\n // DO NOT want to visualy highlight all the items in the legend.\n const isSelectAll = selectedLegendItems === 'ALL';\n const isSelected = !isSelectAll && !!selectedLegendItems[seriesId];\n const showTimeSeries = isSelected || isSelectAll;\n\n if (showTimeSeries) {\n // Use timeChartData.length to ensure the data that is passed into the tooltip accounts for\n // which legend items are selected. This must happen before timeChartData.push to avoid an\n // off-by-one error, seriesIndex cannot be used since it's needed to cycle through palette\n const datasetIndex = timeChartData.length;\n\n // Determine yAxisIndex based on the query's format setting\n const queryFormat = querySettings?.format;\n const yAxisIndex = queryFormat?.unit ? (formatToYAxisIndex.get(queryFormat.unit) ?? 0) : 0;\n\n // Each series is stored as a separate dataset source.\n // https://apache.github.io/echarts-handbook/en/concepts/dataset/#how-to-reference-several-datasets\n timeSeriesMapping.push(\n getTimeSeries(\n seriesId,\n datasetIndex,\n formattedSeriesName,\n visual,\n timeScale,\n seriesColor,\n querySettings,\n yAxisIndex,\n visibleSeriesCount,\n ),\n );\n\n // Store the format for this series for tooltip formatting\n if (queryFormat) {\n seriesFormatMap.set(seriesId, queryFormat);\n\n // Track max value for this format unit (used for dynamic Y axis offset calculation)\n const unitKey = queryFormat.unit;\n if (unitKey) {\n const currentMax = maxValuesByFormat.get(unitKey) ?? 0;\n if (maxValue > currentMax) {\n maxValuesByFormat.set(unitKey, maxValue);\n }\n }\n }\n\n timeChartData.push({\n name: formattedSeriesName,\n values,\n });\n }\n\n if (legend && legendItems) {\n legendItems.push({\n id: seriesId, // Avoids duplicate key console errors when there are duplicate series names\n label: formattedSeriesName,\n color: seriesColor,\n data: calculations,\n });\n }\n\n // Used for repeating colors in Categorical palette\n seriesIndex += 1;\n }\n }\n\n // map thresholds only if there is at least one time series to avoid displaying thresholds without any data\n if (thresholds && thresholds.steps && timeChartData.length > 0) {\n // Convert how thresholds are defined in the panel spec to valid ECharts 'line' series.\n // These are styled with predefined colors and a dashed style to look different than series from query results.\n // Regular series are used instead of markLines since thresholds currently show in our React TimeSeriesTooltip.\n const thresholdsColors = chartsTheme.thresholds;\n const defaultThresholdColor = thresholds.defaultColor ?? thresholdsColors.defaultColor;\n thresholds.steps.forEach((step: StepOptions, index: number) => {\n const stepPaletteColor = thresholdsColors.palette[index] ?? defaultThresholdColor;\n const thresholdLineColor = step.color ?? stepPaletteColor;\n const stepOption: StepOptions = {\n color: thresholdLineColor,\n value:\n // yAxis is passed here since it corresponds to dashboard JSON instead of the already converted ECharts yAxis\n thresholds.mode === 'percent'\n ? convertPercentThreshold(step.value, timeChartData, yAxis?.max, yAxis?.min)\n : step.value,\n };\n const thresholdName = step.name ?? `Threshold ${index + 1}`;\n\n // Generates array of [time, step.value] where time ranges from timescale.startMs to timescale.endMs with an interval of 15s\n const thresholdTimeValueTuple: TimeSeriesValueTuple[] = [];\n let currentTimestamp = timeScale.startMs;\n while (currentTimestamp <= timeScale.endMs) {\n thresholdTimeValueTuple.push([currentTimestamp, stepOption.value]);\n // Used to plot fake thresholds datapoints so correct nearby threshold series shows in tooltip without flicker\n currentTimestamp += 1000 * THRESHOLD_PLOT_INTERVAL;\n }\n\n timeChartData.push({\n name: thresholdName,\n values: thresholdTimeValueTuple,\n });\n timeSeriesMapping.push(getThresholdSeries(thresholdName, stepOption, seriesIndex));\n seriesIndex += 1;\n });\n }\n\n for (const { id, color, yAxisIndex, querySettings, edges } of bands) {\n if (!edges.lo || !edges.hi) continue;\n timeSeriesMapping.push(\n ...getBandSeries(\n id,\n edges.lo,\n edges.hi,\n color ?? muiTheme.palette.primary.main,\n visual.bandOpacity ?? DEFAULT_BAND_OPACITY,\n yAxisIndex,\n querySettings?.lineInterpolation ?? visual.lineInterpolation,\n ),\n );\n }\n\n return {\n timeScale,\n timeChartData,\n timeSeriesMapping,\n legendItems,\n seriesFormatMap,\n maxValuesByFormat,\n };\n }, [\n preparedQueries,\n timeScale,\n thresholds,\n selectedLegendItems,\n legend,\n visual,\n yAxis?.max,\n yAxis?.min,\n categoricalPalette,\n chartsTheme.thresholds,\n muiTheme.palette.primary.main,\n formatToYAxisIndex,\n chartId,\n ]);\n\n // Create multiple Y axes if there are additional formats\n // Uses max values from data to compute dynamic offsets that adapt to label widths\n const multipleYAxes = useMemo(() => {\n if (additionalFormats.length === 0) {\n return undefined; // Use single Y axis (default behavior)\n }\n // Build array of max values for each additional format (in order)\n const maxValues = additionalFormats.map((fmt) => {\n const unitKey = fmt.unit;\n return unitKey ? (maxValuesByFormat?.get(unitKey) ?? 1000) : 1000;\n });\n return getFormattedMultipleYAxes(echartsYAxis, format, additionalFormats, maxValues);\n }, [echartsYAxis, format, additionalFormats, maxValuesByFormat]);\n\n // Translate the legend values into columns for the table legend.\n const legendColumns = useMemo(() => {\n if (!legend?.values) {\n return [];\n }\n\n // Iterating the predefined list of possible values to retain a specific\n // intended order of values.\n return legendValues.reduce(\n (columns, legendValue) => {\n const legendConfig = LEGEND_VALUE_CONFIG[legendValue];\n\n if (legendConfig && legend?.values?.includes(legendValue)) {\n columns.push({\n accessorKey: `data.${legendValue}`,\n header: legendConfig.label,\n headerDescription: legendConfig.description,\n // Intentionally hardcoding a column width to start based on discussions\n // with design around keeping this simple to start. This may need\n // revisiting in the future to handle edge cases with very large values.\n width: 72,\n align: 'right',\n cell: ({ getValue }) => {\n const cellValue = getValue();\n return typeof cellValue === 'number' && format ? formatValue(cellValue, format) : cellValue;\n },\n cellDescription: true,\n enableSorting: true,\n });\n }\n\n return columns;\n },\n [] as Array<TableColumnConfig<LegendItem>>,\n );\n }, [legend?.values, format]);\n\n const gridOverrides: GridComponentOption = useMemo(() => {\n // When Y axes are hidden, disable containLabel to prevent auto-spacing, but add bottom padding for X axis\n return echartsYAxis.show === false\n ? {\n left: 0,\n right: 0,\n bottom: 30,\n containLabel: false,\n }\n : {\n left: yAxis && yAxis.label ? 30 : 20,\n // With containLabel: true in theme, ECharts auto-reserves space for axis labels.\n // For multiple right axes, add extra padding for the last axis labels that extend beyond the grid.\n right: additionalFormats.length > 0 ? 10 : 20,\n bottom: 0,\n containLabel: true,\n };\n }, [echartsYAxis.show, yAxis, additionalFormats.length]);\n\n const handleDataZoom = useCallback(\n (event: ZoomEventData): void => {\n setTimeRange({ start: new Date(event.start), end: new Date(event.end) });\n },\n [setTimeRange],\n );\n\n if (adjustedContentDimensions === undefined) {\n return null;\n }\n\n // Used to opt in to ECharts trigger item which show subgroup data accurately.\n // Derived from the actual series mapping rather than `visual.stack` alone so that\n // bar charts stacked only via per-query overrides also use the right tooltip mode.\n const isStackedBar =\n visual.display === 'bar' && timeSeriesMapping.some((s) => s?.type === 'bar' && s.stack === 'all');\n\n // Turn on tooltip pinning by default but opt out for stacked bar or if explicitly set in tooltip panel spec\n let enablePinning = true;\n if (isStackedBar) {\n enablePinning = false;\n } else if (tooltip?.enablePinning !== undefined) {\n enablePinning = tooltip.enablePinning;\n }\n const tooltipConfig: TooltipConfig = {\n ...DEFAULT_TOOLTIP_CONFIG,\n enablePinning,\n };\n\n return (\n <Box sx={{ padding: `${contentPadding}px` }}>\n <ContentWithLegend\n width={adjustedContentDimensions.width}\n height={adjustedContentDimensions.height}\n // Making this small enough that the medium size doesn't get\n // responsive-handling-ed away when in the panel options editor.\n minChildrenHeight={50}\n legendSize={legend?.size}\n legendProps={\n legend && {\n options: legend,\n data: legendItems || [],\n selectedItems: selectedLegendItems,\n onSelectedItemsChange: setSelectedLegendItems,\n tableProps: {\n columns: legendColumns,\n sorting: legendSorting,\n onSortingChange: setLegendSorting,\n },\n onItemMouseOver: (e, { id }): void => {\n chartRef.current?.highlightSeries({ name: id });\n },\n onItemMouseOut: (): void => {\n chartRef.current?.clearHighlightedSeries();\n },\n }\n }\n >\n {({ height, width }) => {\n return (\n <Box style={{ height, width }}>\n {yAxis && yAxis.show && yAxis.label && <YAxisLabel name={yAxis.label} height={height} />}\n <TimeSeriesChartBase\n ref={chartRef}\n height={height}\n data={timeChartData}\n seriesMapping={timeSeriesMapping}\n exemplars={chartExemplars}\n annotations={annotations}\n timeScale={timeScale}\n yAxis={multipleYAxes ?? echartsYAxis}\n format={format}\n seriesFormatMap={computedSeriesFormatMap}\n grid={gridOverrides}\n isStackedBar={isStackedBar}\n tooltipConfig={tooltipConfig}\n syncGroup=\"default-panel-group\" // TODO: make configurable from dashboard settings and per panel-group overrides\n onDataZoom={handleDataZoom}\n // Show an empty chart when there is no data because the user unselected all items in\n // the legend. Otherwise, show a \"no data\" message.\n noDataVariant={!timeChartData.length && legendItems && legendItems.length > 0 ? 'chart' : 'message'}\n />\n </Box>\n );\n }}\n </ContentWithLegend>\n </Box>\n );\n}\n\n// PanelContent recreates the queryResults array and its wrappers on dashboard\n// hover/focus updates. Compare the underlying references in query order so those\n// updates do not invalidate data preparation or scan the samples and exemplars.\nexport const TimeSeriesChartPanel = memo(\n TimeSeriesChartPanelComponent,\n (previous, next): boolean =>\n previous.spec === next.spec &&\n previous.definition === next.definition &&\n previous.contentDimensions?.width === next.contentDimensions?.width &&\n previous.contentDimensions?.height === next.contentDimensions?.height &&\n previous.queryResults.length === next.queryResults.length &&\n previous.queryResults.every(\n (query, index) =>\n query.data === next.queryResults[index]?.data && query.definition === next.queryResults[index]?.definition,\n ),\n);\n"],"names":["Box","useTheme","YAxisLabel","useChartsTheme","ContentWithLegend","DEFAULT_TOOLTIP_CONFIG","getFormattedMultipleYAxes","DEFAULT_LEGEND","formatValue","usePanelAnnotationsWithData","LEGEND_VALUE_CONFIG","useTimeRange","validateLegendSpec","legendValues","getCalculations","merge","memo","useCallback","useId","useMemo","useRef","useState","BAND_LABEL","DEFAULT_BAND_OPACITY","DEFAULT_FORMAT","DEFAULT_VISUAL","THRESHOLD_PLOT_INTERVAL","TimeSeriesChartBase","convertAnnotationToTimeSeriesAnnotation","getBandSeries","getTimeSeries","getTimeSeriesValues","getCommonTimeScaleForQueries","convertPanelYAxis","getThresholdSeries","convertPercentThreshold","getSeriesColor","labelsKey","labels","JSON","stringify","Object","keys","toSorted","map","labelName","TimeSeriesChartPanelComponent","props","spec","thresholds","yAxis","tooltip","querySettings","querySettingsList","contentDimensions","queryResults","chartsTheme","muiTheme","chartId","chartRef","categoricalPalette","echartsTheme","color","contentPadding","container","padding","default","adjustedContentDimensions","width","height","undefined","legend","format","visual","echartsYAxis","additionalFormats","formatToYAxisIndex","baseUnit","unit","Map","set","qs","unitKey","has","length","push","selectedLegendItems","setSelectedLegendItems","legendSorting","setLegendSorting","setTimeRange","annotationsWithData","definition","annotations","calculationTypes","values","timeScale","preparedQueries","commonScale","settingsByQuery","settings","queryIndex","seriesIndex","queries","result","get","series","data","timeSeries","baseValues","negativeY","t","v","calculations","maxValue","value","Math","max","abs","seriesId","name","exemplars","chartExemplars","groups","seriesByLabels","seriesName","formattedName","defaultCategoricalPalette","muiPrimaryColor","palette","primary","main","queryHasMultipleResults","seriesLabels","yAxisIndex","group","matched","timeChartData","timeSeriesMapping","legendItems","seriesFormatMap","computedSeriesFormatMap","maxValuesByFormat","visibleSeriesCount","bands","entries","band","id","edges","edge","formattedSeriesName","seriesColor","isSelectAll","isSelected","showTimeSeries","datasetIndex","queryFormat","currentMax","label","steps","thresholdsColors","defaultThresholdColor","defaultColor","forEach","step","index","stepPaletteColor","thresholdLineColor","stepOption","mode","min","thresholdName","thresholdTimeValueTuple","currentTimestamp","startMs","endMs","lo","hi","bandOpacity","lineInterpolation","multipleYAxes","maxValues","fmt","legendColumns","reduce","columns","legendValue","legendConfig","includes","accessorKey","header","headerDescription","description","align","cell","getValue","cellValue","cellDescription","enableSorting","gridOverrides","show","left","right","bottom","containLabel","handleDataZoom","event","start","Date","end","isStackedBar","display","some","s","type","stack","enablePinning","tooltipConfig","sx","minChildrenHeight","legendSize","size","legendProps","options","selectedItems","onSelectedItemsChange","tableProps","sorting","onSortingChange","onItemMouseOver","e","current","highlightSeries","onItemMouseOut","clearHighlightedSeries","style","ref","seriesMapping","grid","syncGroup","onDataZoom","noDataVariant","TimeSeriesChartPanel","previous","next","every","query"],"mappings":"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"}
@@ -85,4 +85,17 @@ export declare function convertPanelYAxis(inputAxis?: TimeSeriesChartYAxisOption
85
85
  * 3. -12 --> -20
86
86
  */
87
87
  export declare function roundDown(num: number): number;
88
+ /**
89
+ * A series' values on the chart's time scale, with nulls for the steps it has
90
+ * no value for, so a line breaks where data is missing.
91
+ *
92
+ * Perses's own (getTimeSeriesValues in @perses-dev/components) moves each value
93
+ * onto startMs + n * stepMs. A calendar day, week or month is not a fixed
94
+ * number of ms: across a clock change a day is 23 or 25 hours, and a month is
95
+ * 28 to 31 days. There a value lands on the next step, a day late, and a null
96
+ * breaks the line before it. Here each value keeps its own time, and a null
97
+ * marks only a step missing between two values; a value within half a step of
98
+ * where one was expected is that step's.
99
+ */
100
+ export declare function getTimeSeriesValues(series: TimeSeries, timeScale: TimeScale): TimeSeriesValueTuple[];
88
101
  //# sourceMappingURL=data-transform.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"data-transform.d.ts","sourceRoot":"","sources":["../../../src/utils/data-transform.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"data-transform.d.ts","sourceRoot":"","sources":["../../../src/utils/data-transform.ts"],"names":[],"mappings":"AAaA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EAEjB,gBAAgB,EAChB,WAAW,EACZ,MAAM,wBAAwB,CAAC;AAEhC,OAAO,KAAK,EAAE,oBAAoB,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AACjF,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,UAAU,EAAE,cAAc,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACtH,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,SAAS,CAAC;AACpD,OAAO,KAAK,EAAE,gBAAgB,EAAmB,mBAAmB,EAAE,MAAM,gBAAgB,CAAC;AAE7F,OAAO,KAAK,EACV,4BAA4B,EAC5B,2BAA2B,EAC3B,iBAAiB,EACjB,aAAa,EACd,MAAM,4BAA4B,CAAC;AAWpC,MAAM,MAAM,mBAAmB,GAAG,UAAU,CAAC,OAAO,oBAAoB,CAAC,CAAC;AAE1E,eAAO,MAAM,gBAAgB,EAAE,iBAI9B,CAAC;AAEF,eAAO,MAAM,qBAAqB,KAAK,CAAC;AAExC,eAAO,MAAM,oBAAoB,MAAM,CAAC;AAExC,eAAO,MAAM,yBAAyB,cAAc,CAAC;AAErD,eAAO,MAAM,oBAAoB,KAAK,CAAC;AAEvC;;;GAGG;AACH,MAAM,WAAW,iBAAiB;IAChC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB;;;;;OAKG;IACH,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,EAAE,QAAQ,EAAE,CAAC;CACvB;AAED;;;;GAIG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,cAAc,CAAC,CAAC,GAAG,SAAS,GAAG,SAAS,CAG7G;AAED;;GAEG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,MAAM,EACV,YAAY,EAAE,MAAM,EACpB,aAAa,EAAE,MAAM,EACrB,MAAM,EAAE,4BAA4B,EACpC,SAAS,EAAE,SAAS,EACpB,YAAY,EAAE,MAAM,EACpB,aAAa,CAAC,EAAE;IACd,SAAS,CAAC,EAAE,aAAa,CAAC;IAC1B,iBAAiB,CAAC,EAAE,iBAAiB,CAAC;IACtC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB,EACD,UAAU,CAAC,EAAE,MAAM,EACnB,kBAAkB,SAAI,GACrB,gBAAgB,CAuElB;AAED,8EAA8E;AAC9E,wBAAgB,mBAAmB,CAAC,aAAa,CAAC,EAAE,iBAAiB,GAAG,IAAI,CAAC,gBAAgB,EAAE,QAAQ,GAAG,MAAM,CAAC,CAWhH;AAED;;;;;;GAMG;AACH,wBAAgB,aAAa,CAC3B,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,oBAAoB,EAAE,EAC1B,EAAE,EAAE,oBAAoB,EAAE,EAC1B,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,MAAM,EACf,UAAU,SAAI,EACd,aAAa,CAAC,EAAE,iBAAiB,GAChC,CAAC,gBAAgB,EAAE,gBAAgB,CAAC,CAuBtC;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,iBAAiB,GAAG,mBAAmB,CAmB9E;AAED;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,GAAG,gBAAgB,CA2B9G;AAED;;;GAGG;AACH,wBAAgB,uBAAuB,CACrC,OAAO,EAAE,MAAM,EACf,IAAI,EAAE,gBAAgB,EAAE,GAAG,UAAU,EAAE,EACvC,GAAG,CAAC,EAAE,MAAM,EACZ,GAAG,CAAC,EAAE,MAAM,GACX,MAAM,CAMR;AA6BD;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,SAAS,GAAE,2BAAgC,GAAG,oBAAoB,CAgDnG;AAED;;;;;;;GAOG;AACH,wBAAgB,SAAS,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAI7C;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,SAAS,EAAE,SAAS,GAAG,oBAAoB,EAAE,CAmBpG"}
@@ -328,5 +328,45 @@ function findMax(data) {
328
328
  const firstDigit = Math.floor(num / Math.pow(10, magnitude));
329
329
  return Number((firstDigit * Math.pow(10, magnitude)).toPrecision(1));
330
330
  }
331
+ /**
332
+ * A series' values on the chart's time scale, with nulls for the steps it has
333
+ * no value for, so a line breaks where data is missing.
334
+ *
335
+ * Perses's own (getTimeSeriesValues in @perses-dev/components) moves each value
336
+ * onto startMs + n * stepMs. A calendar day, week or month is not a fixed
337
+ * number of ms: across a clock change a day is 23 or 25 hours, and a month is
338
+ * 28 to 31 days. There a value lands on the next step, a day late, and a null
339
+ * breaks the line before it. Here each value keeps its own time, and a null
340
+ * marks only a step missing between two values; a value within half a step of
341
+ * where one was expected is that step's.
342
+ */ export function getTimeSeriesValues(series, timeScale) {
343
+ const { stepMs, endMs } = timeScale;
344
+ const values = [];
345
+ let expected = timeScale.startMs;
346
+ for (const [t, value] of series.values){
347
+ while(expected + stepMs / 2 < t){
348
+ values.push([
349
+ expected,
350
+ null
351
+ ]);
352
+ expected += stepMs;
353
+ }
354
+ values.push([
355
+ t,
356
+ value
357
+ ]);
358
+ // Never back: a value before the scale's start, or out of order, does not
359
+ // send the nulls after it back over steps already passed.
360
+ expected = Math.max(expected, t + stepMs);
361
+ }
362
+ while(expected <= endMs){
363
+ values.push([
364
+ expected,
365
+ null
366
+ ]);
367
+ expected += stepMs;
368
+ }
369
+ return values;
370
+ }
331
371
 
332
372
  //# sourceMappingURL=data-transform.js.map
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/data-transform.ts"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport type {\n LegacyTimeSeries,\n EChartsDataFormat,\n EChartsValues,\n TimeSeriesOption,\n StepOptions,\n} from '@perses-dev/components';\nimport { OPTIMIZED_MODE_SERIES_LIMIT, getCommonTimeScale } from '@perses-dev/components';\nimport type { useTimeSeriesQueries, PanelData } from '@perses-dev/plugin-system';\nimport type { Exemplar, Labels, TimeScale, TimeSeries, TimeSeriesData, TimeSeriesValueTuple } from '@perses-dev/spec';\nimport type { YAXisComponentOption } from 'echarts';\nimport type { LineSeriesOption, BarSeriesOption, ScatterSeriesOption } from 'echarts/charts';\n\nimport type {\n TimeSeriesChartVisualOptions,\n TimeSeriesChartYAxisOptions,\n LineInterpolation,\n LineStyleType,\n} from '../time-series-chart-model';\nimport {\n DEFAULT_AREA_OPACITY,\n DEFAULT_CONNECT_NULLS,\n DEFAULT_LINE_WIDTH,\n DEFAULT_POINT_RADIUS,\n DEFAULT_Y_AXIS,\n POSITIVE_MIN_VALUE_MULTIPLIER,\n NEGATIVE_MIN_VALUE_MULTIPLIER,\n} from '../time-series-chart-model';\n\nexport type RunningQueriesState = ReturnType<typeof useTimeSeriesQueries>;\n\nexport const EMPTY_GRAPH_DATA: EChartsDataFormat = {\n timeSeries: [],\n xAxis: [],\n legendItems: [],\n};\n\nexport const HIDE_DATAPOINTS_LIMIT = 70;\n\nexport const BLUR_FADEOUT_OPACITY = 0.5;\n\nexport const EXEMPLAR_SERIES_ID_PREFIX = 'exemplar-';\n\nexport const EXEMPLAR_SYMBOL_SIZE = 14;\n\n/**\n * The exemplars of one series, converted to a chart-friendly shape with the\n * rendering attributes (color, y axis) of the matching time series.\n */\nexport interface ExemplarChartData {\n seriesId: string;\n seriesName: string;\n color: string;\n seriesLabels?: Labels;\n yAxisIndex?: number;\n /**\n * When the matching series is rendered with `querySettings.negativeY`, its values are\n * visually negated so it renders below the X axis. The same transform is applied to\n * the exemplar markers' plotted Y values so they stay next to their series. The\n * original (positive) values remain on each `exemplar` for metadata display.\n */\n negativeY?: boolean;\n exemplars: Exemplar[];\n}\n\n/**\n * Given a list of running queries, calculates a common time scale for use on\n * the x axis (i.e. start/end dates and a step that is divisible into all of\n * the queries' steps).\n */\nexport function getCommonTimeScaleForQueries(queries: Array<PanelData<TimeSeriesData>>): TimeScale | undefined {\n const seriesData = queries.map((query) => query.data);\n return getCommonTimeScale(seriesData);\n}\n\n/**\n * Gets ECharts line series option properties for regular trends\n */\nexport function getTimeSeries(\n id: string,\n datasetIndex: number,\n formattedName: string,\n visual: TimeSeriesChartVisualOptions,\n timeScale: TimeScale,\n paletteColor: string,\n querySettings?: {\n lineStyle?: LineStyleType;\n lineInterpolation?: LineInterpolation;\n areaOpacity?: number;\n stack?: boolean;\n },\n yAxisIndex?: number,\n visibleSeriesCount = 1,\n): TimeSeriesOption {\n const lineWidth = visual.lineWidth ?? DEFAULT_LINE_WIDTH;\n const pointRadius = visual.pointRadius ?? DEFAULT_POINT_RADIUS;\n const shouldStack = querySettings?.stack !== undefined ? querySettings.stack : visual.stack === 'all';\n const areaOpacity = querySettings?.areaOpacity ?? visual.areaOpacity ?? DEFAULT_AREA_OPACITY;\n\n // Show automatic point markers only on short, sparse charts. Dense charts\n // otherwise create a symbol for every sample across every visible series.\n const minuteMs = 60000;\n let showPoints = timeScale.rangeMs <= minuteMs * 15 && visibleSeriesCount <= HIDE_DATAPOINTS_LIMIT;\n // Allows overriding default behavior and opt-in to always show all symbols (can hurt performance)\n if (visual.showPoints === 'always') {\n showPoints = true;\n }\n\n if (visual.display === 'bar') {\n const series: BarSeriesOption = {\n type: 'bar',\n id: id,\n datasetIndex,\n name: formattedName,\n color: paletteColor,\n stack: shouldStack ? 'all' : undefined,\n yAxisIndex: yAxisIndex,\n label: {\n show: false,\n },\n };\n return series;\n }\n\n const series: LineSeriesOption = {\n type: 'line',\n id: id,\n datasetIndex,\n name: formattedName,\n connectNulls: visual.connectNulls ?? DEFAULT_CONNECT_NULLS,\n color: paletteColor,\n stack: shouldStack ? 'all' : undefined,\n yAxisIndex: yAxisIndex,\n sampling: 'lttb',\n progressiveThreshold: OPTIMIZED_MODE_SERIES_LIMIT, // https://echarts.apache.org/en/option.html#series-lines.progressiveThreshold\n showSymbol: showPoints,\n showAllSymbol: true,\n symbolSize: pointRadius,\n lineStyle: {\n width: lineWidth,\n type: (querySettings?.lineStyle ?? visual.lineStyle) as LineStyleType,\n },\n ...interpolationOption(querySettings?.lineInterpolation ?? visual.lineInterpolation),\n // ECharts builds an area polygon whenever areaStyle is set, including at opacity 0.\n ...(areaOpacity > 0 ? { areaStyle: { opacity: areaOpacity } } : {}),\n // https://echarts.apache.org/en/option.html#series-line.emphasis\n emphasis: {\n focus: 'series',\n disabled: areaOpacity > 0, // prevents flicker when moving cursor between shaded regions\n lineStyle: {\n width: lineWidth + 1,\n opacity: 1,\n type: visual.lineStyle,\n },\n },\n blur: {\n lineStyle: {\n width: lineWidth,\n opacity: BLUR_FADEOUT_OPACITY,\n type: visual.lineStyle,\n },\n },\n };\n return series;\n}\n\n/** ECharts' line options for an interpolation. Linear is ECharts' default. */\nexport function interpolationOption(interpolation?: LineInterpolation): Pick<LineSeriesOption, 'smooth' | 'step'> {\n switch (interpolation) {\n case 'smooth':\n return { smooth: true };\n case 'stepBefore':\n return { step: 'start' };\n case 'stepAfter':\n return { step: 'end' };\n default:\n return {};\n }\n}\n\n/**\n * The two ECharts series that fill a band between `lo` and `hi`: an invisible\n * lower edge, and the band's height stacked on it with an area. They carry\n * their own data rather than a dataset, and so must come after every series\n * that has one: the tooltip reads datasets by series index, and would\n * otherwise show the band's height as its upper edge.\n */\nexport function getBandSeries(\n id: string,\n lo: TimeSeriesValueTuple[],\n hi: TimeSeriesValueTuple[],\n color: string,\n opacity: number,\n yAxisIndex = 0,\n interpolation?: LineInterpolation,\n): [LineSeriesOption, LineSeriesOption] {\n const highAt = new Map(hi);\n const height = lo.map(([t, low]): [number, number | null] => {\n const high = highAt.get(t);\n return [t, low === null || high === null || high === undefined ? null : high - low];\n });\n const edge: LineSeriesOption = {\n type: 'line',\n stack: id,\n color,\n yAxisIndex,\n silent: true,\n showSymbol: false,\n lineStyle: { opacity: 0 },\n emphasis: { disabled: true },\n tooltip: { show: false },\n z: 1,\n ...interpolationOption(interpolation),\n };\n return [\n { ...edge, id: `${id}-lo`, data: lo },\n { ...edge, id: `${id}-hi`, data: height, areaStyle: { opacity, color } },\n ];\n}\n\n/**\n * Gets an ECharts scatter series rendering exemplar markers for a single series.\n * Each data item embeds its exemplar (and series labels) so the metadata dialog\n * can be populated when a marker is clicked.\n */\nexport function getExemplarSeries(data: ExemplarChartData): ScatterSeriesOption {\n return {\n type: 'scatter',\n id: `${EXEMPLAR_SERIES_ID_PREFIX}${data.seriesId}`,\n name: data.seriesName,\n color: data.color,\n yAxisIndex: data.yAxisIndex,\n symbol: 'diamond',\n symbolSize: EXEMPLAR_SYMBOL_SIZE,\n z: 10,\n cursor: 'pointer',\n data: data.exemplars.map((exemplar) => ({\n // The plotted Y value is negated when negativeY is enabled for the matching series,\n // while `exemplar.value` keeps the original value for the metadata tooltip.\n value: [exemplar.timestamp, data.negativeY ? -exemplar.value : exemplar.value],\n exemplar,\n seriesLabels: data.seriesLabels,\n })),\n };\n}\n\n/**\n * Gets threshold-specific line series styles\n * markLine cannot be used since it does not update yAxis max / min\n * and threshold data needs to show in the tooltip\n */\nexport function getThresholdSeries(name: string, threshold: StepOptions, seriesIndex: number): LineSeriesOption {\n return {\n type: 'line',\n name: name,\n id: name,\n datasetId: name,\n datasetIndex: seriesIndex,\n color: threshold.color,\n label: {\n show: false,\n },\n lineStyle: {\n type: 'dashed',\n width: 2,\n },\n emphasis: {\n focus: 'series',\n lineStyle: {\n width: 2.5,\n },\n },\n blur: {\n lineStyle: {\n opacity: BLUR_FADEOUT_OPACITY,\n },\n },\n };\n}\n\n/**\n * Converts percent threshold into absolute step value\n * If max is undefined, use the max value from time series data as default\n */\nexport function convertPercentThreshold(\n percent: number,\n data: LegacyTimeSeries[] | TimeSeries[],\n max?: number,\n min?: number,\n): number {\n const percentDecimal = percent / 100;\n const adjustedMax = max ?? findMax(data);\n const adjustedMin = min ?? 0;\n const total = adjustedMax - adjustedMin;\n return percentDecimal * total + adjustedMin;\n}\n\nfunction findMax(data: LegacyTimeSeries[] | TimeSeries[]): number {\n let max = 0;\n if (data.length && data[0] !== undefined && (data as TimeSeries[])[0]?.values) {\n (data as TimeSeries[]).forEach((series) => {\n series.values.forEach((valueTuple: TimeSeriesValueTuple) => {\n const [_, value] = valueTuple;\n // Use the absolute value so percent thresholds compute correctly against\n // negated series (e.g. when `querySettings[].negativeY` is enabled).\n if (typeof value === 'number' && Math.abs(value) > max) {\n max = Math.abs(value);\n }\n });\n });\n } else {\n (data as LegacyTimeSeries[]).forEach((series) => {\n if (series.data !== undefined) {\n series.data.forEach((value: EChartsValues) => {\n if (typeof value === 'number' && Math.abs(value) > max) {\n max = Math.abs(value);\n }\n });\n }\n });\n }\n return max;\n}\n\n/**\n * Converts Perses panel yAxis from dashboard spec to ECharts supported yAxis options.\n * Handles both linear and logarithmic scales with appropriate min/max calculations.\n */\nexport function convertPanelYAxis(inputAxis: TimeSeriesChartYAxisOptions = {}): YAXisComponentOption {\n // Determine the appropriate min value based on scale type and user input\n let minValue: YAXisComponentOption['min'];\n if (inputAxis.logBase !== undefined) {\n // For logarithmic scales without explicit min:\n // Let ECharts auto-calculate the range based on data to avoid issues with\n // function-based calculations which can result in improper ranges (e.g., 1-10)\n minValue = undefined;\n } else if (inputAxis?.min !== undefined) {\n // User explicitly set a min value - use it for both linear and log scales\n minValue = inputAxis.min;\n } else {\n // For linear scales without explicit min:\n // Use dynamic calculation with padding for better visualization\n // https://echarts.apache.org/en/option.html#yAxis.min\n minValue = (value): number => {\n if (value.min >= 0 && value.min <= 1) {\n // Helps with PercentDecimal units, or datasets that return 0 or 1 booleans\n return 0;\n }\n\n // Note: We can tweak the MULTIPLIER constants if we want\n // TODO: Experiment with using a padding that is based on the difference between max value and min value\n if (value.min > 0) {\n return roundDown(value.min * POSITIVE_MIN_VALUE_MULTIPLIER);\n } else {\n return roundDown(value.min * NEGATIVE_MIN_VALUE_MULTIPLIER);\n }\n };\n }\n\n // Build the yAxis configuration\n const yAxis = {\n show: inputAxis?.show ?? DEFAULT_Y_AXIS.show,\n min: minValue,\n max: inputAxis?.max,\n } satisfies YAXisComponentOption;\n\n // Apply logarithmic scale settings if requested\n if (inputAxis.logBase !== undefined) {\n return {\n ...yAxis,\n type: 'log',\n logBase: inputAxis.logBase,\n };\n }\n\n return yAxis;\n}\n\n/**\n * Rounds down to nearest number with one significant digit.\n *\n * Examples:\n * 1. 675 --> 600\n * 2. 0.567 --> 0.5\n * 3. -12 --> -20\n */\nexport function roundDown(num: number): number {\n const magnitude = Math.floor(Math.log10(Math.abs(num)));\n const firstDigit = Math.floor(num / Math.pow(10, magnitude));\n return Number((firstDigit * Math.pow(10, magnitude)).toPrecision(1));\n}\n"],"names":["OPTIMIZED_MODE_SERIES_LIMIT","getCommonTimeScale","DEFAULT_AREA_OPACITY","DEFAULT_CONNECT_NULLS","DEFAULT_LINE_WIDTH","DEFAULT_POINT_RADIUS","DEFAULT_Y_AXIS","POSITIVE_MIN_VALUE_MULTIPLIER","NEGATIVE_MIN_VALUE_MULTIPLIER","EMPTY_GRAPH_DATA","timeSeries","xAxis","legendItems","HIDE_DATAPOINTS_LIMIT","BLUR_FADEOUT_OPACITY","EXEMPLAR_SERIES_ID_PREFIX","EXEMPLAR_SYMBOL_SIZE","getCommonTimeScaleForQueries","queries","seriesData","map","query","data","getTimeSeries","id","datasetIndex","formattedName","visual","timeScale","paletteColor","querySettings","yAxisIndex","visibleSeriesCount","lineWidth","pointRadius","shouldStack","stack","undefined","areaOpacity","minuteMs","showPoints","rangeMs","display","series","type","name","color","label","show","connectNulls","sampling","progressiveThreshold","showSymbol","showAllSymbol","symbolSize","lineStyle","width","interpolationOption","lineInterpolation","areaStyle","opacity","emphasis","focus","disabled","blur","interpolation","smooth","step","getBandSeries","lo","hi","highAt","Map","height","t","low","high","get","edge","silent","tooltip","z","getExemplarSeries","seriesId","seriesName","symbol","cursor","exemplars","exemplar","value","timestamp","negativeY","seriesLabels","getThresholdSeries","threshold","seriesIndex","datasetId","convertPercentThreshold","percent","max","min","percentDecimal","adjustedMax","findMax","adjustedMin","total","length","values","forEach","valueTuple","_","Math","abs","convertPanelYAxis","inputAxis","minValue","logBase","roundDown","yAxis","num","magnitude","floor","log10","firstDigit","pow","Number","toPrecision"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/utils/data-transform.ts"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport type {\n LegacyTimeSeries,\n EChartsDataFormat,\n EChartsValues,\n TimeSeriesOption,\n StepOptions,\n} from '@perses-dev/components';\nimport { OPTIMIZED_MODE_SERIES_LIMIT, getCommonTimeScale } from '@perses-dev/components';\nimport type { useTimeSeriesQueries, PanelData } from '@perses-dev/plugin-system';\nimport type { Exemplar, Labels, TimeScale, TimeSeries, TimeSeriesData, TimeSeriesValueTuple } from '@perses-dev/spec';\nimport type { YAXisComponentOption } from 'echarts';\nimport type { LineSeriesOption, BarSeriesOption, ScatterSeriesOption } from 'echarts/charts';\n\nimport type {\n TimeSeriesChartVisualOptions,\n TimeSeriesChartYAxisOptions,\n LineInterpolation,\n LineStyleType,\n} from '../time-series-chart-model';\nimport {\n DEFAULT_AREA_OPACITY,\n DEFAULT_CONNECT_NULLS,\n DEFAULT_LINE_WIDTH,\n DEFAULT_POINT_RADIUS,\n DEFAULT_Y_AXIS,\n POSITIVE_MIN_VALUE_MULTIPLIER,\n NEGATIVE_MIN_VALUE_MULTIPLIER,\n} from '../time-series-chart-model';\n\nexport type RunningQueriesState = ReturnType<typeof useTimeSeriesQueries>;\n\nexport const EMPTY_GRAPH_DATA: EChartsDataFormat = {\n timeSeries: [],\n xAxis: [],\n legendItems: [],\n};\n\nexport const HIDE_DATAPOINTS_LIMIT = 70;\n\nexport const BLUR_FADEOUT_OPACITY = 0.5;\n\nexport const EXEMPLAR_SERIES_ID_PREFIX = 'exemplar-';\n\nexport const EXEMPLAR_SYMBOL_SIZE = 14;\n\n/**\n * The exemplars of one series, converted to a chart-friendly shape with the\n * rendering attributes (color, y axis) of the matching time series.\n */\nexport interface ExemplarChartData {\n seriesId: string;\n seriesName: string;\n color: string;\n seriesLabels?: Labels;\n yAxisIndex?: number;\n /**\n * When the matching series is rendered with `querySettings.negativeY`, its values are\n * visually negated so it renders below the X axis. The same transform is applied to\n * the exemplar markers' plotted Y values so they stay next to their series. The\n * original (positive) values remain on each `exemplar` for metadata display.\n */\n negativeY?: boolean;\n exemplars: Exemplar[];\n}\n\n/**\n * Given a list of running queries, calculates a common time scale for use on\n * the x axis (i.e. start/end dates and a step that is divisible into all of\n * the queries' steps).\n */\nexport function getCommonTimeScaleForQueries(queries: Array<PanelData<TimeSeriesData>>): TimeScale | undefined {\n const seriesData = queries.map((query) => query.data);\n return getCommonTimeScale(seriesData);\n}\n\n/**\n * Gets ECharts line series option properties for regular trends\n */\nexport function getTimeSeries(\n id: string,\n datasetIndex: number,\n formattedName: string,\n visual: TimeSeriesChartVisualOptions,\n timeScale: TimeScale,\n paletteColor: string,\n querySettings?: {\n lineStyle?: LineStyleType;\n lineInterpolation?: LineInterpolation;\n areaOpacity?: number;\n stack?: boolean;\n },\n yAxisIndex?: number,\n visibleSeriesCount = 1,\n): TimeSeriesOption {\n const lineWidth = visual.lineWidth ?? DEFAULT_LINE_WIDTH;\n const pointRadius = visual.pointRadius ?? DEFAULT_POINT_RADIUS;\n const shouldStack = querySettings?.stack !== undefined ? querySettings.stack : visual.stack === 'all';\n const areaOpacity = querySettings?.areaOpacity ?? visual.areaOpacity ?? DEFAULT_AREA_OPACITY;\n\n // Show automatic point markers only on short, sparse charts. Dense charts\n // otherwise create a symbol for every sample across every visible series.\n const minuteMs = 60000;\n let showPoints = timeScale.rangeMs <= minuteMs * 15 && visibleSeriesCount <= HIDE_DATAPOINTS_LIMIT;\n // Allows overriding default behavior and opt-in to always show all symbols (can hurt performance)\n if (visual.showPoints === 'always') {\n showPoints = true;\n }\n\n if (visual.display === 'bar') {\n const series: BarSeriesOption = {\n type: 'bar',\n id: id,\n datasetIndex,\n name: formattedName,\n color: paletteColor,\n stack: shouldStack ? 'all' : undefined,\n yAxisIndex: yAxisIndex,\n label: {\n show: false,\n },\n };\n return series;\n }\n\n const series: LineSeriesOption = {\n type: 'line',\n id: id,\n datasetIndex,\n name: formattedName,\n connectNulls: visual.connectNulls ?? DEFAULT_CONNECT_NULLS,\n color: paletteColor,\n stack: shouldStack ? 'all' : undefined,\n yAxisIndex: yAxisIndex,\n sampling: 'lttb',\n progressiveThreshold: OPTIMIZED_MODE_SERIES_LIMIT, // https://echarts.apache.org/en/option.html#series-lines.progressiveThreshold\n showSymbol: showPoints,\n showAllSymbol: true,\n symbolSize: pointRadius,\n lineStyle: {\n width: lineWidth,\n type: (querySettings?.lineStyle ?? visual.lineStyle) as LineStyleType,\n },\n ...interpolationOption(querySettings?.lineInterpolation ?? visual.lineInterpolation),\n // ECharts builds an area polygon whenever areaStyle is set, including at opacity 0.\n ...(areaOpacity > 0 ? { areaStyle: { opacity: areaOpacity } } : {}),\n // https://echarts.apache.org/en/option.html#series-line.emphasis\n emphasis: {\n focus: 'series',\n disabled: areaOpacity > 0, // prevents flicker when moving cursor between shaded regions\n lineStyle: {\n width: lineWidth + 1,\n opacity: 1,\n type: visual.lineStyle,\n },\n },\n blur: {\n lineStyle: {\n width: lineWidth,\n opacity: BLUR_FADEOUT_OPACITY,\n type: visual.lineStyle,\n },\n },\n };\n return series;\n}\n\n/** ECharts' line options for an interpolation. Linear is ECharts' default. */\nexport function interpolationOption(interpolation?: LineInterpolation): Pick<LineSeriesOption, 'smooth' | 'step'> {\n switch (interpolation) {\n case 'smooth':\n return { smooth: true };\n case 'stepBefore':\n return { step: 'start' };\n case 'stepAfter':\n return { step: 'end' };\n default:\n return {};\n }\n}\n\n/**\n * The two ECharts series that fill a band between `lo` and `hi`: an invisible\n * lower edge, and the band's height stacked on it with an area. They carry\n * their own data rather than a dataset, and so must come after every series\n * that has one: the tooltip reads datasets by series index, and would\n * otherwise show the band's height as its upper edge.\n */\nexport function getBandSeries(\n id: string,\n lo: TimeSeriesValueTuple[],\n hi: TimeSeriesValueTuple[],\n color: string,\n opacity: number,\n yAxisIndex = 0,\n interpolation?: LineInterpolation,\n): [LineSeriesOption, LineSeriesOption] {\n const highAt = new Map(hi);\n const height = lo.map(([t, low]): [number, number | null] => {\n const high = highAt.get(t);\n return [t, low === null || high === null || high === undefined ? null : high - low];\n });\n const edge: LineSeriesOption = {\n type: 'line',\n stack: id,\n color,\n yAxisIndex,\n silent: true,\n showSymbol: false,\n lineStyle: { opacity: 0 },\n emphasis: { disabled: true },\n tooltip: { show: false },\n z: 1,\n ...interpolationOption(interpolation),\n };\n return [\n { ...edge, id: `${id}-lo`, data: lo },\n { ...edge, id: `${id}-hi`, data: height, areaStyle: { opacity, color } },\n ];\n}\n\n/**\n * Gets an ECharts scatter series rendering exemplar markers for a single series.\n * Each data item embeds its exemplar (and series labels) so the metadata dialog\n * can be populated when a marker is clicked.\n */\nexport function getExemplarSeries(data: ExemplarChartData): ScatterSeriesOption {\n return {\n type: 'scatter',\n id: `${EXEMPLAR_SERIES_ID_PREFIX}${data.seriesId}`,\n name: data.seriesName,\n color: data.color,\n yAxisIndex: data.yAxisIndex,\n symbol: 'diamond',\n symbolSize: EXEMPLAR_SYMBOL_SIZE,\n z: 10,\n cursor: 'pointer',\n data: data.exemplars.map((exemplar) => ({\n // The plotted Y value is negated when negativeY is enabled for the matching series,\n // while `exemplar.value` keeps the original value for the metadata tooltip.\n value: [exemplar.timestamp, data.negativeY ? -exemplar.value : exemplar.value],\n exemplar,\n seriesLabels: data.seriesLabels,\n })),\n };\n}\n\n/**\n * Gets threshold-specific line series styles\n * markLine cannot be used since it does not update yAxis max / min\n * and threshold data needs to show in the tooltip\n */\nexport function getThresholdSeries(name: string, threshold: StepOptions, seriesIndex: number): LineSeriesOption {\n return {\n type: 'line',\n name: name,\n id: name,\n datasetId: name,\n datasetIndex: seriesIndex,\n color: threshold.color,\n label: {\n show: false,\n },\n lineStyle: {\n type: 'dashed',\n width: 2,\n },\n emphasis: {\n focus: 'series',\n lineStyle: {\n width: 2.5,\n },\n },\n blur: {\n lineStyle: {\n opacity: BLUR_FADEOUT_OPACITY,\n },\n },\n };\n}\n\n/**\n * Converts percent threshold into absolute step value\n * If max is undefined, use the max value from time series data as default\n */\nexport function convertPercentThreshold(\n percent: number,\n data: LegacyTimeSeries[] | TimeSeries[],\n max?: number,\n min?: number,\n): number {\n const percentDecimal = percent / 100;\n const adjustedMax = max ?? findMax(data);\n const adjustedMin = min ?? 0;\n const total = adjustedMax - adjustedMin;\n return percentDecimal * total + adjustedMin;\n}\n\nfunction findMax(data: LegacyTimeSeries[] | TimeSeries[]): number {\n let max = 0;\n if (data.length && data[0] !== undefined && (data as TimeSeries[])[0]?.values) {\n (data as TimeSeries[]).forEach((series) => {\n series.values.forEach((valueTuple: TimeSeriesValueTuple) => {\n const [_, value] = valueTuple;\n // Use the absolute value so percent thresholds compute correctly against\n // negated series (e.g. when `querySettings[].negativeY` is enabled).\n if (typeof value === 'number' && Math.abs(value) > max) {\n max = Math.abs(value);\n }\n });\n });\n } else {\n (data as LegacyTimeSeries[]).forEach((series) => {\n if (series.data !== undefined) {\n series.data.forEach((value: EChartsValues) => {\n if (typeof value === 'number' && Math.abs(value) > max) {\n max = Math.abs(value);\n }\n });\n }\n });\n }\n return max;\n}\n\n/**\n * Converts Perses panel yAxis from dashboard spec to ECharts supported yAxis options.\n * Handles both linear and logarithmic scales with appropriate min/max calculations.\n */\nexport function convertPanelYAxis(inputAxis: TimeSeriesChartYAxisOptions = {}): YAXisComponentOption {\n // Determine the appropriate min value based on scale type and user input\n let minValue: YAXisComponentOption['min'];\n if (inputAxis.logBase !== undefined) {\n // For logarithmic scales without explicit min:\n // Let ECharts auto-calculate the range based on data to avoid issues with\n // function-based calculations which can result in improper ranges (e.g., 1-10)\n minValue = undefined;\n } else if (inputAxis?.min !== undefined) {\n // User explicitly set a min value - use it for both linear and log scales\n minValue = inputAxis.min;\n } else {\n // For linear scales without explicit min:\n // Use dynamic calculation with padding for better visualization\n // https://echarts.apache.org/en/option.html#yAxis.min\n minValue = (value): number => {\n if (value.min >= 0 && value.min <= 1) {\n // Helps with PercentDecimal units, or datasets that return 0 or 1 booleans\n return 0;\n }\n\n // Note: We can tweak the MULTIPLIER constants if we want\n // TODO: Experiment with using a padding that is based on the difference between max value and min value\n if (value.min > 0) {\n return roundDown(value.min * POSITIVE_MIN_VALUE_MULTIPLIER);\n } else {\n return roundDown(value.min * NEGATIVE_MIN_VALUE_MULTIPLIER);\n }\n };\n }\n\n // Build the yAxis configuration\n const yAxis = {\n show: inputAxis?.show ?? DEFAULT_Y_AXIS.show,\n min: minValue,\n max: inputAxis?.max,\n } satisfies YAXisComponentOption;\n\n // Apply logarithmic scale settings if requested\n if (inputAxis.logBase !== undefined) {\n return {\n ...yAxis,\n type: 'log',\n logBase: inputAxis.logBase,\n };\n }\n\n return yAxis;\n}\n\n/**\n * Rounds down to nearest number with one significant digit.\n *\n * Examples:\n * 1. 675 --> 600\n * 2. 0.567 --> 0.5\n * 3. -12 --> -20\n */\nexport function roundDown(num: number): number {\n const magnitude = Math.floor(Math.log10(Math.abs(num)));\n const firstDigit = Math.floor(num / Math.pow(10, magnitude));\n return Number((firstDigit * Math.pow(10, magnitude)).toPrecision(1));\n}\n\n/**\n * A series' values on the chart's time scale, with nulls for the steps it has\n * no value for, so a line breaks where data is missing.\n *\n * Perses's own (getTimeSeriesValues in @perses-dev/components) moves each value\n * onto startMs + n * stepMs. A calendar day, week or month is not a fixed\n * number of ms: across a clock change a day is 23 or 25 hours, and a month is\n * 28 to 31 days. There a value lands on the next step, a day late, and a null\n * breaks the line before it. Here each value keeps its own time, and a null\n * marks only a step missing between two values; a value within half a step of\n * where one was expected is that step's.\n */\nexport function getTimeSeriesValues(series: TimeSeries, timeScale: TimeScale): TimeSeriesValueTuple[] {\n const { stepMs, endMs } = timeScale;\n const values: TimeSeriesValueTuple[] = [];\n let expected = timeScale.startMs;\n for (const [t, value] of series.values) {\n while (expected + stepMs / 2 < t) {\n values.push([expected, null]);\n expected += stepMs;\n }\n values.push([t, value]);\n // Never back: a value before the scale's start, or out of order, does not\n // send the nulls after it back over steps already passed.\n expected = Math.max(expected, t + stepMs);\n }\n while (expected <= endMs) {\n values.push([expected, null]);\n expected += stepMs;\n }\n return values;\n}\n"],"names":["OPTIMIZED_MODE_SERIES_LIMIT","getCommonTimeScale","DEFAULT_AREA_OPACITY","DEFAULT_CONNECT_NULLS","DEFAULT_LINE_WIDTH","DEFAULT_POINT_RADIUS","DEFAULT_Y_AXIS","POSITIVE_MIN_VALUE_MULTIPLIER","NEGATIVE_MIN_VALUE_MULTIPLIER","EMPTY_GRAPH_DATA","timeSeries","xAxis","legendItems","HIDE_DATAPOINTS_LIMIT","BLUR_FADEOUT_OPACITY","EXEMPLAR_SERIES_ID_PREFIX","EXEMPLAR_SYMBOL_SIZE","getCommonTimeScaleForQueries","queries","seriesData","map","query","data","getTimeSeries","id","datasetIndex","formattedName","visual","timeScale","paletteColor","querySettings","yAxisIndex","visibleSeriesCount","lineWidth","pointRadius","shouldStack","stack","undefined","areaOpacity","minuteMs","showPoints","rangeMs","display","series","type","name","color","label","show","connectNulls","sampling","progressiveThreshold","showSymbol","showAllSymbol","symbolSize","lineStyle","width","interpolationOption","lineInterpolation","areaStyle","opacity","emphasis","focus","disabled","blur","interpolation","smooth","step","getBandSeries","lo","hi","highAt","Map","height","t","low","high","get","edge","silent","tooltip","z","getExemplarSeries","seriesId","seriesName","symbol","cursor","exemplars","exemplar","value","timestamp","negativeY","seriesLabels","getThresholdSeries","threshold","seriesIndex","datasetId","convertPercentThreshold","percent","max","min","percentDecimal","adjustedMax","findMax","adjustedMin","total","length","values","forEach","valueTuple","_","Math","abs","convertPanelYAxis","inputAxis","minValue","logBase","roundDown","yAxis","num","magnitude","floor","log10","firstDigit","pow","Number","toPrecision","getTimeSeriesValues","stepMs","endMs","expected","startMs","push"],"mappings":"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"}
@@ -1,5 +1,9 @@
1
1
  /**
2
- * Creates a timezone-aware axis formatter function for different time ranges
2
+ * Creates a timezone-aware axis formatter function for different time ranges.
3
+ *
4
+ * Months are named rather than numbered: "01.11" is the 1st of November in
5
+ * one country and the 11th of January in another. Within a few days a tick on
6
+ * midnight names its day, so the hours either side of it are placed.
3
7
  */
4
8
  export declare function createTimezoneAwareAxisFormatter(rangeMs: number, timeZone: string): (value: number) => string;
5
9
  //# sourceMappingURL=timezone-formatter.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"timezone-formatter.d.ts","sourceRoot":"","sources":["../../../src/utils/timezone-formatter.ts"],"names":[],"mappings":"AAmBA;;GAEG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WACxD,MAAM,KAAG,MAAM,CAyBxC"}
1
+ {"version":3,"file":"timezone-formatter.d.ts","sourceRoot":"","sources":["../../../src/utils/timezone-formatter.ts"],"names":[],"mappings":"AAmBA;;;;;;GAMG;AACH,wBAAgB,gCAAgC,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,WACxD,MAAM,KAAG,MAAM,CA4BxC"}
@@ -15,7 +15,11 @@ const DAY_MS = 86400000;
15
15
  const MONTH_MS = 2629440000;
16
16
  const YEAR_MS = 31536000000;
17
17
  /**
18
- * Creates a timezone-aware axis formatter function for different time ranges
18
+ * Creates a timezone-aware axis formatter function for different time ranges.
19
+ *
20
+ * Months are named rather than numbered: "01.11" is the 1st of November in
21
+ * one country and the 11th of January in another. Within a few days a tick on
22
+ * midnight names its day, so the hours either side of it are placed.
19
23
  */ export function createTimezoneAwareAxisFormatter(rangeMs, timeZone) {
20
24
  return function(value) {
21
25
  const timeStamp = new Date(Number(value));
@@ -23,19 +27,21 @@ const YEAR_MS = 31536000000;
23
27
  if (rangeMs > YEAR_MS * 5) {
24
28
  return formatWithTimeZone(timeStamp, 'yyyy', timeZone);
25
29
  }
26
- // more than 2 years
27
- if (rangeMs > YEAR_MS * 2) {
30
+ // more than 6 months
31
+ if (rangeMs >= MONTH_MS * 6) {
28
32
  return formatWithTimeZone(timeStamp, 'MMM yyyy', timeZone);
29
33
  }
30
- // between 10 days to 6 months
31
- if (rangeMs > DAY_MS * 10 && rangeMs < MONTH_MS * 6) {
32
- return formatWithTimeZone(timeStamp, 'dd.MM', timeZone);
34
+ // more than 10 days
35
+ if (rangeMs > DAY_MS * 10) {
36
+ return formatWithTimeZone(timeStamp, 'MMM d', timeZone);
33
37
  }
38
+ const time = formatWithTimeZone(timeStamp, 'HH:mm', timeZone);
39
+ const day = formatWithTimeZone(timeStamp, 'MMM d', timeZone);
34
40
  // between 2 and 10 days
35
- if (rangeMs > DAY_MS * 2 && rangeMs <= DAY_MS * 10) {
36
- return formatWithTimeZone(timeStamp, 'dd.MM HH:mm', timeZone);
41
+ if (rangeMs > DAY_MS * 2) {
42
+ return time === '00:00' ? day : `${day} ${time}`;
37
43
  }
38
- return formatWithTimeZone(timeStamp, 'HH:mm', timeZone);
44
+ return time === '00:00' ? day : time;
39
45
  };
40
46
  }
41
47
 
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/utils/timezone-formatter.ts"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { formatWithTimeZone } from '@perses-dev/components';\n\nconst DAY_MS = 86400000;\nconst MONTH_MS = 2629440000;\nconst YEAR_MS = 31536000000;\n\n/**\n * Creates a timezone-aware axis formatter function for different time ranges\n */\nexport function createTimezoneAwareAxisFormatter(rangeMs: number, timeZone: string) {\n return function (value: number): string {\n const timeStamp = new Date(Number(value));\n\n // more than 5 years\n if (rangeMs > YEAR_MS * 5) {\n return formatWithTimeZone(timeStamp, 'yyyy', timeZone);\n }\n\n // more than 2 years\n if (rangeMs > YEAR_MS * 2) {\n return formatWithTimeZone(timeStamp, 'MMM yyyy', timeZone);\n }\n\n // between 10 days to 6 months\n if (rangeMs > DAY_MS * 10 && rangeMs < MONTH_MS * 6) {\n return formatWithTimeZone(timeStamp, 'dd.MM', timeZone);\n }\n\n // between 2 and 10 days\n if (rangeMs > DAY_MS * 2 && rangeMs <= DAY_MS * 10) {\n return formatWithTimeZone(timeStamp, 'dd.MM HH:mm', timeZone);\n }\n\n return formatWithTimeZone(timeStamp, 'HH:mm', timeZone);\n };\n}\n"],"names":["formatWithTimeZone","DAY_MS","MONTH_MS","YEAR_MS","createTimezoneAwareAxisFormatter","rangeMs","timeZone","value","timeStamp","Date","Number"],"mappings":"AAAA,+BAA+B;AAC/B,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAEjC,SAASA,kBAAkB,QAAQ,yBAAyB;AAE5D,MAAMC,SAAS;AACf,MAAMC,WAAW;AACjB,MAAMC,UAAU;AAEhB;;CAEC,GACD,OAAO,SAASC,iCAAiCC,OAAe,EAAEC,QAAgB;IAChF,OAAO,SAAUC,KAAa;QAC5B,MAAMC,YAAY,IAAIC,KAAKC,OAAOH;QAElC,oBAAoB;QACpB,IAAIF,UAAUF,UAAU,GAAG;YACzB,OAAOH,mBAAmBQ,WAAW,QAAQF;QAC/C;QAEA,oBAAoB;QACpB,IAAID,UAAUF,UAAU,GAAG;YACzB,OAAOH,mBAAmBQ,WAAW,YAAYF;QACnD;QAEA,8BAA8B;QAC9B,IAAID,UAAUJ,SAAS,MAAMI,UAAUH,WAAW,GAAG;YACnD,OAAOF,mBAAmBQ,WAAW,SAASF;QAChD;QAEA,wBAAwB;QACxB,IAAID,UAAUJ,SAAS,KAAKI,WAAWJ,SAAS,IAAI;YAClD,OAAOD,mBAAmBQ,WAAW,eAAeF;QACtD;QAEA,OAAON,mBAAmBQ,WAAW,SAASF;IAChD;AACF"}
1
+ {"version":3,"sources":["../../../src/utils/timezone-formatter.ts"],"sourcesContent":["// Copyright The Perses Authors\n// Licensed under the Apache License, Version 2.0 (the \"License\");\n// you may not use this file except in compliance with the License.\n// You may obtain a copy of the License at\n//\n// http://www.apache.org/licenses/LICENSE-2.0\n//\n// Unless required by applicable law or agreed to in writing, software\n// distributed under the License is distributed on an \"AS IS\" BASIS,\n// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n// See the License for the specific language governing permissions and\n// limitations under the License.\n\nimport { formatWithTimeZone } from '@perses-dev/components';\n\nconst DAY_MS = 86400000;\nconst MONTH_MS = 2629440000;\nconst YEAR_MS = 31536000000;\n\n/**\n * Creates a timezone-aware axis formatter function for different time ranges.\n *\n * Months are named rather than numbered: \"01.11\" is the 1st of November in\n * one country and the 11th of January in another. Within a few days a tick on\n * midnight names its day, so the hours either side of it are placed.\n */\nexport function createTimezoneAwareAxisFormatter(rangeMs: number, timeZone: string) {\n return function (value: number): string {\n const timeStamp = new Date(Number(value));\n\n // more than 5 years\n if (rangeMs > YEAR_MS * 5) {\n return formatWithTimeZone(timeStamp, 'yyyy', timeZone);\n }\n\n // more than 6 months\n if (rangeMs >= MONTH_MS * 6) {\n return formatWithTimeZone(timeStamp, 'MMM yyyy', timeZone);\n }\n\n // more than 10 days\n if (rangeMs > DAY_MS * 10) {\n return formatWithTimeZone(timeStamp, 'MMM d', timeZone);\n }\n\n const time = formatWithTimeZone(timeStamp, 'HH:mm', timeZone);\n const day = formatWithTimeZone(timeStamp, 'MMM d', timeZone);\n\n // between 2 and 10 days\n if (rangeMs > DAY_MS * 2) {\n return time === '00:00' ? day : `${day} ${time}`;\n }\n\n return time === '00:00' ? day : time;\n };\n}\n"],"names":["formatWithTimeZone","DAY_MS","MONTH_MS","YEAR_MS","createTimezoneAwareAxisFormatter","rangeMs","timeZone","value","timeStamp","Date","Number","time","day"],"mappings":"AAAA,+BAA+B;AAC/B,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;AAEjC,SAASA,kBAAkB,QAAQ,yBAAyB;AAE5D,MAAMC,SAAS;AACf,MAAMC,WAAW;AACjB,MAAMC,UAAU;AAEhB;;;;;;CAMC,GACD,OAAO,SAASC,iCAAiCC,OAAe,EAAEC,QAAgB;IAChF,OAAO,SAAUC,KAAa;QAC5B,MAAMC,YAAY,IAAIC,KAAKC,OAAOH;QAElC,oBAAoB;QACpB,IAAIF,UAAUF,UAAU,GAAG;YACzB,OAAOH,mBAAmBQ,WAAW,QAAQF;QAC/C;QAEA,qBAAqB;QACrB,IAAID,WAAWH,WAAW,GAAG;YAC3B,OAAOF,mBAAmBQ,WAAW,YAAYF;QACnD;QAEA,oBAAoB;QACpB,IAAID,UAAUJ,SAAS,IAAI;YACzB,OAAOD,mBAAmBQ,WAAW,SAASF;QAChD;QAEA,MAAMK,OAAOX,mBAAmBQ,WAAW,SAASF;QACpD,MAAMM,MAAMZ,mBAAmBQ,WAAW,SAASF;QAEnD,wBAAwB;QACxB,IAAID,UAAUJ,SAAS,GAAG;YACxB,OAAOU,SAAS,UAAUC,MAAM,GAAGA,IAAI,CAAC,EAAED,MAAM;QAClD;QAEA,OAAOA,SAAS,UAAUC,MAAMD;IAClC;AACF"}