import { css, cx, keyframes } from '@emotion/css'; import React from 'react'; import tinycolor from 'tinycolor2'; import { GrafanaTheme2 } from '@grafana/data'; import { SceneComponentProps, SceneGridLayout, SceneGridRow, SceneObjectBase, SceneObjectState } from '@grafana/scenes'; import { LibraryPanel } from '@grafana/schema'; import { IconButton, useStyles2 } from '@grafana/ui'; import { Trans } from 'app/core/internationalization'; import { LibraryPanelsSearch, LibraryPanelsSearchVariant, } from 'app/features/library-panels/components/LibraryPanelsSearch/LibraryPanelsSearch'; import { getDashboardSceneFor } from '../utils/utils'; import { DashboardGridItem } from './DashboardGridItem'; import { DashboardScene } from './DashboardScene'; import { LibraryVizPanel } from './LibraryVizPanel'; export interface AddLibraryPanelWidgetState extends SceneObjectState { key: string; } export class AddLibraryPanelWidget extends SceneObjectBase { public constructor(state: AddLibraryPanelWidgetState) { super({ ...state, }); } private get _dashboard(): DashboardScene { return getDashboardSceneFor(this); } public getDashboard(): DashboardScene { return this._dashboard; } public onCancelAddPanel = (evt: React.MouseEvent) => { evt.preventDefault(); if (!(this._dashboard.state.body instanceof SceneGridLayout)) { throw new Error('Trying to remove the library panel widget in a layout that is not SceneGridLayout'); } const sceneGridLayout = this._dashboard.state.body; const children = []; for (const child of sceneGridLayout.state.children) { if (child.state.key !== this.parent?.state.key) { children.push(child); } if (child instanceof SceneGridRow) { const rowChildren = []; for (const rowChild of child.state.children) { if (rowChild instanceof DashboardGridItem && rowChild.state.key !== this.parent?.state.key) { rowChildren.push(rowChild); } } child.setState({ children: rowChildren }); } } sceneGridLayout.setState({ children }); }; public onAddLibraryPanel = (panelInfo: LibraryPanel) => { if (!(this._dashboard.state.body instanceof SceneGridLayout)) { throw new Error('Trying to add a library panel in a layout that is not SceneGridLayout'); } const body = new LibraryVizPanel({ title: 'Panel Title', uid: panelInfo.uid, name: panelInfo.name, panelKey: this.state.key, }); if (this.parent instanceof DashboardGridItem) { this.parent.setState({ body }); } }; static Component = ({ model }: SceneComponentProps) => { const dashboard = model.getDashboard(); const styles = useStyles2(getStyles); return (
Add panel from panel library
); }; } const getStyles = (theme: GrafanaTheme2) => { const pulsate = keyframes({ '0%': { boxShadow: `0 0 0 2px ${theme.colors.background.canvas}, 0 0 0px 4px ${theme.colors.primary.main}`, }, '50%': { boxShadow: `0 0 0 2px ${theme.components.dashboard.background}, 0 0 0px 4px ${tinycolor(theme.colors.primary.main) .darken(20) .toHexString()}`, }, '100%': { boxShadow: `0 0 0 2px ${theme.components.dashboard.background}, 0 0 0px 4px ${theme.colors.primary.main}`, }, }); return { // wrapper is used to make sure box-shadow animation isn't cut off in dashboard page wrapper: css({ height: '100%', paddingTop: `${theme.spacing(0.5)}`, }), headerRow: css({ display: 'flex', alignItems: 'center', height: '38px', flexShrink: 0, width: '100%', fontSize: theme.typography.fontSize, fontWeight: theme.typography.fontWeightMedium, paddingLeft: `${theme.spacing(1)}`, transition: 'background-color 0.1s ease-in-out', cursor: 'move', '&:hover': { background: `${theme.colors.background.secondary}`, }, }), callToAction: css({ overflow: 'hidden', outline: '2px dotted transparent', outlineOffset: '2px', boxShadow: '0 0 0 2px black, 0 0 0px 4px #1f60c4', animation: `${pulsate} 2s ease infinite`, }), }; };