@grafana/ui
Version:
Grafana Components Library
1 lines • 66 kB
Source Map (JSON)
{"version":3,"file":"TableNG.mjs","sources":["../../../../../src/components/Table/TableNG/TableNG.tsx"],"sourcesContent":["import 'react-data-grid/lib/styles.css';\nimport { css } from '@emotion/css';\nimport { useMemo, useState, useLayoutEffect, useCallback, useRef, useEffect } from 'react';\nimport DataGrid, { RenderCellProps, RenderRowProps, Row, SortColumn, DataGridHandle } from 'react-data-grid';\nimport { useMeasure } from 'react-use';\n\nimport {\n DataFrame,\n DataHoverClearEvent,\n DataHoverEvent,\n Field,\n fieldReducers,\n FieldType,\n formattedValueToString,\n getDefaultTimeRange,\n GrafanaTheme2,\n ReducerID,\n} from '@grafana/data';\nimport { TableCellDisplayMode } from '@grafana/schema';\n\nimport { useStyles2, useTheme2 } from '../../../themes';\nimport { t, Trans } from '../../../utils/i18n';\nimport { ContextMenu } from '../../ContextMenu/ContextMenu';\nimport { MenuItem } from '../../Menu/MenuItem';\nimport { Pagination } from '../../Pagination/Pagination';\nimport { PanelContext, usePanelContext } from '../../PanelChrome';\nimport { TableCellInspector, TableCellInspectorMode } from '../TableCellInspector';\n\nimport { HeaderCell } from './Cells/HeaderCell';\nimport { RowExpander } from './Cells/RowExpander';\nimport { TableCellNG } from './Cells/TableCellNG';\nimport { COLUMN, TABLE } from './constants';\nimport {\n TableNGProps,\n FilterType,\n TableRow,\n TableSummaryRow,\n ColumnTypes,\n TableColumnResizeActionCallback,\n TableColumn,\n TableFieldOptionsType,\n ScrollPosition,\n CellColors,\n} from './types';\nimport {\n frameToRecords,\n getCellColors,\n getCellHeightCalculator,\n getComparator,\n getDefaultRowHeight,\n getDisplayName,\n getFooterItemNG,\n getFooterStyles,\n getIsNestedTable,\n getRowHeight,\n getTextAlign,\n handleSort,\n MapFrameToGridOptions,\n processNestedTableRows,\n shouldTextOverflow,\n} from './utils';\n\nexport function TableNG(props: TableNGProps) {\n const {\n cellHeight,\n enablePagination,\n enableVirtualization = true,\n fieldConfig,\n footerOptions,\n height,\n initialSortBy,\n noHeader,\n onCellFilterAdded,\n onColumnResize,\n onSortByChange,\n width,\n data,\n enableSharedCrosshair,\n showTypeIcons,\n replaceVariables,\n } = props;\n\n const initialSortColumns = useMemo<SortColumn[]>(() => {\n const initialSort = initialSortBy?.map(({ displayName, desc }) => {\n const matchingField = data.fields.find(({ state }) => state?.displayName === displayName);\n const columnKey = matchingField?.name || displayName;\n\n return {\n columnKey,\n direction: desc ? ('DESC' as const) : ('ASC' as const),\n };\n });\n return initialSort ?? [];\n }, []); // eslint-disable-line react-hooks/exhaustive-deps\n\n /* ------------------------------- Local state ------------------------------ */\n const [revId, setRevId] = useState(0);\n const [contextMenuProps, setContextMenuProps] = useState<{\n rowIdx?: number;\n value: string;\n mode?: TableCellInspectorMode.code | TableCellInspectorMode.text;\n top?: number;\n left?: number;\n } | null>(null);\n const [isInspecting, setIsInspecting] = useState(false);\n const [isContextMenuOpen, setIsContextMenuOpen] = useState(false);\n const [filter, setFilter] = useState<FilterType>({});\n const [page, setPage] = useState(0);\n // This state will trigger re-render for recalculating row heights\n const [, setResizeTrigger] = useState(0);\n const [, setReadyForRowHeightCalc] = useState(false);\n const [sortColumns, setSortColumns] = useState<readonly SortColumn[]>(initialSortColumns);\n const [expandedRows, setExpandedRows] = useState<number[]>([]);\n const [isNestedTable, setIsNestedTable] = useState(false);\n const scrollPositionRef = useRef<ScrollPosition>({ x: 0, y: 0 });\n const [hasScroll, setHasScroll] = useState(false);\n\n /* ------------------------------- Local refs ------------------------------- */\n const crossFilterOrder = useRef<string[]>([]);\n const crossFilterRows = useRef<Record<string, TableRow[]>>({});\n const headerCellRefs = useRef<Record<string, HTMLDivElement>>({});\n // TODO: This ref persists sortColumns between renders. setSortColumns is still used to trigger re-render\n const sortColumnsRef = useRef<SortColumn[]>(initialSortColumns);\n const prevProps = useRef(props);\n const calcsRef = useRef<string[]>([]);\n const [paginationWrapperRef, { height: paginationHeight }] = useMeasure<HTMLDivElement>();\n\n const theme = useTheme2();\n const panelContext = usePanelContext();\n\n const isFooterVisible = Boolean(footerOptions?.show && footerOptions.reducer?.length);\n const isCountRowsSet = Boolean(\n footerOptions?.countRows &&\n footerOptions.reducer &&\n footerOptions.reducer.length &&\n footerOptions.reducer[0] === ReducerID.count\n );\n const tableRef = useRef<DataGridHandle | null>(null);\n\n /* --------------------------------- Effects -------------------------------- */\n useEffect(() => {\n // TODO: there is a use case when adding a new column to the table doesn't update the table\n if (\n prevProps.current.data.fields.length !== props.data.fields.length ||\n prevProps.current.fieldConfig?.overrides !== fieldConfig?.overrides ||\n prevProps.current.fieldConfig?.defaults !== fieldConfig?.defaults\n ) {\n setRevId(revId + 1);\n }\n prevProps.current = props;\n }, [props, revId, fieldConfig?.overrides, fieldConfig?.defaults]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useLayoutEffect(() => {\n if (!isContextMenuOpen) {\n return;\n }\n\n function onClick(event: MouseEvent) {\n setIsContextMenuOpen(false);\n }\n\n addEventListener('click', onClick);\n\n return () => {\n removeEventListener('click', onClick);\n };\n }, [isContextMenuOpen]);\n\n useEffect(() => {\n const hasNestedFrames = getIsNestedTable(props.data);\n setIsNestedTable(hasNestedFrames);\n }, [props.data]);\n\n useEffect(() => {\n const el = tableRef.current;\n if (el) {\n const gridElement = el?.element;\n if (gridElement) {\n setHasScroll(\n gridElement.scrollHeight > gridElement.clientHeight || gridElement.scrollWidth > gridElement.clientWidth\n );\n }\n }\n }, []);\n\n // TODO: this is a hack to force the column width to update when the fieldConfig changes\n const columnWidth = useMemo(() => {\n setRevId(revId + 1);\n return fieldConfig?.defaults?.custom?.width || 'auto';\n }, [fieldConfig]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const defaultRowHeight = getDefaultRowHeight(theme, cellHeight);\n const defaultLineHeight = theme.typography.body.lineHeight * theme.typography.fontSize;\n const panelPaddingHeight = theme.components.panel.padding * theme.spacing.gridSize * 2;\n\n /* ------------------------------ Rows & Columns ----------------------------- */\n const rows = useMemo(() => frameToRecords(props.data), [frameToRecords, props.data]); // eslint-disable-line react-hooks/exhaustive-deps\n\n // Create a map of column key to column type\n const columnTypes = useMemo(\n () => props.data.fields.reduce<ColumnTypes>((acc, field) => ({ ...acc, [getDisplayName(field)]: field.type }), {}),\n [props.data.fields]\n );\n\n // Create a map of column key to text wrap\n const textWraps = useMemo(\n () =>\n props.data.fields.reduce<{ [key: string]: boolean }>(\n (acc, field) => ({\n ...acc,\n [getDisplayName(field)]: field.config?.custom?.cellOptions?.wrapText ?? false,\n }),\n {}\n ),\n [props.data.fields]\n );\n\n const textWrap = useMemo(() => Object.values(textWraps).some(Boolean), [textWraps]);\n const styles = useStyles2(getStyles);\n\n // Create a function to get column widths for text wrapping calculations\n const getColumnWidths = useCallback(() => {\n const widths: Record<string, number> = {};\n\n // Set default widths from field config if they exist\n props.data.fields.forEach((field) => {\n const displayName = getDisplayName(field);\n const configWidth = field.config?.custom?.width;\n const totalWidth = typeof configWidth === 'number' ? configWidth : COLUMN.DEFAULT_WIDTH;\n // subtract out padding and 1px right border\n const contentWidth = totalWidth - 2 * TABLE.CELL_PADDING - 1;\n widths[displayName] = contentWidth;\n });\n\n // Measure actual widths if available\n Object.keys(headerCellRefs.current).forEach((key) => {\n const headerCell = headerCellRefs.current[key];\n\n if (headerCell.offsetWidth > 0) {\n widths[key] = headerCell.offsetWidth;\n }\n });\n\n return widths;\n }, [props.data.fields]);\n\n const headersLength = useMemo(() => {\n return props.data.fields.length;\n }, [props.data.fields]);\n\n const fieldDisplayType = useMemo(() => {\n return props.data.fields.reduce<Record<string, TableCellDisplayMode>>((acc, field) => {\n if (field.config?.custom?.cellOptions?.type) {\n acc[getDisplayName(field)] = field.config.custom.cellOptions.type;\n }\n return acc;\n }, {});\n }, [props.data.fields]);\n\n // Clean up fieldsData to simplify\n const fieldsData = useMemo(\n () => ({\n headersLength,\n textWraps,\n columnTypes,\n fieldDisplayType,\n columnWidths: getColumnWidths(),\n }),\n [textWraps, columnTypes, getColumnWidths, headersLength, fieldDisplayType]\n );\n\n // Filter rows\n const filteredRows = useMemo(() => {\n const filterValues = Object.entries(filter);\n if (filterValues.length === 0) {\n // reset cross filter order\n crossFilterOrder.current = [];\n return rows;\n }\n\n // Helper function to get displayed value\n const getDisplayedValue = (row: TableRow, key: string) => {\n const field = props.data.fields.find((field) => getDisplayName(field) === key);\n if (!field || !field.display) {\n return '';\n }\n const displayedValue = formattedValueToString(field.display(row[key]));\n return displayedValue;\n };\n\n // Update crossFilterOrder\n const filterKeys = new Set(filterValues.map(([key]) => key));\n filterKeys.forEach((key) => {\n if (!crossFilterOrder.current.includes(key)) {\n // Each time a filter is added or removed, it is always a single filter.\n // When adding a new filter, it is always appended to the end, maintaining the order.\n crossFilterOrder.current.push(key);\n }\n });\n // Remove keys from crossFilterOrder that are no longer present in the current filter values\n crossFilterOrder.current = crossFilterOrder.current.filter((key) => filterKeys.has(key));\n\n // reset crossFilterRows\n crossFilterRows.current = {};\n\n // For nested tables, only filter parent rows and keep their children\n if (isNestedTable) {\n return processNestedTableRows(rows, (parents) =>\n parents.filter((row) => {\n for (const [key, value] of filterValues) {\n const displayedValue = getDisplayedValue(row, key);\n if (!value.filteredSet.has(displayedValue)) {\n return false;\n }\n // collect rows for crossFilter\n if (!crossFilterRows.current[key]) {\n crossFilterRows.current[key] = [row];\n } else {\n crossFilterRows.current[key].push(row);\n }\n }\n return true;\n })\n );\n }\n\n // Regular filtering for non-nested tables\n return rows.filter((row) => {\n for (const [key, value] of filterValues) {\n const displayedValue = getDisplayedValue(row, key);\n if (!value.filteredSet.has(displayedValue)) {\n return false;\n }\n // collect rows for crossFilter\n if (!crossFilterRows.current[key]) {\n crossFilterRows.current[key] = [row];\n } else {\n crossFilterRows.current[key].push(row);\n }\n }\n return true;\n });\n }, [rows, filter, isNestedTable, props.data.fields]);\n\n // Sort rows\n const sortedRows = useMemo(() => {\n if (sortColumns.length === 0) {\n return filteredRows;\n }\n\n // Common sort comparator function\n const compareRows = (a: TableRow, b: TableRow): number => {\n let result = 0;\n for (let i = 0; i < sortColumns.length; i++) {\n const { columnKey, direction } = sortColumns[i];\n const compare = getComparator(columnTypes[columnKey]);\n const sortDir = direction === 'ASC' ? 1 : -1;\n\n result = sortDir * compare(a[columnKey], b[columnKey]);\n if (result !== 0) {\n break;\n }\n }\n return result;\n };\n\n // Handle nested tables\n if (isNestedTable) {\n return processNestedTableRows(filteredRows, (parents) => [...parents].sort(compareRows));\n }\n\n // Regular sort for tables without nesting\n return filteredRows.slice().sort((a, b) => compareRows(a, b));\n }, [filteredRows, sortColumns, columnTypes, isNestedTable]);\n\n // Paginated rows\n // TODO consolidate calculations into pagination wrapper component and only use when needed\n const numRows = sortedRows.length;\n // calculate number of rowsPerPage based on height stack\n let headerCellHeight = TABLE.MAX_CELL_HEIGHT;\n if (noHeader) {\n headerCellHeight = 0;\n } else if (!noHeader && Object.keys(headerCellRefs.current).length > 0) {\n headerCellHeight = headerCellRefs.current[Object.keys(headerCellRefs.current)[0]].getBoundingClientRect().height;\n }\n let rowsPerPage = Math.floor(\n (height - headerCellHeight - TABLE.SCROLL_BAR_WIDTH - paginationHeight - panelPaddingHeight) / defaultRowHeight\n );\n // if footer calcs are on, remove one row per page\n if (isFooterVisible) {\n rowsPerPage -= 1;\n }\n if (rowsPerPage < 1) {\n // avoid 0 or negative rowsPerPage\n rowsPerPage = 1;\n }\n const numberOfPages = Math.ceil(numRows / rowsPerPage);\n if (page > numberOfPages) {\n // resets pagination to end\n setPage(numberOfPages - 1);\n }\n // calculate row range for pagination summary display\n const itemsRangeStart = page * rowsPerPage + 1;\n let displayedEnd = itemsRangeStart + rowsPerPage - 1;\n if (displayedEnd > numRows) {\n displayedEnd = numRows;\n }\n const smallPagination = width < TABLE.PAGINATION_LIMIT;\n\n const paginatedRows = useMemo(() => {\n const pageOffset = page * rowsPerPage;\n return sortedRows.slice(pageOffset, pageOffset + rowsPerPage);\n }, [rows, sortedRows, page, rowsPerPage]); // eslint-disable-line react-hooks/exhaustive-deps\n\n useMemo(() => {\n calcsRef.current = props.data.fields.map((field, index) => {\n if (field.state?.calcs) {\n delete field.state?.calcs;\n }\n if (isCountRowsSet) {\n return index === 0 ? `${sortedRows.length}` : '';\n }\n if (index === 0) {\n const footerCalcReducer = footerOptions?.reducer?.[0];\n return footerCalcReducer ? fieldReducers.get(footerCalcReducer).name : '';\n }\n return getFooterItemNG(sortedRows, field, footerOptions);\n });\n }, [sortedRows, props.data.fields, footerOptions, isCountRowsSet]); // eslint-disable-line react-hooks/exhaustive-deps\n\n const onCellExpand = (rowIdx: number) => {\n if (!expandedRows.includes(rowIdx)) {\n setExpandedRows([...expandedRows, rowIdx]);\n } else {\n setExpandedRows(expandedRows.filter((id) => id !== rowIdx));\n }\n setResizeTrigger((prev) => prev + 1);\n };\n\n const { ctx, avgCharWidth } = useMemo(() => {\n const font = `${theme.typography.fontSize}px ${theme.typography.fontFamily}`;\n const canvas = document.createElement('canvas');\n const ctx = canvas.getContext('2d')!;\n // set in grafana/data in createTypography.ts\n const letterSpacing = 0.15;\n\n ctx.letterSpacing = `${letterSpacing}px`;\n ctx.font = font;\n let txt =\n \"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\";\n const txtWidth = ctx.measureText(txt).width;\n const avgCharWidth = txtWidth / txt.length + letterSpacing;\n\n return {\n ctx,\n font,\n avgCharWidth,\n };\n }, [theme.typography.fontSize, theme.typography.fontFamily]);\n\n const columns = useMemo(\n () =>\n mapFrameToDataGrid({\n frame: props.data,\n calcsRef,\n options: {\n columnTypes,\n textWraps,\n columnWidth,\n crossFilterOrder,\n crossFilterRows,\n defaultLineHeight,\n defaultRowHeight,\n expandedRows,\n filter,\n headerCellRefs,\n isCountRowsSet,\n onCellFilterAdded,\n ctx,\n onSortByChange,\n rows,\n setContextMenuProps,\n setFilter,\n setIsInspecting,\n setSortColumns,\n sortColumnsRef,\n styles,\n theme,\n showTypeIcons,\n replaceVariables,\n ...props,\n },\n handlers: {\n onCellExpand,\n onColumnResize: onColumnResize!,\n },\n // Adjust table width to account for the scroll bar width\n availableWidth: width - (hasScroll ? TABLE.SCROLL_BAR_WIDTH + TABLE.SCROLL_BAR_MARGIN : 0),\n }),\n [props.data, calcsRef, filter, expandedRows, expandedRows.length, footerOptions, width, hasScroll, sortedRows] // eslint-disable-line react-hooks/exhaustive-deps\n );\n\n // This effect needed to set header cells refs before row height calculation\n useLayoutEffect(() => {\n setReadyForRowHeightCalc(Object.keys(headerCellRefs.current).length > 0);\n }, [columns]);\n\n const renderMenuItems = () => {\n return (\n <>\n <MenuItem\n label={t('grafana-ui.table.inspect-menu-label', 'Inspect value')}\n onClick={() => {\n setIsInspecting(true);\n }}\n className={styles.menuItem}\n />\n </>\n );\n };\n\n const cellHeightCalc = useMemo(() => {\n return getCellHeightCalculator(ctx, defaultLineHeight, defaultRowHeight, TABLE.CELL_PADDING);\n }, [ctx, defaultLineHeight, defaultRowHeight]);\n\n const calculateRowHeight = useCallback(\n (row: TableRow) => {\n // Logic for sub-tables\n if (Number(row.__depth) === 1 && !expandedRows.includes(Number(row.__index))) {\n return 0;\n } else if (Number(row.__depth) === 1 && expandedRows.includes(Number(row.__index))) {\n const headerCount = row?.data?.meta?.custom?.noHeader ? 0 : 1;\n\n // Ensure we have a minimum height for the nested table even if data is empty\n const rowCount = row.data?.length ?? 0;\n return Math.max(defaultRowHeight, defaultRowHeight * (rowCount + headerCount));\n }\n return getRowHeight(row, cellHeightCalc, avgCharWidth, defaultRowHeight, fieldsData);\n },\n [expandedRows, avgCharWidth, defaultRowHeight, fieldsData, cellHeightCalc]\n );\n\n const handleScroll = (event: React.UIEvent<HTMLDivElement>) => {\n const target = event.currentTarget;\n scrollPositionRef.current = {\n x: target.scrollLeft,\n y: target.scrollTop,\n };\n };\n\n // Reset sortColumns when initialSortBy changes\n useEffect(() => {\n if (initialSortColumns.length > 0) {\n setSortColumns(initialSortColumns);\n }\n }, [initialSortColumns]);\n\n // Restore scroll position after re-renders\n useEffect(() => {\n if (tableRef.current?.element) {\n tableRef.current.element.scrollLeft = scrollPositionRef.current.x;\n tableRef.current.element.scrollTop = scrollPositionRef.current.y;\n }\n }, [revId]);\n\n return (\n <>\n <DataGrid<TableRow, TableSummaryRow>\n ref={tableRef}\n className={styles.dataGrid}\n // Default to true, overridden to false for testing\n enableVirtualization={enableVirtualization}\n key={`DataGrid${revId}`}\n rows={enablePagination ? paginatedRows : sortedRows}\n columns={columns}\n headerRowHeight={noHeader ? 0 : undefined}\n defaultColumnOptions={{\n sortable: true,\n resizable: true,\n }}\n rowHeight={textWrap || isNestedTable ? calculateRowHeight : defaultRowHeight}\n // TODO: This doesn't follow current table behavior\n style={{ width, height: height - (enablePagination ? paginationHeight : 0) }}\n renderers={{\n renderRow: (key, props) =>\n myRowRenderer(key, props, expandedRows, panelContext, data, enableSharedCrosshair ?? false),\n }}\n onScroll={handleScroll}\n onCellContextMenu={({ row, column }, event) => {\n event.preventGridDefault();\n // Do not show the default context menu\n event.preventDefault();\n\n const cellValue = row[column.key];\n setContextMenuProps({\n // rowIdx: rows.indexOf(row),\n value: String(cellValue ?? ''),\n top: event.clientY,\n left: event.clientX,\n });\n setIsContextMenuOpen(true);\n }}\n // sorting\n sortColumns={sortColumns}\n // footer\n // TODO figure out exactly how this works - some array needs to be here for it to render regardless of renderSummaryCell()\n bottomSummaryRows={isFooterVisible ? [{}] : undefined}\n onColumnResize={() => {\n // NOTE: This method is called continuously during the column resize drag operation,\n // providing the current column width. There is no separate event for the end of the drag operation.\n if (textWrap) {\n // This is needed only when textWrap is enabled\n // TODO: this is a hack to force rowHeight re-calculation\n setResizeTrigger((prev) => prev + 1);\n }\n }}\n />\n\n {enablePagination && (\n <div className={styles.paginationContainer} ref={paginationWrapperRef}>\n <Pagination\n className=\"table-ng-pagination\"\n currentPage={page + 1}\n numberOfPages={numberOfPages}\n showSmallVersion={smallPagination}\n onNavigate={(toPage) => {\n setPage(toPage - 1);\n }}\n />\n {!smallPagination && (\n <div className={styles.paginationSummary}>\n <Trans i18nKey=\"grafana-ui.table.pagination-summary\">\n {{ itemsRangeStart }} - {{ displayedEnd }} of {{ numRows }} rows\n </Trans>\n </div>\n )}\n </div>\n )}\n\n {isContextMenuOpen && (\n <ContextMenu\n x={contextMenuProps?.left || 0}\n y={contextMenuProps?.top || 0}\n renderMenuItems={renderMenuItems}\n focusOnOpen={false}\n />\n )}\n\n {isInspecting && (\n <TableCellInspector\n mode={contextMenuProps?.mode ?? TableCellInspectorMode.text}\n value={contextMenuProps?.value}\n onDismiss={() => {\n setIsInspecting(false);\n setContextMenuProps(null);\n }}\n />\n )}\n </>\n );\n}\n\nexport function mapFrameToDataGrid({\n frame,\n calcsRef,\n options,\n handlers,\n availableWidth,\n}: {\n frame: DataFrame;\n calcsRef: React.MutableRefObject<string[]>;\n options: MapFrameToGridOptions;\n handlers: { onCellExpand: (rowIdx: number) => void; onColumnResize: TableColumnResizeActionCallback };\n availableWidth: number;\n}): TableColumn[] {\n const {\n columnTypes,\n textWraps,\n crossFilterOrder,\n crossFilterRows,\n defaultLineHeight,\n defaultRowHeight,\n expandedRows,\n filter,\n headerCellRefs,\n isCountRowsSet,\n onCellFilterAdded,\n ctx,\n onSortByChange,\n rows,\n setContextMenuProps,\n setFilter,\n setIsInspecting,\n setSortColumns,\n sortColumnsRef,\n styles,\n theme,\n timeRange,\n getActions,\n showTypeIcons,\n replaceVariables,\n } = options;\n const { onCellExpand, onColumnResize } = handlers;\n\n const columns: TableColumn[] = [];\n const hasNestedFrames = getIsNestedTable(frame);\n\n // If nested frames, add expansion control column\n if (hasNestedFrames) {\n const expanderField: Field = {\n name: '',\n type: FieldType.other,\n config: {},\n values: [],\n };\n columns.push({\n key: 'expanded',\n name: '',\n field: expanderField,\n cellClass: styles.cell,\n colSpan(args) {\n return args.type === 'ROW' && Number(args.row.__depth) === 1 ? frame.fields.length : 1;\n },\n renderCell: ({ row }) => {\n // TODO add TableRow type extension to include row depth and optional data\n if (Number(row.__depth) === 0) {\n const rowIdx = Number(row.__index);\n return (\n <RowExpander\n height={defaultRowHeight}\n onCellExpand={() => onCellExpand(rowIdx)}\n isExpanded={expandedRows.includes(rowIdx)}\n />\n );\n }\n // If it's a child, render entire DataGrid at first column position\n let expandedColumns: TableColumn[] = [];\n let expandedRecords: TableRow[] = [];\n\n // Type guard to check if data exists as it's optional\n if (row.data) {\n expandedColumns = mapFrameToDataGrid({\n frame: row.data,\n calcsRef,\n options: { ...options },\n handlers: { onCellExpand, onColumnResize },\n availableWidth,\n });\n expandedRecords = frameToRecords(row.data);\n }\n\n // TODO add renderHeaderCell HeaderCell's here and handle all features\n return (\n <DataGrid<TableRow, TableSummaryRow>\n rows={expandedRecords}\n columns={expandedColumns}\n rowHeight={defaultRowHeight}\n className={styles.dataGrid}\n style={{ height: '100%', overflow: 'visible', marginLeft: COLUMN.EXPANDER_WIDTH - 1 }}\n headerRowHeight={row.data?.meta?.custom?.noHeader ? 0 : undefined}\n />\n );\n },\n width: COLUMN.EXPANDER_WIDTH,\n minWidth: COLUMN.EXPANDER_WIDTH,\n });\n\n availableWidth -= COLUMN.EXPANDER_WIDTH;\n }\n\n // Row background color function\n let rowBg: Function | undefined = undefined;\n for (const field of frame.fields) {\n const fieldOptions = field.config.custom;\n const cellOptionsExist = fieldOptions !== undefined && fieldOptions.cellOptions !== undefined;\n\n if (\n cellOptionsExist &&\n fieldOptions.cellOptions.type === TableCellDisplayMode.ColorBackground &&\n fieldOptions.cellOptions.applyToRow\n ) {\n rowBg = (rowIndex: number): CellColors => {\n const display = field.display!(field.values[rowIndex]);\n const colors = getCellColors(theme, fieldOptions.cellOptions, display);\n return colors;\n };\n }\n }\n\n let fieldCountWithoutWidth = 0;\n frame.fields.map((field, fieldIndex) => {\n if (field.type === FieldType.nestedFrames || field.config.custom?.hidden) {\n // Don't render nestedFrames type field\n return;\n }\n const fieldTableOptions: TableFieldOptionsType = field.config.custom || {};\n const key = getDisplayName(field);\n const justifyColumnContent = getTextAlign(field);\n const footerStyles = getFooterStyles(justifyColumnContent);\n\n // current/old table width logic calculations\n if (fieldTableOptions.width) {\n availableWidth -= fieldTableOptions.width;\n } else {\n fieldCountWithoutWidth++;\n }\n\n // Add a column for each field\n columns.push({\n key,\n name: field.name,\n field,\n cellClass: textWraps[getDisplayName(field)] ? styles.cellWrapped : styles.cell,\n renderCell: (props: RenderCellProps<TableRow, TableSummaryRow>): JSX.Element => {\n const { row } = props;\n const cellType = field.config?.custom?.cellOptions?.type ?? TableCellDisplayMode.Auto;\n const value = row[key];\n // Cell level rendering here\n return (\n <TableCellNG\n frame={frame}\n key={key}\n value={value}\n field={field}\n theme={theme}\n timeRange={timeRange ?? getDefaultTimeRange()}\n height={defaultRowHeight}\n justifyContent={justifyColumnContent}\n rowIdx={row.__index}\n shouldTextOverflow={() =>\n shouldTextOverflow(\n key,\n row,\n columnTypes,\n headerCellRefs,\n ctx,\n defaultLineHeight,\n defaultRowHeight,\n TABLE.CELL_PADDING,\n textWraps[getDisplayName(field)],\n field,\n cellType\n )\n }\n setIsInspecting={setIsInspecting}\n setContextMenuProps={setContextMenuProps}\n getActions={getActions}\n rowBg={rowBg}\n onCellFilterAdded={onCellFilterAdded}\n replaceVariables={replaceVariables}\n />\n );\n },\n renderSummaryCell: () => {\n if (isCountRowsSet && fieldIndex === 0) {\n return (\n <div style={{ display: 'flex', justifyContent: 'space-between' }}>\n <span>\n <Trans i18nKey=\"grafana-ui.table.count\">Count</Trans>\n </span>\n <span>{calcsRef.current[fieldIndex]}</span>\n </div>\n );\n }\n return <div className={footerStyles.footerCell}>{calcsRef.current[fieldIndex]}</div>;\n },\n renderHeaderCell: ({ column, sortDirection }): JSX.Element => (\n <HeaderCell\n column={column}\n rows={rows}\n field={field}\n onSort={(columnKey, direction, isMultiSort) => {\n handleSort(columnKey, direction, isMultiSort, setSortColumns, sortColumnsRef);\n\n // Update panel context with the new sort order\n if (onSortByChange) {\n const sortByFields = sortColumnsRef.current.map(({ columnKey, direction }) => ({\n displayName: columnKey,\n desc: direction === 'DESC',\n }));\n onSortByChange(sortByFields);\n }\n }}\n direction={sortDirection}\n justifyContent={justifyColumnContent}\n filter={filter}\n setFilter={setFilter}\n onColumnResize={onColumnResize}\n headerCellRefs={headerCellRefs}\n crossFilterOrder={crossFilterOrder}\n crossFilterRows={crossFilterRows}\n showTypeIcons={showTypeIcons}\n />\n ),\n width: fieldTableOptions.width,\n minWidth: fieldTableOptions.minWidth || COLUMN.DEFAULT_WIDTH,\n });\n });\n\n // set columns that are at minimum width\n let sharedWidth = availableWidth / fieldCountWithoutWidth;\n for (let i = fieldCountWithoutWidth; i > 0; i--) {\n for (const column of columns) {\n if (!column.width && column.minWidth! > sharedWidth) {\n column.width = column.minWidth;\n availableWidth -= column.width!;\n fieldCountWithoutWidth -= 1;\n sharedWidth = availableWidth / fieldCountWithoutWidth;\n }\n }\n }\n\n // divide up the rest of the space\n for (const column of columns) {\n if (!column.width) {\n column.width = sharedWidth;\n }\n column.minWidth = COLUMN.MIN_WIDTH;\n }\n\n return columns;\n}\n\nexport function myRowRenderer(\n key: React.Key,\n props: RenderRowProps<TableRow, TableSummaryRow>,\n expandedRows: number[],\n panelContext: PanelContext,\n data: DataFrame,\n enableSharedCrosshair: boolean\n): React.ReactNode {\n // Let's render row level things here!\n // i.e. we can look at row styles and such here\n const { row } = props;\n const rowIdx = Number(row.__index);\n const isExpanded = expandedRows.includes(rowIdx);\n\n // Don't render non expanded child rows\n if (Number(row.__depth) === 1 && !isExpanded) {\n return null;\n }\n\n // Add aria-expanded to parent rows that have nested data\n if (row.data) {\n return <Row key={key} {...props} aria-expanded={isExpanded} />;\n }\n\n return (\n <Row\n key={key}\n {...props}\n onMouseEnter={() => onRowHover(rowIdx, panelContext, data, enableSharedCrosshair)}\n onMouseLeave={() => onRowLeave(panelContext, enableSharedCrosshair)}\n />\n );\n}\n\nexport function onRowHover(idx: number, panelContext: PanelContext, frame: DataFrame, enableSharedCrosshair: boolean) {\n if (!enableSharedCrosshair) {\n return;\n }\n\n const timeField: Field = frame!.fields.find((f) => f.type === FieldType.time)!;\n\n if (!timeField) {\n return;\n }\n\n panelContext.eventBus.publish(\n new DataHoverEvent({\n point: {\n time: timeField.values[idx],\n },\n })\n );\n}\n\nexport function onRowLeave(panelContext: PanelContext, enableSharedCrosshair: boolean) {\n if (!enableSharedCrosshair) {\n return;\n }\n\n panelContext.eventBus.publish(new DataHoverClearEvent());\n}\n\nconst getStyles = (theme: GrafanaTheme2) => ({\n dataGrid: css({\n '--rdg-background-color': theme.colors.background.primary,\n '--rdg-header-background-color': theme.colors.background.primary,\n '--rdg-border-color': 'transparent',\n '--rdg-color': theme.colors.text.primary,\n '&:hover': {\n '--rdg-row-hover-background-color': theme.colors.emphasize(theme.colors.action.hover, 0.6),\n },\n\n // If we rely solely on borderInlineEnd which is added from data grid, we\n // get a small gap where the gridCell borders meet the column header borders.\n // To avoid this, we can unset borderInlineEnd and set borderRight instead.\n '.rdg-cell': {\n borderInlineEnd: 'unset',\n borderRight: `1px solid ${theme.colors.border.medium}`,\n\n '&:last-child': {\n borderRight: 'none',\n },\n },\n\n '.rdg-summary-row': {\n backgroundColor: theme.colors.background.primary,\n '--rdg-summary-border-color': theme.colors.border.medium,\n\n '.rdg-cell': {\n // Prevent collisions with custom cell components\n zIndex: 2,\n borderRight: 'none',\n },\n },\n\n // Due to stylistic choices, we do not want borders on the column headers\n // other than the bottom border.\n 'div[role=columnheader]': {\n borderBottom: `1px solid ${theme.colors.border.medium}`,\n borderInlineEnd: 'unset',\n\n '.r1y6ywlx7-0-0-beta-46': {\n '&:hover': {\n borderRight: `3px solid ${theme.colors.text.link}`,\n },\n },\n },\n\n '::-webkit-scrollbar': {\n width: TABLE.SCROLL_BAR_WIDTH,\n height: TABLE.SCROLL_BAR_WIDTH,\n },\n '::-webkit-scrollbar-thumb': {\n backgroundColor: 'rgba(204, 204, 220, 0.16)',\n borderRadius: '4px',\n },\n '::-webkit-scrollbar-track': {\n background: 'transparent',\n },\n '::-webkit-scrollbar-corner': {\n backgroundColor: 'transparent',\n },\n }),\n menuItem: css({\n maxWidth: '200px',\n }),\n cell: css({\n '--rdg-border-color': theme.colors.border.medium,\n borderLeft: 'none',\n whiteSpace: 'nowrap',\n wordWrap: 'break-word',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n\n // Reset default cell styles for custom cell component styling\n paddingInline: '0',\n }),\n cellWrapped: css({\n '--rdg-border-color': theme.colors.border.medium,\n borderLeft: 'none',\n whiteSpace: 'pre-line',\n wordWrap: 'break-word',\n overflow: 'hidden',\n textOverflow: 'ellipsis',\n\n // Reset default cell styles for custom cell component styling\n paddingInline: '0',\n }),\n paginationContainer: css({\n alignItems: 'center',\n display: 'flex',\n justifyContent: 'center',\n marginTop: '8px',\n width: '100%',\n }),\n paginationSummary: css({\n color: theme.colors.text.secondary,\n fontSize: theme.typography.bodySmall.fontSize,\n display: 'flex',\n justifyContent: 'flex-end',\n padding: theme.spacing(0, 1, 0, 2),\n }),\n});\n"],"names":["_a","_b","field","ctx","avgCharWidth","props","columnKey","direction"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA8DO,SAAS,QAAQ,KAAqB,EAAA;AA9D7C,EAAA,IAAA,EAAA,EAAA,EAAA;AA+DE,EAAM,MAAA;AAAA,IACJ,UAAA;AAAA,IACA,gBAAA;AAAA,IACA,oBAAuB,GAAA,IAAA;AAAA,IACvB,WAAA;AAAA,IACA,aAAA;AAAA,IACA,MAAA;AAAA,IACA,aAAA;AAAA,IACA,QAAA;AAAA,IACA,iBAAA;AAAA,IACA,cAAA;AAAA,IACA,cAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,qBAAA;AAAA,IACA,aAAA;AAAA,IACA;AAAA,GACE,GAAA,KAAA;AAEJ,EAAM,MAAA,kBAAA,GAAqB,QAAsB,MAAM;AACrD,IAAA,MAAM,cAAc,aAAe,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAA,GAAA,CAAI,CAAC,EAAE,WAAA,EAAa,MAAW,KAAA;AAChE,MAAM,MAAA,aAAA,GAAgB,IAAK,CAAA,MAAA,CAAO,IAAK,CAAA,CAAC,EAAE,KAAM,EAAA,KAAA,CAAM,KAAO,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,KAAA,CAAA,WAAA,MAAgB,WAAW,CAAA;AACxF,MAAM,MAAA,SAAA,GAAA,CAAY,+CAAe,IAAQ,KAAA,WAAA;AAEzC,MAAO,OAAA;AAAA,QACL,SAAA;AAAA,QACA,SAAA,EAAW,OAAQ,MAAoB,GAAA;AAAA,OACzC;AAAA,KACF,CAAA;AACA,IAAA,OAAO,oCAAe,EAAC;AAAA,GACzB,EAAG,EAAE,CAAA;AAGL,EAAA,MAAM,CAAC,KAAA,EAAO,QAAQ,CAAA,GAAI,SAAS,CAAC,CAAA;AACpC,EAAA,MAAM,CAAC,gBAAA,EAAkB,mBAAmB,CAAA,GAAI,SAMtC,IAAI,CAAA;AACd,EAAA,MAAM,CAAC,YAAA,EAAc,eAAe,CAAA,GAAI,SAAS,KAAK,CAAA;AACtD,EAAA,MAAM,CAAC,iBAAA,EAAmB,oBAAoB,CAAA,GAAI,SAAS,KAAK,CAAA;AAChE,EAAA,MAAM,CAAC,MAAQ,EAAA,SAAS,CAAI,GAAA,QAAA,CAAqB,EAAE,CAAA;AACnD,EAAA,MAAM,CAAC,IAAA,EAAM,OAAO,CAAA,GAAI,SAAS,CAAC,CAAA;AAElC,EAAA,MAAM,GAAG,gBAAgB,CAAA,GAAI,SAAS,CAAC,CAAA;AACvC,EAAA,MAAM,GAAG,wBAAwB,CAAA,GAAI,SAAS,KAAK,CAAA;AACnD,EAAA,MAAM,CAAC,WAAA,EAAa,cAAc,CAAA,GAAI,SAAgC,kBAAkB,CAAA;AACxF,EAAA,MAAM,CAAC,YAAc,EAAA,eAAe,CAAI,GAAA,QAAA,CAAmB,EAAE,CAAA;AAC7D,EAAA,MAAM,CAAC,aAAA,EAAe,gBAAgB,CAAA,GAAI,SAAS,KAAK,CAAA;AACxD,EAAA,MAAM,oBAAoB,MAAuB,CAAA,EAAE,GAAG,CAAG,EAAA,CAAA,EAAG,GAAG,CAAA;AAC/D,EAAA,MAAM,CAAC,SAAA,EAAW,YAAY,CAAA,GAAI,SAAS,KAAK,CAAA;AAGhD,EAAM,MAAA,gBAAA,GAAmB,MAAiB,CAAA,EAAE,CAAA;AAC5C,EAAM,MAAA,eAAA,GAAkB,MAAmC,CAAA,EAAE,CAAA;AAC7D,EAAM,MAAA,cAAA,GAAiB,MAAuC,CAAA,EAAE,CAAA;AAEhE,EAAM,MAAA,cAAA,GAAiB,OAAqB,kBAAkB,CAAA;AAC9D,EAAM,MAAA,SAAA,GAAY,OAAO,KAAK,CAAA;AAC9B,EAAM,MAAA,QAAA,GAAW,MAAiB,CAAA,EAAE,CAAA;AACpC,EAAA,MAAM,CAAC,oBAAsB,EAAA,EAAE,QAAQ,gBAAiB,EAAC,IAAI,UAA2B,EAAA;AAExF,EAAA,MAAM,QAAQ,SAAU,EAAA;AACxB,EAAA,MAAM,eAAe,eAAgB,EAAA;AAErC,EAAA,MAAM,kBAAkB,OAAQ,CAAA,CAAA,aAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAe,WAAQ,EAAc,GAAA,aAAA,CAAA,OAAA,KAAd,mBAAuB,MAAM,CAAA,CAAA;AACpF,EAAA,MAAM,cAAiB,GAAA,OAAA;AAAA,IACrB,CAAA,aAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAe,SACb,KAAA,aAAA,CAAc,OACd,IAAA,aAAA,CAAc,OAAQ,CAAA,MAAA,IACtB,aAAc,CAAA,OAAA,CAAQ,CAAC,CAAA,KAAM,SAAU,CAAA;AAAA,GAC3C;AACA,EAAM,MAAA,QAAA,GAAW,OAA8B,IAAI,CAAA;AAGnD,EAAA,SAAA,CAAU,MAAM;AA5IlB,IAAA,IAAAA,GAAAC,EAAAA,GAAAA;AA8II,IACE,IAAA,SAAA,CAAU,OAAQ,CAAA,IAAA,CAAK,MAAO,CAAA,MAAA,KAAW,KAAM,CAAA,IAAA,CAAK,MAAO,CAAA,MAAA,IAAA,CAAA,CAC3DD,GAAA,GAAA,SAAA,CAAU,OAAQ,CAAA,WAAA,KAAlB,gBAAAA,GAA+B,CAAA,SAAA,OAAc,WAAa,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,WAAA,CAAA,SAAA,CAAA,IAAA,CAAA,CAC1DC,GAAA,GAAA,SAAA,CAAU,OAAQ,CAAA,WAAA,KAAlB,IAAAA,GAAAA,KAAAA,CAAAA,GAAAA,GAAAA,CAA+B,QAAa,OAAA,WAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,WAAA,CAAa,QACzD,CAAA,EAAA;AACA,MAAA,QAAA,CAAS,QAAQ,CAAC,CAAA;AAAA;AAEpB,IAAA,SAAA,CAAU,OAAU,GAAA,KAAA;AAAA,GACtB,EAAG,CAAC,KAAO,EAAA,KAAA,EAAO,2CAAa,SAAW,EAAA,WAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,WAAA,CAAa,QAAQ,CAAC,CAAA;AAEhE,EAAA,eAAA,CAAgB,MAAM;AACpB,IAAA,IAAI,CAAC,iBAAmB,EAAA;AACtB,MAAA;AAAA;AAGF,IAAA,SAAS,QAAQ,KAAmB,EAAA;AAClC,MAAA,oBAAA,CAAqB,KAAK,CAAA;AAAA;AAG5B,IAAA,gBAAA,CAAiB,SAAS,OAAO,CAAA;AAEjC,IAAA,OAAO,MAAM;AACX,MAAA,mBAAA,CAAoB,SAAS,OAAO,CAAA;AAAA,KACtC;AAAA,GACF,EAAG,CAAC,iBAAiB,CAAC,CAAA;AAEtB,EAAA,SAAA,CAAU,MAAM;AACd,IAAM,MAAA,eAAA,GAAkB,gBAAiB,CAAA,KAAA,CAAM,IAAI,CAAA;AACnD,IAAA,gBAAA,CAAiB,eAAe,CAAA;AAAA,GAC/B,EAAA,CAAC,KAAM,CAAA,IAAI,CAAC,CAAA;AAEf,EAAA,SAAA,CAAU,MAAM;AACd,IAAA,MAAM,KAAK,QAAS,CAAA,OAAA;AACpB,IAAA,IAAI,EAAI,EAAA;AACN,MAAA,MAAM,cAAc,EAAI,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,OAAA;AACxB,MAAA,IAAI,WAAa,EAAA;AACf,QAAA,YAAA;AAAA,UACE,YAAY,YAAe,GAAA,WAAA,CAAY,YAAgB,IAAA,WAAA,CAAY,cAAc,WAAY,CAAA;AAAA,SAC/F;AAAA;AACF;AACF,GACF,EAAG,EAAE,CAAA;AAGL,EAAM,MAAA,WAAA,GAAc,QAAQ,MAAM;AA1LpC,IAAA,IAAAD,GAAAC,EAAAA,GAAAA;AA2LI,IAAA,QAAA,CAAS,QAAQ,CAAC,CAAA;AAClB,IAAOA,OAAAA,CAAAA,CAAAA,GAAAA,GAAAA,CAAAD,MAAA,WAAa,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,WAAA,CAAA,QAAA,KAAb,gBAAAA,GAAuB,CAAA,MAAA,KAAvB,IAAAC,GAAAA,KAAAA,CAAAA,GAAAA,GAAAA,CAA+B,KAAS,KAAA,MAAA;AAAA,GACjD,EAAG,CAAC,WAAW,CAAC,CAAA;AAEhB,EAAM,MAAA,gBAAA,GAAmB,mBAAoB,CAAA,KAAA,EAAO,UAAU,CAAA;AAC9D,EAAA,MAAM,oBAAoB,KAAM,CAAA,UAAA,CAAW,IAAK,CAAA,UAAA,GAAa,MAAM,UAAW,CAAA,QAAA;AAC9E,EAAA,MAAM,qBAAqB,KAAM,CAAA,UAAA,CAAW,MAAM,OAAU,GAAA,KAAA,CAAM,QAAQ,QAAW,GAAA,CAAA;AAGrF,EAAM,MAAA,IAAA,GAAO,OAAQ,CAAA,MAAM,cAAe,CAAA,KAAA,CAAM,IAAI,CAAA,EAAG,CAAC,cAAA,EAAgB,KAAM,CAAA,IAAI,CAAC,CAAA;AAGnF,EAAA,MAAM,WAAc,GAAA,OAAA;AAAA,IAClB,MAAM,MAAM,IAAK,CAAA,MAAA,CAAO,OAAoB,CAAC,GAAA,EAAK,WAAW,EAAE,GAAG,KAAK,CAAC,cAAA,CAAe,KAAK,CAAC,GAAG,MAAM,IAAK,EAAA,CAAA,EAAI,EAAE,CAAA;AAAA,IACjH,CAAC,KAAM,CAAA,IAAA,CAAK,MAAM;AAAA,GACpB;AAGA,EAAA,MAAM,SAAY,GAAA,OAAA;AAAA,IAChB,MACE,KAAM,CAAA,IAAA,CAAK,MAAO,CAAA,MAAA;AAAA,MAChB,CAAC,KAAK,KAAO,KAAA;AAhNrB,QAAA,IAAAD,KAAAC,GAAA,EAAA,EAAA,EAAA,EAAA;AAgNyB,QAAA,OAAA;AAAA,UACf,GAAG,GAAA;AAAA,UACH,CAAC,cAAe,CAAA,KAAK,CAAC,GAAG,CAAA,EAAA,GAAA,CAAA,EAAA,GAAA,CAAAA,OAAAD,GAAA,GAAA,KAAA,CAAM,MAAN,KAAA,IAAA,GAAA,KAAA,CAAA,GAAAA,IAAc,MAAd,KAAA,IAAA,GAAA,KAAA,CAAA,GAAAC,IAAsB,WAAtB,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAmC,aAAnC,IAA+C,GAAA,EAAA,GAAA;AAAA,SAC1E;AAAA,OAAA;AAAA,MACA;AAAC,KACH;AAAA,IACF,CAAC,KAAM,CAAA,IAAA,CAAK,MAAM;AAAA,GACpB;AAEA,EAAA,MAAM,QAAW,GAAA,OAAA,CAAQ,MAAM,MAAA,CAAO,MAAO,CAAA,SAAS,CAAE,CAAA,IAAA,CAAK,OAAO,CAAA,EAAG,CAAC,SAAS,CAAC,CAAA;AAClF,EAAM,MAAA,MAAA,GAAS,WAAW,SAAS,CAAA;AAGnC,EAAM,MAAA,eAAA,GAAkB,YAAY,MAAM;AACxC,IAAA,MAAM,SAAiC,EAAC;AAGxC,IAAA,KAAA,CAAM,IAAK,CAAA,MAAA,CAAO,OAAQ,CAAA,CAAC,KAAU,KAAA;AAjOzC,MAAA,IAAAD,GAAAC,EAAAA,GAAAA;AAkOM,MAAM,MAAA,WAAA,GAAc,eAAe,KAAK,CAAA;AACxC,MAAM,MAAA,WAAA,GAAA,CAAcA,OAAAD,GAAA,GAAA,KAAA,CAAM,WAAN,IAAAA,GAAAA,KAAAA,CAAAA,GAAAA,GAAAA,CAAc,MAAd,KAAA,IAAA,GAAA,KAAA,CAAA,GAAAC,GAAsB,CAAA,KAAA;AAC1C,MAAA,MAAM,UAAa,GAAA,OAAO,WAAgB,KAAA,QAAA,GAAW,cAAc,MAAO,CAAA,aAAA;AAE1E,MAAA,MAAM,YAAe,GAAA,UAAA,GAAa,CAAI,GAAA,KAAA,CAAM,YAAe,GAAA,CAAA;AAC3D,MAAA,MAAA,CAAO,WAAW,CAAI,GAAA,YAAA;AAAA,KACvB,CAAA;AAGD,IAAA,MAAA,CAAO,KAAK,cAAe,CAAA,OAAO,CAAE,CAAA,OAAA,CAAQ,CAAC,GAAQ,KAAA;AACnD,MAAM,MAAA,UAAA,GAAa,cAAe,CAAA,OAAA,CAAQ,GAAG,CAAA;AAE7C,MAAI,IAAA,UAAA,CAAW,cAAc,CAAG,EAAA;AAC9B,QAAO,MAAA,CAAA,GAAG,IAAI,UAAW,CAAA,WAAA;AAAA;AAC3B,KACD,CAAA;AAED,IAAO,OAAA,MAAA;AAAA,GACN,EAAA,CAAC,KAAM,CAAA,IAAA,CAAK,MAAM,CAAC,CAAA;AAEtB,EAAM,MAAA,aAAA,GAAgB,QAAQ,MAAM;AAClC,IAAO,OAAA,KAAA,CAAM,KAAK,MAAO,CAAA,MAAA;AAAA,GACxB,EAAA,CAAC,KAAM,CAAA,IAAA,CAAK,MAAM,CAAC,CAAA;AAEtB,EAAM,MAAA,gBAAA,GAAmB,QAAQ,MAAM;AACrC,IAAA,OAAO,MAAM,IAAK,CAAA,MAAA,CAAO,MAA6C,CAAA,CAAC,KAAK,KAAU,KAAA;AA3P1F,MAAA,IAAAD,KAAAC,GAAA,EAAA,EAAA;AA4PM,MAAA,IAAA,CAAI,EAAAA,GAAAA,CAAAA,GAAAA,GAAAA,CAAAD,GAAA,GAAA,KAAA,CAAM,MAAN,KAAA,IAAA,GAAA,KAAA,CAAA,GAAAA,GAAc,CAAA,MAAA,KAAd,IAAAC,GAAAA,KAAAA,CAAAA,GAAAA,GAAAA,CAAsB,WAAtB,KAAA,IAAA,GAAA,KAAA,CAAA,GAAA,EAAA,CAAmC,IAAM,EAAA;AAC3C,QAAA,GAAA,CAAI,eAAe,KAAK,CAAC,IAAI,KAAM,CAAA,MAAA,CAAO,OAAO,WAAY,CAAA,IAAA;AAAA;AAE/D,MAAO,OAAA,GAAA;AAAA,KACT,EAAG,EAAE,CAAA;AAAA,GACJ,EAAA,CAAC,KAAM,CAAA,IAAA,CAAK,MAAM,CAAC,CAAA;AAGtB,EAAA,MAAM,UAAa,GAAA,OAAA;AAAA,IACjB,OAAO;AAAA,MACL,aAAA;AAAA,MACA,SAAA;AAAA,MACA,WAAA;AAAA,MACA,gBAAA;AAAA,MACA,cAAc,eAAgB;AAAA,KAChC,CAAA;AAAA,IACA,CAAC,SAAA,EAAW,WAAa,EAAA,eAAA,EAAiB,eAAe,gBAAgB;AAAA,GAC3E;AAGA,EAAM,MAAA,YAAA,GAAe,QAAQ,MAAM;AACjC,IAAM,MAAA,YAAA,GAAe,MAAO,CAAA,OAAA,CAAQ,MAAM,CAAA;AAC1C,IAAI,IAAA,YAAA,CAAa,WAAW,CAAG,EAAA;AAE7B,MAAA,gBAAA,CAAiB,UAAU,EAAC;AAC5B,MAAO,OAAA,IAAA;AAAA;AAIT,IAAM,MAAA,iBAAA,GAAoB,CAAC,GAAA,EAAe,GAAgB,KAAA;AACxD,MAAM,MAAA,KAAA,GAAQ,KAAM,CAAA,IAAA,CAAK,MAAO,CAAA,IAAA,CAAK,CAACC,MAAU,KAAA,cAAA,CAAeA,MAAK,CAAA,KAAM,GAAG,CAAA;AAC7E,MAAA,IAAI,CAAC,KAAA,IAAS,CAAC,KAAA,CAAM,OAAS,EAAA;AAC5B,QAAO,OAAA,EAAA;AAAA;AAET,MAAA,MAAM,iBAAiB,sBAAuB,CAAA,KAAA,CAAM,QAAQ,GAAI,CAAA,GAAG,CAAC,CAAC,CAAA;AACrE,MAAO,OAAA,cAAA;AAAA,KACT;AAGA,IAAM,MAAA,UAAA,GAAa,IAAI,GAAA,CAAI,YAAa,CAAA,GAAA,CAAI,CAAC,CAAC,GAAG,CAAM,KAAA,GAAG,CAAC,CAAA;AAC3D,IAAW,UAAA,CAAA,OAAA,CAAQ,CAAC,GAAQ,KAAA;AAC1B,MAAA,IAAI,CAAC,gBAAA,CAAiB,OAAQ,CAAA,QAAA,CAAS,GAAG,CAAG,EAAA;AAG3C,QAAiB,gBAAA,CAAA,OAAA,CAAQ,KAAK,GAAG,CAAA;AAAA;AACnC,KACD,CAAA;AAED,IAAiB,gBAAA,CAAA,OAAA,GAAU,iBAAiB,OAAQ,CAAA,MAAA,CAAO,CAAC,GAAQ,KAAA,UAAA,CAAW,GAAI,CAAA,GAAG,CAAC,CAAA;AAGvF,IAAA,eAAA,CAAgB,UAAU,EAAC;AAG3B,IAAA,IAAI,aAAe,EAAA;AACjB,MAAO,OAAA,sBAAA;AAAA,QAAuB,IAAA;AAAA,QAAM,CAAC,OAAA,KACnC,OAAQ,CAAA,MAAA,CAAO,CAAC,GAAQ,KAAA;AACtB,UAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,YAAc,EAAA;AACvC,YAAM,MAAA,cAAA,GAAiB,iBAAkB,CAAA,GAAA,EAAK,GAAG,CAAA;AACjD,YAAA,IAAI,CAAC,KAAA,CAAM,WAAY,CAAA,GAAA,CAAI,cAAc,CAAG,EAAA;AAC1C,cAAO,OAAA,KAAA;AAAA;AAGT,YAAA,IAAI,CAAC,eAAA,CAAgB,OAAQ,CAAA,GAAG,CAAG,EAAA;AACjC,cAAA,eAAA,CAAgB,OAAQ,CAAA,GAAG,CAAI,GAAA,CAAC,GAAG,CAAA;AAAA,aAC9B,MAAA;AACL,cAAA,eAAA,CAAgB,OAAQ,CAAA,GAAG,CAAE,CAAA,IAAA,CAAK,GAAG,CAAA;AAAA;AACvC;AAEF,UAAO,OAAA,IAAA;AAAA,SACR;AAAA,OACH;AAAA;AAIF,IAAO,OAAA,IAAA,CAAK,MAAO,CAAA,CAAC,GAAQ,KAAA;AAC1B,MAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,CAAA,IAAK,YAAc,EAAA;AACvC,QAAM,MAAA,cAAA,GAAiB,iBAAkB,CAAA,GAAA,EAAK,GAAG,CAAA;AACjD,QAAA,IAAI,CAAC,KAAA,CAAM,WAAY,CAAA,GAAA,CAAI,cAAc,CAAG,EAAA;AAC1C,UAAO,OAAA,KAAA;AAAA;AAGT,QAAA,IAAI,CAAC,eAAA,CAAgB,OAAQ,CAAA,GAAG,CAAG,EAAA;AACjC,UAAA,eAAA,CAAgB,OAAQ,CAAA,GAAG,CAAI,GAAA,CAAC,GAAG,CAAA;AAAA,SAC9B,MAAA;AACL,UAAA,eAAA,CAAgB,OAAQ,CAAA,GAAG,CAAE,CAAA,IAAA,CAAK,GAAG,CAAA;AAAA;AACvC;AAEF,MAAO,OAAA,IAAA;AAAA,KACR,CAAA;AAAA,GACH,EAAG,CAAC,IAAM,EAAA,MAAA,EAAQ,eAAe,KAAM,CAAA,IAAA,CAAK,MAAM,CAAC,CAAA;AAGnD,EAAM,MAAA,UAAA,GAAa,QAAQ,MAAM;AAC/B,IAAI,IAAA,WAAA,CAAY,WAAW,CAAG,EAAA;AAC5B,MAAO,OAAA,YAAA;AAAA;AAIT,IAAM,MAAA,WAAA,GAAc,CAAC,CAAA,EAAa,CAAwB,KAAA;AACxD,MAAA,IAAI,MAAS,GAAA,CAAA;AACb,MAAA,KAAA,IAAS,CAAI,GAAA,CAAA,EAAG,CAAI,GAAA,WAAA,CAAY,QAAQ,CAAK,EAAA,EAAA;AAC3C,QAAA,MAAM,EAAE,SAAA,EAAW,SAAU,EAAA,GAAI,YAAY,CAAC,CAAA;AAC9C,QAAA,MAAM,OAAU,GAAA,aAAA,CAAc,WAAY,CAAA,SAAS,CAAC,CAAA;AACpD,QAAM,MAAA,OAAA,GAAU,SAAc,KAAA,KAAA,GAAQ,CAAI,GAAA,CAAA,CAAA;AAE1C,QAAA,MAAA,GAAS,UAAU,OAAQ,CAAA,CAAA,CAAE,SAAS,CAAG,EAAA,CAAA,CAAE,SAAS,CAAC,CAAA;AACrD,QAAA,IAAI,WAAW,CAAG,EAAA;AAChB,UAAA;AAAA;AACF;AAEF,MAAO,OAAA,MAAA;AAAA,KACT;AAGA,IAAA,IAAI,aAAe,EAAA;AACjB,MAAO,OAAA,sBAAA,CAAuB,YAAc,EAAA,CAAC,OAAY,KAAA,CAAC,GAAG,OAAO,CAAA,CAAE,IAAK,CAAA,WAAW,CAAC,CAAA;AAAA;AAIzF,IAAO,OAAA,YAAA,CAAa,KAAM,EAAA,CAAE,IAAK,CAAA,CAAC,GAAG,CAAM,KAAA,WAAA,CAAY,CAAG,EAAA,CAAC,CAAC,CAAA;AAAA,KAC3D,CAAC,YAAA,EAAc,WAAa,EAAA,WAAA,EAAa,aAAa,CAAC,CAAA;AAI1D,EAAA,MAAM,UAAU,UAAW,CAAA,MAAA;AAE3B,EAAA,IAAI,mBAAmB,KAAM,CAAA,eAAA;AAC7B,EAAA,IAAI,QAAU,EAAA;AACZ,IAAmB,gBAAA,GAAA,CAAA;AAAA,GACrB,MAAA,IAAW,CAAC,QAAY,IAAA,MAAA,CAAO,KAAK,cAAe,CAAA,OAAO,CAAE,CAAA,MAAA,GAAS,CAAG,EAAA;AACtE,IAAmB,gBAAA,GAAA,cAAA,CAAe,OAAQ,CAAA,MAAA,CAAO,IAAK,CAAA,cAAA,CAAe,OAAO,CAAA,CAAE,CAAC,CAAC,CAAE,CAAA,qBAAA,EAAwB,CAAA,MAAA;AAAA;AAE5G,EAAA,IAAI,cAAc,IAAK,CAAA,KAAA;AAAA,IAAA,CACpB,MAAS,GAAA,gBAAA,GAAmB,KAAM,CAAA,gBAAA,GAAmB,mBAAmB,kBAAsB,IAAA;AAAA,GACjG;AAEA,EAAA,IAAI,eAAiB,EAAA;AACnB,IAAe,WAAA,IAAA,CAAA;AAAA;AAEjB,EAAA,IAAI,cAAc,CAAG,EAAA;AAEnB,IAAc,WAAA,GAAA,CAAA;AAAA;AAEhB,EAAA,MAAM,aAAgB,GAAA,IAAA,CAAK,IAAK,CAAA,OAAA,GAAU,WAAW,CAAA;AACrD,EAAA,IAAI,OAAO,aAAe,EAAA;AAExB,IAAA,OAAA,CAAQ,gBAAgB,CAAC,CAAA;AAAA;AAG3B,EAAM,MAAA,eAAA,GAAkB,OAAO,WAAc,GAAA,CAAA;AAC7C,EAAI,IAAA,YAAA,GAAe,kBAAkB,WAAc,GAAA,CAAA;AACnD,EAAA,IAAI,eAAe,OAAS,EAAA;AAC1B,IAAe,YAAA,GAAA,OAAA;AAAA;AAEjB,EAAM,MAAA,eAAA,GAAkB,QAAQ,KAAM,CAAA,gBAAA;AAEtC,EAAM,MAAA,aAAA,GAAgB,QAAQ,MAAM;AAClC,IAAA,MAAM,aAAa,IAAO,GAAA,WAAA;AAC1B,IAAA,OAAO,UAAW,CAAA,KAAA,CAAM,UAAY,EAAA,UAAA,GAAa,WAAW,CAAA;AAAA,KAC3D,CAAC,IAAA,EAAM,UAAY,EAAA,IAAA,EAAM,WAAW,CAAC,CAAA;AAExC,EAAA,OAAA,CAAQ,MAAM;AACZ,IAAA,QAAA,CAAS,UAAU,KAAM,CAAA,IAAA,CAAK,OAAO,GAAI,CAAA,CAAC,OAAO,KAAU,KAAA;AA/Z/D,MAAA,IAAAF,KAAAC,GAAA,EAAA,EAAA;AAgaM,MAAA,IAAA,CAAID,GAAA,GAAA,KAAA,CAAM,KAAN,KAAA,IAAA,GAAA,KAAA,CAAA,GAAAA,IAAa,KAAO,EAAA;AACtB,QAAA,CAAAC,GAAO,GAAA,KAAA,CAAM,KAAb,KAAA,IAAA,GAAA,IAAA,GAAA,OAAAA,GAAoB,CAAA,KAAA;AAAA;AAEtB,MAAA,IAAI,cAAgB,EAAA;AAClB,QAAA,OAAO,KAAU,KAAA,CAAA,GAAI,CAAG,EAAA,UAAA,CAAW,MAAM,CAAK,CAAA,GAAA,EAAA;AAAA;AAEhD,MAAA,IAAI,UAAU,CAAG,EAAA;AACf,QAAM,MAAA,iBAAA,GAAA,CAAoB,EAAe,GAAA,aAAA,IAAA,IAAA,GAAA,KAAA,CAAA,GAAA,aAAA,CAAA,OAAA,KAAf,IAAyB,GAAA,KAAA,CAAA,GAAA,EAAA,CAAA,CAAA,CAAA;AACnD,QAAA,OAAO,iBAAoB,GAAA,aAAA,CAAc,GAAI,CAAA,iBAAiB,EAAE,IAAO,GAAA,EAAA;AAAA;AAEzE,MAAO,OAAA,eAAA,CAAgB,UAAY,EAAA,KAAA,EAAO,aAAa,CAAA;AAAA,KACxD,CAAA;AAAA,GACH,EAAG,CAAC,UAAY,EAAA,KAAA,CAAM,KAAK,MAAQ,EAAA,aAAA,EAAe,cAAc,CAAC,CAAA;AAEjE,EAAM,MAAA,YAAA,GAAe,CAAC,MAAmB,KAAA;AACvC,IAAA,IAAI,CAAC,YAAA,CAAa,QAAS,CAAA,MAAM,CAAG,EAAA;AAClC,MAAA,eAAA,CAAgB,CAAC,GAAG,YAAc,EAAA,MAAM,CAAC,CAAA;AAAA,KACpC,MAAA;AACL,MAAA,eAAA,CAAgB,aAAa,MAAO,CAAA,CAAC,EAAO,KAAA,EAAA,KAAO,MAAM,CAAC,CAAA;AAAA;AAE5D,IAAiB,gBAAA,CAAA,CAAC,IAAS,KAAA,IAAA,GAAO,CAAC,CAAA;AAAA,GACrC;AAEA,EAAA,MAAM,EAAE,GAAA,EAAK,YAAa,EAAA,GAAI,QAAQ,MAAM;AAC1C,IAAM,MAAA,IAAA,GAAO,GAAG,KAAM,CAAA,UAAA,CAAW,QAAQ,CAAM,GAAA,EAAA,KAAA,CAAM,WAAW,UAAU,CAAA,CAAA;AAC1E,IAAM,MAAA,MAAA,GAAS,QAAS,CAAA,aAAA,CAAc,QAAQ,CAAA;AAC9C,IAAME,MAAAA,IAAAA,GAAM,MAAO,CAAA,UAAA,CAAW,IAAI,CAAA;AAElC,IAAA,MAAM,aAAgB,GAAA,IAAA;AAEtB,IAAAA,IAAAA,CAAI,aAAgB,GAAA,CAAA,EAAG,aAAa,CAAA,EAAA,CAAA;AACpC,IAAAA,KAAI,IAAO,GAAA,IAAA;AACX,IAAA,IAAI,GACF,GAAA,yJAAA;AACF,IAAA,MAAM,QAAWA,GAAAA,IAAAA,CAAI,WAAY,CAAA,GAAG,CAAE,CAAA,KAAA;AACtC,IAAMC,