@perses-dev/dashboards 0.0.0-snapshot-panel-actions-520389b → 0.0.0-snapshot-ts-panel-actions-90e9ef0

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.
@@ -9,11 +9,6 @@ export interface PanelProps extends CardProps<'section'> {
9
9
  editHandlers?: PanelHeaderProps['editHandlers'];
10
10
  panelOptions?: PanelOptions;
11
11
  panelGroupItemId?: PanelGroupItemId;
12
- /**
13
- * Project name to use for the panel. If not provided, will attempt to extract from URL.
14
- * @default extracted from URL or 'unknown-project'
15
- */
16
- projectName?: string;
17
12
  }
18
13
  export type PanelOptions = {
19
14
  /**
@@ -1 +1 @@
1
- {"version":3,"file":"Panel.d.ts","sourceRoot":"","sources":["../../../src/components/Panel/Panel.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAqB,SAAS,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAA2B,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAe,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE9D,MAAM,WAAW,UAAW,SAAQ,SAAS,CAAC,SAAS,CAAC;IACtD,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,CAAC,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;IACpC;;;OAGG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC;CACtB;AAED,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;OAEG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;OAEG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AA8CF;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,kDAyHhB,CAAC"}
1
+ {"version":3,"file":"Panel.d.ts","sourceRoot":"","sources":["../../../src/components/Panel/Panel.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAqB,SAAS,EAAE,MAAM,eAAe,CAAC;AAE7D,OAAO,EAAE,eAAe,EAAE,MAAM,kBAAkB,CAAC;AAEnD,OAAO,EAAE,SAAS,EAA2B,MAAM,OAAO,CAAC;AAE3D,OAAO,EAAE,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAEjD,OAAO,EAAe,gBAAgB,EAAE,MAAM,eAAe,CAAC;AAE9D,MAAM,WAAW,UAAW,SAAQ,SAAS,CAAC,SAAS,CAAC;IACtD,UAAU,EAAE,eAAe,CAAC;IAC5B,YAAY,CAAC,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,gBAAgB,CAAC,cAAc,CAAC,CAAC;IAChD,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC;AAED,MAAM,MAAM,YAAY,GAAG;IACzB;;;OAGG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;OAGG;IACH,KAAK,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,SAAS,CAAC;CAC/C,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B;;OAEG;IACH,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC;;OAEG;IACH,gBAAgB,CAAC,EAAE,gBAAgB,CAAC;CACrC,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,KAAK,kDAmGhB,CAAC"}
@@ -18,39 +18,6 @@ import { memo, useMemo, useState } from 'react';
18
18
  import useResizeObserver from 'use-resize-observer';
19
19
  import { PanelContent } from './PanelContent';
20
20
  import { PanelHeader } from './PanelHeader';
21
- // Function to extract project name from URL (kept as fallback)
22
- const extractProjectNameFromUrl = ()=>{
23
- try {
24
- if (process.env.NODE_ENV === 'test') {
25
- return 'test-project';
26
- }
27
- if (typeof window !== 'undefined' && (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')) {
28
- const urlPath = window.location.pathname;
29
- if (urlPath === '/' || urlPath === '') {
30
- return 'dev-project';
31
- }
32
- }
33
- const urlPath = window.location.pathname;
34
- // Split the path and look for the project name after "/projects/"
35
- const pathSegments = urlPath.split('/').filter((segment)=>segment.length > 0);
36
- const projectsIndex = pathSegments.findIndex((segment)=>segment === 'projects');
37
- if (projectsIndex !== -1 && projectsIndex + 1 < pathSegments.length) {
38
- const projectName = pathSegments[projectsIndex + 1];
39
- if (projectName && projectName.trim().length > 0) {
40
- return projectName;
41
- }
42
- }
43
- // Fallback: try to extract from URL parameters
44
- const urlParams = new URLSearchParams(window.location.search);
45
- const projectParam = urlParams.get('project');
46
- if (projectParam && projectParam.trim().length > 0) {
47
- return projectParam;
48
- }
49
- return 'unknown-project';
50
- } catch {
51
- return 'unknown-project';
52
- }
53
- };
54
21
  /**
55
22
  * Renders a PanelDefinition's content inside of a Card.
56
23
  *
@@ -59,7 +26,7 @@ const extractProjectNameFromUrl = ()=>{
59
26
  * <PanelContent> // renders loading, error or panel based on the queries' status
60
27
  * <PanelPluginLoader> // loads a panel plugin from the plugin registry and renders the PanelComponent with data from props.queryResults
61
28
  */ export const Panel = /*#__PURE__*/ memo(function Panel(props) {
62
- const { definition, readHandlers, editHandlers, onMouseEnter, onMouseLeave, sx, panelOptions, panelGroupItemId, projectName: providedProjectName, ...others } = props;
29
+ const { definition, readHandlers, editHandlers, onMouseEnter, onMouseLeave, sx, panelOptions, panelGroupItemId, ...others } = props;
63
30
  // Make sure we have an ID we can use for aria attributes
64
31
  const generatedPanelId = useId('Panel');
65
32
  const headerId = `${generatedPanelId}-header`;
@@ -79,29 +46,6 @@ const extractProjectNameFromUrl = ()=>{
79
46
  ]);
80
47
  const chartsTheme = useChartsTheme();
81
48
  const { queryResults } = useDataQueriesContext();
82
- // Use provided project name or extract from URL as fallback
83
- const projectName = useMemo(()=>{
84
- return providedProjectName || extractProjectNameFromUrl();
85
- }, [
86
- providedProjectName
87
- ]);
88
- const panelPropsForActions = useMemo(()=>{
89
- return {
90
- spec: definition.spec.plugin.spec,
91
- queryResults: queryResults.map((query)=>({
92
- definition: query.definition,
93
- data: query.data
94
- })),
95
- contentDimensions,
96
- definition,
97
- projectName
98
- };
99
- }, [
100
- definition,
101
- contentDimensions,
102
- queryResults,
103
- projectName
104
- ]);
105
49
  const handleMouseEnter = (e)=>{
106
50
  onMouseEnter?.(e);
107
51
  };
@@ -136,12 +80,9 @@ const extractProjectNameFromUrl = ()=>{
136
80
  title: definition.spec.display.name,
137
81
  description: definition.spec.display.description,
138
82
  queryResults: queryResults,
139
- panelPluginKind: definition.spec.plugin.kind,
140
83
  readHandlers: readHandlers,
141
84
  editHandlers: editHandlers,
142
85
  links: definition.spec.links,
143
- projectName: projectName,
144
- panelProps: panelPropsForActions,
145
86
  sx: {
146
87
  paddingX: `${chartsTheme.container.padding.default}px`
147
88
  }
@@ -163,7 +104,8 @@ const extractProjectNameFromUrl = ()=>{
163
104
  children: /*#__PURE__*/ _jsx(ErrorBoundary, {
164
105
  FallbackComponent: ErrorAlert,
165
106
  resetKeys: [
166
- definition.spec
107
+ definition.spec,
108
+ queryResults
167
109
  ],
168
110
  children: /*#__PURE__*/ _jsx(PanelContent, {
169
111
  definition: definition,
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Panel/Panel.tsx"],"sourcesContent":["// Copyright 2023 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 { Card, CardContent, CardProps } from '@mui/material';\nimport { ErrorAlert, ErrorBoundary, combineSx, useChartsTheme, useId } from '@perses-dev/components';\nimport { PanelDefinition } from '@perses-dev/core';\nimport { useDataQueriesContext } from '@perses-dev/plugin-system';\nimport { ReactNode, memo, useMemo, useState } from 'react';\nimport useResizeObserver from 'use-resize-observer';\nimport { PanelGroupItemId } from '../../context';\nimport { PanelContent } from './PanelContent';\nimport { PanelHeader, PanelHeaderProps } from './PanelHeader';\n\nexport interface PanelProps extends CardProps<'section'> {\n definition: PanelDefinition;\n readHandlers?: PanelHeaderProps['readHandlers'];\n editHandlers?: PanelHeaderProps['editHandlers'];\n panelOptions?: PanelOptions;\n panelGroupItemId?: PanelGroupItemId;\n /**\n * Project name to use for the panel. If not provided, will attempt to extract from URL.\n * @default extracted from URL or 'unknown-project'\n */\n projectName?: string;\n}\n\nexport type PanelOptions = {\n /**\n * Allow you to hide the panel header if desired.\n * This can be useful in embedded mode for example.\n */\n hideHeader?: boolean;\n /**\n * Content to render in right of the panel header. (top right of the panel)\n * It will only be rendered when the panel is in edit mode.\n */\n extra?: (props: PanelExtraProps) => ReactNode;\n};\n\nexport type PanelExtraProps = {\n /**\n * The PanelDefinition for the panel.\n */\n panelDefinition?: PanelDefinition;\n /**\n * The PanelGroupItemId for the panel.\n */\n panelGroupItemId?: PanelGroupItemId;\n};\n\n// Function to extract project name from URL (kept as fallback)\nconst extractProjectNameFromUrl = (): string => {\n try {\n if (process.env.NODE_ENV === 'test') {\n return 'test-project';\n }\n\n if (\n typeof window !== 'undefined' &&\n (window.location.hostname === 'localhost' || window.location.hostname === '127.0.0.1')\n ) {\n const urlPath = window.location.pathname;\n\n if (urlPath === '/' || urlPath === '') {\n return 'dev-project';\n }\n }\n\n const urlPath = window.location.pathname;\n\n // Split the path and look for the project name after \"/projects/\"\n const pathSegments = urlPath.split('/').filter((segment) => segment.length > 0);\n const projectsIndex = pathSegments.findIndex((segment) => segment === 'projects');\n\n if (projectsIndex !== -1 && projectsIndex + 1 < pathSegments.length) {\n const projectName = pathSegments[projectsIndex + 1];\n if (projectName && projectName.trim().length > 0) {\n return projectName;\n }\n }\n\n // Fallback: try to extract from URL parameters\n const urlParams = new URLSearchParams(window.location.search);\n const projectParam = urlParams.get('project');\n if (projectParam && projectParam.trim().length > 0) {\n return projectParam;\n }\n\n return 'unknown-project';\n } catch {\n return 'unknown-project';\n }\n};\n\n/**\n * Renders a PanelDefinition's content inside of a Card.\n *\n * Internal structure:\n * <Panel> // renders an entire panel, incl. header and action buttons\n * <PanelContent> // renders loading, error or panel based on the queries' status\n * <PanelPluginLoader> // loads a panel plugin from the plugin registry and renders the PanelComponent with data from props.queryResults\n */\nexport const Panel = memo(function Panel(props: PanelProps) {\n const {\n definition,\n readHandlers,\n editHandlers,\n onMouseEnter,\n onMouseLeave,\n sx,\n panelOptions,\n panelGroupItemId,\n projectName: providedProjectName,\n ...others\n } = props;\n\n // Make sure we have an ID we can use for aria attributes\n const generatedPanelId = useId('Panel');\n const headerId = `${generatedPanelId}-header`;\n\n const [contentElement, setContentElement] = useState<HTMLElement | null>(null);\n\n const { width, height } = useResizeObserver({ ref: contentElement });\n\n const contentDimensions = useMemo(() => {\n if (width === undefined || height === undefined) return undefined;\n return { width, height };\n }, [width, height]);\n\n const chartsTheme = useChartsTheme();\n\n const { queryResults } = useDataQueriesContext();\n\n // Use provided project name or extract from URL as fallback\n const projectName = useMemo(() => {\n return providedProjectName || extractProjectNameFromUrl();\n }, [providedProjectName]);\n\n const panelPropsForActions = useMemo(() => {\n return {\n spec: definition.spec.plugin.spec,\n queryResults: queryResults.map((query) => ({\n definition: query.definition,\n data: query.data,\n })),\n contentDimensions,\n definition,\n projectName,\n };\n }, [definition, contentDimensions, queryResults, projectName]);\n\n const handleMouseEnter: CardProps['onMouseEnter'] = (e) => {\n onMouseEnter?.(e);\n };\n\n const handleMouseLeave: CardProps['onMouseLeave'] = (e) => {\n onMouseLeave?.(e);\n };\n\n return (\n <Card\n component=\"section\"\n sx={combineSx(\n {\n width: '100%',\n height: '100%',\n display: 'flex',\n flexFlow: 'column nowrap',\n ':hover': { '--panel-hover': 'block' },\n },\n sx\n )}\n variant=\"outlined\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n aria-labelledby={headerId}\n aria-describedby={headerId}\n data-testid=\"panel\"\n {...others}\n >\n {!panelOptions?.hideHeader && (\n <PanelHeader\n extra={panelOptions?.extra?.({ panelDefinition: definition, panelGroupItemId })}\n id={headerId}\n title={definition.spec.display.name}\n description={definition.spec.display.description}\n queryResults={queryResults}\n panelPluginKind={definition.spec.plugin.kind}\n readHandlers={readHandlers}\n editHandlers={editHandlers}\n links={definition.spec.links}\n projectName={projectName}\n panelProps={panelPropsForActions}\n sx={{ paddingX: `${chartsTheme.container.padding.default}px` }}\n />\n )}\n <CardContent\n component=\"figure\"\n sx={{\n position: 'relative',\n overflow: 'hidden',\n flexGrow: 1,\n margin: 0,\n padding: 0,\n // Override MUI default style for last-child\n ':last-child': {\n padding: 0,\n },\n }}\n ref={setContentElement}\n >\n <ErrorBoundary FallbackComponent={ErrorAlert} resetKeys={[definition.spec]}>\n <PanelContent\n definition={definition}\n panelPluginKind={definition.spec.plugin.kind}\n spec={definition.spec.plugin.spec}\n contentDimensions={contentDimensions}\n queryResults={queryResults}\n />\n </ErrorBoundary>\n </CardContent>\n </Card>\n );\n});\n"],"names":["Card","CardContent","ErrorAlert","ErrorBoundary","combineSx","useChartsTheme","useId","useDataQueriesContext","memo","useMemo","useState","useResizeObserver","PanelContent","PanelHeader","extractProjectNameFromUrl","process","env","NODE_ENV","window","location","hostname","urlPath","pathname","pathSegments","split","filter","segment","length","projectsIndex","findIndex","projectName","trim","urlParams","URLSearchParams","search","projectParam","get","Panel","props","definition","readHandlers","editHandlers","onMouseEnter","onMouseLeave","sx","panelOptions","panelGroupItemId","providedProjectName","others","generatedPanelId","headerId","contentElement","setContentElement","width","height","ref","contentDimensions","undefined","chartsTheme","queryResults","panelPropsForActions","spec","plugin","map","query","data","handleMouseEnter","e","handleMouseLeave","component","display","flexFlow","variant","aria-labelledby","aria-describedby","data-testid","hideHeader","extra","panelDefinition","id","title","name","description","panelPluginKind","kind","links","panelProps","paddingX","container","padding","default","position","overflow","flexGrow","margin","FallbackComponent","resetKeys"],"mappings":"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"}
1
+ {"version":3,"sources":["../../../src/components/Panel/Panel.tsx"],"sourcesContent":["// Copyright 2023 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 { Card, CardContent, CardProps } from '@mui/material';\nimport { ErrorAlert, ErrorBoundary, combineSx, useChartsTheme, useId } from '@perses-dev/components';\nimport { PanelDefinition } from '@perses-dev/core';\nimport { useDataQueriesContext } from '@perses-dev/plugin-system';\nimport { ReactNode, memo, useMemo, useState } from 'react';\nimport useResizeObserver from 'use-resize-observer';\nimport { PanelGroupItemId } from '../../context';\nimport { PanelContent } from './PanelContent';\nimport { PanelHeader, PanelHeaderProps } from './PanelHeader';\n\nexport interface PanelProps extends CardProps<'section'> {\n definition: PanelDefinition;\n readHandlers?: PanelHeaderProps['readHandlers'];\n editHandlers?: PanelHeaderProps['editHandlers'];\n panelOptions?: PanelOptions;\n panelGroupItemId?: PanelGroupItemId;\n}\n\nexport type PanelOptions = {\n /**\n * Allow you to hide the panel header if desired.\n * This can be useful in embedded mode for example.\n */\n hideHeader?: boolean;\n /**\n * Content to render in right of the panel header. (top right of the panel)\n * It will only be rendered when the panel is in edit mode.\n */\n extra?: (props: PanelExtraProps) => ReactNode;\n};\n\nexport type PanelExtraProps = {\n /**\n * The PanelDefinition for the panel.\n */\n panelDefinition?: PanelDefinition;\n /**\n * The PanelGroupItemId for the panel.\n */\n panelGroupItemId?: PanelGroupItemId;\n};\n\n/**\n * Renders a PanelDefinition's content inside of a Card.\n *\n * Internal structure:\n * <Panel> // renders an entire panel, incl. header and action buttons\n * <PanelContent> // renders loading, error or panel based on the queries' status\n * <PanelPluginLoader> // loads a panel plugin from the plugin registry and renders the PanelComponent with data from props.queryResults\n */\nexport const Panel = memo(function Panel(props: PanelProps) {\n const {\n definition,\n readHandlers,\n editHandlers,\n onMouseEnter,\n onMouseLeave,\n sx,\n panelOptions,\n panelGroupItemId,\n ...others\n } = props;\n\n // Make sure we have an ID we can use for aria attributes\n const generatedPanelId = useId('Panel');\n const headerId = `${generatedPanelId}-header`;\n\n const [contentElement, setContentElement] = useState<HTMLElement | null>(null);\n\n const { width, height } = useResizeObserver({ ref: contentElement });\n\n const contentDimensions = useMemo(() => {\n if (width === undefined || height === undefined) return undefined;\n return { width, height };\n }, [width, height]);\n\n const chartsTheme = useChartsTheme();\n\n const { queryResults } = useDataQueriesContext();\n\n const handleMouseEnter: CardProps['onMouseEnter'] = (e) => {\n onMouseEnter?.(e);\n };\n\n const handleMouseLeave: CardProps['onMouseLeave'] = (e) => {\n onMouseLeave?.(e);\n };\n\n return (\n <Card\n component=\"section\"\n sx={combineSx(\n {\n width: '100%',\n height: '100%',\n display: 'flex',\n flexFlow: 'column nowrap',\n ':hover': { '--panel-hover': 'block' },\n },\n sx\n )}\n variant=\"outlined\"\n onMouseEnter={handleMouseEnter}\n onMouseLeave={handleMouseLeave}\n aria-labelledby={headerId}\n aria-describedby={headerId}\n data-testid=\"panel\"\n {...others}\n >\n {!panelOptions?.hideHeader && (\n <PanelHeader\n extra={panelOptions?.extra?.({ panelDefinition: definition, panelGroupItemId })}\n id={headerId}\n title={definition.spec.display.name}\n description={definition.spec.display.description}\n queryResults={queryResults}\n readHandlers={readHandlers}\n editHandlers={editHandlers}\n links={definition.spec.links}\n sx={{ paddingX: `${chartsTheme.container.padding.default}px` }}\n />\n )}\n <CardContent\n component=\"figure\"\n sx={{\n position: 'relative',\n overflow: 'hidden',\n flexGrow: 1,\n margin: 0,\n padding: 0,\n // Override MUI default style for last-child\n ':last-child': {\n padding: 0,\n },\n }}\n ref={setContentElement}\n >\n <ErrorBoundary FallbackComponent={ErrorAlert} resetKeys={[definition.spec, queryResults]}>\n <PanelContent\n definition={definition}\n panelPluginKind={definition.spec.plugin.kind}\n spec={definition.spec.plugin.spec}\n contentDimensions={contentDimensions}\n queryResults={queryResults}\n />\n </ErrorBoundary>\n </CardContent>\n </Card>\n );\n});\n"],"names":["Card","CardContent","ErrorAlert","ErrorBoundary","combineSx","useChartsTheme","useId","useDataQueriesContext","memo","useMemo","useState","useResizeObserver","PanelContent","PanelHeader","Panel","props","definition","readHandlers","editHandlers","onMouseEnter","onMouseLeave","sx","panelOptions","panelGroupItemId","others","generatedPanelId","headerId","contentElement","setContentElement","width","height","ref","contentDimensions","undefined","chartsTheme","queryResults","handleMouseEnter","e","handleMouseLeave","component","display","flexFlow","variant","aria-labelledby","aria-describedby","data-testid","hideHeader","extra","panelDefinition","id","title","spec","name","description","links","paddingX","container","padding","default","position","overflow","flexGrow","margin","FallbackComponent","resetKeys","panelPluginKind","plugin","kind"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAASA,IAAI,EAAEC,WAAW,QAAmB,gBAAgB;AAC7D,SAASC,UAAU,EAAEC,aAAa,EAAEC,SAAS,EAAEC,cAAc,EAAEC,KAAK,QAAQ,yBAAyB;AAErG,SAASC,qBAAqB,QAAQ,4BAA4B;AAClE,SAAoBC,IAAI,EAAEC,OAAO,EAAEC,QAAQ,QAAQ,QAAQ;AAC3D,OAAOC,uBAAuB,sBAAsB;AAEpD,SAASC,YAAY,QAAQ,iBAAiB;AAC9C,SAASC,WAAW,QAA0B,gBAAgB;AAkC9D;;;;;;;CAOC,GACD,OAAO,MAAMC,sBAAQN,KAAK,SAASM,MAAMC,KAAiB;IACxD,MAAM,EACJC,UAAU,EACVC,YAAY,EACZC,YAAY,EACZC,YAAY,EACZC,YAAY,EACZC,EAAE,EACFC,YAAY,EACZC,gBAAgB,EAChB,GAAGC,QACJ,GAAGT;IAEJ,yDAAyD;IACzD,MAAMU,mBAAmBnB,MAAM;IAC/B,MAAMoB,WAAW,GAAGD,iBAAiB,OAAO,CAAC;IAE7C,MAAM,CAACE,gBAAgBC,kBAAkB,GAAGlB,SAA6B;IAEzE,MAAM,EAAEmB,KAAK,EAAEC,MAAM,EAAE,GAAGnB,kBAAkB;QAAEoB,KAAKJ;IAAe;IAElE,MAAMK,oBAAoBvB,QAAQ;QAChC,IAAIoB,UAAUI,aAAaH,WAAWG,WAAW,OAAOA;QACxD,OAAO;YAAEJ;YAAOC;QAAO;IACzB,GAAG;QAACD;QAAOC;KAAO;IAElB,MAAMI,cAAc7B;IAEpB,MAAM,EAAE8B,YAAY,EAAE,GAAG5B;IAEzB,MAAM6B,mBAA8C,CAACC;QACnDlB,eAAekB;IACjB;IAEA,MAAMC,mBAA8C,CAACD;QACnDjB,eAAeiB;IACjB;IAEA,qBACE,MAACrC;QACCuC,WAAU;QACVlB,IAAIjB,UACF;YACEyB,OAAO;YACPC,QAAQ;YACRU,SAAS;YACTC,UAAU;YACV,UAAU;gBAAE,iBAAiB;YAAQ;QACvC,GACApB;QAEFqB,SAAQ;QACRvB,cAAciB;QACdhB,cAAckB;QACdK,mBAAiBjB;QACjBkB,oBAAkBlB;QAClBmB,eAAY;QACX,GAAGrB,MAAM;;YAET,CAACF,cAAcwB,4BACd,KAACjC;gBACCkC,OAAOzB,cAAcyB,QAAQ;oBAAEC,iBAAiBhC;oBAAYO;gBAAiB;gBAC7E0B,IAAIvB;gBACJwB,OAAOlC,WAAWmC,IAAI,CAACX,OAAO,CAACY,IAAI;gBACnCC,aAAarC,WAAWmC,IAAI,CAACX,OAAO,CAACa,WAAW;gBAChDlB,cAAcA;gBACdlB,cAAcA;gBACdC,cAAcA;gBACdoC,OAAOtC,WAAWmC,IAAI,CAACG,KAAK;gBAC5BjC,IAAI;oBAAEkC,UAAU,GAAGrB,YAAYsB,SAAS,CAACC,OAAO,CAACC,OAAO,CAAC,EAAE,CAAC;gBAAC;;0BAGjE,KAACzD;gBACCsC,WAAU;gBACVlB,IAAI;oBACFsC,UAAU;oBACVC,UAAU;oBACVC,UAAU;oBACVC,QAAQ;oBACRL,SAAS;oBACT,4CAA4C;oBAC5C,eAAe;wBACbA,SAAS;oBACX;gBACF;gBACA1B,KAAKH;0BAEL,cAAA,KAACzB;oBAAc4D,mBAAmB7D;oBAAY8D,WAAW;wBAAChD,WAAWmC,IAAI;wBAAEhB;qBAAa;8BACtF,cAAA,KAACvB;wBACCI,YAAYA;wBACZiD,iBAAiBjD,WAAWmC,IAAI,CAACe,MAAM,CAACC,IAAI;wBAC5ChB,MAAMnC,WAAWmC,IAAI,CAACe,MAAM,CAACf,IAAI;wBACjCnB,mBAAmBA;wBACnBG,cAAcA;;;;;;AAM1B,GAAG"}
@@ -1,12 +1,12 @@
1
1
  /// <reference types="react" />
2
- import { Link, TimeSeriesData } from '@perses-dev/core';
2
+ import { QueryData } from '@perses-dev/plugin-system';
3
+ import { Link } from '@perses-dev/core';
3
4
  export interface PanelActionsProps {
4
5
  title: string;
5
6
  description?: string;
6
7
  descriptionTooltipId: string;
7
8
  links?: Link[];
8
9
  extra?: React.ReactNode;
9
- panelPluginKind: string;
10
10
  editHandlers?: {
11
11
  onEditPanelClick: () => void;
12
12
  onDuplicatePanelClick: () => void;
@@ -16,9 +16,7 @@ export interface PanelActionsProps {
16
16
  isPanelViewed?: boolean;
17
17
  onViewPanelClick: () => void;
18
18
  };
19
- queryResults: TimeSeriesData | undefined;
20
- projectName?: string;
21
- panelProps?: Record<string, unknown>;
19
+ queryResults: QueryData[];
22
20
  }
23
21
  export declare const PanelActions: React.FC<PanelActionsProps>;
24
22
  //# sourceMappingURL=PanelActions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PanelActions.d.ts","sourceRoot":"","sources":["../../../src/components/Panel/PanelActions.tsx"],"names":[],"mappings":";AA0BA,OAAO,EAAE,IAAI,EAAE,cAAc,EAAE,MAAM,kBAAkB,CAAC;AAWxD,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE;QACb,gBAAgB,EAAE,MAAM,IAAI,CAAC;QAC7B,qBAAqB,EAAE,MAAM,IAAI,CAAC;QAClC,kBAAkB,EAAE,MAAM,IAAI,CAAC;KAChC,CAAC;IACF,YAAY,CAAC,EAAE;QACb,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,gBAAgB,EAAE,MAAM,IAAI,CAAC;KAC9B,CAAC;IACF,YAAY,EAAE,cAAc,GAAG,SAAS,CAAC;IACzC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AASD,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CA6RpD,CAAC"}
1
+ {"version":3,"file":"PanelActions.d.ts","sourceRoot":"","sources":["../../../src/components/Panel/PanelActions.tsx"],"names":[],"mappings":";AAuBA,OAAO,EAAE,SAAS,EAAE,MAAM,2BAA2B,CAAC;AAGtD,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AAWxC,MAAM,WAAW,iBAAiB;IAChC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,oBAAoB,EAAE,MAAM,CAAC;IAC7B,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB,YAAY,CAAC,EAAE;QACb,gBAAgB,EAAE,MAAM,IAAI,CAAC;QAC7B,qBAAqB,EAAE,MAAM,IAAI,CAAC;QAClC,kBAAkB,EAAE,MAAM,IAAI,CAAC;KAChC,CAAC;IACF,YAAY,CAAC,EAAE;QACb,aAAa,CAAC,EAAE,OAAO,CAAC;QACxB,gBAAgB,EAAE,MAAM,IAAI,CAAC;KAC9B,CAAC;IACF,YAAY,EAAE,SAAS,EAAE,CAAC;CAC3B;AASD,eAAO,MAAM,YAAY,EAAE,KAAK,CAAC,EAAE,CAAC,iBAAiB,CAqMpD,CAAC"}
@@ -12,9 +12,8 @@
12
12
  // limitations under the License.
13
13
  import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
14
14
  import { Stack, Box, Popover, CircularProgress, styled } from '@mui/material';
15
- import { isValidElement, useMemo, useState, useEffect } from 'react';
15
+ import { isValidElement, useMemo, useState } from 'react';
16
16
  import { InfoTooltip } from '@perses-dev/components';
17
- import { usePluginRegistry, useDataQueriesContext } from '@perses-dev/plugin-system';
18
17
  import ArrowCollapseIcon from 'mdi-material-ui/ArrowCollapse';
19
18
  import ArrowExpandIcon from 'mdi-material-ui/ArrowExpand';
20
19
  import PencilIcon from 'mdi-material-ui/PencilOutline';
@@ -33,72 +32,7 @@ const ConditionalBox = styled(Box)({
33
32
  flexGrow: 1,
34
33
  justifyContent: 'flex-end'
35
34
  });
36
- export const PanelActions = ({ editHandlers, readHandlers, extra, title, description, descriptionTooltipId, links, queryResults, panelPluginKind, projectName: _propsProjectName, panelProps })=>{
37
- const { isFetching, errors } = useDataQueriesContext();
38
- const { getPlugin } = usePluginRegistry();
39
- const [pluginActions, setPluginActions] = useState([]);
40
- useEffect(()=>{
41
- let cancelled = false;
42
- const loadPluginActions = async ()=>{
43
- if (!panelPluginKind || !panelProps) {
44
- if (!cancelled) {
45
- setPluginActions([]);
46
- }
47
- return;
48
- }
49
- try {
50
- // Add defensive check for getPlugin availability
51
- if (!getPlugin || typeof getPlugin !== 'function') {
52
- if (!cancelled) {
53
- setPluginActions([]);
54
- }
55
- return;
56
- }
57
- const plugin = await getPlugin('Panel', panelPluginKind);
58
- if (cancelled) return;
59
- // More defensive checking for plugin and actions
60
- if (!plugin || typeof plugin !== 'object' || !plugin.actions || !Array.isArray(plugin.actions) || plugin.actions.length === 0) {
61
- if (!cancelled) {
62
- setPluginActions([]);
63
- }
64
- return;
65
- }
66
- // Render plugin actions in header location
67
- const headerActions = plugin.actions.filter((action)=>!action.location || action.location === 'header').map((action, index)=>{
68
- const ActionComponent = action.component;
69
- try {
70
- // eslint-disable-next-line @typescript-eslint/no-explicit-any
71
- return /*#__PURE__*/ _jsx(ActionComponent, {
72
- ...panelProps
73
- }, `plugin-action-${index}`);
74
- } catch (error) {
75
- console.warn(`Failed to render plugin action ${index}:`, error);
76
- return null;
77
- }
78
- }).filter((item)=>Boolean(item));
79
- if (!cancelled) {
80
- setPluginActions(headerActions);
81
- }
82
- } catch (error) {
83
- if (!cancelled) {
84
- console.warn('Failed to load plugin actions:', error);
85
- setPluginActions([]);
86
- }
87
- }
88
- };
89
- // Use setTimeout to defer the async operation to the next tick
90
- const timeoutId = setTimeout(()=>{
91
- loadPluginActions();
92
- }, 0);
93
- return ()=>{
94
- cancelled = true;
95
- clearTimeout(timeoutId);
96
- };
97
- }, [
98
- panelPluginKind,
99
- panelProps,
100
- getPlugin
101
- ]);
35
+ export const PanelActions = ({ editHandlers, readHandlers, extra, title, description, descriptionTooltipId, links, queryResults })=>{
102
36
  const descriptionAction = useMemo(()=>{
103
37
  if (description && description.trim().length > 0) {
104
38
  return /*#__PURE__*/ _jsx(InfoTooltip, {
@@ -129,23 +63,19 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
129
63
  });
130
64
  const extraActions = editHandlers === undefined && extra;
131
65
  const queryStateIndicator = useMemo(()=>{
132
- const hasData = queryResults && queryResults.series && queryResults.series.length > 0;
66
+ const hasData = queryResults.some((q)=>q.data);
67
+ const isFetching = queryResults.some((q)=>q.isFetching);
68
+ const queryErrors = queryResults.filter((q)=>q.error);
133
69
  if (isFetching && hasData) {
70
+ // If the panel has no data, the panel content will show the loading overlay.
71
+ // Therefore, show the circular loading indicator only in case the panel doesn't display the loading overlay already.
134
72
  return /*#__PURE__*/ _jsx(CircularProgress, {
135
73
  "aria-label": "loading",
136
74
  size: "1.125rem"
137
75
  });
138
- }
139
- const validErrors = (errors || []).filter((error)=>error !== null);
140
- if (validErrors.length > 0) {
141
- const errorTexts = validErrors.map((e)=>{
142
- if (typeof e === 'string') return e;
143
- if (e && typeof e === 'object') {
144
- const errorObj = e;
145
- return errorObj.message ?? errorObj.toString?.() ?? 'Unknown error';
146
- }
147
- return 'Unknown error';
148
- }).join('\n');
76
+ } else if (queryErrors.length > 0) {
77
+ const errorTexts = queryErrors.map((q)=>q.error).map((e)=>e?.message ?? e?.toString() ?? 'Unknown error') // eslint-disable-line @typescript-eslint/no-explicit-any
78
+ .join('\n');
149
79
  return /*#__PURE__*/ _jsx(InfoTooltip, {
150
80
  description: errorTexts,
151
81
  children: /*#__PURE__*/ _jsx(HeaderIconButton, {
@@ -158,9 +88,7 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
158
88
  });
159
89
  }
160
90
  }, [
161
- queryResults,
162
- isFetching,
163
- errors
91
+ queryResults
164
92
  ]);
165
93
  const readActions = useMemo(()=>{
166
94
  if (readHandlers !== undefined) {
@@ -185,6 +113,7 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
185
113
  ]);
186
114
  const editActions = useMemo(()=>{
187
115
  if (editHandlers !== undefined) {
116
+ // If there are edit handlers, always just show the edit buttons
188
117
  return /*#__PURE__*/ _jsxs(_Fragment, {
189
118
  children: [
190
119
  /*#__PURE__*/ _jsx(InfoTooltip, {
@@ -207,6 +136,8 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
207
136
  children: /*#__PURE__*/ _jsx(ContentCopyIcon, {
208
137
  fontSize: "inherit",
209
138
  sx: {
139
+ // Shrink this icon a little bit to look more consistent
140
+ // with the other icons in the header.
210
141
  transform: 'scale(0.925)'
211
142
  }
212
143
  })
@@ -296,7 +227,6 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
296
227
  editActions
297
228
  ]
298
229
  }),
299
- pluginActions,
300
230
  moveAction
301
231
  ]
302
232
  })
@@ -324,7 +254,6 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
324
254
  extraActions,
325
255
  " ",
326
256
  readActions,
327
- pluginActions,
328
257
  /*#__PURE__*/ _jsx(OverflowMenu, {
329
258
  title: title,
330
259
  children: editActions
@@ -336,6 +265,7 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
336
265
  }),
337
266
  /*#__PURE__*/ _jsxs(ConditionalBox, {
338
267
  sx: (theme)=>({
268
+ // flip the logic here; if the browser (or jsdom) does not support container queries, always show all icons
339
269
  display: 'flex',
340
270
  [theme.containerQueries(HEADER_ACTIONS_CONTAINER_NAME).down(HEADER_MEDIUM_WIDTH)]: {
341
271
  display: 'none'
@@ -357,7 +287,7 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
357
287
  extraActions,
358
288
  " ",
359
289
  readActions,
360
- pluginActions,
290
+ " ",
361
291
  editActions,
362
292
  " ",
363
293
  moveAction
@@ -370,7 +300,7 @@ export const PanelActions = ({ editHandlers, readHandlers, extra, title, descrip
370
300
  };
371
301
  const OverflowMenu = ({ children, title })=>{
372
302
  const [anchorPosition, setAnchorPosition] = useState();
373
- // do not show overflow menu if there is no content
303
+ // do not show overflow menu if there is no content (for example, edit actions are hidden)
374
304
  const hasContent = /*#__PURE__*/ isValidElement(children) || Array.isArray(children) && children.some(isValidElement);
375
305
  if (!hasContent) {
376
306
  return undefined;
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../src/components/Panel/PanelActions.tsx"],"sourcesContent":["// Copyright 2025 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 { Stack, Box, Popover, CircularProgress, styled, PopoverPosition } from '@mui/material';\nimport { isValidElement, PropsWithChildren, ReactNode, useMemo, useState, useEffect, ReactElement } from 'react';\nimport { InfoTooltip } from '@perses-dev/components';\nimport { usePluginRegistry, useDataQueriesContext } from '@perses-dev/plugin-system';\nimport ArrowCollapseIcon from 'mdi-material-ui/ArrowCollapse';\nimport ArrowExpandIcon from 'mdi-material-ui/ArrowExpand';\nimport PencilIcon from 'mdi-material-ui/PencilOutline';\nimport DeleteIcon from 'mdi-material-ui/DeleteOutline';\nimport DragIcon from 'mdi-material-ui/DragVertical';\nimport ContentCopyIcon from 'mdi-material-ui/ContentCopy';\nimport MenuIcon from 'mdi-material-ui/Menu';\nimport AlertIcon from 'mdi-material-ui/Alert';\nimport InformationOutlineIcon from 'mdi-material-ui/InformationOutline';\nimport { Link, TimeSeriesData } from '@perses-dev/core';\nimport {\n ARIA_LABEL_TEXT,\n HEADER_ACTIONS_CONTAINER_NAME,\n HEADER_MEDIUM_WIDTH,\n HEADER_SMALL_WIDTH,\n TOOLTIP_TEXT,\n} from '../../constants';\nimport { HeaderIconButton } from './HeaderIconButton';\nimport { PanelLinks } from './PanelLinks';\n\nexport interface PanelActionsProps {\n title: string;\n description?: string;\n descriptionTooltipId: string;\n links?: Link[];\n extra?: React.ReactNode;\n panelPluginKind: string;\n editHandlers?: {\n onEditPanelClick: () => void;\n onDuplicatePanelClick: () => void;\n onDeletePanelClick: () => void;\n };\n readHandlers?: {\n isPanelViewed?: boolean;\n onViewPanelClick: () => void;\n };\n queryResults: TimeSeriesData | undefined;\n projectName?: string;\n panelProps?: Record<string, unknown>;\n}\n\nconst ConditionalBox = styled(Box)({\n display: 'none',\n alignItems: 'center',\n flexGrow: 1,\n justifyContent: 'flex-end',\n});\n\nexport const PanelActions: React.FC<PanelActionsProps> = ({\n editHandlers,\n readHandlers,\n extra,\n title,\n description,\n descriptionTooltipId,\n links,\n queryResults,\n panelPluginKind,\n projectName: _propsProjectName,\n panelProps,\n}) => {\n const { isFetching, errors } = useDataQueriesContext();\n const { getPlugin } = usePluginRegistry();\n\n const [pluginActions, setPluginActions] = useState<ReactNode[]>([]);\n\n useEffect(() => {\n let cancelled = false;\n\n const loadPluginActions = async (): Promise<void> => {\n if (!panelPluginKind || !panelProps) {\n if (!cancelled) {\n setPluginActions([]);\n }\n return;\n }\n\n try {\n // Add defensive check for getPlugin availability\n if (!getPlugin || typeof getPlugin !== 'function') {\n if (!cancelled) {\n setPluginActions([]);\n }\n return;\n }\n\n const plugin = await getPlugin('Panel', panelPluginKind);\n\n if (cancelled) return;\n\n // More defensive checking for plugin and actions\n if (\n !plugin ||\n typeof plugin !== 'object' ||\n !plugin.actions ||\n !Array.isArray(plugin.actions) ||\n plugin.actions.length === 0\n ) {\n if (!cancelled) {\n setPluginActions([]);\n }\n return;\n }\n\n // Render plugin actions in header location\n const headerActions = plugin.actions\n .filter((action) => !action.location || action.location === 'header')\n .map((action, index): ReactNode | null => {\n const ActionComponent = action.component;\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n return <ActionComponent key={`plugin-action-${index}`} {...(panelProps as any)} />;\n } catch (error) {\n console.warn(`Failed to render plugin action ${index}:`, error);\n return null;\n }\n })\n .filter((item): item is ReactNode => Boolean(item));\n\n if (!cancelled) {\n setPluginActions(headerActions);\n }\n } catch (error) {\n if (!cancelled) {\n console.warn('Failed to load plugin actions:', error);\n setPluginActions([]);\n }\n }\n };\n\n // Use setTimeout to defer the async operation to the next tick\n const timeoutId = setTimeout(() => {\n loadPluginActions();\n }, 0);\n\n return (): void => {\n cancelled = true;\n clearTimeout(timeoutId);\n };\n }, [panelPluginKind, panelProps, getPlugin]);\n\n const descriptionAction = useMemo((): ReactNode | undefined => {\n if (description && description.trim().length > 0) {\n return (\n <InfoTooltip id={descriptionTooltipId} description={description} enterDelay={100}>\n <HeaderIconButton aria-label=\"panel description\" size=\"small\">\n <InformationOutlineIcon\n aria-describedby=\"info-tooltip\"\n aria-hidden={false}\n fontSize=\"inherit\"\n sx={{ color: (theme) => theme.palette.text.secondary }}\n />\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n return undefined;\n }, [descriptionTooltipId, description]);\n\n const linksAction = links && links.length > 0 && <PanelLinks links={links} />;\n const extraActions = editHandlers === undefined && extra;\n\n const queryStateIndicator = useMemo((): ReactNode | undefined => {\n const hasData = queryResults && queryResults.series && queryResults.series.length > 0;\n if (isFetching && hasData) {\n return <CircularProgress aria-label=\"loading\" size=\"1.125rem\" />;\n }\n const validErrors = (errors || []).filter((error) => error !== null);\n if (validErrors.length > 0) {\n const errorTexts = validErrors\n .map((e: unknown) => {\n if (typeof e === 'string') return e;\n if (e && typeof e === 'object') {\n const errorObj = e as { message?: string; toString?: () => string };\n return errorObj.message ?? errorObj.toString?.() ?? 'Unknown error';\n }\n return 'Unknown error';\n })\n .join('\\n');\n\n return (\n <InfoTooltip description={errorTexts}>\n <HeaderIconButton aria-label=\"panel errors\" size=\"small\">\n <AlertIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n }, [queryResults, isFetching, errors]);\n\n const readActions = useMemo((): ReactNode | undefined => {\n if (readHandlers !== undefined) {\n return (\n <InfoTooltip description={TOOLTIP_TEXT.viewPanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.viewPanel(title)}\n size=\"small\"\n onClick={readHandlers.onViewPanelClick}\n >\n {readHandlers.isPanelViewed ? (\n <ArrowCollapseIcon fontSize=\"inherit\" />\n ) : (\n <ArrowExpandIcon fontSize=\"inherit\" />\n )}\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n return undefined;\n }, [readHandlers, title]);\n\n const editActions = useMemo((): ReactNode | undefined => {\n if (editHandlers !== undefined) {\n return (\n <>\n <InfoTooltip description={TOOLTIP_TEXT.editPanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.editPanel(title)}\n size=\"small\"\n onClick={editHandlers.onEditPanelClick}\n >\n <PencilIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n <InfoTooltip description={TOOLTIP_TEXT.duplicatePanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.duplicatePanel(title)}\n size=\"small\"\n onClick={editHandlers.onDuplicatePanelClick}\n >\n <ContentCopyIcon\n fontSize=\"inherit\"\n sx={{\n transform: 'scale(0.925)',\n }}\n />\n </HeaderIconButton>\n </InfoTooltip>\n <InfoTooltip description={TOOLTIP_TEXT.deletePanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.deletePanel(title)}\n size=\"small\"\n onClick={editHandlers.onDeletePanelClick}\n >\n <DeleteIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n </>\n );\n }\n return undefined;\n }, [editHandlers, title]);\n\n const moveAction = useMemo((): ReactNode | undefined => {\n if (editActions && !readHandlers?.isPanelViewed) {\n return (\n <InfoTooltip description={TOOLTIP_TEXT.movePanel}>\n <HeaderIconButton aria-label={ARIA_LABEL_TEXT.movePanel(title)} size=\"small\">\n <DragIcon className=\"drag-handle\" sx={{ cursor: 'grab' }} fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n return undefined;\n }, [editActions, readHandlers, title]);\n\n const divider = <Box sx={{ flexGrow: 1 }}></Box>;\n\n // if the panel is in non-editing, non-fullscreen mode, show certain icons only on hover\n const OnHover = ({ children }: PropsWithChildren): ReactNode =>\n editHandlers === undefined && !readHandlers?.isPanelViewed ? (\n <Box sx={{ display: 'var(--panel-hover, none)' }}>{children}</Box>\n ) : (\n <>{children}</>\n );\n\n return (\n <>\n {/* small panel width: move all icons except move/grab to overflow menu */}\n <ConditionalBox\n sx={(theme) => ({\n [theme.containerQueries(HEADER_ACTIONS_CONTAINER_NAME).between(0, HEADER_SMALL_WIDTH)]: { display: 'flex' },\n })}\n >\n {divider}\n <OnHover>\n <OverflowMenu title={title}>\n {descriptionAction} {linksAction} {queryStateIndicator} {extraActions} {readActions} {editActions}\n </OverflowMenu>\n {pluginActions}\n {moveAction}\n </OnHover>\n </ConditionalBox>\n\n {/* medium panel width: move edit icons to overflow menu */}\n <ConditionalBox\n sx={(theme) => ({\n [theme.containerQueries(HEADER_ACTIONS_CONTAINER_NAME).between(HEADER_SMALL_WIDTH, HEADER_MEDIUM_WIDTH)]: {\n display: 'flex',\n },\n })}\n >\n <OnHover>\n {descriptionAction} {linksAction}\n </OnHover>\n {divider} {queryStateIndicator}\n <OnHover>\n {extraActions} {readActions}\n {pluginActions}\n <OverflowMenu title={title}>{editActions}</OverflowMenu>\n {moveAction}\n </OnHover>\n </ConditionalBox>\n\n {/* large panel width: show all icons in panel header */}\n <ConditionalBox\n sx={(theme) => ({\n display: 'flex',\n [theme.containerQueries(HEADER_ACTIONS_CONTAINER_NAME).down(HEADER_MEDIUM_WIDTH)]: { display: 'none' },\n })}\n >\n <OnHover>\n {descriptionAction} {linksAction}\n </OnHover>\n {divider} {queryStateIndicator}\n <OnHover>\n {extraActions} {readActions}\n {pluginActions}\n {editActions} {moveAction}\n </OnHover>\n </ConditionalBox>\n </>\n );\n};\n\nconst OverflowMenu: React.FC<PropsWithChildren<{ title: string }>> = ({\n children,\n title,\n}): ReactElement | undefined => {\n const [anchorPosition, setAnchorPosition] = useState<PopoverPosition>();\n\n // do not show overflow menu if there is no content\n const hasContent = isValidElement(children) || (Array.isArray(children) && children.some(isValidElement));\n if (!hasContent) {\n return undefined;\n }\n\n const handleClick = (event: React.MouseEvent<HTMLElement>): void => {\n setAnchorPosition(event.currentTarget.getBoundingClientRect());\n };\n\n const handleClose = (): void => {\n setAnchorPosition(undefined);\n };\n\n const open = Boolean(anchorPosition);\n const id = open ? 'actions-menu' : undefined;\n\n return (\n <>\n <HeaderIconButton\n className=\"show-actions\"\n aria-describedby={id}\n onClick={handleClick}\n aria-label={ARIA_LABEL_TEXT.showPanelActions(title)}\n size=\"small\"\n >\n <MenuIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n <Popover\n id={id}\n open={open}\n anchorReference=\"anchorPosition\"\n anchorPosition={anchorPosition}\n onClose={handleClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left',\n }}\n >\n <Stack direction=\"row\" alignItems=\"center\" sx={{ padding: 1 }} onClick={handleClose}>\n {children}\n </Stack>\n </Popover>\n </>\n );\n};\n"],"names":["Stack","Box","Popover","CircularProgress","styled","isValidElement","useMemo","useState","useEffect","InfoTooltip","usePluginRegistry","useDataQueriesContext","ArrowCollapseIcon","ArrowExpandIcon","PencilIcon","DeleteIcon","DragIcon","ContentCopyIcon","MenuIcon","AlertIcon","InformationOutlineIcon","ARIA_LABEL_TEXT","HEADER_ACTIONS_CONTAINER_NAME","HEADER_MEDIUM_WIDTH","HEADER_SMALL_WIDTH","TOOLTIP_TEXT","HeaderIconButton","PanelLinks","ConditionalBox","display","alignItems","flexGrow","justifyContent","PanelActions","editHandlers","readHandlers","extra","title","description","descriptionTooltipId","links","queryResults","panelPluginKind","projectName","_propsProjectName","panelProps","isFetching","errors","getPlugin","pluginActions","setPluginActions","cancelled","loadPluginActions","plugin","actions","Array","isArray","length","headerActions","filter","action","location","map","index","ActionComponent","component","error","console","warn","item","Boolean","timeoutId","setTimeout","clearTimeout","descriptionAction","trim","id","enterDelay","aria-label","size","aria-describedby","aria-hidden","fontSize","sx","color","theme","palette","text","secondary","undefined","linksAction","extraActions","queryStateIndicator","hasData","series","validErrors","errorTexts","e","errorObj","message","toString","join","readActions","viewPanel","onClick","onViewPanelClick","isPanelViewed","editActions","editPanel","onEditPanelClick","duplicatePanel","onDuplicatePanelClick","transform","deletePanel","onDeletePanelClick","moveAction","movePanel","className","cursor","divider","OnHover","children","containerQueries","between","OverflowMenu","down","anchorPosition","setAnchorPosition","hasContent","some","handleClick","event","currentTarget","getBoundingClientRect","handleClose","open","showPanelActions","anchorReference","onClose","anchorOrigin","vertical","horizontal","direction","padding"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAASA,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,gBAAgB,EAAEC,MAAM,QAAyB,gBAAgB;AAC/F,SAASC,cAAc,EAAgCC,OAAO,EAAEC,QAAQ,EAAEC,SAAS,QAAsB,QAAQ;AACjH,SAASC,WAAW,QAAQ,yBAAyB;AACrD,SAASC,iBAAiB,EAAEC,qBAAqB,QAAQ,4BAA4B;AACrF,OAAOC,uBAAuB,gCAAgC;AAC9D,OAAOC,qBAAqB,8BAA8B;AAC1D,OAAOC,gBAAgB,gCAAgC;AACvD,OAAOC,gBAAgB,gCAAgC;AACvD,OAAOC,cAAc,+BAA+B;AACpD,OAAOC,qBAAqB,8BAA8B;AAC1D,OAAOC,cAAc,uBAAuB;AAC5C,OAAOC,eAAe,wBAAwB;AAC9C,OAAOC,4BAA4B,qCAAqC;AAExE,SACEC,eAAe,EACfC,6BAA6B,EAC7BC,mBAAmB,EACnBC,kBAAkB,EAClBC,YAAY,QACP,kBAAkB;AACzB,SAASC,gBAAgB,QAAQ,qBAAqB;AACtD,SAASC,UAAU,QAAQ,eAAe;AAuB1C,MAAMC,iBAAiBxB,OAAOH,KAAK;IACjC4B,SAAS;IACTC,YAAY;IACZC,UAAU;IACVC,gBAAgB;AAClB;AAEA,OAAO,MAAMC,eAA4C,CAAC,EACxDC,YAAY,EACZC,YAAY,EACZC,KAAK,EACLC,KAAK,EACLC,WAAW,EACXC,oBAAoB,EACpBC,KAAK,EACLC,YAAY,EACZC,eAAe,EACfC,aAAaC,iBAAiB,EAC9BC,UAAU,EACX;IACC,MAAM,EAAEC,UAAU,EAAEC,MAAM,EAAE,GAAGpC;IAC/B,MAAM,EAAEqC,SAAS,EAAE,GAAGtC;IAEtB,MAAM,CAACuC,eAAeC,iBAAiB,GAAG3C,SAAsB,EAAE;IAElEC,UAAU;QACR,IAAI2C,YAAY;QAEhB,MAAMC,oBAAoB;YACxB,IAAI,CAACV,mBAAmB,CAACG,YAAY;gBACnC,IAAI,CAACM,WAAW;oBACdD,iBAAiB,EAAE;gBACrB;gBACA;YACF;YAEA,IAAI;gBACF,iDAAiD;gBACjD,IAAI,CAACF,aAAa,OAAOA,cAAc,YAAY;oBACjD,IAAI,CAACG,WAAW;wBACdD,iBAAiB,EAAE;oBACrB;oBACA;gBACF;gBAEA,MAAMG,SAAS,MAAML,UAAU,SAASN;gBAExC,IAAIS,WAAW;gBAEf,iDAAiD;gBACjD,IACE,CAACE,UACD,OAAOA,WAAW,YAClB,CAACA,OAAOC,OAAO,IACf,CAACC,MAAMC,OAAO,CAACH,OAAOC,OAAO,KAC7BD,OAAOC,OAAO,CAACG,MAAM,KAAK,GAC1B;oBACA,IAAI,CAACN,WAAW;wBACdD,iBAAiB,EAAE;oBACrB;oBACA;gBACF;gBAEA,2CAA2C;gBAC3C,MAAMQ,gBAAgBL,OAAOC,OAAO,CACjCK,MAAM,CAAC,CAACC,SAAW,CAACA,OAAOC,QAAQ,IAAID,OAAOC,QAAQ,KAAK,UAC3DC,GAAG,CAAC,CAACF,QAAQG;oBACZ,MAAMC,kBAAkBJ,OAAOK,SAAS;oBACxC,IAAI;wBACF,8DAA8D;wBAC9D,qBAAO,KAACD;4BAAgD,GAAInB,UAAU;2BAAzC,CAAC,cAAc,EAAEkB,OAAO;oBACvD,EAAE,OAAOG,OAAO;wBACdC,QAAQC,IAAI,CAAC,CAAC,+BAA+B,EAAEL,MAAM,CAAC,CAAC,EAAEG;wBACzD,OAAO;oBACT;gBACF,GACCP,MAAM,CAAC,CAACU,OAA4BC,QAAQD;gBAE/C,IAAI,CAAClB,WAAW;oBACdD,iBAAiBQ;gBACnB;YACF,EAAE,OAAOQ,OAAO;gBACd,IAAI,CAACf,WAAW;oBACdgB,QAAQC,IAAI,CAAC,kCAAkCF;oBAC/ChB,iBAAiB,EAAE;gBACrB;YACF;QACF;QAEA,+DAA+D;QAC/D,MAAMqB,YAAYC,WAAW;YAC3BpB;QACF,GAAG;QAEH,OAAO;YACLD,YAAY;YACZsB,aAAaF;QACf;IACF,GAAG;QAAC7B;QAAiBG;QAAYG;KAAU;IAE3C,MAAM0B,oBAAoBpE,QAAQ;QAChC,IAAIgC,eAAeA,YAAYqC,IAAI,GAAGlB,MAAM,GAAG,GAAG;YAChD,qBACE,KAAChD;gBAAYmE,IAAIrC;gBAAsBD,aAAaA;gBAAauC,YAAY;0BAC3E,cAAA,KAACnD;oBAAiBoD,cAAW;oBAAoBC,MAAK;8BACpD,cAAA,KAAC3D;wBACC4D,oBAAiB;wBACjBC,eAAa;wBACbC,UAAS;wBACTC,IAAI;4BAAEC,OAAO,CAACC,QAAUA,MAAMC,OAAO,CAACC,IAAI,CAACC,SAAS;wBAAC;;;;QAK/D;QACA,OAAOC;IACT,GAAG;QAAClD;QAAsBD;KAAY;IAEtC,MAAMoD,cAAclD,SAASA,MAAMiB,MAAM,GAAG,mBAAK,KAAC9B;QAAWa,OAAOA;;IACpE,MAAMmD,eAAezD,iBAAiBuD,aAAarD;IAEnD,MAAMwD,sBAAsBtF,QAAQ;QAClC,MAAMuF,UAAUpD,gBAAgBA,aAAaqD,MAAM,IAAIrD,aAAaqD,MAAM,CAACrC,MAAM,GAAG;QACpF,IAAIX,cAAc+C,SAAS;YACzB,qBAAO,KAAC1F;gBAAiB2E,cAAW;gBAAUC,MAAK;;QACrD;QACA,MAAMgB,cAAc,AAAChD,CAAAA,UAAU,EAAE,AAAD,EAAGY,MAAM,CAAC,CAACO,QAAUA,UAAU;QAC/D,IAAI6B,YAAYtC,MAAM,GAAG,GAAG;YAC1B,MAAMuC,aAAaD,YAChBjC,GAAG,CAAC,CAACmC;gBACJ,IAAI,OAAOA,MAAM,UAAU,OAAOA;gBAClC,IAAIA,KAAK,OAAOA,MAAM,UAAU;oBAC9B,MAAMC,WAAWD;oBACjB,OAAOC,SAASC,OAAO,IAAID,SAASE,QAAQ,QAAQ;gBACtD;gBACA,OAAO;YACT,GACCC,IAAI,CAAC;YAER,qBACE,KAAC5F;gBAAY6B,aAAa0D;0BACxB,cAAA,KAACtE;oBAAiBoD,cAAW;oBAAeC,MAAK;8BAC/C,cAAA,KAAC5D;wBAAU+D,UAAS;;;;QAI5B;IACF,GAAG;QAACzC;QAAcK;QAAYC;KAAO;IAErC,MAAMuD,cAAchG,QAAQ;QAC1B,IAAI6B,iBAAiBsD,WAAW;YAC9B,qBACE,KAAChF;gBAAY6B,aAAab,aAAa8E,SAAS;0BAC9C,cAAA,KAAC7E;oBACCoD,cAAYzD,gBAAgBkF,SAAS,CAAClE;oBACtC0C,MAAK;oBACLyB,SAASrE,aAAasE,gBAAgB;8BAErCtE,aAAauE,aAAa,iBACzB,KAAC9F;wBAAkBsE,UAAS;uCAE5B,KAACrE;wBAAgBqE,UAAS;;;;QAKpC;QACA,OAAOO;IACT,GAAG;QAACtD;QAAcE;KAAM;IAExB,MAAMsE,cAAcrG,QAAQ;QAC1B,IAAI4B,iBAAiBuD,WAAW;YAC9B,qBACE;;kCACE,KAAChF;wBAAY6B,aAAab,aAAamF,SAAS;kCAC9C,cAAA,KAAClF;4BACCoD,cAAYzD,gBAAgBuF,SAAS,CAACvE;4BACtC0C,MAAK;4BACLyB,SAAStE,aAAa2E,gBAAgB;sCAEtC,cAAA,KAAC/F;gCAAWoE,UAAS;;;;kCAGzB,KAACzE;wBAAY6B,aAAab,aAAaqF,cAAc;kCACnD,cAAA,KAACpF;4BACCoD,cAAYzD,gBAAgByF,cAAc,CAACzE;4BAC3C0C,MAAK;4BACLyB,SAAStE,aAAa6E,qBAAqB;sCAE3C,cAAA,KAAC9F;gCACCiE,UAAS;gCACTC,IAAI;oCACF6B,WAAW;gCACb;;;;kCAIN,KAACvG;wBAAY6B,aAAab,aAAawF,WAAW;kCAChD,cAAA,KAACvF;4BACCoD,cAAYzD,gBAAgB4F,WAAW,CAAC5E;4BACxC0C,MAAK;4BACLyB,SAAStE,aAAagF,kBAAkB;sCAExC,cAAA,KAACnG;gCAAWmE,UAAS;;;;;;QAK/B;QACA,OAAOO;IACT,GAAG;QAACvD;QAAcG;KAAM;IAExB,MAAM8E,aAAa7G,QAAQ;QACzB,IAAIqG,eAAe,CAACxE,cAAcuE,eAAe;YAC/C,qBACE,KAACjG;gBAAY6B,aAAab,aAAa2F,SAAS;0BAC9C,cAAA,KAAC1F;oBAAiBoD,cAAYzD,gBAAgB+F,SAAS,CAAC/E;oBAAQ0C,MAAK;8BACnE,cAAA,KAAC/D;wBAASqG,WAAU;wBAAclC,IAAI;4BAAEmC,QAAQ;wBAAO;wBAAGpC,UAAS;;;;QAI3E;QACA,OAAOO;IACT,GAAG;QAACkB;QAAaxE;QAAcE;KAAM;IAErC,MAAMkF,wBAAU,KAACtH;QAAIkF,IAAI;YAAEpD,UAAU;QAAE;;IAEvC,wFAAwF;IACxF,MAAMyF,UAAU,CAAC,EAAEC,QAAQ,EAAqB,GAC9CvF,iBAAiBuD,aAAa,CAACtD,cAAcuE,8BAC3C,KAACzG;YAAIkF,IAAI;gBAAEtD,SAAS;YAA2B;sBAAI4F;2BAEnD;sBAAGA;;IAGP,qBACE;;0BAEE,MAAC7F;gBACCuD,IAAI,CAACE,QAAW,CAAA;wBACd,CAACA,MAAMqC,gBAAgB,CAACpG,+BAA+BqG,OAAO,CAAC,GAAGnG,oBAAoB,EAAE;4BAAEK,SAAS;wBAAO;oBAC5G,CAAA;;oBAEC0F;kCACD,MAACC;;0CACC,MAACI;gCAAavF,OAAOA;;oCAClBqC;oCAAkB;oCAAEgB;oCAAY;oCAAEE;oCAAoB;oCAAED;oCAAa;oCAAEW;oCAAY;oCAAEK;;;4BAEvF1D;4BACAkE;;;;;0BAKL,MAACvF;gBACCuD,IAAI,CAACE,QAAW,CAAA;wBACd,CAACA,MAAMqC,gBAAgB,CAACpG,+BAA+BqG,OAAO,CAACnG,oBAAoBD,qBAAqB,EAAE;4BACxGM,SAAS;wBACX;oBACF,CAAA;;kCAEA,MAAC2F;;4BACE9C;4BAAkB;4BAAEgB;;;oBAEtB6B;oBAAQ;oBAAE3B;kCACX,MAAC4B;;4BACE7B;4BAAa;4BAAEW;4BACfrD;0CACD,KAAC2E;gCAAavF,OAAOA;0CAAQsE;;4BAC5BQ;;;;;0BAKL,MAACvF;gBACCuD,IAAI,CAACE,QAAW,CAAA;wBACdxD,SAAS;wBACT,CAACwD,MAAMqC,gBAAgB,CAACpG,+BAA+BuG,IAAI,CAACtG,qBAAqB,EAAE;4BAAEM,SAAS;wBAAO;oBACvG,CAAA;;kCAEA,MAAC2F;;4BACE9C;4BAAkB;4BAAEgB;;;oBAEtB6B;oBAAQ;oBAAE3B;kCACX,MAAC4B;;4BACE7B;4BAAa;4BAAEW;4BACfrD;4BACA0D;4BAAY;4BAAEQ;;;;;;;AAKzB,EAAE;AAEF,MAAMS,eAA+D,CAAC,EACpEH,QAAQ,EACRpF,KAAK,EACN;IACC,MAAM,CAACyF,gBAAgBC,kBAAkB,GAAGxH;IAE5C,mDAAmD;IACnD,MAAMyH,2BAAa3H,eAAeoH,aAAclE,MAAMC,OAAO,CAACiE,aAAaA,SAASQ,IAAI,CAAC5H;IACzF,IAAI,CAAC2H,YAAY;QACf,OAAOvC;IACT;IAEA,MAAMyC,cAAc,CAACC;QACnBJ,kBAAkBI,MAAMC,aAAa,CAACC,qBAAqB;IAC7D;IAEA,MAAMC,cAAc;QAClBP,kBAAkBtC;IACpB;IAEA,MAAM8C,OAAOjE,QAAQwD;IACrB,MAAMlD,KAAK2D,OAAO,iBAAiB9C;IAEnC,qBACE;;0BACE,KAAC/D;gBACC2F,WAAU;gBACVrC,oBAAkBJ;gBAClB4B,SAAS0B;gBACTpD,cAAYzD,gBAAgBmH,gBAAgB,CAACnG;gBAC7C0C,MAAK;0BAEL,cAAA,KAAC7D;oBAASgE,UAAS;;;0BAErB,KAAChF;gBACC0E,IAAIA;gBACJ2D,MAAMA;gBACNE,iBAAgB;gBAChBX,gBAAgBA;gBAChBY,SAASJ;gBACTK,cAAc;oBACZC,UAAU;oBACVC,YAAY;gBACd;0BAEA,cAAA,KAAC7I;oBAAM8I,WAAU;oBAAMhH,YAAW;oBAASqD,IAAI;wBAAE4D,SAAS;oBAAE;oBAAGvC,SAAS8B;8BACrEb;;;;;AAKX"}
1
+ {"version":3,"sources":["../../../src/components/Panel/PanelActions.tsx"],"sourcesContent":["// Copyright 2025 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 { Stack, Box, Popover, CircularProgress, styled, PopoverPosition } from '@mui/material';\nimport { isValidElement, PropsWithChildren, ReactNode, useMemo, useState } from 'react';\nimport { InfoTooltip } from '@perses-dev/components';\nimport ArrowCollapseIcon from 'mdi-material-ui/ArrowCollapse';\nimport ArrowExpandIcon from 'mdi-material-ui/ArrowExpand';\nimport PencilIcon from 'mdi-material-ui/PencilOutline';\nimport DeleteIcon from 'mdi-material-ui/DeleteOutline';\nimport DragIcon from 'mdi-material-ui/DragVertical';\nimport ContentCopyIcon from 'mdi-material-ui/ContentCopy';\nimport MenuIcon from 'mdi-material-ui/Menu';\nimport { QueryData } from '@perses-dev/plugin-system';\nimport AlertIcon from 'mdi-material-ui/Alert';\nimport InformationOutlineIcon from 'mdi-material-ui/InformationOutline';\nimport { Link } from '@perses-dev/core';\nimport {\n ARIA_LABEL_TEXT,\n HEADER_ACTIONS_CONTAINER_NAME,\n HEADER_MEDIUM_WIDTH,\n HEADER_SMALL_WIDTH,\n TOOLTIP_TEXT,\n} from '../../constants';\nimport { HeaderIconButton } from './HeaderIconButton';\nimport { PanelLinks } from './PanelLinks';\n\nexport interface PanelActionsProps {\n title: string;\n description?: string;\n descriptionTooltipId: string;\n links?: Link[];\n extra?: React.ReactNode;\n editHandlers?: {\n onEditPanelClick: () => void;\n onDuplicatePanelClick: () => void;\n onDeletePanelClick: () => void;\n };\n readHandlers?: {\n isPanelViewed?: boolean;\n onViewPanelClick: () => void;\n };\n queryResults: QueryData[];\n}\n\nconst ConditionalBox = styled(Box)({\n display: 'none',\n alignItems: 'center',\n flexGrow: 1,\n justifyContent: 'flex-end',\n});\n\nexport const PanelActions: React.FC<PanelActionsProps> = ({\n editHandlers,\n readHandlers,\n extra,\n title,\n description,\n descriptionTooltipId,\n links,\n queryResults,\n}) => {\n const descriptionAction = useMemo(() => {\n if (description && description.trim().length > 0) {\n return (\n <InfoTooltip id={descriptionTooltipId} description={description} enterDelay={100}>\n <HeaderIconButton aria-label=\"panel description\" size=\"small\">\n <InformationOutlineIcon\n aria-describedby=\"info-tooltip\"\n aria-hidden={false}\n fontSize=\"inherit\"\n sx={{ color: (theme) => theme.palette.text.secondary }}\n />\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n return undefined;\n }, [descriptionTooltipId, description]);\n const linksAction = links && links.length > 0 && <PanelLinks links={links} />;\n const extraActions = editHandlers === undefined && extra;\n\n const queryStateIndicator = useMemo(() => {\n const hasData = queryResults.some((q) => q.data);\n const isFetching = queryResults.some((q) => q.isFetching);\n const queryErrors = queryResults.filter((q) => q.error);\n if (isFetching && hasData) {\n // If the panel has no data, the panel content will show the loading overlay.\n // Therefore, show the circular loading indicator only in case the panel doesn't display the loading overlay already.\n return <CircularProgress aria-label=\"loading\" size=\"1.125rem\" />;\n } else if (queryErrors.length > 0) {\n const errorTexts = queryErrors\n .map((q) => q.error)\n .map((e: any) => e?.message ?? e?.toString() ?? 'Unknown error') // eslint-disable-line @typescript-eslint/no-explicit-any\n .join('\\n');\n\n return (\n <InfoTooltip description={errorTexts}>\n <HeaderIconButton aria-label=\"panel errors\" size=\"small\">\n <AlertIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n }, [queryResults]);\n\n const readActions = useMemo(() => {\n if (readHandlers !== undefined) {\n return (\n <InfoTooltip description={TOOLTIP_TEXT.viewPanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.viewPanel(title)}\n size=\"small\"\n onClick={readHandlers.onViewPanelClick}\n >\n {readHandlers.isPanelViewed ? (\n <ArrowCollapseIcon fontSize=\"inherit\" />\n ) : (\n <ArrowExpandIcon fontSize=\"inherit\" />\n )}\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n return undefined;\n }, [readHandlers, title]);\n\n const editActions = useMemo(() => {\n if (editHandlers !== undefined) {\n // If there are edit handlers, always just show the edit buttons\n return (\n <>\n <InfoTooltip description={TOOLTIP_TEXT.editPanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.editPanel(title)}\n size=\"small\"\n onClick={editHandlers.onEditPanelClick}\n >\n <PencilIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n <InfoTooltip description={TOOLTIP_TEXT.duplicatePanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.duplicatePanel(title)}\n size=\"small\"\n onClick={editHandlers.onDuplicatePanelClick}\n >\n <ContentCopyIcon\n fontSize=\"inherit\"\n sx={{\n // Shrink this icon a little bit to look more consistent\n // with the other icons in the header.\n transform: 'scale(0.925)',\n }}\n />\n </HeaderIconButton>\n </InfoTooltip>\n <InfoTooltip description={TOOLTIP_TEXT.deletePanel}>\n <HeaderIconButton\n aria-label={ARIA_LABEL_TEXT.deletePanel(title)}\n size=\"small\"\n onClick={editHandlers.onDeletePanelClick}\n >\n <DeleteIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n </>\n );\n }\n return undefined;\n }, [editHandlers, title]);\n\n const moveAction = useMemo(() => {\n if (editActions && !readHandlers?.isPanelViewed) {\n return (\n <InfoTooltip description={TOOLTIP_TEXT.movePanel}>\n <HeaderIconButton aria-label={ARIA_LABEL_TEXT.movePanel(title)} size=\"small\">\n <DragIcon className=\"drag-handle\" sx={{ cursor: 'grab' }} fontSize=\"inherit\" />\n </HeaderIconButton>\n </InfoTooltip>\n );\n }\n return undefined;\n }, [editActions, readHandlers, title]);\n\n const divider = <Box sx={{ flexGrow: 1 }}></Box>;\n\n // if the panel is in non-editing, non-fullscreen mode, show certain icons only on hover\n const OnHover = ({ children }: PropsWithChildren): ReactNode =>\n editHandlers === undefined && !readHandlers?.isPanelViewed ? (\n <Box sx={{ display: 'var(--panel-hover, none)' }}>{children}</Box>\n ) : (\n <>{children}</>\n );\n\n return (\n <>\n {/* small panel width: move all icons except move/grab to overflow menu */}\n <ConditionalBox\n sx={(theme) => ({\n [theme.containerQueries(HEADER_ACTIONS_CONTAINER_NAME).between(0, HEADER_SMALL_WIDTH)]: { display: 'flex' },\n })}\n >\n {divider}\n <OnHover>\n <OverflowMenu title={title}>\n {descriptionAction} {linksAction} {queryStateIndicator} {extraActions} {readActions} {editActions}\n </OverflowMenu>\n {moveAction}\n </OnHover>\n </ConditionalBox>\n\n {/* medium panel width: move edit icons to overflow menu */}\n <ConditionalBox\n sx={(theme) => ({\n [theme.containerQueries(HEADER_ACTIONS_CONTAINER_NAME).between(HEADER_SMALL_WIDTH, HEADER_MEDIUM_WIDTH)]: {\n display: 'flex',\n },\n })}\n >\n <OnHover>\n {descriptionAction} {linksAction}\n </OnHover>\n {divider} {queryStateIndicator}\n <OnHover>\n {extraActions} {readActions}\n <OverflowMenu title={title}>{editActions}</OverflowMenu>\n {moveAction}\n </OnHover>\n </ConditionalBox>\n\n {/* large panel width: show all icons in panel header */}\n <ConditionalBox\n sx={(theme) => ({\n // flip the logic here; if the browser (or jsdom) does not support container queries, always show all icons\n display: 'flex',\n [theme.containerQueries(HEADER_ACTIONS_CONTAINER_NAME).down(HEADER_MEDIUM_WIDTH)]: { display: 'none' },\n })}\n >\n <OnHover>\n {descriptionAction} {linksAction}\n </OnHover>\n {divider} {queryStateIndicator}\n <OnHover>\n {extraActions} {readActions} {editActions} {moveAction}\n </OnHover>\n </ConditionalBox>\n </>\n );\n};\n\nconst OverflowMenu: React.FC<PropsWithChildren<{ title: string }>> = ({ children, title }) => {\n const [anchorPosition, setAnchorPosition] = useState<PopoverPosition>();\n\n // do not show overflow menu if there is no content (for example, edit actions are hidden)\n const hasContent = isValidElement(children) || (Array.isArray(children) && children.some(isValidElement));\n if (!hasContent) {\n return undefined;\n }\n\n const handleClick = (event: React.MouseEvent<HTMLElement>): undefined => {\n setAnchorPosition(event.currentTarget.getBoundingClientRect());\n };\n\n const handleClose = (): undefined => {\n setAnchorPosition(undefined);\n };\n\n const open = Boolean(anchorPosition);\n const id = open ? 'actions-menu' : undefined;\n\n return (\n <>\n <HeaderIconButton\n className=\"show-actions\"\n aria-describedby={id}\n onClick={handleClick}\n aria-label={ARIA_LABEL_TEXT.showPanelActions(title)}\n size=\"small\"\n >\n <MenuIcon fontSize=\"inherit\" />\n </HeaderIconButton>\n <Popover\n id={id}\n open={open}\n anchorReference=\"anchorPosition\"\n anchorPosition={anchorPosition}\n onClose={handleClose}\n anchorOrigin={{\n vertical: 'bottom',\n horizontal: 'left',\n }}\n >\n <Stack direction=\"row\" alignItems=\"center\" sx={{ padding: 1 }} onClick={handleClose}>\n {children}\n </Stack>\n </Popover>\n </>\n );\n};\n"],"names":["Stack","Box","Popover","CircularProgress","styled","isValidElement","useMemo","useState","InfoTooltip","ArrowCollapseIcon","ArrowExpandIcon","PencilIcon","DeleteIcon","DragIcon","ContentCopyIcon","MenuIcon","AlertIcon","InformationOutlineIcon","ARIA_LABEL_TEXT","HEADER_ACTIONS_CONTAINER_NAME","HEADER_MEDIUM_WIDTH","HEADER_SMALL_WIDTH","TOOLTIP_TEXT","HeaderIconButton","PanelLinks","ConditionalBox","display","alignItems","flexGrow","justifyContent","PanelActions","editHandlers","readHandlers","extra","title","description","descriptionTooltipId","links","queryResults","descriptionAction","trim","length","id","enterDelay","aria-label","size","aria-describedby","aria-hidden","fontSize","sx","color","theme","palette","text","secondary","undefined","linksAction","extraActions","queryStateIndicator","hasData","some","q","data","isFetching","queryErrors","filter","error","errorTexts","map","e","message","toString","join","readActions","viewPanel","onClick","onViewPanelClick","isPanelViewed","editActions","editPanel","onEditPanelClick","duplicatePanel","onDuplicatePanelClick","transform","deletePanel","onDeletePanelClick","moveAction","movePanel","className","cursor","divider","OnHover","children","containerQueries","between","OverflowMenu","down","anchorPosition","setAnchorPosition","hasContent","Array","isArray","handleClick","event","currentTarget","getBoundingClientRect","handleClose","open","Boolean","showPanelActions","anchorReference","onClose","anchorOrigin","vertical","horizontal","direction","padding"],"mappings":"AAAA,oCAAoC;AACpC,kEAAkE;AAClE,mEAAmE;AACnE,0CAA0C;AAC1C,EAAE;AACF,6CAA6C;AAC7C,EAAE;AACF,sEAAsE;AACtE,oEAAoE;AACpE,2EAA2E;AAC3E,sEAAsE;AACtE,iCAAiC;;AAEjC,SAASA,KAAK,EAAEC,GAAG,EAAEC,OAAO,EAAEC,gBAAgB,EAAEC,MAAM,QAAyB,gBAAgB;AAC/F,SAASC,cAAc,EAAgCC,OAAO,EAAEC,QAAQ,QAAQ,QAAQ;AACxF,SAASC,WAAW,QAAQ,yBAAyB;AACrD,OAAOC,uBAAuB,gCAAgC;AAC9D,OAAOC,qBAAqB,8BAA8B;AAC1D,OAAOC,gBAAgB,gCAAgC;AACvD,OAAOC,gBAAgB,gCAAgC;AACvD,OAAOC,cAAc,+BAA+B;AACpD,OAAOC,qBAAqB,8BAA8B;AAC1D,OAAOC,cAAc,uBAAuB;AAE5C,OAAOC,eAAe,wBAAwB;AAC9C,OAAOC,4BAA4B,qCAAqC;AAExE,SACEC,eAAe,EACfC,6BAA6B,EAC7BC,mBAAmB,EACnBC,kBAAkB,EAClBC,YAAY,QACP,kBAAkB;AACzB,SAASC,gBAAgB,QAAQ,qBAAqB;AACtD,SAASC,UAAU,QAAQ,eAAe;AAoB1C,MAAMC,iBAAiBrB,OAAOH,KAAK;IACjCyB,SAAS;IACTC,YAAY;IACZC,UAAU;IACVC,gBAAgB;AAClB;AAEA,OAAO,MAAMC,eAA4C,CAAC,EACxDC,YAAY,EACZC,YAAY,EACZC,KAAK,EACLC,KAAK,EACLC,WAAW,EACXC,oBAAoB,EACpBC,KAAK,EACLC,YAAY,EACb;IACC,MAAMC,oBAAoBjC,QAAQ;QAChC,IAAI6B,eAAeA,YAAYK,IAAI,GAAGC,MAAM,GAAG,GAAG;YAChD,qBACE,KAACjC;gBAAYkC,IAAIN;gBAAsBD,aAAaA;gBAAaQ,YAAY;0BAC3E,cAAA,KAACpB;oBAAiBqB,cAAW;oBAAoBC,MAAK;8BACpD,cAAA,KAAC5B;wBACC6B,oBAAiB;wBACjBC,eAAa;wBACbC,UAAS;wBACTC,IAAI;4BAAEC,OAAO,CAACC,QAAUA,MAAMC,OAAO,CAACC,IAAI,CAACC,SAAS;wBAAC;;;;QAK/D;QACA,OAAOC;IACT,GAAG;QAACnB;QAAsBD;KAAY;IACtC,MAAMqB,cAAcnB,SAASA,MAAMI,MAAM,GAAG,mBAAK,KAACjB;QAAWa,OAAOA;;IACpE,MAAMoB,eAAe1B,iBAAiBwB,aAAatB;IAEnD,MAAMyB,sBAAsBpD,QAAQ;QAClC,MAAMqD,UAAUrB,aAAasB,IAAI,CAAC,CAACC,IAAMA,EAAEC,IAAI;QAC/C,MAAMC,aAAazB,aAAasB,IAAI,CAAC,CAACC,IAAMA,EAAEE,UAAU;QACxD,MAAMC,cAAc1B,aAAa2B,MAAM,CAAC,CAACJ,IAAMA,EAAEK,KAAK;QACtD,IAAIH,cAAcJ,SAAS;YACzB,6EAA6E;YAC7E,qHAAqH;YACrH,qBAAO,KAACxD;gBAAiByC,cAAW;gBAAUC,MAAK;;QACrD,OAAO,IAAImB,YAAYvB,MAAM,GAAG,GAAG;YACjC,MAAM0B,aAAaH,YAChBI,GAAG,CAAC,CAACP,IAAMA,EAAEK,KAAK,EAClBE,GAAG,CAAC,CAACC,IAAWA,GAAGC,WAAWD,GAAGE,cAAc,iBAAiB,yDAAyD;aACzHC,IAAI,CAAC;YAER,qBACE,KAAChE;gBAAY2B,aAAagC;0BACxB,cAAA,KAAC5C;oBAAiBqB,cAAW;oBAAeC,MAAK;8BAC/C,cAAA,KAAC7B;wBAAUgC,UAAS;;;;QAI5B;IACF,GAAG;QAACV;KAAa;IAEjB,MAAMmC,cAAcnE,QAAQ;QAC1B,IAAI0B,iBAAiBuB,WAAW;YAC9B,qBACE,KAAC/C;gBAAY2B,aAAab,aAAaoD,SAAS;0BAC9C,cAAA,KAACnD;oBACCqB,cAAY1B,gBAAgBwD,SAAS,CAACxC;oBACtCW,MAAK;oBACL8B,SAAS3C,aAAa4C,gBAAgB;8BAErC5C,aAAa6C,aAAa,iBACzB,KAACpE;wBAAkBuC,UAAS;uCAE5B,KAACtC;wBAAgBsC,UAAS;;;;QAKpC;QACA,OAAOO;IACT,GAAG;QAACvB;QAAcE;KAAM;IAExB,MAAM4C,cAAcxE,QAAQ;QAC1B,IAAIyB,iBAAiBwB,WAAW;YAC9B,gEAAgE;YAChE,qBACE;;kCACE,KAAC/C;wBAAY2B,aAAab,aAAayD,SAAS;kCAC9C,cAAA,KAACxD;4BACCqB,cAAY1B,gBAAgB6D,SAAS,CAAC7C;4BACtCW,MAAK;4BACL8B,SAAS5C,aAAaiD,gBAAgB;sCAEtC,cAAA,KAACrE;gCAAWqC,UAAS;;;;kCAGzB,KAACxC;wBAAY2B,aAAab,aAAa2D,cAAc;kCACnD,cAAA,KAAC1D;4BACCqB,cAAY1B,gBAAgB+D,cAAc,CAAC/C;4BAC3CW,MAAK;4BACL8B,SAAS5C,aAAamD,qBAAqB;sCAE3C,cAAA,KAACpE;gCACCkC,UAAS;gCACTC,IAAI;oCACF,wDAAwD;oCACxD,sCAAsC;oCACtCkC,WAAW;gCACb;;;;kCAIN,KAAC3E;wBAAY2B,aAAab,aAAa8D,WAAW;kCAChD,cAAA,KAAC7D;4BACCqB,cAAY1B,gBAAgBkE,WAAW,CAAClD;4BACxCW,MAAK;4BACL8B,SAAS5C,aAAasD,kBAAkB;sCAExC,cAAA,KAACzE;gCAAWoC,UAAS;;;;;;QAK/B;QACA,OAAOO;IACT,GAAG;QAACxB;QAAcG;KAAM;IAExB,MAAMoD,aAAahF,QAAQ;QACzB,IAAIwE,eAAe,CAAC9C,cAAc6C,eAAe;YAC/C,qBACE,KAACrE;gBAAY2B,aAAab,aAAaiE,SAAS;0BAC9C,cAAA,KAAChE;oBAAiBqB,cAAY1B,gBAAgBqE,SAAS,CAACrD;oBAAQW,MAAK;8BACnE,cAAA,KAAChC;wBAAS2E,WAAU;wBAAcvC,IAAI;4BAAEwC,QAAQ;wBAAO;wBAAGzC,UAAS;;;;QAI3E;QACA,OAAOO;IACT,GAAG;QAACuB;QAAa9C;QAAcE;KAAM;IAErC,MAAMwD,wBAAU,KAACzF;QAAIgD,IAAI;YAAErB,UAAU;QAAE;;IAEvC,wFAAwF;IACxF,MAAM+D,UAAU,CAAC,EAAEC,QAAQ,EAAqB,GAC9C7D,iBAAiBwB,aAAa,CAACvB,cAAc6C,8BAC3C,KAAC5E;YAAIgD,IAAI;gBAAEvB,SAAS;YAA2B;sBAAIkE;2BAEnD;sBAAGA;;IAGP,qBACE;;0BAEE,MAACnE;gBACCwB,IAAI,CAACE,QAAW,CAAA;wBACd,CAACA,MAAM0C,gBAAgB,CAAC1E,+BAA+B2E,OAAO,CAAC,GAAGzE,oBAAoB,EAAE;4BAAEK,SAAS;wBAAO;oBAC5G,CAAA;;oBAECgE;kCACD,MAACC;;0CACC,MAACI;gCAAa7D,OAAOA;;oCAClBK;oCAAkB;oCAAEiB;oCAAY;oCAAEE;oCAAoB;oCAAED;oCAAa;oCAAEgB;oCAAY;oCAAEK;;;4BAEvFQ;;;;;0BAKL,MAAC7D;gBACCwB,IAAI,CAACE,QAAW,CAAA;wBACd,CAACA,MAAM0C,gBAAgB,CAAC1E,+BAA+B2E,OAAO,CAACzE,oBAAoBD,qBAAqB,EAAE;4BACxGM,SAAS;wBACX;oBACF,CAAA;;kCAEA,MAACiE;;4BACEpD;4BAAkB;4BAAEiB;;;oBAEtBkC;oBAAQ;oBAAEhC;kCACX,MAACiC;;4BACElC;4BAAa;4BAAEgB;0CAChB,KAACsB;gCAAa7D,OAAOA;0CAAQ4C;;4BAC5BQ;;;;;0BAKL,MAAC7D;gBACCwB,IAAI,CAACE,QAAW,CAAA;wBACd,2GAA2G;wBAC3GzB,SAAS;wBACT,CAACyB,MAAM0C,gBAAgB,CAAC1E,+BAA+B6E,IAAI,CAAC5E,qBAAqB,EAAE;4BAAEM,SAAS;wBAAO;oBACvG,CAAA;;kCAEA,MAACiE;;4BACEpD;4BAAkB;4BAAEiB;;;oBAEtBkC;oBAAQ;oBAAEhC;kCACX,MAACiC;;4BACElC;4BAAa;4BAAEgB;4BAAY;4BAAEK;4BAAY;4BAAEQ;;;;;;;AAKtD,EAAE;AAEF,MAAMS,eAA+D,CAAC,EAAEH,QAAQ,EAAE1D,KAAK,EAAE;IACvF,MAAM,CAAC+D,gBAAgBC,kBAAkB,GAAG3F;IAE5C,0FAA0F;IAC1F,MAAM4F,2BAAa9F,eAAeuF,aAAcQ,MAAMC,OAAO,CAACT,aAAaA,SAAShC,IAAI,CAACvD;IACzF,IAAI,CAAC8F,YAAY;QACf,OAAO5C;IACT;IAEA,MAAM+C,cAAc,CAACC;QACnBL,kBAAkBK,MAAMC,aAAa,CAACC,qBAAqB;IAC7D;IAEA,MAAMC,cAAc;QAClBR,kBAAkB3C;IACpB;IAEA,MAAMoD,OAAOC,QAAQX;IACrB,MAAMvD,KAAKiE,OAAO,iBAAiBpD;IAEnC,qBACE;;0BACE,KAAChC;gBACCiE,WAAU;gBACV1C,oBAAkBJ;gBAClBiC,SAAS2B;gBACT1D,cAAY1B,gBAAgB2F,gBAAgB,CAAC3E;gBAC7CW,MAAK;0BAEL,cAAA,KAAC9B;oBAASiC,UAAS;;;0BAErB,KAAC9C;gBACCwC,IAAIA;gBACJiE,MAAMA;gBACNG,iBAAgB;gBAChBb,gBAAgBA;gBAChBc,SAASL;gBACTM,cAAc;oBACZC,UAAU;oBACVC,YAAY;gBACd;0BAEA,cAAA,KAAClH;oBAAMmH,WAAU;oBAAMxF,YAAW;oBAASsB,IAAI;wBAAEmE,SAAS;oBAAE;oBAAGzC,SAAS+B;8BACrEd;;;;;AAKX"}
@@ -11,13 +11,9 @@ export interface PanelHeaderProps extends Omit<CardHeaderProps, OmittedProps> {
11
11
  links?: Link[];
12
12
  extra?: ReactNode;
13
13
  queryResults: QueryData[];
14
- panelPluginKind: string;
15
14
  readHandlers?: PanelActionsProps['readHandlers'];
16
15
  editHandlers?: PanelActionsProps['editHandlers'];
17
- projectName?: string;
18
- panelProps?: Record<string, unknown>;
19
16
  }
20
- export declare function PanelHeader({ id, title: rawTitle, description: rawDescription, links, queryResults, readHandlers, editHandlers, sx, extra, panelPluginKind, projectName, panelProps, // ========== ADDED ==========
21
- ...rest }: PanelHeaderProps): ReactElement;
17
+ export declare function PanelHeader({ id, title: rawTitle, description: rawDescription, links, queryResults, readHandlers, editHandlers, sx, extra, ...rest }: PanelHeaderProps): ReactElement;
22
18
  export {};
23
19
  //# sourceMappingURL=PanelHeader.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"PanelHeader.d.ts","sourceRoot":"","sources":["../../../src/components/Panel/PanelHeader.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAc,eAAe,EAAqB,MAAM,eAAe,CAAC;AAE/E,OAAO,EAAE,IAAI,EAAkD,MAAM,kBAAkB,CAAC;AACxF,OAAO,EAAE,SAAS,EAA+B,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAW,MAAM,OAAO,CAAC;AAEzD,OAAO,EAAgB,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEjE,KAAK,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,mBAAmB,CAAC;AAO1E,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,eAAe,EAAE,MAAM,CAAC;IACxB,YAAY,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAC;IACjD,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,UAAU,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACtC;AAED,wBAAgB,WAAW,CAAC,EAC1B,EAAE,EACF,KAAK,EAAE,QAAQ,EACf,WAAW,EAAE,cAAc,EAC3B,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,EAAE,EACF,KAAK,EACL,eAAe,EACf,WAAW,EACX,UAAU,EAAE,8BAA8B;AAC1C,GAAG,IAAI,EACR,EAAE,gBAAgB,GAAG,YAAY,CA2GjC"}
1
+ {"version":3,"file":"PanelHeader.d.ts","sourceRoot":"","sources":["../../../src/components/Panel/PanelHeader.tsx"],"names":[],"mappings":"AAaA,OAAO,EAAc,eAAe,EAAqB,MAAM,eAAe,CAAC;AAE/E,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,SAAS,EAA+B,MAAM,2BAA2B,CAAC;AACnF,OAAO,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAEhD,OAAO,EAAgB,iBAAiB,EAAE,MAAM,gBAAgB,CAAC;AAEjE,KAAK,YAAY,GAAG,UAAU,GAAG,QAAQ,GAAG,OAAO,GAAG,mBAAmB,CAAC;AAE1E,MAAM,WAAW,gBAAiB,SAAQ,IAAI,CAAC,eAAe,EAAE,YAAY,CAAC;IAC3E,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,IAAI,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,SAAS,CAAC;IAClB,YAAY,EAAE,SAAS,EAAE,CAAC;IAC1B,YAAY,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAC;IACjD,YAAY,CAAC,EAAE,iBAAiB,CAAC,cAAc,CAAC,CAAC;CAClD;AAED,wBAAgB,WAAW,CAAC,EAC1B,EAAE,EACF,KAAK,EAAE,QAAQ,EACf,WAAW,EAAE,cAAc,EAC3B,KAAK,EACL,YAAY,EACZ,YAAY,EACZ,YAAY,EACZ,EAAE,EACF,KAAK,EACL,GAAG,IAAI,EACR,EAAE,gBAAgB,GAAG,YAAY,CA0DjC"}
@@ -14,53 +14,13 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
14
14
  import { CardHeader, Stack, Typography } from '@mui/material';
15
15
  import { combineSx } from '@perses-dev/components';
16
16
  import { useReplaceVariablesInString } from '@perses-dev/plugin-system';
17
- import { useMemo } from 'react';
18
17
  import { HEADER_ACTIONS_CONTAINER_NAME } from '../../constants';
19
18
  import { PanelActions } from './PanelActions';
20
- export function PanelHeader({ id, title: rawTitle, description: rawDescription, links, queryResults, readHandlers, editHandlers, sx, extra, panelPluginKind, projectName, panelProps, ...rest }) {
19
+ export function PanelHeader({ id, title: rawTitle, description: rawDescription, links, queryResults, readHandlers, editHandlers, sx, extra, ...rest }) {
21
20
  const titleElementId = `${id}-title`;
22
21
  const descriptionTooltipId = `${id}-description`;
23
22
  const title = useReplaceVariablesInString(rawTitle);
24
23
  const description = useReplaceVariablesInString(rawDescription);
25
- const timeSeriesDataForExport = useMemo(()=>{
26
- // Collect all series from all queries
27
- const allSeries = [];
28
- let timeRange = undefined;
29
- let stepMs = undefined;
30
- let metadata = undefined;
31
- queryResults.forEach((query)=>{
32
- if (query.data && 'series' in query.data) {
33
- const timeSeriesData = query.data;
34
- // Collect series from this query
35
- if (timeSeriesData.series && timeSeriesData.series.length > 0) {
36
- allSeries.push(...timeSeriesData.series);
37
- // Use the first query's metadata/timeRange/stepMs as the base
38
- if (!timeRange && timeSeriesData.timeRange) {
39
- timeRange = timeSeriesData.timeRange;
40
- }
41
- if (!stepMs && timeSeriesData.stepMs) {
42
- stepMs = timeSeriesData.stepMs;
43
- }
44
- if (!metadata && timeSeriesData.metadata) {
45
- metadata = timeSeriesData.metadata;
46
- }
47
- }
48
- }
49
- });
50
- // If we found series, create a combined TimeSeriesData object
51
- if (allSeries.length > 0) {
52
- const combinedData = {
53
- series: allSeries,
54
- timeRange,
55
- stepMs,
56
- metadata
57
- };
58
- return combinedData;
59
- }
60
- return undefined;
61
- }, [
62
- queryResults
63
- ]);
64
24
  return /*#__PURE__*/ _jsx(CardHeader, {
65
25
  id: id,
66
26
  component: "header",
@@ -89,14 +49,10 @@ export function PanelHeader({ id, title: rawTitle, description: rawDescription,
89
49
  description: description,
90
50
  descriptionTooltipId: descriptionTooltipId,
91
51
  links: links,
92
- queryResults: timeSeriesDataForExport,
93
- panelPluginKind: panelPluginKind,
52
+ queryResults: queryResults,
94
53
  readHandlers: readHandlers,
95
54
  editHandlers: editHandlers,
96
- extra: extra,
97
- projectName: projectName,
98
- // ========== ADDED: Pass panel props for actions ==========
99
- panelProps: panelProps
55
+ extra: extra
100
56
  })
101
57
  ]
102
58
  }),