UNPKG

@grafana/ui

Version:
240 lines (237 loc) • 8.32 kB
import { jsxs, jsx, Fragment as Fragment$1 } from 'react/jsx-runtime'; import { css, cx } from '@emotion/css'; import { uniqueId } from 'lodash'; import { useMemo, useCallback, useEffect, Fragment } from 'react'; import { useSortBy, useExpanded, usePagination, useTable } from 'react-table'; import { isTruthy } from '@grafana/data'; import { useStyles2 } from '../../themes/ThemeContext.mjs'; import { Icon } from '../Icon/Icon.mjs'; import { Pagination } from '../Pagination/Pagination.mjs'; import { Tooltip } from '../Tooltip/Tooltip.mjs'; import { getColumns, EXPANDER_CELL_ID } from './utils.mjs'; "use strict"; const getStyles = (theme) => { const rowHoverBg = theme.colors.emphasize(theme.colors.background.primary, 0.03); return { container: css({ display: "flex", gap: theme.spacing(2), flexDirection: "column", width: "100%", overflowX: "auto" }), cell: css({ padding: theme.spacing(1), minWidth: theme.spacing(3) }), table: css({ borderRadius: theme.shape.radius.default, width: "100%" }), disableGrow: css({ width: 0 }), header: css({ borderBottom: `1px solid ${theme.colors.border.weak}`, minWidth: theme.spacing(3), "&, & > button": { position: "relative", whiteSpace: "nowrap", padding: theme.spacing(1) }, "& > button": { "&:after": { content: '"\\00a0"' }, width: "100%", height: "100%", background: "none", border: "none", paddingRight: theme.spacing(2.5), textAlign: "left", fontWeight: theme.typography.fontWeightMedium } }), row: css({ label: "row", borderBottom: `1px solid ${theme.colors.border.weak}`, "&:hover": { backgroundColor: rowHoverBg }, "&:last-child": { borderBottom: 0 } }), expandedRow: css({ label: "expanded-row-content", borderBottom: "none" }), expandedContentCell: css({ borderBottom: `1px solid ${theme.colors.border.weak}`, position: "relative", padding: theme.spacing(2, 2, 2, 5), "&:before": { content: '""', position: "absolute", width: "1px", top: 0, left: "16px", bottom: theme.spacing(2), background: theme.colors.border.medium } }), expandedContentRow: css({ label: "expanded-row-content" }), sortableHeader: css({ /* increases selector's specificity so that it always takes precedence over default styles */ "&&": { padding: 0 } }) }; }; function InteractiveTable({ className, columns, data, getRowId, headerTooltips, pageSize = 0, renderExpandedRow, showExpandAll = false, fetchData, initialSortBy = [] }) { const styles = useStyles2(getStyles); const tableColumns = useMemo(() => { return getColumns(columns, showExpandAll); }, [columns, showExpandAll]); const id = useUniqueId(); const getRowHTMLID = useCallback( (row) => { return `${id}-${row.id}`.replace(/\s/g, ""); }, [id] ); const tableHooks = [useSortBy, useExpanded]; const multiplePages = data.length > pageSize; const paginationEnabled = pageSize > 0; if (paginationEnabled) { tableHooks.push(usePagination); } const tableInstance = useTable( { columns: tableColumns, data, autoResetExpanded: false, autoResetSortBy: false, disableMultiSort: true, // If fetchData is provided, we disable client-side sorting manualSortBy: Boolean(fetchData), getRowId, initialState: { hiddenColumns: [ !renderExpandedRow && EXPANDER_CELL_ID, ...tableColumns.filter((col) => !(col.visible ? col.visible(data) : true)).map((c) => c.id).filter(isTruthy) ].filter(isTruthy), sortBy: initialSortBy } }, ...tableHooks ); const { getTableProps, getTableBodyProps, headerGroups, prepareRow } = tableInstance; const { sortBy } = tableInstance.state; useEffect(() => { if (fetchData) { fetchData({ sortBy }); } }, [sortBy, fetchData]); useEffect(() => { if (paginationEnabled) { tableInstance.setPageSize(pageSize); } }, [paginationEnabled, pageSize, tableInstance.setPageSize, tableInstance]); return /* @__PURE__ */ jsxs("div", { className: styles.container, children: [ /* @__PURE__ */ jsxs("table", { ...getTableProps(), className: cx(styles.table, className), children: [ /* @__PURE__ */ jsx("thead", { children: headerGroups.map((headerGroup) => { const { key, ...headerRowProps } = headerGroup.getHeaderGroupProps(); return /* @__PURE__ */ jsx("tr", { ...headerRowProps, children: headerGroup.headers.map((column) => { const { key: key2, ...headerCellProps } = column.getHeaderProps(); const headerTooltip = headerTooltips == null ? void 0 : headerTooltips[column.id]; return /* @__PURE__ */ jsx( "th", { className: cx(styles.header, { [styles.disableGrow]: column.width === 0, [styles.sortableHeader]: column.canSort }), ...headerCellProps, ...column.isSorted && { "aria-sort": column.isSortedDesc ? "descending" : "ascending" }, children: /* @__PURE__ */ jsx(ColumnHeader, { column, headerTooltip }) }, key2 ); }) }, key); }) }), /* @__PURE__ */ jsx("tbody", { ...getTableBodyProps(), children: (paginationEnabled ? tableInstance.page : tableInstance.rows).map((row) => { prepareRow(row); const { key, ...otherRowProps } = row.getRowProps(); const rowId = getRowHTMLID(row); const isExpanded = row.isExpanded; return /* @__PURE__ */ jsxs(Fragment, { children: [ /* @__PURE__ */ jsx("tr", { ...otherRowProps, className: cx(styles.row, isExpanded && styles.expandedRow), children: row.cells.map((cell) => { const { key: key2, ...otherCellProps } = cell.getCellProps(); return /* @__PURE__ */ jsx("td", { className: styles.cell, ...otherCellProps, children: cell.render("Cell", { __rowID: rowId }) }, key2); }) }), isExpanded && renderExpandedRow && /* @__PURE__ */ jsx("tr", { ...otherRowProps, id: rowId, className: styles.expandedContentRow, children: /* @__PURE__ */ jsx("td", { className: styles.expandedContentCell, colSpan: row.cells.length, children: renderExpandedRow(row.original) }) }) ] }, key); }) }) ] }), paginationEnabled && multiplePages && /* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx( Pagination, { currentPage: tableInstance.state.pageIndex + 1, numberOfPages: tableInstance.pageOptions.length, onNavigate: (toPage) => tableInstance.gotoPage(toPage - 1) } ) }) ] }); } const useUniqueId = () => { return useMemo(() => uniqueId("InteractiveTable"), []); }; const getColumnHeaderStyles = (theme) => ({ sortIcon: css({ position: "absolute", top: theme.spacing(1) }), headerTooltipIcon: css({ marginLeft: theme.spacing(0.5) }) }); function ColumnHeader({ column: { canSort, render, isSorted, isSortedDesc, getSortByToggleProps }, headerTooltip }) { const styles = useStyles2(getColumnHeaderStyles); const { onClick } = getSortByToggleProps(); const children = /* @__PURE__ */ jsxs(Fragment$1, { children: [ render("Header"), headerTooltip && /* @__PURE__ */ jsx(Tooltip, { theme: "info-alt", content: headerTooltip.content, placement: "top-end", children: /* @__PURE__ */ jsx( Icon, { className: styles.headerTooltipIcon, name: headerTooltip.iconName || "info-circle", "data-testid": "header-tooltip-icon" } ) }), isSorted && /* @__PURE__ */ jsx("span", { "aria-hidden": "true", className: styles.sortIcon, children: /* @__PURE__ */ jsx(Icon, { name: isSortedDesc ? "angle-down" : "angle-up" }) }) ] }); if (canSort) { return /* @__PURE__ */ jsx("button", { type: "button", onClick, children }); } return children; } export { InteractiveTable }; //# sourceMappingURL=InteractiveTable.mjs.map