@grafana/ui
Version:
Grafana Components Library
320 lines (317 loc) • 11.2 kB
JavaScript
import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
import { memo, useRef, useState, useMemo, useEffect, useCallback } from 'react';
import { useTable, useFilters, useSortBy, useAbsoluteLayout, useResizeColumns, useExpanded, usePagination } from 'react-table';
import { ReducerID, FieldType, getRowUniqueId, getFieldMatcher } from '@grafana/data';
import { selectors } from '@grafana/e2e-selectors';
import { Trans } from '@grafana/i18n';
import { TableCellHeight } from '@grafana/schema';
import { useTheme2 } from '../../../themes/ThemeContext.mjs';
import { CustomScrollbar } from '../../CustomScrollbar/CustomScrollbar.mjs';
import { Pagination } from '../../Pagination/Pagination.mjs';
import { TableCellInspector } from '../TableCellInspector.mjs';
import { useResetVariableListSizeCache, useFixScrollbarContainer } from '../hooks.mjs';
import { useTableStateReducer, getInitialState } from '../reducer.mjs';
import { getColumns, sortCaseInsensitive, sortNumber, getFooterItems, createFooterCalculationValues, guessLongestField } from '../utils.mjs';
import { FooterRow } from './FooterRow.mjs';
import { HeaderRow } from './HeaderRow.mjs';
import { RowsList } from './RowsList.mjs';
import { useTableStyles } from './styles.mjs';
"use strict";
const COLUMN_MIN_WIDTH = 150;
const FOOTER_ROW_HEIGHT = 36;
const NO_DATA_TEXT = "No data";
const Table = memo((props) => {
var _a, _b, _c, _d;
const {
ariaLabel,
data,
height,
onCellFilterAdded,
onColumnResize,
width,
columnMinWidth = COLUMN_MIN_WIDTH,
noHeader,
resizable = true,
initialSortBy,
footerOptions,
showTypeIcons,
footerValues,
enablePagination,
cellHeight = TableCellHeight.Sm,
timeRange,
enableSharedCrosshair = false,
initialRowIndex = void 0,
fieldConfig,
getActions,
replaceVariables
} = props;
const listRef = useRef(null);
const tableDivRef = useRef(null);
const variableSizeListScrollbarRef = useRef(null);
const theme = useTheme2();
const tableStyles = useTableStyles(theme, cellHeight);
const headerHeight = noHeader ? 0 : tableStyles.rowHeight;
const [footerItems, setFooterItems] = useState(footerValues);
const noValuesDisplayText = (_b = (_a = fieldConfig == null ? void 0 : fieldConfig.defaults) == null ? void 0 : _a.noValue) != null ? _b : NO_DATA_TEXT;
const [inspectCell, setInspectCell] = useState(null);
const footerHeight = useMemo(() => {
const EXTENDED_ROW_HEIGHT = FOOTER_ROW_HEIGHT;
let length = 0;
if (!footerItems) {
return 0;
}
for (const fv of footerItems) {
if (Array.isArray(fv) && fv.length > length) {
length = fv.length;
}
}
if (length > 1) {
return EXTENDED_ROW_HEIGHT * length;
}
return EXTENDED_ROW_HEIGHT;
}, [footerItems]);
const memoizedData = useMemo(() => {
if (!data.fields.length) {
return [];
}
return Array(data.length).fill(0);
}, [data]);
const isCountRowsSet = Boolean(
(footerOptions == null ? void 0 : footerOptions.countRows) && footerOptions.reducer && footerOptions.reducer.length && footerOptions.reducer[0] === ReducerID.count
);
const nestedDataField = data.fields.find((f) => f.type === FieldType.nestedFrames);
const hasNestedData = nestedDataField !== void 0;
const memoizedColumns = useMemo(
() => getColumns(data, width, columnMinWidth, hasNestedData, footerItems, isCountRowsSet),
[data, width, columnMinWidth, hasNestedData, footerItems, isCountRowsSet]
);
const toggleAllRowsExpandedRef = useRef();
const stateReducer = useTableStateReducer({
onColumnResize,
onSortByChange: (state2) => {
toggleAllRowsExpandedRef.current(false);
if (props.onSortByChange) {
props.onSortByChange(state2);
}
},
data
});
const hasUniqueId = !!((_d = (_c = data.meta) == null ? void 0 : _c.uniqueRowIdFields) == null ? void 0 : _d.length);
const options = useMemo(() => {
const options2 = {
columns: memoizedColumns,
data: memoizedData,
disableResizing: !resizable,
stateReducer,
autoResetPage: false,
initialState: getInitialState(initialSortBy, memoizedColumns),
autoResetFilters: false,
sortTypes: {
// the builtin number type on react-table does not handle NaN values
number: sortNumber,
// should be replaced with the builtin string when react-table is upgraded,
// see https://github.com/tannerlinsley/react-table/pull/3235
"alphanumeric-insensitive": sortCaseInsensitive
}
};
if (hasUniqueId) {
options2.getRowId = (row, relativeIndex) => getRowUniqueId(data, relativeIndex);
options2.autoResetExpanded = false;
}
return options2;
}, [initialSortBy, memoizedColumns, memoizedData, resizable, stateReducer, hasUniqueId, data]);
const {
getTableProps,
headerGroups,
footerGroups,
rows,
prepareRow,
totalColumnsWidth,
page,
state,
gotoPage,
setPageSize,
pageOptions,
toggleAllRowsExpanded
} = useTable(options, useFilters, useSortBy, useAbsoluteLayout, useResizeColumns, useExpanded, usePagination);
const extendedState = state;
toggleAllRowsExpandedRef.current = toggleAllRowsExpanded;
useEffect(() => {
if (!footerOptions) {
setFooterItems(footerValues);
}
}, [footerValues, footerOptions]);
useEffect(() => {
var _a2;
if (!footerOptions) {
return;
}
if (!footerOptions.show) {
setFooterItems(void 0);
return;
}
if (isCountRowsSet) {
const footerItemsCountRows = [];
footerItemsCountRows[0] = (_a2 = rows.length.toString()) != null ? _a2 : data.length.toString();
setFooterItems(footerItemsCountRows);
return;
}
const footerItems2 = getFooterItems(
headerGroups[0].headers,
createFooterCalculationValues(rows),
footerOptions,
theme
);
setFooterItems(footerItems2);
}, [footerOptions, theme, state.filters, data]);
let listHeight = height - (headerHeight + footerHeight);
if (enablePagination) {
listHeight -= tableStyles.cellHeight;
}
const pageSize = Math.round(listHeight / tableStyles.rowHeight) - 1;
useEffect(() => {
if (pageSize <= 0) {
return;
}
setPageSize(pageSize);
}, [pageSize, setPageSize]);
useEffect(() => {
if (data.length / pageSize < state.pageIndex) {
gotoPage(0);
}
}, [data]);
useResetVariableListSizeCache(extendedState, listRef, data, hasUniqueId);
useFixScrollbarContainer(variableSizeListScrollbarRef, tableDivRef);
const onNavigate = useCallback(
(toPage) => {
gotoPage(toPage - 1);
},
[gotoPage]
);
const itemCount = enablePagination ? page.length : rows.length;
let paginationEl = null;
if (enablePagination) {
const itemsRangeStart = state.pageIndex * state.pageSize + 1;
let itemsRangeEnd = itemsRangeStart + state.pageSize - 1;
const isSmall = width < 550;
if (itemsRangeEnd > data.length) {
itemsRangeEnd = data.length;
}
const numRows = rows.length;
const displayedEnd = itemsRangeEnd < rows.length ? itemsRangeEnd : rows.length;
paginationEl = /* @__PURE__ */ jsxs("div", { className: tableStyles.paginationWrapper, children: [
/* @__PURE__ */ jsx(
Pagination,
{
currentPage: state.pageIndex + 1,
numberOfPages: pageOptions.length,
showSmallVersion: isSmall,
onNavigate
}
),
isSmall ? null : /* @__PURE__ */ jsx("div", { className: tableStyles.paginationSummary, children: /* @__PURE__ */ jsxs(Trans, { i18nKey: "grafana-ui.table.pagination-summary", children: [
{ itemsRangeStart },
" - ",
{ displayedEnd },
" of ",
{ numRows },
" rows"
] }) })
] });
}
const longestField = fieldConfig ? guessLongestField(fieldConfig, data) : void 0;
let textWrapField = void 0;
if (fieldConfig !== void 0) {
data.fields.forEach((field) => {
fieldConfig.overrides.forEach((override) => {
const matcher = getFieldMatcher(override.matcher);
if (matcher(field, data, [data])) {
for (const property of override.properties) {
if (property.id === "custom.cellOptions" && property.value.wrapText) {
textWrapField = field;
}
}
}
});
});
}
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsxs(
"div",
{
...getTableProps(),
className: tableStyles.table,
"aria-label": ariaLabel,
role: "table",
ref: tableDivRef,
style: { width, height },
children: [
/* @__PURE__ */ jsx(CustomScrollbar, { hideVerticalTrack: true, children: /* @__PURE__ */ jsxs("div", { className: tableStyles.tableContentWrapper(totalColumnsWidth), children: [
!noHeader && /* @__PURE__ */ jsx(HeaderRow, { headerGroups, showTypeIcons, tableStyles }),
itemCount > 0 ? /* @__PURE__ */ jsx(
"div",
{
"data-testid": selectors.components.Panels.Visualization.Table.body,
ref: variableSizeListScrollbarRef,
children: /* @__PURE__ */ jsx(
RowsList,
{
headerGroups,
data,
rows,
width,
cellHeight,
headerHeight,
rowHeight: tableStyles.rowHeight,
itemCount,
pageIndex: state.pageIndex,
listHeight,
listRef,
tableState: state,
prepareRow,
timeRange,
onCellFilterAdded,
nestedDataField,
tableStyles,
footerPaginationEnabled: Boolean(enablePagination),
enableSharedCrosshair,
initialRowIndex,
longestField,
textWrapField,
getActions,
replaceVariables,
setInspectCell
}
)
}
) : /* @__PURE__ */ jsx("div", { style: { height: height - headerHeight, width }, className: tableStyles.noData, children: noValuesDisplayText }),
footerItems && /* @__PURE__ */ jsx(
FooterRow,
{
isPaginationVisible: Boolean(enablePagination),
footerValues: footerItems,
footerGroups,
totalColumnsWidth,
tableStyles
}
)
] }) }),
paginationEl
]
}
),
inspectCell !== null && /* @__PURE__ */ jsx(
TableCellInspector,
{
mode: inspectCell.mode,
value: inspectCell.value,
onDismiss: () => {
setInspectCell(null);
}
}
)
] });
});
Table.displayName = "Table";
export { Table };
//# sourceMappingURL=Table.mjs.map