@grafana/ui
Version:
Grafana Components Library
1 lines • 74.3 kB
Source Map (JSON)
{"version":3,"file":"TableNG.mjs","sources":["../../../../../src/components/Table/TableNG/TableNG.tsx"],"sourcesContent":["import '@grafana/react-data-grid/lib/styles.css';\n\nimport { clsx } from 'clsx';\nimport memoize from 'micro-memoize';\nimport {\n type CSSProperties,\n type JSX,\n type Key,\n type ReactNode,\n Suspense,\n useCallback,\n useEffect,\n useId,\n useMemo,\n useRef,\n useState,\n} from 'react';\n\nimport {\n type DataFrame,\n DataHoverClearEvent,\n DataHoverEvent,\n FALLBACK_COLOR,\n type Field,\n FieldType,\n getDisplayProcessor,\n} from '@grafana/data';\nimport { t, Trans } from '@grafana/i18n';\nimport {\n Cell,\n type CellRendererProps,\n DataGrid,\n type DataGridHandle,\n type DataGridProps,\n type RenderCellProps,\n type Renderers,\n type RenderRowProps,\n Row,\n type SortColumn,\n} from '@grafana/react-data-grid';\nimport { FieldColorModeId, TableCellTooltipPlacement, type TableFooterOptions } from '@grafana/schema';\n\nimport { useStyles2, useTheme2 } from '../../../themes/ThemeContext';\nimport { getTextColorForBackground as _getTextColorForBackground } from '../../../utils/colors';\nimport { Pagination } from '../../Pagination/Pagination';\nimport { type PanelContext, usePanelContext } from '../../PanelChrome';\nimport { DataLinksActionsTooltip } from '../DataLinksActionsTooltip';\nimport { TableCellInspector, TableCellInspectorMode } from '../TableCellInspector';\nimport { type DataLinksActionsTooltipState } from '../cellUtils';\nimport { hasGeoCell, LazyOpenLayersProvider } from '../geo';\nimport { TableCellDisplayMode } from '../types';\n\nimport { getCellRenderer, getCellSpecificStyles } from './Cells/renderers';\nimport { EmptyTablePlaceholder } from './components/EmptyTablePlaceholder';\nimport { HeaderCell } from './components/HeaderCell';\nimport { RowExpander } from './components/RowExpander';\nimport { SummaryCell } from './components/SummaryCell';\nimport { TableCellActions } from './components/TableCellActions';\nimport { TableCellTooltip } from './components/TableCellTooltip';\nimport { COLUMN, TABLE } from './constants';\nimport {\n useColumnResize,\n useColWidths,\n useFilteredRows,\n useHeaderHeight,\n useManagedSort,\n useNestedColWidths,\n useNestedRows,\n usePaginatedRows,\n useRowHeight,\n useScrollbarWidth,\n useSortedRows,\n} from './hooks';\nimport {\n getCellActionStyles,\n getDefaultCellStyles,\n getGridStyles,\n getHeaderCellStyles,\n getLinkStyles,\n getMaxHeightCellStyles,\n getTooltipStyles,\n} from './styles';\nimport {\n type CellRootRenderer,\n type FromFieldsResult,\n type InspectCellProps,\n type TableCellStyleOptions,\n type TableColumn,\n type TableNGProps,\n type TableRow,\n type TableSummaryRow,\n} from './types';\nimport {\n calculateFooterHeight,\n canFieldBeColorized,\n compileFrameToRecordsV1,\n compileFrameToRecordsV2,\n createTypographyContext,\n displayJsonValue,\n extractPixelValue,\n getAlignment,\n getApplyToRowBgFn,\n getCellColorInlineStylesFactory,\n getCellLinks,\n getCellOptions,\n getDefaultRowHeight,\n getDisplayName,\n getIsNestedTable,\n getJustifyContent,\n getStableRowKey,\n getSummaryCellTextAlign,\n getVisibleFields,\n IS_SAFARI_26,\n isCellInspectEnabled,\n parseStyleJson,\n predicateByName,\n rowKeyGetter,\n shouldTextOverflow,\n shouldTextWrap,\n} from './utils';\n\nconst EXPANDED_COLUMN_KEY = 'expanded';\ntype OnCellClick = NonNullable<DataGridProps<TableRow, TableSummaryRow>['onCellClick']>;\n\nexport function TableNG(props: TableNGProps) {\n const {\n cellHeight,\n data,\n disableKeyboardEvents,\n disableSanitizeHtml,\n enablePagination = false,\n enableSharedCrosshair = false,\n enableVirtualization,\n frozenColumns: _frozenColumns = 0,\n getActions = () => [],\n height,\n maxRowHeight: _maxRowHeight,\n noHeader,\n noValue,\n onCellFilterAdded,\n onColumnResize,\n onSortByChange,\n protoParserEnabled,\n showTypeIcons,\n structureRev,\n timeRange,\n transparent,\n width,\n initialRowIndex,\n sortBy,\n sortByBehavior = 'initial',\n } = props;\n const uniqueId = useId();\n const theme = useTheme2();\n\n const panelContext = usePanelContext();\n const userCanExecuteActions = useMemo(() => panelContext.canExecuteActions?.() ?? false, [panelContext]);\n\n const getCellActions = useCallback(\n (field: Field, rowIdx: number) => {\n if (!userCanExecuteActions) {\n return [];\n }\n return getActions(data, field, rowIdx);\n },\n [getActions, data, userCanExecuteActions]\n );\n\n const visibleFields = useMemo(() => getVisibleFields(data.fields), [data.fields]);\n const hasHeader = !noHeader;\n const hasFooter = useMemo(\n () => visibleFields.some((field) => Boolean(field.config.custom?.footer?.reducers?.length)),\n [visibleFields]\n );\n const footerHeight = useMemo(\n () => (hasFooter ? calculateFooterHeight(visibleFields) : 0),\n [hasFooter, visibleFields]\n );\n\n const resizeHandler = useColumnResize(onColumnResize);\n const nestedResizeHandler = useColumnResize(onColumnResize, 'nested');\n\n const hasNestedFrames = useMemo(() => getIsNestedTable(data.fields), [data]);\n const tableHasGeoCell = useMemo(() => hasGeoCell(data), [data]);\n const nestedFramesFieldName = useMemo(() => {\n if (!hasNestedFrames) {\n return;\n }\n const firstNestedField = data.fields.find((f) => f.type === FieldType.nestedFrames);\n if (!firstNestedField) {\n return;\n }\n return getDisplayName(firstNestedField);\n }, [data, hasNestedFrames]);\n const frameToRecords = useMemo(\n () =>\n protoParserEnabled\n ? compileFrameToRecordsV2(data, nestedFramesFieldName)\n : compileFrameToRecordsV1(data, nestedFramesFieldName),\n [data, nestedFramesFieldName, protoParserEnabled]\n );\n const rows = useMemo(() => frameToRecords(data), [frameToRecords, data]);\n\n const nestedData = useMemo(\n (): DataFrame[] | undefined =>\n hasNestedFrames\n ? data.fields.find((f) => getDisplayName(f) === nestedFramesFieldName)?.values.map((v) => v[0])\n : undefined,\n [data, nestedFramesFieldName, hasNestedFrames]\n );\n\n // Returns a stable string key for a row based on the groupBy field values stored in the nested\n // subframe's meta. Falls back to the string index when no stable key is available (non-grouped data).\n const getStableRowKeyForRowIdx = useCallback(\n (rowIdx: number): string => getStableRowKey(rowIdx, nestedData?.[rowIdx]),\n [nestedData]\n );\n\n const firstRowNestedData = useMemo(\n () => (hasNestedFrames && nestedData ? nestedData[0] : undefined),\n [nestedData, hasNestedFrames]\n );\n const nestedFields = useMemo(() => firstRowNestedData?.fields ?? [], [firstRowNestedData]);\n const nestedVisibleFields = useMemo(() => getVisibleFields(nestedFields), [nestedFields]);\n const nestedHasFooter = useMemo(\n () => nestedVisibleFields.some((field) => Boolean(field.config.custom?.footer?.reducers?.length)),\n [nestedVisibleFields]\n );\n const nestedFooterHeight = useMemo(\n () => (nestedHasFooter ? calculateFooterHeight(nestedVisibleFields) : 0),\n [nestedHasFooter, nestedVisibleFields]\n );\n\n const { rows: filteredRows, filter, setFilter, filterResult } = useFilteredRows(rows, data.fields, hasNestedFrames);\n\n const {\n rows: sortedRows,\n sortColumns,\n setSortColumns,\n } = useSortedRows(filteredRows, data.fields, nestedFields, { hasNestedFrames, initialSortBy: sortBy });\n\n useManagedSort({ sortByBehavior, setSortColumns, sortBy });\n\n const nestedRows = useNestedRows(rows, nestedData, hasNestedFrames, nestedFramesFieldName, filter, sortColumns);\n\n const [inspectCell, setInspectCell] = useState<InspectCellProps | null>(null);\n const [tooltipState, setTooltipState] = useState<DataLinksActionsTooltipState>();\n const onCellClick: OnCellClick = useCallback(\n ({ column, row }, ev) => {\n // we attach field to the column, but it doesn't\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n const field = (column as unknown as TableColumn).field;\n\n // let the click event through for the expander column, since it has its own click handler for expanding/collapsing rows.\n if (column.key === EXPANDED_COLUMN_KEY) {\n return;\n }\n\n if (\n ev.target instanceof HTMLElement &&\n // this walks up the tree to find either a faux link wrapper or the cell root\n // it then only proceeds if we matched the faux link wrapper\n ev.target.closest('a[aria-haspopup], .rdg-cell')?.matches('a')\n ) {\n const rowIdx = row.__index;\n setTooltipState({\n coords: {\n clientX: ev.clientX,\n clientY: ev.clientY,\n },\n links: getCellLinks(field, rowIdx),\n actions: getCellActions(field, rowIdx),\n });\n ev.preventGridDefault();\n }\n },\n [getCellActions]\n );\n const [expandedRows, setExpandedRows] = useState<Set<string>>(() => {\n if (data.meta?.custom?.expandAllRows) {\n const nestedField = data.fields.find((f) => f.type === FieldType.nestedFrames);\n return new Set(Array.from({ length: data.length }, (_, i) => getStableRowKey(i, nestedField?.values[i]?.[0])));\n }\n return new Set();\n });\n const [selectedRows, setSelectedRows] = useState((): ReadonlySet<string> => new Set());\n\n // vt scrollbar accounting for column auto-sizing\n const gridRef = useRef<DataGridHandle>(null);\n const scrollbarWidth = useScrollbarWidth(gridRef, height);\n const availableWidth = useMemo(\n () => (hasNestedFrames ? width - COLUMN.EXPANDER_WIDTH : width) - scrollbarWidth,\n [width, hasNestedFrames, scrollbarWidth]\n );\n const getCellColorInlineStyles = useMemo(() => getCellColorInlineStylesFactory(theme), [theme]);\n const applyToRowBgFn = useMemo(\n () => getApplyToRowBgFn(data.fields, getCellColorInlineStyles) ?? undefined,\n [data.fields, getCellColorInlineStyles]\n );\n const getTextColorForBackground = useMemo(() => memoize(_getTextColorForBackground, { maxSize: 1000 }), []);\n\n const typographyCtx = useMemo(\n () =>\n createTypographyContext(\n theme.typography.fontSize,\n theme.typography.fontFamily,\n extractPixelValue(theme.typography.body.letterSpacing!) * theme.typography.fontSize\n ),\n [theme]\n );\n\n // https://github.com/grafana/grafana/issues/118984: nested tables don't support frozen columns yet.\n const frozenColumns = useMemo(() => (hasNestedFrames ? 0 : _frozenColumns), [hasNestedFrames, _frozenColumns]);\n const configuredWidthCount = visibleFields.reduce(\n (count, field) => count + (field.config.custom?.width != null ? 1 : 0),\n 0\n );\n const prevConfiguredWidthCount = useRef(configuredWidthCount);\n const widthConfigResetKey = configuredWidthCount < prevConfiguredWidthCount.current ? Symbol() : undefined;\n const resetColumnWidths = widthConfigResetKey != null ? new Map() : undefined;\n\n prevConfiguredWidthCount.current = configuredWidthCount;\n\n const [widths, numFrozenColsFullyInView] = useColWidths(\n visibleFields,\n availableWidth,\n frozenColumns,\n widthConfigResetKey\n );\n\n const headerHeight = useHeaderHeight({\n columnWidths: widths,\n fields: visibleFields,\n enabled: hasHeader,\n sortColumns,\n showTypeIcons: showTypeIcons ?? false,\n typographyCtx,\n });\n // the minimum max row height we should honor is a single line of text.\n const maxRowHeight = _maxRowHeight != null ? Math.max(TABLE.LINE_HEIGHT, _maxRowHeight) : undefined;\n const visibleNestedRowCounts = useMemo(\n () => nestedRows.map((row, idx) => (expandedRows.has(getStableRowKeyForRowIdx(idx)) ? row.final.length : null)),\n [nestedRows, expandedRows, getStableRowKeyForRowIdx]\n );\n\n const { nestedFieldWidths, nestedColWidths, handleNestedColumnWidthsChange } = useNestedColWidths({\n nestedVisibleFields,\n availableWidth,\n structureRev,\n });\n\n const hasNestedHeaders = useMemo(() => firstRowNestedData?.meta?.custom?.noHeader !== true, [firstRowNestedData]);\n const nestedHeaderHeight = useHeaderHeight({\n columnWidths: nestedFieldWidths,\n fields: nestedVisibleFields,\n enabled: hasNestedHeaders,\n sortColumns,\n showTypeIcons: showTypeIcons ?? false,\n typographyCtx,\n });\n\n const defaultRowHeight = useMemo(\n () => getDefaultRowHeight(theme, visibleFields, cellHeight),\n [theme, visibleFields, cellHeight]\n );\n const defaultNestedRowHeight = useMemo(\n () => getDefaultRowHeight(theme, nestedVisibleFields, cellHeight),\n [theme, nestedVisibleFields, cellHeight]\n );\n\n const rowHeight = useRowHeight({\n columnWidths: widths,\n fields: visibleFields,\n hasNestedFrames,\n defaultHeight: defaultRowHeight,\n defaultNestedHeight: defaultNestedRowHeight,\n visibleNestedRowCounts,\n typographyCtx,\n maxHeight: maxRowHeight,\n nestedColWidths: nestedFieldWidths,\n nestedFields: nestedVisibleFields,\n nestedRows,\n nestedFooterHeight,\n });\n\n const {\n rows: paginatedRows,\n page,\n setPage,\n numPages,\n numRows,\n pageRangeStart,\n pageRangeEnd,\n smallPagination,\n } = usePaginatedRows(sortedRows, {\n enabled: enablePagination,\n width: availableWidth,\n height,\n footerHeight,\n headerHeight: hasHeader ? headerHeight : 0,\n rowHeight,\n hasNestedFrames,\n });\n\n const showPagination = enablePagination && numRows > 0;\n const styles = useStyles2(getGridStyles, showPagination, transparent);\n\n const [scrollToIndex, setScrollToIndex] = useState(initialRowIndex);\n useEffect(() => {\n if (scrollToIndex !== undefined && sortedRows && gridRef.current?.scrollToCell) {\n const rowIdx = sortedRows.findIndex((row) => row.__index === scrollToIndex);\n gridRef.current.scrollToCell({\n rowIdx,\n });\n setScrollToIndex(undefined);\n setSelectedRows(new Set<string>([rowKeyGetter(sortedRows[rowIdx])]));\n }\n }, [scrollToIndex, sortedRows]);\n\n // normalize the row height into a function which returns a number, so we avoid a bunch of conditionals during rendering.\n const rowHeightFn = useMemo((): ((row: TableRow) => number) => {\n if (typeof defaultNestedRowHeight === 'string') {\n return (row: TableRow) => (expandedRows.has(getStableRowKeyForRowIdx(row.__index)) ? TABLE.MAX_CELL_HEIGHT : 0);\n }\n if (typeof rowHeight === 'function') {\n // this is safe because we only return a (row: TableRow) => string function when defaultNestedRowHeight is a string.\n // eslint-disable-next-line @typescript-eslint/consistent-type-assertions\n return rowHeight as unknown as (row: TableRow) => number;\n }\n if (typeof rowHeight === 'string') {\n return () => TABLE.MAX_CELL_HEIGHT;\n }\n return () => rowHeight;\n }, [rowHeight, defaultNestedRowHeight, expandedRows, getStableRowKeyForRowIdx]);\n\n const renderRow = useMemo(\n () => renderRowFactory(data.fields, panelContext, expandedRows, enableSharedCrosshair, getStableRowKeyForRowIdx),\n [data.fields, panelContext, expandedRows, enableSharedCrosshair, getStableRowKeyForRowIdx]\n );\n\n const commonDataGridProps = useMemo(\n () =>\n ({\n enableVirtualization: !IS_SAFARI_26 && enableVirtualization !== false && typeof rowHeight !== 'string',\n defaultColumnOptions: {\n minWidth: 50,\n resizable: true,\n sortable: true,\n // draggable: true,\n },\n onSortColumnsChange: (newSortColumns: SortColumn[]) => {\n setSortColumns(newSortColumns);\n onSortByChange?.(\n newSortColumns.map(({ columnKey, direction }) => ({\n displayName: columnKey,\n desc: direction === 'DESC',\n }))\n );\n },\n sortColumns,\n rowHeight,\n bottomSummaryRows: hasFooter ? [{}] : undefined,\n summaryRowHeight: footerHeight,\n headerRowClass: styles.headerRow,\n headerRowHeight: noHeader ? 0 : headerHeight,\n }) satisfies Partial<DataGridProps<TableRow, TableSummaryRow>>,\n [\n enableVirtualization,\n hasFooter,\n sortColumns,\n rowHeight,\n styles.headerRow,\n noHeader,\n setSortColumns,\n onSortByChange,\n footerHeight,\n headerHeight,\n ]\n );\n\n const buildNestedTableExpanderColumn = useCallback(\n (\n nestedColumnsMatrix: FromFieldsResult[],\n hasNestedHeaders: boolean,\n nestedHeaderHeightPx: number,\n hasNestedFooter: boolean,\n nestedFooterHeightPx: number,\n renderers: Renderers<TableRow, TableSummaryRow>\n ): TableColumn => ({\n key: EXPANDED_COLUMN_KEY,\n sortable: false,\n resizable: false,\n name: t('grafana-ui.table.nested-table.expander-column-name', 'Expand nested rows'),\n field: {\n name: '',\n type: FieldType.other,\n config: {},\n values: [],\n },\n cellClass(row) {\n if (row.__depth !== 0) {\n return styles.cellNested;\n }\n return;\n },\n colSpan(args) {\n return args.type === 'ROW' && args.row.__depth === 1 ? data.fields.length : 1;\n },\n renderCell: ({ row }) => {\n const rowId = `${uniqueId}-nested-table-${row.__index}`;\n\n if (row.__depth === 0) {\n const rowIdx = row.__index;\n const stableKey = getStableRowKeyForRowIdx(rowIdx);\n\n return (\n <RowExpander\n rowId={rowId}\n isExpanded={expandedRows.has(stableKey)}\n onCellExpand={() => {\n setExpandedRows((er) => {\n if (er.has(stableKey)) {\n er.delete(stableKey);\n } else {\n er.add(stableKey);\n }\n return new Set(er);\n });\n }}\n />\n );\n }\n\n const expandedRecords = nestedRows[row.__index]?.final ?? [];\n if (!expandedRecords.length) {\n return (\n <div className={styles.noDataNested}>\n <Trans i18nKey=\"grafana-ui.table.nested-table.no-data\">No data</Trans>\n </div>\n );\n }\n\n const nestedColumns = nestedColumnsMatrix[row.__index].columns;\n\n return (\n <div id={rowId}>\n <DataGrid<TableRow, TableSummaryRow>\n {...commonDataGridProps}\n className={clsx(styles.grid, styles.gridNested)}\n headerRowClass={clsx(styles.headerRow, hasNestedHeaders ? '' : styles.displayNone)}\n headerRowHeight={hasNestedHeaders ? nestedHeaderHeightPx : 0}\n bottomSummaryRows={hasNestedFooter ? [{}] : undefined}\n summaryRowHeight={nestedFooterHeightPx}\n onColumnResize={nestedResizeHandler}\n columns={nestedColumns}\n rows={expandedRecords}\n renderers={{ ...renderers, noRowsFallback: <EmptyTablePlaceholder noValue={noValue} /> }}\n onCellClick={onCellClick}\n columnWidths={nestedColWidths}\n onColumnWidthsChange={handleNestedColumnWidthsChange}\n />\n </div>\n );\n },\n renderHeaderCell(props) {\n return <div className=\"sr-only\">{props.column.name}</div>;\n },\n width: COLUMN.EXPANDER_WIDTH,\n minWidth: COLUMN.EXPANDER_WIDTH,\n }),\n [\n styles.cellNested,\n styles.grid,\n styles.gridNested,\n styles.headerRow,\n styles.displayNone,\n styles.noDataNested,\n data.fields.length,\n commonDataGridProps,\n expandedRows,\n getStableRowKeyForRowIdx,\n nestedRows,\n noValue,\n onCellClick,\n uniqueId,\n nestedColWidths,\n nestedResizeHandler,\n handleNestedColumnWidthsChange,\n ]\n );\n\n const fromFields = useCallback(\n (\n f: Field[],\n widths: number[],\n frame: DataFrame,\n rawRows: TableRow[],\n visibleRows: TableRow[]\n ): FromFieldsResult => {\n const result: FromFieldsResult = {\n columns: [],\n cellRootRenderers: {},\n };\n\n // Derive footer config from the fields being processed so nested tables use\n // their own footer configuration rather than the top-level table's.\n const fieldFooters: Array<TableFooterOptions | undefined> = [];\n let isFieldUniformFooter = true;\n let firstFooterReducers: string[] | undefined;\n for (const field of f) {\n const footer = field.config?.custom?.footer;\n const reducers: string[] | undefined = footer?.reducers;\n\n fieldFooters.push(footer);\n\n // if reducers are undefined or empty on the footer, don't retain them for comparison.\n if (reducers === undefined || reducers.length === 0) {\n continue;\n }\n\n // first time we encounter a viable footer config, store it and move on.\n if (firstFooterReducers === undefined) {\n firstFooterReducers = reducers;\n continue;\n }\n\n // for all other viable footer configs, check to see if the reducers match the first one we encountered.\n if (\n reducers.length !== firstFooterReducers.length ||\n reducers.some((r, idx) => firstFooterReducers?.[idx] !== r)\n ) {\n isFieldUniformFooter = false;\n break;\n }\n }\n\n // Reuse pre-computed filter results — no re-scanning of rows needed.\n // Top-level tables use filterResult from useFilteredRows; nested tables use the\n // filterResult stored on their NestedRowEntry by useNestedRows.\n const parentIndex = visibleRows[0]?.__parentIndex;\n const { crossFilterRows, crossFilterTailRows } =\n parentIndex == null ? filterResult : nestedRows[parentIndex].filterResult;\n\n let lastRowIdx = -1;\n // shared when whole row will be styled by a single cell's color\n let rowCellStyle: Partial<CSSProperties> = {\n color: undefined,\n background: undefined,\n };\n\n for (let i = 0; i < f.length; i++) {\n let field = f[i];\n const cellOptions = getCellOptions(field);\n const cellType = cellOptions.type;\n\n // make sure we use mappings exclusively if they exist, ignore default thresholds mode\n // we hack this by using the single color mode calculator\n if (cellType === TableCellDisplayMode.Pill && (field.config.mappings?.length ?? 0 > 0)) {\n field = {\n ...field,\n config: {\n ...field.config,\n color: {\n ...field.config.color,\n mode: FieldColorModeId.Fixed,\n fixedColor: field.config.color?.fixedColor ?? FALLBACK_COLOR,\n },\n },\n };\n field.display = getDisplayProcessor({ field, theme });\n }\n\n // attach JSONCell custom display function to JSONView cell type\n if (cellType === TableCellDisplayMode.JSONView || field.type === FieldType.other) {\n field.display = displayJsonValue(field);\n }\n\n // For some cells, \"aligning\" the cell will mean aligning the inline contents of the cell with\n // the text-align css property, and for others, we'll use justify-content to align the cell\n // contents with flexbox. We always just get both and provide both when styling the cell.\n const textAlign = getAlignment(field);\n const justifyContent = getJustifyContent(textAlign);\n const displayName = getDisplayName(field);\n const headerCellClass = getHeaderCellStyles(theme, justifyContent);\n const CellType = getCellRenderer(field, cellOptions);\n\n const cellInspect = isCellInspectEnabled(field);\n const showFilters = Boolean(field.config.filterable && onCellFilterAdded != null);\n const showActions = cellInspect || showFilters;\n const width = widths[i];\n\n // helps us avoid string cx and emotion per-cell\n const cellActionClassName = showActions\n ? clsx('table-cell-actions', getCellActionStyles(theme, textAlign))\n : undefined;\n\n const shouldOverflow =\n !IS_SAFARI_26 && typeof rowHeight !== 'string' && (shouldTextOverflow(field) || Boolean(maxRowHeight));\n const textWrap = typeof rowHeight === 'string' || shouldTextWrap(field);\n const canBeColorized = canFieldBeColorized(cellType, applyToRowBgFn);\n const cellStyleOptions: TableCellStyleOptions = {\n textAlign,\n textWrap,\n shouldOverflow,\n maxHeight: maxRowHeight,\n };\n\n const defaultCellStyles = getDefaultCellStyles(theme, cellStyleOptions);\n const cellSpecificStyles = getCellSpecificStyles(cellType, field, theme, cellStyleOptions);\n const linkStyles = getLinkStyles(theme, canBeColorized);\n const cellParentStyles = clsx(defaultCellStyles, linkStyles);\n const maxHeightClassName = maxRowHeight ? getMaxHeightCellStyles(theme, cellStyleOptions) : undefined;\n const styleFieldValue = field.config.custom?.styleField;\n const styleField = styleFieldValue ? frame.fields.find(predicateByName(styleFieldValue)) : undefined;\n const styleFieldName = styleField ? getDisplayName(styleField) : undefined;\n const hasValidStyleField = Boolean(styleFieldName);\n\n // TODO: in future extend this to ensure a non-classic color scheme is set with AutoCell\n\n // this fires first\n const renderCellRoot = (key: Key, props: CellRendererProps<TableRow, TableSummaryRow>): ReactNode => {\n const rowIdx = props.row.__index;\n\n // meh, this should be cached by the renderRow() call?\n if (rowIdx !== lastRowIdx) {\n lastRowIdx = rowIdx;\n\n rowCellStyle.color = undefined;\n rowCellStyle.background = undefined;\n\n // generate shared styles for whole row\n if (applyToRowBgFn != null) {\n rowCellStyle = { ...rowCellStyle, ...applyToRowBgFn(rowIdx) };\n }\n }\n\n let style: CSSProperties = { ...rowCellStyle };\n if (canBeColorized) {\n const value = props.row[props.column.key];\n const displayValue = field.display!(value); // this fires here to get colors, then again to get rendered value?\n const cellColorStyles = getCellColorInlineStyles(cellOptions, displayValue, applyToRowBgFn != null);\n Object.assign(style, cellColorStyles);\n }\n if (hasValidStyleField) {\n style = { ...style, ...parseStyleJson(props.row[styleFieldName!]) };\n }\n\n return (\n <Cell\n key={key}\n {...props}\n className={clsx(\n props.className,\n cellParentStyles,\n cellSpecificStyles != null && maxRowHeight == null ? cellSpecificStyles : ''\n )}\n style={style}\n />\n );\n };\n\n result.cellRootRenderers[displayName] = renderCellRoot;\n\n const renderBasicCellContent = (props: RenderCellProps<TableRow, TableSummaryRow>): JSX.Element => {\n const rowIdx = props.row.__index;\n const value = props.row[props.column.key];\n // TODO: it would be nice to get rid of passing height down as a prop. but this value\n // is cached so the cost of calling for every cell is low.\n // NOTE: some cell types still require a height to be passed down, so that's why string-based\n // cell types are going to just pass down the max cell height as a numeric height for those cells.\n const height = rowHeightFn(props.row);\n\n let result = (\n <>\n <CellType\n cellOptions={cellOptions}\n frame={frame}\n field={field}\n height={height}\n rowIdx={rowIdx}\n theme={theme}\n value={value}\n width={width}\n timeRange={timeRange}\n cellInspect={cellInspect}\n showFilters={showFilters}\n getActions={getCellActions}\n disableSanitizeHtml={disableSanitizeHtml}\n getTextColorForBackground={getTextColorForBackground}\n />\n {showActions && (\n <TableCellActions\n field={field}\n value={value}\n displayName={displayName}\n cellInspect={cellInspect}\n showFilters={showFilters}\n className={cellActionClassName}\n setInspectCell={setInspectCell}\n onCellFilterAdded={onCellFilterAdded}\n />\n )}\n </>\n );\n\n if (maxRowHeight != null) {\n result = <div className={clsx(maxHeightClassName, cellSpecificStyles)}>{result}</div>;\n }\n\n return result;\n };\n\n // renderCellContent fires second.\n let renderCellContent = renderBasicCellContent;\n\n const tooltipFieldName = field.config.custom?.tooltip?.field;\n if (tooltipFieldName) {\n const tooltipField = frame.fields.find(predicateByName(tooltipFieldName));\n if (tooltipField) {\n const tooltipDisplayName = getDisplayName(tooltipField);\n const tooltipCellOptions = getCellOptions(tooltipField);\n const tooltipFieldRenderer = getCellRenderer(tooltipField, tooltipCellOptions);\n\n const tooltipCellStyleOptions = {\n textAlign: getAlignment(tooltipField),\n textWrap: shouldTextWrap(tooltipField),\n shouldOverflow: false,\n maxHeight: maxRowHeight,\n } satisfies TableCellStyleOptions;\n const tooltipCanBeColorized = canFieldBeColorized(tooltipCellOptions.type, applyToRowBgFn);\n const tooltipDefaultStyles = getDefaultCellStyles(theme, tooltipCellStyleOptions);\n const tooltipSpecificStyles = getCellSpecificStyles(\n tooltipCellOptions.type,\n tooltipField,\n theme,\n tooltipCellStyleOptions\n );\n const tooltipLinkStyles = getLinkStyles(theme, tooltipCanBeColorized);\n const tooltipClasses = getTooltipStyles(theme, textAlign);\n\n const placement = field.config.custom?.tooltip?.placement ?? TableCellTooltipPlacement.Auto;\n const tooltipWidth =\n placement === TableCellTooltipPlacement.Left || placement === TableCellTooltipPlacement.Right\n ? tooltipField.config.custom?.width\n : width;\n\n const tooltipProps = {\n cellOptions: tooltipCellOptions,\n classes: tooltipClasses,\n className: clsx(\n tooltipClasses.tooltipContent,\n tooltipDefaultStyles,\n tooltipSpecificStyles,\n tooltipLinkStyles\n ),\n data: frame,\n disableSanitizeHtml,\n field: tooltipField,\n getActions: getCellActions,\n getTextColorForBackground,\n gridRef,\n placement,\n renderer: tooltipFieldRenderer,\n tooltipField,\n theme,\n width: tooltipWidth,\n } satisfies Partial<React.ComponentProps<typeof TableCellTooltip>>;\n\n renderCellContent = (props: RenderCellProps<TableRow, TableSummaryRow>): JSX.Element => {\n // cached so we don't care about multiple calls.\n const tooltipHeight = rowHeightFn(props.row);\n let tooltipStyle: CSSProperties = { ...rowCellStyle };\n if (tooltipCanBeColorized) {\n const tooltipDisplayValue = tooltipField.display!(props.row[tooltipDisplayName]);\n const tooltipCellColorStyles = getCellColorInlineStyles(\n tooltipCellOptions,\n tooltipDisplayValue,\n applyToRowBgFn != null\n );\n Object.assign(tooltipStyle, tooltipCellColorStyles);\n }\n\n return (\n <TableCellTooltip\n {...tooltipProps}\n height={tooltipHeight}\n rowIdx={props.row.__index}\n style={tooltipStyle}\n >\n {renderBasicCellContent(props)}\n </TableCellTooltip>\n );\n };\n }\n }\n\n result.columns.push({\n field,\n key: displayName,\n name: displayName,\n width,\n headerCellClass,\n frozen: Math.min(frozenColumns, numFrozenColsFullyInView) > i,\n renderCell: renderCellContent,\n renderHeaderCell: ({ column, sortDirection }) => (\n <HeaderCell\n column={column}\n rows={rawRows}\n field={field}\n filter={filter}\n setFilter={setFilter}\n disableKeyboardEvents={disableKeyboardEvents}\n direction={sortDirection}\n showTypeIcons={showTypeIcons}\n parentIndex={parentIndex}\n crossFilterRows={crossFilterRows}\n crossFilterTailRows={crossFilterTailRows}\n selectFirstCell={() => {\n gridRef.current?.selectCell({ rowIdx: 0, idx: 0 });\n }}\n />\n ),\n renderSummaryCell: () => (\n <SummaryCell\n rows={visibleRows}\n footers={fieldFooters}\n field={field}\n colIdx={i}\n textAlign={getSummaryCellTextAlign(textAlign, cellType)}\n rowLabel={isFieldUniformFooter && i === 0}\n hideLabel={isFieldUniformFooter && i !== 0}\n />\n ),\n });\n }\n\n return result;\n },\n [\n applyToRowBgFn,\n disableKeyboardEvents,\n disableSanitizeHtml,\n filter,\n filterResult,\n frozenColumns,\n getCellActions,\n getCellColorInlineStyles,\n getTextColorForBackground,\n maxRowHeight,\n nestedRows,\n numFrozenColsFullyInView,\n onCellFilterAdded,\n rowHeight,\n rowHeightFn,\n setFilter,\n showTypeIcons,\n theme,\n timeRange,\n ]\n );\n\n const nestedColumnsMatrix = useMemo(() => {\n const result: FromFieldsResult[] = [];\n if (!hasNestedFrames) {\n return result;\n }\n for (const row of rows) {\n if (row.__depth > 0) {\n const rowNestedFrame = nestedData![row.__index]!;\n result.push(\n fromFields(\n getVisibleFields(rowNestedFrame.fields),\n nestedFieldWidths,\n rowNestedFrame,\n nestedRows[row.__index].raw,\n nestedRows[row.__index].final\n )\n );\n }\n }\n return result;\n }, [rows, hasNestedFrames, nestedData, nestedRows, nestedFieldWidths, fromFields]);\n\n const { columns, cellRootRenderers } = useMemo(() => {\n const result = fromFields(visibleFields, widths, data, rows, sortedRows);\n\n // if nested frames are present, augment the columns to include the nested table expander column.\n if (!firstRowNestedData) {\n return result;\n }\n\n // pre-calculate renderRow and expandedColumns based on the first nested frame's fields.\n const renderRow = renderRowFactory(\n firstRowNestedData.fields,\n panelContext,\n expandedRows,\n enableSharedCrosshair,\n getStableRowKeyForRowIdx\n );\n\n const expanderCellRenderer: CellRootRenderer = (key, props) => <Cell key={key} {...props} />;\n result.cellRootRenderers[EXPANDED_COLUMN_KEY] = expanderCellRenderer;\n\n // If we have nested frames, we need to add a column for the row expansion\n result.columns.unshift(\n buildNestedTableExpanderColumn(\n nestedColumnsMatrix,\n hasNestedHeaders,\n nestedHeaderHeight,\n nestedHasFooter,\n nestedFooterHeight,\n {\n renderRow,\n renderCell: (key, props) =>\n nestedColumnsMatrix[props.row.__parentIndex!].cellRootRenderers[props.column.key](key, props),\n }\n )\n );\n\n return result;\n }, [\n buildNestedTableExpanderColumn,\n data,\n enableSharedCrosshair,\n expandedRows,\n firstRowNestedData,\n fromFields,\n getStableRowKeyForRowIdx,\n hasNestedHeaders,\n nestedColumnsMatrix,\n nestedFooterHeight,\n nestedHasFooter,\n nestedHeaderHeight,\n panelContext,\n rows,\n sortedRows,\n visibleFields,\n widths,\n ]);\n\n // invalidate columns on every structureRev change. this supports width editing in the fieldConfig.\n // eslint-disable-next-line react-hooks/exhaustive-deps\n const structureRevColumns = useMemo(() => columns, [columns, structureRev]);\n const renderCellRoot: CellRootRenderer = useCallback(\n (key, props) => cellRootRenderers[props.column.key](key, props),\n [cellRootRenderers]\n );\n\n // we need to have variables with these exact names for the localization to work properly\n const itemsRangeStart = pageRangeStart;\n const displayedEnd = pageRangeEnd;\n\n let rendered = (\n <>\n <DataGrid<TableRow, TableSummaryRow, string>\n {...commonDataGridProps}\n role={hasNestedFrames ? 'treegrid' : 'grid'}\n ref={gridRef}\n className={styles.grid}\n columns={structureRevColumns}\n rows={paginatedRows}\n rowKeyGetter={rowKeyGetter}\n isRowSelectionDisabled={() => initialRowIndex !== undefined}\n selectedRows={selectedRows}\n onSelectedRowsChange={setSelectedRows}\n headerRowClass={clsx(styles.headerRow, noHeader ? styles.displayNone : '')}\n headerRowHeight={headerHeight}\n columnWidths={resetColumnWidths}\n onColumnWidthsChange={resetColumnWidths != null ? () => {} : undefined}\n onColumnResize={resizeHandler}\n onCellClick={onCellClick}\n onCellKeyDown={({ column, row }, event) => {\n // if top-left cell, use default browser tabbing\n if (column.key === columns[0].key && row.__index === 0 && event.shiftKey && event.key === 'Tab') {\n event.preventGridDefault();\n gridRef.current?.selectCell({ rowIdx: -1, idx: columns.length - 1 }); // select the far right cell of the header\n return;\n }\n\n if (\n disableKeyboardEvents ||\n (hasNestedFrames && event.isDefaultPrevented()) // skip parent grid keyboard navigation if nested grid handled it\n ) {\n event.preventGridDefault();\n }\n }}\n renderers={{\n renderRow,\n renderCell: renderCellRoot,\n noRowsFallback: <EmptyTablePlaceholder noValue={noValue} />,\n }}\n />\n\n {enablePagination && numRows > 0 && (\n <div className={styles.paginationContainer}>\n <Pagination\n className=\"table-ng-pagination\"\n currentPage={page + 1}\n numberOfPages={numPages}\n showSmallVersion={smallPagination}\n onNavigate={(toPage) => {\n setPage(toPage - 1);\n }}\n />\n {!smallPagination && (\n <div className={styles.paginationSummary}>\n {/* TODO: once TableRT is deprecated, we can update the localiziation\n string with the more consistent variable names */}\n <Trans i18nKey=\"grafana-ui.table.pagination-summary\">\n {{ itemsRangeStart }} - {{ displayedEnd }} of {{ numRows }} rows\n </Trans>\n </div>\n )}\n </div>\n )}\n\n {tooltipState && (\n <DataLinksActionsTooltip\n links={tooltipState.links ?? []}\n actions={tooltipState.actions}\n coords={tooltipState.coords}\n onTooltipClose={() => setTooltipState(undefined)}\n />\n )}\n\n {inspectCell && (\n <TableCellInspector\n mode={inspectCell.mode ?? TableCellInspectorMode.text}\n value={inspectCell.value}\n onDismiss={() => setInspectCell(null)}\n />\n )}\n </>\n );\n\n if (IS_SAFARI_26) {\n rendered = <div className={styles.safariWrapper}>{rendered}</div>;\n }\n\n if (!tableHasGeoCell) {\n return rendered;\n }\n\n return (\n <Suspense fallback={rendered}>\n <LazyOpenLayersProvider>{rendered}</LazyOpenLayersProvider>\n </Suspense>\n );\n}\n\n/**\n * this is passed to the top-level `renderRow` prop on DataGrid. applies aria attributes and custom event handlers.\n */\nconst renderRowFactory =\n (\n fields: Field[],\n panelContext: PanelContext,\n expandedRows: Set<string>,\n enableSharedCrosshair: boolean,\n getStableKey: (rowIdx: number) => string\n ) =>\n // eslint-disable-next-line react/display-name\n (key: React.Key, props: RenderRowProps<TableRow, TableSummaryRow>): React.ReactNode => {\n const { row } = props;\n const rowIdx = row.__index;\n const isExpanded = expandedRows.has(getStableKey(rowIdx));\n\n // Don't render non expanded child rows\n if (row.__depth === 1) {\n if (!isExpanded) {\n return null;\n }\n\n // Add aria-expanded and aria-level to parent rows that have nested data\n return <Row key={key} aria-level={row.__index + 1} aria-expanded={isExpanded} {...props} />;\n }\n\n const handlers: Partial<typeof props> = {};\n if (enableSharedCrosshair) {\n const timeField = fields.find((f) => f.type === FieldType.time);\n if (timeField) {\n handlers.onMouseEnter = () => {\n panelContext.eventBus.publish(\n new DataHoverEvent({\n point: {\n time: timeField?.values[rowIdx],\n },\n })\n );\n };\n handlers.onMouseLeave = () => {\n panelContext.eventBus.publish(new DataHoverClearEvent());\n };\n }\n }\n\n return <Row key={key} {...props} {...handlers} />;\n };\n"],"names":["_a","_b","getTextColorForBackground","_getTextColorForBackground","nestedColumnsMatrix","hasNestedHeaders","props","widths","width","renderCellRoot","height","result","renderRow"],"mappings":"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