UNPKG

@grafana/ui

Version:
565 lines (560 loc) • 20.2 kB
'use strict'; Object.defineProperty(exports, '__esModule', { value: true }); var lodash = require('lodash'); var React = require('react'); var data = require('@grafana/data'); var constants = require('./constants.cjs'); var utils = require('./utils.cjs'); "use strict"; function useFilteredRows(rows, fields, hasNestedFrames) { const [filter, setFilter] = React.useState({}); const filterResult = React.useMemo( () => utils.applyFilter(rows, filter, fields, hasNestedFrames), [rows, filter, fields, hasNestedFrames] ); return { rows: filterResult.filteredRows, filter, setFilter, filterResult }; } function useManagedSort({ sortByBehavior, setSortColumns, sortBy }) { React.useEffect(() => { if (sortByBehavior === "managed" && sortBy) { setSortColumns( sortBy.map(({ displayName, desc }) => ({ columnKey: displayName, direction: desc === true ? "DESC" : "ASC" })) ); } }, [setSortColumns, sortBy, sortByBehavior]); } function useSortedRows(rows, fields, nestedFields, { initialSortBy, hasNestedFrames }) { const allFields = React.useMemo(() => [...fields, ...nestedFields], [fields, nestedFields]); const initialSortColumns = React.useMemo( () => { var _a; return (_a = initialSortBy == null ? void 0 : initialSortBy.flatMap(({ displayName, desc }) => { if (!allFields.some((f) => utils.getDisplayName(f) === displayName)) { return []; } return [ { columnKey: displayName, direction: desc ? "DESC" : "ASC" } ]; })) != null ? _a : []; }, [] // eslint-disable-line react-hooks/exhaustive-deps ); const [sortColumns, setSortColumns] = React.useState(initialSortColumns); const columnTypes = React.useMemo(() => utils.getColumnTypes(fields), [fields]); const sortedRows = React.useMemo( () => utils.applySort(rows, fields, sortColumns, columnTypes, hasNestedFrames), [rows, fields, sortColumns, columnTypes, hasNestedFrames] ); return { rows: sortedRows, sortColumns, setSortColumns }; } const PAGINATION_HEIGHT = 38; function usePaginatedRows(rows, { height, width, headerHeight, footerHeight, rowHeight, enabled, hasNestedFrames }) { const [page, setPage] = React.useState(0); const numRows = React.useMemo(() => rows.filter((r) => r.__depth === 0).length, [rows]); const avgRowHeight = React.useMemo(() => { if (!enabled) { return 0; } if (typeof rowHeight === "number") { return rowHeight; } if (typeof rowHeight === "string") { return constants.TABLE.MAX_CELL_HEIGHT; } let sum = 0; let count = 0; for (let i = 0; i < Math.min(100, rows.length); i++) { const row = rows[i]; if (row.__depth > 0) { continue; } sum += rowHeight(rows[i]); count++; } return sum / count; }, [rows, rowHeight, enabled]); const smallPagination = React.useMemo(() => enabled && width < constants.TABLE.PAGINATION_LIMIT, [enabled, width]); const { numPages, rowsPerPage, pageRangeStart, pageRangeEnd } = React.useMemo(() => { if (!enabled) { return { numPages: 0, rowsPerPage: 0, pageRangeStart: 1, pageRangeEnd: numRows }; } 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 numPages2 = Math.ceil(numRows / rowsPerPage2); return { numPages: numPages2, rowsPerPage: rowsPerPage2, pageRangeStart: pageRangeStart2, pageRangeEnd: pageRangeEnd2 }; }, [height, headerHeight, footerHeight, avgRowHeight, enabled, numRows, page]); React.useLayoutEffect(() => { if (!enabled) { return; } if (page > numPages) { setPage(numPages - 1); } }, [numPages, enabled, page, setPage]); const paginatedRows = React.useMemo(() => { if (!enabled) { return rows; } const result = []; const pageOffset = page * rowsPerPage; let count = hasNestedFrames ? -1 * pageOffset : 0; let i = hasNestedFrames ? 0 : pageOffset; while (count <= rowsPerPage && i < rows.length) { const currRow = rows[i]; i++; if (currRow.__depth === 0) { count++; } if (count >= 1 && count <= rowsPerPage) { result.push(currRow); } } return result; }, [page, rowsPerPage, rows, enabled, hasNestedFrames]); return { rows: paginatedRows, page: enabled ? page : -1, numRows, setPage, numPages, rowsPerPage, pageRangeStart, pageRangeEnd, smallPagination }; } const useNestedRows = (rows, nestedData, hasNestedFrames, nestedFramesFieldName, filter, sortColumns, protoParserEnabled = false) => { const frameToRecords = React.useMemo(() => { if (!hasNestedFrames || !nestedFramesFieldName || !(nestedData == null ? void 0 : nestedData[0])) { return; } return protoParserEnabled ? utils.compileFrameToRecordsV2(nestedData[0]) : utils.compileFrameToRecordsV1(nestedData[0], nestedFramesFieldName); }, [hasNestedFrames, nestedFramesFieldName, nestedData, protoParserEnabled]); return React.useMemo(() => { const result = []; if (!hasNestedFrames || !nestedFramesFieldName || !frameToRecords || !nestedData) { return result; } for (const parentRow of rows) { const nestedFrame = nestedData[parentRow.__index]; if (!nestedFrame) { continue; } const rawRows = frameToRecords(nestedFrame, parentRow.__index); const filterResult = utils.applyFilter(rawRows, filter, nestedFrame.fields, false, parentRow.__index); const sortedRows = utils.applySort( filterResult.filteredRows, nestedFrame.fields, sortColumns, utils.getColumnTypes(nestedFrame.fields) ); result[parentRow.__index] = { raw: rawRows, final: sortedRows, filterResult }; } return result; }, [hasNestedFrames, nestedFramesFieldName, rows, sortColumns, filter, frameToRecords, nestedData]); }; const ICON_WIDTH = 16; const ICON_GAP = 4; function useHeaderHeight({ fields, enabled, columnWidths, sortColumns, typographyCtx, showTypeIcons = false }) { const perIconSpace = ICON_WIDTH + ICON_GAP; const measurers = React.useMemo(() => utils.buildHeaderHeightMeasurers(fields, typographyCtx), [fields, typographyCtx]); const columnAvailableWidths = React.useMemo( () => columnWidths.map((c, idx) => { var _a, _b; if (idx >= fields.length) { return 0; } let width = c - 2 * constants.TABLE.CELL_PADDING - constants.TABLE.BORDER_RIGHT; const field = fields[idx]; if ((_b = (_a = field.config) == null ? void 0 : _a.custom) == null ? void 0 : _b.filterable) { width -= perIconSpace; } if (sortColumns.some((col) => col.columnKey === utils.getDisplayName(field))) { width -= perIconSpace; } if (showTypeIcons) { width -= perIconSpace; } return Math.floor(width) - 1; }), [fields, columnWidths, sortColumns, showTypeIcons, perIconSpace] ); const headerHeight = React.useMemo(() => { if (!enabled) { return 0; } return utils.getRowHeight( fields, { __index: -1, __depth: 0 }, columnAvailableWidths, constants.TABLE.HEADER_HEIGHT, measurers, constants.TABLE.LINE_HEIGHT, constants.TABLE.CELL_PADDING ); }, [fields, enabled, columnAvailableWidths, measurers]); return headerHeight; } const getTrueColWidths = (cw) => cw.map((c) => c - (2 * constants.TABLE.CELL_PADDING + constants.TABLE.BORDER_RIGHT)); function useRowHeight({ columnWidths, fields, defaultHeight, defaultNestedHeight, typographyCtx, maxHeight, hasNestedFrames, nestedData, nestedRows, nestedFields, nestedColWidths, visibleNestedRowCounts, nestedFooterHeight = 0 }) { var _a; const nestedMeasurers = React.useMemo( () => utils.buildCellHeightMeasurers(nestedFields, typographyCtx, maxHeight), [nestedFields, typographyCtx, maxHeight] ); const totalParentWidth = React.useMemo(() => columnWidths.reduce((acc, width) => acc + width, 0), [columnWidths]); const totalNestedWidth = React.useMemo(() => nestedColWidths.reduce((acc, width) => acc + width, 0), [nestedColWidths]); const nestedHasOverflow = React.useMemo(() => totalParentWidth < totalNestedWidth, [totalParentWidth, totalNestedWidth]); const getNestedRowHeightWithCache = React.useMemo(() => { var _a2; if (typeof defaultNestedHeight === "string") { return () => 0; } if (((_a2 = nestedMeasurers == null ? void 0 : nestedMeasurers.length) != null ? _a2 : 0) === 0) { return () => defaultNestedHeight; } const nestedRowCache = visibleNestedRowCounts.map( (count) => count == null ? void 0 : Array(count) ); return (row) => { if (row.__parentIndex == null) { return 0; } const nestedRowCacheEntry = nestedRowCache[row.__parentIndex]; if (nestedRowCacheEntry == null) { return 0; } const trueNestedColWidths = getTrueColWidths(nestedColWidths); let result = nestedRowCacheEntry[row.__index]; if (result == null) { result = nestedRowCacheEntry[row.__index] = utils.getRowHeight( nestedFields, row, trueNestedColWidths, defaultNestedHeight, nestedMeasurers ); } return result; }; }, [nestedFields, nestedColWidths, defaultNestedHeight, nestedMeasurers, visibleNestedRowCounts]); const measurers = React.useMemo( () => utils.buildCellHeightMeasurers(fields, typographyCtx, maxHeight), [fields, typographyCtx, maxHeight] ); const hasWrappedCols = ((_a = measurers == null ? void 0 : measurers.length) != null ? _a : 0) > 0; const getRowHeightWithCache = React.useMemo(() => { if (typeof defaultHeight === "string") { return () => 0; } if (!hasWrappedCols) { return () => defaultHeight; } const trueColWidths = getTrueColWidths(columnWidths); const cache = Array(fields[0].values.length); return (row) => { let result = cache[row.__index]; if (result == null) { result = cache[row.__index] = utils.getRowHeight(fields, row, trueColWidths, defaultHeight, measurers); } return result; }; }, [fields, columnWidths, defaultHeight, measurers, hasWrappedCols]); const rowHeight = React.useMemo(() => { if (typeof defaultHeight === "string" || !(hasWrappedCols || hasNestedFrames)) { return defaultHeight; } if (typeof defaultNestedHeight === "string") { return defaultNestedHeight; } return (row) => { var _a2, _b, _c; if (row.__depth > 0) { const visibleNestedRowCount = visibleNestedRowCounts[row.__index]; if (visibleNestedRowCount == null) { return 0; } if (visibleNestedRowCount === 0) { return constants.TABLE.NESTED_NO_DATA_HEIGHT + constants.TABLE.CELL_PADDING * 2 + nestedFooterHeight; } const nestedHeaderHeight = ((_c = (_b = (_a2 = nestedData == null ? void 0 : nestedData[row.__index]) == null ? void 0 : _a2.meta) == null ? void 0 : _b.custom) == null ? void 0 : _c.noHeader) ? 0 : defaultNestedHeight; const nestedRowsHeight = nestedRows[row.__index].final.reduce( (acc, row2) => acc + getNestedRowHeightWithCache(row2), 0 ); const scrollbarHeight = nestedHasOverflow ? constants.TABLE.SCROLLBAR_AFFORDANCE : 0; return nestedRowsHeight + nestedHeaderHeight + nestedFooterHeight + constants.TABLE.CELL_PADDING * 2 + scrollbarHeight; } return row.__parentIndex != null ? getNestedRowHeightWithCache(row) : getRowHeightWithCache(row); }; }, [ getNestedRowHeightWithCache, getRowHeightWithCache, defaultHeight, defaultNestedHeight, hasNestedFrames, hasWrappedCols, nestedFooterHeight, nestedHasOverflow, nestedRows, nestedData, visibleNestedRowCounts ]); return rowHeight; } const INITIAL_COL_RESIZE_STATE = Object.freeze({ columnKey: void 0, width: 0 }); function useColumnResize(onColumnResize = () => { }, fieldScope) { const colResizeState = React.useRef({ ...INITIAL_COL_RESIZE_STATE }); const pointerIsDown = React.useRef(false); React.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 = React.useCallback(() => { if (colResizeState.current.columnKey) { onColumnResize( colResizeState.current.columnKey, Math.floor(colResizeState.current.width), colResizeState.current.fieldScope ); colResizeState.current = { ...INITIAL_COL_RESIZE_STATE }; } window.removeEventListener("click", dispatchEvent, { capture: true }); }, [onColumnResize]); const dataGridResizeHandler = React.useCallback( (column, width) => { if (!colResizeState.current.columnKey) { window.addEventListener("click", dispatchEvent, { capture: true }); } colResizeState.current.columnKey = column.key; colResizeState.current.width = width; if (fieldScope) { colResizeState.current.fieldScope = fieldScope; } if (!pointerIsDown.current) { dispatchEvent(); } }, [fieldScope, dispatchEvent] ); return dataGridResizeHandler; } function useScrollbarWidth(ref, height) { const [scrollbarWidth, setScrollbarWidth] = React.useState(0); const updateScrollbarDimensions = lodash.debounce(() => { var _a; const el = (_a = ref.current) == null ? void 0 : _a.element; if (el) { setScrollbarWidth(el.offsetWidth - el.clientWidth); } }, 150); React.useLayoutEffect(() => { var _a; const el = (_a = ref.current) == null ? void 0 : _a.element; if (!el || utils.IS_SAFARI_26) { return; } updateScrollbarDimensions(); const resizeObserver = new ResizeObserver(updateScrollbarDimensions); resizeObserver.observe(el); return () => { resizeObserver.disconnect(); }; }, [ref, height, updateScrollbarDimensions]); return scrollbarWidth; } function useNestedColWidths({ nestedVisibleFields, availableWidth, structureRev }) { const configuredWidths = React.useMemo( () => utils.computeColWidths(nestedVisibleFields, availableWidth), [nestedVisibleFields, availableWidth] ); const [nestedFieldWidths, setNestedFieldWidths] = React.useState(() => configuredWidths); React.useEffect(() => { const newWidths = utils.computeColWidths(nestedVisibleFields, availableWidth); let hasChanges = false; if (nestedFieldWidths.length !== newWidths.length) { hasChanges = true; } for (let i = 0; i < newWidths.length; i++) { if (nestedFieldWidths[i] !== newWidths[i]) { hasChanges = true; break; } } if (hasChanges) { setNestedFieldWidths(newWidths); } }, [structureRev]); const nestedColWidths = React.useMemo( () => utils.buildNestedColumnWidthsMap(nestedVisibleFields, nestedFieldWidths), [nestedVisibleFields, nestedFieldWidths] ); const handleNestedColumnWidthsChange = React.useCallback( (newColWidths) => { setNestedFieldWidths( nestedVisibleFields.map((f, idx) => { const entry = newColWidths.get(utils.getDisplayName(f)); return entry != null ? entry.width : nestedFieldWidths[idx]; }) ); }, [nestedVisibleFields, nestedFieldWidths] ); return { nestedFieldWidths, nestedColWidths, handleNestedColumnWidthsChange }; } function useColWidths(visibleFields, availableWidth, frozenColumns, resetKey) { const widths = React.useMemo( () => utils.computeColWidths(visibleFields, availableWidth), // Width override removals can mutate width config onto existing field objects. // eslint-disable-next-line react-hooks/exhaustive-deps [visibleFields, availableWidth, resetKey] ); const numFrozenColsFullyInView = React.useMemo(() => { if (!frozenColumns || frozenColumns <= 0) { return -1; } const fullyVisibleCols = widths.reduce( ([count, remainingWidth], nextWidth) => { if (remainingWidth - nextWidth >= 0) { return [count + 1, remainingWidth - nextWidth]; } return [count, 0]; }, [0, availableWidth] )[0]; return Math.min(fullyVisibleCols, frozenColumns); }, [widths, availableWidth, frozenColumns]); return [widths, numFrozenColsFullyInView]; } const isReducer = (maybeReducer) => maybeReducer in data.ReducerID; const nonMathReducers = /* @__PURE__ */ new Set([ data.ReducerID.allValues, data.ReducerID.changeCount, data.ReducerID.count, data.ReducerID.countAll, data.ReducerID.distinctCount, data.ReducerID.first, data.ReducerID.firstNotNull, data.ReducerID.last, data.ReducerID.lastNotNull, data.ReducerID.uniqueValues ]); const isNonMathReducer = (reducer) => isReducer(reducer) && nonMathReducers.has(reducer); const noFormattingReducers = /* @__PURE__ */ new Set([data.ReducerID.count, data.ReducerID.countAll]); const shouldReducerSkipFormatting = (reducer) => isReducer(reducer) && noFormattingReducers.has(reducer); const useReducerEntries = (field, rows, displayName, colIdx) => { return React.useMemo(() => { var _a, _b, _c; const reducers = (_c = (_b = (_a = field.config.custom) == null ? void 0 : _a.footer) == null ? void 0 : _b.reducers) != null ? _c : []; if (reducers.length === 0 || field.type !== data.FieldType.number && reducers.every((reducerId) => !isNonMathReducer(reducerId))) { return []; } const newState = { lastProcessedRowCount: 0, ...field.state || {} // Preserve any existing state properties }; field.state = newState; const currentRowCount = rows.length; const lastRowCount = newState.lastProcessedRowCount; if (lastRowCount !== currentRowCount) { if (newState.calcs) { delete newState.calcs; } newState.lastProcessedRowCount = currentRowCount; } const results = data.reduceField({ field: { ...field, values: rows.map((row) => row[displayName]) }, reducers }); return reducers.map((reducerId) => { if (results[reducerId] === void 0 || // For non-number fields, only show special count reducers field.type !== data.FieldType.number && !isNonMathReducer(reducerId) || // for countAll, only show the reducer in the first column reducerId === data.ReducerID.countAll && colIdx !== 0) { return [reducerId, null]; } const value = results[reducerId]; let result = null; if (!shouldReducerSkipFormatting(reducerId) && field.display) { result = data.formattedValueToString(field.display(value)); } else if (value != null) { result = String(value); } return [reducerId, result]; }); }, [field, rows, displayName, colIdx]); }; exports.useColWidths = useColWidths; exports.useColumnResize = useColumnResize; exports.useFilteredRows = useFilteredRows; exports.useHeaderHeight = useHeaderHeight; exports.useManagedSort = useManagedSort; exports.useNestedColWidths = useNestedColWidths; exports.useNestedRows = useNestedRows; exports.usePaginatedRows = usePaginatedRows; exports.useReducerEntries = useReducerEntries; exports.useRowHeight = useRowHeight; exports.useScrollbarWidth = useScrollbarWidth; exports.useSortedRows = useSortedRows; //# sourceMappingURL=hooks.cjs.map