UNPKG

@grafana/ui

Version:
397 lines (394 loc) • 14 kB
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