import type { GridOptions } from '../types/grid.types'; import type { ColumnDef } from '../types/column.types'; import type { GridStore } from '../core/grid-store'; import type { EventBus } from '../event-bus/event-bus'; import type { ColumnModel } from '../core/column-model'; import type { PaginationEngine } from '../engines/pagination/pagination-engine'; import type { IconRenderer } from '../icons/icon-renderer'; import type { SortEngine } from '../engines/sort/sort-engine'; import type { RowSelectionEngine } from '../engines/selection/row-selection-engine'; import type { GroupingEngine } from '../engines/grouping/grouping-engine'; import type { FilterEngine } from '../engines/filter/filter-engine'; import type { ColumnGroupModel } from '../column-groups/column-group-model'; import type { ColumnGroupHeaderBuilder } from '../column-groups/column-group-header-builder'; import type { DisplayGroupEngine } from '../column-groups/display-group-engine'; import type { VDomStats } from './vdom/vdom.types'; import type { LoadingOverlayConfig, ResolvedLoadingOverlayConfig } from '../types/loading.types'; import { ColumnStyleManager } from './column-style-manager'; import type { SummaryModel } from '../summary/summary-model'; import { GridResizeController } from './grid-resize-controller'; import { CellSelectionEngine } from '../cell-selection/cell-selection-engine'; import type { ExportFormat } from '../export/export.types'; import type { PhotonThemeApi } from '../types/theme-ai.types'; import { ImportSourceType } from '../types/import.types'; import type { PluginLayerOptions, RenderWindow, ScrollMetrics } from '../plugins/plugin.types'; /** * The slice of `PluginHost` the renderer calls back into. * * Declared structurally rather than importing the class so the renderer keeps no * runtime dependency on the plugin subsystem — a grid without plugins never * touches it. */ export interface PluginHostSeam { wantsRenderWindow(): boolean; dispatchRenderWindow(window: RenderWindow): void; } import type { MasterDetailEngine } from '../engines/master-detail/master-detail-engine'; import type { TreeExpansionService } from '../engines/tree/tree-expansion-service'; import type { ThemeManager } from '../theme/theme-manager'; import { type NestedGridFactory } from './detail-row-renderer'; import type { DetailComponent } from '../types/detail-component.types'; import type { PhotonCommandResult } from '../photon-ai/photon-ai.types'; export declare class GridRenderer { private containerEl; private store; private eventBus; private columnModel; private paginationEngine; private iconRenderer; private cellSelectionEngine; private sortEngine; private rowSelectionEngine; private groupingEngine; private options; private wrapperEl; private leftHeaderPanelEl; private centerHeaderInnerEl; private rightHeaderPanelEl; private leftBodyPanelEl; private centerBodyEl; private centerBodyContentEl; private rightBodyPanelEl; private leftBodyContentEl; private rightBodyContentEl; private footerContainerEl; private bodyWrapEl; /** * Container resizing — owns the edge/corner handles and is the single place * the container's width/height are written, including by the `GridApi` size * methods. Always constructed; inert (no handles mounted) unless * `GridOptions.resize` enables it. */ private readonly gridResize; /** * Definition + value store for summary rows. `null` until `setSummaryModel`, * and left `null` for grids that never define any, so the whole feature costs * one null check per frame when unused. */ private summaryModel; /** * The four possible bands, keyed `${position}:${sticky}`. Created lazily on * first use — a grid with only a bottom total allocates one, not four. */ private readonly summaryBands; /** Flex column the sticky bands are inserted into (between header and body / body and h-scrollbar). */ private summaryHostEl; /** Absolutely-positioned layer inside the body that hosts the non-sticky (in-content) bands. */ private summaryLayerEl; /** * Scroll height reserved by the non-sticky bands, split by edge. * * Non-sticky bands occupy real scroll space rather than overlaying rows: the * top band's height shifts every data row down, and both extend the total * scrollable height. Cached because `performRender` needs them *before* the * row window is sliced, which is earlier than the bands themselves render. */ private summaryInlineTopH; private summaryInlineBottomH; /** Previous reserved total, so a summary height change re-runs the scroll sizing that is otherwise keyed off the rows array. */ private _lastSummaryReservedH; private leftStickyRowEl; private centerStickyRowEl; private rightStickyRowEl; private readonly masterDetailEnabledAtConstruction; private readonly treeDataEnabledAtConstruction; /** `nodeId` of the currently-stuck master row, or `null` when none is sticky. */ private stickyNodeId; /** * Last value written to the `--pg-sticky-block-height` CSS variable, so the * per-frame write is skipped while the sticky band's height is unchanged * (the common case on most scroll frames). */ private _lastStickyBlockHeight; private readonly stickyRowTracker; private readonly treeStickyRowTracker; /** Exposed for {@link DisplayGroupEngine} construction in `GridCore`. */ readonly colStyles: ColumnStyleManager; /** * FLIP animator for structural column changes (hide/show/reorder). Owned * here rather than by `HeaderRenderer` because the animation spans the header * *and* every body cell, and this class is the only one that sees both sides * of a rebuild. */ private readonly columnAnimator; /** Column offsets as of the last committed render — the "before" frame every column FLIP inverts against. */ private lastColumnPositions; /** Layout of the last `columns` store value, used to classify the next change. Seeded empty so the first render never animates. */ private lastColumnLayout; private rowPositionSheet; private scrollController; private headerRenderer; /** Lazily-opened "Choose Columns" dialog. Created once, reused across opens. */ private columnChooser; /** Tools-strip launcher that opens {@link columnChooser} — only when `columnsManager` is enabled. */ private columnsManagerLauncher; private bodyRenderer; private footerRenderer; private overlayRenderer; private groupDropZone; private rowDragRenderer; private treeDragConfig; private detailRowRenderer; private masterDetailEngine; private treeExpansionService; private treeToggleColumnId; /** Floating Photon AI command bar — only created when `photonAI.enabled`. */ private photonAIPanel; /** Floating Filters Tool Panel — only created when `filtersToolPanel.enabled`. */ private filtersToolPanel; /** Floating Import menu (launcher + dropdown) — only created when `import.enabled`. */ private importMenu; /** Floating Export menu (launcher + dropdown) — only created when `export.enabled`. */ private exportMenu; /** Runs an export for a format chosen in the Export menu. Wired by GridCore. */ private exportFormatHandler; /** Reports whether a format has a registered exporter. Wired by GridCore. */ private exportAvailabilityFn; /** Configurable top toolbar (tabs + global search) — only created when `toolbar.enabled`. */ private toolbar; /** Top-right Theme Manager launcher — only created when `themeManager` is enabled. */ private themeManagerPanel; /** Lazily resolves the theme API for the Theme Manager (engine exists after this renderer). */ private themeApiProvider; private themeToastProvider; /** Whether the Theme Manager launcher should be mounted. */ private themeManagerEnabled; /** * Shared tools strip (`.pg-grid__tools`) — a dedicated toolbar row above the * header hosting every top-right launcher (Filters funnel, Import, …) so they * sit side-by-side instead of stacking. Created lazily on first use via * {@link getOrCreateToolsBar}; null when no launcher-based feature is enabled. */ private toolsBarEl; /** Left region of the tools strip (toolbar tabs + left-docked search). */ private toolsLeftEl; /** Right region of the tools strip (right-docked search + Filters/Import launchers). */ private toolsRightEl; /** Quick-filter seam shared by the group-bar search and the toolbar search. Wired by GridCore. */ private searchCallback; /** Host handler run when a file-based import source is chosen. Wired by GridCore. */ private importFileHandler; /** Host handler run when *Paste From Clipboard* is chosen. Wired by GridCore. */ private importClipboardHandler; /** Shows a custom floating tooltip for columns with `renderer.tooltip`; a no-op for every other column. */ private tooltipController; private rafId; /** * Whether a same-frame repaint is already queued for the animation frame in * progress. See {@link onScrollRepaint}. */ private inlineRenderQueued; private autoScroller; private unsubscribers; private headerRendered; private lastCenterColStart; private lastCenterColEnd; private rowAnimator; /** * The exact options passed to the last `BodyRenderer.renderRows` call. * * A Virtual DOM patch must format a cell the same way the render did, so it * replays this snapshot rather than rebuilding an equivalent one — there is * no second source of truth to drift. */ private lastBodyOptions; /** * The row window `[start, end)` the body DOM actually holds — recorded by the * last frame that painted rows. * * Read back on frames that deliberately skip `renderRows` (a live column * resize), so every row-geometry write still describes the window on screen * rather than one only this frame's arithmetic knows about. */ private lastPaintedWindow; /** * The owning grid's public `GridApi`, once it exists. * * Late-bound: the API is constructed after the renderer, so this is `null` * until {@link setParentApiForDetail} runs. Handed to every cell renderer as * `params.api` / `ctx.api`, which is what a renderer reads the grid's shared * `context` through. * * Typed as `unknown` to avoid a renderer → api import cycle. */ private gridApi; /** Batches patch requests into one flush per animation frame. */ private readonly patchScheduler; /** Cells written by the in-progress flush, reported by `flushCellPatches`. */ private lastPatchedCells; /** * Set when the active row-model strategy guarantees every row is exactly * `rowHeight` tall. See `RowModelStrategy.uniformRowHeight`. */ private uniformRowHeight; /** * Notified with the row range being painted, so a demand-loading row model * can fetch exactly what is on screen. See `RowModelStrategy.onRenderWindow`. */ private renderWindowCallback; /** * Whether the grid may rewrite row order itself on a row drop. * * Defaults to the active row model's `rowOrderIsClientOwned` and can be * overridden down (never up) by `GridOptions.rowDrag.managed`. When `false` * the drag still runs — only the commit is the application's job. */ private rowReorderManaged; private columnGroupModel; private groupHeaderBuilder; private groupDragHandler; /** New Display Group Engine — takes priority over the legacy ColumnGroupModel when set. */ private displayGroupEngine; private filterEngine; private filterRefreshFn; private activeFilterPanel; /** Last `columns` array reference seen — guards column-width recomputation. */ private _lastColumnsRef; /** Last `groupedColumnIds` array reference seen — guards grouping recomputation. */ private _lastGroupedIdsRef; /** Last `visibleRows` array reference seen — guards total-height recomputation. */ private _lastRowsRef; /** Cached total content height in pixels (sum of all visible row heights). */ private _cachedTotalHeight; /** * Height of the data rows alone, excluding the scroll space reserved by * non-sticky summary bands. Needed separately from {@link _cachedTotalHeight} * to place the bottom in-content band, which sits exactly after the last row. */ private _cachedRowsHeight; /** Cached center-panel content width in pixels. */ private _cachedCenterW; /** * Center-panel `clientWidth` last used to resolve `flex` columns. Flex * widths are normally only re-resolved when the columns array itself * changes (cheap, guards the 60fps scroll path) — but the container can * also resize with the columns reference untouched, e.g. a vertical * scrollbar transiently appearing/disappearing as a Master/Detail row is * inserted. Comparing against this on every render (a single cheap * `clientWidth` read, only paid by grids that actually use `flex` columns) * catches that case so flex columns don't get stuck sized for a stale width. */ private _lastFlexResolvedWidth; /** * Host-supplied loading overlay configuration, kept unresolved so a partial * runtime update via {@link setLoadingOverlayConfig} merges onto what the * host originally asked for rather than onto filled-in defaults. */ private loadingOverlaySource; /** * {@link loadingOverlaySource} with every default applied. Resolved once here * (and again only on an explicit update) so the render path never re-merges. */ private loadingOverlayConfig; constructor(containerEl: HTMLElement, store: GridStore, eventBus: EventBus, columnModel: ColumnModel, paginationEngine: PaginationEngine, iconRenderer: IconRenderer, cellSelectionEngine: CellSelectionEngine, sortEngine: SortEngine, rowSelectionEngine: RowSelectionEngine, groupingEngine: GroupingEngine, options: GridOptions); /** * Enables Tree Data drag-to-reparent on the row-drag system. Must be * called before `mount()` (mirrors `setMasterDetailConfig`) — `mount()` * is when `RowDragRenderer` is actually constructed. */ setTreeDragConfig(active: boolean, reparentHandler: (draggedId: string, targetId: string, position: 'before' | 'after' | 'inside') => boolean): void; /** Wires Tree Data's expansion state + toggle column into the body renderer, so `data-level` indentation and the expand/collapse toggle render on the configured column. A no-op (undefined `treeData` on every `renderRows` call) until this is called. */ setTreeRenderConfig(toggleColumnId: string | undefined, expansionService: TreeExpansionService): void; mount(): void; scheduleRender(): void; /** * Repaints in response to a scroll, on the frame that scroll will be painted * on. * * A wheel or momentum glide publishes its offsets from inside an animation * frame. Deferring the repaint with `requestAnimationFrame` from there would * book the *next* frame, so the rendered row/column window would trail the * panel translate by one frame for the whole glide — briefly exposing * unfilled space past the virtualization buffer on a fast spin. * * The repaint is deferred to a **microtask** rather than run inline, so a * frame that moves both axes (a momentum flick, a diagonal glide) still * renders once instead of twice. Microtasks drain before the browser's * rendering steps, so this is still the same frame — just after both writes * have landed. * * A bound field so both scroll subscriptions share one function reference. */ private readonly onScrollRepaint; /** Renders the repaint queued by {@link onScrollRepaint} for the current frame. */ private readonly flushInlineRender; forceRender(): void; /** * Clears the body-renderer's row cache so the next render fully rebuilds every * visible row from the data model. Use this after in-place data mutations * (paste, cut) where the `visibleRows` reference is unchanged but cell values * have been updated — `updatePanelRow` only refreshes row-level classes, not * cell content, so a cache invalidation + re-render is required. */ invalidateBodyRows(): void; /** * Evicts only the rows with the given node IDs from the render cache and * schedules a repaint. All other rows keep their cached DOM elements so * custom cell renderers (images, flags, etc.) are not needlessly re-executed. * * Prefer this over `invalidateBodyRows` whenever the set of mutated rows is * known (fill, cut, paste, undo/redo). * * @param nodeIds - Node IDs of the rows whose cache entries should be evicted. */ invalidateBodyRowsByIds(nodeIds: Set): void; /** * Wires the renderer to the active row-model strategy. * * Three things flow across this seam: whether rows are uniformly tall (which * lets the total content height be computed rather than summed), a callback * reporting the painted row range (which lets a demand-loading model fetch * exactly what is on screen without re-implementing virtualisation), and * whether row order may be rewritten client-side. * * @param uniformRowHeight - `true` when every row is `GridOptions.rowHeight`. * @param onRenderWindow - Called after each render with the painted range. * @param rowOrderIsClientOwned - `true` when the grid may commit a row reorder * itself; see `RowModelStrategy`. */ setRowModelIntegration(uniformRowHeight: boolean, onRenderWindow: ((startRow: number, endRow: number) => void) | null, rowOrderIsClientOwned?: boolean): void; /** * Reconciles `GridOptions.rowDrag.managed` with what the row model can * actually deliver. * * The option may only turn managed reordering *off*. Asking for it on under a * server-backed model cannot be honoured — the grid would rewrite an array the * datasource re-supplies on the next fetch — so it is refused with an * explanation rather than silently half-working. */ private resolveManagedReorder; /** * Queues a Virtual DOM diff for the given rows, coalesced to one flush per * animation frame. * * This is the real-time update path: it never re-runs the row pipeline, never * rebuilds a row, and writes only the cells whose values actually changed. A * feed pushing thousands of updates per second therefore produces at most one * batched DOM write per frame. * * @param nodeIds - Rows whose data changed, or `null` to re-diff every * rendered row. */ patchCells(nodeIds: Iterable | null): void; /** * Applies any queued cell patches immediately instead of on the next frame. * * @returns The number of cells written to the DOM by the flush. */ flushCellPatches(): number; /** `true` when the given row is rendered and can be patched in place. */ isRowRendered(nodeId: string): boolean; /** Virtual DOM counters — see {@link VDomStats}. */ getVDomStats(): VDomStats; /** Zeroes the Virtual DOM counters. */ resetVDomStats(): void; /** * Runs one Virtual DOM flush. * * Reuses the exact `BodyRendererOptions` from the last paint so a patched * cell is formatted identically to a rendered one. A patch before the first * render is a no-op — there is no DOM to patch yet. */ private readonly runCellPatch; /** * Provides the renderer with a `FilterEngine` reference so it can read the * current filter model and write column filters when the user interacts with * the filter panel. Called from `GridApi` after construction. */ setFilterEngine(engine: FilterEngine): void; /** * Registers a callback that runs the full sort/filter pipeline and triggers * a render whenever the filter state changes from within the panel. * Called from `GridApi` after construction. */ setFilterRefreshCallback(fn: () => void): void; /** * Wire the column-group model and header builder into the renderer. * * Must be called **before** the first `mount()` so that `renderInPanels` * can insert group header rows above the leaf row. Called by `GridCore` * when any top-level `ColumnDef` has a `children` array. * * @param model - The live tree model. * @param builder - The DOM builder instance. */ setColumnGroupModel(model: ColumnGroupModel, builder: ColumnGroupHeaderBuilder): void; /** * Wire the new Display Group Engine into the renderer. * * Creates the drag handler, forwards the engine into `HeaderRenderer`, and * subscribes to the events that trigger header rebuilds. Must be called * before `mount()` when the grid's column definitions contain groups. * * Takes priority over the legacy `setColumnGroupModel` path. * * @param engine - Fully-initialised `DisplayGroupEngine` instance. */ setDisplayGroupEngine(engine: DisplayGroupEngine): void; /** * Opens (or replaces) the floating filter panel for the given column. * Called by `HeaderRenderer` when the user clicks a column's filter icon. * * @param colDef - Column definition the filter applies to. * @param anchorEl - Filter-icon button element — panel positions below this. */ openFilterPanel(colDef: ColumnDef, anchorEl: HTMLElement): void; /** Opens the Filters Tool Panel, if the feature is enabled. No-op otherwise. */ openFiltersToolPanel(): void; /** Closes the Filters Tool Panel, if the feature is enabled. No-op otherwise. */ closeFiltersToolPanel(): void; /** Toggles the Filters Tool Panel open/closed, if the feature is enabled. No-op otherwise. */ toggleFiltersToolPanel(): void; /** * Wires the host handlers the Import menu invokes when a source is chosen. * Called by {@link import('../core/grid-core').GridCore} once the live * {@link import('../core/grid-api').GridApi} exists — the menu itself carries * no import logic. * * @param onFile - Runs an import for a picked file + inferred source. * @param onClipboard - Runs a clipboard import. */ setImportHandlers(onFile: (source: ImportSourceType, file: File) => void, onClipboard: () => void): void; /** * Wires the host handlers the Export menu invokes. * * Called by {@link import('../core/grid-core').GridCore} once the live * {@link import('../core/grid-api').GridApi} exists. Kept as callbacks rather * than an `ExportService` reference so this renderer stays free of the export * pipeline entirely — the same seam the Import menu uses. * * @param onSelectFormat - Runs the export for a chosen format. * @param isAvailable - Whether an exporter is registered for a format; * drives only the dropdown's "Setup" hint. */ setExportHandlers(onSelectFormat: (format: ExportFormat) => void, isAvailable: (format: ExportFormat) => boolean): void; /** Opens the Export dropdown, if the feature is enabled. No-op otherwise. */ openExportMenu(): void; /** Toggles the Export dropdown open/closed, if the feature is enabled. No-op otherwise. */ toggleExportMenu(): void; /** * Applies a live, per-column text filter from an inline filter-row input. * * Builds a single `contains` condition against the column's field so typing * substring-matches every column type (numbers/dates are matched on their * string form, mirroring the quick-filter behaviour). An empty term removes * the column's filter entirely. Reuses the same {@link FilterEngine} pathway * as the filter panel so both entry points stay consistent, then re-runs the * data pipeline via {@link filterRefreshFn}. * * @param colDef - Column whose filter is being edited. * @param term - Current input value; empty/whitespace clears the filter. */ private applyInlineTextFilter; /** * Extracts unique display value/label pairs for set-type (dropdown / array) * filter panels. For `dropdown` columns the predefined `dropdownOptions` * are used directly; for other types unique values are scanned from `allRows`. * * Columns whose renderer transforms its value (a `country` column showing * "United States" for a stored `"US"`) are collected by that **displayed * text**, not by the raw value: the list then reads the way the column does, * and `FilterEngine` compares against the same text so ticking a box matches * exactly the rows the user can see. Collecting by display text also collapses * the rows where one country arrived as `"US"`, `"USA"` and `"United States"` * into the single entry a user expects, instead of three that each hide part * of the answer. */ private extractUniqueOptions; /** * Snapshot current row positions so the next render animates the transition. * Call this **before** any pipeline that reorders or hides rows. * * No-ops when row animations are disabled via `GridOptions.animateRows === false` * (or {@link setRowAnimationEnabled}); with no snapshot captured, the next * render simply skips the animation. * * @param rows - Current visible rows before the pipeline runs. * @param type - `'sort'` (default), `'filter'` or `'detail'` — controls duration and entrance style. */ captureRowAnimation(rows: ReadonlyArray<{ nodeId: string; top: number; }>, type?: import('./row-animator').RowAnimationType): void; /** * Narrows an animation snapshot to the rows the renderer currently has DOM * for, before it reaches {@link RowAnimator.capture}. * * This is a memory guard, not the correctness guard — `RowAnimator` decides * what actually animates by testing each row's start and end position against * the viewport. The problem this solves is upstream of that: `visibleRows` is * the whole current page, and with the large `pageSize` values real grids use * (10k–50k, sometimes the entire dataset) `capture()` would build a Map with * one entry per page row on every sort keystroke, of which ~30 can possibly * matter. Slicing to the rendered window keeps that allocation proportional to * what is on screen, which is what makes sorting a million rows cost the same * as sorting a hundred. * * `firstRenderedRowIndex`/`lastRenderedRowIndex` are written at the end of * every `performRender`, so at capture time (before the pipeline re-runs) they * still describe the window the user is looking at, and they index into the * same pre-pipeline `visibleRows` array passed in here. */ private sliceAnimatableRows; /** * Enable or disable row animations at runtime, overriding the initial * `GridOptions.animateRows` value. Disabling clears any pending capture so an * in-flight transition does not play on the next render. * * @param enabled - `true` to animate row reorders/appearance, `false` to disable. */ setRowAnimationEnabled(enabled: boolean): void; /** Wire up the group-bar search input to an external handler (e.g. api.setQuickFilter). */ setSearchCallback(fn: (term: string) => void): void; /** * Enable the top-right Theme Manager launcher and wire the (lazy) theme API * provider. Must be called before `mount()` so the launcher is built with the * tools strip. * * @param getThemeApi - Lazily resolves the live theme API. * @param enabled - Whether the launcher should be built. * @param getToasts - Lazily resolves the grid's toast service, used to * surface action feedback (import/export/reset) as transient toasts. */ /** Set by `GridCore` when at least one plugin is registered; `null` otherwise. */ private pluginHost; /** Layers handed out by {@link mountPluginLayer}, keyed by name for idempotency. */ private pluginLayers; /** * Extra horizontal content width contributed by a plugin layer. * * The centre panel derives its scrollable width from its columns, but a * plugin can own horizontal content the grid knows nothing about -- a * scheduler timeline being the motivating case, where every resource column * is pinned left and the centre has no columns at all. Without this the * content width would be 0 and the timeline would have no scrollbar. */ private pluginContentWidth; /** Monotonic frame counter published on the render window. */ private pluginFrame; /** Last resolved left/right pinned panel widths, for the render window. */ private lastLeftPanelWidth; private lastRightPanelWidth; /** * Declares horizontal content width owned by a plugin layer. * * Combined with the column width by , so a plugin can only ever * widen the scrollable area, never shrink it below what the columns need. */ setPluginContentWidth(px: number): void; /** Attaches the plugin host. Must run before the first render. */ setPluginHost(host: PluginHostSeam): void; /** * Creates (or returns) a plugin-owned layer inside the grid body. * * Mounted as a **sibling of the pinned/centre panels**, the same position * Master/Detail uses for `.pg-detail-layer` — which is what lets the layer * span the full body while still sitting inside the scroll-transform * coordinate space. * * The optional `followRowOrigin` / `followScrollX` flags apply the same * transforms the grid's own panels use, so a layer that opts in needs no * scroll handling of its own: content positioned in rebased row space and * absolute content-x simply tracks the grid for free. */ mountPluginLayer(name: string, options?: PluginLayerOptions): HTMLElement; /** Current scroll/viewport geometry. Reads cached values only — forces no layout. */ readScrollMetrics(): ScrollMetrics; /** Subscribes to scroll on both axes. Returns a single disposer for the pair. */ addPluginScrollListener(cb: () => void): () => void; setThemeManager(getThemeApi: () => PhotonThemeApi, enabled: boolean, getToasts: () => import('../toast/toast-service').ToastService): void; /** * Selects a toolbar tab by id, if the toolbar feature is enabled. Emits * {@link import('../types/event.types').GridEventType.TOOLBAR_TAB_CHANGED} on * change. No-op when the toolbar is disabled or the id is unknown/disabled. */ setActiveToolbarTab(id: string): void; /** Returns the active toolbar tab id, or `null` when the toolbar is disabled or has no tabs. */ getActiveToolbarTab(): string | null; /** * Wires the Master/Detail engine and nested-grid factory into the renderer. * A no-op when `masterDetail.enabled` was falsy at construction (the * `DetailRowRenderer` instance was never created). Called once from * `GridCore.buildContext`, before `mount()`. */ setMasterDetailConfig(engine: MasterDetailEngine, nestedGridFactory: NestedGridFactory, iconRenderer: IconRenderer, themeManager: ThemeManager): void; /** * Late-bound once the owning `GridCore`'s `GridApi` exists. * * Feeds three things that all need the live API and none of which exist at * construction time: `masterDetail.detailRendererFn`'s `parentApi`, the * column menu's custom-item context, and — through {@link gridApi} — * `params.api` on every cell renderer. That last one is why a cell renderer * can reach `GridApi.getContext()` at all. */ setParentApiForDetail(api: unknown): void; /** The nested grid's `GridApi` for an expanded master row, or `undefined`. Backs `GridApi.getDetailGridApi`. */ getDetailGridApi(parentNodeId: string): unknown; /** The custom detail component mounted for an expanded master row, or `undefined`. Backs `GridApi.getDetailComponent`. */ getDetailComponent(parentNodeId: string): DetailComponent | undefined; /** Re-resolves props and refreshes an expanded master row's custom detail component. Backs `GridApi.refreshDetail`. */ refreshDetailComponent(parentNodeId: string): boolean; /** * Wires the callback the Photon AI panel's send button/Enter key invokes — * late-bound once the owning `GridCore`'s `GridApi` (and therefore its * `PhotonAIService`) exists. A no-op when `photonAI.enabled` was falsy at * construction (the panel was never created). */ setPhotonAISubmitHandler(fn: (text: string) => PhotonCommandResult): void; /** * Wires the async (generative provider) handler for the Photon AI panel. * When set, the panel streams the reply with a loading + typewriter effect * instead of rendering it synchronously. A no-op when the panel doesn't exist. */ setPhotonAIAsyncSubmitHandler(fn: (text: string, signal: AbortSignal) => Promise): void; /** Programmatic entry point mirroring the panel's own UI — backs `GridApi.submitAICommand`. */ submitAICommand(text: string): PhotonCommandResult; /** Async, streaming programmatic entry point — backs `GridApi.submitAICommandAsync`. Falls back to the sync path when no provider is configured. */ submitAICommandAsync(text: string): Promise; /** * Starts the shrink/fade-out animation for `parentNodeId`'s detail row. * Must be called synchronously **before** the pipeline re-runs and removes * the row — see `DetailRowRenderer.beginCollapse` for why the timing matters. */ beginDetailCollapse(parentNodeId: string): void; scrollToRow(rowIndex: number): void; /** * Scrolls the centre region to an absolute horizontal offset, in content * pixels. * * The column-oriented counterpart is `ensureColumnVisible`, which is the right * call when the target is a column. This one exists for content whose * horizontal extent is not columns at all -- a plugin timeline scrolling to a * date, for instance -- where the caller already knows the pixel it wants. */ scrollToX(px: number): void; scrollToTop(): void; /** Whether the body can still scroll further up. Used by a Master/Detail parent to chain wheel scroll into this grid before forwarding it further up itself. */ canScrollUp(): boolean; /** Whether the body can still scroll further down. */ canScrollDown(): boolean; /** * Scrolls the grid body (vertically and horizontally) so that the cell at * `rowIndex` / `colIndex` is fully visible — mirrors AG Grid's auto-scroll * behaviour on keyboard navigation. * * - For pinned-left/right columns only vertical scrolling is applied. * - For center columns both axes are adjusted when the cell is out of view. * * @param rowIndex - Index into `visibleRows` * @param colIndex - Index in the flat visible-columns array (left + center + right) */ scrollToCell(rowIndex: number, colIndex: number): void; getCellRect(rowIndex: number, colIndex: number): DOMRect | null; /** * Scrolls the body vertically so the row at `rowIndex` (index into * `visibleRows`) sits at the requested position. With no `position`, performs * the minimal scroll needed to bring the row fully into view (no-op if it * already is). The horizontal axis is left untouched — see * {@link ensureColumnVisible}. * * @param rowIndex - Index into the current `visibleRows`. * @param position - `'top' | 'middle' | 'bottom'`, or omit for minimal scroll. */ ensureRowVisible(rowIndex: number, position?: 'top' | 'middle' | 'bottom'): void; /** * Scrolls the body horizontally so the center column `colId` is fully * visible. Pinned columns are always on-screen, so this is a no-op for them. * * @param colId - Id of the column to reveal. */ ensureColumnVisible(colId: string): void; enterFullScreen(): void; exitFullScreen(): void; destroy(): void; /** * Re-hit-tests the serial cell under the given viewport point and extends the * active row drag-selection to it. Invoked by the auto-scroller after each * scrolled frame so a drag past the top/bottom edge keeps selecting rows. */ private extendRowDragAtPoint; private buildLayout; /** * Returns the shared tools strip (`.pg-grid__tools`), creating it once on * first use and inserting it as the first child of the grid wrapper so it * forms a dedicated toolbar row above the header. The strip is split into a * left region (toolbar tabs + left-docked search) and a right region * (right-docked search + the Filters/Import launchers). */ private getOrCreateToolsBar; /** Left region of the tools strip — creates the strip if needed. */ private getToolsLeftRegion; /** Right region of the tools strip — creates the strip if needed. */ private getToolsRightRegion; /** * Builds the top-level sticky-row layer and its three left/center/right * regions, mirroring the pinned-column layout via the same * `--pg-left-panel-width` / `--pg-right-panel-width` CSS vars the real * panels use — so a stuck row lines up pixel-for-pixel with the columns * it belongs to. The center region gets its own horizontal-scroll * transform so a stuck row's center cells track the user's horizontal * scroll exactly like the real (non-sticky) center panel does. */ private buildStickyLayer; /** * Supplies the summary definition/value store. * * Called once by `GridCore` during initialization. Until it is, and whenever * the model holds no rows, every summary code path in the render loop * short-circuits on a single null/empty check. */ setSummaryModel(model: SummaryModel): void; /** * The container-resize controller, so `GridApi` can route its size methods * through the same single write path the drag gesture uses. */ get resizeController(): GridResizeController; /** * Returns the band for one `(position, sticky)` pair, creating and mounting it * on first use. * * Lazy so a grid with a single bottom total never builds the other three * bands' scaffolding, and so a grid with no summary at all builds none. */ private getSummaryBand; /** * Creates (once) the absolutely-positioned layer that hosts non-sticky bands. * * Lives inside `.pg-grid__body` alongside the panels rather than inside one of * them: a band spans all three pinned regions, and a panel sets its own * `z-index`, which would trap the layer in that panel's stacking context — the * same reasoning that puts `.pg-sticky-layer` at this level. */ private ensureSummaryLayer; /** * Pairs each of a band's row definitions with its computed values. * * Rows whose snapshot is missing are dropped rather than rendered blank: the * only way that happens is a definition added since the last compute, and a * half-painted row would be worse than one that appears a frame later. */ private collectSummaryBandRows; /** * Recomputes the scroll height the non-sticky bands reserve. * * Must run before the row window is sliced: the top band's height offsets * every data row, so slicing against an unadjusted `scrollTop` would render * the wrong window. * * @returns `true` when either reservation changed, so the caller can re-run * the scroll sizing that is otherwise keyed off the rows array. */ private updateSummaryReservedHeights; /** * Renders every summary band for this frame. * * @param layout - The shared column layout, mirroring the header's. * @param rowsHeight - Total height of the data rows, for placing the bottom in-content band. * @param scrollTop - Current vertical scroll offset. * @param viewportH - Height of the body viewport. */ private renderSummaryBands; /** Detaches every summary band and its host layer. */ private destroySummaryBands; /** * `true` when any summary cell spans more than one column. * * Such a band renders every center column instead of the virtual window: a * span is a single element covering several columns, and the window's edge * could fall in the middle of one — leaving a cell sized for columns that are * not there, and every column after it misaligned. Rendering all of them keeps * the total center width identical (the spacers go to zero), so the band still * lines up with the header. * * The cost is bounded and opt-in: it applies only to grids that use `colSpan`, * and only to the handful of rows in a summary band — never to data rows. */ private summaryUsesColSpan; /** * The loading overlay configuration currently in force, with defaults applied. * * @returns The resolved configuration. Reached publicly through * `GridApi.getLoadingOverlayConfig()`. */ getLoadingOverlayConfig(): ResolvedLoadingOverlayConfig; /** * Replaces part of the loading overlay configuration at runtime — swapping * the spinner for skeleton placeholders mid-session, for example. * * The patch merges onto the host's *original* configuration, not onto the * resolved one, so omitted keys fall back to their documented defaults rather * than sticking at whatever a previous patch happened to resolve them to. * * Callers are responsible for scheduling a repaint; `GridApi` does this. * * @param config - Partial configuration to merge over the current one. */ setLoadingOverlayConfig(config: LoadingOverlayConfig): void; /** * Body geometry for the skeleton indicator: row height, the placeholder row * count that fills the viewport, and the visible column ids per panel. * * Forces no layout. The viewport height comes from `ScrollController`, which * maintains it from the `ResizeObserver` it already runs (reading * `clientHeight` here would be a synchronous layout on every loading frame), * and column *widths* are never read at all — the placeholder cells carry * `data-col-id` and pick their widths up from `ColumnStyleManager`'s * generated rules. * * The row count is bucketed here rather than in the overlay so a sub-row * container resize leaves the skeleton's cache signature unchanged. */ private buildLoadingGeometry; /** * Per-panel column offsets for the current layout, from `colStyles`' resolved * widths rather than the DOM — so a column FLIP forces no layout, and center * columns outside the virtual window are still positioned correctly. */ private captureColumnPositions; private performRender; /** * Commits a pure column permutation. * * Every panel still holds exactly the same columns, so no row's DOM needs to * be discarded: the header is rebuilt (stateless and cheap — no user cell * renderer, no in-flight image) while `BodyRenderer.renderRows` moves the * surviving cell elements into their new order. A sparkline keeps its canvas, * an `` keeps its decoded bitmap, an open editor keeps its focus — which * is exactly what a reorder should cost. * * The render is forced rather than scheduled. A drop removes the live * `--pg-drag-x` transforms synchronously, so deferring the commit to the next * animation frame would paint one frame of the *old* order in between — the * flash that reads as the column snapping back before jumping into place. */ private applyColumnReorder; private subscribeToStore; /** * Called when the user clicks a group collapse/expand toggle. * * When **collapsing**: hides all leaf columns except the first one (the "peek" * column) so the group header continues to show meaningful data. * When **expanding**: restores all leaf columns to visible. * * `setColumnVisible` fires `COLUMNS_STATE_CHANGED` → full rebuild. */ private handleGroupToggle; /** * Called when the user drags a group resize handle. * Distributes the new width proportionally among all visible leaf columns. */ private handleGroupResize; /** * Re-wires column-group references back into `HeaderRenderer` after * `headerRenderer.destroy()` has cleared them. */ private rewireGroupModelIntoHeaderRenderer; /** * Full header rebuild — clears inner HTML and resets the rendered flag so * the next `performRender` call re-runs `renderInPanels` with the current * group model state. */ private rebuildHeader; private generateId; } //# sourceMappingURL=grid-renderer.d.ts.map