LibraryPanelsView.tsx 3.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128
  1. import { css, cx } from '@emotion/css';
  2. import React, { useMemo, useReducer } from 'react';
  3. import { useDebounce } from 'react-use';
  4. import { GrafanaTheme, LoadingState } from '@grafana/data';
  5. import { Pagination, useStyles } from '@grafana/ui';
  6. import { LibraryElementDTO } from '../../types';
  7. import { LibraryPanelCard } from '../LibraryPanelCard/LibraryPanelCard';
  8. import { asyncDispatcher, deleteLibraryPanel, searchForLibraryPanels } from './actions';
  9. import { changePage, initialLibraryPanelsViewState, libraryPanelsViewReducer } from './reducer';
  10. interface LibraryPanelViewProps {
  11. className?: string;
  12. onClickCard: (panel: LibraryElementDTO) => void;
  13. showSecondaryActions?: boolean;
  14. currentPanelId?: string;
  15. searchString: string;
  16. sortDirection?: string;
  17. panelFilter?: string[];
  18. folderFilter?: string[];
  19. perPage?: number;
  20. }
  21. export const LibraryPanelsView: React.FC<LibraryPanelViewProps> = ({
  22. className,
  23. onClickCard,
  24. searchString,
  25. sortDirection,
  26. panelFilter,
  27. folderFilter,
  28. showSecondaryActions,
  29. currentPanelId: currentPanel,
  30. perPage: propsPerPage = 40,
  31. }) => {
  32. const styles = useStyles(getPanelViewStyles);
  33. const [{ libraryPanels, page, perPage, numberOfPages, loadingState, currentPanelId }, dispatch] = useReducer(
  34. libraryPanelsViewReducer,
  35. {
  36. ...initialLibraryPanelsViewState,
  37. currentPanelId: currentPanel,
  38. perPage: propsPerPage,
  39. }
  40. );
  41. const asyncDispatch = useMemo(() => asyncDispatcher(dispatch), [dispatch]);
  42. useDebounce(
  43. () =>
  44. asyncDispatch(
  45. searchForLibraryPanels({
  46. searchString,
  47. sortDirection,
  48. panelFilter,
  49. folderFilter,
  50. page,
  51. perPage,
  52. currentPanelId,
  53. })
  54. ),
  55. 300,
  56. [searchString, sortDirection, panelFilter, folderFilter, page, asyncDispatch]
  57. );
  58. const onDelete = ({ uid }: LibraryElementDTO) =>
  59. asyncDispatch(deleteLibraryPanel(uid, { searchString, page, perPage }));
  60. const onPageChange = (page: number) => asyncDispatch(changePage({ page }));
  61. return (
  62. <div className={cx(styles.container, className)}>
  63. <div className={styles.libraryPanelList}>
  64. {loadingState === LoadingState.Loading ? (
  65. <p>Loading library panels...</p>
  66. ) : libraryPanels.length < 1 ? (
  67. <p className={styles.noPanelsFound}>No library panels found.</p>
  68. ) : (
  69. libraryPanels?.map((item, i) => (
  70. <LibraryPanelCard
  71. key={`library-panel=${i}`}
  72. libraryPanel={item}
  73. onDelete={onDelete}
  74. onClick={onClickCard}
  75. showSecondaryActions={showSecondaryActions}
  76. />
  77. ))
  78. )}
  79. </div>
  80. {libraryPanels.length ? (
  81. <div className={styles.pagination}>
  82. <Pagination
  83. currentPage={page}
  84. numberOfPages={numberOfPages}
  85. onNavigate={onPageChange}
  86. hideWhenSinglePage={true}
  87. />
  88. </div>
  89. ) : null}
  90. </div>
  91. );
  92. };
  93. const getPanelViewStyles = (theme: GrafanaTheme) => {
  94. return {
  95. container: css`
  96. display: flex;
  97. flex-direction: column;
  98. flex-wrap: nowrap;
  99. `,
  100. libraryPanelList: css`
  101. max-width: 100%;
  102. display: grid;
  103. grid-gap: ${theme.spacing.sm};
  104. `,
  105. searchHeader: css`
  106. display: flex;
  107. `,
  108. newPanelButton: css`
  109. margin-top: 10px;
  110. align-self: flex-start;
  111. `,
  112. pagination: css`
  113. align-self: center;
  114. margin-top: ${theme.spacing.sm};
  115. `,
  116. noPanelsFound: css`
  117. label: noPanelsFound;
  118. min-height: 200px;
  119. `,
  120. };
  121. };