@perses-dev/table-plugin 0.14.0-beta.7 → 0.14.0-rc.1

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 (102) hide show
  1. package/__mf/js/Table.a3908f66.js +7 -0
  2. package/__mf/js/async/1186.c1b14e64.js +2 -0
  3. package/__mf/js/async/1616.58269394.js +1 -0
  4. package/__mf/js/async/2035.9b7993d8.js +1 -0
  5. package/__mf/js/async/2043.231e3e9c.js +2 -0
  6. package/__mf/js/async/2062.db498990.js +7 -0
  7. package/__mf/js/async/2465.a95673ec.js +1 -0
  8. package/__mf/js/async/{3632.269403ab.js → 3632.a80fa015.js} +1 -1
  9. package/__mf/js/async/392.16b8c016.js +2 -0
  10. package/__mf/js/async/3923.1a5a1a34.js +1 -0
  11. package/__mf/js/async/4077.4d399855.js +1 -0
  12. package/__mf/js/async/4121.28d73fa4.js +2 -0
  13. package/__mf/js/async/4122.c44aa0dd.js +37 -0
  14. package/__mf/js/async/4496.1555a936.js +1 -0
  15. package/__mf/js/async/5002.da5c7909.js +1 -0
  16. package/__mf/js/async/5054.ceb0de94.js +1 -0
  17. package/__mf/js/async/5071.25ee2344.js +1 -0
  18. package/__mf/js/async/5153.d0a5188e.js +1 -0
  19. package/__mf/js/async/5587.ae5b65d8.js +1 -0
  20. package/__mf/js/async/{5741.b157792e.js → 5741.675e49c8.js} +2 -2
  21. package/__mf/js/async/6274.a022b53a.js +2 -0
  22. package/__mf/js/async/7192.13903b23.js +1 -0
  23. package/__mf/js/async/7534.cc00b47e.js +1 -0
  24. package/__mf/js/async/7862.86d45646.js +2 -0
  25. package/__mf/js/async/7893.377f2764.js +88 -0
  26. package/__mf/js/async/7968.6d77e809.js +1 -0
  27. package/__mf/js/async/7978.e4341773.js +1 -0
  28. package/__mf/js/async/8356.6fc36a50.js +1 -0
  29. package/__mf/js/async/8470.a7afb117.js +2 -0
  30. package/__mf/js/async/873.cf58d4f4.js +1 -0
  31. package/__mf/js/async/9207.1bb95977.js +1 -0
  32. package/__mf/js/async/9235.d62a73ea.js +1 -0
  33. package/__mf/js/async/9321.d6e366f3.js +1 -0
  34. package/__mf/js/async/941.99874609.js +2 -0
  35. package/__mf/js/async/9588.29a8dbd5.js +1 -0
  36. package/__mf/js/async/9996.438ee6a0.js +3 -0
  37. package/__mf/js/async/__federation_expose_Table.98b8d4f5.js +1 -0
  38. package/__mf/js/async/lib-router.b83965b4.js +2 -0
  39. package/__mf/js/main.7b8cd485.js +7 -0
  40. package/lib/TableExportAction.d.ts.map +1 -1
  41. package/lib/TableExportAction.js +14 -11
  42. package/lib/TableExportAction.js.map +1 -1
  43. package/lib/components/ColumnsEditor/EmbeddedPanelOptionsEditor.d.ts.map +1 -1
  44. package/lib/components/ColumnsEditor/EmbeddedPanelOptionsEditor.js +6 -7
  45. package/lib/components/ColumnsEditor/EmbeddedPanelOptionsEditor.js.map +1 -1
  46. package/lib/components/TablePanel.d.ts.map +1 -1
  47. package/lib/components/TablePanel.js +52 -40
  48. package/lib/components/TablePanel.js.map +1 -1
  49. package/lib/models/model.d.ts.map +1 -1
  50. package/lib/models/model.js.map +1 -1
  51. package/mf-manifest.json +48 -48
  52. package/mf-stats.json +48 -48
  53. package/package.json +5 -5
  54. package/__mf/js/Table.deed13bc.js +0 -7
  55. package/__mf/js/async/1186.b9a973e2.js +0 -2
  56. package/__mf/js/async/1616.3031cf3e.js +0 -1
  57. package/__mf/js/async/2035.ee8b4b52.js +0 -1
  58. package/__mf/js/async/2043.d0c2546f.js +0 -2
  59. package/__mf/js/async/2465.40da59c4.js +0 -1
  60. package/__mf/js/async/2880.33bd0501.js +0 -7
  61. package/__mf/js/async/3487.e55572a8.js +0 -88
  62. package/__mf/js/async/392.fb3eb124.js +0 -2
  63. package/__mf/js/async/3923.346f1a1d.js +0 -1
  64. package/__mf/js/async/4077.77178ae1.js +0 -1
  65. package/__mf/js/async/4121.89a4dd9e.js +0 -2
  66. package/__mf/js/async/4122.c90e83a2.js +0 -37
  67. package/__mf/js/async/4496.783a8195.js +0 -1
  68. package/__mf/js/async/4566.59e93c3a.js +0 -3
  69. package/__mf/js/async/5002.bb4b6e85.js +0 -1
  70. package/__mf/js/async/5054.191180ab.js +0 -1
  71. package/__mf/js/async/5071.9704c71f.js +0 -1
  72. package/__mf/js/async/5153.883dd7a2.js +0 -1
  73. package/__mf/js/async/5587.9ec9977a.js +0 -1
  74. package/__mf/js/async/6274.322baa72.js +0 -2
  75. package/__mf/js/async/7192.34122aea.js +0 -1
  76. package/__mf/js/async/7534.b2d90478.js +0 -1
  77. package/__mf/js/async/7862.311b69b4.js +0 -2
  78. package/__mf/js/async/7968.eda00b79.js +0 -1
  79. package/__mf/js/async/7978.9974e83f.js +0 -1
  80. package/__mf/js/async/8356.0a98e41f.js +0 -1
  81. package/__mf/js/async/8470.17adcb24.js +0 -2
  82. package/__mf/js/async/873.48577c79.js +0 -1
  83. package/__mf/js/async/9207.f5856086.js +0 -1
  84. package/__mf/js/async/9235.6a2d4989.js +0 -1
  85. package/__mf/js/async/9321.2116928b.js +0 -1
  86. package/__mf/js/async/941.9c88b153.js +0 -2
  87. package/__mf/js/async/9588.3db87d01.js +0 -1
  88. package/__mf/js/async/__federation_expose_Table.1015b3b5.js +0 -1
  89. package/__mf/js/async/lib-router.59a67a6b.js +0 -2
  90. package/__mf/js/main.df0404c2.js +0 -7
  91. /package/__mf/css/async/{2880.85c2cbf6.css → 2062.85c2cbf6.css} +0 -0
  92. /package/__mf/js/async/{2043.d0c2546f.js.LICENSE.txt → 2043.231e3e9c.js.LICENSE.txt} +0 -0
  93. /package/__mf/js/async/{2880.33bd0501.js.LICENSE.txt → 2062.db498990.js.LICENSE.txt} +0 -0
  94. /package/__mf/js/async/{392.fb3eb124.js.LICENSE.txt → 392.16b8c016.js.LICENSE.txt} +0 -0
  95. /package/__mf/js/async/{4121.89a4dd9e.js.LICENSE.txt → 4121.28d73fa4.js.LICENSE.txt} +0 -0
  96. /package/__mf/js/async/{5741.b157792e.js.LICENSE.txt → 5741.675e49c8.js.LICENSE.txt} +0 -0
  97. /package/__mf/js/async/{6274.322baa72.js.LICENSE.txt → 6274.a022b53a.js.LICENSE.txt} +0 -0
  98. /package/__mf/js/async/{7862.311b69b4.js.LICENSE.txt → 7862.86d45646.js.LICENSE.txt} +0 -0
  99. /package/__mf/js/async/{3487.e55572a8.js.LICENSE.txt → 7893.377f2764.js.LICENSE.txt} +0 -0
  100. /package/__mf/js/async/{8470.17adcb24.js.LICENSE.txt → 8470.a7afb117.js.LICENSE.txt} +0 -0
  101. /package/__mf/js/async/{941.9c88b153.js.LICENSE.txt → 941.99874609.js.LICENSE.txt} +0 -0
  102. /package/__mf/js/async/{lib-router.59a67a6b.js.LICENSE.txt → lib-router.b83965b4.js.LICENSE.txt} +0 -0
@@ -60,6 +60,19 @@ import { buildRawTableData } from "./table-data-utils.js";
60
60
  columns
61
61
  };
62
62
  }
63
+ function downloadCsv(csvBlob, filename) {
64
+ const url = URL.createObjectURL(csvBlob);
65
+ try {
66
+ const link = document.createElement('a');
67
+ link.href = url;
68
+ link.download = filename;
69
+ document.body.appendChild(link);
70
+ link.click();
71
+ document.body.removeChild(link);
72
+ } finally{
73
+ URL.revokeObjectURL(url);
74
+ }
75
+ }
63
76
  export const TableExportAction = ({ queryResults, spec, definition })=>{
64
77
  const tableData = useMemo(()=>buildTableData(queryResults, spec), [
65
78
  queryResults,
@@ -84,17 +97,7 @@ export const TableExportAction = ({ queryResults, spec, definition })=>{
84
97
  });
85
98
  const baseFilename = sanitizeFilename(title);
86
99
  const filename = `${baseFilename}_data.csv`;
87
- const url = URL.createObjectURL(csvBlob);
88
- try {
89
- const link = document.createElement('a');
90
- link.href = url;
91
- link.download = filename;
92
- document.body.appendChild(link);
93
- link.click();
94
- document.body.removeChild(link);
95
- } finally{
96
- URL.revokeObjectURL(url);
97
- }
100
+ downloadCsv(csvBlob, filename);
98
101
  } catch (error) {
99
102
  console.error('Table CSV export failed:', error);
100
103
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/TableExportAction.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 { IconButton } from '@mui/material';\nimport { InfoTooltip, transformData } from '@perses-dev/components';\nimport type { PanelData } from '@perses-dev/plugin-system';\nimport { escapeCsvValue, sanitizeFilename } from '@perses-dev/plugin-system';\nimport type { QueryDataType } from '@perses-dev/spec';\nimport DownloadIcon from 'mdi-material-ui/Download';\nimport React, { useCallback, useMemo } from 'react';\n\nimport type { TableProps } from './components';\nimport type { TableOptions } from './models';\nimport { buildRawTableData } from './table-data-utils';\n\nexport interface ExportColumn {\n key: string;\n header: string;\n}\n\n/**\n * Converts raw query results into the same tabular structure that TablePanel\n * renders, applying indexed column naming and configured transforms so the\n * CSV output matches the visual table.\n */\nexport function buildTableData(\n queryResults: Array<PanelData<QueryDataType>>,\n spec: TableOptions,\n): { data: Array<Record<string, unknown>>; columns: ExportColumn[] } {\n // Use shared utility with forExport=true to get raw scalar values\n const rawData = buildRawTableData(queryResults, spec, { forExport: true });\n\n const transformed = transformData(rawData, spec.transforms ?? []);\n\n const allKeys: string[] = [];\n for (const entry of transformed) {\n for (const key of Object.keys(entry)) {\n if (!allKeys.includes(key)) allKeys.push(key);\n }\n }\n\n const columnSettings = spec.columnSettings ?? [];\n const columns: ExportColumn[] = [];\n const customized = new Set<string>();\n\n for (const col of columnSettings) {\n if (customized.has(col.name)) continue;\n customized.add(col.name);\n if (col.hide) continue;\n columns.push({ key: col.name, header: col.header ?? col.name });\n }\n\n if (!spec.defaultColumnHidden) {\n for (const key of allKeys) {\n if (!customized.has(key)) {\n columns.push({ key, header: key });\n }\n }\n }\n\n return { data: transformed, columns };\n}\n\nexport const TableExportAction: React.FC<TableProps> = ({ queryResults, spec, definition }) => {\n const tableData = useMemo(() => buildTableData(queryResults, spec), [queryResults, spec]);\n\n const canExport = tableData.data.length > 0 && tableData.columns.length > 0;\n\n const handleExport = useCallback(() => {\n if (!canExport) return;\n\n try {\n const title = definition?.spec?.display?.name || 'Table Data';\n const { data, columns } = tableData;\n\n const headerRow = columns.map((c) => escapeCsvValue(c.header)).join(',');\n const dataRows = data.map((row) => columns.map((col) => escapeCsvValue(row[col.key])).join(','));\n\n const csvString = [headerRow, ...dataRows].join('\\n') + '\\n';\n const csvBlob = new Blob([csvString], { type: 'text/csv;charset=utf-8' });\n\n const baseFilename = sanitizeFilename(title);\n const filename = `${baseFilename}_data.csv`;\n\n const url = URL.createObjectURL(csvBlob);\n try {\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n } finally {\n URL.revokeObjectURL(url);\n }\n } catch (error) {\n console.error('Table CSV export failed:', error);\n }\n }, [canExport, tableData, definition]);\n\n if (!canExport) {\n return null;\n }\n\n return (\n <InfoTooltip description=\"Export as CSV\">\n <IconButton size=\"small\" onClick={handleExport} aria-label=\"Export table data as CSV\">\n <DownloadIcon fontSize=\"inherit\" />\n </IconButton>\n </InfoTooltip>\n );\n};\n"],"names":["IconButton","InfoTooltip","transformData","escapeCsvValue","sanitizeFilename","DownloadIcon","React","useCallback","useMemo","buildRawTableData","buildTableData","queryResults","spec","rawData","forExport","transformed","transforms","allKeys","entry","key","Object","keys","includes","push","columnSettings","columns","customized","Set","col","has","name","add","hide","header","defaultColumnHidden","data","TableExportAction","definition","tableData","canExport","length","handleExport","title","display","headerRow","map","c","join","dataRows","row","csvString","csvBlob","Blob","type","baseFilename","filename","url","URL","createObjectURL","link","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","error","console","description","size","onClick","aria-label","fontSize"],"mappings":"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"}
1
+ {"version":3,"sources":["../../src/TableExportAction.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 { IconButton } from '@mui/material';\nimport { InfoTooltip, transformData } from '@perses-dev/components';\nimport type { PanelData } from '@perses-dev/plugin-system';\nimport { escapeCsvValue, sanitizeFilename } from '@perses-dev/plugin-system';\nimport type { QueryDataType } from '@perses-dev/spec';\nimport DownloadIcon from 'mdi-material-ui/Download';\nimport React, { useCallback, useMemo } from 'react';\n\nimport type { TableProps } from './components';\nimport type { TableOptions } from './models';\nimport { buildRawTableData } from './table-data-utils';\n\nexport interface ExportColumn {\n key: string;\n header: string;\n}\n\n/**\n * Converts raw query results into the same tabular structure that TablePanel\n * renders, applying indexed column naming and configured transforms so the\n * CSV output matches the visual table.\n */\nexport function buildTableData(\n queryResults: Array<PanelData<QueryDataType>>,\n spec: TableOptions,\n): { data: Array<Record<string, unknown>>; columns: ExportColumn[] } {\n // Use shared utility with forExport=true to get raw scalar values\n const rawData = buildRawTableData(queryResults, spec, { forExport: true });\n\n const transformed = transformData(rawData, spec.transforms ?? []);\n\n const allKeys: string[] = [];\n for (const entry of transformed) {\n for (const key of Object.keys(entry)) {\n if (!allKeys.includes(key)) allKeys.push(key);\n }\n }\n\n const columnSettings = spec.columnSettings ?? [];\n const columns: ExportColumn[] = [];\n const customized = new Set<string>();\n\n for (const col of columnSettings) {\n if (customized.has(col.name)) continue;\n customized.add(col.name);\n if (col.hide) continue;\n columns.push({ key: col.name, header: col.header ?? col.name });\n }\n\n if (!spec.defaultColumnHidden) {\n for (const key of allKeys) {\n if (!customized.has(key)) {\n columns.push({ key, header: key });\n }\n }\n }\n\n return { data: transformed, columns };\n}\n\nfunction downloadCsv(csvBlob: Blob, filename: string): void {\n const url = URL.createObjectURL(csvBlob);\n try {\n const link = document.createElement('a');\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n } finally {\n URL.revokeObjectURL(url);\n }\n}\n\nexport const TableExportAction: React.FC<TableProps> = ({ queryResults, spec, definition }) => {\n const tableData = useMemo(() => buildTableData(queryResults, spec), [queryResults, spec]);\n\n const canExport = tableData.data.length > 0 && tableData.columns.length > 0;\n\n const handleExport = useCallback(() => {\n if (!canExport) return;\n\n try {\n const title = definition?.spec?.display?.name || 'Table Data';\n const { data, columns } = tableData;\n\n const headerRow = columns.map((c) => escapeCsvValue(c.header)).join(',');\n const dataRows = data.map((row) => columns.map((col) => escapeCsvValue(row[col.key])).join(','));\n\n const csvString = [headerRow, ...dataRows].join('\\n') + '\\n';\n const csvBlob = new Blob([csvString], { type: 'text/csv;charset=utf-8' });\n\n const baseFilename = sanitizeFilename(title);\n const filename = `${baseFilename}_data.csv`;\n\n downloadCsv(csvBlob, filename);\n } catch (error) {\n console.error('Table CSV export failed:', error);\n }\n }, [canExport, tableData, definition]);\n\n if (!canExport) {\n return null;\n }\n\n return (\n <InfoTooltip description=\"Export as CSV\">\n <IconButton size=\"small\" onClick={handleExport} aria-label=\"Export table data as CSV\">\n <DownloadIcon fontSize=\"inherit\" />\n </IconButton>\n </InfoTooltip>\n );\n};\n"],"names":["IconButton","InfoTooltip","transformData","escapeCsvValue","sanitizeFilename","DownloadIcon","React","useCallback","useMemo","buildRawTableData","buildTableData","queryResults","spec","rawData","forExport","transformed","transforms","allKeys","entry","key","Object","keys","includes","push","columnSettings","columns","customized","Set","col","has","name","add","hide","header","defaultColumnHidden","data","downloadCsv","csvBlob","filename","url","URL","createObjectURL","link","document","createElement","href","download","body","appendChild","click","removeChild","revokeObjectURL","TableExportAction","definition","tableData","canExport","length","handleExport","title","display","headerRow","map","c","join","dataRows","row","csvString","Blob","type","baseFilename","error","console","description","size","onClick","aria-label","fontSize"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"EmbeddedPanelOptionsEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/ColumnsEditor/EmbeddedPanelOptionsEditor.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CACvC;AAaD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,+BAA+B,GAAG,YAAY,CAyElH"}
1
+ {"version":3,"file":"EmbeddedPanelOptionsEditor.d.ts","sourceRoot":"","sources":["../../../../src/components/ColumnsEditor/EmbeddedPanelOptionsEditor.tsx"],"names":[],"mappings":"AAgBA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAG1C,MAAM,WAAW,+BAA+B;IAC9C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,WAAW,CAAC;IAClB,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAC;CACvC;AAaD;;;GAGG;AACH,wBAAgB,0BAA0B,CAAC,EAAE,IAAI,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,+BAA+B,GAAG,YAAY,CAwElH"}
@@ -12,9 +12,9 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
12
12
  // See the License for the specific language governing permissions and
13
13
  // limitations under the License.
14
14
  import { CircularProgress, Stack, Typography } from "@mui/material";
15
- import { OptionsEditorTabs, usePlugin } from "@perses-dev/plugin-system";
15
+ import { OptionsEditorTabs, useEvent, usePlugin } from "@perses-dev/plugin-system";
16
16
  import merge from "lodash/merge";
17
- import { useEffect, useMemo, useRef } from "react";
17
+ import { useEffect, useMemo } from "react";
18
18
  function isSpecEmpty(spec) {
19
19
  if (spec === undefined || spec === null) return true;
20
20
  if (typeof spec !== 'object') return false;
@@ -39,18 +39,17 @@ function mergeWithPluginDefaults(plugin, spec) {
39
39
  panelPlugin,
40
40
  spec
41
41
  ]);
42
- const onChangeRef = useRef(onChange);
43
- onChangeRef.current = onChange;
42
+ const handleChange = useEvent(onChange);
44
43
  // Persist plugin defaults when the column still has an empty spec (e.g. after switching panel kind).
45
44
  useEffect(()=>{
46
45
  if (!panelPlugin || !isSpecEmpty(spec)) {
47
46
  return;
48
47
  }
49
- onChangeRef.current(mergeWithPluginDefaults(panelPlugin, spec));
48
+ handleChange(mergeWithPluginDefaults(panelPlugin, spec));
50
49
  }, [
51
50
  panelPlugin,
52
- kind,
53
- spec
51
+ spec,
52
+ handleChange
54
53
  ]);
55
54
  if (isLoading) {
56
55
  return /*#__PURE__*/ _jsxs(Stack, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/ColumnsEditor/EmbeddedPanelOptionsEditor.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 { CircularProgress, Stack, Typography } from '@mui/material';\nimport type { PanelPlugin } from '@perses-dev/plugin-system';\nimport { OptionsEditorTabs, usePlugin } from '@perses-dev/plugin-system';\nimport type { UnknownSpec } from '@perses-dev/spec';\nimport merge from 'lodash/merge';\nimport type { ReactElement } from 'react';\nimport { useEffect, useMemo, useRef } from 'react';\n\nexport interface EmbeddedPanelOptionsEditorProps {\n kind: string;\n spec: UnknownSpec;\n onChange: (next: UnknownSpec) => void;\n}\n\nfunction isSpecEmpty(spec: UnknownSpec | undefined): boolean {\n if (spec === undefined || spec === null) return true;\n if (typeof spec !== 'object') return false;\n return Object.keys(spec as object).length === 0;\n}\n\nfunction mergeWithPluginDefaults(plugin: PanelPlugin, spec: UnknownSpec | undefined): UnknownSpec {\n const initial = plugin.createInitialOptions() ?? {};\n return merge({}, initial, spec ?? {}) as UnknownSpec;\n}\n\n/**\n * Renders a panel plugin's settings tabs (thresholds, units, colors, …).\n * Used for embedded GaugeChart columns only; other embedded panel kinds use defaults.\n */\nexport function EmbeddedPanelOptionsEditor({ kind, spec, onChange }: EmbeddedPanelOptionsEditorProps): ReactElement {\n const { data: plugin, isLoading, isError, error } = usePlugin('Panel', kind);\n\n const panelPlugin = plugin as PanelPlugin | undefined;\n\n const mergedSpec = useMemo(() => {\n if (!panelPlugin) {\n return spec;\n }\n return mergeWithPluginDefaults(panelPlugin, spec);\n }, [panelPlugin, spec]);\n\n const onChangeRef = useRef(onChange);\n onChangeRef.current = onChange;\n\n // Persist plugin defaults when the column still has an empty spec (e.g. after switching panel kind).\n useEffect(() => {\n if (!panelPlugin || !isSpecEmpty(spec)) {\n return;\n }\n onChangeRef.current(mergeWithPluginDefaults(panelPlugin, spec));\n }, [panelPlugin, kind, spec]);\n\n if (isLoading) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={1} sx={{ py: 1 }}>\n <CircularProgress size={22} />\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading panel settings…\n </Typography>\n </Stack>\n );\n }\n\n if (isError || !plugin) {\n return (\n <Typography variant=\"body2\" color=\"error\">\n {error?.message ?? 'Could not load panel plugin.'}\n </Typography>\n );\n }\n\n const loadedPlugin = plugin as PanelPlugin;\n const editorTabs = loadedPlugin.panelOptionsEditorComponents ?? [];\n\n if (editorTabs.length === 0) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n This visualization has no editable settings.\n </Typography>\n );\n }\n\n return (\n <Stack spacing={2.5} sx={{ width: '100%', py: 1 }}>\n <OptionsEditorTabs\n tabs={editorTabs.map((tab) => {\n const Content = tab.content;\n return {\n label: tab.label,\n content: (\n <Content\n value={mergedSpec}\n onChange={(next) => {\n onChange(next as UnknownSpec);\n }}\n />\n ),\n };\n })}\n />\n </Stack>\n );\n}\n"],"names":["CircularProgress","Stack","Typography","OptionsEditorTabs","usePlugin","merge","useEffect","useMemo","useRef","isSpecEmpty","spec","undefined","Object","keys","length","mergeWithPluginDefaults","plugin","initial","createInitialOptions","EmbeddedPanelOptionsEditor","kind","onChange","data","isLoading","isError","error","panelPlugin","mergedSpec","onChangeRef","current","direction","alignItems","spacing","sx","py","size","variant","color","message","loadedPlugin","editorTabs","panelOptionsEditorComponents","width","tabs","map","tab","Content","content","label","value","next"],"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,gBAAgB,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAgB;AAEpE,SAASC,iBAAiB,EAAEC,SAAS,QAAQ,4BAA4B;AAEzE,OAAOC,WAAW,eAAe;AAEjC,SAASC,SAAS,EAAEC,OAAO,EAAEC,MAAM,QAAQ,QAAQ;AAQnD,SAASC,YAAYC,IAA6B;IAChD,IAAIA,SAASC,aAAaD,SAAS,MAAM,OAAO;IAChD,IAAI,OAAOA,SAAS,UAAU,OAAO;IACrC,OAAOE,OAAOC,IAAI,CAACH,MAAgBI,MAAM,KAAK;AAChD;AAEA,SAASC,wBAAwBC,MAAmB,EAAEN,IAA6B;IACjF,MAAMO,UAAUD,OAAOE,oBAAoB,MAAM,CAAC;IAClD,OAAOb,MAAM,CAAC,GAAGY,SAASP,QAAQ,CAAC;AACrC;AAEA;;;CAGC,GACD,OAAO,SAASS,2BAA2B,EAAEC,IAAI,EAAEV,IAAI,EAAEW,QAAQ,EAAmC;IAClG,MAAM,EAAEC,MAAMN,MAAM,EAAEO,SAAS,EAAEC,OAAO,EAAEC,KAAK,EAAE,GAAGrB,UAAU,SAASgB;IAEvE,MAAMM,cAAcV;IAEpB,MAAMW,aAAapB,QAAQ;QACzB,IAAI,CAACmB,aAAa;YAChB,OAAOhB;QACT;QACA,OAAOK,wBAAwBW,aAAahB;IAC9C,GAAG;QAACgB;QAAahB;KAAK;IAEtB,MAAMkB,cAAcpB,OAAOa;IAC3BO,YAAYC,OAAO,GAAGR;IAEtB,qGAAqG;IACrGf,UAAU;QACR,IAAI,CAACoB,eAAe,CAACjB,YAAYC,OAAO;YACtC;QACF;QACAkB,YAAYC,OAAO,CAACd,wBAAwBW,aAAahB;IAC3D,GAAG;QAACgB;QAAaN;QAAMV;KAAK;IAE5B,IAAIa,WAAW;QACb,qBACE,MAACtB;YAAM6B,WAAU;YAAMC,YAAW;YAASC,SAAS;YAAGC,IAAI;gBAAEC,IAAI;YAAE;;8BACjE,KAAClC;oBAAiBmC,MAAM;;8BACxB,KAACjC;oBAAWkC,SAAQ;oBAAQC,OAAM;8BAAiB;;;;IAKzD;IAEA,IAAIb,WAAW,CAACR,QAAQ;QACtB,qBACE,KAACd;YAAWkC,SAAQ;YAAQC,OAAM;sBAC/BZ,OAAOa,WAAW;;IAGzB;IAEA,MAAMC,eAAevB;IACrB,MAAMwB,aAAaD,aAAaE,4BAA4B,IAAI,EAAE;IAElE,IAAID,WAAW1B,MAAM,KAAK,GAAG;QAC3B,qBACE,KAACZ;YAAWkC,SAAQ;YAAQC,OAAM;sBAAiB;;IAIvD;IAEA,qBACE,KAACpC;QAAM+B,SAAS;QAAKC,IAAI;YAAES,OAAO;YAAQR,IAAI;QAAE;kBAC9C,cAAA,KAAC/B;YACCwC,MAAMH,WAAWI,GAAG,CAAC,CAACC;gBACpB,MAAMC,UAAUD,IAAIE,OAAO;gBAC3B,OAAO;oBACLC,OAAOH,IAAIG,KAAK;oBAChBD,uBACE,KAACD;wBACCG,OAAOtB;wBACPN,UAAU,CAAC6B;4BACT7B,SAAS6B;wBACX;;gBAGN;YACF;;;AAIR"}
1
+ {"version":3,"sources":["../../../../src/components/ColumnsEditor/EmbeddedPanelOptionsEditor.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 { CircularProgress, Stack, Typography } from '@mui/material';\nimport type { PanelPlugin } from '@perses-dev/plugin-system';\nimport { OptionsEditorTabs, useEvent, usePlugin } from '@perses-dev/plugin-system';\nimport type { UnknownSpec } from '@perses-dev/spec';\nimport merge from 'lodash/merge';\nimport type { ReactElement } from 'react';\nimport { useEffect, useMemo } from 'react';\n\nexport interface EmbeddedPanelOptionsEditorProps {\n kind: string;\n spec: UnknownSpec;\n onChange: (next: UnknownSpec) => void;\n}\n\nfunction isSpecEmpty(spec: UnknownSpec | undefined): boolean {\n if (spec === undefined || spec === null) return true;\n if (typeof spec !== 'object') return false;\n return Object.keys(spec as object).length === 0;\n}\n\nfunction mergeWithPluginDefaults(plugin: PanelPlugin, spec: UnknownSpec | undefined): UnknownSpec {\n const initial = plugin.createInitialOptions() ?? {};\n return merge({}, initial, spec ?? {}) as UnknownSpec;\n}\n\n/**\n * Renders a panel plugin's settings tabs (thresholds, units, colors, …).\n * Used for embedded GaugeChart columns only; other embedded panel kinds use defaults.\n */\nexport function EmbeddedPanelOptionsEditor({ kind, spec, onChange }: EmbeddedPanelOptionsEditorProps): ReactElement {\n const { data: plugin, isLoading, isError, error } = usePlugin('Panel', kind);\n\n const panelPlugin = plugin as PanelPlugin | undefined;\n\n const mergedSpec = useMemo(() => {\n if (!panelPlugin) {\n return spec;\n }\n return mergeWithPluginDefaults(panelPlugin, spec);\n }, [panelPlugin, spec]);\n\n const handleChange = useEvent(onChange);\n\n // Persist plugin defaults when the column still has an empty spec (e.g. after switching panel kind).\n useEffect(() => {\n if (!panelPlugin || !isSpecEmpty(spec)) {\n return;\n }\n handleChange(mergeWithPluginDefaults(panelPlugin, spec));\n }, [panelPlugin, spec, handleChange]);\n\n if (isLoading) {\n return (\n <Stack direction=\"row\" alignItems=\"center\" spacing={1} sx={{ py: 1 }}>\n <CircularProgress size={22} />\n <Typography variant=\"body2\" color=\"text.secondary\">\n Loading panel settings…\n </Typography>\n </Stack>\n );\n }\n\n if (isError || !plugin) {\n return (\n <Typography variant=\"body2\" color=\"error\">\n {error?.message ?? 'Could not load panel plugin.'}\n </Typography>\n );\n }\n\n const loadedPlugin = plugin as PanelPlugin;\n const editorTabs = loadedPlugin.panelOptionsEditorComponents ?? [];\n\n if (editorTabs.length === 0) {\n return (\n <Typography variant=\"body2\" color=\"text.secondary\">\n This visualization has no editable settings.\n </Typography>\n );\n }\n\n return (\n <Stack spacing={2.5} sx={{ width: '100%', py: 1 }}>\n <OptionsEditorTabs\n tabs={editorTabs.map((tab) => {\n const Content = tab.content;\n return {\n label: tab.label,\n content: (\n <Content\n value={mergedSpec}\n onChange={(next) => {\n onChange(next as UnknownSpec);\n }}\n />\n ),\n };\n })}\n />\n </Stack>\n );\n}\n"],"names":["CircularProgress","Stack","Typography","OptionsEditorTabs","useEvent","usePlugin","merge","useEffect","useMemo","isSpecEmpty","spec","undefined","Object","keys","length","mergeWithPluginDefaults","plugin","initial","createInitialOptions","EmbeddedPanelOptionsEditor","kind","onChange","data","isLoading","isError","error","panelPlugin","mergedSpec","handleChange","direction","alignItems","spacing","sx","py","size","variant","color","message","loadedPlugin","editorTabs","panelOptionsEditorComponents","width","tabs","map","tab","Content","content","label","value","next"],"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,gBAAgB,EAAEC,KAAK,EAAEC,UAAU,QAAQ,gBAAgB;AAEpE,SAASC,iBAAiB,EAAEC,QAAQ,EAAEC,SAAS,QAAQ,4BAA4B;AAEnF,OAAOC,WAAW,eAAe;AAEjC,SAASC,SAAS,EAAEC,OAAO,QAAQ,QAAQ;AAQ3C,SAASC,YAAYC,IAA6B;IAChD,IAAIA,SAASC,aAAaD,SAAS,MAAM,OAAO;IAChD,IAAI,OAAOA,SAAS,UAAU,OAAO;IACrC,OAAOE,OAAOC,IAAI,CAACH,MAAgBI,MAAM,KAAK;AAChD;AAEA,SAASC,wBAAwBC,MAAmB,EAAEN,IAA6B;IACjF,MAAMO,UAAUD,OAAOE,oBAAoB,MAAM,CAAC;IAClD,OAAOZ,MAAM,CAAC,GAAGW,SAASP,QAAQ,CAAC;AACrC;AAEA;;;CAGC,GACD,OAAO,SAASS,2BAA2B,EAAEC,IAAI,EAAEV,IAAI,EAAEW,QAAQ,EAAmC;IAClG,MAAM,EAAEC,MAAMN,MAAM,EAAEO,SAAS,EAAEC,OAAO,EAAEC,KAAK,EAAE,GAAGpB,UAAU,SAASe;IAEvE,MAAMM,cAAcV;IAEpB,MAAMW,aAAanB,QAAQ;QACzB,IAAI,CAACkB,aAAa;YAChB,OAAOhB;QACT;QACA,OAAOK,wBAAwBW,aAAahB;IAC9C,GAAG;QAACgB;QAAahB;KAAK;IAEtB,MAAMkB,eAAexB,SAASiB;IAE9B,qGAAqG;IACrGd,UAAU;QACR,IAAI,CAACmB,eAAe,CAACjB,YAAYC,OAAO;YACtC;QACF;QACAkB,aAAab,wBAAwBW,aAAahB;IACpD,GAAG;QAACgB;QAAahB;QAAMkB;KAAa;IAEpC,IAAIL,WAAW;QACb,qBACE,MAACtB;YAAM4B,WAAU;YAAMC,YAAW;YAASC,SAAS;YAAGC,IAAI;gBAAEC,IAAI;YAAE;;8BACjE,KAACjC;oBAAiBkC,MAAM;;8BACxB,KAAChC;oBAAWiC,SAAQ;oBAAQC,OAAM;8BAAiB;;;;IAKzD;IAEA,IAAIZ,WAAW,CAACR,QAAQ;QACtB,qBACE,KAACd;YAAWiC,SAAQ;YAAQC,OAAM;sBAC/BX,OAAOY,WAAW;;IAGzB;IAEA,MAAMC,eAAetB;IACrB,MAAMuB,aAAaD,aAAaE,4BAA4B,IAAI,EAAE;IAElE,IAAID,WAAWzB,MAAM,KAAK,GAAG;QAC3B,qBACE,KAACZ;YAAWiC,SAAQ;YAAQC,OAAM;sBAAiB;;IAIvD;IAEA,qBACE,KAACnC;QAAM8B,SAAS;QAAKC,IAAI;YAAES,OAAO;YAAQR,IAAI;QAAE;kBAC9C,cAAA,KAAC9B;YACCuC,MAAMH,WAAWI,GAAG,CAAC,CAACC;gBACpB,MAAMC,UAAUD,IAAIE,OAAO;gBAC3B,OAAO;oBACLC,OAAOH,IAAIG,KAAK;oBAChBD,uBACE,KAACD;wBACCG,OAAOrB;wBACPN,UAAU,CAAC4B;4BACT5B,SAAS4B;wBACX;;gBAGN;YACF;;;AAIR"}
@@ -1 +1 @@
1
- {"version":3,"file":"TablePanel.d.ts","sourceRoot":"","sources":["../../../src/components/TablePanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAA4B,UAAU,EAAoB,MAAM,2BAA2B,CAAC;AAExG,OAAO,KAAK,EAAE,QAAQ,EAAiB,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAI1C,OAAO,KAAK,EAAgC,YAAY,EAAE,MAAM,WAAW,CAAC;AAsV5E,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,YAAY,GAAG;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,CAK3F;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,YAAY,EAAE,cAAc,GAAG,QAAQ,CAAC,CAAC;AAE7E,wBAAgB,UAAU,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CAilBrG"}
1
+ {"version":3,"file":"TablePanel.d.ts","sourceRoot":"","sources":["../../../src/components/TablePanel.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAA4B,UAAU,EAAoB,MAAM,2BAA2B,CAAC;AAExG,OAAO,KAAK,EAAE,QAAQ,EAAiB,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAEhF,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,OAAO,CAAC;AAI1C,OAAO,KAAK,EAAgC,YAAY,EAAE,MAAM,WAAW,CAAC;AAwV5E,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,YAAY,GAAG;IAAE,IAAI,EAAE,SAAS,GAAG,OAAO,CAAA;CAAE,CAK3F;AAED,MAAM,MAAM,UAAU,GAAG,UAAU,CAAC,YAAY,EAAE,cAAc,GAAG,QAAQ,CAAC,CAAC;AAE7E,wBAAgB,UAAU,CAAC,EAAE,iBAAiB,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,UAAU,GAAG,YAAY,GAAG,IAAI,CA4lBrG"}
@@ -15,7 +15,7 @@ import { Box, Typography, useTheme } from "@mui/material";
15
15
  import { formatValue, Table, transformData, useSelection } from "@perses-dev/components";
16
16
  import { useSelectionItemActions } from "@perses-dev/dashboards";
17
17
  import { CalculationsMap, replaceVariablesInString, useAllVariableValues } from "@perses-dev/plugin-system";
18
- import { useCallback, useEffect, useMemo, useRef, useState } from "react";
18
+ import { useCallback, useEffect, useLayoutEffect, useMemo, useRef, useState } from "react";
19
19
  import { createPortal } from "react-dom";
20
20
  import { evaluateConditionalFormatting } from "../models/index.js";
21
21
  import { buildRawTableData, getTablePanelQueryMode } from "../table-data-utils.js";
@@ -387,7 +387,7 @@ export function TablePanel({ contentDimensions, spec, queryResults }) {
387
387
  });
388
388
  const filteredDataRef = useRef([]);
389
389
  // Refs used to keep the filter row in sync with the table's horizontal
390
- const panelContainerRef = useRef(null);
390
+ const [panelContainer, setPanelContainer] = useState(null);
391
391
  const filterRowInnerRef = useRef(null);
392
392
  const filterCellRefs = useRef([]);
393
393
  // Convert selectionMap to TanStack's RowSelectionState format
@@ -665,10 +665,10 @@ export function TablePanel({ contentDimensions, spec, queryResults }) {
665
665
  });
666
666
  setOpenFilterColumn(columnId);
667
667
  };
668
- const handleFilterClose = ()=>{
668
+ const handleFilterClose = useCallback(()=>{
669
669
  setFilterAnchorEl({});
670
670
  setOpenFilterColumn(null);
671
- };
671
+ }, []);
672
672
  // Close filter when clicking outside
673
673
  useEffect(()=>{
674
674
  if (!openFilterColumn) return;
@@ -686,34 +686,33 @@ export function TablePanel({ contentDimensions, spec, queryResults }) {
686
686
  document.removeEventListener('click', handleClick);
687
687
  };
688
688
  }, [
689
- openFilterColumn
689
+ openFilterColumn,
690
+ handleFilterClose
690
691
  ]);
691
692
  // Keep ref in sync with filtered data for use in selection handler
692
- filteredDataRef.current = filteredData;
693
+ useLayoutEffect(()=>{
694
+ filteredDataRef.current = filteredData;
695
+ }, [
696
+ filteredData
697
+ ]);
693
698
  const [pagination, setPagination] = useState(spec.pagination ? {
694
699
  pageIndex: 0,
695
700
  pageSize: 10
696
701
  } : undefined);
697
- useEffect(()=>{
698
- // If the pagination setting changes from no pagination to pagination, but the pagination state is undefined, update the pagination state
699
- if (spec.pagination && !pagination) {
700
- setPagination({
701
- pageIndex: 0,
702
- pageSize: 10
703
- });
704
- } else if (!spec.pagination && pagination) {
705
- setPagination(undefined);
706
- }
707
- }, [
708
- spec.pagination,
709
- pagination
710
- ]);
702
+ if (spec.pagination && !pagination) {
703
+ setPagination({
704
+ pageIndex: 0,
705
+ pageSize: 10
706
+ });
707
+ } else if (!spec.pagination && pagination) {
708
+ setPagination(undefined);
709
+ }
711
710
  // Sync the filter row's horizontal position with the table scroll.
712
711
  useEffect(()=>{
713
712
  if (!spec.enableFiltering) {
714
713
  return;
715
714
  }
716
- const scrollContainer = panelContainerRef.current?.querySelector('.MuiTableContainer-root');
715
+ const scrollContainer = panelContainer?.querySelector('.MuiTableContainer-root');
717
716
  const filterRowInner = filterRowInnerRef.current;
718
717
  if (!scrollContainer || !filterRowInner) {
719
718
  return;
@@ -723,7 +722,7 @@ export function TablePanel({ contentDimensions, spec, queryResults }) {
723
722
  setOpenFilterColumn((current)=>current === null ? current : null);
724
723
  setFilterAnchorEl((current)=>Object.keys(current).length === 0 ? current : {});
725
724
  };
726
- syncFilterRowScroll();
725
+ filterRowInner.style.transform = `translateX(-${scrollContainer.scrollLeft}px)`;
727
726
  scrollContainer.addEventListener('scroll', syncFilterRowScroll, {
728
727
  passive: true
729
728
  });
@@ -731,23 +730,18 @@ export function TablePanel({ contentDimensions, spec, queryResults }) {
731
730
  scrollContainer.removeEventListener('scroll', syncFilterRowScroll);
732
731
  };
733
732
  }, [
734
- spec.enableFiltering,
735
- columns,
736
- contentDimensions
733
+ panelContainer,
734
+ spec.enableFiltering
737
735
  ]);
738
736
  // Sync filter cell widths with the actual rendered table column widths to keep them aligned.
739
737
  useEffect(()=>{
740
738
  if (!spec.enableFiltering) {
741
739
  return;
742
740
  }
743
- const scrollContainer = panelContainerRef.current?.querySelector('.MuiTableContainer-root');
741
+ const scrollContainer = panelContainer?.querySelector('.MuiTableContainer-root');
744
742
  if (!scrollContainer) {
745
743
  return;
746
744
  }
747
- const headerRow = scrollContainer.querySelector('thead tr');
748
- if (!headerRow) {
749
- return;
750
- }
751
745
  const syncColumnWidths = ()=>{
752
746
  const headerCells = scrollContainer.querySelectorAll('thead tr th');
753
747
  columns.forEach((_, idx)=>{
@@ -762,21 +756,39 @@ export function TablePanel({ contentDimensions, spec, queryResults }) {
762
756
  filterCell.style.maxWidth = width;
763
757
  });
764
758
  };
765
- syncColumnWidths();
766
- // Re-sync whenever the header row's size changes
759
+ // Re-sync whenever the header row's size changes.
767
760
  const resizeObserver = new ResizeObserver(syncColumnWidths);
768
- if (headerRow) {
769
- resizeObserver.observe(headerRow);
770
- }
761
+ // The virtualized table remounts its header row when its width, rows, or columns change,
762
+ // so follow the current header row instead of observing a detached one.
763
+ let headerRow = null;
764
+ const observeHeaderRow = ()=>{
765
+ const currentHeaderRow = scrollContainer.querySelector('thead tr');
766
+ if (currentHeaderRow === headerRow) {
767
+ return;
768
+ }
769
+ if (headerRow) {
770
+ resizeObserver.unobserve(headerRow);
771
+ }
772
+ headerRow = currentHeaderRow;
773
+ if (headerRow) {
774
+ resizeObserver.observe(headerRow);
775
+ }
776
+ syncColumnWidths();
777
+ };
778
+ observeHeaderRow();
779
+ const mutationObserver = new MutationObserver(observeHeaderRow);
780
+ mutationObserver.observe(scrollContainer, {
781
+ childList: true,
782
+ subtree: true
783
+ });
771
784
  return ()=>{
785
+ mutationObserver.disconnect();
772
786
  resizeObserver.disconnect();
773
787
  };
774
788
  }, [
789
+ panelContainer,
775
790
  spec.enableFiltering,
776
- columns,
777
- contentDimensions,
778
- selectionEnabled,
779
- actionButtons
791
+ columns
780
792
  ]);
781
793
  if (contentDimensions === undefined) {
782
794
  return null;
@@ -795,7 +807,7 @@ export function TablePanel({ contentDimensions, spec, queryResults }) {
795
807
  });
796
808
  }
797
809
  return /*#__PURE__*/ _jsxs("div", {
798
- ref: panelContainerRef,
810
+ ref: setPanelContainer,
799
811
  style: {
800
812
  display: 'contents'
801
813
  },