2021-05-05 17:09:12 +08:00
|
|
|
import { css } from '@emotion/css';
|
2024-06-25 19:43:47 +08:00
|
|
|
import { ReactElement, useState } from 'react';
|
2023-12-07 17:56:12 +08:00
|
|
|
import Skeleton from 'react-loading-skeleton';
|
2022-04-22 21:33:13 +08:00
|
|
|
|
2021-06-18 14:03:42 +08:00
|
|
|
import { GrafanaTheme2 } from '@grafana/data';
|
2022-04-22 21:33:13 +08:00
|
|
|
import { config } from '@grafana/runtime';
|
2021-05-05 17:09:12 +08:00
|
|
|
import { Icon, Link, useStyles2 } from '@grafana/ui';
|
2025-03-12 21:14:32 +08:00
|
|
|
import { SkeletonComponent, attachSkeleton } from '@grafana/ui/unstable';
|
2022-04-22 21:33:13 +08:00
|
|
|
import { getPanelPluginNotFound } from 'app/features/panel/components/PanelPluginError';
|
2021-10-13 22:59:47 +08:00
|
|
|
import { PanelTypeCard } from 'app/features/panel/components/VizTypePicker/PanelTypeCard';
|
2022-04-22 21:33:13 +08:00
|
|
|
|
|
|
|
|
import { LibraryElementDTO } from '../../types';
|
2021-03-24 18:43:27 +08:00
|
|
|
import { DeleteLibraryPanelModal } from '../DeleteLibraryPanelModal/DeleteLibraryPanelModal';
|
2021-02-25 18:26:28 +08:00
|
|
|
|
|
|
|
|
export interface LibraryPanelCardProps {
|
2021-05-11 13:10:19 +08:00
|
|
|
libraryPanel: LibraryElementDTO;
|
|
|
|
|
onClick: (panel: LibraryElementDTO) => void;
|
|
|
|
|
onDelete?: (panel: LibraryElementDTO) => void;
|
2021-02-25 18:26:28 +08:00
|
|
|
showSecondaryActions?: boolean;
|
|
|
|
|
}
|
|
|
|
|
|
2023-03-15 22:56:09 +08:00
|
|
|
type Props = LibraryPanelCardProps & { children?: JSX.Element | JSX.Element[] };
|
|
|
|
|
|
2023-12-12 19:05:36 +08:00
|
|
|
const LibraryPanelCardComponent = ({ libraryPanel, onClick, onDelete, showSecondaryActions }: Props) => {
|
2021-02-25 18:26:28 +08:00
|
|
|
const [showDeletionModal, setShowDeletionModal] = useState(false);
|
|
|
|
|
|
|
|
|
|
const onDeletePanel = () => {
|
2021-03-18 18:19:41 +08:00
|
|
|
onDelete?.(libraryPanel);
|
2021-02-25 18:26:28 +08:00
|
|
|
setShowDeletionModal(false);
|
|
|
|
|
};
|
|
|
|
|
|
2021-06-18 14:03:42 +08:00
|
|
|
const panelPlugin = config.panels[libraryPanel.model.type] ?? getPanelPluginNotFound(libraryPanel.model.type).meta;
|
2021-03-25 15:33:13 +08:00
|
|
|
|
2021-02-25 18:26:28 +08:00
|
|
|
return (
|
|
|
|
|
<>
|
2021-03-25 15:33:13 +08:00
|
|
|
<PanelTypeCard
|
|
|
|
|
isCurrent={false}
|
|
|
|
|
title={libraryPanel.name}
|
2021-04-28 19:47:12 +08:00
|
|
|
description={libraryPanel.description}
|
2021-03-25 15:33:13 +08:00
|
|
|
plugin={panelPlugin}
|
2021-09-20 16:58:24 +08:00
|
|
|
onClick={() => onClick?.(libraryPanel)}
|
2021-03-25 15:33:13 +08:00
|
|
|
onDelete={showSecondaryActions ? () => setShowDeletionModal(true) : undefined}
|
2021-05-05 17:09:12 +08:00
|
|
|
>
|
|
|
|
|
<FolderLink libraryPanel={libraryPanel} />
|
|
|
|
|
</PanelTypeCard>
|
2021-02-25 18:26:28 +08:00
|
|
|
{showDeletionModal && (
|
2021-03-24 18:43:27 +08:00
|
|
|
<DeleteLibraryPanelModal
|
|
|
|
|
libraryPanel={libraryPanel}
|
2021-02-25 18:26:28 +08:00
|
|
|
onConfirm={onDeletePanel}
|
|
|
|
|
onDismiss={() => setShowDeletionModal(false)}
|
|
|
|
|
/>
|
|
|
|
|
)}
|
|
|
|
|
</>
|
|
|
|
|
);
|
|
|
|
|
};
|
2021-05-05 17:09:12 +08:00
|
|
|
|
2023-12-12 19:05:36 +08:00
|
|
|
const LibraryPanelCardSkeleton: SkeletonComponent<Pick<Props, 'showSecondaryActions'>> = ({
|
|
|
|
|
showSecondaryActions,
|
|
|
|
|
rootProps,
|
|
|
|
|
}) => {
|
2023-12-07 17:56:12 +08:00
|
|
|
const styles = useStyles2(getStyles);
|
|
|
|
|
|
|
|
|
|
return (
|
2023-12-12 19:05:36 +08:00
|
|
|
<PanelTypeCard.Skeleton hasDelete={showSecondaryActions} {...rootProps}>
|
2023-12-07 17:56:12 +08:00
|
|
|
<Skeleton containerClassName={styles.metaContainer} width={80} />
|
|
|
|
|
</PanelTypeCard.Skeleton>
|
|
|
|
|
);
|
|
|
|
|
};
|
|
|
|
|
|
2023-12-12 19:05:36 +08:00
|
|
|
export const LibraryPanelCard = attachSkeleton(LibraryPanelCardComponent, LibraryPanelCardSkeleton);
|
2023-12-07 17:56:12 +08:00
|
|
|
|
2021-05-05 17:09:12 +08:00
|
|
|
interface FolderLinkProps {
|
2021-05-11 13:10:19 +08:00
|
|
|
libraryPanel: LibraryElementDTO;
|
2021-05-05 17:09:12 +08:00
|
|
|
}
|
|
|
|
|
|
2021-09-20 16:58:24 +08:00
|
|
|
function FolderLink({ libraryPanel }: FolderLinkProps): ReactElement | null {
|
2021-05-05 17:09:12 +08:00
|
|
|
const styles = useStyles2(getStyles);
|
|
|
|
|
|
2023-01-30 12:14:12 +08:00
|
|
|
if (!libraryPanel.meta?.folderUid && !libraryPanel.meta?.folderName) {
|
2021-09-20 16:58:24 +08:00
|
|
|
return null;
|
|
|
|
|
}
|
|
|
|
|
|
2024-06-14 21:01:18 +08:00
|
|
|
// LibraryPanels API returns folder-less library panels with an empty string folder UID
|
2021-05-05 17:09:12 +08:00
|
|
|
if (!libraryPanel.meta.folderUid) {
|
|
|
|
|
return (
|
|
|
|
|
<span className={styles.metaContainer}>
|
|
|
|
|
<Icon name={'folder'} size="sm" />
|
2024-06-14 21:01:18 +08:00
|
|
|
<span>Dashboards</span>
|
2021-05-05 17:09:12 +08:00
|
|
|
</span>
|
|
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
2021-05-11 13:10:19 +08:00
|
|
|
<span className={styles.metaContainer}>
|
|
|
|
|
<Link href={`/dashboards/f/${libraryPanel.meta.folderUid}`}>
|
2021-05-05 17:09:12 +08:00
|
|
|
<Icon name={'folder-upload'} size="sm" />
|
2021-05-11 13:10:19 +08:00
|
|
|
<span>{libraryPanel.meta.folderName}</span>
|
|
|
|
|
</Link>
|
|
|
|
|
</span>
|
2021-05-05 17:09:12 +08:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getStyles(theme: GrafanaTheme2) {
|
|
|
|
|
return {
|
2023-12-07 17:56:12 +08:00
|
|
|
metaContainer: css({
|
|
|
|
|
display: 'flex',
|
|
|
|
|
alignItems: 'center',
|
|
|
|
|
color: theme.colors.text.secondary,
|
|
|
|
|
fontSize: theme.typography.bodySmall.fontSize,
|
|
|
|
|
paddingTop: theme.spacing(0.5),
|
|
|
|
|
|
|
|
|
|
svg: {
|
|
|
|
|
marginRight: theme.spacing(0.5),
|
|
|
|
|
marginBottom: 3,
|
|
|
|
|
},
|
|
|
|
|
}),
|
2021-05-05 17:09:12 +08:00
|
|
|
};
|
|
|
|
|
}
|