@perses-dev/components 0.54.0-beta.8 → 0.54.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 (67) hide show
  1. package/dist/EChart/index.d.ts +1 -0
  2. package/dist/EChart/index.d.ts.map +1 -1
  3. package/dist/EChart/index.js +1 -0
  4. package/dist/EChart/index.js.map +1 -1
  5. package/dist/EChart/timezone-formatter.d.ts +5 -0
  6. package/dist/EChart/timezone-formatter.d.ts.map +1 -0
  7. package/dist/EChart/timezone-formatter.js +43 -0
  8. package/dist/EChart/timezone-formatter.js.map +1 -0
  9. package/dist/LinksEditor/index.d.ts +0 -1
  10. package/dist/LinksEditor/index.d.ts.map +1 -1
  11. package/dist/LinksEditor/index.js +0 -1
  12. package/dist/LinksEditor/index.js.map +1 -1
  13. package/dist/TimeSeriesTooltip/TimeChartTooltip.d.ts.map +1 -1
  14. package/dist/TimeSeriesTooltip/TimeChartTooltip.js +25 -6
  15. package/dist/TimeSeriesTooltip/TimeChartTooltip.js.map +1 -1
  16. package/dist/TimeSeriesTooltip/TooltipContent.d.ts +1 -1
  17. package/dist/TimeSeriesTooltip/TooltipContent.d.ts.map +1 -1
  18. package/dist/TimeSeriesTooltip/TooltipContent.js.map +1 -1
  19. package/dist/TimeSeriesTooltip/TooltipHeader.d.ts +1 -1
  20. package/dist/TimeSeriesTooltip/TooltipHeader.d.ts.map +1 -1
  21. package/dist/TimeSeriesTooltip/TooltipHeader.js.map +1 -1
  22. package/dist/TimeSeriesTooltip/index.d.ts +1 -0
  23. package/dist/TimeSeriesTooltip/index.d.ts.map +1 -1
  24. package/dist/TimeSeriesTooltip/index.js +1 -0
  25. package/dist/TimeSeriesTooltip/index.js.map +1 -1
  26. package/dist/TimeSeriesTooltip/nearby-series.d.ts +5 -23
  27. package/dist/TimeSeriesTooltip/nearby-series.d.ts.map +1 -1
  28. package/dist/TimeSeriesTooltip/nearby-series.js +218 -153
  29. package/dist/TimeSeriesTooltip/nearby-series.js.map +1 -1
  30. package/dist/TimeSeriesTooltip/tooltip-model.d.ts +1 -1
  31. package/dist/TimeSeriesTooltip/tooltip-model.d.ts.map +1 -1
  32. package/dist/TimeSeriesTooltip/tooltip-model.js.map +1 -1
  33. package/dist/TimeSeriesTooltip/types.d.ts +30 -0
  34. package/dist/TimeSeriesTooltip/types.d.ts.map +1 -0
  35. package/dist/TimeSeriesTooltip/types.js +15 -0
  36. package/dist/TimeSeriesTooltip/types.js.map +1 -0
  37. package/dist/TimeSeriesTooltip/utils.d.ts +26 -1
  38. package/dist/TimeSeriesTooltip/utils.d.ts.map +1 -1
  39. package/dist/TimeSeriesTooltip/utils.js +105 -9
  40. package/dist/TimeSeriesTooltip/utils.js.map +1 -1
  41. package/dist/cjs/EChart/index.js +1 -0
  42. package/dist/cjs/EChart/timezone-formatter.js +49 -0
  43. package/dist/cjs/LinksEditor/index.js +0 -1
  44. package/dist/cjs/TimeSeriesTooltip/TimeChartTooltip.js +24 -5
  45. package/dist/cjs/TimeSeriesTooltip/index.js +1 -0
  46. package/dist/cjs/TimeSeriesTooltip/nearby-series.js +216 -151
  47. package/dist/cjs/TimeSeriesTooltip/types.js +16 -0
  48. package/dist/cjs/TimeSeriesTooltip/utils.js +109 -8
  49. package/dist/cjs/utils/chart-actions.js +12 -4
  50. package/dist/cjs/utils/request-interpolation.js +20 -2
  51. package/dist/cjs/utils/variable-interpolation.js +6 -0
  52. package/dist/utils/chart-actions.d.ts.map +1 -1
  53. package/dist/utils/chart-actions.js +12 -4
  54. package/dist/utils/chart-actions.js.map +1 -1
  55. package/dist/utils/request-interpolation.d.ts.map +1 -1
  56. package/dist/utils/request-interpolation.js +20 -2
  57. package/dist/utils/request-interpolation.js.map +1 -1
  58. package/dist/utils/variable-interpolation.d.ts +3 -1
  59. package/dist/utils/variable-interpolation.d.ts.map +1 -1
  60. package/dist/utils/variable-interpolation.js +6 -0
  61. package/dist/utils/variable-interpolation.js.map +1 -1
  62. package/package.json +3 -4
  63. package/dist/LinksEditor/LinksEditor.d.ts +0 -8
  64. package/dist/LinksEditor/LinksEditor.d.ts.map +0 -1
  65. package/dist/LinksEditor/LinksEditor.js +0 -149
  66. package/dist/LinksEditor/LinksEditor.js.map +0 -1
  67. package/dist/cjs/LinksEditor/LinksEditor.js +0 -162
@@ -56,6 +56,7 @@ var InterpolationFormat = /*#__PURE__*/ function(InterpolationFormat) {
56
56
  InterpolationFormat["SQLSTRING"] = "sqlstring";
57
57
  InterpolationFormat["TEXT"] = "text";
58
58
  InterpolationFormat["QUERYPARAM"] = "queryparam";
59
+ InterpolationFormat["REGEX_LITERAL"] = "regexliteral";
59
60
  return InterpolationFormat;
60
61
  }({});
61
62
  function stringToFormat(val) {
@@ -104,6 +105,11 @@ function interpolate(values, name, format) {
104
105
  const escapedRegex = values.map((v)=>v.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'));
105
106
  return `(${escapedRegex.join('|')})`;
106
107
  }
108
+ case "regexliteral":
109
+ {
110
+ const escapedRegex = values.map((v)=>v.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\\\$&'));
111
+ return `(${escapedRegex.join('|')})`;
112
+ }
107
113
  case "singlequote":
108
114
  return values.map((v)=>`'${v}'`).join(',');
109
115
  case "sqlstring":
@@ -1 +1 @@
1
- {"version":3,"file":"chart-actions.d.ts","sourceRoot":"","sources":["../../src/utils/chart-actions.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAgC,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAE/F,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAa3D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAKzD;AAMD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAYnE;AAOD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,GAAG,IAAI,CAYnH;AAOD,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,eAAe,EACtB,mBAAmB,EAAE,MAAM,EAAE,EAC7B,uBAAuB,EAAE,MAAM,EAAE,EACjC,0BAA0B,EAAE,MAAM,EAAE,EACpC,oBAAoB,EAAE,aAAa,EAAE,EACrC,mBAAmB,EAAE,aAAa,EAAE,GACnC,IAAI,CAiDN;AAKD,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,sBAAsB,GAAG,OAAO,CAGzF;AAMD,wBAAgB,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,oBAAoB,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB9G;AAKD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAapG"}
1
+ {"version":3,"file":"chart-actions.d.ts","sourceRoot":"","sources":["../../src/utils/chart-actions.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,cAAc,CAAC;AAC1D,OAAO,EAAE,UAAU,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AACpE,OAAO,EAAE,aAAa,EAAgC,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAE/F,MAAM,WAAW,aAAa;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,GAAG,EAAE,MAAM,CAAC;CACb;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAa3D;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAKzD;AAMD,wBAAgB,sBAAsB,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAYnE;AAOD,wBAAgB,cAAc,CAAC,YAAY,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,eAAe,GAAG,MAAM,EAAE,GAAG,IAAI,CAYnH;AAOD,wBAAgB,gCAAgC,CAC9C,KAAK,EAAE,eAAe,EACtB,mBAAmB,EAAE,MAAM,EAAE,EAC7B,uBAAuB,EAAE,MAAM,EAAE,EACjC,0BAA0B,EAAE,MAAM,EAAE,EACpC,oBAAoB,EAAE,aAAa,EAAE,EACrC,mBAAmB,EAAE,aAAa,EAAE,GACnC,IAAI,CA0DN;AAKD,wBAAgB,0BAA0B,CAAC,aAAa,EAAE,sBAAsB,GAAG,OAAO,CAGzF;AAMD,wBAAgB,mBAAmB,CAAC,gBAAgB,CAAC,EAAE,oBAAoB,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAgB9G;AAKD,wBAAgB,gCAAgC,CAAC,IAAI,EAAE,UAAU,EAAE,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAapG"}
@@ -88,12 +88,20 @@ import { PINNED_CROSSHAIR_SERIES_NAME } from '../model';
88
88
  escapeConnect: true
89
89
  });
90
90
  }
91
- // Clears emphasis state of all lines that are not emphasized.
92
- // Emphasized is a subset of just the nearby series that are closest to cursor.
91
+ // Blanket downplay clears axis-triggered emphasis (enlarged "big point" markers) before
92
+ // re-applying emphasis to only the winner series.
93
+ // https://echarts.apache.org/en/api.html#action.downplay
93
94
  chart.dispatchAction({
94
- type: 'downplay',
95
- seriesIndex: nonEmphasizedSeriesIndexes
95
+ type: 'downplay'
96
96
  });
97
+ // Clears emphasis state of all lines that are not emphasized.
98
+ // Emphasized is a subset of just the nearby series that are closest to cursor.
99
+ if (nonEmphasizedSeriesIndexes.length > 0) {
100
+ chart.dispatchAction({
101
+ type: 'downplay',
102
+ seriesIndex: nonEmphasizedSeriesIndexes
103
+ });
104
+ }
97
105
  // https://echarts.apache.org/en/api.html#action.highlight
98
106
  if (emphasizedSeriesIndexes.length > 0) {
99
107
  // Fadeout opacity of all series not closest to cursor.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/chart-actions.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 { ECharts as EChartsInstance } from 'echarts/core';\nimport { TimeSeries, TimeSeriesValueTuple } from '@perses-dev/spec';\nimport { DatapointInfo, PINNED_CROSSHAIR_SERIES_NAME, TimeChartSeriesMapping } from '../model';\n\nexport interface ZoomEventData {\n start: number;\n end: number;\n}\n\n/**\n * Enable dataZoom without requring user to click toolbox icon.\n * https://stackoverflow.com/questions/57183297/is-there-a-way-to-use-zoom-of-type-select-without-showing-the-toolbar\n */\nexport function enableDataZoom(chart: EChartsInstance): void {\n const chartModel = chart['_model'];\n if (chartModel === undefined) return;\n if (chartModel.option.toolbox !== undefined && chartModel.option.toolbox.length > 0) {\n // check if hidden data zoom icon is unselected (if selected it would be 'emphasis' instead of 'normal')\n if (chartModel.option.toolbox[0].feature.dataZoom.iconStatus.zoom === 'normal') {\n chart.dispatchAction({\n type: 'takeGlobalCursor',\n key: 'dataZoomSelect',\n dataZoomSelectActive: true,\n });\n }\n }\n}\n\n/**\n * Restore chart to original state before zoom or other actions were dispatched\n */\nexport function restoreChart(chart: EChartsInstance): void {\n // TODO: support incremental unzoom instead of restore to original state\n chart.dispatchAction({\n type: 'restore', // https://echarts.apache.org/en/api.html#events.restore\n });\n}\n\n/*\n * Clear all highlighted series when cursor exits canvas\n * https://echarts.apache.org/en/api.html#action.downplay\n */\nexport function clearHighlightedSeries(chart: EChartsInstance): void {\n if (chart.dispatchAction !== undefined) {\n // Clear any selected data points\n chart.dispatchAction({\n type: 'unselect',\n });\n\n // Clear any highlighted series\n chart.dispatchAction({\n type: 'downplay',\n });\n }\n}\n\n/*\n * Convert a point from pixel coordinate to logical coordinate.\n * Used to determine if cursor is over chart canvas and closest datapoint.\n * https://echarts.apache.org/en/api.html#echartsInstance.convertFromPixel\n */\nexport function getPointInGrid(cursorCoordX: number, cursorCoordY: number, chart?: EChartsInstance): number[] | null {\n if (chart === undefined) {\n return null;\n }\n\n const pointInPixel = [cursorCoordX, cursorCoordY];\n if (!chart.containPixel('grid', pointInPixel)) {\n return null;\n }\n\n const pointInGrid: number[] = chart.convertFromPixel('grid', pointInPixel);\n return pointInGrid;\n}\n\n/*\n * TimeSeriesChart tooltip is built custom to support finding nearby series instead of single or all series.\n * This means ECharts actions need to be dispatched manually for series highlighting, datapoint select state, etc.\n * More info: https://echarts.apache.org/en/api.html#action\n */\nexport function batchDispatchNearbySeriesActions(\n chart: EChartsInstance,\n nearbySeriesIndexes: number[],\n emphasizedSeriesIndexes: number[],\n nonEmphasizedSeriesIndexes: number[],\n emphasizedDatapoints: DatapointInfo[],\n duplicateDatapoints: DatapointInfo[]\n): void {\n // Accounts for multiple series that are rendered direct on top of eachother.\n // Only applies select state to the datapoint that is visible to avoid color mismatch.\n const lastEmphasizedDatapoint =\n duplicateDatapoints.length > 0\n ? duplicateDatapoints[duplicateDatapoints.length - 1]\n : emphasizedDatapoints[emphasizedDatapoints.length - 1];\n if (lastEmphasizedDatapoint !== undefined) {\n // Corresponds to select options inside getTimeSeries util.\n // https://echarts.apache.org/en/option.html#series-line.select.itemStyle\n chart.dispatchAction({\n type: 'select',\n seriesIndex: lastEmphasizedDatapoint.seriesIndex,\n dataIndex: lastEmphasizedDatapoint.dataIndex,\n // Shared crosshair should not emphasize datapoints on adjacent charts.\n escapeConnect: true, // TODO: try to remove escapeConnect and match by seriesName for cross panel correlation\n });\n }\n\n // Clears emphasis state of all lines that are not emphasized.\n // Emphasized is a subset of just the nearby series that are closest to cursor.\n chart.dispatchAction({\n type: 'downplay',\n seriesIndex: nonEmphasizedSeriesIndexes,\n });\n\n // https://echarts.apache.org/en/api.html#action.highlight\n if (emphasizedSeriesIndexes.length > 0) {\n // Fadeout opacity of all series not closest to cursor.\n chart.dispatchAction({\n type: 'highlight',\n seriesIndex: emphasizedSeriesIndexes,\n notBlur: false, // ensure blur IS triggered, this is default but setting so it is explicit\n escapeConnect: true, // shared crosshair should not emphasize series on adjacent charts\n });\n } else {\n // When no emphasized series with bold text, notBlur allows opacity fadeout to not trigger.\n chart.dispatchAction({\n type: 'highlight',\n seriesIndex: nearbySeriesIndexes,\n notBlur: true, // do not trigger blur state when cursor is not immediately close to any series\n escapeConnect: true, // shared crosshair should not emphasize series on adjacent charts\n });\n\n // Clears selected datapoints since no bold series in tooltip, restore does not impact highlighting\n chart.dispatchAction({\n type: 'toggleSelect', // https://echarts.apache.org/en/api.html#action.toggleSelect\n });\n }\n}\n\n/*\n * Determine whether a markLine was pushed into the final series, which means crosshair was already pinned onClick\n */\nexport function checkCrosshairPinnedStatus(seriesMapping: TimeChartSeriesMapping): boolean {\n const isCrosshairPinned = seriesMapping[seriesMapping.length - 1]?.name === PINNED_CROSSHAIR_SERIES_NAME;\n return isCrosshairPinned;\n}\n\n/*\n * Find closest timestamp to logical x coordinate returned from echartsInstance.convertFromPixel\n * Used to find nearby series in time series tooltip.\n */\nexport function getClosestTimestamp(timeSeriesValues?: TimeSeriesValueTuple[], cursorX?: number): number | null {\n if (timeSeriesValues === undefined || cursorX === undefined) {\n return null;\n }\n\n let currentClosestTimestamp: number | null = null;\n let currentClosestDistance = Infinity;\n\n for (const [timestamp] of timeSeriesValues) {\n const distance = Math.abs(timestamp - cursorX);\n if (distance < currentClosestDistance) {\n currentClosestTimestamp = timestamp;\n currentClosestDistance = distance;\n }\n }\n return currentClosestTimestamp;\n}\n\n/*\n * Find closest timestamp in full dataset, used to snap crosshair into place onClick when tooltip is pinned.\n */\nexport function getClosestTimestampInFullDataset(data: TimeSeries[], cursorX?: number): number | null {\n if (cursorX === undefined) {\n return null;\n }\n const totalSeries = data.length;\n let closestTimestamp = null;\n for (let seriesIdx = 0; seriesIdx < totalSeries; seriesIdx++) {\n const currentDataset = totalSeries > 0 ? data[seriesIdx] : null;\n if (!currentDataset) break;\n const currentValues: TimeSeriesValueTuple[] = currentDataset.values;\n closestTimestamp = getClosestTimestamp(currentValues, cursorX);\n }\n return closestTimestamp;\n}\n"],"names":["PINNED_CROSSHAIR_SERIES_NAME","enableDataZoom","chart","chartModel","undefined","option","toolbox","length","feature","dataZoom","iconStatus","zoom","dispatchAction","type","key","dataZoomSelectActive","restoreChart","clearHighlightedSeries","getPointInGrid","cursorCoordX","cursorCoordY","pointInPixel","containPixel","pointInGrid","convertFromPixel","batchDispatchNearbySeriesActions","nearbySeriesIndexes","emphasizedSeriesIndexes","nonEmphasizedSeriesIndexes","emphasizedDatapoints","duplicateDatapoints","lastEmphasizedDatapoint","seriesIndex","dataIndex","escapeConnect","notBlur","checkCrosshairPinnedStatus","seriesMapping","isCrosshairPinned","name","getClosestTimestamp","timeSeriesValues","cursorX","currentClosestTimestamp","currentClosestDistance","Infinity","timestamp","distance","Math","abs","getClosestTimestampInFullDataset","data","totalSeries","closestTimestamp","seriesIdx","currentDataset","currentValues","values"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utils/chart-actions.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 { ECharts as EChartsInstance } from 'echarts/core';\nimport { TimeSeries, TimeSeriesValueTuple } from '@perses-dev/spec';\nimport { DatapointInfo, PINNED_CROSSHAIR_SERIES_NAME, TimeChartSeriesMapping } from '../model';\n\nexport interface ZoomEventData {\n start: number;\n end: number;\n}\n\n/**\n * Enable dataZoom without requring user to click toolbox icon.\n * https://stackoverflow.com/questions/57183297/is-there-a-way-to-use-zoom-of-type-select-without-showing-the-toolbar\n */\nexport function enableDataZoom(chart: EChartsInstance): void {\n const chartModel = chart['_model'];\n if (chartModel === undefined) return;\n if (chartModel.option.toolbox !== undefined && chartModel.option.toolbox.length > 0) {\n // check if hidden data zoom icon is unselected (if selected it would be 'emphasis' instead of 'normal')\n if (chartModel.option.toolbox[0].feature.dataZoom.iconStatus.zoom === 'normal') {\n chart.dispatchAction({\n type: 'takeGlobalCursor',\n key: 'dataZoomSelect',\n dataZoomSelectActive: true,\n });\n }\n }\n}\n\n/**\n * Restore chart to original state before zoom or other actions were dispatched\n */\nexport function restoreChart(chart: EChartsInstance): void {\n // TODO: support incremental unzoom instead of restore to original state\n chart.dispatchAction({\n type: 'restore', // https://echarts.apache.org/en/api.html#events.restore\n });\n}\n\n/*\n * Clear all highlighted series when cursor exits canvas\n * https://echarts.apache.org/en/api.html#action.downplay\n */\nexport function clearHighlightedSeries(chart: EChartsInstance): void {\n if (chart.dispatchAction !== undefined) {\n // Clear any selected data points\n chart.dispatchAction({\n type: 'unselect',\n });\n\n // Clear any highlighted series\n chart.dispatchAction({\n type: 'downplay',\n });\n }\n}\n\n/*\n * Convert a point from pixel coordinate to logical coordinate.\n * Used to determine if cursor is over chart canvas and closest datapoint.\n * https://echarts.apache.org/en/api.html#echartsInstance.convertFromPixel\n */\nexport function getPointInGrid(cursorCoordX: number, cursorCoordY: number, chart?: EChartsInstance): number[] | null {\n if (chart === undefined) {\n return null;\n }\n\n const pointInPixel = [cursorCoordX, cursorCoordY];\n if (!chart.containPixel('grid', pointInPixel)) {\n return null;\n }\n\n const pointInGrid: number[] = chart.convertFromPixel('grid', pointInPixel);\n return pointInGrid;\n}\n\n/*\n * TimeSeriesChart tooltip is built custom to support finding nearby series instead of single or all series.\n * This means ECharts actions need to be dispatched manually for series highlighting, datapoint select state, etc.\n * More info: https://echarts.apache.org/en/api.html#action\n */\nexport function batchDispatchNearbySeriesActions(\n chart: EChartsInstance,\n nearbySeriesIndexes: number[],\n emphasizedSeriesIndexes: number[],\n nonEmphasizedSeriesIndexes: number[],\n emphasizedDatapoints: DatapointInfo[],\n duplicateDatapoints: DatapointInfo[]\n): void {\n // Accounts for multiple series that are rendered direct on top of eachother.\n // Only applies select state to the datapoint that is visible to avoid color mismatch.\n const lastEmphasizedDatapoint =\n duplicateDatapoints.length > 0\n ? duplicateDatapoints[duplicateDatapoints.length - 1]\n : emphasizedDatapoints[emphasizedDatapoints.length - 1];\n if (lastEmphasizedDatapoint !== undefined) {\n // Corresponds to select options inside getTimeSeries util.\n // https://echarts.apache.org/en/option.html#series-line.select.itemStyle\n chart.dispatchAction({\n type: 'select',\n seriesIndex: lastEmphasizedDatapoint.seriesIndex,\n dataIndex: lastEmphasizedDatapoint.dataIndex,\n // Shared crosshair should not emphasize datapoints on adjacent charts.\n escapeConnect: true, // TODO: try to remove escapeConnect and match by seriesName for cross panel correlation\n });\n }\n\n // Blanket downplay clears axis-triggered emphasis (enlarged \"big point\" markers) before\n // re-applying emphasis to only the winner series.\n // https://echarts.apache.org/en/api.html#action.downplay\n chart.dispatchAction({\n type: 'downplay',\n });\n\n // Clears emphasis state of all lines that are not emphasized.\n // Emphasized is a subset of just the nearby series that are closest to cursor.\n if (nonEmphasizedSeriesIndexes.length > 0) {\n chart.dispatchAction({\n type: 'downplay',\n seriesIndex: nonEmphasizedSeriesIndexes,\n });\n }\n\n // https://echarts.apache.org/en/api.html#action.highlight\n if (emphasizedSeriesIndexes.length > 0) {\n // Fadeout opacity of all series not closest to cursor.\n chart.dispatchAction({\n type: 'highlight',\n seriesIndex: emphasizedSeriesIndexes,\n notBlur: false, // ensure blur IS triggered, this is default but setting so it is explicit\n escapeConnect: true, // shared crosshair should not emphasize series on adjacent charts\n });\n } else {\n // When no emphasized series with bold text, notBlur allows opacity fadeout to not trigger.\n chart.dispatchAction({\n type: 'highlight',\n seriesIndex: nearbySeriesIndexes,\n notBlur: true, // do not trigger blur state when cursor is not immediately close to any series\n escapeConnect: true, // shared crosshair should not emphasize series on adjacent charts\n });\n\n // Clears selected datapoints since no bold series in tooltip, restore does not impact highlighting\n chart.dispatchAction({\n type: 'toggleSelect', // https://echarts.apache.org/en/api.html#action.toggleSelect\n });\n }\n}\n\n/*\n * Determine whether a markLine was pushed into the final series, which means crosshair was already pinned onClick\n */\nexport function checkCrosshairPinnedStatus(seriesMapping: TimeChartSeriesMapping): boolean {\n const isCrosshairPinned = seriesMapping[seriesMapping.length - 1]?.name === PINNED_CROSSHAIR_SERIES_NAME;\n return isCrosshairPinned;\n}\n\n/*\n * Find closest timestamp to logical x coordinate returned from echartsInstance.convertFromPixel\n * Used to find nearby series in time series tooltip.\n */\nexport function getClosestTimestamp(timeSeriesValues?: TimeSeriesValueTuple[], cursorX?: number): number | null {\n if (timeSeriesValues === undefined || cursorX === undefined) {\n return null;\n }\n\n let currentClosestTimestamp: number | null = null;\n let currentClosestDistance = Infinity;\n\n for (const [timestamp] of timeSeriesValues) {\n const distance = Math.abs(timestamp - cursorX);\n if (distance < currentClosestDistance) {\n currentClosestTimestamp = timestamp;\n currentClosestDistance = distance;\n }\n }\n return currentClosestTimestamp;\n}\n\n/*\n * Find closest timestamp in full dataset, used to snap crosshair into place onClick when tooltip is pinned.\n */\nexport function getClosestTimestampInFullDataset(data: TimeSeries[], cursorX?: number): number | null {\n if (cursorX === undefined) {\n return null;\n }\n const totalSeries = data.length;\n let closestTimestamp = null;\n for (let seriesIdx = 0; seriesIdx < totalSeries; seriesIdx++) {\n const currentDataset = totalSeries > 0 ? data[seriesIdx] : null;\n if (!currentDataset) break;\n const currentValues: TimeSeriesValueTuple[] = currentDataset.values;\n closestTimestamp = getClosestTimestamp(currentValues, cursorX);\n }\n return closestTimestamp;\n}\n"],"names":["PINNED_CROSSHAIR_SERIES_NAME","enableDataZoom","chart","chartModel","undefined","option","toolbox","length","feature","dataZoom","iconStatus","zoom","dispatchAction","type","key","dataZoomSelectActive","restoreChart","clearHighlightedSeries","getPointInGrid","cursorCoordX","cursorCoordY","pointInPixel","containPixel","pointInGrid","convertFromPixel","batchDispatchNearbySeriesActions","nearbySeriesIndexes","emphasizedSeriesIndexes","nonEmphasizedSeriesIndexes","emphasizedDatapoints","duplicateDatapoints","lastEmphasizedDatapoint","seriesIndex","dataIndex","escapeConnect","notBlur","checkCrosshairPinnedStatus","seriesMapping","isCrosshairPinned","name","getClosestTimestamp","timeSeriesValues","cursorX","currentClosestTimestamp","currentClosestDistance","Infinity","timestamp","distance","Math","abs","getClosestTimestampInFullDataset","data","totalSeries","closestTimestamp","seriesIdx","currentDataset","currentValues","values"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"request-interpolation.d.ts","sourceRoot":"","sources":["../../src/utils/request-interpolation.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAEL,gBAAgB,EAGjB,MAAM,0BAA0B,CAAC;AAElC,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AAwCjE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,EAAE,gBAAgB,GAAG,cAAc,CAMnH;AAED,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,gBAAgB,EAC7B,aAAa,EAAE,gBAAgB,GAC9B,gBAAgB,CAUlB"}
1
+ {"version":3,"file":"request-interpolation.d.ts","sourceRoot":"","sources":["../../src/utils/request-interpolation.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACpD,OAAO,EAEL,gBAAgB,EAGjB,MAAM,0BAA0B,CAAC;AAGlC,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC;AA4DjE,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,EAAE,aAAa,EAAE,gBAAgB,GAAG,cAAc,CAMnH;AAED,wBAAgB,sBAAsB,CACpC,WAAW,EAAE,gBAAgB,EAC7B,aAAa,EAAE,gBAAgB,GAC9B,gBAAgB,CAUlB"}
@@ -11,6 +11,7 @@
11
11
  // See the License for the specific language governing permissions and
12
12
  // limitations under the License.
13
13
  import { replaceVariables, parseVariablesAndFormat, InterpolationFormat } from './variable-interpolation';
14
+ import { createRegexFromString } from './regexp';
14
15
  function expandQueryParamValue(value, variableState) {
15
16
  // If value is an array, process each element
16
17
  if (Array.isArray(value)) {
@@ -22,13 +23,30 @@ function expandQueryParamValue(value, variableState) {
22
23
  // Find the first multi-value variable that should expand to repeated keys
23
24
  for (const [varName, format] of variablesMap){
24
25
  const varState = variableState[varName];
25
- if (!varState || !Array.isArray(varState.value)) continue;
26
+ if (!varState) continue;
27
+ const options = varState.options ?? [];
28
+ const allOptionValues = options.map((o)=>o.value);
29
+ const isArray = Array.isArray(varState.value);
30
+ const isCustomAllValue = varState.customAllValue !== undefined;
31
+ let expandValues;
32
+ if (isArray) {
33
+ expandValues = varState.value;
34
+ } else if (isCustomAllValue) {
35
+ try {
36
+ const regex = createRegexFromString(varState.customAllValue);
37
+ const matched = options.filter((o)=>regex.test(o.value)).map((o)=>o.value);
38
+ expandValues = matched.length > 0 ? matched : allOptionValues;
39
+ } catch {
40
+ expandValues = allOptionValues;
41
+ }
42
+ }
43
+ if (!expandValues) continue;
26
44
  // If format is queryparam or not specified (default for query params), expand to array
27
45
  if (!format || format === InterpolationFormat.QUERYPARAM) {
28
46
  // Build syntax patterns for this variable reference
29
47
  const simpleSyntax = '$' + varName;
30
48
  const bracketSyntax = format ? '${' + varName + ':' + format + '}' : '${' + varName + '}';
31
- return varState.value.map((singleValue)=>{
49
+ return expandValues.map((singleValue)=>{
32
50
  let text = valueString;
33
51
  // Replace the variable reference with the raw value directly.
34
52
  // We cannot use replaceVariables here because it would apply QUERYPARAM format
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/request-interpolation.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 { RequestHeaders } from '@perses-dev/client';\nimport {\n replaceVariables,\n VariableStateMap,\n parseVariablesAndFormat,\n InterpolationFormat,\n} from './variable-interpolation';\n\nexport type QueryParamValues = Record<string, string | string[]>;\n\nfunction expandQueryParamValue(value: string | string[], variableState: VariableStateMap): string | string[] {\n // If value is an array, process each element\n if (Array.isArray(value)) {\n return value.map((v) => expandQueryParamValue(v, variableState) as string);\n }\n\n // Now we know value is a string, so we can safely use string methods\n const valueString = value as string;\n const variablesMap = parseVariablesAndFormat(valueString);\n\n // Find the first multi-value variable that should expand to repeated keys\n for (const [varName, format] of variablesMap) {\n const varState = variableState[varName];\n if (!varState || !Array.isArray(varState.value)) continue;\n\n // If format is queryparam or not specified (default for query params), expand to array\n if (!format || format === InterpolationFormat.QUERYPARAM) {\n // Build syntax patterns for this variable reference\n const simpleSyntax = '$' + varName;\n const bracketSyntax = format ? '${' + varName + ':' + format + '}' : '${' + varName + '}';\n\n return varState.value.map((singleValue) => {\n let text = valueString;\n // Replace the variable reference with the raw value directly.\n // We cannot use replaceVariables here because it would apply QUERYPARAM format\n // to the single value, producing \"varName=val\" instead of just \"val\".\n text = text.replaceAll(bracketSyntax, singleValue);\n text = text.replaceAll(simpleSyntax, singleValue);\n // Replace any remaining variables (other than the expanded one) using standard interpolation\n return replaceVariables(text, variableState);\n });\n }\n }\n\n // No multi-value expansion needed — use standard interpolation\n return replaceVariables(valueString, variableState);\n}\n\nexport function interpolateHeaders(headers: Record<string, string>, variableState: VariableStateMap): RequestHeaders {\n const result: RequestHeaders = {};\n for (const [key, value] of Object.entries(headers)) {\n result[key] = replaceVariables(value, variableState);\n }\n return result;\n}\n\nexport function interpolateQueryParams(\n queryParams: QueryParamValues,\n variableState: VariableStateMap\n): QueryParamValues {\n const result: QueryParamValues = {};\n for (const [key, value] of Object.entries(queryParams)) {\n if (Array.isArray(value)) {\n result[key] = value.map((v) => replaceVariables(v, variableState));\n } else {\n result[key] = expandQueryParamValue(value, variableState);\n }\n }\n return result;\n}\n"],"names":["replaceVariables","parseVariablesAndFormat","InterpolationFormat","expandQueryParamValue","value","variableState","Array","isArray","map","v","valueString","variablesMap","varName","format","varState","QUERYPARAM","simpleSyntax","bracketSyntax","singleValue","text","replaceAll","interpolateHeaders","headers","result","key","Object","entries","interpolateQueryParams","queryParams"],"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;AAGjC,SACEA,gBAAgB,EAEhBC,uBAAuB,EACvBC,mBAAmB,QACd,2BAA2B;AAIlC,SAASC,sBAAsBC,KAAwB,EAAEC,aAA+B;IACtF,6CAA6C;IAC7C,IAAIC,MAAMC,OAAO,CAACH,QAAQ;QACxB,OAAOA,MAAMI,GAAG,CAAC,CAACC,IAAMN,sBAAsBM,GAAGJ;IACnD;IAEA,qEAAqE;IACrE,MAAMK,cAAcN;IACpB,MAAMO,eAAeV,wBAAwBS;IAE7C,0EAA0E;IAC1E,KAAK,MAAM,CAACE,SAASC,OAAO,IAAIF,aAAc;QAC5C,MAAMG,WAAWT,aAAa,CAACO,QAAQ;QACvC,IAAI,CAACE,YAAY,CAACR,MAAMC,OAAO,CAACO,SAASV,KAAK,GAAG;QAEjD,uFAAuF;QACvF,IAAI,CAACS,UAAUA,WAAWX,oBAAoBa,UAAU,EAAE;YACxD,oDAAoD;YACpD,MAAMC,eAAe,MAAMJ;YAC3B,MAAMK,gBAAgBJ,SAAS,OAAOD,UAAU,MAAMC,SAAS,MAAM,OAAOD,UAAU;YAEtF,OAAOE,SAASV,KAAK,CAACI,GAAG,CAAC,CAACU;gBACzB,IAAIC,OAAOT;gBACX,8DAA8D;gBAC9D,+EAA+E;gBAC/E,sEAAsE;gBACtES,OAAOA,KAAKC,UAAU,CAACH,eAAeC;gBACtCC,OAAOA,KAAKC,UAAU,CAACJ,cAAcE;gBACrC,6FAA6F;gBAC7F,OAAOlB,iBAAiBmB,MAAMd;YAChC;QACF;IACF;IAEA,+DAA+D;IAC/D,OAAOL,iBAAiBU,aAAaL;AACvC;AAEA,OAAO,SAASgB,mBAAmBC,OAA+B,EAAEjB,aAA+B;IACjG,MAAMkB,SAAyB,CAAC;IAChC,KAAK,MAAM,CAACC,KAAKpB,MAAM,IAAIqB,OAAOC,OAAO,CAACJ,SAAU;QAClDC,MAAM,CAACC,IAAI,GAAGxB,iBAAiBI,OAAOC;IACxC;IACA,OAAOkB;AACT;AAEA,OAAO,SAASI,uBACdC,WAA6B,EAC7BvB,aAA+B;IAE/B,MAAMkB,SAA2B,CAAC;IAClC,KAAK,MAAM,CAACC,KAAKpB,MAAM,IAAIqB,OAAOC,OAAO,CAACE,aAAc;QACtD,IAAItB,MAAMC,OAAO,CAACH,QAAQ;YACxBmB,MAAM,CAACC,IAAI,GAAGpB,MAAMI,GAAG,CAAC,CAACC,IAAMT,iBAAiBS,GAAGJ;QACrD,OAAO;YACLkB,MAAM,CAACC,IAAI,GAAGrB,sBAAsBC,OAAOC;QAC7C;IACF;IACA,OAAOkB;AACT"}
1
+ {"version":3,"sources":["../../src/utils/request-interpolation.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 { RequestHeaders } from '@perses-dev/client';\nimport {\n replaceVariables,\n VariableStateMap,\n parseVariablesAndFormat,\n InterpolationFormat,\n} from './variable-interpolation';\nimport { createRegexFromString } from './regexp';\n\nexport type QueryParamValues = Record<string, string | string[]>;\n\nfunction expandQueryParamValue(value: string | string[], variableState: VariableStateMap): string | string[] {\n // If value is an array, process each element\n if (Array.isArray(value)) {\n return value.map((v) => expandQueryParamValue(v, variableState) as string);\n }\n\n // Now we know value is a string, so we can safely use string methods\n const valueString = value as string;\n const variablesMap = parseVariablesAndFormat(valueString);\n\n // Find the first multi-value variable that should expand to repeated keys\n for (const [varName, format] of variablesMap) {\n const varState = variableState[varName];\n if (!varState) continue;\n\n const options = varState.options ?? [];\n const allOptionValues = options.map((o) => o.value);\n\n const isArray = Array.isArray(varState.value);\n const isCustomAllValue = varState.customAllValue !== undefined;\n\n let expandValues: string[] | undefined;\n if (isArray) {\n expandValues = varState.value as string[];\n } else if (isCustomAllValue) {\n try {\n const regex = createRegexFromString(varState.customAllValue as string);\n const matched = options.filter((o) => regex.test(o.value)).map((o) => o.value);\n expandValues = matched.length > 0 ? matched : allOptionValues;\n } catch {\n expandValues = allOptionValues;\n }\n }\n if (!expandValues) continue;\n\n // If format is queryparam or not specified (default for query params), expand to array\n if (!format || format === InterpolationFormat.QUERYPARAM) {\n // Build syntax patterns for this variable reference\n const simpleSyntax = '$' + varName;\n const bracketSyntax = format ? '${' + varName + ':' + format + '}' : '${' + varName + '}';\n\n return expandValues.map((singleValue) => {\n let text = valueString;\n // Replace the variable reference with the raw value directly.\n // We cannot use replaceVariables here because it would apply QUERYPARAM format\n // to the single value, producing \"varName=val\" instead of just \"val\".\n text = text.replaceAll(bracketSyntax, singleValue);\n text = text.replaceAll(simpleSyntax, singleValue);\n // Replace any remaining variables (other than the expanded one) using standard interpolation\n return replaceVariables(text, variableState);\n });\n }\n }\n\n // No multi-value expansion needed — use standard interpolation\n return replaceVariables(valueString, variableState);\n}\n\nexport function interpolateHeaders(headers: Record<string, string>, variableState: VariableStateMap): RequestHeaders {\n const result: RequestHeaders = {};\n for (const [key, value] of Object.entries(headers)) {\n result[key] = replaceVariables(value, variableState);\n }\n return result;\n}\n\nexport function interpolateQueryParams(\n queryParams: QueryParamValues,\n variableState: VariableStateMap\n): QueryParamValues {\n const result: QueryParamValues = {};\n for (const [key, value] of Object.entries(queryParams)) {\n if (Array.isArray(value)) {\n result[key] = value.map((v) => replaceVariables(v, variableState));\n } else {\n result[key] = expandQueryParamValue(value, variableState);\n }\n }\n return result;\n}\n"],"names":["replaceVariables","parseVariablesAndFormat","InterpolationFormat","createRegexFromString","expandQueryParamValue","value","variableState","Array","isArray","map","v","valueString","variablesMap","varName","format","varState","options","allOptionValues","o","isCustomAllValue","customAllValue","undefined","expandValues","regex","matched","filter","test","length","QUERYPARAM","simpleSyntax","bracketSyntax","singleValue","text","replaceAll","interpolateHeaders","headers","result","key","Object","entries","interpolateQueryParams","queryParams"],"mappings":"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"}
@@ -23,6 +23,7 @@ export type VariableState = {
23
23
  */
24
24
  overridden?: boolean;
25
25
  defaultValue?: VariableValue;
26
+ customAllValue?: string;
26
27
  };
27
28
  /**
28
29
  * Map of variable names to their states
@@ -46,7 +47,8 @@ export declare enum InterpolationFormat {
46
47
  SINGLEQUOTE = "singlequote",
47
48
  SQLSTRING = "sqlstring",
48
49
  TEXT = "text",
49
- QUERYPARAM = "queryparam"
50
+ QUERYPARAM = "queryparam",
51
+ REGEX_LITERAL = "regexliteral"
50
52
  }
51
53
  /**
52
54
  * Interpolate an array of values with a specific format
@@ -1 +1 @@
1
- {"version":3,"file":"variable-interpolation.d.ts","sourceRoot":"","sources":["../../src/utils/variable-interpolation.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE7D;;GAEG;AACH,oBAAY,mBAAmB;IAC7B,GAAG,QAAQ;IACX,WAAW,gBAAgB;IAC3B,WAAW,gBAAgB;IAC3B,IAAI,SAAS;IACb,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,aAAa,kBAAkB;IAC/B,IAAI,SAAS;IACb,UAAU,eAAe;IACzB,GAAG,QAAQ;IACX,KAAK,UAAU;IACf,WAAW,gBAAgB;IAC3B,SAAS,cAAc;IACvB,IAAI,SAAS;IACb,UAAU,eAAe;CAC1B;AASD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAgD/F;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,aAAa,EAC5B,SAAS,CAAC,EAAE,mBAAmB,GAC9B,MAAM,CAcR;AAUD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBrD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC,CAoBlG;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,GAAG,MAAM,CAiBtF"}
1
+ {"version":3,"file":"variable-interpolation.d.ts","sourceRoot":"","sources":["../../src/utils/variable-interpolation.ts"],"names":[],"mappings":"AAaA,OAAO,EAAE,aAAa,EAAE,MAAM,kBAAkB,CAAC;AAEjD;;GAEG;AACH,MAAM,MAAM,cAAc,GAAG;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAE9D;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,KAAK,EAAE,aAAa,CAAC;IACrB,OAAO,CAAC,EAAE,cAAc,EAAE,CAAC;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE,KAAK,CAAC;IACd;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;OAEG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,YAAY,CAAC,EAAE,aAAa,CAAC;IAC7B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB,CAAC;AAEF;;GAEG;AACH,MAAM,MAAM,gBAAgB,GAAG,MAAM,CAAC,MAAM,EAAE,aAAa,CAAC,CAAC;AAE7D;;GAEG;AACH,oBAAY,mBAAmB;IAC7B,GAAG,QAAQ;IACX,WAAW,gBAAgB;IAC3B,WAAW,gBAAgB;IAC3B,IAAI,SAAS;IACb,IAAI,SAAS;IACb,MAAM,WAAW;IACjB,aAAa,kBAAkB;IAC/B,IAAI,SAAS;IACb,UAAU,eAAe;IACzB,GAAG,QAAQ;IACX,KAAK,UAAU;IACf,WAAW,gBAAgB;IAC3B,SAAS,cAAc;IACvB,IAAI,SAAS;IACb,UAAU,eAAe;IACzB,aAAa,iBAAiB;CAC/B;AASD;;GAEG;AACH,wBAAgB,WAAW,CAAC,MAAM,EAAE,MAAM,EAAE,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,mBAAmB,GAAG,MAAM,CAoD/F;AAED;;GAEG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,MAAM,EACf,aAAa,EAAE,aAAa,EAC5B,SAAS,CAAC,EAAE,mBAAmB,GAC9B,MAAM,CAaR;AAUD;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,EAAE,CAiBrD;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,MAAM,GAAG,GAAG,CAAC,MAAM,EAAE,mBAAmB,GAAG,SAAS,CAAC,CAoBlG;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,aAAa,EAAE,gBAAgB,GAAG,MAAM,CAiBtF"}
@@ -28,6 +28,7 @@
28
28
  InterpolationFormat["SQLSTRING"] = "sqlstring";
29
29
  InterpolationFormat["TEXT"] = "text";
30
30
  InterpolationFormat["QUERYPARAM"] = "queryparam";
31
+ InterpolationFormat["REGEX_LITERAL"] = "regexliteral";
31
32
  return InterpolationFormat;
32
33
  }({});
33
34
  function stringToFormat(val) {
@@ -78,6 +79,11 @@ function stringToFormat(val) {
78
79
  const escapedRegex = values.map((v)=>v.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'));
79
80
  return `(${escapedRegex.join('|')})`;
80
81
  }
82
+ case "regexliteral":
83
+ {
84
+ const escapedRegex = values.map((v)=>v.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\\\$&'));
85
+ return `(${escapedRegex.join('|')})`;
86
+ }
81
87
  case "singlequote":
82
88
  return values.map((v)=>`'${v}'`).join(',');
83
89
  case "sqlstring":
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/utils/variable-interpolation.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 { VariableValue } from '@perses-dev/spec';\n\n/**\n * Option for a variable with label and value\n */\nexport type VariableOption = { label: string; value: string };\n\n/**\n * State of a variable including its current value, options, and loading state\n */\nexport type VariableState = {\n value: VariableValue;\n options?: VariableOption[];\n loading: boolean;\n error?: Error;\n /**\n * If a local variable is overriding an external variable, local var will have the flag ``overriding=true``.\n */\n overriding?: boolean;\n /**\n * If a local variable is overriding an external variable, external var will have the flag ``overridden=true``.\n */\n overridden?: boolean;\n defaultValue?: VariableValue;\n};\n\n/**\n * Map of variable names to their states\n */\nexport type VariableStateMap = Record<string, VariableState>;\n\n/**\n * Supported interpolation formats for variable values\n */\nexport enum InterpolationFormat {\n CSV = 'csv',\n DISTRIBUTED = 'distributed',\n DOUBLEQUOTE = 'doublequote',\n GLOB = 'glob',\n JSON = 'json',\n LUCENE = 'lucene',\n PERCENTENCODE = 'percentencode',\n PIPE = 'pipe',\n PROMETHEUS = 'prometheus',\n RAW = 'raw',\n REGEX = 'regex',\n SINGLEQUOTE = 'singlequote',\n SQLSTRING = 'sqlstring',\n TEXT = 'text',\n QUERYPARAM = 'queryparam',\n}\n\nfunction stringToFormat(val: string | undefined): InterpolationFormat | undefined {\n if (!val) return undefined;\n\n const lowerVal = val.toLowerCase();\n return Object.values(InterpolationFormat).find((format) => format === lowerVal) || undefined;\n}\n\n/**\n * Interpolate an array of values with a specific format\n */\nexport function interpolate(values: string[], name: string, format: InterpolationFormat): string {\n switch (format) {\n case InterpolationFormat.CSV:\n case InterpolationFormat.RAW:\n return values.join(',');\n case InterpolationFormat.DISTRIBUTED: {\n const [first, ...rest] = values;\n return `${[first, ...rest.map((v) => `${name}=${v}`)].join(',')}`;\n }\n case InterpolationFormat.DOUBLEQUOTE:\n return values.map((v) => `\"${v}\"`).join(',');\n case InterpolationFormat.GLOB:\n return `{${values.join(',')}}`;\n case InterpolationFormat.JSON: {\n // values might contain stringified JSON objects so we need to parse them first\n // and then return a JSON stringified array to return valid JSON\n const parsedValues = values.map((v) => {\n try {\n return JSON.parse(v);\n } catch {\n return v;\n }\n });\n\n return JSON.stringify(parsedValues);\n }\n case InterpolationFormat.LUCENE:\n return `(${values.map((v) => `\"${v}\"`).join(' OR ')})`;\n case InterpolationFormat.PERCENTENCODE:\n return encodeURIComponent(values.join(','));\n case InterpolationFormat.PIPE:\n return values.join('|');\n case InterpolationFormat.REGEX: {\n const escapedRegex = values.map((v) => v.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&'));\n return `(${escapedRegex.join('|')})`;\n }\n case InterpolationFormat.SINGLEQUOTE:\n return values.map((v) => `'${v}'`).join(',');\n case InterpolationFormat.SQLSTRING:\n return values.map((v) => `'${v.replace(/'/g, \"''\")}'`).join(',');\n case InterpolationFormat.TEXT:\n return values.join(' + ');\n case InterpolationFormat.QUERYPARAM:\n return values.map((v) => `${name}=${encodeURIComponent(v)}`).join('&');\n case InterpolationFormat.PROMETHEUS:\n default:\n return `(${values.join('|')})`;\n }\n}\n\n/**\n * Replace a single variable in text with its value\n */\nexport function replaceVariable(\n text: string,\n varName: string,\n variableValue: VariableValue,\n varFormat?: InterpolationFormat\n): string {\n const variableSyntax = '$' + varName;\n const alternativeVariableSyntax = '${' + varName + (varFormat ? ':' + varFormat : '') + '}';\n\n let replaceString = '';\n if (Array.isArray(variableValue)) {\n replaceString = interpolate(variableValue, varName, varFormat || InterpolationFormat.PROMETHEUS);\n }\n if (typeof variableValue === 'string') {\n replaceString = interpolate([variableValue], varName, varFormat || InterpolationFormat.RAW);\n }\n\n text = text.replaceAll(variableSyntax, replaceString);\n return text.replaceAll(alternativeVariableSyntax, replaceString);\n}\n\n// This regular expression is designed to identify variable references in a string.\n// It supports two formats for referencing variables:\n// 1. $variableName - This is a simpler format, and the regular expression captures the variable name (\\w+ matches one or more word characters).\n// 2. ${variableName} - This is a more complex format and the regular expression captures the variable name (\\w+ matches one or more word characters) in the curly braces.\n// 3. ${variableName:format} - This is a more complex format that allows specifying a format interpolation.\n\nconst VARIABLE_REGEX = /\\$(\\w+)|\\${(\\w+)(?:\\.([^:^}]+))?(?::([^}]+))?}/gm;\n\n/**\n * Returns a list of variables\n */\nexport function parseVariables(text: string): string[] {\n const matches = new Set<string>();\n let match;\n\n while ((match = VARIABLE_REGEX.exec(text)) !== null) {\n if (match) {\n if (match[1]) {\n // \\$(\\w+)\\\n matches.add(match[1]);\n } else if (match[2]) {\n // \\${(\\w+)}\\\n matches.add(match[2]);\n }\n }\n }\n // return unique matches\n return Array.from(matches.values());\n}\n\n/**\n * Returns a map of variable names and its format. If no format is specified, it will be undefined.\n */\nexport function parseVariablesAndFormat(text: string): Map<string, InterpolationFormat | undefined> {\n const matches = new Map<string, InterpolationFormat | undefined>();\n let match;\n\n while ((match = VARIABLE_REGEX.exec(text)) !== null) {\n if (match) {\n let format = undefined;\n if (match[4]) {\n format = match[4];\n }\n if (match[1]) {\n // \\$(\\w+)\\\n matches.set(match[1], stringToFormat(format));\n } else if (match[2]) {\n // \\${(\\w+)}\\\n matches.set(match[2], stringToFormat(format));\n }\n }\n }\n return matches;\n}\n\n/**\n * Replace all variables in text with their values from the variable state map\n */\nexport function replaceVariables(text: string, variableState: VariableStateMap): string {\n const variablesMap = parseVariablesAndFormat(text);\n const variables = Array.from(variablesMap.keys());\n let finalText = text;\n variables\n // Sorting variables by their length.\n // In order to not have a variable name have contained in another variable name.\n // i.e.: $__range replacing $__range_ms => '3600_ms' instead of '3600000'\n .sort((a, b) => b.length - a.length)\n .forEach((v) => {\n const variable = variableState[v];\n if (variable && variable.value !== undefined) {\n finalText = replaceVariable(finalText, v, variable?.value, variablesMap.get(v));\n }\n });\n\n return finalText;\n}\n"],"names":["InterpolationFormat","stringToFormat","val","undefined","lowerVal","toLowerCase","Object","values","find","format","interpolate","name","join","first","rest","map","v","parsedValues","JSON","parse","stringify","encodeURIComponent","escapedRegex","replace","replaceVariable","text","varName","variableValue","varFormat","variableSyntax","alternativeVariableSyntax","replaceString","Array","isArray","replaceAll","VARIABLE_REGEX","parseVariables","matches","Set","match","exec","add","from","parseVariablesAndFormat","Map","set","replaceVariables","variableState","variablesMap","variables","keys","finalText","sort","a","b","length","forEach","variable","value","get"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/utils/variable-interpolation.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 { VariableValue } from '@perses-dev/spec';\n\n/**\n * Option for a variable with label and value\n */\nexport type VariableOption = { label: string; value: string };\n\n/**\n * State of a variable including its current value, options, and loading state\n */\nexport type VariableState = {\n value: VariableValue;\n options?: VariableOption[];\n loading: boolean;\n error?: Error;\n /**\n * If a local variable is overriding an external variable, local var will have the flag ``overriding=true``.\n */\n overriding?: boolean;\n /**\n * If a local variable is overriding an external variable, external var will have the flag ``overridden=true``.\n */\n overridden?: boolean;\n defaultValue?: VariableValue;\n customAllValue?: string;\n};\n\n/**\n * Map of variable names to their states\n */\nexport type VariableStateMap = Record<string, VariableState>;\n\n/**\n * Supported interpolation formats for variable values\n */\nexport enum InterpolationFormat {\n CSV = 'csv',\n DISTRIBUTED = 'distributed',\n DOUBLEQUOTE = 'doublequote',\n GLOB = 'glob',\n JSON = 'json',\n LUCENE = 'lucene',\n PERCENTENCODE = 'percentencode',\n PIPE = 'pipe',\n PROMETHEUS = 'prometheus',\n RAW = 'raw',\n REGEX = 'regex',\n SINGLEQUOTE = 'singlequote',\n SQLSTRING = 'sqlstring',\n TEXT = 'text',\n QUERYPARAM = 'queryparam',\n REGEX_LITERAL = 'regexliteral',\n}\n\nfunction stringToFormat(val: string | undefined): InterpolationFormat | undefined {\n if (!val) return undefined;\n\n const lowerVal = val.toLowerCase();\n return Object.values(InterpolationFormat).find((format) => format === lowerVal) || undefined;\n}\n\n/**\n * Interpolate an array of values with a specific format\n */\nexport function interpolate(values: string[], name: string, format: InterpolationFormat): string {\n switch (format) {\n case InterpolationFormat.CSV:\n case InterpolationFormat.RAW:\n return values.join(',');\n case InterpolationFormat.DISTRIBUTED: {\n const [first, ...rest] = values;\n return `${[first, ...rest.map((v) => `${name}=${v}`)].join(',')}`;\n }\n case InterpolationFormat.DOUBLEQUOTE:\n return values.map((v) => `\"${v}\"`).join(',');\n case InterpolationFormat.GLOB:\n return `{${values.join(',')}}`;\n case InterpolationFormat.JSON: {\n // values might contain stringified JSON objects so we need to parse them first\n // and then return a JSON stringified array to return valid JSON\n const parsedValues = values.map((v) => {\n try {\n return JSON.parse(v);\n } catch {\n return v;\n }\n });\n\n return JSON.stringify(parsedValues);\n }\n case InterpolationFormat.LUCENE:\n return `(${values.map((v) => `\"${v}\"`).join(' OR ')})`;\n case InterpolationFormat.PERCENTENCODE:\n return encodeURIComponent(values.join(','));\n case InterpolationFormat.PIPE:\n return values.join('|');\n case InterpolationFormat.REGEX: {\n const escapedRegex = values.map((v) => v.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&'));\n return `(${escapedRegex.join('|')})`;\n }\n case InterpolationFormat.REGEX_LITERAL: {\n const escapedRegex = values.map((v) => v.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\\\\\$&'));\n return `(${escapedRegex.join('|')})`;\n }\n case InterpolationFormat.SINGLEQUOTE:\n return values.map((v) => `'${v}'`).join(',');\n case InterpolationFormat.SQLSTRING:\n return values.map((v) => `'${v.replace(/'/g, \"''\")}'`).join(',');\n case InterpolationFormat.TEXT:\n return values.join(' + ');\n case InterpolationFormat.QUERYPARAM:\n return values.map((v) => `${name}=${encodeURIComponent(v)}`).join('&');\n case InterpolationFormat.PROMETHEUS:\n default:\n return `(${values.join('|')})`;\n }\n}\n\n/**\n * Replace a single variable in text with its value\n */\nexport function replaceVariable(\n text: string,\n varName: string,\n variableValue: VariableValue,\n varFormat?: InterpolationFormat\n): string {\n const variableSyntax = '$' + varName;\n const alternativeVariableSyntax = '${' + varName + (varFormat ? ':' + varFormat : '') + '}';\n\n let replaceString = '';\n if (Array.isArray(variableValue)) {\n replaceString = interpolate(variableValue, varName, varFormat || InterpolationFormat.PROMETHEUS);\n }\n if (typeof variableValue === 'string') {\n replaceString = interpolate([variableValue], varName, varFormat || InterpolationFormat.RAW);\n }\n text = text.replaceAll(variableSyntax, replaceString);\n return text.replaceAll(alternativeVariableSyntax, replaceString);\n}\n\n// This regular expression is designed to identify variable references in a string.\n// It supports two formats for referencing variables:\n// 1. $variableName - This is a simpler format, and the regular expression captures the variable name (\\w+ matches one or more word characters).\n// 2. ${variableName} - This is a more complex format and the regular expression captures the variable name (\\w+ matches one or more word characters) in the curly braces.\n// 3. ${variableName:format} - This is a more complex format that allows specifying a format interpolation.\n\nconst VARIABLE_REGEX = /\\$(\\w+)|\\${(\\w+)(?:\\.([^:^}]+))?(?::([^}]+))?}/gm;\n\n/**\n * Returns a list of variables\n */\nexport function parseVariables(text: string): string[] {\n const matches = new Set<string>();\n let match;\n\n while ((match = VARIABLE_REGEX.exec(text)) !== null) {\n if (match) {\n if (match[1]) {\n // \\$(\\w+)\\\n matches.add(match[1]);\n } else if (match[2]) {\n // \\${(\\w+)}\\\n matches.add(match[2]);\n }\n }\n }\n // return unique matches\n return Array.from(matches.values());\n}\n\n/**\n * Returns a map of variable names and its format. If no format is specified, it will be undefined.\n */\nexport function parseVariablesAndFormat(text: string): Map<string, InterpolationFormat | undefined> {\n const matches = new Map<string, InterpolationFormat | undefined>();\n let match;\n\n while ((match = VARIABLE_REGEX.exec(text)) !== null) {\n if (match) {\n let format = undefined;\n if (match[4]) {\n format = match[4];\n }\n if (match[1]) {\n // \\$(\\w+)\\\n matches.set(match[1], stringToFormat(format));\n } else if (match[2]) {\n // \\${(\\w+)}\\\n matches.set(match[2], stringToFormat(format));\n }\n }\n }\n return matches;\n}\n\n/**\n * Replace all variables in text with their values from the variable state map\n */\nexport function replaceVariables(text: string, variableState: VariableStateMap): string {\n const variablesMap = parseVariablesAndFormat(text);\n const variables = Array.from(variablesMap.keys());\n let finalText = text;\n variables\n // Sorting variables by their length.\n // In order to not have a variable name have contained in another variable name.\n // i.e.: $__range replacing $__range_ms => '3600_ms' instead of '3600000'\n .sort((a, b) => b.length - a.length)\n .forEach((v) => {\n const variable = variableState[v];\n if (variable && variable.value !== undefined) {\n finalText = replaceVariable(finalText, v, variable?.value, variablesMap.get(v));\n }\n });\n\n return finalText;\n}\n"],"names":["InterpolationFormat","stringToFormat","val","undefined","lowerVal","toLowerCase","Object","values","find","format","interpolate","name","join","first","rest","map","v","parsedValues","JSON","parse","stringify","encodeURIComponent","escapedRegex","replace","replaceVariable","text","varName","variableValue","varFormat","variableSyntax","alternativeVariableSyntax","replaceString","Array","isArray","replaceAll","VARIABLE_REGEX","parseVariables","matches","Set","match","exec","add","from","parseVariablesAndFormat","Map","set","replaceVariables","variableState","variablesMap","variables","keys","finalText","sort","a","b","length","forEach","variable","value","get"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@perses-dev/components",
3
- "version": "0.54.0-beta.8",
3
+ "version": "0.54.0-rc.0",
4
4
  "description": "Common UI components used across Perses features",
5
5
  "license": "Apache-2.0",
6
6
  "homepage": "https://github.com/perses/perses/blob/main/README.md",
@@ -34,8 +34,8 @@
34
34
  "@date-fns/tz": "^1.4.1",
35
35
  "@fontsource/inter": "^5.0.0",
36
36
  "@mui/x-date-pickers": "^7.23.1",
37
- "@perses-dev/spec": "0.2.0-beta.6",
38
- "@perses-dev/client": "0.54.0-beta.8",
37
+ "@perses-dev/spec": "0.2.0-beta.9",
38
+ "@perses-dev/client": "0.54.0-rc.0",
39
39
  "numbro": "^2.3.6",
40
40
  "@tanstack/match-sorter-utils": "^8.19.4",
41
41
  "@tanstack/react-table": "^8.20.5",
@@ -49,7 +49,6 @@
49
49
  "notistack": "^3.0.2",
50
50
  "react-colorful": "^5.6.1",
51
51
  "react-error-boundary": "^3.1.4",
52
- "react-hook-form": "^7.51.3",
53
52
  "react-virtuoso": "^4.12.2"
54
53
  },
55
54
  "devDependencies": {
@@ -1,8 +0,0 @@
1
- import { HTMLAttributes, ReactElement } from 'react';
2
- import { Control } from 'react-hook-form';
3
- import { PanelEditorValues } from '@perses-dev/spec';
4
- export interface LinksEditorProps extends HTMLAttributes<HTMLDivElement> {
5
- control: Control<PanelEditorValues>;
6
- }
7
- export declare function LinksEditor({ control, ...props }: LinksEditorProps): ReactElement;
8
- //# sourceMappingURL=LinksEditor.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"LinksEditor.d.ts","sourceRoot":"","sources":["../../src/LinksEditor/LinksEditor.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAY,cAAc,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAE/D,OAAO,EAA6B,OAAO,EAAE,MAAM,iBAAiB,CAAC;AAGrE,OAAO,EAAE,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAGrD,MAAM,WAAW,gBAAiB,SAAQ,cAAc,CAAC,cAAc,CAAC;IACtE,OAAO,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;CACrC;AAED,wBAAgB,WAAW,CAAC,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,EAAE,gBAAgB,GAAG,YAAY,CAiCjF"}
@@ -1,149 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- // Copyright The Perses Authors
3
- // Licensed under the Apache License, Version 2.0 (the "License");
4
- // you may not use this file except in compliance with the License.
5
- // You may obtain a copy of the License at
6
- //
7
- // http://www.apache.org/licenses/LICENSE-2.0
8
- //
9
- // Unless required by applicable law or agreed to in writing, software
10
- // distributed under the License is distributed on an "AS IS" BASIS,
11
- // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12
- // See the License for the specific language governing permissions and
13
- // limitations under the License.
14
- import { Fragment } from 'react';
15
- import { Divider, IconButton, Stack, Typography } from '@mui/material';
16
- import { Controller, useFieldArray } from 'react-hook-form';
17
- import PlusIcon from 'mdi-material-ui/Plus';
18
- import MinusIcon from 'mdi-material-ui/Minus';
19
- import { LinkEditorForm } from './LinkEditorForm';
20
- export function LinksEditor({ control, ...props }) {
21
- const { fields, append, remove } = useFieldArray({
22
- control: control,
23
- name: 'panelDefinition.spec.links'
24
- });
25
- return /*#__PURE__*/ _jsxs(Stack, {
26
- ...props,
27
- gap: 3,
28
- children: [
29
- fields && fields.length > 0 ? fields.map((field, index)=>/*#__PURE__*/ _jsxs(Fragment, {
30
- children: [
31
- /*#__PURE__*/ _jsxs(Stack, {
32
- direction: "row",
33
- gap: 1,
34
- alignItems: "center",
35
- children: [
36
- /*#__PURE__*/ _jsx(LinkControl, {
37
- control: control,
38
- index: index
39
- }),
40
- /*#__PURE__*/ _jsx(IconButton, {
41
- style: {
42
- width: 'fit-content',
43
- height: 'fit-content'
44
- },
45
- onClick: ()=>remove(index),
46
- children: /*#__PURE__*/ _jsx(MinusIcon, {})
47
- })
48
- ]
49
- }),
50
- /*#__PURE__*/ _jsx(Divider, {})
51
- ]
52
- }, field.id)) : /*#__PURE__*/ _jsx(Typography, {
53
- variant: "subtitle1",
54
- mb: 2,
55
- fontStyle: "italic",
56
- children: "No links defined"
57
- }),
58
- /*#__PURE__*/ _jsx(IconButton, {
59
- style: {
60
- width: 'fit-content',
61
- height: 'fit-content'
62
- },
63
- onClick: ()=>append({
64
- url: '',
65
- name: '',
66
- tooltip: '',
67
- renderVariables: false,
68
- targetBlank: false
69
- }),
70
- children: /*#__PURE__*/ _jsx(PlusIcon, {})
71
- })
72
- ]
73
- });
74
- }
75
- function LinkControl({ control, index }) {
76
- const defaultLink = {
77
- url: '',
78
- name: '',
79
- tooltip: '',
80
- renderVariables: false,
81
- targetBlank: false
82
- };
83
- return /*#__PURE__*/ _jsx(Controller, {
84
- control: control,
85
- name: `panelDefinition.spec.links.${index}`,
86
- render: ({ field, field: { value: link }, fieldState })=>{
87
- const safeLink = link ?? defaultLink;
88
- return /*#__PURE__*/ _jsx(LinkEditorForm, {
89
- mode: "inline",
90
- url: {
91
- value: safeLink.url,
92
- label: 'URL',
93
- error: {
94
- hasError: !!fieldState.error,
95
- helperText: fieldState.error?.message
96
- },
97
- onChange: (url)=>{
98
- field.onChange({
99
- ...link,
100
- url
101
- });
102
- }
103
- },
104
- newTabOpen: {
105
- value: !!safeLink.targetBlank,
106
- onChange: (targetBlank)=>{
107
- field.onChange({
108
- ...link,
109
- targetBlank
110
- });
111
- },
112
- label: 'Open in new tab'
113
- },
114
- name: {
115
- value: safeLink.name ?? '',
116
- label: 'Name',
117
- onChange: (name)=>{
118
- field.onChange({
119
- ...link,
120
- name
121
- });
122
- }
123
- },
124
- renderVariables: {
125
- value: !!safeLink.renderVariables,
126
- label: 'Render variables',
127
- onChange: (renderVariables)=>{
128
- field.onChange({
129
- ...link,
130
- renderVariables
131
- });
132
- }
133
- },
134
- tooltip: {
135
- value: safeLink.tooltip ?? '',
136
- label: 'Tooltip',
137
- onChange: (tooltip)=>{
138
- field.onChange({
139
- ...link,
140
- tooltip
141
- });
142
- }
143
- }
144
- });
145
- }
146
- });
147
- }
148
-
149
- //# sourceMappingURL=LinksEditor.js.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/LinksEditor/LinksEditor.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 { Fragment, HTMLAttributes, ReactElement } from 'react';\nimport { Divider, IconButton, Stack, Typography } from '@mui/material';\nimport { Controller, useFieldArray, Control } from 'react-hook-form';\nimport PlusIcon from 'mdi-material-ui/Plus';\nimport MinusIcon from 'mdi-material-ui/Minus';\nimport { PanelEditorValues } from '@perses-dev/spec';\nimport { LinkEditorForm } from './LinkEditorForm';\n\nexport interface LinksEditorProps extends HTMLAttributes<HTMLDivElement> {\n control: Control<PanelEditorValues>;\n}\n\nexport function LinksEditor({ control, ...props }: LinksEditorProps): ReactElement {\n const { fields, append, remove } = useFieldArray({\n control: control,\n name: 'panelDefinition.spec.links',\n });\n\n return (\n <Stack {...props} gap={3}>\n {fields && fields.length > 0 ? (\n fields.map((field, index) => (\n <Fragment key={field.id}>\n <Stack direction=\"row\" gap={1} alignItems=\"center\">\n <LinkControl control={control} index={index} />\n <IconButton style={{ width: 'fit-content', height: 'fit-content' }} onClick={() => remove(index)}>\n <MinusIcon />\n </IconButton>\n </Stack>\n <Divider />\n </Fragment>\n ))\n ) : (\n <Typography variant=\"subtitle1\" mb={2} fontStyle=\"italic\">\n No links defined\n </Typography>\n )}\n <IconButton\n style={{ width: 'fit-content', height: 'fit-content' }}\n onClick={() => append({ url: '', name: '', tooltip: '', renderVariables: false, targetBlank: false })}\n >\n <PlusIcon />\n </IconButton>\n </Stack>\n );\n}\n\nfunction LinkControl({ control, index }: { control: Control<PanelEditorValues>; index: number }): ReactElement {\n const defaultLink = { url: '', name: '', tooltip: '', renderVariables: false, targetBlank: false };\n return (\n <Controller\n control={control}\n name={`panelDefinition.spec.links.${index}`}\n render={({ field, field: { value: link }, fieldState }) => {\n const safeLink = link ?? defaultLink;\n return (\n <LinkEditorForm\n mode=\"inline\"\n url={{\n value: safeLink.url,\n label: 'URL',\n error: { hasError: !!fieldState.error, helperText: fieldState.error?.message },\n onChange: (url) => {\n field.onChange({ ...link, url });\n },\n }}\n newTabOpen={{\n value: !!safeLink.targetBlank,\n onChange: (targetBlank) => {\n field.onChange({ ...link, targetBlank });\n },\n label: 'Open in new tab',\n }}\n name={{\n value: safeLink.name ?? '',\n label: 'Name',\n onChange: (name) => {\n field.onChange({ ...link, name });\n },\n }}\n renderVariables={{\n value: !!safeLink.renderVariables,\n label: 'Render variables',\n onChange: (renderVariables) => {\n field.onChange({ ...link, renderVariables });\n },\n }}\n tooltip={{\n value: safeLink.tooltip ?? '',\n label: 'Tooltip',\n onChange: (tooltip) => {\n field.onChange({ ...link, tooltip });\n },\n }}\n />\n );\n }}\n />\n );\n}\n"],"names":["Fragment","Divider","IconButton","Stack","Typography","Controller","useFieldArray","PlusIcon","MinusIcon","LinkEditorForm","LinksEditor","control","props","fields","append","remove","name","gap","length","map","field","index","direction","alignItems","LinkControl","style","width","height","onClick","id","variant","mb","fontStyle","url","tooltip","renderVariables","targetBlank","defaultLink","render","value","link","fieldState","safeLink","mode","label","error","hasError","helperText","message","onChange","newTabOpen"],"mappings":"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"}