@grafana/ui
Version:
Grafana Components Library
888 lines (885 loc) • 31.8 kB
JavaScript
import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
import 'react-data-grid/lib/styles.css';
import { css } from '@emotion/css';
import { useMemo, useState, useRef, useEffect, useLayoutEffect, useCallback } from 'react';
import DataGrid, { Row } from 'react-data-grid';
import { useMeasure } from 'react-use';
import { ReducerID, fieldReducers, FieldType, getDefaultTimeRange, DataHoverEvent, DataHoverClearEvent, formattedValueToString } from '@grafana/data';
import { TableCellDisplayMode } from '@grafana/schema';
import { useTheme2, useStyles2 } from '../../../themes/ThemeContext.mjs';
import 'micro-memoize';
import '@emotion/react';
import 'tinycolor2';
import '../../../utils/skeleton.mjs';
import { Trans, t } from '../../../utils/i18n.mjs';
import { ContextMenu } from '../../ContextMenu/ContextMenu.mjs';
import { MenuItem } from '../../Menu/MenuItem.mjs';
import { Pagination } from '../../Pagination/Pagination.mjs';
import '../../PanelChrome/LoadingIndicator.mjs';
import '@grafana/e2e-selectors';
import '../../../utils/dom.mjs';
import '../../../utils/colors.mjs';
import 'slate';
import 'lodash';
import '../../../utils/logger.mjs';
import '../../ElementSelectionContext/ElementSelectionContext.mjs';
import '../../Icon/Icon.mjs';
import '../../Text/Text.mjs';
import '../../Tooltip/Tooltip.mjs';
import '../../Dropdown/Dropdown.mjs';
import '../../ToolbarButton/ToolbarButton.mjs';
import '../../ToolbarButton/ToolbarButtonRow.mjs';
import '../../PanelChrome/TitleItem.mjs';
import { usePanelContext } from '../../PanelChrome/PanelContext.mjs';
import { TableCellInspector, TableCellInspectorMode } from '../TableCellInspector.mjs';
import { HeaderCell } from './Cells/HeaderCell.mjs';
import { RowExpander } from './Cells/RowExpander.mjs';
import { TableCellNG } from './Cells/TableCellNG.mjs';
import { TABLE, COLUMN } from './constants.mjs';
import { getIsNestedTable, getDefaultRowHeight, frameToRecords, getDisplayName, processNestedTableRows, getFooterItemNG, getCellHeightCalculator, getRowHeight, getTextAlign, getFooterStyles, shouldTextOverflow, handleSort, getComparator, getCellColors } from './utils.mjs';
function TableNG(props) {
var _a, _b;
const {
cellHeight,
enablePagination,
enableVirtualization = true,
fieldConfig,
footerOptions,
height,
initialSortBy,
noHeader,
onCellFilterAdded,
onColumnResize,
onSortByChange,
width,
data,
enableSharedCrosshair,
showTypeIcons,
replaceVariables
} = props;
const initialSortColumns = useMemo(() => {
const initialSort = initialSortBy == null ? void 0 : initialSortBy.map(({ displayName, desc }) => {
const matchingField = data.fields.find(({ state }) => (state == null ? void 0 : state.displayName) === displayName);
const columnKey = (matchingField == null ? void 0 : matchingField.name) || displayName;
return {
columnKey,
direction: desc ? "DESC" : "ASC"
};
});
return initialSort != null ? initialSort : [];
}, []);
const [revId, setRevId] = useState(0);
const [contextMenuProps, setContextMenuProps] = useState(null);
const [isInspecting, setIsInspecting] = useState(false);
const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);
const [filter, setFilter] = useState({});
const [page, setPage] = useState(0);
const [, setResizeTrigger] = useState(0);
const [, setReadyForRowHeightCalc] = useState(false);
const [sortColumns, setSortColumns] = useState(initialSortColumns);
const [expandedRows, setExpandedRows] = useState([]);
const [isNestedTable, setIsNestedTable] = useState(false);
const scrollPositionRef = useRef({ x: 0, y: 0 });
const [hasScroll, setHasScroll] = useState(false);
const crossFilterOrder = useRef([]);
const crossFilterRows = useRef({});
const headerCellRefs = useRef({});
const sortColumnsRef = useRef(initialSortColumns);
const prevProps = useRef(props);
const calcsRef = useRef([]);
const [paginationWrapperRef, { height: paginationHeight }] = useMeasure();
const theme = useTheme2();
const panelContext = usePanelContext();
const isFooterVisible = Boolean((footerOptions == null ? void 0 : footerOptions.show) && ((_a = footerOptions.reducer) == null ? void 0 : _a.length));
const isCountRowsSet = Boolean(
(footerOptions == null ? void 0 : footerOptions.countRows) && footerOptions.reducer && footerOptions.reducer.length && footerOptions.reducer[0] === ReducerID.count
);
const tableRef = useRef(null);
useEffect(() => {
var _a2, _b2;
if (prevProps.current.data.fields.length !== props.data.fields.length || ((_a2 = prevProps.current.fieldConfig) == null ? void 0 : _a2.overrides) !== (fieldConfig == null ? void 0 : fieldConfig.overrides) || ((_b2 = prevProps.current.fieldConfig) == null ? void 0 : _b2.defaults) !== (fieldConfig == null ? void 0 : fieldConfig.defaults)) {
setRevId(revId + 1);
}
prevProps.current = props;
}, [props, revId, fieldConfig == null ? void 0 : fieldConfig.overrides, fieldConfig == null ? void 0 : fieldConfig.defaults]);
useLayoutEffect(() => {
if (!isContextMenuOpen) {
return;
}
function onClick(event) {
setIsContextMenuOpen(false);
}
addEventListener("click", onClick);
return () => {
removeEventListener("click", onClick);
};
}, [isContextMenuOpen]);
useEffect(() => {
const hasNestedFrames = getIsNestedTable(props.data);
setIsNestedTable(hasNestedFrames);
}, [props.data]);
useEffect(() => {
const el = tableRef.current;
if (el) {
const gridElement = el == null ? void 0 : el.element;
if (gridElement) {
setHasScroll(
gridElement.scrollHeight > gridElement.clientHeight || gridElement.scrollWidth > gridElement.clientWidth
);
}
}
}, []);
const columnWidth = useMemo(() => {
var _a2, _b2;
setRevId(revId + 1);
return ((_b2 = (_a2 = fieldConfig == null ? void 0 : fieldConfig.defaults) == null ? void 0 : _a2.custom) == null ? void 0 : _b2.width) || "auto";
}, [fieldConfig]);
const defaultRowHeight = getDefaultRowHeight(theme, cellHeight);
const defaultLineHeight = theme.typography.body.lineHeight * theme.typography.fontSize;
const panelPaddingHeight = theme.components.panel.padding * theme.spacing.gridSize * 2;
const rows = useMemo(() => frameToRecords(props.data), [frameToRecords, props.data]);
const columnTypes = useMemo(
() => props.data.fields.reduce((acc, field) => ({ ...acc, [getDisplayName(field)]: field.type }), {}),
[props.data.fields]
);
const textWraps = useMemo(
() => props.data.fields.reduce(
(acc, field) => {
var _a2, _b2, _c, _d;
return {
...acc,
[getDisplayName(field)]: (_d = (_c = (_b2 = (_a2 = field.config) == null ? void 0 : _a2.custom) == null ? void 0 : _b2.cellOptions) == null ? void 0 : _c.wrapText) != null ? _d : false
};
},
{}
),
[props.data.fields]
);
const textWrap = useMemo(() => Object.values(textWraps).some(Boolean), [textWraps]);
const styles = useStyles2(getStyles);
const getColumnWidths = useCallback(() => {
const widths = {};
props.data.fields.forEach((field) => {
var _a2, _b2;
const displayName = getDisplayName(field);
const configWidth = (_b2 = (_a2 = field.config) == null ? void 0 : _a2.custom) == null ? void 0 : _b2.width;
const totalWidth = typeof configWidth === "number" ? configWidth : COLUMN.DEFAULT_WIDTH;
const contentWidth = totalWidth - 2 * TABLE.CELL_PADDING - 1;
widths[displayName] = contentWidth;
});
Object.keys(headerCellRefs.current).forEach((key) => {
const headerCell = headerCellRefs.current[key];
if (headerCell.offsetWidth > 0) {
widths[key] = headerCell.offsetWidth;
}
});
return widths;
}, [props.data.fields]);
const headersLength = useMemo(() => {
return props.data.fields.length;
}, [props.data.fields]);
const fieldDisplayType = useMemo(() => {
return props.data.fields.reduce((acc, field) => {
var _a2, _b2, _c;
if ((_c = (_b2 = (_a2 = field.config) == null ? void 0 : _a2.custom) == null ? void 0 : _b2.cellOptions) == null ? void 0 : _c.type) {
acc[getDisplayName(field)] = field.config.custom.cellOptions.type;
}
return acc;
}, {});
}, [props.data.fields]);
const fieldsData = useMemo(
() => ({
headersLength,
textWraps,
columnTypes,
fieldDisplayType,
columnWidths: getColumnWidths()
}),
[textWraps, columnTypes, getColumnWidths, headersLength, fieldDisplayType]
);
const filteredRows = useMemo(() => {
const filterValues = Object.entries(filter);
if (filterValues.length === 0) {
crossFilterOrder.current = [];
return rows;
}
const getDisplayedValue = (row, key) => {
const field = props.data.fields.find((field2) => getDisplayName(field2) === key);
if (!field || !field.display) {
return "";
}
const displayedValue = formattedValueToString(field.display(row[key]));
return displayedValue;
};
const filterKeys = new Set(filterValues.map(([key]) => key));
filterKeys.forEach((key) => {
if (!crossFilterOrder.current.includes(key)) {
crossFilterOrder.current.push(key);
}
});
crossFilterOrder.current = crossFilterOrder.current.filter((key) => filterKeys.has(key));
crossFilterRows.current = {};
if (isNestedTable) {
return processNestedTableRows(
rows,
(parents) => parents.filter((row) => {
for (const [key, value] of filterValues) {
const displayedValue = getDisplayedValue(row, key);
if (!value.filteredSet.has(displayedValue)) {
return false;
}
if (!crossFilterRows.current[key]) {
crossFilterRows.current[key] = [row];
} else {
crossFilterRows.current[key].push(row);
}
}
return true;
})
);
}
return rows.filter((row) => {
for (const [key, value] of filterValues) {
const displayedValue = getDisplayedValue(row, key);
if (!value.filteredSet.has(displayedValue)) {
return false;
}
if (!crossFilterRows.current[key]) {
crossFilterRows.current[key] = [row];
} else {
crossFilterRows.current[key].push(row);
}
}
return true;
});
}, [rows, filter, isNestedTable, props.data.fields]);
const sortedRows = useMemo(() => {
if (sortColumns.length === 0) {
return filteredRows;
}
const compareRows = (a, b) => {
let result = 0;
for (let i = 0; i < sortColumns.length; i++) {
const { columnKey, direction } = sortColumns[i];
const compare = getComparator(columnTypes[columnKey]);
const sortDir = direction === "ASC" ? 1 : -1;
result = sortDir * compare(a[columnKey], b[columnKey]);
if (result !== 0) {
break;
}
}
return result;
};
if (isNestedTable) {
return processNestedTableRows(filteredRows, (parents) => [...parents].sort(compareRows));
}
return filteredRows.slice().sort((a, b) => compareRows(a, b));
}, [filteredRows, sortColumns, columnTypes, isNestedTable]);
const numRows = sortedRows.length;
let headerCellHeight = TABLE.MAX_CELL_HEIGHT;
if (noHeader) {
headerCellHeight = 0;
} else if (!noHeader && Object.keys(headerCellRefs.current).length > 0) {
headerCellHeight = headerCellRefs.current[Object.keys(headerCellRefs.current)[0]].getBoundingClientRect().height;
}
let rowsPerPage = Math.floor(
(height - headerCellHeight - TABLE.SCROLL_BAR_WIDTH - paginationHeight - panelPaddingHeight) / defaultRowHeight
);
if (isFooterVisible) {
rowsPerPage -= 1;
}
if (rowsPerPage < 1) {
rowsPerPage = 1;
}
const numberOfPages = Math.ceil(numRows / rowsPerPage);
if (page > numberOfPages) {
setPage(numberOfPages - 1);
}
const itemsRangeStart = page * rowsPerPage + 1;
let displayedEnd = itemsRangeStart + rowsPerPage - 1;
if (displayedEnd > numRows) {
displayedEnd = numRows;
}
const smallPagination = width < TABLE.PAGINATION_LIMIT;
const paginatedRows = useMemo(() => {
const pageOffset = page * rowsPerPage;
return sortedRows.slice(pageOffset, pageOffset + rowsPerPage);
}, [rows, sortedRows, page, rowsPerPage]);
useMemo(() => {
calcsRef.current = props.data.fields.map((field, index) => {
var _a2, _b2, _c;
if ((_a2 = field.state) == null ? void 0 : _a2.calcs) {
(_b2 = field.state) == null ? true : delete _b2.calcs;
}
if (isCountRowsSet) {
return index === 0 ? `${sortedRows.length}` : "";
}
if (index === 0) {
const footerCalcReducer = (_c = footerOptions == null ? void 0 : footerOptions.reducer) == null ? void 0 : _c[0];
return footerCalcReducer ? fieldReducers.get(footerCalcReducer).name : "";
}
return getFooterItemNG(sortedRows, field, footerOptions);
});
}, [sortedRows, props.data.fields, footerOptions, isCountRowsSet]);
const onCellExpand = (rowIdx) => {
if (!expandedRows.includes(rowIdx)) {
setExpandedRows([...expandedRows, rowIdx]);
} else {
setExpandedRows(expandedRows.filter((id) => id !== rowIdx));
}
setResizeTrigger((prev) => prev + 1);
};
const { ctx, avgCharWidth } = useMemo(() => {
const font = `${theme.typography.fontSize}px ${theme.typography.fontFamily}`;
const canvas = document.createElement("canvas");
const ctx2 = canvas.getContext("2d");
const letterSpacing = 0.15;
ctx2.letterSpacing = `${letterSpacing}px`;
ctx2.font = font;
let txt = "Lorem Ipsum is simply dummy text of the printing and typesetting industry. Lorem Ipsum has been the industry's standard dummy text ever since the 1500s";
const txtWidth = ctx2.measureText(txt).width;
const avgCharWidth2 = txtWidth / txt.length + letterSpacing;
return {
ctx: ctx2,
font,
avgCharWidth: avgCharWidth2
};
}, [theme.typography.fontSize, theme.typography.fontFamily]);
const columns = useMemo(
() => mapFrameToDataGrid({
frame: props.data,
calcsRef,
options: {
columnTypes,
textWraps,
columnWidth,
crossFilterOrder,
crossFilterRows,
defaultLineHeight,
defaultRowHeight,
expandedRows,
filter,
headerCellRefs,
isCountRowsSet,
onCellFilterAdded,
ctx,
onSortByChange,
rows,
setContextMenuProps,
setFilter,
setIsInspecting,
setSortColumns,
sortColumnsRef,
styles,
theme,
showTypeIcons,
replaceVariables,
...props
},
handlers: {
onCellExpand,
onColumnResize
},
// Adjust table width to account for the scroll bar width
availableWidth: width - (hasScroll ? TABLE.SCROLL_BAR_WIDTH + TABLE.SCROLL_BAR_MARGIN : 0)
}),
[props.data, calcsRef, filter, expandedRows, expandedRows.length, footerOptions, width, hasScroll, sortedRows]
// eslint-disable-line react-hooks/exhaustive-deps
);
useLayoutEffect(() => {
setReadyForRowHeightCalc(Object.keys(headerCellRefs.current).length > 0);
}, [columns]);
const renderMenuItems = () => {
return /* @__PURE__ */ jsx(Fragment, { children: /* @__PURE__ */ jsx(
MenuItem,
{
label: t("grafana-ui.table.inspect-menu-label", "Inspect value"),
onClick: () => {
setIsInspecting(true);
},
className: styles.menuItem
}
) });
};
const cellHeightCalc = useMemo(() => {
return getCellHeightCalculator(ctx, defaultLineHeight, defaultRowHeight, TABLE.CELL_PADDING);
}, [ctx, defaultLineHeight, defaultRowHeight]);
const calculateRowHeight = useCallback(
(row) => {
var _a2, _b2, _c, _d, _e;
if (Number(row.__depth) === 1 && !expandedRows.includes(Number(row.__index))) {
return 0;
} else if (Number(row.__depth) === 1 && expandedRows.includes(Number(row.__index))) {
const headerCount = ((_c = (_b2 = (_a2 = row == null ? void 0 : row.data) == null ? void 0 : _a2.meta) == null ? void 0 : _b2.custom) == null ? void 0 : _c.noHeader) ? 0 : 1;
const rowCount = (_e = (_d = row.data) == null ? void 0 : _d.length) != null ? _e : 0;
return Math.max(defaultRowHeight, defaultRowHeight * (rowCount + headerCount));
}
return getRowHeight(row, cellHeightCalc, avgCharWidth, defaultRowHeight, fieldsData);
},
[expandedRows, avgCharWidth, defaultRowHeight, fieldsData, cellHeightCalc]
);
const handleScroll = (event) => {
const target = event.currentTarget;
scrollPositionRef.current = {
x: target.scrollLeft,
y: target.scrollTop
};
};
useEffect(() => {
if (initialSortColumns.length > 0) {
setSortColumns(initialSortColumns);
}
}, [initialSortColumns]);
useEffect(() => {
var _a2;
if ((_a2 = tableRef.current) == null ? void 0 : _a2.element) {
tableRef.current.element.scrollLeft = scrollPositionRef.current.x;
tableRef.current.element.scrollTop = scrollPositionRef.current.y;
}
}, [revId]);
return /* @__PURE__ */ jsxs(Fragment, { children: [
/* @__PURE__ */ jsx(
DataGrid,
{
ref: tableRef,
className: styles.dataGrid,
enableVirtualization,
rows: enablePagination ? paginatedRows : sortedRows,
columns,
headerRowHeight: noHeader ? 0 : void 0,
defaultColumnOptions: {
sortable: true,
resizable: true
},
rowHeight: textWrap || isNestedTable ? calculateRowHeight : defaultRowHeight,
style: { width, height: height - (enablePagination ? paginationHeight : 0) },
renderers: {
renderRow: (key, props2) => myRowRenderer(key, props2, expandedRows, panelContext, data, enableSharedCrosshair != null ? enableSharedCrosshair : false)
},
onScroll: handleScroll,
onCellContextMenu: ({ row, column }, event) => {
event.preventGridDefault();
event.preventDefault();
const cellValue = row[column.key];
setContextMenuProps({
// rowIdx: rows.indexOf(row),
value: String(cellValue != null ? cellValue : ""),
top: event.clientY,
left: event.clientX
});
setIsContextMenuOpen(true);
},
sortColumns,
bottomSummaryRows: isFooterVisible ? [{}] : void 0,
onColumnResize: () => {
if (textWrap) {
setResizeTrigger((prev) => prev + 1);
}
}
},
`DataGrid${revId}`
),
enablePagination && /* @__PURE__ */ jsxs("div", { className: styles.paginationContainer, ref: paginationWrapperRef, children: [
/* @__PURE__ */ jsx(
Pagination,
{
className: "table-ng-pagination",
currentPage: page + 1,
numberOfPages,
showSmallVersion: smallPagination,
onNavigate: (toPage) => {
setPage(toPage - 1);
}
}
),
!smallPagination && /* @__PURE__ */ jsx("div", { className: styles.paginationSummary, children: /* @__PURE__ */ jsxs(Trans, { i18nKey: "grafana-ui.table.pagination-summary", children: [
{ itemsRangeStart },
" - ",
{ displayedEnd },
" of ",
{ numRows },
" rows"
] }) })
] }),
isContextMenuOpen && /* @__PURE__ */ jsx(
ContextMenu,
{
x: (contextMenuProps == null ? void 0 : contextMenuProps.left) || 0,
y: (contextMenuProps == null ? void 0 : contextMenuProps.top) || 0,
renderMenuItems,
focusOnOpen: false
}
),
isInspecting && /* @__PURE__ */ jsx(
TableCellInspector,
{
mode: (_b = contextMenuProps == null ? void 0 : contextMenuProps.mode) != null ? _b : TableCellInspectorMode.text,
value: contextMenuProps == null ? void 0 : contextMenuProps.value,
onDismiss: () => {
setIsInspecting(false);
setContextMenuProps(null);
}
}
)
] });
}
function mapFrameToDataGrid({
frame,
calcsRef,
options,
handlers,
availableWidth
}) {
const {
columnTypes,
textWraps,
crossFilterOrder,
crossFilterRows,
defaultLineHeight,
defaultRowHeight,
expandedRows,
filter,
headerCellRefs,
isCountRowsSet,
onCellFilterAdded,
ctx,
onSortByChange,
rows,
setContextMenuProps,
setFilter,
setIsInspecting,
setSortColumns,
sortColumnsRef,
styles,
theme,
timeRange,
getActions,
showTypeIcons,
replaceVariables
} = options;
const { onCellExpand, onColumnResize } = handlers;
const columns = [];
const hasNestedFrames = getIsNestedTable(frame);
if (hasNestedFrames) {
const expanderField = {
name: "",
type: FieldType.other,
config: {},
values: []
};
columns.push({
key: "expanded",
name: "",
field: expanderField,
cellClass: styles.cell,
colSpan(args) {
return args.type === "ROW" && Number(args.row.__depth) === 1 ? frame.fields.length : 1;
},
renderCell: ({ row }) => {
var _a, _b, _c;
if (Number(row.__depth) === 0) {
const rowIdx = Number(row.__index);
return /* @__PURE__ */ jsx(
RowExpander,
{
height: defaultRowHeight,
onCellExpand: () => onCellExpand(rowIdx),
isExpanded: expandedRows.includes(rowIdx)
}
);
}
let expandedColumns = [];
let expandedRecords = [];
if (row.data) {
expandedColumns = mapFrameToDataGrid({
frame: row.data,
calcsRef,
options: { ...options },
handlers: { onCellExpand, onColumnResize },
availableWidth
});
expandedRecords = frameToRecords(row.data);
}
return /* @__PURE__ */ jsx(
DataGrid,
{
rows: expandedRecords,
columns: expandedColumns,
rowHeight: defaultRowHeight,
className: styles.dataGrid,
style: { height: "100%", overflow: "visible", marginLeft: COLUMN.EXPANDER_WIDTH - 1 },
headerRowHeight: ((_c = (_b = (_a = row.data) == null ? void 0 : _a.meta) == null ? void 0 : _b.custom) == null ? void 0 : _c.noHeader) ? 0 : void 0
}
);
},
width: COLUMN.EXPANDER_WIDTH,
minWidth: COLUMN.EXPANDER_WIDTH
});
availableWidth -= COLUMN.EXPANDER_WIDTH;
}
let rowBg = void 0;
for (const field of frame.fields) {
const fieldOptions = field.config.custom;
const cellOptionsExist = fieldOptions !== void 0 && fieldOptions.cellOptions !== void 0;
if (cellOptionsExist && fieldOptions.cellOptions.type === TableCellDisplayMode.ColorBackground && fieldOptions.cellOptions.applyToRow) {
rowBg = (rowIndex) => {
const display = field.display(field.values[rowIndex]);
const colors = getCellColors(theme, fieldOptions.cellOptions, display);
return colors;
};
}
}
let fieldCountWithoutWidth = 0;
frame.fields.map((field, fieldIndex) => {
var _a;
if (field.type === FieldType.nestedFrames || ((_a = field.config.custom) == null ? void 0 : _a.hidden)) {
return;
}
const fieldTableOptions = field.config.custom || {};
const key = getDisplayName(field);
const justifyColumnContent = getTextAlign(field);
const footerStyles = getFooterStyles(justifyColumnContent);
if (fieldTableOptions.width) {
availableWidth -= fieldTableOptions.width;
} else {
fieldCountWithoutWidth++;
}
columns.push({
key,
name: field.name,
field,
cellClass: textWraps[getDisplayName(field)] ? styles.cellWrapped : styles.cell,
renderCell: (props) => {
var _a2, _b, _c, _d;
const { row } = props;
const cellType = (_d = (_c = (_b = (_a2 = field.config) == null ? void 0 : _a2.custom) == null ? void 0 : _b.cellOptions) == null ? void 0 : _c.type) != null ? _d : TableCellDisplayMode.Auto;
const value = row[key];
return /* @__PURE__ */ jsx(
TableCellNG,
{
frame,
value,
field,
theme,
timeRange: timeRange != null ? timeRange : getDefaultTimeRange(),
height: defaultRowHeight,
justifyContent: justifyColumnContent,
rowIdx: row.__index,
shouldTextOverflow: () => shouldTextOverflow(
key,
row,
columnTypes,
headerCellRefs,
ctx,
defaultLineHeight,
defaultRowHeight,
TABLE.CELL_PADDING,
textWraps[getDisplayName(field)],
field,
cellType
),
setIsInspecting,
setContextMenuProps,
getActions,
rowBg,
onCellFilterAdded,
replaceVariables
},
key
);
},
renderSummaryCell: () => {
if (isCountRowsSet && fieldIndex === 0) {
return /* @__PURE__ */ jsxs("div", { style: { display: "flex", justifyContent: "space-between" }, children: [
/* @__PURE__ */ jsx("span", { children: /* @__PURE__ */ jsx(Trans, { i18nKey: "grafana-ui.table.count", children: "Count" }) }),
/* @__PURE__ */ jsx("span", { children: calcsRef.current[fieldIndex] })
] });
}
return /* @__PURE__ */ jsx("div", { className: footerStyles.footerCell, children: calcsRef.current[fieldIndex] });
},
renderHeaderCell: ({ column, sortDirection }) => /* @__PURE__ */ jsx(
HeaderCell,
{
column,
rows,
field,
onSort: (columnKey, direction, isMultiSort) => {
handleSort(columnKey, direction, isMultiSort, setSortColumns, sortColumnsRef);
if (onSortByChange) {
const sortByFields = sortColumnsRef.current.map(({ columnKey: columnKey2, direction: direction2 }) => ({
displayName: columnKey2,
desc: direction2 === "DESC"
}));
onSortByChange(sortByFields);
}
},
direction: sortDirection,
justifyContent: justifyColumnContent,
filter,
setFilter,
onColumnResize,
headerCellRefs,
crossFilterOrder,
crossFilterRows,
showTypeIcons
}
),
width: fieldTableOptions.width,
minWidth: fieldTableOptions.minWidth || COLUMN.DEFAULT_WIDTH
});
});
let sharedWidth = availableWidth / fieldCountWithoutWidth;
for (let i = fieldCountWithoutWidth; i > 0; i--) {
for (const column of columns) {
if (!column.width && column.minWidth > sharedWidth) {
column.width = column.minWidth;
availableWidth -= column.width;
fieldCountWithoutWidth -= 1;
sharedWidth = availableWidth / fieldCountWithoutWidth;
}
}
}
for (const column of columns) {
if (!column.width) {
column.width = sharedWidth;
}
column.minWidth = COLUMN.MIN_WIDTH;
}
return columns;
}
function myRowRenderer(key, props, expandedRows, panelContext, data, enableSharedCrosshair) {
const { row } = props;
const rowIdx = Number(row.__index);
const isExpanded = expandedRows.includes(rowIdx);
if (Number(row.__depth) === 1 && !isExpanded) {
return null;
}
if (row.data) {
return /* @__PURE__ */ jsx(Row, { ...props, "aria-expanded": isExpanded }, key);
}
return /* @__PURE__ */ jsx(
Row,
{
...props,
onMouseEnter: () => onRowHover(rowIdx, panelContext, data, enableSharedCrosshair),
onMouseLeave: () => onRowLeave(panelContext, enableSharedCrosshair)
},
key
);
}
function onRowHover(idx, panelContext, frame, enableSharedCrosshair) {
if (!enableSharedCrosshair) {
return;
}
const timeField = frame.fields.find((f) => f.type === FieldType.time);
if (!timeField) {
return;
}
panelContext.eventBus.publish(
new DataHoverEvent({
point: {
time: timeField.values[idx]
}
})
);
}
function onRowLeave(panelContext, enableSharedCrosshair) {
if (!enableSharedCrosshair) {
return;
}
panelContext.eventBus.publish(new DataHoverClearEvent());
}
const getStyles = (theme) => ({
dataGrid: css({
"--rdg-background-color": theme.colors.background.primary,
"--rdg-header-background-color": theme.colors.background.primary,
"--rdg-border-color": "transparent",
"--rdg-color": theme.colors.text.primary,
"&:hover": {
"--rdg-row-hover-background-color": theme.colors.emphasize(theme.colors.action.hover, 0.6)
},
// If we rely solely on borderInlineEnd which is added from data grid, we
// get a small gap where the gridCell borders meet the column header borders.
// To avoid this, we can unset borderInlineEnd and set borderRight instead.
".rdg-cell": {
borderInlineEnd: "unset",
borderRight: `1px solid ${theme.colors.border.medium}`,
"&:last-child": {
borderRight: "none"
}
},
".rdg-summary-row": {
backgroundColor: theme.colors.background.primary,
"--rdg-summary-border-color": theme.colors.border.medium,
".rdg-cell": {
// Prevent collisions with custom cell components
zIndex: 2,
borderRight: "none"
}
},
// Due to stylistic choices, we do not want borders on the column headers
// other than the bottom border.
"div[role=columnheader]": {
borderBottom: `1px solid ${theme.colors.border.medium}`,
borderInlineEnd: "unset",
".r1y6ywlx7-0-0-beta-46": {
"&:hover": {
borderRight: `3px solid ${theme.colors.text.link}`
}
}
},
"::-webkit-scrollbar": {
width: TABLE.SCROLL_BAR_WIDTH,
height: TABLE.SCROLL_BAR_WIDTH
},
"::-webkit-scrollbar-thumb": {
backgroundColor: "rgba(204, 204, 220, 0.16)",
borderRadius: "4px"
},
"::-webkit-scrollbar-track": {
background: "transparent"
},
"::-webkit-scrollbar-corner": {
backgroundColor: "transparent"
}
}),
menuItem: css({
maxWidth: "200px"
}),
cell: css({
"--rdg-border-color": theme.colors.border.medium,
borderLeft: "none",
whiteSpace: "nowrap",
wordWrap: "break-word",
overflow: "hidden",
textOverflow: "ellipsis",
// Reset default cell styles for custom cell component styling
paddingInline: "0"
}),
cellWrapped: css({
"--rdg-border-color": theme.colors.border.medium,
borderLeft: "none",
whiteSpace: "pre-line",
wordWrap: "break-word",
overflow: "hidden",
textOverflow: "ellipsis",
// Reset default cell styles for custom cell component styling
paddingInline: "0"
}),
paginationContainer: css({
alignItems: "center",
display: "flex",
justifyContent: "center",
marginTop: "8px",
width: "100%"
}),
paginationSummary: css({
color: theme.colors.text.secondary,
fontSize: theme.typography.bodySmall.fontSize,
display: "flex",
justifyContent: "flex-end",
padding: theme.spacing(0, 1, 0, 2)
})
});
export { TableNG, mapFrameToDataGrid, myRowRenderer, onRowHover, onRowLeave };
//# sourceMappingURL=TableNG.mjs.map