@perses-dev/timeseries-chart-plugin 0.13.0-beta.2 → 0.13.0-rc.0

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 (114) hide show
  1. package/__mf/js/TimeSeriesChart.3eab0aad.js +7 -0
  2. package/__mf/js/async/121.c3c0e54f.js +2 -0
  3. package/__mf/js/async/137.59c998ab.js +38 -0
  4. package/__mf/js/async/177.2fef2962.js +1 -0
  5. package/__mf/js/async/187.f5e8932b.js +1 -0
  6. package/__mf/js/async/192.c3dd56d5.js +1 -0
  7. package/__mf/js/async/2.60fa4a40.js +1 -0
  8. package/__mf/js/async/208.b9f374ad.js +7 -0
  9. package/__mf/js/async/235.d23c8ef0.js +1 -0
  10. package/__mf/js/async/274.72dfa9c8.js +2 -0
  11. package/__mf/js/async/356.8170c96d.js +1 -0
  12. package/__mf/js/async/392.b9ba5bf2.js +2 -0
  13. package/__mf/js/async/470.e09f33b7.js +2 -0
  14. package/__mf/js/async/491.85194549.js +1 -0
  15. package/__mf/js/async/52.0d78de2b.js +1 -0
  16. package/__mf/js/async/527.f97d1b86.js +110 -0
  17. package/__mf/js/async/558.8ac6d4e3.js +3 -0
  18. package/__mf/js/async/568.45da2c30.js +1 -0
  19. package/__mf/js/async/587.c9460696.js +1 -0
  20. package/__mf/js/async/588.0a1aa144.js +1 -0
  21. package/__mf/js/async/603.3b771310.js +2 -0
  22. package/__mf/js/async/603.3b771310.js.LICENSE.txt +9 -0
  23. package/__mf/js/async/616.79484ac8.js +1 -0
  24. package/__mf/js/async/655.dbf8c618.js +1 -0
  25. package/__mf/js/async/694.0fb9ee5a.js +2 -0
  26. package/__mf/js/async/702.785cd635.js +2 -0
  27. package/__mf/js/async/71.8c6cf914.js +1 -0
  28. package/__mf/js/async/724.c45dcf5b.js +1 -0
  29. package/__mf/js/async/873.04a201bf.js +1 -0
  30. package/__mf/js/async/941.b89f8595.js +2 -0
  31. package/__mf/js/async/955.176fe200.js +1 -0
  32. package/__mf/js/async/968.2a9fb1a0.js +1 -0
  33. package/__mf/js/async/978.a9865b23.js +1 -0
  34. package/__mf/js/async/984.4793f667.js +22 -0
  35. package/__mf/js/async/{90.16425d5a.js.LICENSE.txt → 984.4793f667.js.LICENSE.txt} +0 -10
  36. package/__mf/js/async/__federation_expose_TimeSeriesChart.62d8f589.js +1 -0
  37. package/__mf/js/async/lib-router.319c02cd.js +2 -0
  38. package/__mf/js/main.23e38819.js +7 -0
  39. package/lib/QuerySettingsEditor.d.ts.map +1 -1
  40. package/lib/QuerySettingsEditor.js +93 -14
  41. package/lib/QuerySettingsEditor.js.map +1 -1
  42. package/lib/TimeSeriesChartBase.d.ts +2 -0
  43. package/lib/TimeSeriesChartBase.d.ts.map +1 -1
  44. package/lib/TimeSeriesChartBase.js +90 -7
  45. package/lib/TimeSeriesChartBase.js.map +1 -1
  46. package/lib/TimeSeriesChartPanel.d.ts.map +1 -1
  47. package/lib/TimeSeriesChartPanel.js +24 -3
  48. package/lib/TimeSeriesChartPanel.js.map +1 -1
  49. package/lib/VisualOptionsEditor.js.map +1 -1
  50. package/lib/annotations/AnnotationTooltip.d.ts +18 -0
  51. package/lib/annotations/AnnotationTooltip.d.ts.map +1 -0
  52. package/lib/annotations/AnnotationTooltip.js +352 -0
  53. package/lib/annotations/AnnotationTooltip.js.map +1 -0
  54. package/lib/cjs/QuerySettingsEditor.js +91 -12
  55. package/lib/cjs/TimeSeriesChartBase.js +90 -7
  56. package/lib/cjs/TimeSeriesChartPanel.js +24 -3
  57. package/lib/cjs/annotations/AnnotationTooltip.js +370 -0
  58. package/lib/cjs/utils/annotation.js +37 -0
  59. package/lib/cjs/utils/data-transform.js +9 -13
  60. package/lib/time-series-chart-model.d.ts +7 -0
  61. package/lib/time-series-chart-model.d.ts.map +1 -1
  62. package/lib/time-series-chart-model.js.map +1 -1
  63. package/lib/utils/annotation.d.ts +5 -0
  64. package/lib/utils/annotation.d.ts.map +1 -0
  65. package/lib/utils/annotation.js +29 -0
  66. package/lib/utils/annotation.js.map +1 -0
  67. package/lib/utils/data-transform.d.ts +1 -0
  68. package/lib/utils/data-transform.d.ts.map +1 -1
  69. package/lib/utils/data-transform.js +9 -13
  70. package/lib/utils/data-transform.js.map +1 -1
  71. package/mf-manifest.json +61 -36
  72. package/mf-stats.json +68 -36
  73. package/package.json +5 -4
  74. package/__mf/js/TimeSeriesChart.e9912623.js +0 -7
  75. package/__mf/js/async/121.df4898d6.js +0 -2
  76. package/__mf/js/async/177.56f1be10.js +0 -1
  77. package/__mf/js/async/192.beef3a1a.js +0 -1
  78. package/__mf/js/async/2.c3740bb9.js +0 -1
  79. package/__mf/js/async/235.c24f24f7.js +0 -1
  80. package/__mf/js/async/274.d112354f.js +0 -2
  81. package/__mf/js/async/291.5ce0f3cb.js +0 -39
  82. package/__mf/js/async/356.9f551c8a.js +0 -1
  83. package/__mf/js/async/392.2350fb02.js +0 -2
  84. package/__mf/js/async/470.a4fd777e.js +0 -2
  85. package/__mf/js/async/491.6f81cf51.js +0 -1
  86. package/__mf/js/async/568.cdcaf97d.js +0 -1
  87. package/__mf/js/async/587.c12780da.js +0 -1
  88. package/__mf/js/async/588.6662e16a.js +0 -1
  89. package/__mf/js/async/616.52acb1b4.js +0 -1
  90. package/__mf/js/async/681.8d61b348.js +0 -110
  91. package/__mf/js/async/702.de247ad8.js +0 -2
  92. package/__mf/js/async/71.84e486c9.js +0 -1
  93. package/__mf/js/async/724.3439df06.js +0 -1
  94. package/__mf/js/async/78.91de282a.js +0 -1
  95. package/__mf/js/async/821.aa2ab3e8.js +0 -7
  96. package/__mf/js/async/873.9aad328a.js +0 -1
  97. package/__mf/js/async/90.16425d5a.js +0 -24
  98. package/__mf/js/async/941.c0e2d25e.js +0 -2
  99. package/__mf/js/async/968.6de5fa7c.js +0 -1
  100. package/__mf/js/async/978.5c14e175.js +0 -1
  101. package/__mf/js/async/__federation_expose_TimeSeriesChart.58a7f341.js +0 -1
  102. package/__mf/js/async/lib-router.7a3f7bb4.js +0 -2
  103. package/__mf/js/main.de1f5d2f.js +0 -7
  104. /package/__mf/css/async/{821.85c2cbf6.css → 208.85c2cbf6.css} +0 -0
  105. /package/__mf/js/async/{121.df4898d6.js.LICENSE.txt → 121.c3c0e54f.js.LICENSE.txt} +0 -0
  106. /package/__mf/js/async/{821.aa2ab3e8.js.LICENSE.txt → 208.b9f374ad.js.LICENSE.txt} +0 -0
  107. /package/__mf/js/async/{274.d112354f.js.LICENSE.txt → 274.72dfa9c8.js.LICENSE.txt} +0 -0
  108. /package/__mf/js/async/{392.2350fb02.js.LICENSE.txt → 392.b9ba5bf2.js.LICENSE.txt} +0 -0
  109. /package/__mf/js/async/{470.a4fd777e.js.LICENSE.txt → 470.e09f33b7.js.LICENSE.txt} +0 -0
  110. /package/__mf/js/async/{681.8d61b348.js.LICENSE.txt → 527.f97d1b86.js.LICENSE.txt} +0 -0
  111. /package/__mf/js/async/{291.5ce0f3cb.js.LICENSE.txt → 694.0fb9ee5a.js.LICENSE.txt} +0 -0
  112. /package/__mf/js/async/{702.de247ad8.js.LICENSE.txt → 702.785cd635.js.LICENSE.txt} +0 -0
  113. /package/__mf/js/async/{941.c0e2d25e.js.LICENSE.txt → 941.b89f8595.js.LICENSE.txt} +0 -0
  114. /package/__mf/js/async/{lib-router.7a3f7bb4.js.LICENSE.txt → lib-router.319c02cd.js.LICENSE.txt} +0 -0
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../src/annotations/AnnotationTooltip.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, Divider, Portal, Stack, Typography } from '@mui/material';\nimport Pin from 'mdi-material-ui/Pin';\nimport PinOutline from 'mdi-material-ui/PinOutline';\nimport useResizeObserver from 'use-resize-observer';\nimport type { LineSeriesOption } from 'echarts';\nimport {\n assembleTransform,\n CursorCoordinates,\n getDateAndTime,\n getTooltipStyles,\n PIN_TOOLTIP_HELP_TEXT,\n TOOLTIP_BG_COLOR_FALLBACK,\n TOOLTIP_MAX_WIDTH,\n UNPIN_TOOLTIP_HELP_TEXT,\n useMousePosition,\n} from '@perses-dev/components';\nimport { DEFAULT_ANNOTATION_COLOR } from '@perses-dev/plugin-system';\nimport { TimeSeriesAnnotation } from '../utils/annotation';\n\nexport interface AnnotationTooltipProps {\n annotation: TimeSeriesAnnotation;\n containerId?: string;\n formatWithUserTimeZone: (date: Date, formatString: string) => string;\n pinnedPos: CursorCoordinates | null;\n enablePinning?: boolean;\n onUnpinClick?: () => void;\n}\n\nexport function AnnotationTooltip({\n annotation,\n containerId,\n formatWithUserTimeZone,\n pinnedPos,\n enablePinning = true,\n onUnpinClick,\n}: AnnotationTooltipProps): JSX.Element | null {\n const mousePos = useMousePosition();\n const { height, width, ref: tooltipRef } = useResizeObserver<HTMLDivElement>();\n\n const isPinned = pinnedPos !== null;\n if (!isPinned && mousePos === null) return null;\n\n const containerElement = containerId ? document.querySelector(containerId) : undefined;\n const maxHeight = containerElement ? containerElement.getBoundingClientRect().height : undefined;\n const transform = assembleTransform(mousePos, pinnedPos, height ?? 0, width ?? 0, containerElement);\n\n const start = getDateAndTime(annotation.start, formatWithUserTimeZone);\n const end = annotation.end !== undefined ? getDateAndTime(annotation.end, formatWithUserTimeZone) : null;\n\n return (\n <Portal container={containerElement}>\n <Box ref={tooltipRef} sx={(theme) => getTooltipStyles(theme, pinnedPos, maxHeight)} style={{ transform }}>\n <Stack spacing={0.5}>\n <Box\n sx={(theme) => ({\n width: '100%',\n maxWidth: TOOLTIP_MAX_WIDTH,\n padding: theme.spacing(1.5, 2, 0.5, 2),\n backgroundColor: theme.palette.designSystem?.grey[800] ?? TOOLTIP_BG_COLOR_FALLBACK,\n position: 'sticky',\n top: 0,\n left: 0,\n })}\n >\n <Box sx={{ display: 'flex', alignItems: 'center', paddingBottom: 0.5, width: '100%' }}>\n <Box\n sx={{\n width: 8,\n height: 8,\n borderRadius: '50%',\n backgroundColor: annotation.color ?? DEFAULT_ANNOTATION_COLOR,\n marginRight: 1,\n flexShrink: 0,\n }}\n />\n <Box sx={{ flexGrow: 1, minWidth: 0 }}>\n <Typography variant=\"caption\">\n {start.formattedDate} - <strong>{start.formattedTime}</strong>\n </Typography>\n {end && (\n <>\n <Typography variant=\"caption\">{' → '}</Typography>\n <Typography variant=\"caption\">\n {end.formattedDate} - <strong>{end.formattedTime}</strong>\n </Typography>\n </>\n )}\n </Box>\n {enablePinning && (\n <Stack direction=\"row\" alignItems=\"center\" sx={{ marginLeft: 1, flexShrink: 0 }}>\n <Typography sx={{ marginRight: 0.5, fontSize: 11, verticalAlign: 'middle' }}>\n {isPinned ? UNPIN_TOOLTIP_HELP_TEXT : PIN_TOOLTIP_HELP_TEXT}\n </Typography>\n {isPinned ? (\n <Pin\n onClick={() => {\n if (onUnpinClick !== undefined) onUnpinClick();\n }}\n sx={{ fontSize: 16, cursor: 'pointer' }}\n />\n ) : (\n <PinOutline sx={{ fontSize: 16 }} />\n )}\n </Stack>\n )}\n </Box>\n <Divider sx={(theme) => ({ width: '100%', borderColor: theme.palette.grey['500'] })} />\n </Box>\n <Box sx={(theme) => ({ padding: theme.spacing(0.5, 2, 1.5, 2) })}>\n {annotation.title && (\n <Typography variant=\"caption\" sx={{ display: 'block', fontWeight: 'bold', marginBottom: 0.5 }}>\n {annotation.title}\n </Typography>\n )}\n {annotation.legend && (\n <Typography variant=\"caption\" sx={{ display: 'block', marginBottom: 0.5 }}>\n {annotation.legend}\n </Typography>\n )}\n {annotation.tags && Object.keys(annotation.tags).length > 0 && (\n <Box sx={{ display: 'flex', flexWrap: 'wrap', gap: 0.5, marginTop: 0.5 }}>\n {Object.entries(annotation.tags).map(([key, value]) => (\n <Box\n key={key}\n sx={(theme) => ({\n backgroundColor: theme.palette.grey['700'],\n borderRadius: '4px',\n padding: '2px 6px',\n fontSize: '0.7rem',\n fontFamily: 'monospace',\n })}\n >\n {key}: {value}\n </Box>\n ))}\n </Box>\n )}\n </Box>\n </Stack>\n </Box>\n </Portal>\n );\n}\n\n/**\n * Build ECharts series options for rendering annotations as markArea (range),\n * markLine (vertical dashed lines) and markPoint (triangle markers under the X-axis).\n */\nexport function buildAnnotationSeries(annotations: TimeSeriesAnnotation[] | undefined): LineSeriesOption[] {\n if (!annotations?.length) return [];\n\n const markAreaData: Array<[{ xAxis: number; itemStyle?: { color: string; opacity: number } }, { xAxis: number }]> =\n [];\n const markLineData: Array<{\n xAxis: number;\n lineStyle?: { color: string; width: number; type: 'dashed' | 'solid' | 'dotted' };\n label?: { show: boolean };\n annotationIndex?: number;\n }> = [];\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const markPointData: any[] = [];\n\n annotations.forEach((annotation, index) => {\n const color = annotation.color ?? DEFAULT_ANNOTATION_COLOR;\n const opacity = 0.3;\n\n if (annotation.end !== undefined) {\n // Range annotation - use markArea and markLine (silent) + markers at start/end\n markLineData.push({\n xAxis: annotation.start,\n lineStyle: { color, width: 2, type: 'dashed' as const },\n label: { show: false },\n annotationIndex: index,\n });\n\n markLineData.push({\n xAxis: annotation.end,\n lineStyle: { color, width: 2, type: 'dashed' as const },\n label: { show: false },\n annotationIndex: index,\n });\n\n markAreaData.push([\n {\n xAxis: annotation.start,\n itemStyle: { color, opacity },\n },\n { xAxis: annotation.end },\n ]);\n\n // Add start and end markers\n for (const isStart of [true, false]) {\n markPointData.push({\n coord: [isStart ? annotation.start : annotation.end, 0],\n symbol: 'triangle',\n symbolSize: [12, 12],\n symbolRotate: 0,\n symbolOffset: [0, 4], // Position below X-axis\n itemStyle: { color },\n annotationIndex: index,\n emphasis: {\n disabled: true,\n },\n isStart: isStart,\n isEnd: !isStart,\n });\n }\n } else {\n // Point annotation - use markLine (silent) + single marker\n markLineData.push({\n xAxis: annotation.start,\n lineStyle: { color, width: 2, type: 'dashed' as const },\n label: { show: false },\n annotationIndex: index,\n });\n\n // Add point marker\n markPointData.push({\n coord: [annotation.start, 0],\n symbol: 'triangle',\n symbolSize: [12, 10],\n symbolRotate: 0,\n symbolOffset: [0, '120%'], // Position below X-axis\n itemStyle: { color },\n annotationIndex: index,\n isPoint: true,\n });\n }\n });\n\n const series: LineSeriesOption = {\n type: 'line',\n data: [],\n silent: false,\n markArea:\n markAreaData.length > 0\n ? {\n silent: true, // Make area silent, only markers are interactive\n data: markAreaData,\n label: {\n show: false,\n },\n }\n : undefined,\n markLine:\n markLineData.length > 0\n ? {\n silent: true, // Non-interactive: hovering the dashed line keeps the TimeSeries tooltip\n symbol: ['none', 'none'],\n data: markLineData,\n lineStyle: {\n type: 'dashed',\n },\n }\n : undefined,\n markPoint:\n markPointData.length > 0\n ? {\n silent: false, // Markers are interactive\n data: markPointData,\n label: {\n show: false,\n },\n }\n : undefined,\n };\n\n return [series];\n}\n"],"names":["Box","Divider","Portal","Stack","Typography","Pin","PinOutline","useResizeObserver","assembleTransform","getDateAndTime","getTooltipStyles","PIN_TOOLTIP_HELP_TEXT","TOOLTIP_BG_COLOR_FALLBACK","TOOLTIP_MAX_WIDTH","UNPIN_TOOLTIP_HELP_TEXT","useMousePosition","DEFAULT_ANNOTATION_COLOR","AnnotationTooltip","annotation","containerId","formatWithUserTimeZone","pinnedPos","enablePinning","onUnpinClick","mousePos","height","width","ref","tooltipRef","isPinned","containerElement","document","querySelector","undefined","maxHeight","getBoundingClientRect","transform","start","end","container","sx","theme","style","spacing","maxWidth","padding","backgroundColor","palette","designSystem","grey","position","top","left","display","alignItems","paddingBottom","borderRadius","color","marginRight","flexShrink","flexGrow","minWidth","variant","formattedDate","strong","formattedTime","direction","marginLeft","fontSize","verticalAlign","onClick","cursor","borderColor","title","fontWeight","marginBottom","legend","tags","Object","keys","length","flexWrap","gap","marginTop","entries","map","key","value","fontFamily","buildAnnotationSeries","annotations","markAreaData","markLineData","markPointData","forEach","index","opacity","push","xAxis","lineStyle","type","label","show","annotationIndex","itemStyle","isStart","coord","symbol","symbolSize","symbolRotate","symbolOffset","emphasis","disabled","isEnd","isPoint","series","data","silent","markArea","markLine","markPoint"],"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,GAAG,EAAEC,OAAO,EAAEC,MAAM,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAgB;AACxE,OAAOC,SAAS,sBAAsB;AACtC,OAAOC,gBAAgB,6BAA6B;AACpD,OAAOC,uBAAuB,sBAAsB;AAEpD,SACEC,iBAAiB,EAEjBC,cAAc,EACdC,gBAAgB,EAChBC,qBAAqB,EACrBC,yBAAyB,EACzBC,iBAAiB,EACjBC,uBAAuB,EACvBC,gBAAgB,QACX,yBAAyB;AAChC,SAASC,wBAAwB,QAAQ,4BAA4B;AAYrE,OAAO,SAASC,kBAAkB,EAChCC,UAAU,EACVC,WAAW,EACXC,sBAAsB,EACtBC,SAAS,EACTC,gBAAgB,IAAI,EACpBC,YAAY,EACW;IACvB,MAAMC,WAAWT;IACjB,MAAM,EAAEU,MAAM,EAAEC,KAAK,EAAEC,KAAKC,UAAU,EAAE,GAAGrB;IAE3C,MAAMsB,WAAWR,cAAc;IAC/B,IAAI,CAACQ,YAAYL,aAAa,MAAM,OAAO;IAE3C,MAAMM,mBAAmBX,cAAcY,SAASC,aAAa,CAACb,eAAec;IAC7E,MAAMC,YAAYJ,mBAAmBA,iBAAiBK,qBAAqB,GAAGV,MAAM,GAAGQ;IACvF,MAAMG,YAAY5B,kBAAkBgB,UAAUH,WAAWI,UAAU,GAAGC,SAAS,GAAGI;IAElF,MAAMO,QAAQ5B,eAAeS,WAAWmB,KAAK,EAAEjB;IAC/C,MAAMkB,MAAMpB,WAAWoB,GAAG,KAAKL,YAAYxB,eAAeS,WAAWoB,GAAG,EAAElB,0BAA0B;IAEpG,qBACE,KAAClB;QAAOqC,WAAWT;kBACjB,cAAA,KAAC9B;YAAI2B,KAAKC;YAAYY,IAAI,CAACC,QAAU/B,iBAAiB+B,OAAOpB,WAAWa;YAAYQ,OAAO;gBAAEN;YAAU;sBACrG,cAAA,MAACjC;gBAAMwC,SAAS;;kCACd,MAAC3C;wBACCwC,IAAI,CAACC,QAAW,CAAA;gCACdf,OAAO;gCACPkB,UAAU/B;gCACVgC,SAASJ,MAAME,OAAO,CAAC,KAAK,GAAG,KAAK;gCACpCG,iBAAiBL,MAAMM,OAAO,CAACC,YAAY,EAAEC,IAAI,CAAC,IAAI,IAAIrC;gCAC1DsC,UAAU;gCACVC,KAAK;gCACLC,MAAM;4BACR,CAAA;;0CAEA,MAACpD;gCAAIwC,IAAI;oCAAEa,SAAS;oCAAQC,YAAY;oCAAUC,eAAe;oCAAK7B,OAAO;gCAAO;;kDAClF,KAAC1B;wCACCwC,IAAI;4CACFd,OAAO;4CACPD,QAAQ;4CACR+B,cAAc;4CACdV,iBAAiB5B,WAAWuC,KAAK,IAAIzC;4CACrC0C,aAAa;4CACbC,YAAY;wCACd;;kDAEF,MAAC3D;wCAAIwC,IAAI;4CAAEoB,UAAU;4CAAGC,UAAU;wCAAE;;0DAClC,MAACzD;gDAAW0D,SAAQ;;oDACjBzB,MAAM0B,aAAa;oDAAC;kEAAG,KAACC;kEAAQ3B,MAAM4B,aAAa;;;;4CAErD3B,qBACC;;kEACE,KAAClC;wDAAW0D,SAAQ;kEAAW;;kEAC/B,MAAC1D;wDAAW0D,SAAQ;;4DACjBxB,IAAIyB,aAAa;4DAAC;0EAAG,KAACC;0EAAQ1B,IAAI2B,aAAa;;;;;;;;oCAKvD3C,+BACC,MAACnB;wCAAM+D,WAAU;wCAAMZ,YAAW;wCAASd,IAAI;4CAAE2B,YAAY;4CAAGR,YAAY;wCAAE;;0DAC5E,KAACvD;gDAAWoC,IAAI;oDAAEkB,aAAa;oDAAKU,UAAU;oDAAIC,eAAe;gDAAS;0DACvExC,WAAWf,0BAA0BH;;4CAEvCkB,yBACC,KAACxB;gDACCiE,SAAS;oDACP,IAAI/C,iBAAiBU,WAAWV;gDAClC;gDACAiB,IAAI;oDAAE4B,UAAU;oDAAIG,QAAQ;gDAAU;+DAGxC,KAACjE;gDAAWkC,IAAI;oDAAE4B,UAAU;gDAAG;;;;;;0CAKvC,KAACnE;gCAAQuC,IAAI,CAACC,QAAW,CAAA;wCAAEf,OAAO;wCAAQ8C,aAAa/B,MAAMM,OAAO,CAACE,IAAI,CAAC,MAAM;oCAAC,CAAA;;;;kCAEnF,MAACjD;wBAAIwC,IAAI,CAACC,QAAW,CAAA;gCAAEI,SAASJ,MAAME,OAAO,CAAC,KAAK,GAAG,KAAK;4BAAG,CAAA;;4BAC3DzB,WAAWuD,KAAK,kBACf,KAACrE;gCAAW0D,SAAQ;gCAAUtB,IAAI;oCAAEa,SAAS;oCAASqB,YAAY;oCAAQC,cAAc;gCAAI;0CACzFzD,WAAWuD,KAAK;;4BAGpBvD,WAAW0D,MAAM,kBAChB,KAACxE;gCAAW0D,SAAQ;gCAAUtB,IAAI;oCAAEa,SAAS;oCAASsB,cAAc;gCAAI;0CACrEzD,WAAW0D,MAAM;;4BAGrB1D,WAAW2D,IAAI,IAAIC,OAAOC,IAAI,CAAC7D,WAAW2D,IAAI,EAAEG,MAAM,GAAG,mBACxD,KAAChF;gCAAIwC,IAAI;oCAAEa,SAAS;oCAAQ4B,UAAU;oCAAQC,KAAK;oCAAKC,WAAW;gCAAI;0CACpEL,OAAOM,OAAO,CAAClE,WAAW2D,IAAI,EAAEQ,GAAG,CAAC,CAAC,CAACC,KAAKC,MAAM,iBAChD,MAACvF;wCAECwC,IAAI,CAACC,QAAW,CAAA;gDACdK,iBAAiBL,MAAMM,OAAO,CAACE,IAAI,CAAC,MAAM;gDAC1CO,cAAc;gDACdX,SAAS;gDACTuB,UAAU;gDACVoB,YAAY;4CACd,CAAA;;4CAECF;4CAAI;4CAAGC;;uCATHD;;;;;;;;AAmBzB;AAEA;;;CAGC,GACD,OAAO,SAASG,sBAAsBC,WAA+C;IACnF,IAAI,CAACA,aAAaV,QAAQ,OAAO,EAAE;IAEnC,MAAMW,eACJ,EAAE;IACJ,MAAMC,eAKD,EAAE;IACP,8DAA8D;IAC9D,MAAMC,gBAAuB,EAAE;IAE/BH,YAAYI,OAAO,CAAC,CAAC5E,YAAY6E;QAC/B,MAAMtC,QAAQvC,WAAWuC,KAAK,IAAIzC;QAClC,MAAMgF,UAAU;QAEhB,IAAI9E,WAAWoB,GAAG,KAAKL,WAAW;YAChC,+EAA+E;YAC/E2D,aAAaK,IAAI,CAAC;gBAChBC,OAAOhF,WAAWmB,KAAK;gBACvB8D,WAAW;oBAAE1C;oBAAO/B,OAAO;oBAAG0E,MAAM;gBAAkB;gBACtDC,OAAO;oBAAEC,MAAM;gBAAM;gBACrBC,iBAAiBR;YACnB;YAEAH,aAAaK,IAAI,CAAC;gBAChBC,OAAOhF,WAAWoB,GAAG;gBACrB6D,WAAW;oBAAE1C;oBAAO/B,OAAO;oBAAG0E,MAAM;gBAAkB;gBACtDC,OAAO;oBAAEC,MAAM;gBAAM;gBACrBC,iBAAiBR;YACnB;YAEAJ,aAAaM,IAAI,CAAC;gBAChB;oBACEC,OAAOhF,WAAWmB,KAAK;oBACvBmE,WAAW;wBAAE/C;wBAAOuC;oBAAQ;gBAC9B;gBACA;oBAAEE,OAAOhF,WAAWoB,GAAG;gBAAC;aACzB;YAED,4BAA4B;YAC5B,KAAK,MAAMmE,WAAW;gBAAC;gBAAM;aAAM,CAAE;gBACnCZ,cAAcI,IAAI,CAAC;oBACjBS,OAAO;wBAACD,UAAUvF,WAAWmB,KAAK,GAAGnB,WAAWoB,GAAG;wBAAE;qBAAE;oBACvDqE,QAAQ;oBACRC,YAAY;wBAAC;wBAAI;qBAAG;oBACpBC,cAAc;oBACdC,cAAc;wBAAC;wBAAG;qBAAE;oBACpBN,WAAW;wBAAE/C;oBAAM;oBACnB8C,iBAAiBR;oBACjBgB,UAAU;wBACRC,UAAU;oBACZ;oBACAP,SAASA;oBACTQ,OAAO,CAACR;gBACV;YACF;QACF,OAAO;YACL,2DAA2D;YAC3Db,aAAaK,IAAI,CAAC;gBAChBC,OAAOhF,WAAWmB,KAAK;gBACvB8D,WAAW;oBAAE1C;oBAAO/B,OAAO;oBAAG0E,MAAM;gBAAkB;gBACtDC,OAAO;oBAAEC,MAAM;gBAAM;gBACrBC,iBAAiBR;YACnB;YAEA,mBAAmB;YACnBF,cAAcI,IAAI,CAAC;gBACjBS,OAAO;oBAACxF,WAAWmB,KAAK;oBAAE;iBAAE;gBAC5BsE,QAAQ;gBACRC,YAAY;oBAAC;oBAAI;iBAAG;gBACpBC,cAAc;gBACdC,cAAc;oBAAC;oBAAG;iBAAO;gBACzBN,WAAW;oBAAE/C;gBAAM;gBACnB8C,iBAAiBR;gBACjBmB,SAAS;YACX;QACF;IACF;IAEA,MAAMC,SAA2B;QAC/Bf,MAAM;QACNgB,MAAM,EAAE;QACRC,QAAQ;QACRC,UACE3B,aAAaX,MAAM,GAAG,IAClB;YACEqC,QAAQ;YACRD,MAAMzB;YACNU,OAAO;gBACLC,MAAM;YACR;QACF,IACArE;QACNsF,UACE3B,aAAaZ,MAAM,GAAG,IAClB;YACEqC,QAAQ;YACRV,QAAQ;gBAAC;gBAAQ;aAAO;YACxBS,MAAMxB;YACNO,WAAW;gBACTC,MAAM;YACR;QACF,IACAnE;QACNuF,WACE3B,cAAcb,MAAM,GAAG,IACnB;YACEqC,QAAQ;YACRD,MAAMvB;YACNQ,OAAO;gBACLC,MAAM;YACR;QACF,IACArE;IACR;IAEA,OAAO;QAACkF;KAAO;AACjB"}
@@ -225,12 +225,41 @@ function QuerySettingsEditor(props) {
225
225
  qs.format = undefined;
226
226
  });
227
227
  };
228
+ const addStack = (i)=>{
229
+ updateQuerySettings(i, (qs)=>{
230
+ qs.stack = false;
231
+ });
232
+ };
233
+ const removeStack = (i)=>{
234
+ updateQuerySettings(i, (qs)=>{
235
+ qs.stack = undefined;
236
+ });
237
+ };
238
+ const handleStackChange = (i, checked)=>{
239
+ updateQuerySettings(i, (qs)=>{
240
+ qs.stack = checked;
241
+ });
242
+ };
228
243
  const handleFormatChange = (i, format)=>{
229
244
  updateQuerySettings(i, (qs)=>{
230
245
  qs.format = format;
231
246
  });
232
247
  };
233
- const queryCount = (0, _pluginsystem.useQueryCountContext)();
248
+ const addNegativeY = (i)=>{
249
+ updateQuerySettings(i, (qs)=>{
250
+ qs.negativeY = true;
251
+ });
252
+ };
253
+ const removeNegativeY = (i)=>{
254
+ updateQuerySettings(i, (qs)=>{
255
+ qs.negativeY = undefined;
256
+ });
257
+ };
258
+ const { queryDefinitions } = (0, _pluginsystem.useDataQueriesContext)();
259
+ const queryCount = queryDefinitions.length;
260
+ const queryNames = (0, _react.useMemo)(()=>(0, _pluginsystem.generateQueryNames)(queryDefinitions), [
261
+ queryDefinitions
262
+ ]);
234
263
  // Compute the list of query indexes for which query settings are not already defined.
235
264
  // This is to avoid already-booked indexes to still be selectable in the dropdown(s)
236
265
  const availableQueryIndexes = (0, _react.useMemo)(()=>{
@@ -269,10 +298,11 @@ function QuerySettingsEditor(props) {
269
298
  mb: 2,
270
299
  fontStyle: "italic",
271
300
  children: "No query defined"
272
- }) : querySettingsList?.length && querySettingsList.map((querySettings, i)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(QuerySettingsInput, {
301
+ }) : querySettingsList?.map((querySettings, i)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(QuerySettingsInput, {
273
302
  inputRef: i === querySettingsList.length - 1 ? recentlyAddedInputRef : undefined,
274
303
  querySettings: querySettings,
275
304
  availableQueryIndexes: availableQueryIndexes,
305
+ queryNames: queryNames,
276
306
  onQueryIndexChange: (e)=>{
277
307
  handleQueryIndexChange(e, i);
278
308
  },
@@ -291,7 +321,12 @@ function QuerySettingsEditor(props) {
291
321
  onRemoveAreaOpacity: ()=>removeAreaOpacity(i),
292
322
  onAddFormat: ()=>addFormat(i),
293
323
  onRemoveFormat: ()=>removeFormat(i),
294
- onFormatChange: (format)=>handleFormatChange(i, format)
324
+ onFormatChange: (format)=>handleFormatChange(i, format),
325
+ onAddNegativeY: ()=>addNegativeY(i),
326
+ onRemoveNegativeY: ()=>removeNegativeY(i),
327
+ onAddStack: ()=>addStack(i),
328
+ onRemoveStack: ()=>removeStack(i),
329
+ onStackChange: (checked)=>handleStackChange(i, checked)
295
330
  }, i)),
296
331
  queryCount > 0 && firstAvailableQueryIndex !== NO_INDEX_AVAILABLE && /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Button, {
297
332
  variant: "contained",
@@ -305,7 +340,7 @@ function QuerySettingsEditor(props) {
305
340
  ]
306
341
  });
307
342
  }
308
- function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue, lineStyle, areaOpacity, format }, availableQueryIndexes, onQueryIndexChange, onColorModeChange, onColorValueChange, onLineStyleChange, onAreaOpacityChange, onDelete, inputRef, onAddColor: onAddColor, onRemoveColor: onRemoveColor, onAddLineStyle, onRemoveLineStyle, onAddAreaOpacity, onRemoveAreaOpacity, onAddFormat, onRemoveFormat, onFormatChange }) {
343
+ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue, lineStyle, areaOpacity, format, negativeY, stack }, availableQueryIndexes, queryNames, onQueryIndexChange, onColorModeChange, onColorValueChange, onLineStyleChange, onAreaOpacityChange, onDelete, inputRef, onAddColor: onAddColor, onRemoveColor: onRemoveColor, onAddLineStyle, onRemoveLineStyle, onAddAreaOpacity, onRemoveAreaOpacity, onAddFormat, onRemoveFormat, onFormatChange, onAddNegativeY, onRemoveNegativeY, onAddStack, onRemoveStack, onStackChange }) {
309
344
  // current query index should also be selectable
310
345
  const selectableQueryIndexes = availableQueryIndexes.concat(queryIndex).sort((a, b)=>a - b);
311
346
  // State for dropdown menu
@@ -333,16 +368,30 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
333
368
  label: 'Format',
334
369
  action: onAddFormat
335
370
  });
371
+ if (!negativeY) options.push({
372
+ key: 'negativeY',
373
+ label: 'Negative Y',
374
+ action: onAddNegativeY
375
+ });
376
+ if (stack === undefined) options.push({
377
+ key: 'stack',
378
+ label: 'Stack',
379
+ action: onAddStack
380
+ });
336
381
  return options;
337
382
  }, [
338
383
  colorMode,
339
384
  lineStyle,
340
385
  areaOpacity,
341
386
  format,
387
+ negativeY,
388
+ stack,
342
389
  onAddColor,
343
390
  onAddLineStyle,
344
391
  onAddAreaOpacity,
345
- onAddFormat
392
+ onAddFormat,
393
+ onAddNegativeY,
394
+ onAddStack
346
395
  ]);
347
396
  const handleAddMenuClick = (event)=>{
348
397
  if (availableOptions.length === 1 && availableOptions[0]) {
@@ -389,12 +438,9 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
389
438
  sx: {
390
439
  minWidth: '75px'
391
440
  },
392
- children: selectableQueryIndexes.map((qi)=>/*#__PURE__*/ (0, _jsxruntime.jsxs)(_material.MenuItem, {
441
+ children: selectableQueryIndexes.map((qi)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(_material.MenuItem, {
393
442
  value: qi,
394
- children: [
395
- "#",
396
- qi + 1
397
- ]
443
+ children: queryNames[qi] ?? `#${qi + 1}`
398
444
  }, `query-${qi}`))
399
445
  }),
400
446
  colorMode && /*#__PURE__*/ (0, _jsxruntime.jsxs)(SettingsSection, {
@@ -421,7 +467,7 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
421
467
  ]
422
468
  }),
423
469
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_components.OptionsColorPicker, {
424
- label: `Query n°${queryIndex + 1}`,
470
+ label: queryNames[queryIndex] ?? `Query n°${queryIndex + 1}`,
425
471
  color: colorValue || DEFAULT_COLOR_VALUE,
426
472
  onColorChange: onColorValueChange
427
473
  })
@@ -489,6 +535,39 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
489
535
  })
490
536
  })
491
537
  }),
538
+ negativeY && /*#__PURE__*/ (0, _jsxruntime.jsx)(SettingsSection, {
539
+ label: "Negative Y",
540
+ onRemove: onRemoveNegativeY,
541
+ children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Typography, {
542
+ variant: "body2",
543
+ color: "text.secondary",
544
+ sx: {
545
+ flexGrow: 1,
546
+ py: 0.5
547
+ },
548
+ children: "Series rendered below the X axis"
549
+ })
550
+ }),
551
+ stack !== undefined && /*#__PURE__*/ (0, _jsxruntime.jsxs)(SettingsSection, {
552
+ label: "Stack",
553
+ onRemove: onRemoveStack,
554
+ children: [
555
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Switch, {
556
+ checked: stack,
557
+ onChange: (e)=>onStackChange(e.target.checked),
558
+ slotProps: {
559
+ input: {
560
+ 'aria-label': 'stack override'
561
+ }
562
+ }
563
+ }),
564
+ /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.Box, {
565
+ sx: {
566
+ flexGrow: 1
567
+ }
568
+ })
569
+ ]
570
+ }),
492
571
  availableOptions.length > 0 && /*#__PURE__*/ (0, _jsxruntime.jsxs)(_jsxruntime.Fragment, {
493
572
  children: [
494
573
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.IconButton, {
@@ -531,7 +610,7 @@ function QuerySettingsInput({ querySettings: { queryIndex, colorMode, colorValue
531
610
  justifyContent: 'center'
532
611
  },
533
612
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_material.IconButton, {
534
- "aria-label": `delete settings for query n°${queryIndex + 1}`,
613
+ "aria-label": `delete settings for ${queryNames[queryIndex] ?? `query n°${queryIndex + 1}`}`,
535
614
  onClick: onDelete,
536
615
  children: /*#__PURE__*/ (0, _jsxruntime.jsx)(_DeleteOutline.default, {})
537
616
  })
@@ -31,6 +31,7 @@ const _components = require("echarts/components");
31
31
  const _renderers = require("echarts/renderers");
32
32
  const _components1 = require("@perses-dev/components");
33
33
  const _timezoneformatter = require("./utils/timezone-formatter");
34
+ const _AnnotationTooltip = require("./annotations/AnnotationTooltip");
34
35
  function _interop_require_default(obj) {
35
36
  return obj && obj.__esModule ? obj : {
36
37
  default: obj
@@ -50,7 +51,7 @@ function _interop_require_default(obj) {
50
51
  _components.TooltipComponent,
51
52
  _renderers.CanvasRenderer
52
53
  ]);
53
- const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeChart({ height, data, seriesMapping, timeScale: timeScaleProp, yAxis, format, seriesFormatMap, grid, isStackedBar = false, tooltipConfig = _components1.DEFAULT_TOOLTIP_CONFIG, noDataVariant = 'message', syncGroup, onDataZoom, onDoubleClick, __experimentalEChartsOptionsOverride }, ref) {
54
+ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeChart({ height, data, seriesMapping, annotations, timeScale: timeScaleProp, yAxis, format, seriesFormatMap, grid, isStackedBar = false, tooltipConfig = _components1.DEFAULT_TOOLTIP_CONFIG, noDataVariant = 'message', syncGroup, onDataZoom, onDoubleClick, __experimentalEChartsOptionsOverride }, ref) {
54
55
  const { chartsTheme, enablePinning, enableSyncGrouping, lastTooltipPinnedCoords, setLastTooltipPinnedCoords } = (0, _components1.useChartsContext)();
55
56
  const isPinningEnabled = tooltipConfig.enablePinning && enablePinning;
56
57
  const chartRef = (0, _react.useRef)();
@@ -59,7 +60,10 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
59
60
  const [pinnedCrosshair, setPinnedCrosshair] = (0, _react.useState)(null);
60
61
  const [isDragging, setIsDragging] = (0, _react.useState)(false);
61
62
  const [startX, setStartX] = (0, _react.useState)(0);
62
- const { timeZone } = (0, _components1.useTimeZone)();
63
+ const [hoveredAnnotation, setHoveredAnnotation] = (0, _react.useState)(null);
64
+ const [pinnedAnnotation, setPinnedAnnotation] = (0, _react.useState)(null);
65
+ const [pinnedAnnotationPos, setPinnedAnnotationPos] = (0, _react.useState)(null);
66
+ const { timeZone, formatWithUserTimeZone } = (0, _components1.useTimeZone)();
63
67
  const getTimezoneAwareAxisFormatter = (0, _react.useCallback)((rangeMs)=>(0, _timezoneformatter.createTimezoneAwareAxisFormatter)(rangeMs, timeZone), [
64
68
  timeZone
65
69
  ]);
@@ -130,11 +134,44 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
130
134
  if (chartRef.current !== undefined) {
131
135
  (0, _components1.enableDataZoom)(chartRef.current);
132
136
  }
137
+ },
138
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
139
+ mouseover: (params)=>{
140
+ // Only markPoint (triangles under the X-axis) opens the annotation tooltip.
141
+ // Hovering markLine or anything else keeps the regular TimeSeries tooltip visible
142
+ // and clears any stale hovered annotation (mouseout is sometimes missed by ECharts).
143
+ if (annotations && params.componentType === 'markPoint' && params.data?.annotationIndex !== undefined) {
144
+ const matchedAnnotation = annotations[params.data.annotationIndex] || null;
145
+ if (matchedAnnotation) {
146
+ setHoveredAnnotation(matchedAnnotation);
147
+ return;
148
+ }
149
+ }
150
+ setHoveredAnnotation(null);
151
+ },
152
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
153
+ mouseout: (params)=>{
154
+ if (annotations && params.componentType === 'markPoint' && params.data?.annotationIndex !== undefined && annotations) {
155
+ // Only clear if the mouseout corresponds to the currently hovered annotation, so that
156
+ // a quick move from one markPoint to another isn't cancelled by a late mouseout event.
157
+ const leaving = annotations[params.data.annotationIndex] || null;
158
+ setHoveredAnnotation((current)=>current === leaving ? null : current);
159
+ }
160
+ },
161
+ globalout: ()=>{
162
+ if (annotations) {
163
+ // Cursor left the chart canvas — guarantee the annotation tooltip is dismissed.
164
+ setHoveredAnnotation(null);
165
+ }
133
166
  }
134
167
  };
135
168
  }, [
136
- onDataZoom,
137
- setTooltipPinnedCoords
169
+ annotations,
170
+ onDataZoom
171
+ ]);
172
+ // Generate annotation series for ECharts markArea (range), markLine (point), and markPoint (markers under X-axis)
173
+ const annotationSeries = (0, _react.useMemo)(()=>(0, _AnnotationTooltip.buildAnnotationSeries)(annotations), [
174
+ annotations
138
175
  ]);
139
176
  const { noDataOption } = chartsTheme;
140
177
  const option = (0, _react.useMemo)(()=>{
@@ -165,8 +202,12 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
165
202
  });
166
203
  const updatedSeriesMapping = enablePinning && pinnedCrosshair !== null ? [
167
204
  ...seriesMapping,
168
- pinnedCrosshair
169
- ] : seriesMapping;
205
+ pinnedCrosshair,
206
+ ...annotationSeries
207
+ ] : [
208
+ ...seriesMapping,
209
+ ...annotationSeries
210
+ ];
170
211
  const option = {
171
212
  dataset: dataset,
172
213
  series: updatedSeriesMapping,
@@ -218,6 +259,7 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
218
259
  }, [
219
260
  data,
220
261
  seriesMapping,
262
+ annotationSeries,
221
263
  timeScale,
222
264
  yAxis,
223
265
  format,
@@ -258,6 +300,34 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
258
300
  // e.preventDefault(); // Prevent the default behaviour when right clicked
259
301
  // }}
260
302
  onClick: (e)=>{
303
+ // If clicking while hovering an annotation, toggle the annotation tooltip pin
304
+ // instead of pinning the TimeChartTooltip, so pinned TimeChartTooltip is preserved.
305
+ if (hoveredAnnotation !== null && e.target instanceof HTMLCanvasElement) {
306
+ const pinnedPos = {
307
+ page: {
308
+ x: e.pageX,
309
+ y: e.pageY
310
+ },
311
+ client: {
312
+ x: e.clientX,
313
+ y: e.clientY
314
+ },
315
+ plotCanvas: {
316
+ x: e.nativeEvent.offsetX,
317
+ y: e.nativeEvent.offsetY
318
+ },
319
+ target: e.target
320
+ };
321
+ setPinnedAnnotation((current)=>{
322
+ if (current === hoveredAnnotation) {
323
+ setPinnedAnnotationPos(null);
324
+ return null;
325
+ }
326
+ setPinnedAnnotationPos(pinnedPos);
327
+ return hoveredAnnotation;
328
+ });
329
+ return;
330
+ }
261
331
  // Allows user to opt-in to multi tooltip pinning when Ctrl or Cmd key held down
262
332
  const isControlKeyPressed = e.ctrlKey || e.metaKey;
263
333
  if (isControlKeyPressed) {
@@ -350,6 +420,8 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
350
420
  if (tooltipPinnedCoords === null) {
351
421
  setShowTooltip(false);
352
422
  }
423
+ // Defensive: clear hovered annotation in case ECharts missed a mouseout event.
424
+ setHoveredAnnotation(null);
353
425
  if (chartRef.current !== undefined) {
354
426
  (0, _components1.clearHighlightedSeries)(chartRef.current);
355
427
  }
@@ -372,7 +444,7 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
372
444
  }
373
445
  },
374
446
  children: [
375
- showTooltip === true && option.tooltip?.showContent === false && tooltipConfig.hidden !== true && /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.TimeChartTooltip, {
447
+ showTooltip === true && (tooltipPinnedCoords !== null || hoveredAnnotation === null) && option.tooltip?.showContent === false && tooltipConfig.hidden !== true && /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.TimeChartTooltip, {
376
448
  containerId: chartsTheme.tooltipPortalContainerId,
377
449
  chartRef: chartRef,
378
450
  data: data,
@@ -389,6 +461,17 @@ const TimeSeriesChartBase = /*#__PURE__*/ (0, _react.forwardRef)(function TimeCh
389
461
  setPinnedCrosshair(null);
390
462
  }
391
463
  }),
464
+ (pinnedAnnotation ?? hoveredAnnotation) && /*#__PURE__*/ (0, _jsxruntime.jsx)(_AnnotationTooltip.AnnotationTooltip, {
465
+ annotation: pinnedAnnotation ?? hoveredAnnotation,
466
+ containerId: chartsTheme.tooltipPortalContainerId,
467
+ formatWithUserTimeZone: formatWithUserTimeZone,
468
+ pinnedPos: pinnedAnnotation !== null ? pinnedAnnotationPos : null,
469
+ enablePinning: isPinningEnabled,
470
+ onUnpinClick: ()=>{
471
+ setPinnedAnnotation(null);
472
+ setPinnedAnnotationPos(null);
473
+ }
474
+ }),
392
475
  /*#__PURE__*/ (0, _jsxruntime.jsx)(_components1.EChart, {
393
476
  sx: {
394
477
  width: '100%',
@@ -26,10 +26,12 @@ const _material = require("@mui/material");
26
26
  const _merge = /*#__PURE__*/ _interop_require_default(require("lodash/merge"));
27
27
  const _pluginsystem = require("@perses-dev/plugin-system");
28
28
  const _components = require("@perses-dev/components");
29
+ const _dashboards = require("@perses-dev/dashboards");
29
30
  const _timeserieschartmodel = require("./time-series-chart-model");
30
31
  const _datatransform = require("./utils/data-transform");
31
32
  const _palettegen = require("./utils/palette-gen");
32
33
  const _TimeSeriesChartBase = require("./TimeSeriesChartBase");
34
+ const _annotation = require("./utils/annotation");
33
35
  function _interop_require_default(obj) {
34
36
  return obj && obj.__esModule ? obj : {
35
37
  default: obj
@@ -107,6 +109,10 @@ function TimeSeriesChartPanel(props) {
107
109
  const [selectedLegendItems, setSelectedLegendItems] = (0, _react.useState)('ALL');
108
110
  const [legendSorting, setLegendSorting] = (0, _react.useState)();
109
111
  const { setTimeRange } = (0, _pluginsystem.useTimeRange)();
112
+ const annotationsWithData = (0, _dashboards.useAnnotationsWithData)();
113
+ const annotations = (0, _react.useMemo)(()=>(0, _annotation.convertAnnotationToTimeSeriesAnnotation)(annotationsWithData), [
114
+ annotationsWithData
115
+ ]);
110
116
  // Populate series data based on query results
111
117
  const { timeScale, timeChartData, timeSeriesMapping, legendItems, seriesFormatMap: computedSeriesFormatMap, maxValuesByFormat } = (0, _react.useMemo)(()=>{
112
118
  const timeScale = (0, _datatransform.getCommonTimeScaleForQueries)(queryResults);
@@ -197,9 +203,21 @@ function TimeSeriesChartPanel(props) {
197
203
  }
198
204
  }
199
205
  }
206
+ // When negativeY is set on this query, negate the rendered values so the
207
+ // series renders below the X axis. The original (positive) values are
208
+ // preserved in `timeSeries.values` (used for legend calculations) and in
209
+ // `queryResults` (used for CSV export).
210
+ const baseValues = (0, _components.getTimeSeriesValues)(timeSeries, timeScale);
211
+ const renderedValues = querySettings?.negativeY ? baseValues.map((tuple)=>{
212
+ const [t, v] = tuple;
213
+ return [
214
+ t,
215
+ v === null ? null : -v
216
+ ];
217
+ }) : baseValues;
200
218
  timeChartData.push({
201
219
  name: formattedSeriesName,
202
- values: (0, _components.getTimeSeriesValues)(timeSeries, timeScale)
220
+ values: renderedValues
203
221
  });
204
222
  }
205
223
  if (legend && legendItems) {
@@ -355,8 +373,10 @@ function TimeSeriesChartPanel(props) {
355
373
  end: new Date(event.end)
356
374
  });
357
375
  };
358
- // Used to opt in to ECharts trigger item which show subgroup data accurately
359
- const isStackedBar = visual.display === 'bar' && visual.stack === 'all';
376
+ // Used to opt in to ECharts trigger item which show subgroup data accurately.
377
+ // Derived from the actual series mapping rather than `visual.stack` alone so that
378
+ // bar charts stacked only via per-query overrides also use the right tooltip mode.
379
+ const isStackedBar = visual.display === 'bar' && timeSeriesMapping.some((s)=>s?.type === 'bar' && s.stack === 'all');
360
380
  // Turn on tooltip pinning by default but opt out for stacked bar or if explicitly set in tooltip panel spec
361
381
  let enablePinning = true;
362
382
  if (isStackedBar) {
@@ -414,6 +434,7 @@ function TimeSeriesChartPanel(props) {
414
434
  height: height,
415
435
  data: timeChartData,
416
436
  seriesMapping: timeSeriesMapping,
437
+ annotations: annotations,
417
438
  timeScale: timeScale,
418
439
  yAxis: multipleYAxes ?? echartsYAxis,
419
440
  format: format,