@grafana/ui
Version:
Grafana Components Library
240 lines (237 loc) • 8.32 kB
JavaScript
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