@grafana/ui
Version:
Grafana Components Library
397 lines (394 loc) • 14 kB
JavaScript
import { useState, useMemo, useEffect, useRef, useLayoutEffect, useCallback } from 'react';
import { varPreLine } from 'uwrap';
import { fieldReducers, FieldType, reduceField, formattedValueToString } from '@grafana/data';
import { useTheme2 } from '../../../themes/ThemeContext.mjs';
import { TableCellDisplayMode } from '@grafana/schema';
import { TABLE } from './constants.mjs';
import { processNestedTableRows, getDisplayName, getColumnTypes, applySort, getMaxWrapCell, getCellOptions, getCellLinks } from './utils.mjs';
const getDisplayedValue = (row, key, fields) => {
const field = fields.find((field2) => getDisplayName(field2) === key);
if (!field || !field.display) {
return "";
}
const displayedValue = formattedValueToString(field.display(row[key]));
return displayedValue;
};
function useFilteredRows(rows, fields, { hasNestedFrames }) {
const [filter, setFilter] = useState({});
const filterValues = useMemo(() => Object.entries(filter), [filter]);
const crossFilterOrder = useMemo(
() => Array.from(new Set(filterValues.map(([key]) => key))),
[filterValues]
);
const [filteredRows, crossFilterRows] = useMemo(() => {
const crossFilterRows2 = {};
const filterRows = (row) => {
var _a;
for (const [key, value] of filterValues) {
const displayedValue = getDisplayedValue(row, key, fields);
if (!value.filteredSet.has(displayedValue)) {
return false;
}
crossFilterRows2[key] = (_a = crossFilterRows2[key]) != null ? _a : [];
crossFilterRows2[key].push(row);
}
return true;
};
const filteredRows2 = hasNestedFrames ? processNestedTableRows(rows, (parents) => parents.filter(filterRows)) : rows.filter(filterRows);
return [filteredRows2, crossFilterRows2];
}, [filterValues, rows, fields, hasNestedFrames]);
return {
rows: filteredRows,
filter,
setFilter,
crossFilterOrder,
crossFilterRows
};
}
function useSortedRows(rows, fields, { initialSortBy, hasNestedFrames }) {
const initialSortColumns = useMemo(
() => {
var _a;
return (_a = initialSortBy == null ? void 0 : initialSortBy.flatMap(({ displayName, desc }) => {
if (!fields.some((f) => getDisplayName(f) === displayName)) {
return [];
}
return [
{
columnKey: displayName,
direction: desc ? "DESC" : "ASC"
}
];
})) != null ? _a : [];
},
[]
// eslint-disable-line react-hooks/exhaustive-deps
);
const [sortColumns, setSortColumns] = useState(initialSortColumns);
const columnTypes = useMemo(() => getColumnTypes(fields), [fields]);
const sortedRows = useMemo(
() => applySort(rows, fields, sortColumns, columnTypes, hasNestedFrames),
[rows, fields, sortColumns, hasNestedFrames, columnTypes]
);
return {
rows: sortedRows,
sortColumns,
setSortColumns
};
}
const PAGINATION_HEIGHT = 38;
function usePaginatedRows(rows, { height, width, headerHeight, footerHeight, rowHeight, enabled }) {
const [page, setPage] = useState(0);
const numRows = rows.length;
const avgRowHeight = useMemo(() => {
if (!enabled) {
return 0;
}
if (typeof rowHeight === "number") {
return rowHeight;
}
return rows.slice(0, 100).reduce((avg, row, _, { length }) => avg + rowHeight(row) / length, 0);
}, [rows, rowHeight, enabled]);
const { numPages, rowsPerPage, pageRangeStart, pageRangeEnd, smallPagination } = useMemo(() => {
if (!enabled) {
return { numPages: 0, rowsPerPage: 0, pageRangeStart: 1, pageRangeEnd: numRows, smallPagination: false };
}
const rowAreaHeight = height - headerHeight - footerHeight - PAGINATION_HEIGHT;
const heightPerRow = Math.floor(rowAreaHeight / (avgRowHeight || 1));
let rowsPerPage2 = heightPerRow > 1 ? heightPerRow : 1;
const pageRangeStart2 = page * rowsPerPage2 + 1;
let pageRangeEnd2 = pageRangeStart2 + rowsPerPage2 - 1;
if (pageRangeEnd2 > numRows) {
pageRangeEnd2 = numRows;
}
const smallPagination2 = width < TABLE.PAGINATION_LIMIT;
const numPages2 = Math.ceil(numRows / rowsPerPage2);
return {
numPages: numPages2,
rowsPerPage: rowsPerPage2,
pageRangeStart: pageRangeStart2,
pageRangeEnd: pageRangeEnd2,
smallPagination: smallPagination2
};
}, [width, height, headerHeight, footerHeight, avgRowHeight, enabled, numRows, page]);
useEffect(() => {
if (!enabled) {
return;
}
if (page > numPages) {
setPage(numPages - 1);
}
}, [numPages, enabled, page, setPage]);
const paginatedRows = useMemo(() => {
if (!enabled) {
return rows;
}
const pageOffset = page * rowsPerPage;
return rows.slice(pageOffset, pageOffset + rowsPerPage);
}, [page, rowsPerPage, rows, enabled]);
return {
rows: paginatedRows,
page: enabled ? page : -1,
setPage,
numPages,
rowsPerPage,
pageRangeStart,
pageRangeEnd,
smallPagination
};
}
function useFooterCalcs(rows, fields, { enabled, footerOptions, isCountRowsSet }) {
return useMemo(() => {
const footerReducers = footerOptions == null ? void 0 : footerOptions.reducer;
if (!enabled || !footerOptions || !Array.isArray(footerReducers) || !footerReducers.length) {
return [];
}
return fields.map((field, index) => {
var _a, _b, _c, _d;
if ((_a = field.state) == null ? void 0 : _a.calcs) {
(_b = field.state) == null ? true : delete _b.calcs;
}
if (isCountRowsSet) {
return index === 0 ? `${rows.length}` : "";
}
if (index === 0) {
const footerCalcReducer = footerReducers[0];
return footerCalcReducer ? fieldReducers.get(footerCalcReducer).name : "";
}
if (field.type !== FieldType.number) {
return "";
}
const displayFn = field.display;
if (!displayFn) {
return "";
}
if (((_c = footerOptions.fields) == null ? void 0 : _c.length) && !((_d = footerOptions.fields) == null ? void 0 : _d.includes(getDisplayName(field)))) {
return "";
}
const calc = footerReducers[0];
const value = reduceField({
field: {
...field,
values: rows.map((row) => row[getDisplayName(field)])
},
reducers: footerReducers
})[calc];
return formattedValueToString(displayFn(value));
});
}, [fields, enabled, footerOptions, isCountRowsSet, rows]);
}
function useTypographyCtx() {
const theme = useTheme2();
const typographyCtx = useMemo(() => {
const font = `${theme.typography.fontSize}px ${theme.typography.fontFamily}`;
const canvas = document.createElement("canvas");
const ctx = canvas.getContext("2d");
const letterSpacing = 0.15;
ctx.letterSpacing = `${letterSpacing}px`;
ctx.font = font;
const 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 = ctx.measureText(txt).width;
const avgCharWidth = txtWidth / txt.length + letterSpacing;
const { count } = varPreLine(ctx);
const calcRowHeight = (text, cellWidth, defaultHeight) => {
if (text === "") {
return defaultHeight;
}
const numLines = count(text, cellWidth);
const totalHeight = numLines * TABLE.LINE_HEIGHT + 2 * TABLE.CELL_PADDING;
return Math.max(totalHeight, defaultHeight);
};
return {
calcRowHeight,
ctx,
font,
avgCharWidth
};
}, [theme.typography.fontSize, theme.typography.fontFamily]);
return typographyCtx;
}
const ICON_WIDTH = 16;
const ICON_GAP = 4;
function useHeaderHeight({
fields,
enabled,
columnWidths,
defaultHeight,
sortColumns,
typographyCtx: { calcRowHeight, avgCharWidth },
showTypeIcons = false
}) {
const perIconSpace = ICON_WIDTH + ICON_GAP;
const columnAvailableWidths = useMemo(
() => columnWidths.map((c, idx) => {
var _a, _b, _c;
let width = c - 2 * TABLE.CELL_PADDING - TABLE.BORDER_RIGHT;
if ((_c = (_b = (_a = fields[idx]) == null ? void 0 : _a.config) == null ? void 0 : _b.custom) == null ? void 0 : _c.filterable) {
width -= perIconSpace;
}
if (sortColumns.some((col) => col.columnKey === getDisplayName(fields[idx]))) {
width -= perIconSpace;
}
if (showTypeIcons) {
width -= perIconSpace;
}
return Math.floor(width);
}),
[fields, columnWidths, sortColumns, showTypeIcons, perIconSpace]
);
const [wrappedColHeaderIdxs, hasWrappedColHeaders] = useMemo(() => {
let hasWrappedColHeaders2 = false;
return [
fields.map((field) => {
var _a, _b, _c;
const wrapText = (_c = (_b = (_a = field.config) == null ? void 0 : _a.custom) == null ? void 0 : _b.wrapHeaderText) != null ? _c : false;
if (wrapText) {
hasWrappedColHeaders2 = true;
}
return wrapText;
}),
hasWrappedColHeaders2
];
}, [fields]);
const maxWrapCellOptions = useMemo(
() => ({
colWidths: columnAvailableWidths,
avgCharWidth,
wrappedColIdxs: wrappedColHeaderIdxs
}),
[columnAvailableWidths, avgCharWidth, wrappedColHeaderIdxs]
);
const headerHeight = useMemo(() => {
if (!enabled) {
return 0;
}
if (!hasWrappedColHeaders) {
return defaultHeight - TABLE.CELL_PADDING;
}
const { text: maxLinesText, idx: maxLinesIdx } = getMaxWrapCell(fields, -1, maxWrapCellOptions);
return calcRowHeight(maxLinesText, columnAvailableWidths[maxLinesIdx], defaultHeight) - TABLE.CELL_PADDING;
}, [fields, enabled, hasWrappedColHeaders, maxWrapCellOptions, calcRowHeight, columnAvailableWidths, defaultHeight]);
return headerHeight;
}
function useRowHeight({
columnWidths,
fields,
hasNestedFrames,
defaultHeight,
headerHeight,
expandedRows,
typographyCtx: { calcRowHeight, avgCharWidth }
}) {
const [wrappedColIdxs, hasWrappedCols] = useMemo(() => {
let hasWrappedCols2 = false;
return [
fields.map((field) => {
if (field.type !== FieldType.string) {
return false;
}
const cellOptions = getCellOptions(field);
const wrapText = "wrapText" in cellOptions && cellOptions.wrapText;
const type = cellOptions.type;
const result = !!wrapText && type !== TableCellDisplayMode.Image;
if (result === true) {
hasWrappedCols2 = true;
}
return result;
}),
hasWrappedCols2
];
}, [fields]);
const colWidths = useMemo(
() => columnWidths.map((c) => c - 2 * TABLE.CELL_PADDING - TABLE.BORDER_RIGHT),
[columnWidths]
);
const maxWrapCellOptions = useMemo(
() => ({
colWidths,
avgCharWidth,
wrappedColIdxs
}),
[colWidths, avgCharWidth, wrappedColIdxs]
);
const rowHeight = useMemo(() => {
if (!hasNestedFrames && !hasWrappedCols) {
return defaultHeight;
}
return (row) => {
var _a, _b;
if (Number(row.__depth) > 0) {
if (!expandedRows[row.__index]) {
return 0;
}
const rowCount = (_b = (_a = row.data) == null ? void 0 : _a.length) != null ? _b : 0;
return Math.max(defaultHeight, defaultHeight * rowCount + headerHeight);
}
const { text: maxLinesText, idx: maxLinesIdx } = getMaxWrapCell(fields, row.__index, maxWrapCellOptions);
return calcRowHeight(maxLinesText, colWidths[maxLinesIdx], defaultHeight);
};
}, [
calcRowHeight,
defaultHeight,
expandedRows,
fields,
hasNestedFrames,
hasWrappedCols,
headerHeight,
maxWrapCellOptions,
colWidths
]);
return rowHeight;
}
const INITIAL_COL_RESIZE_STATE = Object.freeze({ columnKey: void 0, width: 0 });
function useColumnResize(onColumnResize = () => {
}) {
const colResizeState = useRef({ ...INITIAL_COL_RESIZE_STATE });
const pointerIsDown = useRef(false);
useLayoutEffect(() => {
function pointerDown(_event) {
pointerIsDown.current = true;
}
function pointerUp(_event) {
pointerIsDown.current = false;
}
window.addEventListener("pointerdown", pointerDown);
window.addEventListener("pointerup", pointerUp);
return () => {
window.removeEventListener("pointerdown", pointerDown);
window.removeEventListener("pointerup", pointerUp);
};
});
const dispatchEvent = useCallback(() => {
if (colResizeState.current.columnKey) {
onColumnResize(colResizeState.current.columnKey, Math.floor(colResizeState.current.width));
colResizeState.current = { ...INITIAL_COL_RESIZE_STATE };
}
window.removeEventListener("click", dispatchEvent, { capture: true });
}, [onColumnResize]);
const dataGridResizeHandler = useCallback(
(column, width) => {
if (!colResizeState.current.columnKey) {
window.addEventListener("click", dispatchEvent, { capture: true });
}
colResizeState.current.columnKey = column.key;
colResizeState.current.width = width;
if (!pointerIsDown.current) {
dispatchEvent();
}
},
[dispatchEvent]
);
return dataGridResizeHandler;
}
function useSingleLink(field, rowIdx) {
var _a, _b, _c, _d;
const linksCount = (_b = (_a = field.config.links) == null ? void 0 : _a.length) != null ? _b : 0;
const actionsCount = (_d = (_c = field.config.actions) == null ? void 0 : _c.length) != null ? _d : 0;
const shouldShowLink = linksCount === 1 && actionsCount === 0;
return useMemo(() => {
var _a2;
return (shouldShowLink ? (_a2 = getCellLinks(field, rowIdx)) != null ? _a2 : [] : [])[0];
}, [field, shouldShowLink, rowIdx]);
}
export { useColumnResize, useFilteredRows, useFooterCalcs, useHeaderHeight, usePaginatedRows, useRowHeight, useSingleLink, useSortedRows, useTypographyCtx };
//# sourceMappingURL=hooks.mjs.map