@toolbox-web/grid 1.1.2 → 1.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +80 -22
- package/all.d.ts +1 -0
- package/all.d.ts.map +1 -1
- package/all.js +557 -365
- package/all.js.map +1 -1
- package/index.d.ts +1 -1
- package/index.d.ts.map +1 -1
- package/index.js +903 -769
- package/index.js.map +1 -1
- package/lib/core/grid.d.ts +102 -3
- package/lib/core/grid.d.ts.map +1 -1
- package/lib/core/internal/row-animation.d.ts +37 -0
- package/lib/core/internal/row-animation.d.ts.map +1 -0
- package/lib/core/internal/rows.d.ts.map +1 -1
- package/lib/core/internal/shell.d.ts.map +1 -1
- package/lib/core/plugin/base-plugin.d.ts +65 -3
- package/lib/core/plugin/base-plugin.d.ts.map +1 -1
- package/lib/core/plugin/index.d.ts +1 -1
- package/lib/core/plugin/index.d.ts.map +1 -1
- package/lib/core/plugin/plugin-manager.d.ts +25 -1
- package/lib/core/plugin/plugin-manager.d.ts.map +1 -1
- package/lib/core/plugin/types.d.ts +62 -0
- package/lib/core/plugin/types.d.ts.map +1 -1
- package/lib/core/types.d.ts +64 -1
- package/lib/core/types.d.ts.map +1 -1
- package/lib/plugins/clipboard/index.js +73 -69
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/clipboard/types.d.ts +1 -0
- package/lib/plugins/clipboard/types.d.ts.map +1 -1
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
- package/lib/plugins/editing/index.js +69 -40
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -1
- package/lib/plugins/master-detail/index.js +14 -12
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/reorder/index.js.map +1 -1
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/row-reorder/RowReorderPlugin.d.ts +155 -0
- package/lib/plugins/row-reorder/RowReorderPlugin.d.ts.map +1 -0
- package/lib/plugins/row-reorder/index.d.ts +9 -0
- package/lib/plugins/row-reorder/index.d.ts.map +1 -0
- package/lib/plugins/row-reorder/index.js +597 -0
- package/lib/plugins/row-reorder/index.js.map +1 -0
- package/lib/plugins/row-reorder/types.d.ts +80 -0
- package/lib/plugins/row-reorder/types.d.ts.map +1 -0
- package/lib/plugins/selection/SelectionPlugin.d.ts +13 -0
- package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
- package/lib/plugins/selection/index.d.ts +1 -1
- package/lib/plugins/selection/index.d.ts.map +1 -1
- package/lib/plugins/selection/index.js +95 -64
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/selection/types.d.ts +50 -6
- package/lib/plugins/selection/types.d.ts.map +1 -1
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +21 -4
- package/public.d.ts +15 -2
- package/public.d.ts.map +1 -1
- package/umd/grid.all.umd.js +23 -23
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +15 -15
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/clipboard.umd.js +5 -5
- package/umd/plugins/clipboard.umd.js.map +1 -1
- package/umd/plugins/editing.umd.js +1 -1
- package/umd/plugins/editing.umd.js.map +1 -1
- package/umd/plugins/master-detail.umd.js +1 -1
- package/umd/plugins/master-detail.umd.js.map +1 -1
- package/umd/plugins/row-reorder.umd.js +2 -0
- package/umd/plugins/row-reorder.umd.js.map +1 -0
- package/umd/plugins/selection.umd.js +2 -2
- package/umd/plugins/selection.umd.js.map +1 -1
package/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sources":["../../../libs/grid/src/lib/core/types.ts","../../../libs/grid/src/lib/core/internal/columns.ts","../../../libs/grid/src/lib/core/internal/inference.ts","../../../libs/grid/src/lib/core/internal/sanitize.ts","../../../libs/grid/src/lib/core/internal/config-manager.ts","../../../libs/grid/src/lib/core/internal/utils.ts","../../../libs/grid/src/lib/core/internal/rows.ts","../../../libs/grid/src/lib/core/internal/keyboard.ts","../../../libs/grid/src/lib/core/internal/event-delegation.ts","../../../libs/grid/src/lib/core/internal/sorting.ts","../../../libs/grid/src/lib/core/internal/header.ts","../../../libs/grid/src/lib/core/internal/idle-scheduler.ts","../../../libs/grid/src/lib/core/internal/render-scheduler.ts","../../../libs/grid/src/lib/core/internal/resize.ts","../../../libs/grid/src/lib/core/internal/dom-builder.ts","../../../libs/grid/src/lib/core/internal/shell.ts","../../../libs/grid/src/lib/core/internal/touch-scroll.ts","../../../libs/grid/src/lib/core/internal/validate-config.ts","../../../libs/grid/src/lib/core/plugin/plugin-manager.ts","../../../libs/grid/src/lib/core/grid.ts","../../../libs/grid/src/lib/core/plugin/types.ts","../../../libs/grid/src/lib/core/plugin/base-plugin.ts","../../../libs/grid/src/lib/core/constants.ts","../../../libs/grid/src/public.ts","../../../libs/grid/src/lib/core/internal/aggregators.ts"],"sourcesContent":["import type { PluginQuery } from './plugin/base-plugin';\nimport type { CellMouseEvent } from './plugin/types';\n\n/**\n * The compiled webcomponent interface for DataGrid\n */\nexport interface DataGridElement extends PublicGrid, HTMLElement {}\n\n/**\n * Public API interface for DataGrid component.\n *\n * **Property Getters vs Setters:**\n *\n * Property getters return the EFFECTIVE (resolved) value after merging all config sources.\n * This is the \"current situation\" - what consumers and plugins need to know.\n *\n * Property setters accept input values which are merged into the effective config.\n * Multiple sources can contribute (gridConfig, columns prop, light DOM, individual props).\n *\n * For example:\n * - `grid.fitMode` returns the resolved fitMode (e.g., 'stretch' even if you set undefined)\n * - `grid.columns` returns the effective columns after merging\n * - `grid.gridConfig` returns the full effective config\n */\nexport interface PublicGrid<T = any> {\n /**\n * Full config object. Setter merges with other inputs per precedence rules.\n * Getter returns the effective (resolved) config.\n */\n gridConfig?: GridConfig<T>;\n /**\n * Column definitions.\n * Getter returns effective columns (after merging config, light DOM, inference).\n */\n columns?: ColumnConfig<T>[];\n /** Current row data (after plugin processing like grouping, filtering). */\n rows?: T[];\n /** Resolves once the component has finished initial work (layout, inference). */\n ready?: () => Promise<void>;\n /** Force a layout / measurement pass (e.g. after container resize). */\n forceLayout?: () => Promise<void>;\n /** Return effective resolved config (after inference & precedence). */\n getConfig?: () => Promise<Readonly<GridConfig<T>>>;\n /** Toggle expansion state of a group row by its generated key. */\n toggleGroup?: (key: string) => Promise<void>;\n\n // Custom Styles API\n /**\n * Register custom CSS styles to be injected into the grid.\n * Use this to style custom cell renderers, editors, or detail panels.\n * @param id - Unique identifier for the style block (for removal/updates)\n * @param css - CSS string to inject\n */\n registerStyles?: (id: string, css: string) => void;\n /**\n * Remove previously registered custom styles.\n * @param id - The ID used when registering the styles\n */\n unregisterStyles?: (id: string) => void;\n /**\n * Get list of registered custom style IDs.\n */\n getRegisteredStyles?: () => string[];\n}\n\n/**\n * Internal-only augmented interface for DataGrid component.\n *\n * Member prefixes indicate accessibility:\n * - `_underscore` = protected members - private outside core, accessible to plugins. Marked with @internal.\n * - `__doubleUnderscore` = deeply internal members - private outside core, only for internal functions.\n *\n * @category Plugin Development\n */\nexport interface InternalGrid<T = any> extends PublicGrid<T>, GridConfig<T> {\n // Element methods available because DataGridElement extends HTMLElement\n querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;\n querySelector<E extends Element = Element>(selectors: string): E | null;\n querySelectorAll<K extends keyof HTMLElementTagNameMap>(selectors: K): NodeListOf<HTMLElementTagNameMap[K]>;\n querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;\n _rows: T[];\n _columns: ColumnInternal<T>[];\n /** Visible columns only (excludes hidden). Use for rendering. */\n _visibleColumns: ColumnInternal<T>[];\n _headerRowEl: HTMLElement;\n _bodyEl: HTMLElement;\n _rowPool: RowElementInternal[];\n _resizeController: ResizeController;\n _sortState: { field: string; direction: 1 | -1 } | null;\n /** Original unfiltered/unprocessed rows. @internal */\n sourceRows: T[];\n /** Framework adapter instance (set by Grid directives). @internal */\n __frameworkAdapter?: FrameworkAdapter;\n __originalOrder: T[];\n __rowRenderEpoch: number;\n __didInitialAutoSize?: boolean;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n /** Cell display value cache. @internal */\n __cellDisplayCache?: Map<number, string[]>;\n /** Cache epoch for cell display values. @internal */\n __cellCacheEpoch?: number;\n /** Cached header row count for virtualization. @internal */\n __cachedHeaderRowCount?: number;\n /** Cached flag for whether grid has special columns (custom renderers, etc.). @internal */\n __hasSpecialColumns?: boolean;\n /** Cached flag for whether any plugin has renderRow hooks. @internal */\n __hasRenderRowPlugins?: boolean;\n _gridTemplate: string;\n _virtualization: VirtualState;\n _focusRow: number;\n _focusCol: number;\n /** Currently active edit row index. Injected by EditingPlugin. @internal */\n _activeEditRows?: number;\n /** Snapshots of row data before editing. Injected by EditingPlugin. @internal */\n _rowEditSnapshots?: Map<number, T>;\n /** Get all changed rows. Injected by EditingPlugin. */\n changedRows?: T[];\n /** Get IDs of all changed rows. Injected by EditingPlugin. */\n changedRowIds?: string[];\n effectiveConfig?: GridConfig<T>;\n findHeaderRow?: () => HTMLElement;\n refreshVirtualWindow: (full: boolean, skipAfterRender?: boolean) => void;\n updateTemplate?: () => void;\n findRenderedRowElement?: (rowIndex: number) => HTMLElement | null;\n /** Get a row by its ID. Implemented in grid.ts */\n getRow?: (id: string) => T | undefined;\n /** Get the unique ID for a row. Implemented in grid.ts */\n getRowId?: (row: T) => string;\n /** Update a row by ID. Implemented in grid.ts */\n updateRow?: (id: string, changes: Partial<T>, source?: UpdateSource) => void;\n /** Begin bulk edit on a row. Injected by EditingPlugin. */\n beginBulkEdit?: (rowIndex: number) => void;\n /** Commit active row edit. Injected by EditingPlugin. */\n commitActiveRowEdit?: () => void;\n /** Dispatch cell click to plugin system, returns true if handled */\n _dispatchCellClick?: (event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement) => boolean;\n /** Dispatch row click to plugin system, returns true if handled */\n _dispatchRowClick?: (event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement) => boolean;\n /** Dispatch header click to plugin system, returns true if handled */\n _dispatchHeaderClick?: (event: MouseEvent, colIndex: number, headerEl: HTMLElement) => boolean;\n /** Dispatch keydown to plugin system, returns true if handled */\n _dispatchKeyDown?: (event: KeyboardEvent) => boolean;\n /** Dispatch cell mouse events for drag operations. Returns true if any plugin started a drag. */\n _dispatchCellMouseDown?: (event: CellMouseEvent) => boolean;\n /** Dispatch cell mouse move during drag. */\n _dispatchCellMouseMove?: (event: CellMouseEvent) => void;\n /** Dispatch cell mouse up to end drag. */\n _dispatchCellMouseUp?: (event: CellMouseEvent) => void;\n /** Get horizontal scroll boundary offsets from plugins */\n _getHorizontalScrollOffsets?: (\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ) => { left: number; right: number; skipScroll?: boolean };\n /** Query all plugins with a generic query and collect responses */\n queryPlugins?: <T>(query: PluginQuery) => T[];\n /** Request emission of column-state-change event (debounced) */\n requestStateChange?: () => void;\n}\n\nexport type PrimitiveColumnType = 'number' | 'string' | 'date' | 'boolean' | 'select';\n\n/**\n * Column type - built-in primitives or custom type strings.\n * Custom types (e.g., 'currency', 'country') can have type-level defaults via `typeDefaults`.\n */\nexport type ColumnType = PrimitiveColumnType | (string & {});\n\n/**\n * Type-level defaults for renderers and editors.\n * Applied to all columns of a given type unless overridden at column level.\n *\n * @example\n * ```typescript\n * typeDefaults: {\n * country: {\n * renderer: (ctx) => {\n * const span = document.createElement('span');\n * span.innerHTML = `<img src=\"/flags/${ctx.value}.svg\" /> ${ctx.value}`;\n * return span;\n * }\n * },\n * date: {\n * editor: (ctx) => createDatePickerEditor(ctx)\n * }\n * }\n * ```\n */\nexport interface TypeDefault<TRow = unknown> {\n /** Renderer template for this type */\n renderer?: ColumnViewRenderer<TRow, unknown>;\n /** Editor template for this type (requires EditingPlugin) */\n editor?: ColumnEditorSpec<TRow, unknown>;\n /** Default editorParams for this type */\n editorParams?: Record<string, unknown>;\n}\n\n/**\n * Base contract for a column. Public; kept intentionally lean so host apps can extend via intersection types.\n * Prefer adding optional properties here only when broadly useful to most grids.\n */\nexport interface BaseColumnConfig<TRow = any, TValue = any> {\n /** Unique field key referencing property in row objects */\n field: keyof TRow & string;\n /** Visible header label; defaults to capitalized field */\n header?: string;\n /**\n * Column data type.\n *\n * Built-in types: `'string'`, `'number'`, `'date'`, `'boolean'`, `'select'`\n *\n * Custom types (e.g., `'currency'`, `'country'`) can have type-level defaults\n * via `gridConfig.typeDefaults` or framework adapter registries.\n *\n * @default Inferred from first row data\n */\n type?: ColumnType;\n /** Column width in pixels; fixed size (no flexibility) */\n width?: string | number;\n /** Minimum column width in pixels (stretch mode only); when set, column uses minmax(minWidth, 1fr) */\n minWidth?: number;\n /** Whether column can be sorted */\n sortable?: boolean;\n /** Whether column can be resized by user */\n resizable?: boolean;\n /** Optional custom comparator for sorting (a,b) -> number */\n sortComparator?: (a: TValue, b: TValue, rowA: TRow, rowB: TRow) => number;\n /** For select type - available options */\n options?: Array<{ label: string; value: unknown }> | (() => Array<{ label: string; value: unknown }>);\n /** For select - allow multi select */\n multi?: boolean;\n /** Optional formatter */\n format?: (value: TValue, row: TRow) => string;\n /** Arbitrary extra metadata */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Full column configuration including optional custom view/renderer & grouping metadata.\n */\nexport interface ColumnConfig<TRow = any> extends BaseColumnConfig<TRow, any> {\n /**\n * Optional custom cell renderer function. Alias for `viewRenderer`.\n * Can return an HTMLElement, a Node, or an HTML string (which will be sanitized).\n *\n * @example\n * ```typescript\n * // Simple string template\n * renderer: (ctx) => `<span class=\"badge\">${ctx.value}</span>`\n *\n * // DOM element\n * renderer: (ctx) => {\n * const el = document.createElement('span');\n * el.textContent = ctx.value;\n * return el;\n * }\n * ```\n */\n renderer?: ColumnViewRenderer<TRow, any>;\n /** Optional custom view renderer used instead of default text rendering */\n viewRenderer?: ColumnViewRenderer<TRow, any>;\n /** External view spec (lets host app mount any framework component) */\n externalView?: {\n component: unknown;\n props?: Record<string, unknown>;\n mount?: (options: {\n placeholder: HTMLElement;\n context: CellRenderContext<TRow, unknown>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n /** Whether the column is initially hidden */\n hidden?: boolean;\n /** Prevent this column from being hidden programmatically */\n lockVisible?: boolean;\n /**\n * Dynamic CSS class(es) for cells in this column.\n * Called for each cell during rendering. Return class names to add to the cell element.\n *\n * @example\n * ```typescript\n * // Highlight negative values\n * cellClass: (value, row, column) => value < 0 ? ['negative', 'text-red'] : []\n *\n * // Status-based styling\n * cellClass: (value) => [`status-${value}`]\n * ```\n */\n cellClass?: (value: unknown, row: TRow, column: ColumnConfig<TRow>) => string[];\n}\n\nexport type ColumnConfigMap<TRow = any> = ColumnConfig<TRow>[];\n\n/** External editor spec: tag name, factory function, or external mount spec */\nexport type ColumnEditorSpec<TRow = unknown, TValue = unknown> =\n | string // custom element tag name\n | ((context: ColumnEditorContext<TRow, TValue>) => HTMLElement | string)\n | {\n /** Arbitrary component reference (class, function, token) */\n component: unknown;\n /** Optional static props passed to mount */\n props?: Record<string, unknown>;\n /** Optional custom mount function; if provided we call it directly instead of emitting an event */\n mount?: (options: {\n placeholder: HTMLElement;\n context: ColumnEditorContext<TRow, TValue>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n\n/**\n * Context object provided to editor factories allowing mutation (commit/cancel) of a cell value.\n */\nexport interface ColumnEditorContext<TRow = any, TValue = any> {\n /** Underlying full row object for the active edit. */\n row: TRow;\n /** Current cell value (mutable only via commit). */\n value: TValue;\n /** Field name being edited. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n /** Accept the edit; triggers change tracking + rerender. */\n commit: (newValue: TValue) => void;\n /** Abort edit without persisting changes. */\n cancel: () => void;\n}\n\n/**\n * Context passed to custom view renderers (pure display – no commit helpers).\n */\nexport interface CellRenderContext<TRow = any, TValue = any> {\n /** Row object for the cell being rendered. */\n row: TRow;\n /** Value at field. */\n value: TValue;\n /** Field key. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n /**\n * The cell DOM element being rendered into.\n * Framework adapters can use this to cache per-cell state (e.g., React roots).\n * @internal\n */\n cellEl?: HTMLElement;\n}\n\nexport type ColumnViewRenderer<TRow = unknown, TValue = unknown> = (\n ctx: CellRenderContext<TRow, TValue>,\n) => Node | string | void | null;\n\n/**\n * Framework adapter interface for handling framework-specific component instantiation.\n * Allows framework libraries (Angular, React, Vue) to register handlers that convert\n * declarative light DOM elements into functional renderers/editors.\n *\n * @example\n * ```typescript\n * // In @toolbox-web/grid-angular\n * class AngularGridAdapter implements FrameworkAdapter {\n * canHandle(element: HTMLElement): boolean {\n * return element.tagName.startsWith('APP-');\n * }\n * createRenderer(element: HTMLElement): ColumnViewRenderer {\n * return (ctx) => {\n * // Angular-specific instantiation logic\n * const componentRef = createComponent(...);\n * componentRef.setInput('value', ctx.value);\n * return componentRef.location.nativeElement;\n * };\n * }\n * createEditor(element: HTMLElement): ColumnEditorSpec {\n * return (ctx) => {\n * // Angular-specific editor with commit/cancel\n * const componentRef = createComponent(...);\n * componentRef.setInput('value', ctx.value);\n * // Subscribe to commit/cancel outputs\n * return componentRef.location.nativeElement;\n * };\n * }\n * }\n *\n * // User registers adapter once in their app\n * GridElement.registerAdapter(new AngularGridAdapter(injector, appRef));\n * ```\n * @category Framework Adapters\n */\nexport interface FrameworkAdapter {\n /**\n * Determines if this adapter can handle the given element.\n * Typically checks tag name, attributes, or other conventions.\n */\n canHandle(element: HTMLElement): boolean;\n\n /**\n * Creates a view renderer function from a light DOM element.\n * The renderer receives cell context and returns DOM or string.\n */\n createRenderer<TRow = unknown, TValue = unknown>(element: HTMLElement): ColumnViewRenderer<TRow, TValue>;\n\n /**\n * Creates an editor spec from a light DOM element.\n * The editor receives context with commit/cancel and returns DOM.\n */\n createEditor<TRow = unknown, TValue = unknown>(element: HTMLElement): ColumnEditorSpec<TRow, TValue>;\n\n /**\n * Creates a tool panel renderer from a light DOM element.\n * The renderer receives a container element and optionally returns a cleanup function.\n */\n createToolPanelRenderer?(element: HTMLElement): ((container: HTMLElement) => void | (() => void)) | undefined;\n\n /**\n * Gets type-level defaults from an application-level registry.\n * Used by Angular's `GridTypeRegistry` and React's `GridTypeProvider`.\n *\n * @param type - The column type (e.g., 'date', 'currency', 'country')\n * @returns Type defaults for renderer/editor, or undefined if not registered\n */\n getTypeDefault?<TRow = unknown>(type: string): TypeDefault<TRow> | undefined;\n}\n\n// #region Internal-only augmented types (not re-exported publicly)\n\n/**\n * Column internal properties used during light DOM parsing.\n * Stores attribute-based names before they're resolved to actual functions.\n * @internal\n */\nexport interface ColumnParsedAttributes {\n /** Editor name from `editor` attribute (resolved later) */\n __editorName?: string;\n /** Renderer name from `renderer` attribute (resolved later) */\n __rendererName?: string;\n}\n\n/**\n * Extended column config used internally.\n * Includes all internal properties needed during grid lifecycle.\n *\n * @category Plugin Development\n * @internal\n */\nexport interface ColumnInternal<T = any> extends ColumnConfig<T>, ColumnParsedAttributes {\n __autoSized?: boolean;\n __userResized?: boolean;\n __renderedWidth?: number;\n /** Original configured width (for reset on double-click) */\n __originalWidth?: number;\n __viewTemplate?: HTMLElement;\n __editorTemplate?: HTMLElement;\n __headerTemplate?: HTMLElement;\n __compiledView?: CompiledViewFunction<T>;\n __compiledEditor?: (ctx: EditorExecContext<T>) => string;\n}\n\n/**\n * Row element with internal tracking properties.\n * Used during virtualization and row pooling.\n *\n * @category Plugin Development\n * @internal\n */\nexport interface RowElementInternal extends HTMLElement {\n /** Epoch marker for row render invalidation */\n __epoch?: number;\n /** Reference to the row data object for change detection */\n __rowDataRef?: unknown;\n /** Count of cells currently in edit mode */\n __editingCellCount?: number;\n /** Flag indicating this is a custom-rendered row (group row, etc.) */\n __isCustomRow?: boolean;\n}\n\n/**\n * Type-safe access to element.part API (DOMTokenList-like).\n * Used for CSS ::part styling support.\n * @internal\n */\nexport interface ElementWithPart {\n part?: DOMTokenList;\n}\n\n/**\n * Compiled view function type with optional blocked flag.\n * The __blocked flag is set when a template contains unsafe expressions.\n *\n * @category Plugin Development\n * @internal\n */\nexport interface CompiledViewFunction<T = any> {\n (ctx: CellContext<T>): string;\n /** Set to true when template was blocked due to unsafe expressions */\n __blocked?: boolean;\n}\n\n/**\n * Runtime cell context used internally for compiled template execution.\n *\n * @category Plugin Development\n */\nexport interface CellContext<T = any> {\n row: T;\n value: unknown;\n field: string;\n column: ColumnInternal<T>;\n}\n\n/**\n * Internal editor execution context extending the generic cell context with commit helpers.\n *\n * @category Plugin Development\n */\nexport interface EditorExecContext<T = any> extends CellContext<T> {\n commit: (newValue: unknown) => void;\n cancel: () => void;\n}\n\n/**\n * Controller managing drag-based column resize lifecycle.\n *\n * @category Plugin Development\n */\nexport interface ResizeController {\n start: (e: MouseEvent, colIndex: number, cell: HTMLElement) => void;\n /** Reset a column to its configured width (or auto-size if none configured). */\n resetColumn: (colIndex: number) => void;\n dispose: () => void;\n /** True while a resize drag is in progress (used to suppress header click/sort). */\n isResizing: boolean;\n}\n\n/**\n * Virtual window bookkeeping; modified in-place as scroll position changes.\n *\n * @category Plugin Development\n */\nexport interface VirtualState {\n enabled: boolean;\n rowHeight: number;\n /** Threshold for bypassing virtualization (renders all rows if totalRows <= bypassThreshold) */\n bypassThreshold: number;\n start: number;\n end: number;\n /** Faux scrollbar element that provides scroll events (AG Grid pattern) */\n container: HTMLElement | null;\n /** Rows viewport element for measuring visible area height */\n viewportEl: HTMLElement | null;\n /** Spacer element inside faux scrollbar for setting virtual height */\n totalHeightEl: HTMLElement | null;\n}\n\n// RowElementInternal is now defined earlier in the file with all internal properties\n\n/**\n * Union type for input-like elements that have a `value` property.\n * Covers standard form elements and custom elements with value semantics.\n *\n * @category Plugin Development\n * @internal\n */\nexport type InputLikeElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | { value: unknown };\n// #endregion\n\n// #region Grouping & Footer Public Types\n/**\n * Group row rendering customization options.\n * Used within grouping-rows plugin config for presentation of group rows.\n */\nexport interface RowGroupRenderConfig {\n /** If true, group rows span all columns (single full-width cell). Default false. */\n fullWidth?: boolean;\n /** Optional label formatter override. Receives raw group value + depth. */\n formatLabel?: (value: unknown, depth: number, key: string) => string;\n /** Optional aggregate overrides per field for group summary cells (only when not fullWidth). */\n aggregators?: Record<string, AggregatorRef>;\n /** Additional CSS class applied to each group row root element. */\n class?: string;\n}\n\nexport type AggregatorRef = string | ((rows: unknown[], field: string, column?: unknown) => unknown);\n\n/** Result of automatic column inference from sample rows. */\nexport interface InferredColumnResult<TRow = unknown> {\n columns: ColumnConfigMap<TRow>;\n typeMap: Record<string, ColumnType>;\n}\n\nexport const FitModeEnum = {\n STRETCH: 'stretch',\n FIXED: 'fixed',\n} as const;\nexport type FitMode = (typeof FitModeEnum)[keyof typeof FitModeEnum]; // evaluates to 'stretch' | 'fixed'\n// #endregion\n\n// #region Plugin Interface\n/**\n * Minimal plugin interface for type-checking.\n * This interface is defined here to avoid circular imports with BaseGridPlugin.\n * All plugins must satisfy this shape (BaseGridPlugin implements it).\n *\n * @category Plugin Development\n */\nexport interface GridPlugin {\n /** Unique plugin identifier */\n readonly name: string;\n /** Plugin version */\n readonly version: string;\n /** CSS styles to inject into the grid */\n readonly styles?: string;\n}\n// #endregion\n\n// #region Grid Config\n/**\n * Grid configuration object - the **single source of truth** for grid behavior.\n *\n * Users can configure the grid via multiple input methods, all of which converge\n * into an effective `GridConfig` internally:\n *\n * **Configuration Input Methods:**\n * - `gridConfig` property - direct assignment of this object\n * - `columns` property - shorthand for `gridConfig.columns`\n * - `fitMode` property - shorthand for `gridConfig.fitMode`\n * - Light DOM `<tbw-grid-column>` - declarative columns (merged into `columns`)\n * - Light DOM `<tbw-grid-header>` - declarative shell header (merged into `shell.header`)\n *\n * **Precedence (when same property set multiple ways):**\n * Individual props (`fitMode`) > `columns` prop > Light DOM > `gridConfig`\n *\n * @example\n * ```ts\n * // Via gridConfig (recommended for complex setups)\n * grid.gridConfig = {\n * columns: [{ field: 'name' }, { field: 'age' }],\n * fitMode: 'stretch',\n * plugins: [new SelectionPlugin()],\n * shell: { header: { title: 'My Grid' } }\n * };\n *\n * // Via individual props (convenience for simple cases)\n * grid.columns = [{ field: 'name' }, { field: 'age' }];\n * grid.fitMode = 'stretch';\n * ```\n */\nexport interface GridConfig<TRow = any> {\n /** Column definitions. Can also be set via `columns` prop or `<tbw-grid-column>` light DOM. */\n columns?: ColumnConfigMap<TRow>;\n /**\n * Dynamic CSS class(es) for data rows.\n * Called for each row during rendering. Return class names to add to the row element.\n *\n * @example\n * ```typescript\n * // Highlight inactive rows\n * rowClass: (row) => row.active ? [] : ['inactive', 'dimmed']\n *\n * // Status-based row styling\n * rowClass: (row) => [`priority-${row.priority}`]\n * ```\n */\n rowClass?: (row: TRow) => string[];\n /** Sizing mode for columns. Can also be set via `fitMode` prop. */\n fitMode?: FitMode;\n /**\n * Edit trigger mode. Requires `EditingPlugin` to be loaded.\n *\n * Configure via `new EditingPlugin({ editOn: 'click' })` or set on gridConfig.\n * Plugin config takes precedence over gridConfig.\n *\n * - `'click'`: Single click to edit\n * - `'dblclick'`: Double-click to edit (default)\n * - `'manual'`: Only via programmatic API (beginEdit)\n * - `false`: Disable editing entirely\n */\n editOn?: 'click' | 'dblclick' | 'manual' | false;\n /**\n * Row height in pixels for virtualization calculations.\n * The virtualization system assumes uniform row heights for performance.\n *\n * If not specified, the grid measures the first rendered row's height,\n * which respects the CSS variable `--tbw-row-height` set by themes.\n *\n * Set this explicitly when:\n * - Row content may wrap to multiple lines (also set `--tbw-cell-white-space: normal`)\n * - Using custom row templates with variable content\n * - You want to override theme-defined row height\n *\n * @default Auto-measured from first row (respects --tbw-row-height CSS variable)\n *\n * @example\n * ```ts\n * // Fixed height for rows that may wrap to 2 lines\n * gridConfig = { rowHeight: 56 };\n * ```\n */\n rowHeight?: number;\n /**\n * Array of plugin instances.\n * Each plugin is instantiated with its configuration and attached to this grid.\n *\n * @example\n * ```ts\n * plugins: [\n * new SelectionPlugin({ mode: 'range' }),\n * new MultiSortPlugin(),\n * new FilteringPlugin({ debounceMs: 150 }),\n * ]\n * ```\n */\n plugins?: GridPlugin[];\n\n /**\n * Saved column state to restore on initialization.\n * Includes order, width, visibility, sort, and plugin-contributed state.\n */\n columnState?: GridColumnState;\n\n /**\n * Shell configuration for header bar and tool panels.\n * When configured, adds an optional wrapper with title, toolbar, and collapsible side panels.\n */\n shell?: ShellConfig;\n\n /**\n * Grid-wide icon configuration.\n * Provides consistent icons across all plugins (tree, grouping, sorting, etc.).\n * Plugins will use these by default but can override with their own config.\n */\n icons?: GridIcons;\n\n /**\n * Grid-wide animation configuration.\n * Controls animations for expand/collapse, reordering, and other visual transitions.\n * Individual plugins can override these defaults in their own config.\n */\n animation?: AnimationConfig;\n\n /**\n * Custom sort handler for full control over sorting behavior.\n *\n * When provided, this handler is called instead of the built-in sorting logic.\n * Enables custom sorting algorithms, server-side sorting, or plugin-specific sorting.\n *\n * The handler receives:\n * - `rows`: Current row array to sort\n * - `sortState`: Sort field and direction (1 = asc, -1 = desc)\n * - `columns`: Column configurations (for accessing sortComparator)\n *\n * Return the sorted array (sync) or a Promise that resolves to the sorted array (async).\n * For server-side sorting, return a Promise that resolves when data is fetched.\n *\n * @example\n * ```ts\n * // Custom stable sort\n * sortHandler: (rows, state, cols) => {\n * return stableSort(rows, (a, b) => compare(a[state.field], b[state.field]) * state.direction);\n * }\n *\n * // Server-side sorting\n * sortHandler: async (rows, state) => {\n * const response = await fetch(`/api/data?sort=${state.field}&dir=${state.direction}`);\n * return response.json();\n * }\n * ```\n */\n sortHandler?: SortHandler<TRow>;\n\n /**\n * Function to extract a unique identifier from a row.\n * Used by `updateRow()`, `getRow()`, and ID-based tracking.\n *\n * If not provided, falls back to `row.id` or `row._id` if present.\n * Rows without IDs are silently skipped during map building.\n * Only throws when explicitly calling `getRowId()` or `updateRow()` on a row without an ID.\n *\n * @example\n * ```ts\n * // Simple field\n * getRowId: (row) => row.id\n *\n * // Composite key\n * getRowId: (row) => `${row.voyageId}-${row.legNumber}`\n *\n * // UUID field\n * getRowId: (row) => row.uuid\n * ```\n */\n getRowId?: (row: TRow) => string;\n\n /**\n * Type-level renderer and editor defaults.\n *\n * Keys can be:\n * - Built-in types: `'string'`, `'number'`, `'date'`, `'boolean'`, `'select'`\n * - Custom types: `'currency'`, `'country'`, `'status'`, etc.\n *\n * Resolution order (highest priority first):\n * 1. Column-level (`column.renderer` / `column.editor`)\n * 2. Grid-level (`gridConfig.typeDefaults[column.type]`)\n * 3. App-level (Angular `GridTypeRegistry`, React `GridTypeProvider`)\n * 4. Built-in (checkbox for boolean, select for select, etc.)\n * 5. Fallback (plain text / text input)\n *\n * @example\n * ```typescript\n * typeDefaults: {\n * date: { editor: myDatePickerEditor },\n * country: {\n * renderer: (ctx) => {\n * const span = document.createElement('span');\n * span.innerHTML = `<img src=\"/flags/${ctx.value}.svg\" /> ${ctx.value}`;\n * return span;\n * },\n * editor: (ctx) => createCountrySelect(ctx)\n * }\n * }\n * ```\n */\n typeDefaults?: Record<string, TypeDefault<TRow>>;\n}\n// #endregion\n\n// #region Animation\n\n/**\n * Sort state passed to custom sort handlers.\n */\nexport interface SortState {\n /** Field to sort by */\n field: string;\n /** Sort direction: 1 = ascending, -1 = descending */\n direction: 1 | -1;\n}\n\n/**\n * Custom sort handler function signature.\n *\n * @param rows - Current row array to sort\n * @param sortState - Sort field and direction\n * @param columns - Column configurations (for accessing sortComparator)\n * @returns Sorted array (sync) or Promise resolving to sorted array (async)\n */\nexport type SortHandler<TRow = any> = (\n rows: TRow[],\n sortState: SortState,\n columns: ColumnConfig<TRow>[],\n) => TRow[] | Promise<TRow[]>;\n\n// #region Data Update Management\n\n/**\n * Indicates the origin of a data change.\n * Used to prevent infinite loops in cascade update handlers.\n *\n * - `'user'`: Direct user interaction via EditingPlugin (typing, selecting)\n * - `'cascade'`: Triggered by `updateRow()` in an event handler\n * - `'api'`: External programmatic update via `grid.updateRow()`\n *\n * @category Data Management\n */\nexport type UpdateSource = 'user' | 'cascade' | 'api';\n\n/**\n * Detail for cell-change event (emitted by core after mutation).\n * This is an informational event that fires for ALL data mutations.\n *\n * @category Events\n */\nexport interface CellChangeDetail<TRow = unknown> {\n /** The row object (after mutation) */\n row: TRow;\n /** Stable row identifier */\n rowId: string;\n /** Current index in rows array */\n rowIndex: number;\n /** Field that changed */\n field: string;\n /** Value before change */\n oldValue: unknown;\n /** Value after change */\n newValue: unknown;\n /** All changes passed to updateRow/updateRows (for context) */\n changes: Partial<TRow>;\n /** Origin of this change */\n source: UpdateSource;\n}\n\n/**\n * Batch update specification for updateRows().\n *\n * @category Data Management\n */\nexport interface RowUpdate<TRow = unknown> {\n /** Row identifier (from getRowId) */\n id: string;\n /** Fields to update */\n changes: Partial<TRow>;\n}\n\n// #endregion\n\n/**\n * Animation behavior mode.\n * - `true` or `'on'`: Animations always enabled\n * - `false` or `'off'`: Animations always disabled\n * - `'reduced-motion'`: Respects `prefers-reduced-motion` media query (default)\n */\nexport type AnimationMode = boolean | 'on' | 'off' | 'reduced-motion';\n\n/**\n * Animation style for visual transitions.\n * - `'slide'`: Slide/transform animation (e.g., expand down, slide left/right)\n * - `'fade'`: Opacity fade animation\n * - `'flip'`: FLIP technique for position changes (First, Last, Invert, Play)\n * - `false`: No animation for this specific feature\n */\nexport type AnimationStyle = 'slide' | 'fade' | 'flip' | false;\n\n/**\n * Animation style for expand/collapse operations.\n * Subset of AnimationStyle - excludes 'flip' which is for position changes.\n * - `'slide'`: Slide down/up animation for expanding/collapsing content\n * - `'fade'`: Fade in/out animation\n * - `false`: No animation\n */\nexport type ExpandCollapseAnimation = 'slide' | 'fade' | false;\n\n/**\n * Grid-wide animation configuration.\n * Controls global animation behavior - individual plugins define their own animation styles.\n * Duration and easing values set corresponding CSS variables on the grid element.\n */\nexport interface AnimationConfig {\n /**\n * Global animation mode.\n * @default 'reduced-motion'\n */\n mode?: AnimationMode;\n\n /**\n * Default animation duration in milliseconds.\n * Sets `--tbw-animation-duration` CSS variable.\n * @default 200\n */\n duration?: number;\n\n /**\n * Default easing function.\n * Sets `--tbw-animation-easing` CSS variable.\n * @default 'ease-out'\n */\n easing?: string;\n}\n\n/** Default animation configuration */\nexport const DEFAULT_ANIMATION_CONFIG: Required<Omit<AnimationConfig, 'sort'>> = {\n mode: 'reduced-motion',\n duration: 200,\n easing: 'ease-out',\n};\n\n// #endregion\n\n// #region Grid Icons\n\n/** Icon value - can be a string (text/HTML) or HTMLElement */\nexport type IconValue = string | HTMLElement;\n\n/**\n * Grid-wide icon configuration.\n * All icons are optional - sensible defaults are used when not specified.\n */\nexport interface GridIcons {\n /** Expand icon for collapsed items (trees, groups, details). Default: '▶' */\n expand?: IconValue;\n /** Collapse icon for expanded items (trees, groups, details). Default: '▼' */\n collapse?: IconValue;\n /** Sort ascending indicator. Default: '▲' */\n sortAsc?: IconValue;\n /** Sort descending indicator. Default: '▼' */\n sortDesc?: IconValue;\n /** Sort neutral/unsorted indicator. Default: '⇅' */\n sortNone?: IconValue;\n /** Submenu arrow for context menus. Default: '▶' */\n submenuArrow?: IconValue;\n /** Drag handle icon for reordering. Default: '⋮⋮' */\n dragHandle?: IconValue;\n /** Tool panel toggle icon in toolbar. Default: '☰' */\n toolPanel?: IconValue;\n}\n\n/** Default icons used when not overridden */\nexport const DEFAULT_GRID_ICONS: Required<GridIcons> = {\n expand: '▶',\n collapse: '▼',\n sortAsc: '▲',\n sortDesc: '▼',\n sortNone: '⇅',\n submenuArrow: '▶',\n dragHandle: '⋮⋮',\n toolPanel: '☰',\n};\n// #endregion\n\n// #region Shell Configuration\n\n/**\n * Shell configuration for the grid's optional header bar and tool panels.\n */\nexport interface ShellConfig {\n /** Shell header bar configuration */\n header?: ShellHeaderConfig;\n /** Tool panel configuration */\n toolPanel?: ToolPanelConfig;\n /**\n * Registered tool panels (from plugins, API, or Light DOM).\n * These are the actual panel definitions that can be opened.\n * @internal Set by ConfigManager during merge\n */\n toolPanels?: ToolPanelDefinition[];\n /**\n * Registered header content sections (from plugins or API).\n * Content rendered in the center of the shell header.\n * @internal Set by ConfigManager during merge\n */\n headerContents?: HeaderContentDefinition[];\n}\n\n/**\n * Shell header bar configuration\n */\nexport interface ShellHeaderConfig {\n /** Grid title displayed on the left (optional) */\n title?: string;\n /** Custom toolbar content (rendered before tool panel toggle) */\n toolbarContents?: ToolbarContentDefinition[];\n /**\n * Light DOM header content elements (parsed from <tbw-grid-header> children).\n * @internal Set by ConfigManager during merge\n */\n lightDomContent?: HTMLElement[];\n /**\n * Whether a tool buttons container was found in light DOM.\n * @internal Set by ConfigManager during merge\n */\n hasToolButtonsContainer?: boolean;\n}\n\n/**\n * Tool panel configuration\n */\nexport interface ToolPanelConfig {\n /** Panel position: 'left' | 'right' (default: 'right') */\n position?: 'left' | 'right';\n /** Default panel width in pixels (default: 280) */\n width?: number;\n /** Panel ID to open by default on load */\n defaultOpen?: string;\n /** Whether to persist open/closed state (requires Column State Events) */\n persistState?: boolean;\n}\n\n/**\n * Toolbar content definition for the shell header toolbar area.\n * Register via `registerToolbarContent()` or use light DOM `<tbw-grid-tool-buttons>`.\n *\n * @example\n * ```typescript\n * grid.registerToolbarContent({\n * id: 'my-toolbar',\n * order: 10,\n * render: (container) => {\n * const btn = document.createElement('button');\n * btn.textContent = 'Refresh';\n * btn.onclick = () => console.log('clicked');\n * container.appendChild(btn);\n * return () => btn.remove();\n * },\n * });\n * ```\n */\nexport interface ToolbarContentDefinition {\n /** Unique content ID */\n id: string;\n /** Content factory - called once when shell header renders */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when content is removed (for cleanup) */\n onDestroy?: () => void;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n}\n\n/**\n * Tool panel definition registered by plugins or consumers.\n */\nexport interface ToolPanelDefinition {\n /** Unique panel ID */\n id: string;\n /** Panel title shown in accordion header */\n title: string;\n /** Icon for accordion section header (optional, emoji or SVG) */\n icon?: string;\n /** Tooltip for accordion section header */\n tooltip?: string;\n /** Panel content factory - called when panel section opens */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when panel closes (for cleanup) */\n onClose?: () => void;\n /** Panel order priority (lower = first, default: 100) */\n order?: number;\n}\n\n/**\n * Header content definition for plugins contributing to shell header center section.\n */\nexport interface HeaderContentDefinition {\n /** Unique content ID */\n id: string;\n /** Content factory - called once when shell header renders */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when content is removed (for cleanup) */\n onDestroy?: () => void;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n}\n// #endregion\n\n// #region Column State (Persistence)\n\n/**\n * State for a single column. Captures user-driven changes at runtime.\n * Plugins can extend this interface via module augmentation to add their own state.\n *\n * @example\n * ```ts\n * // In filtering plugin\n * declare module '@toolbox-web/grid' {\n * interface ColumnState {\n * filter?: FilterValue;\n * }\n * }\n * ```\n */\nexport interface ColumnState {\n /** Column field identifier */\n field: string;\n /** Position index after reordering (0-based) */\n order: number;\n /** Width in pixels (undefined = use default) */\n width?: number;\n /** Visibility state */\n visible: boolean;\n /** Sort state (undefined = not sorted) */\n sort?: ColumnSortState;\n}\n\n/**\n * Sort state for a column\n */\nexport interface ColumnSortState {\n /** Sort direction */\n direction: 'asc' | 'desc';\n /** Priority for multi-sort (0 = primary, 1 = secondary, etc.) */\n priority: number;\n}\n\n/**\n * Complete grid column state for persistence.\n * Contains state for all columns, including plugin-contributed properties.\n */\nexport interface GridColumnState {\n columns: ColumnState[];\n}\n// #endregion\n\n// #region Public Event Detail Interfaces\n/**\n * Event detail for cell value commit.\n *\n * @category Events\n */\nexport interface CellCommitDetail<TRow = unknown> {\n /** The row object (not yet mutated if event is cancelable). */\n row: TRow;\n /** Stable row identifier (from getRowId). */\n rowId: string;\n /** Field name whose value changed. */\n field: string;\n /** Previous value before change. */\n oldValue: unknown;\n /** New value to be stored. */\n value: unknown;\n /** Index of the row in current data set. */\n rowIndex: number;\n /** All rows that have at least one committed change (snapshot list). */\n changedRows: TRow[];\n /** IDs of changed rows. */\n changedRowIds: string[];\n /** True if this row just entered the changed set. */\n firstTimeForRow: boolean;\n /**\n * Update other fields in this row.\n * Convenience wrapper for grid.updateRow(rowId, changes, 'cascade').\n * Useful for cascade updates (e.g., calculating totals).\n */\n updateRow: (changes: Partial<TRow>) => void;\n}\n\n/**\n * Detail payload for a committed row edit (may or may not include changes).\n *\n * @category Events\n */\nexport interface RowCommitDetail<TRow = unknown> {\n /** Row index that lost edit focus. */\n rowIndex: number;\n /** Stable row identifier (from getRowId). */\n rowId: string;\n /** Row object reference. */\n row: TRow;\n /** Whether any cell changes were actually committed in this row during the session. */\n changed: boolean;\n /** Current changed row collection. */\n changedRows: TRow[];\n /** IDs of changed rows. */\n changedRowIds: string[];\n}\n\n/**\n * Emitted when the changed rows tracking set is cleared programmatically.\n *\n * @category Events\n */\nexport interface ChangedRowsResetDetail<TRow = unknown> {\n /** New (empty) changed rows array after reset. */\n rows: TRow[];\n /** IDs of changed rows (likely empty). */\n ids: string[];\n}\n\n/**\n * Detail for a cell click event.\n * Provides full context about the clicked cell including row data.\n *\n * @category Events\n */\nexport interface CellClickDetail<TRow = unknown> {\n /** Zero-based row index of the clicked cell. */\n rowIndex: number;\n /** Zero-based column index of the clicked cell. */\n colIndex: number;\n /** Field name of the clicked column. */\n field: string;\n /** Cell value at the clicked position. */\n value: unknown;\n /** Full row data object. */\n row: TRow;\n /** The clicked cell element. */\n cellEl: HTMLElement;\n /** The original mouse event. */\n originalEvent: MouseEvent;\n}\n\n/**\n * Detail for a row click event.\n * Provides context about the clicked row.\n *\n * @category Events\n */\nexport interface RowClickDetail<TRow = unknown> {\n /** Zero-based row index of the clicked row. */\n rowIndex: number;\n /** Full row data object. */\n row: TRow;\n /** The clicked row element. */\n rowEl: HTMLElement;\n /** The original mouse event. */\n originalEvent: MouseEvent;\n}\n\n/**\n * Detail for a sort change (direction 0 indicates cleared sort).\n *\n * @category Events\n */\nexport interface SortChangeDetail {\n /** Sorted field key. */\n field: string;\n /** Direction: 1 ascending, -1 descending, 0 cleared. */\n direction: 1 | -1 | 0;\n}\n\n/**\n * Column resize event detail containing final pixel width.\n *\n * @category Events\n */\nexport interface ColumnResizeDetail {\n /** Resized column field key. */\n field: string;\n /** New width in pixels. */\n width: number;\n}\n\n/**\n * Trigger type for cell activation.\n * - `'keyboard'`: Enter key pressed on focused cell\n * - `'pointer'`: Mouse/touch/pen click on cell\n *\n * @category Events\n */\nexport type CellActivateTrigger = 'keyboard' | 'pointer';\n\n/**\n * Fired when a cell is activated by user interaction (Enter key or click).\n * Unified event for both keyboard and pointer activation.\n *\n * @category Events\n */\nexport interface CellActivateDetail<TRow = unknown> {\n /** Zero-based row index of the activated cell. */\n rowIndex: number;\n /** Zero-based column index of the activated cell. */\n colIndex: number;\n /** Field name of the activated column. */\n field: string;\n /** Cell value at the activated position. */\n value: unknown;\n /** Full row data object. */\n row: TRow;\n /** The activated cell element. */\n cellEl: HTMLElement;\n /** What triggered the activation. */\n trigger: CellActivateTrigger;\n /** The original event (KeyboardEvent for keyboard, MouseEvent/PointerEvent for pointer). */\n originalEvent: KeyboardEvent | MouseEvent | PointerEvent;\n}\n\n/**\n * @deprecated Use `CellActivateDetail` instead. Will be removed in next major version.\n * Kept for backwards compatibility.\n *\n * @category Events\n */\nexport interface ActivateCellDetail {\n /** Zero-based row index now focused. */\n row: number;\n /** Zero-based column index now focused. */\n col: number;\n}\n\n/**\n * Event detail for mounting external view renderers.\n *\n * @category Events\n */\nexport interface ExternalMountViewDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: { row: TRow; value: unknown; field: string; column: unknown };\n}\n\n/**\n * Event detail for mounting external editor renderers.\n *\n * @category Events\n */\nexport interface ExternalMountEditorDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: {\n row: TRow;\n value: unknown;\n field: string;\n column: unknown;\n commit: (v: unknown) => void;\n cancel: () => void;\n };\n}\n\n/**\n * Maps event names to their detail payload types.\n *\n * @category Events\n */\nexport interface DataGridEventMap<TRow = unknown> {\n 'cell-click': CellClickDetail<TRow>;\n 'row-click': RowClickDetail<TRow>;\n 'cell-activate': CellActivateDetail<TRow>;\n 'cell-change': CellChangeDetail<TRow>;\n 'cell-commit': CellCommitDetail<TRow>;\n 'row-commit': RowCommitDetail<TRow>;\n 'changed-rows-reset': ChangedRowsResetDetail<TRow>;\n 'mount-external-view': ExternalMountViewDetail<TRow>;\n 'mount-external-editor': ExternalMountEditorDetail<TRow>;\n 'sort-change': SortChangeDetail;\n 'column-resize': ColumnResizeDetail;\n /** @deprecated Use 'cell-activate' instead */\n 'activate-cell': ActivateCellDetail;\n 'column-state-change': GridColumnState;\n}\n\n/**\n * Extracts the event detail type for a given event name.\n *\n * @category Events\n */\nexport type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = DataGridEventMap<TRow>[K];\n\n/**\n * Custom event type for DataGrid events with typed detail payload.\n *\n * @category Events\n */\nexport type DataGridCustomEvent<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = CustomEvent<\n DataGridEventMap<TRow>[K]\n>;\n\n// Internal code now reuses the public ColumnEditorContext; provide alias for backward compatibility\nexport type EditorContext<T = unknown> = ColumnEditorContext<T, unknown>;\n\n/**\n * Template evaluation context for dynamic templates.\n *\n * @category Plugin Development\n */\nexport interface EvalContext {\n value: unknown;\n row: Record<string, unknown> | null;\n}\n// #endregion\n","import type { ColumnConfig, ColumnInternal, ElementWithPart, InternalGrid, PrimitiveColumnType } from '../types';\nimport { FitModeEnum } from '../types';\n\n/** Global DataGridElement class (may or may not be registered) */\ninterface DataGridElementClass {\n getAdapters?: () => Array<{\n canHandle: (el: HTMLElement) => boolean;\n createRenderer: (el: HTMLElement) => ((ctx: unknown) => Node | string | void) | undefined;\n createEditor: (el: HTMLElement) => ((ctx: unknown) => HTMLElement | string) | undefined;\n }>;\n}\n\n/**\n * Parse `<tbw-grid-column>` elements from the host light DOM into column config objects,\n * capturing template elements for later cloning / compilation.\n */\nexport function parseLightDomColumns(host: HTMLElement): ColumnInternal[] {\n const domColumns = Array.from(host.querySelectorAll('tbw-grid-column')) as HTMLElement[];\n return domColumns\n .map((el) => {\n const field = el.getAttribute('field') || '';\n if (!field) return null;\n const rawType = el.getAttribute('type') || undefined;\n const allowedTypes = new Set<PrimitiveColumnType>(['number', 'string', 'date', 'boolean', 'select']);\n const type =\n rawType && allowedTypes.has(rawType as PrimitiveColumnType) ? (rawType as PrimitiveColumnType) : undefined;\n const header = el.getAttribute('header') || undefined;\n const sortable = el.hasAttribute('sortable');\n const editable = el.hasAttribute('editable');\n const config: ColumnInternal = { field, type, header, sortable, editable };\n\n // Parse width attribute (supports px values, percentages, or plain numbers)\n const widthAttr = el.getAttribute('width');\n if (widthAttr) {\n const numericWidth = parseFloat(widthAttr);\n if (!isNaN(numericWidth) && /^\\d+(\\.\\d+)?$/.test(widthAttr.trim())) {\n config.width = numericWidth;\n } else {\n config.width = widthAttr; // e.g. \"100px\", \"20%\", \"1fr\"\n }\n }\n\n // Parse minWidth attribute (numeric only)\n const minWidthAttr = el.getAttribute('minWidth') || el.getAttribute('min-width');\n if (minWidthAttr) {\n const numericMinWidth = parseFloat(minWidthAttr);\n if (!isNaN(numericMinWidth)) {\n config.minWidth = numericMinWidth;\n }\n }\n\n if (el.hasAttribute('resizable')) config.resizable = true;\n if (el.hasAttribute('sizable')) config.resizable = true; // legacy attribute support\n\n // Parse editor and renderer attribute names for programmatic lookup\n const editorName = el.getAttribute('editor');\n const rendererName = el.getAttribute('renderer');\n if (editorName) config.__editorName = editorName;\n if (rendererName) config.__rendererName = rendererName;\n\n // Parse options attribute for select/typeahead: \"value1:Label1,value2:Label2\" or \"value1,value2\"\n const optionsAttr = el.getAttribute('options');\n if (optionsAttr) {\n config.options = optionsAttr.split(',').map((item) => {\n const [value, label] = item.includes(':') ? item.split(':') : [item.trim(), item.trim()];\n return { value: value.trim(), label: label?.trim() || value.trim() };\n });\n }\n const viewTpl = el.querySelector('tbw-grid-column-view');\n const editorTpl = el.querySelector('tbw-grid-column-editor');\n const headerTpl = el.querySelector('tbw-grid-column-header');\n if (viewTpl) config.__viewTemplate = viewTpl as HTMLElement;\n if (editorTpl) config.__editorTemplate = editorTpl as HTMLElement;\n if (headerTpl) config.__headerTemplate = headerTpl as HTMLElement;\n\n // Check if framework adapters can handle template wrapper elements or the column element itself\n // React adapter registers on the column element, Angular uses inner template wrappers\n const DataGridElementClassRef = (globalThis as { DataGridElement?: DataGridElementClass }).DataGridElement;\n const adapters = DataGridElementClassRef?.getAdapters?.() ?? [];\n\n // First check inner view template, then column element itself\n const viewTarget = (viewTpl ?? el) as HTMLElement;\n const viewAdapter = adapters.find((a) => a.canHandle(viewTarget));\n if (viewAdapter) {\n // Only assign if adapter returns a truthy renderer\n // Adapters return undefined when only an editor is registered (no view template)\n const renderer = viewAdapter.createRenderer(viewTarget);\n if (renderer) {\n config.viewRenderer = renderer;\n }\n }\n\n // First check inner editor template, then column element itself\n const editorTarget = (editorTpl ?? el) as HTMLElement;\n const editorAdapter = adapters.find((a) => a.canHandle(editorTarget));\n if (editorAdapter) {\n // Only assign if adapter returns a truthy editor\n const editor = editorAdapter.createEditor(editorTarget);\n if (editor) {\n config.editor = editor;\n }\n }\n\n return config;\n })\n .filter((c): c is ColumnInternal => !!c);\n}\n\n/**\n * Merge programmatic columns with light DOM columns by field name, allowing DOM-provided\n * attributes / templates to supplement (not overwrite) programmatic definitions.\n * Any DOM columns without a programmatic counterpart are appended.\n * When multiple DOM columns exist for the same field (e.g., separate renderer and editor),\n * their properties are merged together.\n */\nexport function mergeColumns(\n programmatic: ColumnConfig[] | undefined,\n dom: ColumnConfig[] | undefined,\n): ColumnInternal[] {\n if ((!programmatic || !programmatic.length) && (!dom || !dom.length)) return [];\n if (!programmatic || !programmatic.length) return (dom || []) as ColumnInternal[];\n if (!dom || !dom.length) return programmatic as ColumnInternal[];\n\n // Build domMap by merging multiple DOM columns with the same field\n // This supports React pattern where renderer and editor are in separate GridColumn elements\n const domMap: Record<string, ColumnInternal> = {};\n (dom as ColumnInternal[]).forEach((c) => {\n const existing = domMap[c.field];\n if (existing) {\n // Merge this column's properties into the existing one\n if (c.header && !existing.header) existing.header = c.header;\n if (c.type && !existing.type) existing.type = c.type;\n if (c.sortable) existing.sortable = true;\n if (c.editable) existing.editable = true;\n if (c.resizable) existing.resizable = true;\n if (c.width != null && existing.width == null) existing.width = c.width;\n if (c.minWidth != null && existing.minWidth == null) existing.minWidth = c.minWidth;\n if (c.__viewTemplate) existing.__viewTemplate = c.__viewTemplate;\n if (c.__editorTemplate) existing.__editorTemplate = c.__editorTemplate;\n if (c.__headerTemplate) existing.__headerTemplate = c.__headerTemplate;\n // Support both 'renderer' alias and 'viewRenderer'\n const cRenderer = c.renderer || c.viewRenderer;\n const existingRenderer = existing.renderer || existing.viewRenderer;\n if (cRenderer && !existingRenderer) {\n existing.viewRenderer = cRenderer;\n if (c.renderer) existing.renderer = cRenderer;\n }\n if (c.editor && !existing.editor) existing.editor = c.editor;\n } else {\n domMap[c.field] = { ...c };\n }\n });\n\n const merged: ColumnInternal[] = (programmatic as ColumnInternal[]).map((c) => {\n const d = domMap[c.field];\n if (!d) return c;\n const m: ColumnInternal = { ...c };\n if (d.header && !m.header) m.header = d.header;\n if (d.type && !m.type) m.type = d.type;\n m.sortable = c.sortable || d.sortable;\n if (c.resizable === true || d.resizable === true) m.resizable = true;\n m.editable = c.editable || d.editable;\n // Merge width/minWidth from DOM if not set programmatically\n if (d.width != null && m.width == null) m.width = d.width;\n if (d.minWidth != null && m.minWidth == null) m.minWidth = d.minWidth;\n if (d.__viewTemplate) m.__viewTemplate = d.__viewTemplate;\n if (d.__editorTemplate) m.__editorTemplate = d.__editorTemplate;\n if (d.__headerTemplate) m.__headerTemplate = d.__headerTemplate;\n // Merge framework adapter renderers/editors from DOM (support both 'renderer' alias and 'viewRenderer')\n const dRenderer = d.renderer || d.viewRenderer;\n const mRenderer = m.renderer || m.viewRenderer;\n if (dRenderer && !mRenderer) {\n m.viewRenderer = dRenderer;\n if (d.renderer) m.renderer = dRenderer;\n }\n if (d.editor && !m.editor) m.editor = d.editor;\n delete domMap[c.field];\n return m;\n });\n Object.keys(domMap).forEach((field) => merged.push(domMap[field]));\n return merged;\n}\n\n/**\n * Safely add a token to an element's `part` attribute (supporting the CSS ::part API)\n * without duplicating values. Falls back to string manipulation if `el.part` API isn't present.\n */\nexport function addPart(el: HTMLElement, token: string): void {\n try {\n (el as ElementWithPart).part?.add?.(token);\n } catch {\n /* empty */\n }\n const existing = el.getAttribute('part');\n if (!existing) el.setAttribute('part', token);\n else if (!existing.split(/\\s+/).includes(token)) el.setAttribute('part', existing + ' ' + token);\n}\n\n/**\n * Measure rendered header + visible cell content to assign initial pixel widths\n * to columns when in `content` fit mode. Runs only once unless fit mode changes.\n */\nexport function autoSizeColumns(grid: InternalGrid): void {\n const mode = grid.effectiveConfig?.fitMode || grid.fitMode || FitModeEnum.STRETCH;\n // Run for both stretch (to derive baseline pixel widths before fr distribution) and fixed.\n if (mode !== FitModeEnum.STRETCH && mode !== FitModeEnum.FIXED) return;\n if (grid.__didInitialAutoSize) return;\n if (!(grid as unknown as HTMLElement).isConnected) return;\n const headerCells = Array.from(grid._headerRowEl?.children || []) as HTMLElement[];\n if (!headerCells.length) return;\n let changed = false;\n grid._visibleColumns.forEach((col: ColumnInternal, i: number) => {\n if (col.width) return;\n const headerCell = headerCells[i];\n let max = headerCell ? headerCell.scrollWidth : 0;\n for (const rowEl of grid._rowPool) {\n const cell = rowEl.children[i] as HTMLElement | undefined;\n if (cell) {\n const w = cell.scrollWidth;\n if (w > max) max = w;\n }\n }\n if (max > 0) {\n col.width = max + 2;\n col.__autoSized = true;\n changed = true;\n }\n });\n if (changed) updateTemplate(grid);\n grid.__didInitialAutoSize = true;\n}\n\n/**\n * Compute and apply the CSS grid template string that drives column layout.\n * Uses `fr` units for flexible (non user-resized) columns in stretch mode, otherwise\n * explicit pixel widths or auto sizing.\n */\nexport function updateTemplate(grid: InternalGrid): void {\n // Modes:\n // - 'stretch': columns with explicit width use that width; columns without width are flexible\n // Uses minmax(minWidth, maxWidth) when both min/max specified (bounded flex)\n // Uses minmax(minWidth, 1fr) when only min specified (grows unbounded)\n // Uses minmax(defaultMin, maxWidth) when only max specified (capped growth)\n // - 'fixed': columns with explicit width use that width; columns without width use max-content\n const mode = grid.effectiveConfig?.fitMode || grid.fitMode || FitModeEnum.STRETCH;\n\n if (mode === FitModeEnum.STRETCH) {\n grid._gridTemplate = grid._visibleColumns\n .map((c: ColumnInternal) => {\n if (c.width) return `${c.width}px`;\n // Flexible column: pure 1fr unless minWidth specified\n const min = c.minWidth;\n return min != null ? `minmax(${min}px, 1fr)` : '1fr';\n })\n .join(' ')\n .trim();\n } else {\n // fixed mode: explicit pixel widths or max-content for content-based sizing\n grid._gridTemplate = grid._visibleColumns\n .map((c: ColumnInternal) => (c.width ? `${c.width}px` : 'max-content'))\n .join(' ');\n }\n (grid as unknown as HTMLElement).style.setProperty('--tbw-column-template', grid._gridTemplate);\n}\n","import type { ColumnConfigMap, ColumnType, InferredColumnResult, PrimitiveColumnType } from '../types';\n/**\n * Best-effort primitive type inference for a cell value used during automatic column generation.\n */\nfunction inferType(value: any): PrimitiveColumnType {\n if (value == null) return 'string';\n if (typeof value === 'number') return 'number';\n if (typeof value === 'boolean') return 'boolean';\n if (value instanceof Date) return 'date';\n if (typeof value === 'string' && /\\d{4}-\\d{2}-\\d{2}/.test(value) && !isNaN(Date.parse(value))) return 'date';\n return 'string';\n}\n/**\n * Derive column definitions from provided configuration or by inspecting the first row of data.\n * Returns both the resolved column array and a field->type map.\n */\nexport function inferColumns<TRow extends Record<string, unknown>>(\n rows: TRow[],\n provided?: ColumnConfigMap<TRow>,\n): InferredColumnResult<TRow> {\n if (provided && provided.length) {\n const typeMap: Record<string, ColumnType> = {};\n provided.forEach((col) => {\n if (col.type) typeMap[col.field] = col.type;\n });\n return { columns: provided, typeMap };\n }\n const sample = rows[0] || ({} as TRow);\n const columns: ColumnConfigMap<TRow> = Object.keys(sample).map((k) => {\n const v = (sample as Record<string, unknown>)[k];\n const type = inferType(v);\n return { field: k as keyof TRow & string, header: k.charAt(0).toUpperCase() + k.slice(1), type };\n });\n const typeMap: Record<string, ColumnType> = {};\n columns.forEach((c) => {\n typeMap[c.field] = c.type || 'string';\n });\n return { columns, typeMap };\n}\nexport { inferType };\n","// Centralized template expression evaluation & sanitization utilities.\n// Responsible for safely interpolating {{ }} expressions while blocking\n// access to dangerous globals / reflective capabilities.\nimport type { CompiledViewFunction, EvalContext } from '../types';\n\nconst EXPR_RE = /{{\\s*([^}]+)\\s*}}/g;\nconst EMPTY_SENTINEL = '__DG_EMPTY__';\nconst SAFE_EXPR = /^[\\w$. '?+\\-*/%:()!<>=,&|]+$/;\nconst FORBIDDEN =\n /__(proto|defineGetter|defineSetter)|constructor|window|globalThis|global|process|Function|import|eval|Reflect|Proxy|Error|arguments|document|location|cookie|localStorage|sessionStorage|indexedDB|fetch|XMLHttpRequest|WebSocket|Worker|SharedWorker|ServiceWorker|opener|parent|top|frames|self|this\\b/;\n\n// #region HTML Sanitization\n\n/**\n * Escape a plain text string for safe insertion into HTML.\n * Converts special HTML characters to their entity equivalents.\n *\n * @param text - Plain text string to escape\n * @returns HTML-safe string\n */\nexport function escapeHtml(text: string): string {\n if (!text || typeof text !== 'string') return '';\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Tags that are considered dangerous and will be completely removed.\n * These can execute scripts, load external resources, or manipulate the page.\n */\nconst DANGEROUS_TAGS = new Set([\n 'script',\n 'iframe',\n 'object',\n 'embed',\n 'form',\n 'input',\n 'button',\n 'textarea',\n 'select',\n 'link',\n 'meta',\n 'base',\n 'style',\n 'template',\n 'slot',\n 'portal',\n 'frame',\n 'frameset',\n 'applet',\n 'noscript',\n 'noembed',\n 'plaintext',\n 'xmp',\n 'listing',\n]);\n\n/**\n * Attributes that are considered dangerous - event handlers and data loading.\n */\nconst DANGEROUS_ATTR_PATTERN = /^on\\w+$/i;\n\n/**\n * Attributes that can contain URLs which might be javascript: or data: URIs.\n */\nconst URL_ATTRS = new Set(['href', 'src', 'action', 'formaction', 'data', 'srcdoc', 'xlink:href', 'poster', 'srcset']);\n\n/**\n * Protocol patterns that are dangerous in URLs.\n */\nconst DANGEROUS_URL_PROTOCOL = /^\\s*(javascript|vbscript|data|blob):/i;\n\n/**\n * Sanitize an HTML string by removing dangerous tags and attributes.\n * This is a defense-in-depth measure for content rendered via innerHTML.\n *\n * @param html - Raw HTML string to sanitize\n * @returns Sanitized HTML string safe for innerHTML\n */\nexport function sanitizeHTML(html: string): string {\n if (!html || typeof html !== 'string') return '';\n\n // Fast path: if no HTML tags at all, return as-is (already safe)\n if (html.indexOf('<') === -1) return html;\n\n const template = document.createElement('template');\n template.innerHTML = html;\n\n sanitizeNode(template.content);\n\n return template.innerHTML;\n}\n\n/**\n * Recursively sanitize a DOM node tree.\n */\nfunction sanitizeNode(root: DocumentFragment | Element): void {\n const toRemove: Element[] = [];\n\n // Use querySelectorAll to find all elements, then filter\n const elements = root.querySelectorAll('*');\n\n for (const el of elements) {\n const tagName = el.tagName.toLowerCase();\n\n // Check if tag is dangerous\n if (DANGEROUS_TAGS.has(tagName)) {\n toRemove.push(el);\n continue;\n }\n\n // SVG elements need special handling - they can contain script-like behavior\n if (tagName === 'svg' || el.namespaceURI === 'http://www.w3.org/2000/svg') {\n // Remove entire SVG if it has any suspicious attributes\n const hasDangerousContent = Array.from(el.attributes).some(\n (attr) => DANGEROUS_ATTR_PATTERN.test(attr.name) || attr.name === 'href' || attr.name === 'xlink:href',\n );\n if (hasDangerousContent) {\n toRemove.push(el);\n continue;\n }\n }\n\n // Check and remove dangerous attributes\n const attrsToRemove: string[] = [];\n for (const attr of el.attributes) {\n const attrName = attr.name.toLowerCase();\n\n // Event handlers (onclick, onerror, onload, etc.)\n if (DANGEROUS_ATTR_PATTERN.test(attrName)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // URL attributes with dangerous protocols\n if (URL_ATTRS.has(attrName) && DANGEROUS_URL_PROTOCOL.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // style attribute can contain expressions (IE) or url() with javascript:\n if (attrName === 'style' && /expression\\s*\\(|javascript:|behavior\\s*:/i.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n }\n\n attrsToRemove.forEach((name) => el.removeAttribute(name));\n }\n\n // Remove dangerous elements (do this after iteration to avoid modifying during traversal)\n toRemove.forEach((el) => el.remove());\n}\n\n// #endregion\n\nexport function evalTemplateString(raw: string, ctx: EvalContext): string {\n if (!raw || raw.indexOf('{{') === -1) return raw; // fast path (no expressions)\n const parts: { expr: string; result: string }[] = [];\n const evaluated = raw.replace(EXPR_RE, (_m, expr) => {\n const res = evalSingle(expr, ctx);\n parts.push({ expr: expr.trim(), result: res });\n return res;\n });\n const finalStr = postProcess(evaluated);\n // If every part evaluated to EMPTY_SENTINEL we treat this as intentionally blank.\n // If any expression was blocked due to forbidden token (EMPTY_SENTINEL) we *still* only output ''\n // but do not escalate to BLOCKED_SENTINEL unless the original contained explicit forbidden tokens.\n const allEmpty = parts.length && parts.every((p) => p.result === '' || p.result === EMPTY_SENTINEL);\n const hadForbidden = /Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(raw);\n if (hadForbidden || allEmpty) return '';\n return finalStr;\n}\n\nfunction evalSingle(expr: string, ctx: EvalContext): string {\n expr = (expr || '').trim();\n if (!expr) return EMPTY_SENTINEL;\n if (/\\b(Reflect|Proxy|ownKeys)\\b/.test(expr)) return EMPTY_SENTINEL;\n if (expr === 'value') return ctx.value == null ? EMPTY_SENTINEL : String(ctx.value);\n if (expr.startsWith('row.') && !/[()?]/.test(expr) && !expr.includes(':')) {\n const key = expr.slice(4);\n const v = ctx.row ? ctx.row[key] : undefined;\n return v == null ? EMPTY_SENTINEL : String(v);\n }\n if (expr.length > 80) return EMPTY_SENTINEL;\n if (!SAFE_EXPR.test(expr) || FORBIDDEN.test(expr)) return EMPTY_SENTINEL;\n const dotChain = expr.match(/\\./g);\n if (dotChain && dotChain.length > 1) return EMPTY_SENTINEL;\n try {\n const fn = new Function('value', 'row', `return (${expr});`);\n const out = fn(ctx.value, ctx.row);\n const str = out == null ? '' : String(out);\n if (/Reflect|Proxy|ownKeys/.test(str)) return EMPTY_SENTINEL;\n return str || EMPTY_SENTINEL;\n } catch {\n return EMPTY_SENTINEL;\n }\n}\n\nfunction postProcess(s: string): string {\n if (!s) return s;\n return s\n .replace(new RegExp(EMPTY_SENTINEL, 'g'), '')\n .replace(/Reflect\\.[^<>{}\\s]+/g, '')\n .replace(/\\bProxy\\b/g, '')\n .replace(/ownKeys\\([^)]*\\)/g, '');\n}\n\nexport function finalCellScrub(cell: HTMLElement): void {\n if (/Reflect|Proxy|ownKeys/.test(cell.textContent || '')) {\n Array.from(cell.childNodes).forEach((n) => {\n if (n.nodeType === Node.TEXT_NODE && /Reflect|Proxy|ownKeys/.test(n.textContent || '')) n.textContent = '';\n });\n if (/Reflect|Proxy|ownKeys/.test(cell.textContent || '')) {\n // If remaining content still includes forbidden tokens inside element nodes, clear children entirely.\n const still = /Reflect|Proxy|ownKeys/.test(cell.textContent || '');\n if (still) {\n while (cell.firstChild) cell.removeChild(cell.firstChild);\n }\n cell.textContent = (cell.textContent || '').replace(/Reflect|Proxy|ownKeys/g, '');\n }\n if ((cell.textContent || '').trim().length === 0) cell.textContent = '';\n }\n}\n\nexport function compileTemplate(raw: string) {\n const forceBlank = /Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(raw);\n const fn = ((ctx: EvalContext) => {\n if (forceBlank) return '';\n const out = evalTemplateString(raw, ctx);\n return out;\n }) as CompiledViewFunction;\n fn.__blocked = forceBlank;\n return fn;\n}\n","/**\n * ConfigManager - Unified Configuration Lifecycle Management\n *\n * Manages all configuration concerns for the grid:\n * - Source collection (gridConfig, columns, attributes, Light DOM)\n * - Two-layer config (frozen original + mutable effective)\n * - State persistence (collect/apply/reset via diff)\n * - Change notification for re-rendering\n *\n * This is an internal module - grid.ts delegates to ConfigManager\n * but the public API remains unchanged.\n */\n\nimport type { BaseGridPlugin } from '../plugin';\nimport type {\n ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n ColumnSortState,\n ColumnState,\n FitMode,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n ToolbarContentDefinition,\n ToolPanelDefinition,\n} from '../types';\nimport { mergeColumns, parseLightDomColumns } from './columns';\nimport { inferColumns } from './inference';\nimport { compileTemplate } from './sanitize';\n\n/** Debounce timeout for state change events */\nconst STATE_CHANGE_DEBOUNCE_MS = 100;\n\n/**\n * Callbacks for ConfigManager to notify the grid of changes.\n */\nexport interface ConfigManagerCallbacks<T> {\n /** Get current rows for column inference */\n getRows: () => T[];\n /** Get current sort state */\n getSortState: () => { field: string; direction: 1 | -1 } | null;\n /** Set sort state */\n setSortState: (state: { field: string; direction: 1 | -1 } | null) => void;\n /** Notify that config changed and re-render is needed */\n onConfigChange: () => void;\n /** Emit a custom event */\n emit: (eventName: string, detail: unknown) => void;\n /** Clear row pool for visibility changes */\n clearRowPool: () => void;\n /** Run setup after state changes */\n setup: () => void;\n /** Render header */\n renderHeader: () => void;\n /** Update column template */\n updateTemplate: () => void;\n /** Refresh virtual window */\n refreshVirtualWindow: () => void;\n /** Get virtualization state for row height application */\n getVirtualization: () => { rowHeight: number };\n /** Set row height on virtualization state */\n setRowHeight: (height: number) => void;\n /** Apply animation config to host element */\n applyAnimationConfig: (config: GridConfig<T>) => void;\n /** Get light DOM title from shell state (synced from parseLightDomShell) */\n getShellLightDomTitle: () => string | null;\n /** Get registered tool panels from shell state */\n getShellToolPanels: () => Map<string, ToolPanelDefinition>;\n /** Get registered header contents from shell state */\n getShellHeaderContents: () => Map<string, HeaderContentDefinition>;\n /** Get registered toolbar contents from shell state */\n getShellToolbarContents: () => Map<string, ToolbarContentDefinition>;\n /** Get light DOM header content elements */\n getShellLightDomHeaderContent: () => HTMLElement[];\n /** Get whether a tool buttons container was found in light DOM */\n getShellHasToolButtonsContainer: () => boolean;\n}\n\n/**\n * ConfigManager handles all configuration lifecycle for the grid.\n *\n * Manages:\n * - Source collection (gridConfig, columns, attributes, Light DOM)\n * - Effective config (merged from all sources)\n * - State persistence (collectState, applyState, resetState)\n * - Column visibility and ordering\n */\nexport class ConfigManager<T = unknown> {\n // ============================================================================\n // Sources (raw input from user)\n // ============================================================================\n #gridConfig?: GridConfig<T>;\n #columns?: ColumnConfig<T>[] | ColumnConfigMap<T>;\n #fitMode?: FitMode;\n\n // Light DOM cache\n #lightDomColumnsCache?: ColumnInternal<T>[];\n #originalColumnNodes?: HTMLElement[];\n\n // ============================================================================\n // Two-Layer Config Architecture\n // ============================================================================\n /**\n * Original config (frozen) - Built from sources, never mutated.\n * This is the \"canonical\" config that sources compile into.\n * Used as the reset point for effectiveConfig.\n */\n #originalConfig: GridConfig<T> = {};\n\n /**\n * Effective config (mutable) - Cloned from original, runtime mutations here.\n * This is what rendering reads from.\n * Runtime changes: hidden, width, sort order, column order.\n */\n #effectiveConfig: GridConfig<T> = {};\n\n // ============================================================================\n // State Tracking\n // ============================================================================\n #sourcesChanged = true;\n #changeListeners: Array<() => void> = [];\n #lightDomObserver?: MutationObserver;\n #stateChangeTimeoutId?: ReturnType<typeof setTimeout>;\n #initialColumnState?: GridColumnState;\n #callbacks: ConfigManagerCallbacks<T>;\n\n // Shell state (Light DOM title)\n #lightDomTitle?: string;\n\n constructor(callbacks: ConfigManagerCallbacks<T>) {\n this.#callbacks = callbacks;\n }\n\n // ============================================================================\n // Getters for Grid Access\n // ============================================================================\n\n /** Get the frozen original config (compiled from sources, immutable) */\n get original(): GridConfig<T> {\n return this.#originalConfig;\n }\n\n /** Get the mutable effective config (current runtime state) */\n get effective(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n\n /** Get columns from effective config */\n get columns(): ColumnInternal<T>[] {\n return (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n }\n\n /** Set columns on effective config */\n set columns(value: ColumnInternal<T>[]) {\n this.#effectiveConfig.columns = value as ColumnConfig<T>[];\n }\n\n /** Get light DOM columns cache */\n get lightDomColumnsCache(): ColumnInternal<T>[] | undefined {\n return this.#lightDomColumnsCache;\n }\n\n /** Set light DOM columns cache */\n set lightDomColumnsCache(value: ColumnInternal<T>[] | undefined) {\n this.#lightDomColumnsCache = value;\n }\n\n /** Get original column nodes */\n get originalColumnNodes(): HTMLElement[] | undefined {\n return this.#originalColumnNodes;\n }\n\n /** Set original column nodes */\n set originalColumnNodes(value: HTMLElement[] | undefined) {\n this.#originalColumnNodes = value;\n }\n\n /** Get light DOM title */\n get lightDomTitle(): string | undefined {\n return this.#lightDomTitle;\n }\n\n /** Set light DOM title */\n set lightDomTitle(value: string | undefined) {\n this.#lightDomTitle = value;\n }\n\n /** Get initial column state */\n get initialColumnState(): GridColumnState | undefined {\n return this.#initialColumnState;\n }\n\n /** Set initial column state */\n set initialColumnState(value: GridColumnState | undefined) {\n this.#initialColumnState = value;\n }\n\n // ============================================================================\n // Source Management\n // ============================================================================\n\n /**\n * Check if sources have changed since last merge.\n */\n get sourcesChanged(): boolean {\n return this.#sourcesChanged;\n }\n\n /**\n * Mark that sources have changed and need re-merging.\n * Call this when external state (shell maps, etc.) that feeds into\n * collectAllSources() has been updated.\n */\n markSourcesChanged(): void {\n this.#sourcesChanged = true;\n }\n\n // ============================================================================\n // Source Setters (mark dirty)\n // ============================================================================\n\n /** Set gridConfig source */\n setGridConfig(config: GridConfig<T> | undefined): void {\n this.#gridConfig = config;\n this.#sourcesChanged = true;\n // Clear light DOM cache for framework async content\n this.#lightDomColumnsCache = undefined;\n }\n\n /** Get the raw gridConfig source */\n getGridConfig(): GridConfig<T> | undefined {\n return this.#gridConfig;\n }\n\n /** Set columns source */\n setColumns(columns: ColumnConfig<T>[] | ColumnConfigMap<T> | undefined): void {\n this.#columns = columns;\n this.#sourcesChanged = true;\n }\n\n /** Get the raw columns source */\n getColumns(): ColumnConfig<T>[] | ColumnConfigMap<T> | undefined {\n return this.#columns;\n }\n\n /** Set fitMode source */\n setFitMode(mode: FitMode | undefined): void {\n this.#fitMode = mode;\n this.#sourcesChanged = true;\n }\n\n /** Get the raw fitMode source */\n getFitMode(): FitMode | undefined {\n return this.#fitMode;\n }\n\n // ============================================================================\n // Config Lifecycle\n // ============================================================================\n\n /**\n * Merge all sources into effective config.\n * Also applies post-merge operations (rowHeight, fixed mode widths, animation).\n *\n * Called by RenderScheduler's mergeConfig phase.\n *\n * Two-layer architecture:\n * 1. Sources → #originalConfig (frozen, immutable)\n * 2. Clone → #effectiveConfig (mutable, runtime changes)\n *\n * When sources change, both layers are rebuilt.\n * When sources haven't changed AND columns exist, this is a no-op.\n * Runtime mutations only affect effectiveConfig.\n * resetState() clones originalConfig back to effectiveConfig.\n */\n merge(): void {\n // Only rebuild when sources have actually changed.\n // Exception: always rebuild if we don't have columns yet (inference may be needed)\n const hasColumns = (this.#effectiveConfig.columns?.length ?? 0) > 0;\n if (!this.#sourcesChanged && hasColumns) {\n return; // effectiveConfig is already valid\n }\n\n // Build config from all sources\n const base = this.#collectAllSources();\n\n // Mark sources as processed\n this.#sourcesChanged = false;\n\n // Freeze as the new original config (immutable reference point)\n this.#originalConfig = base;\n Object.freeze(this.#originalConfig);\n if (this.#originalConfig.columns) {\n // Deep freeze columns array (but not the column objects themselves,\n // as we need effectiveConfig columns to be mutable)\n Object.freeze(this.#originalConfig.columns);\n }\n\n // Clone to effective config (mutable copy for runtime changes)\n this.#effectiveConfig = this.#cloneConfig(this.#originalConfig);\n\n // Apply post-merge operations to effectiveConfig\n this.#applyPostMergeOperations();\n }\n\n /**\n * Deep clone a config object, handling functions (renderers, editors).\n * Uses structuredClone where possible, with fallback for function properties.\n */\n #cloneConfig(config: GridConfig<T>): GridConfig<T> {\n // Can't use structuredClone because config may contain functions\n const clone: GridConfig<T> = { ...config };\n\n // Deep clone columns (they may have runtime-mutable state)\n if (config.columns) {\n clone.columns = config.columns.map((col) => ({ ...col }));\n }\n\n // Deep clone shell if present\n if (config.shell) {\n clone.shell = {\n ...config.shell,\n header: config.shell.header ? { ...config.shell.header } : undefined,\n toolPanel: config.shell.toolPanel ? { ...config.shell.toolPanel } : undefined,\n toolPanels: config.shell.toolPanels?.map((p) => ({ ...p })),\n headerContents: config.shell.headerContents?.map((h) => ({ ...h })),\n };\n }\n\n return clone;\n }\n\n /**\n * Apply operations that depend on the merged effective config.\n * These were previously in grid.ts #mergeEffectiveConfig().\n */\n #applyPostMergeOperations(): void {\n const config = this.#effectiveConfig;\n\n // Apply rowHeight from config if specified\n if (config.rowHeight && config.rowHeight > 0) {\n this.#callbacks.setRowHeight(config.rowHeight);\n }\n\n // If fixed mode and width not specified: assign default 80px\n if (config.fitMode === 'fixed') {\n const columns = this.columns;\n columns.forEach((c) => {\n if (c.width == null) c.width = 80;\n });\n }\n\n // Apply animation configuration to host element\n this.#callbacks.applyAnimationConfig(config);\n }\n\n /**\n * Collect all sources into a single config object.\n * This is the core merge logic extracted from grid.ts #mergeEffectiveConfig.\n *\n * Collects all sources into a canonical config object.\n * This becomes the frozen #originalConfig.\n *\n * Sources (in order of precedence, low to high):\n * 1. gridConfig.columns\n * 2. Light DOM columns (merged with config columns)\n * 3. columns prop (overrides if set)\n * 4. Inferred columns (if still empty)\n *\n * Runtime state (hidden, width) is NOT preserved here - that's in effectiveConfig.\n */\n #collectAllSources(): GridConfig<T> {\n const base: GridConfig<T> = this.#gridConfig ? { ...this.#gridConfig } : {};\n const configColumns: ColumnConfig<T>[] = Array.isArray(base.columns) ? [...base.columns] : [];\n\n // Light DOM cached parse - clone to avoid mutation\n const domCols: ColumnConfig<T>[] = (this.#lightDomColumnsCache ?? []).map((c) => ({\n ...c,\n })) as ColumnConfig<T>[];\n\n // Use mergeColumns to combine config columns with light DOM columns\n // This handles all the complex merge logic including templates and renderers\n let columns: ColumnInternal<T>[] = mergeColumns(\n configColumns as ColumnInternal<T>[],\n domCols as ColumnInternal<T>[],\n ) as ColumnInternal<T>[];\n\n // Columns prop highest structural precedence (overrides merged result)\n if (this.#columns && (this.#columns as ColumnConfig<T>[]).length) {\n // When columns prop is set, merge with light DOM columns for renderers/templates\n columns = mergeColumns(\n this.#columns as ColumnInternal<T>[],\n domCols as ColumnInternal<T>[],\n ) as ColumnInternal<T>[];\n }\n\n // Inference if still empty\n const rows = this.#callbacks.getRows();\n if (columns.length === 0 && rows.length) {\n const result = inferColumns(rows as Record<string, unknown>[]);\n columns = result.columns as ColumnInternal<T>[];\n }\n\n if (columns.length) {\n // Apply per-column defaults\n columns.forEach((c) => {\n if (c.sortable === undefined) c.sortable = true;\n if (c.resizable === undefined) c.resizable = true;\n if (c.__originalWidth === undefined && typeof c.width === 'number') {\n c.__originalWidth = c.width;\n }\n });\n\n // Compile inline templates (from light DOM <template> elements)\n columns.forEach((c) => {\n if (c.__viewTemplate && !c.__compiledView) {\n c.__compiledView = compileTemplate((c.__viewTemplate as HTMLElement).innerHTML);\n }\n if (c.__editorTemplate && !c.__compiledEditor) {\n c.__compiledEditor = compileTemplate(c.__editorTemplate.innerHTML);\n }\n });\n\n base.columns = columns as ColumnConfig<T>[];\n }\n\n // Individual prop overrides\n if (this.#fitMode) base.fitMode = this.#fitMode;\n if (!base.fitMode) base.fitMode = 'stretch';\n\n // ========================================================================\n // Merge shell configuration from ShellState into effectiveConfig.shell\n // This ensures a single source of truth for all shell config\n // ========================================================================\n this.#mergeShellConfig(base);\n\n // Store columnState from gridConfig if not already set\n if (base.columnState && !this.#initialColumnState) {\n this.#initialColumnState = base.columnState;\n }\n\n return base;\n }\n\n /**\n * Merge shell state into base config's shell property.\n * Ensures effectiveConfig.shell is the single source of truth.\n *\n * IMPORTANT: This method must NOT mutate the original gridConfig.\n * We shallow-clone the shell hierarchy to avoid side effects.\n */\n #mergeShellConfig(base: GridConfig<T>): void {\n // Clone shell hierarchy to avoid mutating original gridConfig\n // base.shell may still reference this.#gridConfig.shell, so we need fresh objects\n base.shell = base.shell ? { ...base.shell } : {};\n base.shell.header = base.shell.header ? { ...base.shell.header } : {};\n\n // Sync light DOM title\n const shellLightDomTitle = this.#callbacks.getShellLightDomTitle();\n if (shellLightDomTitle) {\n this.#lightDomTitle = shellLightDomTitle;\n }\n if (this.#lightDomTitle && !base.shell.header.title) {\n base.shell.header.title = this.#lightDomTitle;\n }\n\n // Sync light DOM header content elements\n const lightDomHeaderContent = this.#callbacks.getShellLightDomHeaderContent();\n if (lightDomHeaderContent?.length > 0) {\n base.shell.header.lightDomContent = lightDomHeaderContent;\n }\n\n // Sync hasToolButtonsContainer from shell state\n if (this.#callbacks.getShellHasToolButtonsContainer()) {\n base.shell.header.hasToolButtonsContainer = true;\n }\n\n // Sync tool panels (from plugins + API + Light DOM)\n const toolPanelsMap = this.#callbacks.getShellToolPanels();\n if (toolPanelsMap.size > 0) {\n const panels = Array.from(toolPanelsMap.values());\n // Sort by order (lower = first, default 100)\n panels.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n base.shell.toolPanels = panels;\n }\n\n // Sync header contents (from plugins + API)\n const headerContentsMap = this.#callbacks.getShellHeaderContents();\n if (headerContentsMap.size > 0) {\n const contents = Array.from(headerContentsMap.values());\n // Sort by order\n contents.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n base.shell.headerContents = contents;\n }\n\n // Sync toolbar contents (from API - config contents are already in base.shell.header.toolbarContents)\n // We need to merge config contents (from gridConfig) with API contents (from registerToolbarContent)\n // API contents can be added/removed dynamically, so we need to rebuild from current state each time\n const toolbarContentsMap = this.#callbacks.getShellToolbarContents();\n const apiContents = Array.from(toolbarContentsMap.values());\n\n // Get ORIGINAL config contents (from gridConfig, not from previous merges)\n // We use a fresh read from gridConfig to avoid accumulating stale API contents\n const originalConfigContents = this.#gridConfig?.shell?.header?.toolbarContents ?? [];\n\n // Merge: config contents + API contents (config takes precedence by id)\n const configIds = new Set(originalConfigContents.map((c) => c.id));\n const mergedContents = [...originalConfigContents];\n for (const content of apiContents) {\n if (!configIds.has(content.id)) {\n mergedContents.push(content);\n }\n }\n\n // Sort by order\n mergedContents.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n base.shell.header.toolbarContents = mergedContents;\n }\n\n // ============================================================================\n // State Persistence (Replaces column-state.ts)\n // ============================================================================\n\n /**\n * Collect current column state by diffing original vs effective.\n * Returns only the changes from the original configuration.\n */\n collectState(plugins: BaseGridPlugin[]): GridColumnState {\n const columns = this.columns;\n const sortStates = this.#getSortState();\n\n return {\n columns: columns.map((col, index) => {\n const state: ColumnState = {\n field: col.field,\n order: index,\n visible: !col.hidden,\n };\n\n // Include width if set\n const internalCol = col as ColumnInternal<T>;\n if (internalCol.__renderedWidth !== undefined) {\n state.width = internalCol.__renderedWidth;\n } else if (col.width !== undefined) {\n state.width = typeof col.width === 'string' ? parseFloat(col.width) : col.width;\n }\n\n // Include sort state\n const sortState = sortStates.get(col.field);\n if (sortState) {\n state.sort = sortState;\n }\n\n // Collect from plugins\n for (const plugin of plugins) {\n if (plugin.getColumnState) {\n const pluginState = plugin.getColumnState(col.field);\n if (pluginState) {\n Object.assign(state, pluginState);\n }\n }\n }\n\n return state;\n }),\n };\n }\n\n /**\n * Apply column state to the grid.\n */\n applyState(state: GridColumnState, plugins: BaseGridPlugin[]): void {\n if (!state.columns || state.columns.length === 0) return;\n\n const allColumns = this.columns;\n const stateMap = new Map(state.columns.map((s) => [s.field, s]));\n\n // Apply width and visibility\n const updatedColumns = allColumns.map((col) => {\n const s = stateMap.get(col.field);\n if (!s) return col;\n\n const updated: ColumnInternal<T> = { ...col };\n\n if (s.width !== undefined) {\n updated.width = s.width;\n updated.__renderedWidth = s.width;\n }\n\n if (s.visible !== undefined) {\n updated.hidden = !s.visible;\n }\n\n return updated;\n });\n\n // Reorder columns\n updatedColumns.sort((a, b) => {\n const orderA = stateMap.get(a.field)?.order ?? Infinity;\n const orderB = stateMap.get(b.field)?.order ?? Infinity;\n return orderA - orderB;\n });\n\n this.columns = updatedColumns;\n\n // Apply sort state\n const sortedByPriority = state.columns\n .filter((s) => s.sort !== undefined)\n .sort((a, b) => (a.sort?.priority ?? 0) - (b.sort?.priority ?? 0));\n\n if (sortedByPriority.length > 0) {\n const primarySort = sortedByPriority[0];\n if (primarySort.sort) {\n this.#callbacks.setSortState({\n field: primarySort.field,\n direction: primarySort.sort.direction === 'asc' ? 1 : -1,\n });\n }\n } else {\n this.#callbacks.setSortState(null);\n }\n\n // Let plugins apply their state\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n for (const colState of state.columns) {\n plugin.applyColumnState(colState.field, colState);\n }\n }\n }\n }\n\n /**\n * Reset state to original configuration.\n *\n * Two-layer architecture: Clones #originalConfig back to #effectiveConfig.\n * This discards all runtime changes (hidden, width, order) and restores\n * the state to what was compiled from sources.\n */\n resetState(plugins: BaseGridPlugin[]): void {\n // Clear initial state\n this.#initialColumnState = undefined;\n\n // Reset sort state\n this.#callbacks.setSortState(null);\n\n // Clone original config back to effective (discards all runtime changes)\n this.#effectiveConfig = this.#cloneConfig(this.#originalConfig);\n\n // Apply post-merge operations (rowHeight, fixed mode widths, animation)\n this.#applyPostMergeOperations();\n\n // Notify plugins to reset\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n for (const col of this.columns) {\n plugin.applyColumnState(col.field, {\n field: col.field,\n order: 0,\n visible: true,\n });\n }\n }\n }\n\n // Request state change notification\n this.requestStateChange(plugins);\n }\n\n /**\n * Get sort state as a map.\n */\n #getSortState(): Map<string, ColumnSortState> {\n const sortMap = new Map<string, ColumnSortState>();\n const sortState = this.#callbacks.getSortState();\n\n if (sortState) {\n sortMap.set(sortState.field, {\n direction: sortState.direction === 1 ? 'asc' : 'desc',\n priority: 0,\n });\n }\n\n return sortMap;\n }\n\n /**\n * Request a debounced state change event.\n */\n requestStateChange(plugins: BaseGridPlugin[]): void {\n if (this.#stateChangeTimeoutId) {\n clearTimeout(this.#stateChangeTimeoutId);\n }\n\n this.#stateChangeTimeoutId = setTimeout(() => {\n this.#stateChangeTimeoutId = undefined;\n const state = this.collectState(plugins);\n this.#callbacks.emit('column-state-change', state);\n }, STATE_CHANGE_DEBOUNCE_MS);\n }\n\n // ============================================================================\n // Column Visibility API\n // ============================================================================\n\n /**\n * Set the visibility of a column.\n * @returns true if visibility changed, false otherwise\n */\n setColumnVisible(field: string, visible: boolean): boolean {\n const allCols = this.columns;\n const col = allCols.find((c) => c.field === field);\n\n if (!col) return false;\n if (!visible && col.lockVisible) return false;\n\n // Ensure at least one column remains visible\n if (!visible) {\n const remainingVisible = allCols.filter((c) => !c.hidden && c.field !== field).length;\n if (remainingVisible === 0) return false;\n }\n\n const wasHidden = !!col.hidden;\n if (wasHidden === !visible) return false; // No change\n\n col.hidden = !visible;\n\n this.#callbacks.emit('column-visibility', {\n field,\n visible,\n visibleColumns: allCols.filter((c) => !c.hidden).map((c) => c.field),\n });\n\n this.#callbacks.clearRowPool();\n this.#callbacks.setup();\n\n return true;\n }\n\n /**\n * Toggle column visibility.\n */\n toggleColumnVisibility(field: string): boolean {\n const col = this.columns.find((c) => c.field === field);\n return col ? this.setColumnVisible(field, !!col.hidden) : false;\n }\n\n /**\n * Check if a column is visible.\n */\n isColumnVisible(field: string): boolean {\n const col = this.columns.find((c) => c.field === field);\n return col ? !col.hidden : false;\n }\n\n /**\n * Show all columns.\n */\n showAllColumns(): void {\n const allCols = this.columns;\n if (!allCols.some((c) => c.hidden)) return;\n\n allCols.forEach((c) => (c.hidden = false));\n\n this.#callbacks.emit('column-visibility', {\n visibleColumns: allCols.map((c) => c.field),\n });\n\n this.#callbacks.clearRowPool();\n this.#callbacks.setup();\n }\n\n /**\n * Get all columns with visibility info.\n */\n getAllColumns(): Array<{\n field: string;\n header: string;\n visible: boolean;\n lockVisible?: boolean;\n utility?: boolean;\n }> {\n return this.columns.map((c) => ({\n field: c.field,\n header: c.header || c.field,\n visible: !c.hidden,\n lockVisible: c.lockVisible,\n utility: c.meta?.utility === true,\n }));\n }\n\n /**\n * Get current column order.\n */\n getColumnOrder(): string[] {\n return this.columns.map((c) => c.field);\n }\n\n /**\n * Set column order.\n */\n setColumnOrder(order: string[]): void {\n if (!order.length) return;\n\n const columnMap = new Map(this.columns.map((c) => [c.field as string, c]));\n const reordered: ColumnInternal<T>[] = [];\n\n for (const field of order) {\n const col = columnMap.get(field);\n if (col) {\n reordered.push(col);\n columnMap.delete(field);\n }\n }\n\n // Add remaining columns not in order\n for (const col of columnMap.values()) {\n reordered.push(col);\n }\n\n this.columns = reordered;\n\n this.#callbacks.renderHeader();\n this.#callbacks.updateTemplate();\n this.#callbacks.refreshVirtualWindow();\n }\n\n // ============================================================================\n // Light DOM Observer\n // ============================================================================\n\n /**\n * Parse light DOM columns from host element.\n */\n parseLightDomColumns(host: HTMLElement): void {\n if (!this.#lightDomColumnsCache) {\n this.#originalColumnNodes = Array.from(host.querySelectorAll('tbw-grid-column')) as HTMLElement[];\n this.#lightDomColumnsCache = this.#originalColumnNodes.length ? parseLightDomColumns(host) : [];\n }\n }\n\n /**\n * Clear the light DOM columns cache.\n */\n clearLightDomCache(): void {\n this.#lightDomColumnsCache = undefined;\n }\n\n /**\n * Registered Light DOM element handlers.\n * Maps element tag names to callbacks that are invoked when those elements change.\n *\n * This is a generic mechanism - plugins (or future ShellPlugin) register\n * what elements they care about and handle parsing themselves.\n */\n #lightDomHandlers: Map<string, () => void> = new Map();\n\n /**\n * Register a handler for Light DOM element changes.\n * When elements matching the tag name are added/removed/changed,\n * the callback will be invoked (debounced).\n *\n * @param tagName - The lowercase tag name to watch (e.g., 'tbw-grid-header')\n * @param callback - Called when matching elements change\n */\n registerLightDomHandler(tagName: string, callback: () => void): void {\n this.#lightDomHandlers.set(tagName.toLowerCase(), callback);\n }\n\n /**\n * Unregister a Light DOM element handler.\n */\n unregisterLightDomHandler(tagName: string): void {\n this.#lightDomHandlers.delete(tagName.toLowerCase());\n }\n\n /**\n * Set up MutationObserver to watch for Light DOM changes.\n * This is generic infrastructure - specific handling is done via registered handlers.\n *\n * When Light DOM elements are added/removed/changed, the observer:\n * 1. Identifies which registered tag names were affected\n * 2. Debounces multiple mutations into a single callback per handler\n * 3. Invokes the registered callbacks\n *\n * This mechanism allows plugins to register their own Light DOM elements\n * and handle parsing themselves, then hand config to ConfigManager.\n *\n * @param host - The host element to observe (the grid element)\n */\n observeLightDOM(host: HTMLElement): void {\n // Clean up any existing observer\n if (this.#lightDomObserver) {\n this.#lightDomObserver.disconnect();\n }\n\n // Track which handlers need to be called (debounced)\n const pendingCallbacks = new Set<string>();\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const processPendingCallbacks = () => {\n debounceTimer = null;\n for (const tagName of pendingCallbacks) {\n const handler = this.#lightDomHandlers.get(tagName);\n handler?.();\n }\n pendingCallbacks.clear();\n };\n\n this.#lightDomObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n // Check added nodes\n for (const node of mutation.addedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\n const el = node as Element;\n const tagName = el.tagName.toLowerCase();\n\n // Check if any handler is interested in this element\n if (this.#lightDomHandlers.has(tagName)) {\n pendingCallbacks.add(tagName);\n }\n }\n\n // Check for attribute changes\n if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {\n const el = mutation.target as Element;\n const tagName = el.tagName.toLowerCase();\n if (this.#lightDomHandlers.has(tagName)) {\n pendingCallbacks.add(tagName);\n }\n }\n }\n\n // Debounce - batch all mutations into single callbacks\n if (pendingCallbacks.size > 0 && !debounceTimer) {\n debounceTimer = setTimeout(processPendingCallbacks, 0);\n }\n });\n\n // Observe children and their attributes\n this.#lightDomObserver.observe(host, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['title', 'field', 'header', 'width', 'hidden', 'id', 'icon', 'tooltip', 'order'],\n });\n }\n\n // ============================================================================\n // Change Notification\n // ============================================================================\n\n /**\n * Register a change listener.\n */\n onChange(callback: () => void): void {\n this.#changeListeners.push(callback);\n }\n\n /**\n * Notify all change listeners.\n */\n notifyChange(): void {\n for (const cb of this.#changeListeners) {\n cb();\n }\n }\n\n // ============================================================================\n // Cleanup\n // ============================================================================\n\n /**\n * Dispose of the ConfigManager and clean up resources.\n */\n dispose(): void {\n this.#lightDomObserver?.disconnect();\n this.#changeListeners = [];\n if (this.#stateChangeTimeoutId) {\n clearTimeout(this.#stateChangeTimeoutId);\n }\n }\n}\n","// ────────────────────────────────────────────────────────────────────────────\n// Cell Rendering Helpers (reduces duplicate code in rows.ts)\n// ────────────────────────────────────────────────────────────────────────────\n\n/** Unicode checkmark for true boolean values */\nconst BOOL_TRUE = '\\u{1F5F9}';\n/** Unicode empty checkbox for false boolean values */\nconst BOOL_FALSE = '\\u2610';\n\n/**\n * Generate accessible HTML for a boolean cell.\n * Uses role=\"checkbox\" with proper aria attributes.\n */\nexport function booleanCellHTML(value: boolean): string {\n return `<span role=\"checkbox\" aria-checked=\"${value}\" aria-label=\"${value}\">${value ? '🗹' : '☐'}</span>`;\n}\n\n/**\n * Format a date value for display.\n * Handles Date objects, timestamps, and date strings.\n * Returns empty string for invalid dates.\n */\nexport function formatDateValue(value: unknown): string {\n if (value == null || value === '') return '';\n if (value instanceof Date) {\n return isNaN(value.getTime()) ? '' : value.toLocaleDateString();\n }\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return isNaN(d.getTime()) ? '' : d.toLocaleDateString();\n }\n return '';\n}\n\n/**\n * Format a boolean value for text display (not HTML).\n */\nexport function formatBooleanValue(value: unknown): string {\n return value ? BOOL_TRUE : BOOL_FALSE;\n}\n\n/**\n * Get the row index from a cell element's data-row attribute.\n * Falls back to calculating from parent row's DOM position if data-row is missing.\n * Returns -1 if no valid row index is found.\n */\nexport function getRowIndexFromCell(cell: Element | null): number {\n if (!cell) return -1;\n const attr = cell.getAttribute('data-row');\n if (attr) return parseInt(attr, 10);\n\n // Fallback: find the parent .data-grid-row and calculate index from siblings\n const rowEl = cell.closest('.data-grid-row');\n if (!rowEl) return -1;\n\n const parent = rowEl.parentElement;\n if (!parent) return -1;\n\n // Get all data-grid-row siblings and find this row's index\n const rows = parent.querySelectorAll(':scope > .data-grid-row');\n for (let i = 0; i < rows.length; i++) {\n if (rows[i] === rowEl) return i;\n }\n return -1;\n}\n\n/**\n * Get the column index from a cell element's data-col attribute.\n * Returns -1 if no valid column index is found.\n */\nexport function getColIndexFromCell(cell: Element | null): number {\n if (!cell) return -1;\n const attr = cell.getAttribute('data-col');\n return attr ? parseInt(attr, 10) : -1;\n}\n\n/**\n * Clear all cell-focus styling from a root element (shadowRoot or bodyEl).\n * Used when changing focus or when selection plugin takes over focus management.\n */\nexport function clearCellFocus(root: Element | ShadowRoot | null): void {\n if (!root) return;\n root.querySelectorAll('.cell-focus').forEach((el) => el.classList.remove('cell-focus'));\n}\n","import type { ColumnInternal, ColumnViewRenderer, InternalGrid, RowElementInternal } from '../types';\nimport { ensureCellVisible } from './keyboard';\nimport { evalTemplateString, finalCellScrub, sanitizeHTML } from './sanitize';\nimport { booleanCellHTML, clearCellFocus, formatDateValue, getRowIndexFromCell } from './utils';\n\n/** Callback type for plugin row rendering hook */\nexport type RenderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number) => boolean;\n\n// ============================================================================\n// Type Defaults Resolution\n// ============================================================================\n\n/**\n * Resolves the renderer for a column using the priority chain:\n * 1. Column-level (`column.renderer` / `column.viewRenderer`)\n * 2. Grid-level (`gridConfig.typeDefaults[column.type]`)\n * 3. App-level (framework adapter's `getTypeDefault`)\n * 4. Returns undefined (caller uses built-in or fallback)\n */\nexport function resolveRenderer<TRow>(\n grid: InternalGrid<TRow>,\n col: ColumnInternal<TRow>,\n): ColumnViewRenderer<TRow, unknown> | undefined {\n // 1. Column-level renderer (highest priority)\n const columnRenderer = col.renderer || col.viewRenderer;\n if (columnRenderer) return columnRenderer;\n\n // No type specified - no type defaults to check\n if (!col.type) return undefined;\n\n // 2. Grid-level typeDefaults (access via effectiveConfig)\n const gridTypeDefaults = (grid as any).effectiveConfig?.typeDefaults;\n if (gridTypeDefaults?.[col.type]?.renderer) {\n return gridTypeDefaults[col.type].renderer;\n }\n\n // 3. App-level registry (via framework adapter)\n const adapter = grid.__frameworkAdapter;\n if (adapter?.getTypeDefault) {\n const appDefault = adapter.getTypeDefault<TRow>(col.type);\n if (appDefault?.renderer) {\n return appDefault.renderer;\n }\n }\n\n // 4. No custom renderer - caller uses built-in/fallback\n return undefined;\n}\n\n// ============================================================================\n// DOM State Helpers (used for virtualization cleanup)\n// ============================================================================\n\n/**\n * CSS selector for focusable editor elements within a cell.\n * Used by EditingPlugin and keyboard navigation.\n */\nexport const FOCUSABLE_EDITOR_SELECTOR =\n 'input,select,textarea,[contenteditable=\"true\"],[contenteditable=\"\"],[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Check if a row element has any cells in editing mode.\n * This is a DOM-level check used for virtualization recycling.\n */\nfunction hasEditingCells(rowEl: RowElementInternal): boolean {\n return (rowEl.__editingCellCount ?? 0) > 0;\n}\n\n/**\n * Clear all editing state from a row element.\n * Called when a row element is recycled for a different data row.\n */\nfunction clearEditingState(rowEl: RowElementInternal): void {\n rowEl.__editingCellCount = 0;\n rowEl.removeAttribute('data-has-editing');\n // Clear editing class from all cells\n const cells = rowEl.querySelectorAll('.cell.editing');\n cells.forEach((cell) => cell.classList.remove('editing'));\n}\n\n// ============== Template Cloning System ==============\n// Using template cloning is 3-4x faster than document.createElement + setAttribute\n// for repetitive element creation because the browser can skip parsing.\n\n/**\n * Cell template for cloning. Pre-configured with static attributes.\n * Dynamic attributes (data-col, data-row, etc.) are set after cloning.\n */\nconst cellTemplate = document.createElement('template');\ncellTemplate.innerHTML = '<div class=\"cell\" role=\"gridcell\" part=\"cell\"></div>';\n\n/**\n * Row template for cloning. Pre-configured with static attributes.\n * Dynamic attributes (data-row) and children (cells) are set after cloning.\n */\nconst rowTemplate = document.createElement('template');\nrowTemplate.innerHTML = '<div class=\"data-grid-row\" role=\"row\" part=\"row\"></div>';\n\n/**\n * Create a cell element from template. Significantly faster than createElement + setAttribute.\n */\nfunction createCellFromTemplate(): HTMLDivElement {\n return cellTemplate.content.firstElementChild!.cloneNode(true) as HTMLDivElement;\n}\n\n/**\n * Create a row element from template. Significantly faster than createElement + setAttribute.\n */\nexport function createRowFromTemplate(): HTMLDivElement {\n return rowTemplate.content.firstElementChild!.cloneNode(true) as HTMLDivElement;\n}\n\n// ============== End Template Cloning System ==============\n\n/**\n * Invalidate the cell cache (call when rows or columns change).\n */\nexport function invalidateCellCache(grid: InternalGrid): void {\n grid.__cellDisplayCache = undefined;\n grid.__cellCacheEpoch = undefined;\n grid.__hasSpecialColumns = undefined; // Reset fast-path check\n}\n\n/**\n * Render / patch the visible window of rows [start, end) using a recyclable DOM pool.\n * Newly required row elements are created and appended; excess are detached.\n * Uses an epoch counter to force full row rebuilds when structural changes (like columns) occur.\n * @param renderRowHook - Optional callback that plugins can use to render custom rows (e.g., group rows).\n * If it returns true, default rendering is skipped for that row.\n */\nexport function renderVisibleRows(\n grid: InternalGrid,\n start: number,\n end: number,\n epoch?: number,\n renderRowHook?: RenderRowHook,\n): void {\n const needed = Math.max(0, end - start);\n const bodyEl = grid._bodyEl;\n const columns = grid._visibleColumns;\n const colLen = columns.length;\n\n // Cache header row count once (check for group header row existence)\n let headerRowCount = grid.__cachedHeaderRowCount;\n if (headerRowCount === undefined) {\n headerRowCount = grid.querySelector('.header-group-row') ? 2 : 1;\n grid.__cachedHeaderRowCount = headerRowCount;\n }\n\n // Pool management: grow pool if needed\n // Note: click/dblclick handlers are delegated at grid level for efficiency\n while (grid._rowPool.length < needed) {\n // Use template cloning - 3-4x faster than createElement + setAttribute\n const rowEl = createRowFromTemplate();\n grid._rowPool.push(rowEl);\n }\n\n // Remove excess pool elements from DOM and shrink pool\n if (grid._rowPool.length > needed) {\n for (let i = needed; i < grid._rowPool.length; i++) {\n const el = grid._rowPool[i];\n if (el.parentNode === bodyEl) el.remove();\n }\n grid._rowPool.length = needed;\n }\n\n // Check if any plugin has a renderRow hook (cache this)\n const hasRenderRowPlugins = renderRowHook && grid.__hasRenderRowPlugins !== false;\n\n for (let i = 0; i < needed; i++) {\n const rowIndex = start + i;\n const rowData = grid._rows[rowIndex];\n const rowEl = grid._rowPool[i] as RowElementInternal;\n\n // Always set aria-rowindex (1-based, accounting for header rows)\n rowEl.setAttribute('aria-rowindex', String(rowIndex + headerRowCount + 1));\n\n // Let plugins handle custom row rendering (e.g., group rows)\n if (hasRenderRowPlugins && renderRowHook!(rowData, rowEl, rowIndex)) {\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n continue;\n }\n\n const rowEpoch = rowEl.__epoch;\n const prevRef = rowEl.__rowDataRef;\n const cellCount = rowEl.children.length;\n\n // Check if we need a full rebuild vs fast update\n const epochMatch = rowEpoch === epoch;\n const structureValid = epochMatch && cellCount === colLen;\n const dataRefChanged = prevRef !== rowData;\n\n // Need external view rebuild check when structure is valid but data changed\n let needsExternalRebuild = false;\n if (structureValid && dataRefChanged) {\n for (let c = 0; c < colLen; c++) {\n const col = columns[c];\n if (col.externalView) {\n const cellCheck = rowEl.querySelector(`.cell[data-col=\"${c}\"] [data-external-view]`);\n if (!cellCheck) {\n needsExternalRebuild = true;\n break;\n }\n }\n }\n }\n\n if (!structureValid || needsExternalRebuild) {\n // Full rebuild needed - epoch changed, cell count mismatch, or external view missing\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n // (This happens when virtualization recycles the DOM element for a different row)\n if (hasEditing && !isActivelyEditedRow) {\n // Force full rebuild to clear stale editors\n if (rowEl.__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n rowEl.__isCustomRow = false;\n }\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n } else if (hasEditing && isActivelyEditedRow) {\n // Row is in editing mode AND this is the correct row - preserve editors\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n rowEl.__rowDataRef = rowData;\n } else {\n if (rowEl.__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n rowEl.__isCustomRow = false;\n }\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n // NOTE: If this is the actively edited row, EditingPlugin's onScrollRender() will inject editors\n }\n } else if (dataRefChanged) {\n // Same structure, different row data - fast update\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditing && !isActivelyEditedRow) {\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n rowEl.__rowDataRef = rowData;\n // NOTE: If this is the actively edited row, EditingPlugin's onScrollRender() will inject editors\n }\n } else {\n // Same row data reference - just patch if any values changed\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditing && !isActivelyEditedRow) {\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n // NOTE: If this is the actively edited row, EditingPlugin's onScrollRender() will inject editors\n }\n }\n\n // Changed class toggle - check if row ID is in changedRowIds Set (EditingPlugin)\n let isChanged = false;\n const changedRowIds = grid.changedRowIds;\n if (changedRowIds && changedRowIds.length > 0) {\n try {\n const rowId = grid.getRowId?.(rowData);\n if (rowId) {\n isChanged = changedRowIds.includes(rowId);\n }\n } catch {\n // Row has no ID - not tracked as changed\n }\n }\n const hasChangedClass = rowEl.classList.contains('changed');\n if (isChanged !== hasChangedClass) {\n rowEl.classList.toggle('changed', isChanged);\n }\n\n // Apply rowClass callback if configured\n const rowClassFn = grid.effectiveConfig?.rowClass;\n if (rowClassFn) {\n // Remove previous dynamic classes (stored in data attribute)\n const prevClasses = rowEl.getAttribute('data-dynamic-classes');\n if (prevClasses) {\n prevClasses.split(' ').forEach((cls) => cls && rowEl.classList.remove(cls));\n }\n try {\n const newClasses = rowClassFn(rowData);\n if (newClasses && newClasses.length > 0) {\n const validClasses = newClasses.filter((c) => c && typeof c === 'string');\n validClasses.forEach((cls) => rowEl.classList.add(cls));\n rowEl.setAttribute('data-dynamic-classes', validClasses.join(' '));\n } else {\n rowEl.removeAttribute('data-dynamic-classes');\n }\n } catch (e) {\n console.warn(`[tbw-grid] rowClass callback error:`, e);\n rowEl.removeAttribute('data-dynamic-classes');\n }\n }\n\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n }\n}\n\n/**\n * Fast patch path for an already-rendered row: updates plain text cells whose data changed\n * while skipping cells with external views, templates, or active editors.\n *\n * Optimized for scroll performance - avoids querySelectorAll in favor of children access.\n */\nfunction fastPatchRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n const children = rowEl.children;\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const childLen = children.length;\n const minLen = colsLen < childLen ? colsLen : childLen;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n\n // Ultra-fast path: if no special columns (templates, formatters, etc.), use direct assignment\n // Check is cached on grid to avoid repeated iteration\n let hasSpecialCols = grid.__hasSpecialColumns;\n if (hasSpecialCols === undefined) {\n hasSpecialCols = false;\n const typeDefaults = (grid as any).effectiveConfig?.typeDefaults;\n const adapter = grid.__frameworkAdapter;\n for (let i = 0; i < colsLen; i++) {\n const col = columns[i];\n if (\n col.__viewTemplate ||\n col.__compiledView ||\n col.renderer ||\n col.viewRenderer ||\n col.externalView ||\n col.format ||\n col.type === 'date' ||\n col.type === 'boolean' ||\n // Check for type-level renderers (grid-level or adapter-level)\n (col.type && typeDefaults?.[col.type]?.renderer) ||\n (col.type && adapter?.getTypeDefault?.(col.type)?.renderer)\n ) {\n hasSpecialCols = true;\n break;\n }\n }\n grid.__hasSpecialColumns = hasSpecialCols;\n }\n\n const rowIndexStr = String(rowIndex);\n\n // Ultra-fast path for plain text grids - just set textContent directly\n if (!hasSpecialCols) {\n for (let i = 0; i < minLen; i++) {\n const cell = children[i] as HTMLElement;\n\n // Skip cells in edit mode - they have editors that must be preserved\n if (cell.classList.contains('editing')) continue;\n\n const value = rowData[columns[i].field];\n cell.textContent = value == null ? '' : String(value);\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n // aria-selected only valid for gridcell, not checkbox (but ultra-fast path has no special cols)\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n }\n return;\n }\n\n // Check if any external view placeholder is missing - if so, do full rebuild\n for (let i = 0; i < minLen; i++) {\n const col = columns[i];\n if (col.externalView) {\n const cell = children[i] as HTMLElement;\n if (!cell.querySelector('[data-external-view]')) {\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n return;\n }\n }\n }\n\n // Standard path for grids with special columns\n for (let i = 0; i < minLen; i++) {\n const col = columns[i];\n const cell = children[i] as HTMLElement;\n\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n\n // Apply cellClass callback if configured\n const cellClassFn = col.cellClass;\n if (cellClassFn) {\n // Remove previous dynamic classes\n const prevClasses = cell.getAttribute('data-dynamic-classes');\n if (prevClasses) {\n prevClasses.split(' ').forEach((cls) => cls && cell.classList.remove(cls));\n }\n try {\n const value = rowData[col.field];\n const cellClasses = cellClassFn(value, rowData, col);\n if (cellClasses && cellClasses.length > 0) {\n const validClasses = cellClasses.filter((c: string) => c && typeof c === 'string');\n validClasses.forEach((cls: string) => cell.classList.add(cls));\n cell.setAttribute('data-dynamic-classes', validClasses.join(' '));\n } else {\n cell.removeAttribute('data-dynamic-classes');\n }\n } catch (e) {\n console.warn(`[tbw-grid] cellClass callback error for column '${col.field}':`, e);\n cell.removeAttribute('data-dynamic-classes');\n }\n }\n\n // Skip cells in edit mode\n if (cell.classList.contains('editing')) continue;\n\n // Handle viewRenderer/renderer - must re-invoke to get updated content\n // Uses priority chain: column → typeDefaults → adapter → built-in\n const cellRenderer = resolveRenderer(grid, col);\n if (cellRenderer) {\n const value = rowData[col.field];\n // Pass cellEl for framework adapters that want to cache per-cell\n const produced = cellRenderer({ row: rowData, value, field: col.field, column: col, cellEl: cell });\n if (typeof produced === 'string') {\n cell.innerHTML = sanitizeHTML(produced);\n } else if (produced instanceof Node) {\n // Check if this container is already a child of the cell (reused by framework adapter)\n if (produced.parentElement !== cell) {\n cell.innerHTML = '';\n cell.appendChild(produced);\n }\n // If already a child, the framework adapter has re-rendered in place\n } else if (produced == null) {\n // Renderer returned null/undefined - show raw value\n cell.textContent = value == null ? '' : String(value);\n }\n // If produced is truthy but not a string or Node, the framework handles it\n continue;\n }\n\n // Skip templated / external cells (these need full rebuild to remount)\n if (col.__viewTemplate || col.__compiledView || col.externalView) {\n continue;\n }\n\n // Compute and set display value\n const value = rowData[col.field];\n let displayStr: string;\n\n if (col.format) {\n try {\n const formatted = col.format(value, rowData);\n displayStr = formatted == null ? '' : String(formatted);\n } catch (e) {\n // Log format errors as warnings (user configuration issue)\n console.warn(`[tbw-grid] Format error in column '${col.field}':`, e);\n displayStr = value == null ? '' : String(value);\n }\n } else if (col.type === 'date') {\n displayStr = formatDateValue(value);\n cell.textContent = displayStr;\n } else if (col.type === 'boolean') {\n // Boolean cells have inner span with checkbox role for ARIA compliance\n cell.innerHTML = booleanCellHTML(!!value);\n } else {\n displayStr = value == null ? '' : String(value);\n cell.textContent = displayStr;\n }\n }\n}\n\n/**\n * Full reconstruction of a row's set of cells including templated, external view, and formatted content.\n * Attaches event handlers for editing and accessibility per cell.\n */\nexport function renderInlineRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n rowEl.innerHTML = '';\n\n // Pre-cache values used in the loop\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n const gridEl = grid as unknown as HTMLElement;\n\n // Use DocumentFragment for batch DOM insertion\n const fragment = document.createDocumentFragment();\n\n for (let colIndex = 0; colIndex < colsLen; colIndex++) {\n const col = columns[colIndex];\n // Use template cloning - 3-4x faster than createElement + setAttribute\n const cell = createCellFromTemplate();\n\n // Only set dynamic attributes (role, class, part are already set in template)\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(colIndex + 1));\n cell.setAttribute('data-col', String(colIndex));\n cell.setAttribute('data-row', String(rowIndex));\n cell.setAttribute('data-field', col.field); // Field name for column identification\n cell.setAttribute('data-header', col.header ?? col.field); // Header text for responsive CSS\n if (col.type) cell.setAttribute('data-type', col.type);\n\n let value = (rowData as Record<string, unknown>)[col.field];\n if (col.format) {\n try {\n value = col.format(value, rowData);\n } catch (e) {\n // Log format errors as warnings (user configuration issue)\n console.warn(`[tbw-grid] Format error in column '${col.field}':`, e);\n }\n }\n\n const compiled = col.__compiledView;\n const tplHolder = col.__viewTemplate;\n // Resolve renderer using priority chain: column → typeDefaults → adapter → built-in\n const viewRenderer = resolveRenderer(grid, col);\n const externalView = col.externalView;\n\n // Track if we used a template that needs sanitization\n let needsSanitization = false;\n\n if (viewRenderer) {\n // Pass cellEl for framework adapters that want to cache per-cell\n const produced = viewRenderer({ row: rowData, value, field: col.field, column: col, cellEl: cell });\n if (typeof produced === 'string') {\n // Sanitize HTML from viewRenderer to prevent XSS from user-controlled data\n cell.innerHTML = sanitizeHTML(produced);\n needsSanitization = true;\n } else if (produced instanceof Node) {\n // Check if this container is already a child of the cell (reused by framework adapter)\n if (produced.parentElement !== cell) {\n // Clear any existing content before appending new container\n cell.textContent = '';\n cell.appendChild(produced);\n }\n // If already a child, the framework adapter has re-rendered in place\n } else if (produced == null) {\n // Renderer returned null/undefined - show raw value\n cell.textContent = value == null ? '' : String(value);\n }\n // If produced is truthy but not a string or Node (e.g., framework placeholder),\n // don't modify the cell - the framework adapter handles rendering\n } else if (externalView) {\n const spec = externalView;\n const placeholder = document.createElement('div');\n placeholder.setAttribute('data-external-view', '');\n placeholder.setAttribute('data-field', col.field);\n cell.appendChild(placeholder);\n const context = { row: rowData, value, field: col.field, column: col };\n if (spec.mount) {\n try {\n spec.mount({ placeholder, context, spec });\n } catch (e) {\n // Log mount errors as warnings (user configuration issue)\n console.warn(`[tbw-grid] External view mount error for column '${col.field}':`, e);\n }\n } else {\n queueMicrotask(() => {\n try {\n gridEl.dispatchEvent(\n new CustomEvent('mount-external-view', {\n bubbles: true,\n composed: true,\n detail: { placeholder, spec, context },\n }),\n );\n } catch (e) {\n // Log dispatch errors as warnings\n console.warn(`[tbw-grid] External view event dispatch error for column '${col.field}':`, e);\n }\n });\n }\n placeholder.setAttribute('data-mounted', '');\n } else if (compiled) {\n const output = compiled({ row: rowData, value, field: col.field, column: col });\n const blocked = compiled.__blocked;\n // Sanitize compiled template output to prevent XSS\n cell.innerHTML = blocked ? '' : sanitizeHTML(output);\n needsSanitization = true;\n if (blocked) {\n // Forcefully clear any residual whitespace text nodes for deterministic emptiness\n cell.textContent = '';\n cell.setAttribute('data-blocked-template', '');\n }\n } else if (tplHolder) {\n const rawTpl = tplHolder.innerHTML;\n if (/Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(rawTpl)) {\n cell.textContent = '';\n cell.setAttribute('data-blocked-template', '');\n } else {\n // Sanitize inline template output to prevent XSS\n cell.innerHTML = sanitizeHTML(evalTemplateString(rawTpl, { row: rowData, value }));\n needsSanitization = true;\n }\n } else {\n // Plain value rendering - compute display directly (matches Stencil performance)\n if (col.type === 'date') {\n cell.textContent = formatDateValue(value);\n } else if (col.type === 'boolean') {\n // Wrap checkbox in span to satisfy ARIA: gridcell can contain checkbox\n cell.innerHTML = booleanCellHTML(!!value);\n } else {\n cell.textContent = value == null ? '' : String(value);\n }\n }\n\n // Only run expensive sanitization when we used innerHTML with user content\n if (needsSanitization) {\n finalCellScrub(cell);\n // Defensive: if forbidden tokens leaked via async or framework hydration, scrub again.\n const textContent = cell.textContent || '';\n if (/Proxy|Reflect\\.ownKeys/.test(textContent)) {\n cell.textContent = textContent.replace(/Proxy|Reflect\\.ownKeys/g, '').trim();\n if (/Proxy|Reflect\\.ownKeys/.test(cell.textContent || '')) cell.textContent = '';\n }\n }\n\n if (cell.hasAttribute('data-blocked-template')) {\n // If anything at all remains (e.g., 'function () { [native code] }'), blank it completely.\n if ((cell.textContent || '').trim().length) cell.textContent = '';\n }\n // Mark editable cells with tabindex for keyboard navigation\n // Event handlers are set up via delegation in setupCellEventDelegation()\n if (col.editable) {\n cell.tabIndex = 0;\n } else if (col.type === 'boolean') {\n // Non-editable boolean cells should NOT toggle on space key\n // They are read-only, only set tabindex for focus navigation\n if (!cell.hasAttribute('tabindex')) cell.tabIndex = 0;\n }\n\n // Initialize focus state (must match fastPatchRow for consistent behavior)\n if (focusRow === rowIndex && focusCol === colIndex) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n } else {\n cell.setAttribute('aria-selected', 'false');\n }\n\n // Apply cellClass callback if configured\n const cellClassFn = col.cellClass;\n if (cellClassFn) {\n try {\n const cellValue = (rowData as Record<string, unknown>)[col.field];\n const cellClasses = cellClassFn(cellValue, rowData, col);\n if (cellClasses && cellClasses.length > 0) {\n const validClasses = cellClasses.filter((c) => c && typeof c === 'string');\n validClasses.forEach((cls) => cell.classList.add(cls));\n cell.setAttribute('data-dynamic-classes', validClasses.join(' '));\n }\n } catch (e) {\n console.warn(`[tbw-grid] cellClass callback error for column '${col.field}':`, e);\n }\n }\n\n fragment.appendChild(cell);\n }\n\n // Single DOM operation to append all cells\n rowEl.appendChild(fragment);\n}\n\n/**\n * Handle click / double click interaction to focus cells.\n * Edit triggering is handled by EditingPlugin via onCellClick hook.\n */\nexport function handleRowClick(grid: InternalGrid, e: MouseEvent, rowEl: HTMLElement): void {\n if ((e.target as HTMLElement)?.closest('.resize-handle')) return;\n const firstCell = rowEl.querySelector('.cell[data-row]') as HTMLElement | null;\n const rowIndex = getRowIndexFromCell(firstCell);\n if (rowIndex < 0) return;\n const rowData = grid._rows[rowIndex];\n if (!rowData) return;\n\n // Dispatch row click to plugin system first (e.g., for master-detail expansion)\n if (grid._dispatchRowClick?.(e, rowIndex, rowData, rowEl)) {\n return;\n }\n\n const cellEl = (e.target as HTMLElement)?.closest('.cell[data-col]') as HTMLElement | null;\n if (cellEl) {\n const colIndex = Number(cellEl.getAttribute('data-col'));\n if (!isNaN(colIndex)) {\n // Dispatch to plugin system first - if handled (e.g., edit triggered), stop propagation\n if (grid._dispatchCellClick?.(e, rowIndex, colIndex, cellEl)) {\n return;\n }\n\n // Always update focus to the clicked cell\n const focusChanged = grid._focusRow !== rowIndex || grid._focusCol !== colIndex;\n grid._focusRow = rowIndex;\n grid._focusCol = colIndex;\n\n // If clicking an already-editing cell, just update focus styling and return\n if (cellEl.classList.contains('editing')) {\n if (focusChanged) {\n // Update .cell-focus class to reflect new focus (clear from grid element)\n clearCellFocus(grid._bodyEl ?? grid);\n cellEl.classList.add('cell-focus');\n }\n // Focus the editor in the cell\n const editor = cellEl.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n return;\n }\n\n ensureCellVisible(grid);\n }\n }\n}\n","/**\n * Central keyboard handler attached to the host element. Manages navigation, paging,\n * and edit lifecycle triggers while respecting active form field interactions.\n */\nimport type { InternalGrid } from '../types';\nimport { FOCUSABLE_EDITOR_SELECTOR } from './rows';\nimport { clearCellFocus } from './utils';\n\nexport function handleGridKeyDown(grid: InternalGrid, e: KeyboardEvent): void {\n // Dispatch to plugin system first - if any plugin handles it, stop here\n if (grid._dispatchKeyDown?.(e)) {\n return;\n }\n\n const maxRow = grid._rows.length - 1;\n const maxCol = grid._visibleColumns.length - 1;\n const editing = grid._activeEditRows !== undefined && grid._activeEditRows !== -1;\n const col = grid._visibleColumns[grid._focusCol];\n const colType = col?.type;\n const path = e.composedPath?.() ?? [];\n const target = (path.length ? path[0] : e.target) as HTMLElement | null;\n const isFormField = (el: HTMLElement | null) => {\n if (!el) return false;\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') return true;\n if (el.isContentEditable) return true;\n return false;\n };\n if (isFormField(target) && (e.key === 'Home' || e.key === 'End')) return;\n if (isFormField(target) && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {\n if ((target as HTMLInputElement).tagName === 'INPUT' && (target as HTMLInputElement).type === 'number') return;\n }\n // Let arrow left/right navigate within text inputs instead of moving cells\n if (isFormField(target) && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) return;\n // Let Enter/Escape be handled by the input's own handlers first\n if (isFormField(target) && (e.key === 'Enter' || e.key === 'Escape')) return;\n if (editing && colType === 'select' && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) return;\n switch (e.key) {\n case 'Tab': {\n e.preventDefault();\n const forward = !e.shiftKey;\n if (forward) {\n if (grid._focusCol < maxCol) grid._focusCol += 1;\n else {\n if (typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n if (grid._focusRow < maxRow) {\n grid._focusRow += 1;\n grid._focusCol = 0;\n }\n }\n } else {\n if (grid._focusCol > 0) grid._focusCol -= 1;\n else if (grid._focusRow > 0) {\n if (typeof grid.commitActiveRowEdit === 'function' && grid._activeEditRows === grid._focusRow)\n grid.commitActiveRowEdit();\n grid._focusRow -= 1;\n grid._focusCol = maxCol;\n }\n }\n ensureCellVisible(grid);\n return;\n }\n case 'ArrowDown':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.min(maxRow, grid._focusRow + 1);\n e.preventDefault();\n break;\n case 'ArrowUp':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.max(0, grid._focusRow - 1);\n e.preventDefault();\n break;\n case 'ArrowRight':\n grid._focusCol = Math.min(maxCol, grid._focusCol + 1);\n e.preventDefault();\n break;\n case 'ArrowLeft':\n grid._focusCol = Math.max(0, grid._focusCol - 1);\n e.preventDefault();\n break;\n case 'Home':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+Home: navigate to first row, first cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = 0;\n grid._focusCol = 0;\n } else {\n // Home: navigate to first cell in current row\n grid._focusCol = 0;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollLeft: true });\n return;\n case 'End':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+End: navigate to last row, last cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = maxRow;\n grid._focusCol = maxCol;\n } else {\n // End: navigate to last cell in current row\n grid._focusCol = maxCol;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollRight: true });\n return;\n case 'PageDown':\n grid._focusRow = Math.min(maxRow, grid._focusRow + 20);\n e.preventDefault();\n break;\n case 'PageUp':\n grid._focusRow = Math.max(0, grid._focusRow - 20);\n e.preventDefault();\n break;\n // NOTE: Enter key is handled by EditingPlugin. If no plugin handles it,\n // we dispatch the unified cell-activate event for custom handling.\n case 'Enter': {\n const rowIndex = grid._focusRow;\n const colIndex = grid._focusCol;\n const column = grid._visibleColumns[colIndex];\n const row = grid._rows[rowIndex];\n const field = column?.field ?? '';\n const value = field && row ? (row as Record<string, unknown>)[field] : undefined;\n const cellEl = (grid as unknown as HTMLElement).querySelector(\n `[data-row=\"${rowIndex}\"][data-col=\"${colIndex}\"]`,\n ) as HTMLElement | undefined;\n\n const detail = {\n rowIndex,\n colIndex,\n field,\n value,\n row,\n cellEl,\n trigger: 'keyboard' as const,\n originalEvent: e,\n };\n\n // Emit unified cell-activate event\n const activateEvent = new CustomEvent('cell-activate', {\n cancelable: true,\n detail,\n });\n (grid as unknown as HTMLElement).dispatchEvent(activateEvent);\n\n // Also emit deprecated activate-cell for backwards compatibility\n const legacyEvent = new CustomEvent('activate-cell', {\n cancelable: true,\n detail: { row: rowIndex, col: colIndex },\n });\n (grid as unknown as HTMLElement).dispatchEvent(legacyEvent);\n\n // If either event was prevented, block further keyboard processing\n if (activateEvent.defaultPrevented || legacyEvent.defaultPrevented) {\n e.preventDefault();\n return;\n }\n // Otherwise allow normal keyboard processing\n break;\n }\n default:\n return;\n }\n ensureCellVisible(grid);\n}\n\n/**\n * Options for ensureCellVisible to control scroll behavior.\n */\ninterface EnsureCellVisibleOptions {\n /** Force scroll to the leftmost position (for Home key) */\n forceScrollLeft?: boolean;\n /** Force scroll to the rightmost position (for End key) */\n forceScrollRight?: boolean;\n}\n\n/**\n * Scroll the viewport (virtualized or static) so the focused cell's row is visible\n * and apply visual focus styling / tabindex management.\n */\nexport function ensureCellVisible(grid: InternalGrid, options?: EnsureCellVisibleOptions): void {\n if (grid._virtualization?.enabled) {\n const { rowHeight, container, viewportEl } = grid._virtualization;\n // container is the faux scrollbar element that handles actual scrolling\n // viewportEl is the visible area element that has the correct height\n const scrollEl = container as HTMLElement | undefined;\n const visibleHeight = viewportEl?.clientHeight ?? scrollEl?.clientHeight ?? 0;\n if (scrollEl && visibleHeight > 0) {\n const y = grid._focusRow * rowHeight;\n if (y < scrollEl.scrollTop) {\n scrollEl.scrollTop = y;\n } else if (y + rowHeight > scrollEl.scrollTop + visibleHeight) {\n scrollEl.scrollTop = y - visibleHeight + rowHeight;\n }\n }\n }\n // Skip refreshVirtualWindow when in edit mode to avoid wiping editors\n const isEditing = grid._activeEditRows !== undefined && grid._activeEditRows !== -1;\n if (!isEditing) {\n grid.refreshVirtualWindow(false);\n }\n clearCellFocus(grid._bodyEl);\n // Clear previous aria-selected markers\n Array.from(grid._bodyEl.querySelectorAll('[aria-selected=\"true\"]')).forEach((el) => {\n el.setAttribute('aria-selected', 'false');\n });\n const rowIndex = grid._focusRow;\n const vStart = grid._virtualization.start ?? 0;\n const vEnd = grid._virtualization.end ?? grid._rows.length;\n if (rowIndex >= vStart && rowIndex < vEnd) {\n const rowEl = grid._bodyEl.querySelectorAll('.data-grid-row')[rowIndex - vStart] as HTMLElement | null;\n // Try exact column match first, then query by data-col, then fallback to first cell (for full-width group rows)\n let cell = rowEl?.children[grid._focusCol] as HTMLElement | undefined;\n if (!cell || !cell.classList?.contains('cell')) {\n cell = (rowEl?.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`) ??\n rowEl?.querySelector('.cell[data-col]')) as HTMLElement | undefined;\n }\n if (cell) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n\n // Horizontal scroll: ensure focused cell is visible in the horizontal scroll area\n // The .tbw-scroll-area element handles horizontal scrolling\n // Skip horizontal scrolling when in edit mode to prevent scroll jumps when editors are created\n const scrollArea = grid.querySelector('.tbw-scroll-area') as HTMLElement | null;\n if (scrollArea && cell && !isEditing) {\n // Handle forced scroll for Home/End keys - always scroll to edge\n if (options?.forceScrollLeft) {\n scrollArea.scrollLeft = 0;\n } else if (options?.forceScrollRight) {\n scrollArea.scrollLeft = scrollArea.scrollWidth - scrollArea.clientWidth;\n } else {\n // Get scroll boundary offsets from plugins (e.g., pinned columns)\n // This allows plugins to report how much of the scroll area they obscure\n // and whether the focused cell should skip scrolling (e.g., pinned cells are always visible)\n const offsets = grid._getHorizontalScrollOffsets?.(rowEl ?? undefined, cell) ?? { left: 0, right: 0 };\n\n if (!offsets.skipScroll) {\n // Get cell position relative to the scroll area\n const cellRect = cell.getBoundingClientRect();\n const scrollAreaRect = scrollArea.getBoundingClientRect();\n // Calculate the cell's position relative to scroll area's visible region\n const cellLeft = cellRect.left - scrollAreaRect.left + scrollArea.scrollLeft;\n const cellRight = cellLeft + cellRect.width;\n // Adjust visible boundaries to account for plugin-reported offsets\n const visibleLeft = scrollArea.scrollLeft + offsets.left;\n const visibleRight = scrollArea.scrollLeft + scrollArea.clientWidth - offsets.right;\n // Scroll horizontally if needed\n if (cellLeft < visibleLeft) {\n scrollArea.scrollLeft = cellLeft - offsets.left;\n } else if (cellRight > visibleRight) {\n scrollArea.scrollLeft = cellRight - scrollArea.clientWidth + offsets.right;\n }\n }\n }\n }\n\n if (grid._activeEditRows !== undefined && grid._activeEditRows !== -1 && cell.classList.contains('editing')) {\n const focusTarget = cell.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n if (focusTarget && document.activeElement !== focusTarget) {\n try {\n focusTarget.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n } else if (!cell.contains(document.activeElement)) {\n if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '-1');\n try {\n cell.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n }\n }\n}\n","/**\n * Event Delegation Module\n *\n * Consolidates all delegated event handling for the grid.\n * Uses event delegation (single listener on container) rather than per-cell/per-row\n * listeners to minimize memory usage.\n *\n * This module provides:\n * - setupCellEventDelegation: Body-level handlers (mousedown, click, dblclick on cells/rows)\n * - setupRootEventDelegation: Root-level handlers (keydown, mousedown for plugins, drag tracking)\n *\n * Edit triggering is handled separately by the EditingPlugin via\n * onCellClick and onKeyDown hooks.\n */\n\nimport type { CellMouseEvent } from '../plugin/types';\nimport type { InternalGrid } from '../types';\nimport { handleGridKeyDown } from './keyboard';\nimport { handleRowClick } from './rows';\nimport { clearCellFocus, getColIndexFromCell, getRowIndexFromCell } from './utils';\n\n// Track drag state per grid instance (avoids polluting InternalGrid interface)\nconst dragState = new WeakMap<InternalGrid, boolean>();\n\n/**\n * Handle delegated mousedown on cells.\n * Updates focus position for navigation.\n *\n * IMPORTANT: This must NOT call refreshVirtualWindow or any function that\n * re-renders DOM elements. Doing so would replace the element the user clicked on,\n * causing the subsequent click event to fire on a detached element and not bubble\n * to parent handlers (like handleRowClick).\n *\n * For mouse interactions, the cell is already visible (user clicked on it),\n * so we only need to update focus state without scrolling or re-rendering.\n */\nfunction handleCellMousedown(grid: InternalGrid, cell: HTMLElement): void {\n const rowIndex = getRowIndexFromCell(cell);\n const colIndex = getColIndexFromCell(cell);\n if (rowIndex < 0 || colIndex < 0) return;\n\n grid._focusRow = rowIndex;\n grid._focusCol = colIndex;\n\n // Update focus styling directly without triggering re-render.\n // ensureCellVisible() would call refreshVirtualWindow() which replaces DOM elements,\n // breaking the click event that follows this mousedown.\n clearCellFocus(grid._bodyEl);\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n}\n\n/**\n * Build a CellMouseEvent from a native MouseEvent.\n * Extracts cell/row information from the event target.\n */\nfunction buildCellMouseEvent(\n grid: InternalGrid,\n renderRoot: HTMLElement,\n e: MouseEvent,\n type: 'mousedown' | 'mousemove' | 'mouseup',\n): CellMouseEvent {\n // For document-level events (mousemove/mouseup during drag), e.target won't be inside shadow DOM.\n // Use composedPath to find elements inside shadow roots, or fall back to elementFromPoint.\n let target: Element | null = null;\n\n // composedPath gives us the full path including shadow DOM elements\n const path = e.composedPath?.() as Element[] | undefined;\n if (path && path.length > 0) {\n target = path[0];\n } else {\n target = e.target as Element;\n }\n\n // If target is not inside our element (e.g., for document-level events),\n // use elementFromPoint to find the actual element under the mouse\n if (target && !renderRoot.contains(target)) {\n const elAtPoint = document.elementFromPoint(e.clientX, e.clientY);\n if (elAtPoint) {\n target = elAtPoint;\n }\n }\n\n // Cells have data-col and data-row attributes\n const cellEl = target?.closest?.('[data-col]') as HTMLElement | null;\n const rowEl = target?.closest?.('.data-grid-row') as HTMLElement | null;\n const headerEl = target?.closest?.('.header-row') as HTMLElement | null;\n\n let rowIndex: number | undefined;\n let colIndex: number | undefined;\n let row: unknown;\n let field: string | undefined;\n let value: unknown;\n let column: unknown;\n\n if (cellEl) {\n // Get indices from cell attributes\n rowIndex = parseInt(cellEl.getAttribute('data-row') ?? '-1', 10);\n colIndex = parseInt(cellEl.getAttribute('data-col') ?? '-1', 10);\n if (rowIndex >= 0 && colIndex >= 0) {\n row = grid._rows[rowIndex];\n column = grid._columns[colIndex];\n field = (column as { field?: string })?.field;\n value = row && field ? (row as Record<string, unknown>)[field] : undefined;\n }\n }\n\n return {\n type,\n row,\n rowIndex: rowIndex !== undefined && rowIndex >= 0 ? rowIndex : undefined,\n colIndex: colIndex !== undefined && colIndex >= 0 ? colIndex : undefined,\n field,\n value,\n column: column as CellMouseEvent['column'],\n originalEvent: e,\n cellElement: cellEl ?? undefined,\n rowElement: rowEl ?? undefined,\n isHeader: !!headerEl,\n cell:\n rowIndex !== undefined && colIndex !== undefined && rowIndex >= 0 && colIndex >= 0\n ? { row: rowIndex, col: colIndex }\n : undefined,\n };\n}\n\n/**\n * Handle mousedown events and dispatch to plugin system.\n */\nfunction handleMouseDown(grid: InternalGrid, renderRoot: HTMLElement, e: MouseEvent): void {\n const event = buildCellMouseEvent(grid, renderRoot, e, 'mousedown');\n const handled = grid._dispatchCellMouseDown?.(event) ?? false;\n\n // If any plugin handled mousedown, start tracking for drag\n if (handled) {\n dragState.set(grid, true);\n }\n}\n\n/**\n * Handle mousemove events (only when dragging).\n */\nfunction handleMouseMove(grid: InternalGrid, renderRoot: HTMLElement, e: MouseEvent): void {\n if (!dragState.get(grid)) return;\n\n const event = buildCellMouseEvent(grid, renderRoot, e, 'mousemove');\n grid._dispatchCellMouseMove?.(event);\n}\n\n/**\n * Handle mouseup events.\n */\nfunction handleMouseUp(grid: InternalGrid, renderRoot: HTMLElement, e: MouseEvent): void {\n if (!dragState.get(grid)) return;\n\n const event = buildCellMouseEvent(grid, renderRoot, e, 'mouseup');\n grid._dispatchCellMouseUp?.(event);\n dragState.set(grid, false);\n}\n\n/**\n * Set up delegated event listeners on the grid body.\n * Consolidates all row/cell mouse event handling into a single set of listeners.\n * Call once during grid initialization.\n *\n * Benefits:\n * - 3 listeners total vs N*2 listeners (where N = pool size)\n * - Consistent event handling across all rows\n * - Automatic cleanup via AbortController signal\n *\n * @param grid - The grid instance\n * @param bodyEl - The .rows element containing all data rows\n * @param signal - AbortSignal for cleanup\n */\nexport function setupCellEventDelegation(grid: InternalGrid, bodyEl: HTMLElement, signal: AbortSignal): void {\n // Mousedown - update focus on any cell (not just editable)\n bodyEl.addEventListener(\n 'mousedown',\n (e) => {\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n // Skip if clicking inside an editing cell (let the editor handle it)\n if (cell.classList.contains('editing')) return;\n\n handleCellMousedown(grid, cell);\n },\n { signal },\n );\n\n // Click - handle row/cell click interactions\n bodyEl.addEventListener(\n 'click',\n (e) => {\n const rowEl = (e.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n if (rowEl) handleRowClick(grid, e as MouseEvent, rowEl);\n },\n { signal },\n );\n\n // Dblclick - same handler as click (edit triggering handled by EditingPlugin)\n bodyEl.addEventListener(\n 'dblclick',\n (e) => {\n const rowEl = (e.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n if (rowEl) handleRowClick(grid, e as MouseEvent, rowEl);\n },\n { signal },\n );\n}\n\n/**\n * Set up root-level and document-level event listeners.\n * These are added once per grid lifetime (not re-attached on DOM recreation).\n *\n * Includes:\n * - keydown: Keyboard navigation (arrows, Enter, Escape)\n * - mousedown: Plugin dispatch for cell interactions\n * - mousemove/mouseup: Global drag tracking\n *\n * @param grid - The grid instance\n * @param gridElement - The grid element (for keydown)\n * @param renderRoot - The render root element (for mousedown)\n * @param signal - AbortSignal for cleanup\n */\nexport function setupRootEventDelegation(\n grid: InternalGrid,\n gridElement: HTMLElement,\n renderRoot: HTMLElement,\n signal: AbortSignal,\n): void {\n // Element-level keydown handler for keyboard navigation\n gridElement.addEventListener('keydown', (e) => handleGridKeyDown(grid, e), { signal });\n\n // Central mouse event handling for plugins\n renderRoot.addEventListener('mousedown', (e) => handleMouseDown(grid, renderRoot, e as MouseEvent), { signal });\n\n // Track global mousemove/mouseup for drag operations (column resize, selection, etc.)\n document.addEventListener('mousemove', (e: MouseEvent) => handleMouseMove(grid, renderRoot, e), { signal });\n document.addEventListener('mouseup', (e: MouseEvent) => handleMouseUp(grid, renderRoot, e), { signal });\n}\n","/**\n * Sorting Module\n *\n * Handles column sorting state transitions and row ordering.\n */\n\nimport type { ColumnConfig, InternalGrid, SortHandler, SortState } from '../types';\nimport { renderHeader } from './header';\n\n/**\n * Default comparator for sorting values.\n * Handles nulls (pushed to end), numbers, and string fallback.\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n return a > b ? 1 : a < b ? -1 : 0;\n}\n\n/**\n * Built-in sort implementation using column comparator or default.\n * This is the default sortHandler when none is configured.\n */\nexport function builtInSort<T>(rows: T[], sortState: SortState, columns: ColumnConfig<T>[]): T[] {\n const col = columns.find((c) => c.field === sortState.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n const { field, direction } = sortState;\n\n return [...rows].sort((rA: any, rB: any) => {\n return comparator(rA[field], rB[field], rA, rB) * direction;\n });\n}\n\n/**\n * Apply sort result to grid and update UI.\n * Called after sync or async sort completes.\n */\nfunction finalizeSortResult<T>(grid: InternalGrid<T>, sortedRows: T[], col: ColumnConfig<T>, dir: 1 | -1): void {\n grid._rows = sortedRows;\n // Bump epoch so renderVisibleRows triggers full inline rebuild\n grid.__rowRenderEpoch++;\n // Invalidate pooled rows to guarantee rebuild\n grid._rowPool.forEach((r) => (r.__epoch = -1));\n renderHeader(grid);\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: dir } }),\n );\n // Trigger state change after sort applied\n grid.requestStateChange?.();\n}\n\n/**\n * Cycle sort state for a column: none -> ascending -> descending -> none.\n * Restores original row order when clearing sort.\n */\nexport function toggleSort(grid: InternalGrid, col: ColumnConfig<any>): void {\n if (!grid._sortState || grid._sortState.field !== col.field) {\n if (!grid._sortState) grid.__originalOrder = grid._rows.slice();\n applySort(grid, col, 1);\n } else if (grid._sortState.direction === 1) {\n applySort(grid, col, -1);\n } else {\n grid._sortState = null;\n // Force full row rebuild after clearing sort so templated cells reflect original order\n grid.__rowRenderEpoch++;\n // Invalidate existing pooled row epochs so virtualization triggers a full inline rebuild\n grid._rowPool.forEach((r) => (r.__epoch = -1));\n grid._rows = grid.__originalOrder.slice();\n renderHeader(grid);\n // After re-render ensure cleared column shows aria-sort=\"none\" baseline.\n const headers = grid._headerRowEl?.querySelectorAll('[role=\"columnheader\"].sortable');\n headers?.forEach((h) => {\n if (!h.getAttribute('aria-sort')) h.setAttribute('aria-sort', 'none');\n else if (h.getAttribute('aria-sort') === 'ascending' || h.getAttribute('aria-sort') === 'descending') {\n // The active column was re-rendered already, but normalize any missing ones.\n if (!grid._sortState) h.setAttribute('aria-sort', 'none');\n }\n });\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: 0 } }),\n );\n // Trigger state change after sort is cleared\n grid.requestStateChange?.();\n }\n}\n\n/**\n * Apply a concrete sort direction to rows.\n *\n * Uses custom sortHandler from gridConfig if provided, otherwise uses built-in sorting.\n * Supports both sync and async handlers (for server-side sorting).\n */\nexport function applySort(grid: InternalGrid, col: ColumnConfig<any>, dir: 1 | -1): void {\n grid._sortState = { field: col.field, direction: dir };\n\n const sortState: SortState = { field: col.field, direction: dir };\n const columns = grid._columns as ColumnConfig<any>[];\n\n // Get custom handler from effectiveConfig, or use built-in\n const handler: SortHandler<any> = grid.effectiveConfig?.sortHandler ?? builtInSort;\n\n const result = handler(grid._rows, sortState, columns);\n\n // Handle async (Promise) or sync result\n if (result && typeof (result as Promise<unknown[]>).then === 'function') {\n // Async handler - wait for result\n (result as Promise<unknown[]>).then((sortedRows) => {\n finalizeSortResult(grid, sortedRows, col, dir);\n });\n } else {\n // Sync handler - apply immediately\n finalizeSortResult(grid, result as unknown[], col, dir);\n }\n}\n","/**\n * Header Rendering Module\n *\n * Handles rendering of the grid header row with sorting and resize affordances.\n */\n\nimport type { ColumnInternal, IconValue, InternalGrid } from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\nimport { addPart } from './columns';\nimport { toggleSort } from './sorting';\n\n/**\n * Set an icon value on an element. Handles both string and HTMLElement icons.\n */\nfunction setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.textContent = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n}\n\n/**\n * Rebuild the header row DOM based on current column configuration, attaching\n * sorting and resize affordances where enabled.\n */\nexport function renderHeader(grid: InternalGrid): void {\n grid._headerRowEl = grid.findHeaderRow!();\n const headerRow = grid._headerRowEl as HTMLElement;\n\n // Guard: DOM may not be built yet\n if (!headerRow) {\n return;\n }\n\n headerRow.innerHTML = '';\n\n grid._visibleColumns.forEach((col: ColumnInternal, i: number) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n addPart(cell, 'header-cell');\n cell.setAttribute('role', 'columnheader');\n\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(i + 1));\n cell.setAttribute('data-field', col.field);\n cell.setAttribute('data-col', String(i)); // Add data-col for consistency with body cells\n\n // Column grouping styling is handled by the grouping-columns plugin via afterRender\n const maybeTpl = col.__headerTemplate;\n if (maybeTpl) Array.from(maybeTpl.childNodes).forEach((n) => cell.appendChild(n.cloneNode(true)));\n else {\n // Use header if defined (including empty string), otherwise fall back to field name\n const label = col.header ?? col.field;\n const span = document.createElement('span');\n span.textContent = label;\n cell.appendChild(span);\n }\n if (col.sortable) {\n cell.classList.add('sortable');\n cell.tabIndex = 0;\n const icon = document.createElement('span');\n addPart(icon, 'sort-indicator');\n const active = grid._sortState?.field === col.field ? grid._sortState.direction : 0;\n // Use grid-level icons (fall back to defaults)\n const icons = { ...DEFAULT_GRID_ICONS, ...grid.icons };\n const iconValue = active === 1 ? icons.sortAsc : active === -1 ? icons.sortDesc : icons.sortNone;\n setIcon(icon, iconValue);\n cell.appendChild(icon);\n // Always set a baseline aria-sort for sortable headers for assistive tech clarity.\n cell.setAttribute('aria-sort', active === 0 ? 'none' : active === 1 ? 'ascending' : 'descending');\n cell.addEventListener('click', (e) => {\n // Ignore clicks that are the result of a resize drag ending\n if (grid._resizeController?.isResizing) return;\n // Let plugins handle the click first (e.g., multi-sort)\n if (grid._dispatchHeaderClick?.(e, i, cell)) return;\n toggleSort(grid, col);\n });\n cell.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n // Let plugins handle the keydown first\n if (grid._dispatchHeaderClick?.(e as unknown as MouseEvent, i, cell)) return;\n toggleSort(grid, col);\n }\n });\n }\n if (col.resizable) {\n // Add class for resize handle positioning context (CSS provides position: relative)\n // Note: If a plugin applies position: sticky (e.g., PinnedColumnsPlugin), it will override this\n cell.classList.add('resizable');\n const handle = document.createElement('div');\n handle.className = 'resize-handle';\n handle.setAttribute('aria-hidden', 'true');\n handle.addEventListener('mousedown', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.start(e, i, cell);\n });\n // Double-click to reset column width to default\n handle.addEventListener('dblclick', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.resetColumn(i);\n });\n cell.appendChild(handle);\n }\n headerRow.appendChild(cell);\n });\n\n // Ensure every sortable header has a baseline aria-sort if not already set during construction.\n headerRow.querySelectorAll('.cell.sortable').forEach((el) => {\n if (!el.getAttribute('aria-sort')) el.setAttribute('aria-sort', 'none');\n });\n\n // Set ARIA role only if header has children (role=\"row\" requires columnheader children)\n // When grid is cleared with 0 columns, the header row should not have role=\"row\"\n if (headerRow.children.length > 0) {\n headerRow.setAttribute('role', 'row');\n headerRow.setAttribute('aria-rowindex', '1');\n } else {\n headerRow.removeAttribute('role');\n headerRow.removeAttribute('aria-rowindex');\n }\n}\n","/**\n * Idle Scheduler - Defer non-critical work to browser idle periods.\n *\n * Uses requestIdleCallback where available, with fallback to setTimeout.\n * This allows the main thread to remain responsive during startup.\n */\n\n/**\n * Check if requestIdleCallback is available (not in Safari < 17.4).\n */\nconst hasIdleCallback = typeof requestIdleCallback === 'function';\n\n/**\n * IdleDeadline-compatible interface for fallback.\n */\ninterface IdleDeadlineLike {\n didTimeout: boolean;\n timeRemaining(): number;\n}\n\n/**\n * Schedule work to run during browser idle time.\n * Falls back to setTimeout(0) if requestIdleCallback is not available.\n *\n * @param callback - Work to run when idle\n * @param options - Optional timeout configuration\n * @returns Handle for cancellation\n */\nexport function scheduleIdle(callback: (deadline: IdleDeadlineLike) => void, options?: { timeout?: number }): number {\n if (hasIdleCallback) {\n return requestIdleCallback(callback, options);\n }\n\n // Fallback for Safari (before 17.4) and older browsers\n return window.setTimeout(() => {\n const start = Date.now();\n callback({\n didTimeout: false,\n timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),\n });\n }, 1) as unknown as number;\n}\n\n/**\n * Cancel a scheduled idle callback.\n */\nexport function cancelIdle(handle: number): void {\n if (hasIdleCallback) {\n cancelIdleCallback(handle);\n } else {\n clearTimeout(handle);\n }\n}\n\n/**\n * Queue of deferred tasks to run during idle periods.\n */\ninterface DeferredTask {\n fn: () => void;\n priority: number; // Lower = higher priority\n}\n\n/**\n * Deferred work queue that runs tasks during idle periods.\n * Groups related work and processes in priority order.\n */\nexport class IdleQueue {\n private tasks: DeferredTask[] = [];\n private scheduled = false;\n private handle: number | null = null;\n\n /**\n * Add a task to the queue.\n * @param fn - Function to execute\n * @param priority - Priority (lower = run sooner). Default 10.\n */\n add(fn: () => void, priority = 10): void {\n this.tasks.push({ fn, priority });\n\n if (!this.scheduled) {\n this.scheduled = true;\n this.handle = scheduleIdle((deadline) => this.process(deadline), { timeout: 100 });\n }\n }\n\n /**\n * Process tasks until we run out of time or tasks.\n */\n private process(deadline: IdleDeadlineLike): void {\n // Sort by priority (stable sort for same priority)\n this.tasks.sort((a, b) => a.priority - b.priority);\n\n // Process tasks while we have time\n while (this.tasks.length > 0 && (deadline.timeRemaining() > 0 || deadline.didTimeout)) {\n const task = this.tasks.shift();\n if (task) {\n try {\n task.fn();\n } catch (error) {\n console.error('[IdleQueue] Task error:', error);\n }\n }\n }\n\n // If tasks remain, schedule another idle callback\n if (this.tasks.length > 0) {\n this.handle = scheduleIdle((d) => this.process(d), { timeout: 100 });\n } else {\n this.scheduled = false;\n this.handle = null;\n }\n }\n\n /**\n * Cancel all pending tasks.\n */\n cancel(): void {\n this.tasks = [];\n if (this.handle !== null) {\n cancelIdle(this.handle);\n this.handle = null;\n }\n this.scheduled = false;\n }\n\n /**\n * Check if the queue is empty.\n */\n get isEmpty(): boolean {\n return this.tasks.length === 0;\n }\n}\n","/**\n * Centralized Render Scheduler for the Grid component.\n *\n * This scheduler batches all rendering work into a single requestAnimationFrame,\n * eliminating race conditions between different parts of the grid (ResizeObserver,\n * framework adapters, virtualization, etc.) that previously scheduled independent RAFs.\n *\n * ## Design Principles\n *\n * 1. **Single RAF per frame**: All render requests are batched into one RAF callback\n * 2. **Phase-based execution**: Work is organized into ordered phases that run sequentially\n * 3. **Highest-phase wins**: Multiple requests merge to the highest requested phase\n * 4. **Framework-agnostic timing**: Eliminates need for \"double RAF\" hacks\n *\n * ## Render Phases (execute in order)\n *\n * - STYLE (1): Lightweight style/class updates, plugin afterRender hooks\n * - VIRTUALIZATION (2): Virtual window recalculation (scroll, resize)\n * - HEADER (3): Header re-render only\n * - ROWS (4): Row model rebuild + header + template + virtual window\n * - COLUMNS (5): Column processing + rows phase work\n * - FULL (6): Complete render including config merge\n *\n * @example\n * ```typescript\n * const scheduler = new RenderScheduler({\n * mergeConfig: () => this.#mergeEffectiveConfig(),\n * processColumns: () => this.#processColumns(),\n * processRows: () => this.#rebuildRowModel(),\n * renderHeader: () => renderHeader(this),\n * updateTemplate: () => updateTemplate(this),\n * renderVirtualWindow: () => this.refreshVirtualWindow(true),\n * afterRender: () => this.#pluginManager?.afterRender(),\n * isConnected: () => this.isConnected && this.#connected,\n * });\n *\n * // Request a full render\n * scheduler.requestPhase(RenderPhase.FULL, 'initial-setup');\n *\n * // Wait for render to complete\n * await scheduler.whenReady();\n * ```\n */\n\n/**\n * Render phases in order of execution.\n * Higher phases include all lower phase work.\n *\n * @category Plugin Development\n */\nexport enum RenderPhase {\n /** Lightweight style updates only (plugin afterRender hooks) */\n STYLE = 1,\n /** Virtual window recalculation (includes STYLE) */\n VIRTUALIZATION = 2,\n /** Header re-render (includes VIRTUALIZATION) */\n HEADER = 3,\n /** Row model rebuild (includes HEADER) */\n ROWS = 4,\n /** Column processing (includes ROWS) */\n COLUMNS = 5,\n /** Full render including config merge (includes COLUMNS) */\n FULL = 6,\n}\n\n/**\n/**\n * Callbacks that the scheduler invokes during flush.\n * Each callback corresponds to work done in a specific phase.\n */\nexport interface RenderCallbacks {\n /** Merge effective config (FULL phase) */\n mergeConfig: () => void;\n /** Process columns through plugins (COLUMNS phase) */\n processColumns: () => void;\n /** Rebuild row model through plugins (ROWS phase) */\n processRows: () => void;\n /** Render header DOM (HEADER phase) */\n renderHeader: () => void;\n /** Update CSS grid template (ROWS phase) */\n updateTemplate: () => void;\n /** Recalculate virtual window (VIRTUALIZATION phase) */\n renderVirtualWindow: () => void;\n /** Run plugin afterRender hooks (STYLE phase) */\n afterRender: () => void;\n /** Check if grid is still connected to DOM */\n isConnected: () => boolean;\n}\n\n/**\n * Centralized render scheduler that batches all grid rendering into single RAF.\n */\nexport class RenderScheduler {\n readonly #callbacks: RenderCallbacks;\n\n /** Current pending phase (0 = none pending) */\n #pendingPhase: RenderPhase | 0 = 0;\n\n /** RAF handle for cancellation */\n #rafHandle = 0;\n\n /** Promise that resolves when current render completes */\n #readyPromise: Promise<void> | null = null;\n #readyResolve: (() => void) | null = null;\n\n /** Initial ready resolver (for component's initial ready() promise) */\n #initialReadyResolver: (() => void) | null = null;\n #initialReadyFired = false;\n\n constructor(callbacks: RenderCallbacks) {\n this.#callbacks = callbacks;\n }\n\n /**\n * Request a render at the specified phase.\n * Multiple requests are batched - the highest phase wins.\n *\n * @param phase - The render phase to execute\n * @param _source - Debug identifier for what triggered this request (unused, kept for API compatibility)\n */\n requestPhase(phase: RenderPhase, _source: string): void {\n // Merge to highest phase\n if (phase > this.#pendingPhase) {\n this.#pendingPhase = phase;\n }\n\n // Schedule RAF if not already scheduled\n if (this.#rafHandle === 0) {\n this.#ensureReadyPromise();\n this.#rafHandle = requestAnimationFrame(() => this.#flush());\n }\n }\n\n /**\n * Returns a promise that resolves when the current render cycle completes.\n * If no render is pending, returns an already-resolved promise.\n */\n whenReady(): Promise<void> {\n if (this.#readyPromise) {\n return this.#readyPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Set the initial ready resolver (called once on first render).\n * This connects to the grid's `ready()` promise.\n */\n setInitialReadyResolver(resolver: () => void): void {\n this.#initialReadyResolver = resolver;\n }\n\n /**\n * Cancel any pending render.\n * Useful for cleanup when component disconnects.\n */\n cancel(): void {\n if (this.#rafHandle !== 0) {\n cancelAnimationFrame(this.#rafHandle);\n this.#rafHandle = 0;\n }\n this.#pendingPhase = 0;\n\n // Resolve any pending ready promise (don't leave it hanging)\n if (this.#readyResolve) {\n this.#readyResolve();\n this.#readyResolve = null;\n this.#readyPromise = null;\n }\n }\n\n /**\n * Check if a render is currently pending.\n */\n get isPending(): boolean {\n return this.#pendingPhase !== 0;\n }\n\n /**\n * Get the current pending phase (0 if none).\n */\n get pendingPhase(): RenderPhase | 0 {\n return this.#pendingPhase;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Private Methods\n // ─────────────────────────────────────────────────────────────────────────\n\n #ensureReadyPromise(): void {\n if (!this.#readyPromise) {\n this.#readyPromise = new Promise<void>((resolve) => {\n this.#readyResolve = resolve;\n });\n }\n }\n\n /**\n * Execute all pending render work in phase order.\n * This is the single RAF callback that does all rendering.\n */\n #flush(): void {\n this.#rafHandle = 0;\n\n // Bail if component disconnected\n if (!this.#callbacks.isConnected()) {\n this.#pendingPhase = 0;\n if (this.#readyResolve) {\n this.#readyResolve();\n this.#readyResolve = null;\n this.#readyPromise = null;\n }\n return;\n }\n\n const phase = this.#pendingPhase;\n this.#pendingPhase = 0;\n\n // Execute phases in order (higher phases include lower phase work)\n // The execution order respects data dependencies:\n // mergeConfig → processRows → processColumns → renderHeader → virtualWindow → afterRender\n\n // mergeConfig runs for FULL phase OR COLUMNS phase (to pick up framework adapter renderers)\n // IMPORTANT: mergeConfig must run BEFORE processRows because the row model depends on\n // column configuration, and framework adapters (React/Angular) may register renderers\n // asynchronously after the initial gridConfig is set.\n if (phase >= RenderPhase.COLUMNS) {\n this.#callbacks.mergeConfig();\n }\n\n // Phase 4 (ROWS): Rebuild row model\n // NOTE: processRows MUST run before processColumns because tree plugin's\n // processColumns depends on flattenedRows populated by processRows\n if (phase >= RenderPhase.ROWS) {\n this.#callbacks.processRows();\n }\n\n // Phase 5 (COLUMNS): Process columns + update template\n if (phase >= RenderPhase.COLUMNS) {\n this.#callbacks.processColumns();\n this.#callbacks.updateTemplate();\n }\n\n // Phase 3 (HEADER): Render header\n if (phase >= RenderPhase.HEADER) {\n this.#callbacks.renderHeader();\n }\n\n // Phase 2 (VIRTUALIZATION): Recalculate virtual window\n if (phase >= RenderPhase.VIRTUALIZATION) {\n this.#callbacks.renderVirtualWindow();\n }\n\n // Phase 1 (STYLE): Run afterRender hooks (always runs)\n if (phase >= RenderPhase.STYLE) {\n this.#callbacks.afterRender();\n }\n\n // Fire initial ready resolver once\n if (!this.#initialReadyFired && this.#initialReadyResolver) {\n this.#initialReadyFired = true;\n this.#initialReadyResolver();\n }\n\n // Resolve the ready promise\n if (this.#readyResolve) {\n this.#readyResolve();\n this.#readyResolve = null;\n this.#readyPromise = null;\n }\n }\n}\n","import type { InternalGrid, ResizeController } from '../types';\n\nexport function createResizeController(grid: InternalGrid): ResizeController {\n let resizeState: { startX: number; colIndex: number; startWidth: number } | null = null;\n let pendingRaf: number | null = null;\n let prevCursor: string | null = null;\n let prevUserSelect: string | null = null;\n const onMove = (e: MouseEvent) => {\n if (!resizeState) return;\n const delta = e.clientX - resizeState.startX;\n const width = Math.max(40, resizeState.startWidth + delta);\n const col = grid._visibleColumns[resizeState.colIndex];\n col.width = width;\n col.__userResized = true;\n col.__renderedWidth = width;\n if (pendingRaf == null) {\n pendingRaf = requestAnimationFrame(() => {\n pendingRaf = null;\n grid.updateTemplate?.();\n });\n }\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize', { detail: { field: col.field, width } }),\n );\n };\n let justFinishedResize = false;\n const onUp = () => {\n const hadResize = resizeState !== null;\n // Set flag to suppress click events that fire immediately after mouseup\n if (hadResize) {\n justFinishedResize = true;\n requestAnimationFrame(() => {\n justFinishedResize = false;\n });\n }\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n if (prevCursor !== null) {\n document.documentElement.style.cursor = prevCursor;\n prevCursor = null;\n }\n if (prevUserSelect !== null) {\n document.body.style.userSelect = prevUserSelect;\n prevUserSelect = null;\n }\n resizeState = null;\n // Trigger state change after resize completes\n if (hadResize && grid.requestStateChange) {\n grid.requestStateChange();\n }\n };\n return {\n get isResizing() {\n return resizeState !== null || justFinishedResize;\n },\n start(e, colIndex, cell) {\n e.preventDefault();\n // Use the column's configured/rendered width, not the cell's bounding rect.\n // The bounding rect can be incorrect if CSS grid-column spanning is in effect\n // (e.g., when previous columns are display:none and this cell spans multiple tracks).\n const col = grid._visibleColumns[colIndex];\n // Only use numeric widths; string widths (e.g., \"100px\", \"20%\") fall back to bounding rect\n const colWidth = typeof col?.width === 'number' ? col.width : undefined;\n const startWidth = col?.__renderedWidth ?? colWidth ?? cell.getBoundingClientRect().width;\n resizeState = { startX: e.clientX, colIndex, startWidth };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n if (prevCursor === null) prevCursor = document.documentElement.style.cursor;\n document.documentElement.style.cursor = 'e-resize';\n if (prevUserSelect === null) prevUserSelect = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n },\n resetColumn(colIndex) {\n const col = grid._visibleColumns[colIndex];\n if (!col) return;\n\n // Reset to original configured width (or undefined for auto-sizing)\n col.__userResized = false;\n col.__renderedWidth = undefined;\n col.width = col.__originalWidth;\n\n grid.updateTemplate?.();\n grid.requestStateChange?.();\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize-reset', { detail: { field: col.field, width: col.width } }),\n );\n },\n dispose() {\n onUp();\n },\n };\n}\n","/**\n * DOM Builder - Direct DOM construction for performance.\n *\n * Using direct DOM APIs instead of innerHTML is significantly faster:\n * - No HTML parsing by the browser\n * - No template string concatenation\n * - Immediate element creation without serialization/deserialization\n *\n * Benchmark: DOM construction is ~2-3x faster than innerHTML for complex structures.\n */\n\n/**\n * Create an element with attributes and optional children.\n * Optimized helper that avoids repeated function calls.\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string | null | undefined)[],\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n\n if (children) {\n for (const child of children) {\n if (child == null) continue;\n if (typeof child === 'string') {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n }\n\n return el;\n}\n\n/**\n * Create a text node (shorthand).\n */\nexport function text(content: string): Text {\n return document.createTextNode(content);\n}\n\n/**\n * Create an element with class (common pattern).\n */\nexport function div(className?: string, attrs?: Record<string, string>): HTMLDivElement {\n const el = document.createElement('div');\n if (className) el.className = className;\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n return el;\n}\n\n/**\n * Create a button element.\n */\nexport function button(className?: string, attrs?: Record<string, string>, content?: string | Node): HTMLButtonElement {\n const el = document.createElement('button');\n if (className) el.className = className;\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n if (content) {\n if (typeof content === 'string') {\n el.textContent = content;\n } else {\n el.appendChild(content);\n }\n }\n return el;\n}\n\n/**\n * Append multiple children to a parent element.\n */\nexport function appendChildren(parent: Element, children: (Node | null | undefined)[]): void {\n for (const child of children) {\n if (child) parent.appendChild(child);\n }\n}\n\n/**\n * Set multiple attributes on an element.\n */\nexport function setAttrs(el: Element, attrs: Record<string, string | undefined>): void {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n}\n\n// ============================================================================\n// Grid Structure Templates (Pre-built for cloning)\n// ============================================================================\n\n/**\n * Template for grid content (the core scrollable grid area).\n * Pre-built once, then cloned for each grid instance.\n */\nconst gridContentTemplate = document.createElement('template');\ngridContentTemplate.innerHTML = `\n <div class=\"tbw-scroll-area\">\n <div class=\"rows-body-wrapper\">\n <div class=\"rows-body\" role=\"grid\">\n <div class=\"header\" role=\"rowgroup\">\n <div class=\"header-row\" role=\"row\" part=\"header-row\"></div>\n </div>\n <div class=\"rows-container\" role=\"presentation\">\n <div class=\"rows-viewport\" role=\"presentation\">\n <div class=\"rows\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"faux-vscroll\">\n <div class=\"faux-vscroll-spacer\"></div>\n </div>\n`;\n\n/**\n * Clone the grid content structure.\n * Using template cloning is faster than createElement for nested structures.\n */\nexport function cloneGridContent(): DocumentFragment {\n return gridContentTemplate.content.cloneNode(true) as DocumentFragment;\n}\n\n/**\n * Build the grid root structure using direct DOM construction.\n * This is called once per grid instance during initial render.\n */\nexport interface GridDOMOptions {\n hasShell: boolean;\n /** Shell header element (pre-built) */\n shellHeader?: Element;\n /** Shell body element with tool panel (pre-built) */\n shellBody?: Element;\n}\n\n/**\n * Build the complete grid DOM structure.\n * Returns a DocumentFragment ready to be appended to shadow root.\n */\nexport function buildGridDOM(options: GridDOMOptions): DocumentFragment {\n const fragment = document.createDocumentFragment();\n\n const root = div(options.hasShell ? 'tbw-grid-root has-shell' : 'tbw-grid-root');\n\n if (options.hasShell && options.shellHeader && options.shellBody) {\n // Shell mode: header + body (with grid content inside)\n root.appendChild(options.shellHeader);\n root.appendChild(options.shellBody);\n } else {\n // No shell: just grid content in wrapper\n const contentWrapper = div('tbw-grid-content');\n contentWrapper.appendChild(cloneGridContent());\n root.appendChild(contentWrapper);\n }\n\n fragment.appendChild(root);\n return fragment;\n}\n\n/**\n * Build shell header using direct DOM construction.\n *\n * Note: The grid no longer creates buttons from config (icon/action).\n * Config/API buttons only use element or render function.\n * Light DOM buttons are slotted directly.\n * The ONLY button the grid creates is the panel toggle.\n */\nexport interface ShellHeaderOptions {\n title?: string;\n hasPanels: boolean;\n isPanelOpen: boolean;\n toolPanelIcon: string;\n /** Config toolbar contents with render function (pre-sorted by order) */\n configButtons: Array<{\n id: string;\n hasRender?: boolean;\n }>;\n /** API toolbar contents with render function (pre-sorted by order) */\n apiButtons: Array<{\n id: string;\n hasRender?: boolean;\n }>;\n}\n\n/**\n * Build shell header element directly without innerHTML.\n */\nexport function buildShellHeader(options: ShellHeaderOptions): HTMLDivElement {\n const header = div('tbw-shell-header', { part: 'shell-header', role: 'presentation' });\n\n // Title\n if (options.title) {\n const titleEl = div('tbw-shell-title');\n titleEl.textContent = options.title;\n header.appendChild(titleEl);\n }\n\n // Shell content with placeholder for light DOM header content\n const content = div('tbw-shell-content', {\n part: 'shell-content',\n role: 'presentation',\n 'data-light-dom-header-content': '',\n });\n header.appendChild(content);\n\n // Toolbar\n const toolbar = div('tbw-shell-toolbar', { part: 'shell-toolbar', role: 'presentation' });\n\n // Placeholders for config toolbar contents with render function\n for (const btn of options.configButtons) {\n if (btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-content-slot', { 'data-toolbar-content': btn.id }));\n }\n }\n // Placeholders for API toolbar contents with render function\n for (const btn of options.apiButtons) {\n if (btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-content-slot', { 'data-toolbar-content': btn.id }));\n }\n }\n\n // Separator between custom content and panel toggle\n const hasCustomContent =\n options.configButtons.some((b) => b.hasRender) || options.apiButtons.some((b) => b.hasRender);\n if (hasCustomContent && options.hasPanels) {\n toolbar.appendChild(div('tbw-toolbar-separator'));\n }\n\n // Panel toggle button\n if (options.hasPanels) {\n const toggleBtn = button(options.isPanelOpen ? 'tbw-toolbar-btn active' : 'tbw-toolbar-btn', {\n 'data-panel-toggle': '',\n title: 'Settings',\n 'aria-label': 'Toggle settings panel',\n 'aria-pressed': String(options.isPanelOpen),\n 'aria-controls': 'tbw-tool-panel',\n });\n toggleBtn.innerHTML = options.toolPanelIcon;\n toolbar.appendChild(toggleBtn);\n }\n\n header.appendChild(toolbar);\n return header;\n}\n\n/**\n * Build shell body (grid content + optional tool panel).\n */\nexport interface ShellBodyOptions {\n position: 'left' | 'right';\n isPanelOpen: boolean;\n expandIcon: string;\n collapseIcon: string;\n /** Sorted panels for accordion */\n panels: Array<{\n id: string;\n title: string;\n icon?: string;\n isExpanded: boolean;\n }>;\n}\n\n/**\n * Build shell body element directly without innerHTML.\n */\nexport function buildShellBody(options: ShellBodyOptions): HTMLDivElement {\n const body = div('tbw-shell-body');\n const hasPanel = options.panels.length > 0;\n const isSinglePanel = options.panels.length === 1;\n\n // Grid content wrapper with cloned grid structure\n const gridContent = div('tbw-grid-content');\n gridContent.appendChild(cloneGridContent());\n\n // Tool panel\n let panelEl: HTMLElement | null = null;\n if (hasPanel) {\n panelEl = createElement('aside', {\n class: options.isPanelOpen ? 'tbw-tool-panel open' : 'tbw-tool-panel',\n part: 'tool-panel',\n 'data-position': options.position,\n role: 'presentation',\n id: 'tbw-tool-panel',\n });\n\n // Resize handle\n const resizeHandlePosition = options.position === 'left' ? 'right' : 'left';\n panelEl.appendChild(\n div('tbw-tool-panel-resize', {\n 'data-resize-handle': '',\n 'data-handle-position': resizeHandlePosition,\n 'aria-hidden': 'true',\n }),\n );\n\n // Panel content with accordion\n const panelContent = div('tbw-tool-panel-content', { role: 'presentation' });\n const accordion = div('tbw-accordion');\n\n for (const panel of options.panels) {\n const sectionClasses = `tbw-accordion-section${panel.isExpanded ? ' expanded' : ''}${isSinglePanel ? ' single' : ''}`;\n const section = div(sectionClasses, { 'data-section': panel.id });\n\n // Accordion header button\n const headerBtn = button('tbw-accordion-header', {\n 'aria-expanded': String(panel.isExpanded),\n 'aria-controls': `tbw-section-${panel.id}`,\n });\n if (isSinglePanel) headerBtn.setAttribute('aria-disabled', 'true');\n\n // Icon\n if (panel.icon) {\n const iconSpan = createElement('span', { class: 'tbw-accordion-icon' });\n iconSpan.innerHTML = panel.icon;\n headerBtn.appendChild(iconSpan);\n }\n\n // Title\n const titleSpan = createElement('span', { class: 'tbw-accordion-title' });\n titleSpan.textContent = panel.title;\n headerBtn.appendChild(titleSpan);\n\n // Chevron (hidden for single panel)\n if (!isSinglePanel) {\n const chevronSpan = createElement('span', { class: 'tbw-accordion-chevron' });\n chevronSpan.innerHTML = panel.isExpanded ? options.collapseIcon : options.expandIcon;\n headerBtn.appendChild(chevronSpan);\n }\n\n section.appendChild(headerBtn);\n\n // Accordion content (empty, will be filled by panel render functions)\n section.appendChild(\n div('tbw-accordion-content', {\n id: `tbw-section-${panel.id}`,\n role: 'presentation',\n }),\n );\n\n accordion.appendChild(section);\n }\n\n panelContent.appendChild(accordion);\n panelEl.appendChild(panelContent);\n }\n\n // Append in correct order based on position\n if (options.position === 'left' && panelEl) {\n body.appendChild(panelEl);\n body.appendChild(gridContent);\n } else {\n body.appendChild(gridContent);\n if (panelEl) body.appendChild(panelEl);\n }\n\n return body;\n}\n","/**\n * Shell infrastructure for grid header bar and tool panels.\n *\n * The shell is an optional wrapper layer that provides:\n * - Header bar with title, plugin content, and toolbar buttons\n * - Tool panels that plugins can register content into\n * - Light DOM parsing for framework-friendly configuration\n */\n\nimport type {\n HeaderContentDefinition,\n IconValue,\n ShellConfig,\n ToolbarContentDefinition,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\nimport { escapeHtml } from './sanitize';\n\n/**\n * Convert an IconValue to a string for rendering in HTML.\n */\nfunction iconToString(icon: IconValue | undefined): string {\n if (!icon) return '';\n if (typeof icon === 'string') return icon;\n // For HTMLElement, get the outerHTML\n return icon.outerHTML;\n}\n\n/**\n * State for managing shell UI.\n *\n * This interface holds both configuration-like properties (toolPanels, headerContents)\n * and runtime state (isPanelOpen, expandedSections). The Maps allow for efficient\n * registration/unregistration of panels and content.\n */\nexport interface ShellState {\n /** Registered tool panels (from plugins + consumer API) */\n toolPanels: Map<string, ToolPanelDefinition>;\n /** Registered header content (from plugins + consumer API) */\n headerContents: Map<string, HeaderContentDefinition>;\n /** Toolbar content registered via API or light DOM */\n toolbarContents: Map<string, ToolbarContentDefinition>;\n /** Whether a <tbw-grid-tool-buttons> container was found in light DOM */\n hasToolButtonsContainer: boolean;\n /** Light DOM header content elements */\n lightDomHeaderContent: HTMLElement[];\n /** Light DOM header title from <tbw-grid-header title=\"...\"> */\n lightDomTitle: string | null;\n /** IDs of tool panels registered from light DOM (to avoid re-parsing) */\n lightDomToolPanelIds: Set<string>;\n /** IDs of toolbar content registered from light DOM (to avoid re-parsing) */\n lightDomToolbarContentIds: Set<string>;\n /** IDs of tool panels registered via registerToolPanel API */\n apiToolPanelIds: Set<string>;\n /** Whether the tool panel sidebar is open */\n isPanelOpen: boolean;\n /** Which accordion sections are expanded (by panel ID) */\n expandedSections: Set<string>;\n /** Whether light DOM header content has been moved to placeholder (perf optimization) */\n lightDomContentMoved: boolean;\n /** Cleanup functions for header content render returns */\n headerContentCleanups: Map<string, () => void>;\n /** Cleanup functions for each panel section's render return */\n panelCleanups: Map<string, () => void>;\n /** Cleanup functions for toolbar content render returns */\n toolbarContentCleanups: Map<string, () => void>;\n}\n\n/**\n * Runtime-only shell state (not configuration).\n *\n * Configuration (toolPanels, headerContents, toolbarContents, title) lives in\n * effectiveConfig.shell. This state holds runtime UI state and cleanup functions.\n */\nexport interface ShellRuntimeState {\n /** Whether the tool panel sidebar is currently open */\n isPanelOpen: boolean;\n /** Which accordion sections are currently expanded (by panel ID) */\n expandedSections: Set<string>;\n /** Cleanup functions for header content render returns */\n headerContentCleanups: Map<string, () => void>;\n /** Cleanup functions for each panel section's render return */\n panelCleanups: Map<string, () => void>;\n /** Cleanup functions for toolbar content render returns */\n toolbarContentCleanups: Map<string, () => void>;\n /** IDs of tool panels registered from light DOM (to avoid re-parsing) */\n lightDomToolPanelIds: Set<string>;\n /** IDs of tool panels registered via registerToolPanel API */\n apiToolPanelIds: Set<string>;\n /** Whether a <tbw-grid-tool-buttons> container was found in light DOM */\n hasToolButtonsContainer: boolean;\n}\n\n/**\n * Create initial shell runtime state.\n */\nexport function createShellRuntimeState(): ShellRuntimeState {\n return {\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarContentCleanups: new Map(),\n lightDomToolPanelIds: new Set(),\n apiToolPanelIds: new Set(),\n hasToolButtonsContainer: false,\n };\n}\n\n/**\n * Create initial shell state.\n */\nexport function createShellState(): ShellState {\n return {\n toolPanels: new Map(),\n headerContents: new Map(),\n toolbarContents: new Map(),\n hasToolButtonsContainer: false,\n lightDomHeaderContent: [],\n lightDomTitle: null,\n lightDomToolPanelIds: new Set(),\n lightDomToolbarContentIds: new Set(),\n apiToolPanelIds: new Set(),\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarContentCleanups: new Map(),\n lightDomContentMoved: false,\n };\n}\n\n/**\n * Determine if shell header should be rendered.\n * Reads only from effectiveConfig.shell (single source of truth).\n */\nexport function shouldRenderShellHeader(config: ShellConfig | undefined): boolean {\n // Check if title is configured\n if (config?.header?.title) return true;\n\n // Check if config has toolbar contents\n if (config?.header?.toolbarContents?.length) return true;\n\n // Check if any tool panels are registered\n if (config?.toolPanels?.length) return true;\n\n // Check if any header content is registered\n if (config?.headerContents?.length) return true;\n\n // Check if light DOM has header elements\n if (config?.header?.lightDomContent?.length) return true;\n\n // Check if a toolbar buttons container was found\n if (config?.header?.hasToolButtonsContainer) return true;\n\n return false;\n}\n\n/**\n * Render the shell header HTML.\n *\n * Toolbar contents come from two sources:\n * 1. Light DOM slot (users provide their own HTML in <tbw-grid-tool-buttons>)\n * 2. Config/API with render function (programmatic insertion)\n *\n * Users have full control over toolbar HTML, styling, and behavior.\n * The only button the grid creates is the tool panel toggle.\n *\n * @param toolPanelIcon - Icon for the tool panel toggle (from grid icon config)\n */\nexport function renderShellHeader(\n config: ShellConfig | undefined,\n state: ShellState,\n toolPanelIcon: IconValue = '☰',\n): string {\n const title = config?.header?.title ?? state.lightDomTitle ?? '';\n const hasTitle = !!title;\n const iconStr = iconToString(toolPanelIcon);\n\n // Get all toolbar contents from effectiveConfig (already merged: config + API + light DOM)\n // The config-manager merges state.toolbarContents into effectiveConfig.shell.header.toolbarContents\n // Also include state.toolbarContents directly for cases where renderShellHeader is called\n // before config-manager has merged (e.g., unit tests, initial render)\n const configContents = config?.header?.toolbarContents ?? [];\n const stateContents = [...state.toolbarContents.values()];\n\n // Merge: use config contents, add state contents that aren't in config\n const configIds = new Set(configContents.map((c) => c.id));\n const allContents = [...configContents];\n for (const content of stateContents) {\n if (!configIds.has(content.id)) {\n allContents.push(content);\n }\n }\n\n const hasCustomContent = allContents.length > 0;\n const hasPanels = state.toolPanels.size > 0;\n const showSeparator = hasCustomContent && hasPanels;\n\n // Sort contents by order for slot placement\n const sortedContents = [...allContents].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n\n // Build toolbar HTML\n let toolbarHtml = '';\n\n // Create slots for all contents (unified: config + API + light DOM)\n for (const content of sortedContents) {\n toolbarHtml += `<div class=\"tbw-toolbar-content-slot\" data-toolbar-content=\"${content.id}\"></div>`;\n }\n\n // Separator between custom content and panel toggle\n if (showSeparator) {\n toolbarHtml += '<div class=\"tbw-toolbar-separator\"></div>';\n }\n\n // Single panel toggle button (the ONLY button the grid creates)\n if (hasPanels) {\n const isOpen = state.isPanelOpen;\n const toggleClass = isOpen ? 'tbw-toolbar-btn active' : 'tbw-toolbar-btn';\n toolbarHtml += `<button class=\"${toggleClass}\" data-panel-toggle title=\"Settings\" aria-label=\"Toggle settings panel\" aria-pressed=\"${isOpen}\" aria-controls=\"tbw-tool-panel\">${iconStr}</button>`;\n }\n\n return `\n <div class=\"tbw-shell-header\" part=\"shell-header\" role=\"presentation\">\n ${hasTitle ? `<div class=\"tbw-shell-title\">${escapeHtml(title)}</div>` : ''}\n <div class=\"tbw-shell-content\" part=\"shell-content\" role=\"presentation\" data-light-dom-header-content></div>\n <div class=\"tbw-shell-toolbar\" part=\"shell-toolbar\" role=\"presentation\">\n ${toolbarHtml}\n </div>\n </div>\n `;\n}\n\n/**\n * Render the shell body wrapper HTML (contains grid content + accordion-style tool panel).\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderShellBody(\n config: ShellConfig | undefined,\n state: ShellState,\n gridContentHtml: string,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): string {\n const position = config?.toolPanel?.position ?? 'right';\n const hasPanel = state.toolPanels.size > 0;\n const isOpen = state.isPanelOpen;\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n // Sort panels by order for accordion sections\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const isSinglePanel = sortedPanels.length === 1;\n\n // Build accordion sections HTML\n let accordionHtml = '';\n for (const panel of sortedPanels) {\n const isExpanded = state.expandedSections.has(panel.id);\n const iconHtml = panel.icon ? `<span class=\"tbw-accordion-icon\">${panel.icon}</span>` : '';\n // Hide chevron for single panel (no toggling needed)\n const chevronHtml = isSinglePanel\n ? ''\n : `<span class=\"tbw-accordion-chevron\">${isExpanded ? collapseIcon : expandIcon}</span>`;\n // Disable accordion toggle for single panel\n const sectionClasses = `tbw-accordion-section${isExpanded ? ' expanded' : ''}${isSinglePanel ? ' single' : ''}`;\n accordionHtml += `\n <div class=\"${sectionClasses}\" data-section=\"${panel.id}\">\n <button class=\"tbw-accordion-header\" aria-expanded=\"${isExpanded}\" aria-controls=\"tbw-section-${panel.id}\"${isSinglePanel ? ' aria-disabled=\"true\"' : ''}>\n ${iconHtml}\n <span class=\"tbw-accordion-title\">${panel.title}</span>\n ${chevronHtml}\n </button>\n <div class=\"tbw-accordion-content\" id=\"tbw-section-${panel.id}\" role=\"presentation\"></div>\n </div>\n `;\n }\n\n // Resize handle position depends on panel position\n const resizeHandlePosition = position === 'left' ? 'right' : 'left';\n\n const panelHtml = hasPanel\n ? `\n <aside class=\"tbw-tool-panel${isOpen ? ' open' : ''}\" part=\"tool-panel\" data-position=\"${position}\" role=\"presentation\" id=\"tbw-tool-panel\">\n <div class=\"tbw-tool-panel-resize\" data-resize-handle data-handle-position=\"${resizeHandlePosition}\" aria-hidden=\"true\"></div>\n <div class=\"tbw-tool-panel-content\" role=\"presentation\">\n <div class=\"tbw-accordion\">\n ${accordionHtml}\n </div>\n </div>\n </aside>\n `\n : '';\n\n // For left position, panel comes before content in DOM order\n if (position === 'left') {\n return `\n <div class=\"tbw-shell-body\">\n ${panelHtml}\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n </div>\n `;\n }\n\n return `\n <div class=\"tbw-shell-body\">\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n ${panelHtml}\n </div>\n `;\n}\n\n/**\n * Parse light DOM shell elements (tbw-grid-header, etc.).\n * Safe to call multiple times - will only parse once when elements are available.\n */\nexport function parseLightDomShell(host: HTMLElement, state: ShellState): void {\n const headerEl = host.querySelector('tbw-grid-header');\n if (!headerEl) return;\n\n // Parse title attribute (only if not already parsed)\n if (!state.lightDomTitle) {\n const title = headerEl.getAttribute('title');\n if (title) {\n state.lightDomTitle = title;\n }\n }\n\n // Parse header content elements - store references but don't set slot (light DOM doesn't use slots)\n const headerContents = headerEl.querySelectorAll('tbw-grid-header-content');\n if (headerContents.length > 0 && state.lightDomHeaderContent.length === 0) {\n state.lightDomHeaderContent = Array.from(headerContents) as HTMLElement[];\n }\n\n // Hide the light DOM header container (it was just for declarative config)\n (headerEl as HTMLElement).style.display = 'none';\n}\n\n/**\n * Callback type for creating a toolbar content renderer from a light DOM container.\n * This is used by framework adapters (Angular, React, etc.) to create renderers\n * from their template syntax.\n */\nexport type ToolbarContentRendererFactory = (\n container: HTMLElement,\n) => ((target: HTMLElement) => void | (() => void)) | undefined;\n\n/**\n * Parse toolbar buttons container element (<tbw-grid-tool-buttons>).\n * This is a content container - we don't parse individual children.\n * The entire container content is registered as a single toolbar content entry.\n *\n * Example:\n * ```html\n * <tbw-grid>\n * <tbw-grid-tool-buttons>\n * <button>My button</button>\n * <button>My other button</button>\n * </tbw-grid-tool-buttons>\n * </tbw-grid>\n * ```\n *\n * The container's children are moved to the toolbar area during render.\n * We treat this as opaque content - users control what goes inside.\n *\n * @param host - The grid host element\n * @param state - Shell state to update\n * @param rendererFactory - Optional factory for creating renderers (used by framework adapters)\n */\nexport function parseLightDomToolButtons(\n host: HTMLElement,\n state: ShellState,\n rendererFactory?: ToolbarContentRendererFactory,\n): void {\n // Look for the toolbar buttons container element\n const toolButtonsContainer = host.querySelector(':scope > tbw-grid-tool-buttons') as HTMLElement | null;\n if (!toolButtonsContainer) return;\n\n // Mark that we found the container (for shouldRenderShellHeader)\n state.hasToolButtonsContainer = true;\n\n // Skip if already registered\n const id = 'light-dom-toolbar-content';\n if (state.lightDomToolbarContentIds.has(id)) return;\n\n // Register as a single content entry with a render function\n const adapterRenderer = rendererFactory?.(toolButtonsContainer);\n\n const contentDef: ToolbarContentDefinition = {\n id,\n order: 0, // Light DOM content comes first\n render:\n adapterRenderer ??\n ((target: HTMLElement) => {\n // Move all children from the light DOM container to the target\n while (toolButtonsContainer.firstChild) {\n target.appendChild(toolButtonsContainer.firstChild);\n }\n // No cleanup needed - elements are just moved\n }),\n };\n\n state.toolbarContents.set(id, contentDef);\n state.lightDomToolbarContentIds.add(id);\n\n // Hide the original container\n toolButtonsContainer.style.display = 'none';\n}\n\n/**\n * Callback type for creating a tool panel renderer from a light DOM element.\n * This is used by framework adapters (Angular, React, etc.) to create renderers\n * from their template syntax.\n */\nexport type ToolPanelRendererFactory = (\n element: HTMLElement,\n) => ((container: HTMLElement) => void | (() => void)) | undefined;\n\n/**\n * Parse light DOM tool panel elements (<tbw-grid-tool-panel>).\n * These can appear as direct children of <tbw-grid> for declarative tool panel configuration.\n *\n * Attributes:\n * - `id` (required): Unique panel identifier\n * - `title` (required): Panel title shown in accordion header\n * - `icon`: Icon for accordion section header (emoji or text)\n * - `tooltip`: Tooltip for accordion section header\n * - `order`: Panel order priority (lower = first, default: 100)\n *\n * For vanilla JS, the element's innerHTML is used as the panel content.\n * For framework adapters, the adapter can provide a custom renderer factory.\n *\n * @param host - The grid host element\n * @param state - Shell state to update\n * @param rendererFactory - Optional factory for creating renderers (used by framework adapters)\n */\nexport function parseLightDomToolPanels(\n host: HTMLElement,\n state: ShellState,\n rendererFactory?: ToolPanelRendererFactory,\n): void {\n const toolPanelElements = host.querySelectorAll(':scope > tbw-grid-tool-panel');\n\n toolPanelElements.forEach((element) => {\n const panelEl = element as HTMLElement;\n const id = panelEl.getAttribute('id');\n const title = panelEl.getAttribute('title');\n\n // Skip if required attributes are missing\n if (!id || !title) {\n console.warn(\n `[parseLightDomToolPanels] Tool panel missing required id or title attribute: id=\"${id ?? ''}\", title=\"${title ?? ''}\"`,\n );\n return;\n }\n\n const icon = panelEl.getAttribute('icon') ?? undefined;\n const tooltip = panelEl.getAttribute('tooltip') ?? undefined;\n const order = parseInt(panelEl.getAttribute('order') ?? '100', 10);\n\n // Try framework adapter first, then fall back to innerHTML\n let render: (container: HTMLElement) => void | (() => void);\n\n const adapterRenderer = rendererFactory?.(panelEl);\n if (adapterRenderer) {\n render = adapterRenderer;\n } else {\n // Vanilla fallback: use innerHTML as static content\n const content = panelEl.innerHTML.trim();\n render = (container: HTMLElement) => {\n const wrapper = document.createElement('div');\n wrapper.innerHTML = content;\n container.appendChild(wrapper);\n return () => wrapper.remove();\n };\n }\n\n // Check if panel was already parsed\n const existingPanel = state.toolPanels.get(id);\n\n // If already parsed and we have an adapter renderer, update the render function\n // and re-read attributes from DOM (Angular may have updated them after initial parse)\n // This handles the case where Angular templates register after initial parsing\n if (existingPanel) {\n if (adapterRenderer) {\n // Update render function with framework adapter renderer\n existingPanel.render = render;\n\n // Re-read attributes from DOM - framework may have set them after initial parse\n // (e.g., Angular directive sets attributes in an effect after template is available)\n existingPanel.order = order;\n existingPanel.icon = icon;\n existingPanel.tooltip = tooltip;\n // Note: title and id are required and shouldn't change\n\n // Clear existing cleanup to force re-render with new renderer\n const cleanup = state.panelCleanups.get(id);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(id);\n }\n }\n return;\n }\n\n // Register the tool panel\n const panel: ToolPanelDefinition = {\n id,\n title,\n icon,\n tooltip,\n order,\n render,\n };\n\n state.toolPanels.set(id, panel);\n state.lightDomToolPanelIds.add(id);\n\n // Hide the light DOM element\n panelEl.style.display = 'none';\n });\n}\n\n/**\n * Set up event listeners for shell toolbar buttons and accordion.\n */\nexport function setupShellEventListeners(\n renderRoot: Element,\n config: ShellConfig | undefined,\n state: ShellState,\n callbacks: {\n onPanelToggle: () => void;\n onSectionToggle: (sectionId: string) => void;\n },\n): void {\n const toolbar = renderRoot.querySelector('.tbw-shell-toolbar');\n if (toolbar) {\n toolbar.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n\n // Handle single panel toggle button\n const panelToggle = target.closest('[data-panel-toggle]') as HTMLElement | null;\n if (panelToggle) {\n callbacks.onPanelToggle();\n return;\n }\n });\n }\n\n // Accordion header clicks\n const accordion = renderRoot.querySelector('.tbw-accordion');\n if (accordion) {\n accordion.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n const header = target.closest('.tbw-accordion-header') as HTMLElement | null;\n if (header) {\n const section = header.closest('[data-section]') as HTMLElement | null;\n const sectionId = section?.getAttribute('data-section');\n if (sectionId) {\n callbacks.onSectionToggle(sectionId);\n }\n }\n });\n }\n}\n\n/**\n * Set up resize handle for tool panel.\n * Returns a cleanup function to remove event listeners.\n */\nexport function setupToolPanelResize(\n renderRoot: Element,\n config: ShellConfig | undefined,\n onResize: (width: number) => void,\n): () => void {\n const panel = renderRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n const handle = renderRoot.querySelector('[data-resize-handle]') as HTMLElement | null;\n const shellBody = renderRoot.querySelector('.tbw-shell-body') as HTMLElement | null;\n if (!panel || !handle || !shellBody) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const position = config?.toolPanel?.position ?? 'right';\n const minWidth = 200;\n\n let startX = 0;\n let startWidth = 0;\n let maxWidth = 0;\n let isResizing = false;\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n e.preventDefault();\n\n // For right-positioned panel: dragging left (negative clientX change) should expand\n // For left-positioned panel: dragging right (positive clientX change) should expand\n const delta = position === 'left' ? e.clientX - startX : startX - e.clientX;\n const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidth + delta));\n\n panel.style.width = `${newWidth}px`;\n };\n\n const onMouseUp = () => {\n if (!isResizing) return;\n isResizing = false;\n handle.classList.remove('resizing');\n panel.style.transition = ''; // Re-enable transition\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n\n // Get final width and notify\n const finalWidth = panel.getBoundingClientRect().width;\n onResize(finalWidth);\n\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n isResizing = true;\n startX = e.clientX;\n startWidth = panel.getBoundingClientRect().width;\n // Calculate max width dynamically based on grid container width\n maxWidth = shellBody.getBoundingClientRect().width - 20; // Leave 20px margin\n handle.classList.add('resizing');\n panel.style.transition = 'none'; // Disable transition for smooth resize\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n\n // Return cleanup function\n return () => {\n handle.removeEventListener('mousedown', onMouseDown);\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n}\n\n/**\n * Render toolbar content (render functions) into toolbar slots.\n * All contents (config + API + light DOM) are now unified.\n */\nexport function renderCustomToolbarContents(\n renderRoot: Element,\n config: ShellConfig | undefined,\n state: ShellState,\n): void {\n // Merge config contents with state contents (same logic as renderShellHeader)\n const configContents = config?.header?.toolbarContents ?? [];\n const stateContents = [...state.toolbarContents.values()];\n const configIds = new Set(configContents.map((c) => c.id));\n const allContents = [...configContents];\n for (const content of stateContents) {\n if (!configIds.has(content.id)) {\n allContents.push(content);\n }\n }\n\n // Only process contents that need rendering (have render and cleanup not already set)\n for (const content of allContents) {\n // Skip if already rendered (cleanup exists)\n if (state.toolbarContentCleanups.has(content.id)) continue;\n if (!content.render) continue;\n\n const slot = renderRoot.querySelector(`[data-toolbar-content=\"${content.id}\"]`);\n if (!slot) continue;\n\n const cleanup = content.render(slot as HTMLElement);\n if (cleanup) {\n state.toolbarContentCleanups.set(content.id, cleanup);\n }\n }\n}\n\n/**\n * Render header content from plugins into the shell content area.\n * Also moves light DOM header content to the placeholder (once).\n */\nexport function renderHeaderContent(renderRoot: Element, state: ShellState): void {\n // Early exit if nothing to do (most common path after initial render)\n const hasLightDomContent = state.lightDomHeaderContent.length > 0 && !state.lightDomContentMoved;\n const hasPluginContent = state.headerContents.size > 0;\n if (!hasLightDomContent && !hasPluginContent) return;\n\n const contentArea = renderRoot.querySelector('.tbw-shell-content');\n if (!contentArea) return;\n\n // Move light DOM header content to placeholder - only once (perf optimization)\n if (hasLightDomContent) {\n for (const el of state.lightDomHeaderContent) {\n el.style.display = ''; // Show it (was hidden in the original container)\n contentArea.appendChild(el);\n }\n state.lightDomContentMoved = true;\n }\n\n // Sort by order\n const sortedContents = [...state.headerContents.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n for (const content of sortedContents) {\n // Clean up previous render if any\n const existingCleanup = state.headerContentCleanups.get(content.id);\n if (existingCleanup) {\n existingCleanup();\n state.headerContentCleanups.delete(content.id);\n }\n\n // Check if container already exists\n let container = contentArea.querySelector(`[data-header-content=\"${content.id}\"]`) as HTMLElement | null;\n if (!container) {\n container = document.createElement('div');\n container.setAttribute('data-header-content', content.id);\n contentArea.appendChild(container);\n }\n\n const cleanup = content.render(container);\n if (cleanup) {\n state.headerContentCleanups.set(content.id, cleanup);\n }\n }\n}\n\n/**\n * Render content for expanded accordion sections.\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderPanelContent(\n renderRoot: Element,\n state: ShellState,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): void {\n if (!state.isPanelOpen) return;\n\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n for (const [panelId, panel] of state.toolPanels) {\n const isExpanded = state.expandedSections.has(panelId);\n const section = renderRoot.querySelector(`[data-section=\"${panelId}\"]`);\n const contentArea = section?.querySelector('.tbw-accordion-content') as HTMLElement | null;\n\n if (!section || !contentArea) continue;\n\n // Update expanded state\n section.classList.toggle('expanded', isExpanded);\n const header = section.querySelector('.tbw-accordion-header');\n if (header) {\n header.setAttribute('aria-expanded', String(isExpanded));\n }\n const chevron = section.querySelector('.tbw-accordion-chevron');\n if (chevron) {\n chevron.innerHTML = isExpanded ? collapseIcon : expandIcon;\n }\n\n if (isExpanded) {\n // Check if content is already rendered\n if (contentArea.children.length === 0) {\n // Render panel content\n const cleanup = panel.render(contentArea);\n if (cleanup) {\n state.panelCleanups.set(panelId, cleanup);\n }\n }\n } else {\n // Clean up and clear content when collapsed\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n contentArea.innerHTML = '';\n }\n }\n}\n\n/**\n * Update toolbar button active states.\n */\nexport function updateToolbarActiveStates(renderRoot: Element, state: ShellState): void {\n // Update single panel toggle button\n const panelToggle = renderRoot.querySelector('[data-panel-toggle]');\n if (panelToggle) {\n panelToggle.classList.toggle('active', state.isPanelOpen);\n panelToggle.setAttribute('aria-pressed', String(state.isPanelOpen));\n }\n}\n\n/**\n * Update tool panel open/close state.\n */\nexport function updatePanelState(renderRoot: Element, state: ShellState): void {\n const panel = renderRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n if (!panel) return;\n\n panel.classList.toggle('open', state.isPanelOpen);\n\n // Clear inline width when closing (resize sets inline style that overrides CSS)\n if (!state.isPanelOpen) {\n panel.style.width = '';\n }\n}\n\n/**\n * Cleanup all shell state when grid disconnects.\n */\nexport function cleanupShellState(state: ShellState): void {\n // Clean up header content\n for (const cleanup of state.headerContentCleanups.values()) {\n cleanup();\n }\n state.headerContentCleanups.clear();\n\n // Clean up panel content\n for (const cleanup of state.panelCleanups.values()) {\n cleanup();\n }\n state.panelCleanups.clear();\n\n // Clean up toolbar contents\n for (const cleanup of state.toolbarContentCleanups.values()) {\n cleanup();\n }\n state.toolbarContentCleanups.clear();\n\n // Call onDestroy for all toolbar contents\n for (const content of state.toolbarContents.values()) {\n content.onDestroy?.();\n }\n\n // Invoke onClose for all open panels\n if (state.isPanelOpen) {\n for (const sectionId of state.expandedSections) {\n const panel = state.toolPanels.get(sectionId);\n panel?.onClose?.();\n }\n }\n\n // Reset panel state\n state.isPanelOpen = false;\n state.expandedSections.clear();\n\n // Clear registrations\n state.toolPanels.clear();\n state.headerContents.clear();\n state.toolbarContents.clear();\n state.lightDomHeaderContent = [];\n\n // Clear light DOM tracking sets (allow re-parsing)\n state.lightDomToolPanelIds.clear();\n state.lightDomToolbarContentIds.clear();\n\n // Reset move tracking flag (allow re-initialization)\n state.lightDomContentMoved = false;\n}\n\n// ============================================================================\n// ShellController - Encapsulates tool panel orchestration logic\n// ============================================================================\n\n/**\n * Callbacks for ShellController to communicate with the grid.\n * This interface decouples the controller from grid internals.\n */\nexport interface ShellControllerCallbacks {\n /** Get the render root for DOM queries (the grid element) */\n getShadow: () => Element;\n /** Get the current shell config */\n getShellConfig: () => ShellConfig | undefined;\n /** Get accordion expand/collapse icons */\n getAccordionIcons: () => { expand: IconValue; collapse: IconValue };\n /** Emit an event from the grid */\n emit: (eventName: string, detail: unknown) => void;\n /** Refresh the shell header (re-parse light DOM and re-render) */\n refreshShellHeader: () => void;\n}\n\n/**\n * Controller interface for managing shell/tool panel behavior.\n */\nexport interface ShellController {\n /** Whether the shell has been initialized */\n readonly isInitialized: boolean;\n /** Set the initialized state */\n setInitialized(value: boolean): void;\n /** Whether the tool panel is currently open */\n readonly isPanelOpen: boolean;\n /** Get the currently active panel ID (deprecated) */\n readonly activePanel: string | null;\n /** Get IDs of expanded accordion sections */\n readonly expandedSections: string[];\n /** Open the tool panel */\n openToolPanel(): void;\n /** Close the tool panel */\n closeToolPanel(): void;\n /** Toggle the tool panel */\n toggleToolPanel(): void;\n /** Toggle an accordion section */\n toggleToolPanelSection(sectionId: string): void;\n /** Get registered tool panels */\n getToolPanels(): ToolPanelDefinition[];\n /** Register a tool panel */\n registerToolPanel(panel: ToolPanelDefinition): void;\n /** Unregister a tool panel */\n unregisterToolPanel(panelId: string): void;\n /** Get registered header contents */\n getHeaderContents(): HeaderContentDefinition[];\n /** Register header content */\n registerHeaderContent(content: HeaderContentDefinition): void;\n /** Unregister header content */\n unregisterHeaderContent(contentId: string): void;\n /** Get all registered toolbar contents */\n getToolbarContents(): ToolbarContentDefinition[];\n /** Register toolbar content */\n registerToolbarContent(content: ToolbarContentDefinition): void;\n /** Unregister toolbar content */\n unregisterToolbarContent(contentId: string): void;\n}\n\n/**\n * Create a ShellController instance.\n * The controller encapsulates all tool panel orchestration logic.\n */\nexport function createShellController(state: ShellState, callbacks: ShellControllerCallbacks): ShellController {\n let initialized = false;\n\n const controller: ShellController = {\n get isInitialized() {\n return initialized;\n },\n setInitialized(value: boolean) {\n initialized = value;\n },\n\n get isPanelOpen() {\n return state.isPanelOpen;\n },\n\n get activePanel() {\n // For backward compatibility, return first expanded section if panel is open\n if (state.isPanelOpen && state.expandedSections.size > 0) {\n return [...state.expandedSections][0];\n }\n return null;\n },\n\n get expandedSections() {\n return [...state.expandedSections];\n },\n\n openToolPanel() {\n if (state.isPanelOpen) return;\n if (state.toolPanels.size === 0) {\n console.warn('[tbw-grid] No tool panels registered');\n return;\n }\n\n state.isPanelOpen = true;\n\n // Auto-expand first section if none expanded\n if (state.expandedSections.size === 0 && state.toolPanels.size > 0) {\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const firstPanel = sortedPanels[0];\n if (firstPanel) {\n state.expandedSections.add(firstPanel.id);\n }\n }\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Render accordion sections\n renderPanelContent(shadow, state, callbacks.getAccordionIcons());\n\n // Emit event\n callbacks.emit('tool-panel-open', { sections: controller.expandedSections });\n },\n\n closeToolPanel() {\n if (!state.isPanelOpen) return;\n\n // Clean up all panel content\n for (const cleanup of state.panelCleanups.values()) {\n cleanup();\n }\n state.panelCleanups.clear();\n\n // Call onClose for all panels\n for (const panel of state.toolPanels.values()) {\n panel.onClose?.();\n }\n\n state.isPanelOpen = false;\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Emit event\n callbacks.emit('tool-panel-close', {});\n },\n\n toggleToolPanel() {\n if (state.isPanelOpen) {\n controller.closeToolPanel();\n } else {\n controller.openToolPanel();\n }\n },\n\n toggleToolPanelSection(sectionId: string) {\n const panel = state.toolPanels.get(sectionId);\n if (!panel) {\n console.warn(`[tbw-grid] Tool panel section \"${sectionId}\" not found`);\n return;\n }\n\n // Don't allow toggling when there's only one panel (it should stay expanded)\n if (state.toolPanels.size === 1) {\n return;\n }\n\n const shadow = callbacks.getShadow();\n const isExpanded = state.expandedSections.has(sectionId);\n\n if (isExpanded) {\n // Collapsing current section\n const cleanup = state.panelCleanups.get(sectionId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(sectionId);\n }\n panel.onClose?.();\n state.expandedSections.delete(sectionId);\n updateAccordionSectionState(shadow, sectionId, false);\n } else {\n // Expanding - first collapse all others (exclusive accordion)\n for (const [otherId, otherPanel] of state.toolPanels) {\n if (otherId !== sectionId && state.expandedSections.has(otherId)) {\n const cleanup = state.panelCleanups.get(otherId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(otherId);\n }\n otherPanel.onClose?.();\n state.expandedSections.delete(otherId);\n updateAccordionSectionState(shadow, otherId, false);\n // Clear content of collapsed section\n const contentEl = shadow.querySelector(`[data-section=\"${otherId}\"] .tbw-accordion-content`);\n if (contentEl) contentEl.innerHTML = '';\n }\n }\n // Now expand the target section\n state.expandedSections.add(sectionId);\n updateAccordionSectionState(shadow, sectionId, true);\n renderAccordionSectionContent(shadow, state, sectionId);\n }\n\n // Emit event\n callbacks.emit('tool-panel-section-toggle', { id: sectionId, expanded: !isExpanded });\n },\n\n getToolPanels() {\n return [...state.toolPanels.values()];\n },\n\n registerToolPanel(panel: ToolPanelDefinition) {\n if (state.toolPanels.has(panel.id)) {\n console.warn(`[tbw-grid] Tool panel \"${panel.id}\" already registered`);\n return;\n }\n state.toolPanels.set(panel.id, panel);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolPanel(panelId: string) {\n // Close panel if open and this section is expanded\n if (state.expandedSections.has(panelId)) {\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n state.expandedSections.delete(panelId);\n }\n\n state.toolPanels.delete(panelId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n getHeaderContents() {\n return [...state.headerContents.values()];\n },\n\n registerHeaderContent(content: HeaderContentDefinition) {\n if (state.headerContents.has(content.id)) {\n console.warn(`[tbw-grid] Header content \"${content.id}\" already registered`);\n return;\n }\n state.headerContents.set(content.id, content);\n\n if (initialized) {\n renderHeaderContent(callbacks.getShadow(), state);\n }\n },\n\n unregisterHeaderContent(contentId: string) {\n // Clean up\n const cleanup = state.headerContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n state.headerContentCleanups.delete(contentId);\n }\n\n // Call onDestroy\n const content = state.headerContents.get(contentId);\n content?.onDestroy?.();\n\n state.headerContents.delete(contentId);\n\n // Remove DOM element\n const el = callbacks.getShadow().querySelector(`[data-header-content=\"${contentId}\"]`);\n el?.remove();\n },\n\n getToolbarContents() {\n return [...state.toolbarContents.values()].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n },\n\n registerToolbarContent(content: ToolbarContentDefinition) {\n if (state.toolbarContents.has(content.id)) {\n console.warn(`[tbw-grid] Toolbar content \"${content.id}\" already registered`);\n return;\n }\n state.toolbarContents.set(content.id, content);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolbarContent(contentId: string) {\n // Clean up\n const cleanup = state.toolbarContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n state.toolbarContentCleanups.delete(contentId);\n }\n\n // Call onDestroy if defined\n const content = state.toolbarContents.get(contentId);\n if (content?.onDestroy) {\n content.onDestroy();\n }\n\n state.toolbarContents.delete(contentId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n };\n\n return controller;\n}\n\n/**\n * Update accordion section visual state.\n */\nfunction updateAccordionSectionState(renderRoot: Element, sectionId: string, expanded: boolean): void {\n const section = renderRoot.querySelector(`[data-section=\"${sectionId}\"]`);\n if (section) {\n section.classList.toggle('expanded', expanded);\n }\n}\n\n/**\n * Render content for a single accordion section.\n */\nfunction renderAccordionSectionContent(renderRoot: Element, state: ShellState, sectionId: string): void {\n const panel = state.toolPanels.get(sectionId);\n if (!panel?.render) return;\n\n const contentEl = renderRoot.querySelector(`[data-section=\"${sectionId}\"] .tbw-accordion-content`);\n if (!contentEl) return;\n\n const cleanup = panel.render(contentEl as HTMLElement);\n if (cleanup) {\n state.panelCleanups.set(sectionId, cleanup);\n }\n}\n\n// ============================================================================\n// Grid Render HTML\n// ============================================================================\n\n/**\n * Core grid content HTML template.\n * Uses faux scrollbar pattern for smooth virtualized scrolling.\n */\nexport const GRID_CONTENT_HTML = `\n <div class=\"tbw-scroll-area\">\n <div class=\"rows-body-wrapper\">\n <div class=\"rows-body\" role=\"grid\">\n <div class=\"header\" role=\"rowgroup\">\n <div class=\"header-row\" role=\"row\" part=\"header-row\"></div>\n </div>\n <div class=\"rows-container\" role=\"presentation\">\n <div class=\"rows-viewport\" role=\"presentation\">\n <div class=\"rows\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"faux-vscroll\">\n <div class=\"faux-vscroll-spacer\"></div>\n </div>\n`;\n\n// ============================================================================\n// DOM-based Grid Rendering (Performance Optimized)\n// ============================================================================\n\nimport {\n buildGridDOM,\n buildShellBody,\n buildShellHeader,\n type ShellBodyOptions,\n type ShellHeaderOptions,\n} from './dom-builder';\n\n/**\n * Build the complete grid DOM structure using direct DOM construction.\n * This is 2-3x faster than innerHTML for initial render.\n *\n * @param renderRoot - The element to render into (will be cleared)\n * @param shellConfig - Shell configuration\n * @param state - Shell state\n * @param icons - Optional icons\n * @returns Whether shell is active (for post-render setup)\n */\nexport function buildGridDOMIntoElement(\n renderRoot: Element,\n shellConfig: ShellConfig | undefined,\n runtimeState: { isPanelOpen: boolean; expandedSections: Set<string> },\n icons?: { toolPanel?: IconValue; expand?: IconValue; collapse?: IconValue },\n): boolean {\n const hasShell = shouldRenderShellHeader(shellConfig);\n\n // Preserve light DOM elements before clearing (they contain user content)\n // These are custom elements used for declarative configuration\n const lightDomElements: Element[] = [];\n const lightDomSelectors = ['tbw-grid-header', 'tbw-grid-tool-buttons', 'tbw-grid-tool-panel', 'tbw-grid-column'];\n for (const selector of lightDomSelectors) {\n const elements = renderRoot.querySelectorAll(`:scope > ${selector}`);\n elements.forEach((el) => lightDomElements.push(el));\n }\n\n // Clear existing content (this would delete light DOM elements, so we preserved them first)\n renderRoot.replaceChildren();\n\n // Re-append preserved light DOM elements (hidden, they're used for config)\n for (const el of lightDomElements) {\n renderRoot.appendChild(el);\n }\n\n if (hasShell) {\n const toolPanelIcon = iconToString(icons?.toolPanel ?? DEFAULT_GRID_ICONS.toolPanel);\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n // All toolbar contents now come from shellConfig (merged by ConfigManager)\n const allContents = shellConfig?.header?.toolbarContents ?? [];\n const sortedContents = [...allContents].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n\n // All panels now come from shellConfig (merged by ConfigManager)\n const allPanels = shellConfig?.toolPanels ?? [];\n const sortedPanels = [...allPanels].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build header options\n const headerOptions: ShellHeaderOptions = {\n title: shellConfig?.header?.title ?? undefined,\n hasPanels: sortedPanels.length > 0,\n isPanelOpen: runtimeState.isPanelOpen,\n toolPanelIcon,\n // All contents are now in config (no more separate config vs API distinction for rendering)\n configButtons: sortedContents.map((c) => ({\n id: c.id,\n hasElement: false,\n hasRender: !!c.render,\n })),\n apiButtons: [], // No longer needed - all contents merged into configButtons\n };\n\n // Build body options\n const bodyOptions: ShellBodyOptions = {\n position: shellConfig?.toolPanel?.position ?? 'right',\n isPanelOpen: runtimeState.isPanelOpen,\n expandIcon,\n collapseIcon,\n panels: sortedPanels.map((p) => ({\n id: p.id,\n title: p.title,\n icon: iconToString(p.icon),\n isExpanded: runtimeState.expandedSections.has(p.id),\n })),\n };\n\n // Build shell elements\n const shellHeader = buildShellHeader(headerOptions);\n const shellBody = buildShellBody(bodyOptions);\n\n // Build and append complete DOM\n const fragment = buildGridDOM({\n hasShell: true,\n shellHeader,\n shellBody,\n });\n renderRoot.appendChild(fragment);\n } else {\n // No shell - just grid content\n const fragment = buildGridDOM({ hasShell: false });\n renderRoot.appendChild(fragment);\n }\n\n return hasShell;\n}\n","/**\n * Touch scrolling controller for mobile devices.\n *\n * Handles touch events for scrolling with momentum physics.\n * Supports both vertical (faux scrollbar) and horizontal (scroll area) scrolling.\n */\n\nexport interface TouchScrollState {\n startY: number | null;\n startX: number | null;\n scrollTop: number | null;\n scrollLeft: number | null;\n lastY: number | null;\n lastX: number | null;\n lastTime: number | null;\n velocityY: number;\n velocityX: number;\n momentumRaf: number;\n}\n\nexport interface TouchScrollElements {\n fauxScrollbar: HTMLElement;\n scrollArea: HTMLElement | null;\n}\n\n/**\n * Create initial touch scroll state.\n */\nexport function createTouchScrollState(): TouchScrollState {\n return {\n startY: null,\n startX: null,\n scrollTop: null,\n scrollLeft: null,\n lastY: null,\n lastX: null,\n lastTime: null,\n velocityY: 0,\n velocityX: 0,\n momentumRaf: 0,\n };\n}\n\n/**\n * Reset touch scroll state (called on touchend or cleanup).\n */\nexport function resetTouchState(state: TouchScrollState): void {\n state.startY = null;\n state.startX = null;\n state.scrollTop = null;\n state.scrollLeft = null;\n state.lastY = null;\n state.lastX = null;\n state.lastTime = null;\n}\n\n/**\n * Cancel any ongoing momentum animation.\n */\nexport function cancelMomentum(state: TouchScrollState): void {\n if (state.momentumRaf) {\n cancelAnimationFrame(state.momentumRaf);\n state.momentumRaf = 0;\n }\n}\n\n/**\n * Handle touchstart event.\n */\nexport function handleTouchStart(e: TouchEvent, state: TouchScrollState, elements: TouchScrollElements): void {\n if (e.touches.length !== 1) return;\n\n // Cancel any ongoing momentum animation\n cancelMomentum(state);\n\n const touch = e.touches[0];\n state.startY = touch.clientY;\n state.startX = touch.clientX;\n state.lastY = touch.clientY;\n state.lastX = touch.clientX;\n state.lastTime = performance.now();\n state.scrollTop = elements.fauxScrollbar.scrollTop;\n state.scrollLeft = elements.scrollArea?.scrollLeft ?? 0;\n state.velocityY = 0;\n state.velocityX = 0;\n}\n\n/**\n * Handle touchmove event.\n * Returns true if the event should be prevented (grid scrolled).\n */\nexport function handleTouchMove(e: TouchEvent, state: TouchScrollState, elements: TouchScrollElements): boolean {\n if (\n e.touches.length !== 1 ||\n state.startY === null ||\n state.startX === null ||\n state.scrollTop === null ||\n state.scrollLeft === null\n ) {\n return false;\n }\n\n const touch = e.touches[0];\n const currentY = touch.clientY;\n const currentX = touch.clientX;\n const now = performance.now();\n\n const deltaY = state.startY - currentY;\n const deltaX = state.startX - currentX;\n\n // Calculate velocity for momentum scrolling\n if (state.lastTime !== null && state.lastY !== null && state.lastX !== null) {\n const dt = now - state.lastTime;\n if (dt > 0) {\n // Velocity in pixels per millisecond\n state.velocityY = (state.lastY - currentY) / dt;\n state.velocityX = (state.lastX - currentX) / dt;\n }\n }\n state.lastY = currentY;\n state.lastX = currentX;\n state.lastTime = now;\n\n // Check if grid can scroll in the requested directions\n const { scrollTop, scrollHeight, clientHeight } = elements.fauxScrollbar;\n const maxScrollY = scrollHeight - clientHeight;\n const canScrollVertically = (deltaY > 0 && scrollTop < maxScrollY) || (deltaY < 0 && scrollTop > 0);\n\n let canScrollHorizontally = false;\n if (elements.scrollArea) {\n const { scrollLeft, scrollWidth, clientWidth } = elements.scrollArea;\n const maxScrollX = scrollWidth - clientWidth;\n canScrollHorizontally = (deltaX > 0 && scrollLeft < maxScrollX) || (deltaX < 0 && scrollLeft > 0);\n }\n\n // Apply scroll if grid can scroll in that direction\n if (canScrollVertically) {\n elements.fauxScrollbar.scrollTop = state.scrollTop + deltaY;\n }\n if (canScrollHorizontally && elements.scrollArea) {\n elements.scrollArea.scrollLeft = state.scrollLeft + deltaX;\n }\n\n // Return true to prevent page scroll when we scrolled the grid\n return canScrollVertically || canScrollHorizontally;\n}\n\n/**\n * Handle touchend event.\n * Starts momentum scrolling if velocity is significant.\n */\nexport function handleTouchEnd(state: TouchScrollState, elements: TouchScrollElements): void {\n const minVelocity = 0.1; // pixels per ms threshold\n\n // Start momentum scrolling if there's significant velocity\n if (Math.abs(state.velocityY) > minVelocity || Math.abs(state.velocityX) > minVelocity) {\n startMomentumScroll(state, elements);\n }\n\n resetTouchState(state);\n}\n\n/**\n * Start momentum scrolling animation.\n */\nfunction startMomentumScroll(state: TouchScrollState, elements: TouchScrollElements): void {\n const friction = 0.95; // Deceleration factor per frame\n const minVelocity = 0.01; // Stop threshold in px/ms\n\n const animate = () => {\n // Apply friction\n state.velocityY *= friction;\n state.velocityX *= friction;\n\n // Convert velocity (px/ms) to per-frame scroll amount (~16ms per frame)\n const scrollY = state.velocityY * 16;\n const scrollX = state.velocityX * 16;\n\n // Apply scroll if above threshold\n if (Math.abs(state.velocityY) > minVelocity) {\n elements.fauxScrollbar.scrollTop += scrollY;\n }\n if (Math.abs(state.velocityX) > minVelocity && elements.scrollArea) {\n elements.scrollArea.scrollLeft += scrollX;\n }\n\n // Continue animation if still moving\n if (Math.abs(state.velocityY) > minVelocity || Math.abs(state.velocityX) > minVelocity) {\n state.momentumRaf = requestAnimationFrame(animate);\n } else {\n state.momentumRaf = 0;\n }\n };\n\n state.momentumRaf = requestAnimationFrame(animate);\n}\n\n/**\n * Set up touch scroll event listeners on the grid content element.\n * Returns a cleanup function that removes all listeners.\n */\nexport function setupTouchScrollListeners(\n gridContentEl: HTMLElement,\n state: TouchScrollState,\n elements: TouchScrollElements,\n signal: AbortSignal,\n): void {\n gridContentEl.addEventListener('touchstart', (e: TouchEvent) => handleTouchStart(e, state, elements), {\n passive: true,\n signal,\n });\n\n gridContentEl.addEventListener(\n 'touchmove',\n (e: TouchEvent) => {\n const shouldPrevent = handleTouchMove(e, state, elements);\n if (shouldPrevent) {\n e.preventDefault();\n }\n },\n { passive: false, signal },\n );\n\n gridContentEl.addEventListener('touchend', () => handleTouchEnd(state, elements), { passive: true, signal });\n}\n","/**\n * Configuration Validation\n *\n * Runtime validators that check for plugin-specific properties in config\n * and throw helpful errors if the required plugin is not loaded.\n *\n * This catches common mistakes like using `editable: true` without EditingPlugin.\n *\n * Uses a static registry of known plugin-owned properties to detect when users\n * configure features that require plugins they haven't loaded.\n */\n\nimport type { BaseGridPlugin, PluginManifest, PluginPropertyDefinition } from '../plugin';\nimport type { ColumnConfig, GridConfig } from '../types';\n\n/**\n * Internal property definition with plugin name attached.\n * Extends PluginPropertyDefinition with required pluginName for validation.\n */\ninterface InternalPropertyDefinition extends PluginPropertyDefinition {\n pluginName: string;\n}\n\n// ============================================================================\n// Known Plugin-Owned Properties (Static Registry)\n// ============================================================================\n\n/**\n * Static registry of known plugin-owned column properties.\n * This enables detection of plugin properties even when the plugin isn't loaded.\n * Properties defined here allow helpful error messages when plugins are missing.\n *\n * ## Why This Exists (The Validation Paradox)\n *\n * We need to detect when a developer uses a plugin-owned property (like `editable`)\n * but forgets to add the plugin. However, if the plugin isn't loaded, we can't\n * read its manifest! The manifest only exists when the plugin class is imported.\n *\n * This static registry solves that: it's a \"well-known properties\" list that exists\n * independently of whether plugins are loaded.\n *\n * ## When Adding New Plugin-Owned Properties\n *\n * 1. **Always**: Add to the plugin's manifest `ownedProperties` (documentation, lives with plugin)\n * 2. **Optionally**: Add here if you want \"forgot to add plugin\" detection for that property\n *\n * Not every property needs to be here - only high-value ones where developers commonly\n * forget to add the plugin. Third-party plugins can't be listed here anyway.\n *\n * ## Future Improvement\n *\n * A build-time script could generate these arrays from plugin manifests,\n * creating a single source of truth. For now, they're maintained manually.\n */\nconst KNOWN_COLUMN_PROPERTIES: InternalPropertyDefinition[] = [\n // EditingPlugin\n {\n property: 'editable',\n pluginName: 'editing',\n level: 'column',\n description: 'the \"editable\" column property',\n importHint: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n isUsed: (v) => v === true,\n },\n {\n property: 'editor',\n pluginName: 'editing',\n level: 'column',\n description: 'the \"editor\" column property',\n importHint: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n },\n {\n property: 'editorParams',\n pluginName: 'editing',\n level: 'column',\n description: 'the \"editorParams\" column property',\n importHint: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n },\n // GroupingColumnsPlugin\n {\n property: 'group',\n pluginName: 'groupingColumns',\n level: 'column',\n description: 'the \"group\" column property',\n importHint: \"import { GroupingColumnsPlugin } from '@toolbox-web/grid/plugins/grouping-columns';\",\n },\n // PinnedColumnsPlugin\n {\n property: 'sticky',\n pluginName: 'pinnedColumns',\n level: 'column',\n description: 'the \"sticky\" column property',\n importHint: \"import { PinnedColumnsPlugin } from '@toolbox-web/grid/plugins/pinned-columns';\",\n isUsed: (v) => v === 'left' || v === 'right',\n },\n];\n\n/**\n * Static registry of known plugin-owned grid config properties.\n */\nconst KNOWN_CONFIG_PROPERTIES: InternalPropertyDefinition[] = [\n // GroupingColumnsPlugin\n {\n property: 'columnGroups',\n pluginName: 'groupingColumns',\n level: 'config',\n description: 'the \"columnGroups\" config property',\n importHint: \"import { GroupingColumnsPlugin } from '@toolbox-web/grid/plugins/grouping-columns';\",\n isUsed: (v) => Array.isArray(v) && v.length > 0,\n },\n];\n\n// ============================================================================\n// Import Hints (for error messages)\n// ============================================================================\n\n/**\n * Map of known plugin names to their npm import paths.\n * Used to generate helpful error messages with import hints.\n */\nconst PLUGIN_IMPORT_HINTS: Record<string, string> = {\n editing: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n selection: \"import { SelectionPlugin } from '@toolbox-web/grid/plugins/selection';\",\n reorder: \"import { ReorderPlugin } from '@toolbox-web/grid/plugins/reorder';\",\n clipboard: \"import { ClipboardPlugin } from '@toolbox-web/grid/plugins/clipboard';\",\n filtering: \"import { FilteringPlugin } from '@toolbox-web/grid/plugins/filtering';\",\n multiSort: \"import { MultiSortPlugin } from '@toolbox-web/grid/plugins/multi-sort';\",\n groupingRows: \"import { GroupingRowsPlugin } from '@toolbox-web/grid/plugins/grouping-rows';\",\n groupingColumns: \"import { GroupingColumnsPlugin } from '@toolbox-web/grid/plugins/grouping-columns';\",\n tree: \"import { TreePlugin } from '@toolbox-web/grid/plugins/tree';\",\n masterDetail: \"import { MasterDetailPlugin } from '@toolbox-web/grid/plugins/master-detail';\",\n pinnedColumns: \"import { PinnedColumnsPlugin } from '@toolbox-web/grid/plugins/pinned-columns';\",\n pinnedRows: \"import { PinnedRowsPlugin } from '@toolbox-web/grid/plugins/pinned-rows';\",\n visibility: \"import { VisibilityPlugin } from '@toolbox-web/grid/plugins/visibility';\",\n undoRedo: \"import { UndoRedoPlugin } from '@toolbox-web/grid/plugins/undo-redo';\",\n export: \"import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\",\n contextMenu: \"import { ContextMenuPlugin } from '@toolbox-web/grid/plugins/context-menu';\",\n pivot: \"import { PivotPlugin } from '@toolbox-web/grid/plugins/pivot';\",\n serverSide: \"import { ServerSidePlugin } from '@toolbox-web/grid/plugins/server-side';\",\n columnVirtualization: \"import { ColumnVirtualizationPlugin } from '@toolbox-web/grid/plugins/column-virtualization';\",\n};\n\n/**\n * Get the import hint for a plugin, with a fallback for unknown plugins.\n */\nfunction getImportHint(pluginName: string): string {\n return (\n PLUGIN_IMPORT_HINTS[pluginName] ??\n `import { ${capitalize(pluginName)}Plugin } from '@toolbox-web/grid/plugins/${pluginName}';`\n );\n}\n\n// ============================================================================\n// Development Mode Detection\n// ============================================================================\n\n/**\n * Check if we're running in a development environment.\n * Warnings are only shown in development to avoid polluting production logs.\n */\nfunction isDevelopment(): boolean {\n // Check for localhost (browser environment)\n if (typeof window !== 'undefined' && window.location) {\n const hostname = window.location.hostname;\n if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1') {\n return true;\n }\n }\n // Check for NODE_ENV (build-time or SSR)\n if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {\n return true;\n }\n return false;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Helper to capitalize a plugin name for display.\n */\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n/**\n * Check if a plugin with the given name is present in the plugins array.\n */\nfunction hasPlugin(plugins: readonly BaseGridPlugin[], pluginName: string): boolean {\n return plugins.some((p) => p.name === pluginName);\n}\n\n// ============================================================================\n// Property Validation\n// ============================================================================\n\n/**\n * Validate that column properties requiring plugins have those plugins loaded.\n *\n * @param config - The merged grid configuration\n * @param plugins - The array of loaded plugins\n * @throws Error if a plugin-owned property is used without the plugin\n */\nexport function validatePluginProperties<T>(config: GridConfig<T>, plugins: readonly BaseGridPlugin[]): void {\n // Use static registries of known plugin-owned properties\n const columnProps = KNOWN_COLUMN_PROPERTIES;\n const configProps = KNOWN_CONFIG_PROPERTIES;\n\n // Group errors by plugin to avoid spamming multiple errors\n const missingPlugins = new Map<\n string,\n { description: string; importHint: string; fields: string[]; isConfigProperty?: boolean }\n >();\n\n // Helper to add an error for a missing plugin\n function addError(\n pluginName: string,\n description: string,\n importHint: string,\n field: string,\n isConfigProperty = false,\n ) {\n if (!missingPlugins.has(pluginName)) {\n missingPlugins.set(pluginName, { description, importHint, fields: [], isConfigProperty });\n }\n // Entry is guaranteed to exist after the set above\n const entry = missingPlugins.get(pluginName)!;\n if (!entry.fields.includes(field)) {\n entry.fields.push(field);\n }\n }\n\n // Validate grid config properties\n for (const def of configProps) {\n const value = (config as Record<string, unknown>)[def.property];\n const isUsed = def.isUsed ? def.isUsed(value) : value !== undefined;\n\n if (isUsed && !hasPlugin(plugins, def.pluginName)) {\n addError(def.pluginName, def.description, def.importHint ?? getImportHint(def.pluginName), def.property, true);\n }\n }\n\n // Validate column properties\n const columns = config.columns;\n if (columns && columns.length > 0) {\n for (const column of columns) {\n for (const def of columnProps) {\n const value = (column as unknown as Record<string, unknown>)[def.property];\n // Use custom isUsed check if provided, otherwise check for defined value\n const isUsed = def.isUsed ? def.isUsed(value) : value !== undefined;\n\n if (isUsed && !hasPlugin(plugins, def.pluginName)) {\n const field = (column as ColumnConfig).field || '<unknown>';\n addError(def.pluginName, def.description, def.importHint ?? getImportHint(def.pluginName), field);\n }\n }\n }\n }\n\n // Throw a single consolidated error if any missing plugins\n if (missingPlugins.size > 0) {\n const errors: string[] = [];\n for (const [pluginName, { description, importHint, fields, isConfigProperty }] of missingPlugins) {\n if (isConfigProperty) {\n // Config-level property error\n errors.push(\n `Config uses ${description}, but the required plugin is not loaded.\\n` +\n ` → Add the plugin to your gridConfig.plugins array:\\n` +\n ` ${importHint}\\n` +\n ` plugins: [new ${pluginName.charAt(0).toUpperCase() + pluginName.slice(1)}Plugin(), ...]`,\n );\n } else {\n // Column-level property error\n const fieldList = fields.slice(0, 3).join(', ') + (fields.length > 3 ? `, ... (${fields.length} total)` : '');\n errors.push(\n `Column(s) [${fieldList}] use ${description}, but the required plugin is not loaded.\\n` +\n ` → Add the plugin to your gridConfig.plugins array:\\n` +\n ` ${importHint}\\n` +\n ` plugins: [new ${pluginName.charAt(0).toUpperCase() + pluginName.slice(1)}Plugin(), ...]`,\n );\n }\n }\n\n throw new Error(\n `[tbw-grid] Configuration error:\\n\\n${errors.join('\\n\\n')}\\n\\n` +\n `This validation helps catch misconfigurations early. ` +\n `The properties listed above require their respective plugins to function.`,\n );\n }\n}\n\n// ============================================================================\n// Plugin Config Rules Validation\n// ============================================================================\n\n/**\n * Validate plugin configuration rules declared in manifests.\n * Called after plugins are attached to check for invalid/conflicting configurations.\n *\n * Rules with severity 'error' throw an error.\n * Rules with severity 'warn' log a warning to console.\n *\n * @param plugins - The array of attached plugins (with config already merged)\n */\nexport function validatePluginConfigRules(plugins: readonly BaseGridPlugin[]): void {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n for (const plugin of plugins) {\n const PluginClass = plugin.constructor as typeof BaseGridPlugin;\n const manifest = PluginClass.manifest as PluginManifest | undefined;\n if (!manifest?.configRules) continue;\n\n for (const rule of manifest.configRules) {\n // Access plugin's merged config via protected property\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pluginConfig = (plugin as any).config;\n if (rule.check(pluginConfig)) {\n const prefix = `[tbw-grid:${capitalize(plugin.name)}Plugin]`;\n const formatted = `${prefix} Configuration warning: ${rule.message}`;\n if (rule.severity === 'error') {\n errors.push(formatted);\n } else {\n warnings.push(formatted);\n }\n }\n }\n }\n\n // Log warnings only in development (don't pollute production logs)\n if (warnings.length > 0 && isDevelopment()) {\n for (const warning of warnings) {\n console.warn(warning);\n }\n }\n\n // Throw consolidated error if any (always, regardless of environment)\n if (errors.length > 0) {\n throw new Error(`[tbw-grid] Configuration error:\\n\\n${errors.join('\\n\\n')}`);\n }\n}\n\n// ============================================================================\n// Plugin Dependency Validation\n// ============================================================================\n\n/**\n * Validate plugin-to-plugin dependencies.\n * Called by PluginManager when attaching a new plugin.\n *\n * Dependencies are read from the plugin's static `dependencies` property.\n *\n * For hard dependencies (required: true), throws an error if the dependency is not loaded.\n * For soft dependencies (required: false), logs an info message but continues.\n *\n * @param plugin - The plugin instance being attached\n * @param loadedPlugins - The array of already-loaded plugins\n * @throws Error if a required dependency is missing\n */\nexport function validatePluginDependencies(plugin: BaseGridPlugin, loadedPlugins: readonly BaseGridPlugin[]): void {\n const pluginName = plugin.name;\n const PluginClass = plugin.constructor as typeof BaseGridPlugin;\n\n // Get dependencies from plugin's static property\n const dependencies = PluginClass.dependencies ?? [];\n\n // Validate each dependency\n for (const dep of dependencies) {\n const requiredPlugin = dep.name;\n const required = dep.required ?? true; // Default to required\n const reason = dep.reason;\n const hasRequired = loadedPlugins.some((p) => p.name === requiredPlugin);\n\n if (!hasRequired) {\n const reasonText = reason ?? `${capitalize(pluginName)}Plugin requires ${capitalize(requiredPlugin)}Plugin`;\n const importHint = getImportHint(requiredPlugin);\n\n if (required) {\n throw new Error(\n `[tbw-grid] Plugin dependency error:\\n\\n` +\n `${reasonText}.\\n\\n` +\n ` → Add the plugin to your gridConfig.plugins array BEFORE ${capitalize(pluginName)}Plugin:\\n` +\n ` ${importHint}\\n` +\n ` plugins: [new ${capitalize(requiredPlugin)}Plugin(), new ${capitalize(pluginName)}Plugin()]`,\n );\n } else {\n // Soft dependency - log info message but continue\n console.info(\n `[tbw-grid] ${capitalize(pluginName)}Plugin: Optional \"${requiredPlugin}\" plugin not found. ` +\n `Some features may be unavailable.`,\n );\n }\n }\n }\n}\n// ============================================================================\n// Plugin Incompatibility Validation\n// ============================================================================\n\n/**\n * Validate that no incompatible plugins are loaded together.\n * Called after all plugins are attached to the grid.\n *\n * Incompatibilities are read from each plugin's manifest `incompatibleWith` property.\n * When a conflict is detected, a warning is logged (in development mode).\n *\n * @param plugins - All attached plugins\n */\nexport function validatePluginIncompatibilities(plugins: readonly BaseGridPlugin[]): void {\n // Only warn in development mode to avoid polluting production logs\n if (!isDevelopment()) return;\n\n const pluginNames = new Set(plugins.map((p) => p.name));\n const warned = new Set<string>(); // Avoid duplicate warnings for symmetric conflicts\n\n for (const plugin of plugins) {\n const PluginClass = plugin.constructor as typeof BaseGridPlugin;\n const manifest = PluginClass.manifest as PluginManifest | undefined;\n if (!manifest?.incompatibleWith) continue;\n\n for (const incompatibility of manifest.incompatibleWith) {\n if (pluginNames.has(incompatibility.name)) {\n // Create a symmetric key to avoid warning twice (A→B and B→A)\n const key = [plugin.name, incompatibility.name].sort().join('↔');\n if (warned.has(key)) continue;\n warned.add(key);\n\n console.warn(\n `[tbw-grid] Plugin incompatibility warning:\\n\\n` +\n `${capitalize(plugin.name)}Plugin and ${capitalize(incompatibility.name)}Plugin are both loaded, ` +\n `but they are currently incompatible.\\n\\n` +\n ` → ${incompatibility.reason}\\n\\n` +\n ` Consider removing one of these plugins to avoid unexpected behavior.`,\n );\n }\n }\n }\n}","/**\n * Plugin Manager\n *\n * Manages plugin instances for a single grid.\n * Each grid has its own PluginManager with its own set of plugin instances.\n *\n * **Plugin Order Matters**: Plugins are executed in the order they appear in the\n * `plugins` array. This affects the order of hook execution (processRows, processColumns,\n * afterRender, etc.). For example, if you want filtering to run before grouping,\n * add FilteringPlugin before GroupingRowsPlugin in the array.\n */\n\nimport { validatePluginDependencies } from '../internal/validate-config';\nimport type { ColumnConfig } from '../types';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n GridElement,\n HeaderClickEvent,\n HeaderRenderer,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './base-plugin';\n\n/**\n * Manages plugins for a single grid instance.\n *\n * Plugins are executed in array order. This is intentional and documented behavior.\n * Place plugins in the order you want their hooks to execute.\n */\nexport class PluginManager {\n /** Plugin instances in order of attachment */\n private plugins: BaseGridPlugin[] = [];\n\n /** Get all registered plugins (read-only) */\n getPlugins(): readonly BaseGridPlugin[] {\n return this.plugins;\n }\n\n /** Map from plugin class to instance for fast lookup */\n private pluginMap: Map<new (...args: unknown[]) => BaseGridPlugin, BaseGridPlugin> = new Map();\n\n /** Cell renderers registered by plugins */\n private cellRenderers: Map<string, CellRenderer> = new Map();\n\n /** Header renderers registered by plugins */\n private headerRenderers: Map<string, HeaderRenderer> = new Map();\n\n /** Cell editors registered by plugins */\n private cellEditors: Map<string, CellEditor> = new Map();\n\n constructor(private grid: GridElement) {}\n\n /**\n * Attach all plugins from the config.\n */\n attachAll(plugins: BaseGridPlugin[]): void {\n for (const plugin of plugins) {\n this.attach(plugin);\n }\n }\n\n /**\n * Attach a plugin to this grid.\n * Validates dependencies and notifies other plugins of the new attachment.\n */\n attach(plugin: BaseGridPlugin): void {\n // Validate plugin dependencies BEFORE attaching\n // This throws if a required dependency is missing\n validatePluginDependencies(plugin, this.plugins);\n\n // Store by constructor for type-safe lookup\n this.pluginMap.set(plugin.constructor as new (...args: unknown[]) => BaseGridPlugin, plugin);\n this.plugins.push(plugin);\n\n // Register renderers/editors\n if (plugin.cellRenderers) {\n for (const [type, renderer] of Object.entries(plugin.cellRenderers)) {\n this.cellRenderers.set(type, renderer);\n }\n }\n if (plugin.headerRenderers) {\n for (const [type, renderer] of Object.entries(plugin.headerRenderers)) {\n this.headerRenderers.set(type, renderer);\n }\n }\n if (plugin.cellEditors) {\n for (const [type, editor] of Object.entries(plugin.cellEditors)) {\n this.cellEditors.set(type, editor);\n }\n }\n\n // Call attach lifecycle method\n plugin.attach(this.grid);\n\n // Notify existing plugins of the new attachment\n for (const existingPlugin of this.plugins) {\n if (existingPlugin !== plugin && existingPlugin.onPluginAttached) {\n existingPlugin.onPluginAttached(plugin.name, plugin);\n }\n }\n }\n\n /**\n * Detach all plugins and clean up.\n * Notifies plugins of detachment via onPluginDetached hook.\n */\n detachAll(): void {\n // Notify all plugins before detaching (in forward order)\n for (const plugin of this.plugins) {\n for (const otherPlugin of this.plugins) {\n if (otherPlugin !== plugin && otherPlugin.onPluginDetached) {\n otherPlugin.onPluginDetached(plugin.name);\n }\n }\n }\n\n // Detach in reverse order\n for (let i = this.plugins.length - 1; i >= 0; i--) {\n this.plugins[i].detach();\n }\n this.plugins = [];\n this.pluginMap.clear();\n this.cellRenderers.clear();\n this.headerRenderers.clear();\n this.cellEditors.clear();\n }\n\n /**\n * Get a plugin instance by its class.\n */\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.pluginMap.get(PluginClass) as T | undefined;\n }\n\n /**\n * Get a plugin instance by its name.\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.plugins.find((p) => p.name === name);\n }\n\n /**\n * Check if a plugin is attached.\n */\n hasPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): boolean {\n return this.pluginMap.has(PluginClass);\n }\n\n /**\n * Get all attached plugins.\n */\n getAll(): readonly BaseGridPlugin[] {\n return this.plugins;\n }\n\n /**\n * Get names of all registered plugins (for debugging).\n */\n getRegisteredPluginNames(): string[] {\n return this.plugins.map((p) => p.name);\n }\n\n /**\n * Get a cell renderer by type name.\n */\n getCellRenderer(type: string): CellRenderer | undefined {\n return this.cellRenderers.get(type);\n }\n\n /**\n * Get a header renderer by type name.\n */\n getHeaderRenderer(type: string): HeaderRenderer | undefined {\n return this.headerRenderers.get(type);\n }\n\n /**\n * Get a cell editor by type name.\n */\n getCellEditor(type: string): CellEditor | undefined {\n return this.cellEditors.get(type);\n }\n\n /**\n * Get all CSS styles from all plugins as structured data.\n * Returns an array of { name, styles } for each plugin with styles.\n */\n getPluginStyles(): Array<{ name: string; styles: string }> {\n return this.plugins.filter((p) => p.styles).map((p) => ({ name: p.name, styles: p.styles! }));\n }\n\n // #region Hook execution methods\n\n /**\n * Execute processRows hook on all plugins.\n */\n processRows(rows: readonly any[]): any[] {\n let result = [...rows];\n for (const plugin of this.plugins) {\n if (plugin.processRows) {\n result = plugin.processRows(result);\n }\n }\n return result;\n }\n\n /**\n * Execute processColumns hook on all plugins.\n */\n processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n let result = [...columns];\n for (const plugin of this.plugins) {\n if (plugin.processColumns) {\n result = plugin.processColumns(result);\n }\n }\n return result;\n }\n\n /**\n * Execute beforeRender hook on all plugins.\n */\n beforeRender(): void {\n for (const plugin of this.plugins) {\n plugin.beforeRender?.();\n }\n }\n\n /**\n * Execute afterRender hook on all plugins.\n */\n afterRender(): void {\n for (const plugin of this.plugins) {\n plugin.afterRender?.();\n }\n }\n\n /**\n * Execute onScrollRender hook on all plugins.\n * Called after scroll-triggered row rendering for lightweight visual state updates.\n */\n onScrollRender(): void {\n for (const plugin of this.plugins) {\n plugin.onScrollRender?.();\n }\n }\n\n /**\n * Get total extra height contributed by plugins (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations.\n */\n getExtraHeight(): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeight === 'function') {\n total += plugin.getExtraHeight();\n }\n }\n return total;\n }\n\n /**\n * Check if any plugin is contributing extra height.\n * When true, plugins are managing variable row heights and the grid should\n * not override the base row height via #measureRowHeight().\n */\n hasExtraHeight(): boolean {\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeight === 'function' && plugin.getExtraHeight() > 0) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Get extra height from plugins that appears before a given row index.\n * Used by virtualization to correctly position the scroll window.\n */\n getExtraHeightBefore(beforeRowIndex: number): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeightBefore === 'function') {\n total += plugin.getExtraHeightBefore(beforeRowIndex);\n }\n }\n return total;\n }\n\n /**\n * Adjust the virtualization start index based on plugin needs.\n * Returns the minimum start index from all plugins.\n */\n adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n let adjustedStart = start;\n for (const plugin of this.plugins) {\n if (typeof plugin.adjustVirtualStart === 'function') {\n const pluginStart = plugin.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginStart < adjustedStart) {\n adjustedStart = pluginStart;\n }\n }\n }\n return adjustedStart;\n }\n\n /**\n * Execute renderRow hook on all plugins.\n * Returns true if any plugin handled the row.\n */\n renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean {\n for (const plugin of this.plugins) {\n if (plugin.renderRow?.(row, rowEl, rowIndex)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n *\n * Common query types are defined in PLUGIN_QUERIES, but plugins can define their own.\n *\n * @param query - The query object containing type and context\n * @returns Array of non-undefined responses from plugins\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n const responses: T[] = [];\n for (const plugin of this.plugins) {\n const response = plugin.onPluginQuery?.(query);\n if (response !== undefined) {\n responses.push(response as T);\n }\n }\n return responses;\n }\n\n /**\n * Execute onKeyDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onKeyDown(event: KeyboardEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onKeyDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellClick(event: CellClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onRowClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onRowClick(event: RowClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onRowClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onHeaderClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onHeaderClick(event: HeaderClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onHeaderClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onScroll hook on all plugins.\n */\n onScroll(event: ScrollEvent): void {\n for (const plugin of this.plugins) {\n plugin.onScroll?.(event);\n }\n }\n\n /**\n * Execute onCellMouseDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseDown(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseMove hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseMove(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseMove?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseUp hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseUp(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseUp?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Collect horizontal scroll boundary offsets from all plugins.\n * Combines offsets from all plugins that report them.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Combined left and right pixel offsets, plus skipScroll if any plugin requests it\n */\n getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n let left = 0;\n let right = 0;\n let skipScroll = false;\n for (const plugin of this.plugins) {\n const offsets = plugin.getHorizontalScrollOffsets?.(rowEl, focusedCell);\n if (offsets) {\n left += offsets.left;\n right += offsets.right;\n if (offsets.skipScroll) {\n skipScroll = true;\n }\n }\n }\n return { left, right, skipScroll };\n }\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Collect tool panels from all plugins.\n * Returns panels sorted by order (ascending).\n */\n getToolPanels(): {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] {\n const panels: {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const panel = plugin.getToolPanel?.();\n if (panel) {\n panels.push({ plugin, panel });\n }\n }\n // Sort by order (ascending), default to 0\n return panels.sort((a, b) => (a.panel.order ?? 0) - (b.panel.order ?? 0));\n }\n\n /**\n * Collect header contents from all plugins.\n * Returns contents sorted by order (ascending).\n */\n getHeaderContents(): {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] {\n const contents: {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const content = plugin.getHeaderContent?.();\n if (content) {\n contents.push({ plugin, content });\n }\n }\n // Sort by order (ascending), default to 0\n return contents.sort((a, b) => (a.content.order ?? 0) - (b.content.order ?? 0));\n }\n // #endregion\n}\n","import styles from './grid.css?inline';\nimport { autoSizeColumns, updateTemplate } from './internal/columns';\nimport { ConfigManager } from './internal/config-manager';\nimport { setupCellEventDelegation, setupRootEventDelegation } from './internal/event-delegation';\nimport { renderHeader } from './internal/header';\nimport { cancelIdle, scheduleIdle } from './internal/idle-scheduler';\nimport { ensureCellVisible } from './internal/keyboard';\nimport { RenderPhase, RenderScheduler } from './internal/render-scheduler';\nimport { createResizeController } from './internal/resize';\nimport { invalidateCellCache, renderVisibleRows } from './internal/rows';\nimport {\n buildGridDOMIntoElement,\n cleanupShellState,\n createShellController,\n createShellState,\n parseLightDomShell,\n parseLightDomToolButtons,\n parseLightDomToolPanels,\n renderCustomToolbarContents,\n renderHeaderContent,\n renderShellHeader,\n setupShellEventListeners,\n setupToolPanelResize,\n shouldRenderShellHeader,\n type ShellController,\n type ShellState,\n type ToolPanelRendererFactory,\n} from './internal/shell';\nimport {\n cancelMomentum,\n createTouchScrollState,\n setupTouchScrollListeners,\n type TouchScrollState,\n} from './internal/touch-scroll';\nimport { validatePluginConfigRules, validatePluginIncompatibilities, validatePluginProperties } from './internal/validate-config';\nimport type { CellMouseEvent, ScrollEvent } from './plugin';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n HeaderClickEvent,\n PluginQuery,\n RowClickEvent,\n} from './plugin/base-plugin';\nimport { PluginManager } from './plugin/plugin-manager';\nimport type {\n AnimationConfig,\n CellChangeDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n FitMode,\n FrameworkAdapter,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n InternalGrid,\n ResizeController,\n ToolbarContentDefinition,\n ToolPanelDefinition,\n UpdateSource,\n VirtualState,\n} from './types';\nimport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS } from './types';\n\n/**\n * High-performance data grid web component.\n *\n * ## Instantiation\n *\n * **Do not call the constructor directly.** Web components must be created via\n * the DOM API. Use one of these approaches:\n *\n * ```typescript\n * // Recommended: Use the createGrid() factory for TypeScript type safety\n * import { createGrid, SelectionPlugin } from '@toolbox-web/grid/all';\n *\n * const grid = createGrid<Employee>({\n * columns: [\n * { field: 'name', header: 'Name' },\n * { field: 'email', header: 'Email' }\n * ],\n * plugins: [new SelectionPlugin()]\n * });\n * grid.rows = employees;\n * document.body.appendChild(grid);\n *\n * // Alternative: Query existing element from DOM\n * import { queryGrid } from '@toolbox-web/grid';\n * const grid = queryGrid<Employee>('#my-grid');\n *\n * // Alternative: Use document.createElement (loses type inference)\n * const grid = document.createElement('tbw-grid');\n * ```\n *\n * ## Configuration Architecture\n *\n * The grid follows a **single source of truth** pattern where all configuration\n * is managed by ConfigManager. Users can set configuration via multiple inputs:\n *\n * **Input Sources (precedence low → high):**\n * 1. `gridConfig` property - base configuration object\n * 2. Light DOM elements:\n * - `<tbw-grid-column>` → `effectiveConfig.columns`\n * - `<tbw-grid-header title=\"...\">` → `effectiveConfig.shell.header.title`\n * - `<tbw-grid-header-content>` → `effectiveConfig.shell.header.content`\n * 3. `columns` property → merged into `effectiveConfig.columns`\n * 4. `fitMode` property → merged into `effectiveConfig.fitMode`\n * 5. Column inference from first row (if no columns defined)\n *\n * **Derived State:**\n * - `_columns` - processed columns from `effectiveConfig.columns` after plugin hooks\n * - `_rows` - processed rows after plugin hooks (grouping, filtering, etc.)\n *\n * ConfigManager.merge() is the single place where all inputs converge.\n * All rendering and logic should read from `effectiveConfig` or derived state.\n *\n * @element tbw-grid\n *\n * @csspart container - The main grid container\n * @csspart header - The header row container\n * @csspart body - The body/rows container\n *\n * @cssprop --tbw-color-bg - Background color\n * @cssprop --tbw-color-fg - Foreground/text color\n */\n// Injected by Vite at build time from package.json\ndeclare const __GRID_VERSION__: string;\n\nexport class DataGridElement<T = any> extends HTMLElement implements InternalGrid<T> {\n // TODO: Rename to 'data-grid' when migration is complete\n static readonly tagName = 'tbw-grid';\n /** Version of the grid component, injected at build time from package.json */\n static readonly version = typeof __GRID_VERSION__ !== 'undefined' ? __GRID_VERSION__ : 'dev';\n\n // ---------------- Framework Adapters ----------------\n /**\n * Registry of framework adapters that handle converting light DOM elements\n * to functional renderers/editors. Framework libraries (Angular, React, Vue)\n * register adapters to enable zero-boilerplate component integration.\n */\n private static adapters: FrameworkAdapter[] = [];\n\n /**\n * Register a framework adapter for handling framework-specific components.\n * Adapters are checked in registration order when processing light DOM templates.\n *\n * @example\n * ```typescript\n * // In @toolbox-web/grid-angular\n * import { AngularGridAdapter } from '@toolbox-web/grid-angular';\n *\n * // One-time setup in app\n * GridElement.registerAdapter(new AngularGridAdapter(injector, appRef));\n * ```\n * @category Framework Adapters\n */\n static registerAdapter(adapter: FrameworkAdapter): void {\n this.adapters.push(adapter);\n }\n\n /**\n * Get all registered framework adapters.\n * Used internally by light DOM parsing to find adapters that can handle templates.\n * @category Framework Adapters\n */\n static getAdapters(): readonly FrameworkAdapter[] {\n return this.adapters;\n }\n\n /**\n * Clear all registered adapters (primarily for testing).\n * @category Framework Adapters\n */\n static clearAdapters(): void {\n this.adapters = [];\n }\n\n // ---------------- Observed Attributes ----------------\n static get observedAttributes(): string[] {\n return ['rows', 'columns', 'grid-config', 'fit-mode'];\n }\n\n /**\n * The render root for the grid. Without Shadow DOM, this is the element itself.\n * This abstraction allows internal code to work the same way regardless of DOM mode.\n */\n get #renderRoot(): HTMLElement {\n return this;\n }\n\n #initialized = false;\n\n // ---------------- Ready Promise ----------------\n #readyPromise: Promise<void>;\n #readyResolve?: () => void;\n\n // #region Input Properties\n // Raw rows are stored here. Config sources (gridConfig, columns, fitMode)\n // are owned by ConfigManager. Grid.ts property setters delegate to ConfigManager.\n #rows: T[] = [];\n // #endregion\n\n // #region Private properties\n // effectiveConfig is owned by ConfigManager - access via getter\n get #effectiveConfig(): GridConfig<T> {\n return this.#configManager?.effective ?? {};\n }\n\n #connected = false;\n\n // ---------------- Batched Updates ----------------\n // When multiple properties are set in rapid succession (within same microtask),\n // we batch them into a single update to avoid redundant re-renders.\n #pendingUpdate = false;\n #pendingUpdateFlags = {\n rows: false,\n columns: false,\n gridConfig: false,\n fitMode: false,\n };\n\n // ---------------- Render Scheduler ----------------\n // Centralizes all rendering through a single RAF-based pipeline\n #scheduler!: RenderScheduler;\n\n #scrollRaf = 0;\n #pendingScrollTop: number | null = null;\n #hasScrollPlugins = false; // Cached flag for plugin scroll handlers\n #renderRowHook?: (row: any, rowEl: HTMLElement, rowIndex: number) => boolean; // Cached hook to avoid closures\n #touchState: TouchScrollState = createTouchScrollState();\n #eventAbortController?: AbortController;\n #resizeObserver?: ResizeObserver;\n #rowHeightObserver?: ResizeObserver; // Watches first row for size changes (CSS loading, custom renderers)\n #idleCallbackHandle?: number; // Handle for cancelling deferred idle work\n\n // Pooled scroll event object (reused to avoid GC pressure during scroll)\n #pooledScrollEvent: ScrollEvent = {\n scrollTop: 0,\n scrollLeft: 0,\n scrollHeight: 0,\n scrollWidth: 0,\n clientHeight: 0,\n clientWidth: 0,\n };\n\n // ---------------- Plugin System ----------------\n #pluginManager!: PluginManager;\n #lastPluginsArray?: BaseGridPlugin[]; // Track last attached plugins to avoid unnecessary re-initialization\n\n // ---------------- Event Listeners ----------------\n #eventListenersAdded = false; // Guard against adding duplicate component-level listeners\n #scrollAbortController?: AbortController; // Separate controller for DOM scroll listeners (recreated on DOM changes)\n\n // ---------------- Column State ----------------\n #initialColumnState?: GridColumnState;\n\n // ---------------- Config Manager ----------------\n #configManager!: ConfigManager<T>;\n\n // ---------------- Shell State ----------------\n #shellState: ShellState = createShellState();\n #shellController!: ShellController;\n #resizeCleanup?: () => void;\n\n // ---------------- Row ID Map ----------------\n // O(1) lookup for rows by ID. Rebuilt when rows change.\n #rowIdMap = new Map<string, { row: T; index: number }>();\n // #endregion\n\n // #region Derived State\n // _rows: result of applying plugin processRows hooks\n _rows: T[] = [];\n\n // _baseColumns: columns before plugin transformation (analogous to #rows for row processing)\n // This is the source of truth for processColumns - plugins transform these\n #baseColumns: ColumnInternal<T>[] = [];\n\n // _columns is a getter/setter that operates on effectiveConfig.columns\n // This ensures effectiveConfig.columns is the single source of truth for columns\n // _columns always contains ALL columns (including hidden)\n get _columns(): ColumnInternal<T>[] {\n return (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n }\n set _columns(value: ColumnInternal<T>[]) {\n this.#effectiveConfig.columns = value as ColumnConfig<T>[];\n }\n\n // visibleColumns returns only visible columns for rendering\n // This is what header/row rendering should use\n get _visibleColumns(): ColumnInternal<T>[] {\n return this._columns.filter((c) => !c.hidden);\n }\n // #endregion\n\n // #region Runtime State (Plugin-accessible)\n // DOM references\n _headerRowEl!: HTMLElement;\n _bodyEl!: HTMLElement;\n _rowPool: HTMLElement[] = [];\n _resizeController!: ResizeController;\n\n // Virtualization & scroll state\n _virtualization: VirtualState = {\n enabled: true,\n rowHeight: 28,\n bypassThreshold: 24,\n start: 0,\n end: 0,\n container: null,\n viewportEl: null,\n totalHeightEl: null,\n };\n\n // Focus & navigation\n _focusRow = 0;\n _focusCol = 0;\n /** Flag to restore focus styling after next render. @internal */\n _restoreFocusAfterRender = false;\n\n // Sort state\n _sortState: { field: string; direction: 1 | -1 } | null = null;\n\n // Layout\n _gridTemplate = '';\n // #endregion\n\n // #region Implementation Details (Internal only)\n __rowRenderEpoch = 0;\n __didInitialAutoSize = false;\n\n /** Light DOM columns cache - delegates to ConfigManager */\n get __lightDomColumnsCache(): ColumnInternal[] | undefined {\n return this.#configManager?.lightDomColumnsCache as ColumnInternal[] | undefined;\n }\n set __lightDomColumnsCache(value: ColumnInternal[] | undefined) {\n if (this.#configManager) {\n this.#configManager.lightDomColumnsCache = value as ColumnInternal<T>[] | undefined;\n }\n }\n\n /** Original column nodes - delegates to ConfigManager */\n get __originalColumnNodes(): HTMLElement[] | undefined {\n return this.#configManager?.originalColumnNodes;\n }\n set __originalColumnNodes(value: HTMLElement[] | undefined) {\n if (this.#configManager) {\n this.#configManager.originalColumnNodes = value;\n }\n }\n\n __originalOrder: T[] = [];\n\n /**\n * Framework adapter instance set by framework directives (Angular Grid, React DataGrid).\n * Used to handle framework-specific component rendering.\n * @internal\n */\n __frameworkAdapter?: FrameworkAdapter;\n\n // Cached DOM refs for hot path (refreshVirtualWindow) - avoid querySelector per scroll\n __rowsBodyEl: HTMLElement | null = null;\n // #endregion\n\n // #region Public API Props (getters/setters)\n // Getters return the EFFECTIVE value (after merging), not the raw input.\n // This is what consumers and plugins need - the current resolved state.\n // Setters update input properties which trigger re-merge into effectiveConfig.\n\n /**\n * Get or set the row data displayed in the grid.\n *\n * The getter returns processed rows (after filtering, sorting, grouping by plugins).\n * The setter accepts new source data and triggers a re-render.\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Set initial data\n * grid.rows = employees;\n *\n * // Update with new data (triggers re-render)\n * grid.rows = [...employees, newEmployee];\n *\n * // Read current (processed) rows\n * console.log(`Displaying ${grid.rows.length} rows`);\n * ```\n */\n get rows(): T[] {\n return this._rows;\n }\n set rows(value: T[]) {\n const oldValue = this.#rows;\n this.#rows = value;\n if (oldValue !== value) {\n this.#queueUpdate('rows');\n }\n }\n\n /**\n * Get the original unfiltered/unprocessed source rows.\n *\n * Use this when you need access to all source data regardless of active\n * filters, sorting, or grouping applied by plugins. The `rows` property\n * returns processed data, while `sourceRows` returns the original input.\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Get total count including filtered-out rows\n * console.log(`${grid.rows.length} of ${grid.sourceRows.length} rows visible`);\n *\n * // Export all data, not just visible\n * exportToCSV(grid.sourceRows);\n * ```\n */\n get sourceRows(): T[] {\n return this.#rows;\n }\n\n /**\n * Get or set the column configurations.\n *\n * The getter returns processed columns (after plugin transformations).\n * The setter accepts an array of column configs or a column config map.\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Set columns as array\n * grid.columns = [\n * { field: 'name', header: 'Name', width: 200 },\n * { field: 'email', header: 'Email' },\n * { field: 'role', header: 'Role', width: 120 }\n * ];\n *\n * // Set columns as map (keyed by field)\n * grid.columns = {\n * name: { header: 'Name', width: 200 },\n * email: { header: 'Email' },\n * role: { header: 'Role', width: 120 }\n * };\n *\n * // Read current columns\n * grid.columns.forEach(col => {\n * console.log(`${col.field}: ${col.width ?? 'auto'}`);\n * });\n * ```\n */\n get columns(): ColumnConfig<T>[] {\n return [...this._columns] as ColumnConfig<T>[];\n }\n set columns(value: ColumnConfig<T>[] | ColumnConfigMap<T> | undefined) {\n const oldValue = this.#configManager?.getColumns();\n this.#configManager?.setColumns(value);\n if (oldValue !== value) {\n this.#queueUpdate('columns');\n }\n }\n\n /**\n * Get or set the full grid configuration object.\n *\n * The getter returns the effective (merged) configuration.\n * The setter accepts a new configuration and triggers a full re-render.\n *\n * @group Configuration\n * @example\n * ```typescript\n * import { SelectionPlugin, SortingPlugin } from '@toolbox-web/grid/all';\n *\n * // Set full configuration\n * grid.gridConfig = {\n * columns: [\n * { field: 'name', header: 'Name' },\n * { field: 'status', header: 'Status' }\n * ],\n * fitMode: 'stretch',\n * plugins: [\n * new SelectionPlugin({ mode: 'row' }),\n * new SortingPlugin()\n * ]\n * };\n *\n * // Read current configuration\n * console.log('Fit mode:', grid.gridConfig.fitMode);\n * console.log('Columns:', grid.gridConfig.columns?.length);\n * ```\n */\n get gridConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n set gridConfig(value: GridConfig<T> | undefined) {\n const oldValue = this.#configManager?.getGridConfig();\n this.#configManager?.setGridConfig(value);\n if (oldValue !== value) {\n // Clear light DOM column cache so columns are re-parsed from light DOM\n // This is needed for frameworks like Angular that project content asynchronously\n this.#configManager.clearLightDomCache();\n this.#queueUpdate('gridConfig');\n }\n }\n\n /**\n * Get or set the column sizing mode.\n *\n * - `'stretch'` (default): Columns stretch to fill available width\n * - `'fixed'`: Columns use explicit widths; horizontal scroll if needed\n * - `'auto'`: Columns auto-size to content on initial render\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Use fixed widths with horizontal scroll\n * grid.fitMode = 'fixed';\n *\n * // Stretch columns to fill container\n * grid.fitMode = 'stretch';\n *\n * // Auto-size columns based on content\n * grid.fitMode = 'auto';\n * ```\n */\n get fitMode(): FitMode {\n return this.#effectiveConfig.fitMode ?? 'stretch';\n }\n set fitMode(value: FitMode | undefined) {\n const oldValue = this.#configManager?.getFitMode();\n this.#configManager?.setFitMode(value);\n if (oldValue !== value) {\n this.#queueUpdate('fitMode');\n }\n }\n\n /**\n * Effective config accessor for internal modules and plugins.\n * Returns the merged config (single source of truth) before plugin processing.\n * Use this when you need the raw merged config (e.g., for column definitions including hidden).\n * @group State Access\n * @internal Plugin API\n */\n get effectiveConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Plugins and internal code can use this for automatic listener cleanup.\n * @group State Access\n * @internal Plugin API\n * @example\n * element.addEventListener('click', handler, { signal: this.grid.disconnectSignal });\n */\n get disconnectSignal(): AbortSignal {\n // Ensure AbortController exists (created in connectedCallback before plugins attach)\n if (!this.#eventAbortController) {\n this.#eventAbortController = new AbortController();\n }\n return this.#eventAbortController.signal;\n }\n // #endregion\n\n /**\n * @internal Do not call directly. Use `createGrid()` or `document.createElement('tbw-grid')`.\n */\n constructor() {\n super();\n // No Shadow DOM - render directly into the element\n void this.#injectStyles(); // Fire and forget - styles load asynchronously\n this.#readyPromise = new Promise((res) => (this.#readyResolve = res));\n\n // Initialize render scheduler with callbacks\n this.#scheduler = new RenderScheduler({\n mergeConfig: () => {\n // Re-parse light DOM columns to pick up framework adapter renderers\n // This is essential for React/Angular where renderers register asynchronously\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n this.#configManager.merge();\n this.#updatePluginConfigs(); // Sync plugin configs (including auto-detection) before processing\n // Validate that plugin-specific column properties have their required plugins loaded\n // This runs after plugins are loaded and config is merged\n validatePluginProperties(this.#effectiveConfig, this.#pluginManager?.getPlugins() ?? []);\n // Validate plugin configRules (errors/warnings for invalid config combinations)\n validatePluginConfigRules(this.#pluginManager?.getPlugins() ?? []);\n // Validate plugin incompatibilities (warnings for conflicting plugin combinations)\n validatePluginIncompatibilities(this.#pluginManager?.getPlugins() ?? []);\n // Store base columns before plugin transformation\n this.#baseColumns = [...this._columns];\n },\n processColumns: () => this.#processColumns(),\n processRows: () => this.#rebuildRowModel(),\n renderHeader: () => renderHeader(this),\n updateTemplate: () => updateTemplate(this),\n renderVirtualWindow: () => this.refreshVirtualWindow(true, true),\n afterRender: () => {\n this.#pluginManager?.afterRender();\n // Auto-size columns on first render if fitMode is 'fixed'\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed' && !this.__didInitialAutoSize) {\n this.__didInitialAutoSize = true;\n autoSizeColumns(this);\n }\n // Restore focus styling if requested by a plugin\n if (this._restoreFocusAfterRender) {\n this._restoreFocusAfterRender = false;\n ensureCellVisible(this);\n }\n // Set up row height observer after first render (rows are now in DOM)\n if (this._virtualization.enabled && !this.#rowHeightObserverSetup) {\n this.#setupRowHeightObserver();\n }\n },\n isConnected: () => this.isConnected && this.#connected,\n });\n // Connect ready promise to scheduler\n this.#scheduler.setInitialReadyResolver(() => this.#readyResolve?.());\n\n // Initialize shell controller with callbacks\n this.#shellController = createShellController(this.#shellState, {\n getShadow: () => this.#renderRoot,\n getShellConfig: () => this.#effectiveConfig?.shell,\n getAccordionIcons: () => ({\n expand: this.#effectiveConfig?.icons?.expand ?? DEFAULT_GRID_ICONS.expand,\n collapse: this.#effectiveConfig?.icons?.collapse ?? DEFAULT_GRID_ICONS.collapse,\n }),\n emit: (eventName, detail) => this.#emit(eventName, detail),\n refreshShellHeader: () => this.refreshShellHeader(),\n });\n\n // Initialize config manager with callbacks\n this.#configManager = new ConfigManager<T>({\n getRows: () => this.#rows,\n getSortState: () => this._sortState,\n setSortState: (state) => {\n this._sortState = state;\n },\n onConfigChange: () => {\n this.#scheduler.requestPhase(RenderPhase.FULL, 'configChange');\n },\n emit: (eventName, detail) => this.#emit(eventName, detail),\n clearRowPool: () => {\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n },\n setup: () => this.#setup(),\n renderHeader: () => renderHeader(this),\n updateTemplate: () => updateTemplate(this),\n refreshVirtualWindow: () => this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'configManager'),\n getVirtualization: () => this._virtualization,\n setRowHeight: (height) => {\n this._virtualization.rowHeight = height;\n },\n applyAnimationConfig: (config) => this.#applyAnimationConfig(config),\n getShellLightDomTitle: () => this.#shellState.lightDomTitle,\n getShellToolPanels: () => this.#shellState.toolPanels,\n getShellHeaderContents: () => this.#shellState.headerContents,\n getShellToolbarContents: () => this.#shellState.toolbarContents,\n getShellLightDomHeaderContent: () => this.#shellState.lightDomHeaderContent,\n getShellHasToolButtonsContainer: () => this.#shellState.hasToolButtonsContainer,\n });\n }\n\n /** ID for the consolidated grid stylesheet in document.head */\n static readonly #STYLE_ELEMENT_ID = 'tbw-grid-styles';\n\n /** Track injected base styles CSS text */\n static #baseStyles = '';\n\n /** Track injected plugin styles by plugin name (accumulates across all grid instances) */\n static #pluginStylesMap = new Map<string, string>();\n\n /**\n * Get or create the consolidated style element in document.head.\n * All grid and plugin styles are combined into this single element.\n */\n static #getStyleElement(): HTMLStyleElement {\n let styleEl = document.getElementById(this.#STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = this.#STYLE_ELEMENT_ID;\n styleEl.setAttribute('data-tbw-grid', 'true');\n document.head.appendChild(styleEl);\n }\n return styleEl;\n }\n\n /**\n * Update the consolidated stylesheet with current base + plugin styles.\n */\n static #updateStyleElement(): void {\n const styleEl = this.#getStyleElement();\n // Combine base styles and all accumulated plugin styles\n const pluginStyles = Array.from(this.#pluginStylesMap.values()).join('\\n');\n styleEl.textContent = `${this.#baseStyles}\\n\\n/* Plugin Styles */\\n${pluginStyles}`;\n }\n\n /**\n * Inject grid styles into the document.\n * All styles go into a single <style id=\"tbw-grid-styles\"> element in document.head.\n * Uses a singleton pattern to avoid duplicate injection across multiple grid instances.\n */\n async #injectStyles(): Promise<void> {\n // If base styles already injected, nothing to do\n if (DataGridElement.#baseStyles) {\n return;\n }\n\n // If styles is a string (from ?inline import in Vite builds), use it directly\n if (typeof styles === 'string' && styles.length > 0) {\n DataGridElement.#baseStyles = styles;\n DataGridElement.#updateStyleElement();\n return;\n }\n\n // Fallback: styles is undefined (e.g., when imported in Angular from source without Vite processing)\n // Angular includes grid.css in global styles - extract it from document.styleSheets\n // Wait a bit for Angular to finish loading styles\n await new Promise((resolve) => setTimeout(resolve, 50));\n\n try {\n let gridCssText = '';\n\n // Try to find the stylesheet containing grid CSS\n // Angular bundles all CSS files from angular.json styles array into one stylesheet\n // We need to find the stylesheet with grid CSS and extract ALL of it (including plugin CSS)\n for (const stylesheet of Array.from(document.styleSheets)) {\n try {\n // For inline/bundled stylesheets, check if it contains grid CSS\n const rules = Array.from(stylesheet.cssRules || []);\n const cssText = rules.map((rule) => rule.cssText).join('\\n');\n\n // Check if this stylesheet contains grid CSS by looking for distinctive selectors\n // Without Shadow DOM, we look for tbw-grid nesting selectors\n if (cssText.includes('.tbw-grid-root') && cssText.includes('tbw-grid')) {\n // Found the bundled stylesheet with grid CSS - use ALL of it\n // This includes core grid.css + all plugin CSS files\n gridCssText = cssText;\n break;\n }\n } catch (e) {\n // CORS or access restriction - skip\n continue;\n }\n }\n\n if (gridCssText) {\n DataGridElement.#baseStyles = gridCssText;\n DataGridElement.#updateStyleElement();\n } else if (typeof process === 'undefined' || process.env?.['NODE_ENV'] !== 'test') {\n // Only warn in non-test environments - test environments (happy-dom, jsdom) don't load stylesheets\n console.warn(\n '[tbw-grid] Could not find grid.css in document.styleSheets. Grid styling will not work.',\n 'Available stylesheets:',\n Array.from(document.styleSheets).map((s) => s.href || '(inline)'),\n );\n }\n } catch (err) {\n console.warn('[tbw-grid] Failed to extract grid.css from document stylesheets:', err);\n }\n }\n\n // ---------------- Plugin System ----------------\n\n /**\n * Get a plugin instance by its class.\n * Used by plugins for inter-plugin communication.\n * @group Plugin Communication\n * @internal Plugin API\n */\n getPlugin<P extends BaseGridPlugin>(PluginClass: new (...args: any[]) => P): P | undefined {\n return this.#pluginManager?.getPlugin(PluginClass);\n }\n\n /**\n * Get a plugin instance by its name.\n * Used for loose coupling between plugins (avoids static imports).\n * @group Plugin Communication\n * @internal Plugin API\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.#pluginManager?.getPluginByName(name);\n }\n\n /**\n * Request a full re-render of the grid.\n * Called by plugins when they need the grid to update.\n * Note: This does NOT reset plugin state - just re-processes rows/columns and renders.\n * @group Rendering\n * @internal Plugin API\n */\n requestRender(): void {\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'plugin:requestRender');\n }\n\n /**\n * Request a full re-render and restore focus styling afterward.\n * Use this when a plugin action (like expand/collapse) triggers a render\n * but needs to maintain keyboard navigation focus.\n * @group Rendering\n * @internal Plugin API\n */\n requestRenderWithFocus(): void {\n this._restoreFocusAfterRender = true;\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'plugin:requestRenderWithFocus');\n }\n\n /**\n * Update the grid's column template CSS.\n * Called by resize controller during column resize operations.\n * @group Rendering\n * @internal Plugin API\n */\n updateTemplate(): void {\n updateTemplate(this);\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Called by plugins when they only need to update CSS classes/styles.\n * This runs all plugin afterRender hooks without rebuilding row/column DOM.\n * @group Rendering\n * @internal Plugin API\n */\n requestAfterRender(): void {\n this.#scheduler.requestPhase(RenderPhase.STYLE, 'plugin:requestAfterRender');\n }\n\n /**\n * Initialize plugin system with instances from config.\n * Plugins are class instances passed in gridConfig.plugins[].\n */\n #initializePlugins(): void {\n // Create plugin manager for this grid\n this.#pluginManager = new PluginManager(this);\n\n // Get plugin instances from config - ensure it's an array\n const pluginsConfig = this.#effectiveConfig?.plugins;\n const plugins = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Attach all plugins\n this.#pluginManager.attachAll(plugins);\n }\n\n /**\n * Inject all plugin styles into the consolidated style element.\n * Plugin styles are appended after base grid styles in the same <style> element.\n * Uses a Map to accumulate styles from all grid instances on the page.\n */\n #injectAllPluginStyles(): void {\n const pluginStyles = this.#pluginManager?.getPluginStyles() ?? [];\n let hasNewStyles = false;\n\n for (const { name, styles } of pluginStyles) {\n if (!DataGridElement.#pluginStylesMap.has(name)) {\n DataGridElement.#pluginStylesMap.set(name, styles);\n hasNewStyles = true;\n }\n }\n\n if (hasNewStyles) {\n DataGridElement.#updateStyleElement();\n }\n }\n\n /**\n * Update plugins when grid config changes.\n * With class-based plugins, we need to detach old and attach new.\n * Skips re-initialization if the plugins array hasn't changed.\n */\n #updatePluginConfigs(): void {\n // Get the new plugins array from config\n const pluginsConfig = this.#effectiveConfig?.plugins;\n const newPlugins = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Check if plugins have actually changed (same array reference or same contents)\n // This avoids unnecessary detach/attach cycles on every render\n if (this.#lastPluginsArray === newPlugins) {\n return; // Same array reference - no change\n }\n\n // Check if the arrays have the same plugin instances\n if (\n this.#lastPluginsArray &&\n this.#lastPluginsArray.length === newPlugins.length &&\n this.#lastPluginsArray.every((p, i) => p === newPlugins[i])\n ) {\n // Same plugins in same order - just update the reference tracking\n this.#lastPluginsArray = newPlugins;\n return;\n }\n\n // Plugins have changed - detach old and attach new\n if (this.#pluginManager) {\n this.#pluginManager.detachAll();\n }\n\n // Clear plugin-contributed panels BEFORE re-initializing plugins\n // This is critical: when plugins are re-initialized, they create NEW instances\n // with NEW render functions. The old panel definitions have stale closures.\n // We preserve light DOM panels (tracked in lightDomToolPanelIds) and\n // API-registered panels (tracked in apiToolPanelIds).\n for (const panelId of this.#shellState.toolPanels.keys()) {\n const isLightDom = this.#shellState.lightDomToolPanelIds.has(panelId);\n const isApiRegistered = this.#shellState.apiToolPanelIds.has(panelId);\n if (!isLightDom && !isApiRegistered) {\n // Clean up any active panel cleanup function\n const cleanup = this.#shellState.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n this.#shellState.panelCleanups.delete(panelId);\n }\n this.#shellState.toolPanels.delete(panelId);\n }\n }\n\n // Similarly clear plugin-contributed header contents\n // Header contents don't have a light DOM tracking set, so clear all and re-collect\n for (const contentId of this.#shellState.headerContents.keys()) {\n const cleanup = this.#shellState.headerContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n this.#shellState.headerContentCleanups.delete(contentId);\n }\n this.#shellState.headerContents.delete(contentId);\n }\n\n this.#initializePlugins();\n this.#injectAllPluginStyles();\n\n // Track the new plugins array\n this.#lastPluginsArray = newPlugins;\n\n // Re-collect plugin shell contributions (tool panels, header content)\n // Now the new plugin instances will add their fresh panel definitions\n this.#collectPluginShellContributions();\n\n // Update cached scroll plugin flag\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n }\n\n /**\n * Clean up plugin states when grid disconnects.\n */\n #destroyPlugins(): void {\n this.#pluginManager?.detachAll();\n }\n\n /**\n * Collect tool panels and header content from all plugins.\n * Called after plugins are attached but before render.\n */\n #collectPluginShellContributions(): void {\n if (!this.#pluginManager) return;\n\n // Collect tool panels from plugins\n const pluginPanels = this.#pluginManager.getToolPanels();\n for (const { panel } of pluginPanels) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.toolPanels.has(panel.id)) {\n this.#shellState.toolPanels.set(panel.id, panel);\n }\n }\n\n // Collect header contents from plugins\n const pluginContents = this.#pluginManager.getHeaderContents();\n for (const { content } of pluginContents) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.headerContents.has(content.id)) {\n this.#shellState.headerContents.set(content.id, content);\n }\n }\n }\n\n /**\n * Gets a renderer factory for tool panels from registered framework adapters.\n * Returns a factory function that tries each adapter in order until one handles the element.\n */\n #getToolPanelRendererFactory(): ToolPanelRendererFactory | undefined {\n const adapters = DataGridElement.getAdapters();\n if (adapters.length === 0 && !this.__frameworkAdapter) return undefined;\n\n // Also check for instance-level adapter (e.g., __frameworkAdapter from Angular Grid directive)\n const instanceAdapter = this.__frameworkAdapter;\n\n return (element: HTMLElement) => {\n // Try instance adapter first (from Grid directive)\n if (instanceAdapter?.createToolPanelRenderer) {\n const renderer = instanceAdapter.createToolPanelRenderer(element);\n if (renderer) return renderer;\n }\n\n // Try global adapters\n for (const adapter of adapters) {\n if (adapter.createToolPanelRenderer) {\n const renderer = adapter.createToolPanelRenderer(element);\n if (renderer) return renderer;\n }\n }\n\n return undefined;\n };\n }\n\n // ---------------- Lifecycle ----------------\n /** @internal Web component lifecycle - not part of public API */\n connectedCallback(): void {\n if (!this.hasAttribute('tabindex')) this.tabIndex = 0;\n if (!this.hasAttribute('version')) this.setAttribute('version', DataGridElement.version);\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Create AbortController for all event listeners (grid internal + plugins)\n // This must happen BEFORE plugins attach so they can use disconnectSignal\n // Abort any previous controller first (in case of re-connect)\n if (this.#eventAbortController) {\n this.#eventAbortController.abort();\n this.#eventListenersAdded = false; // Reset so listeners can be re-added\n }\n this.#eventAbortController = new AbortController();\n\n // Cancel any pending idle work from previous connection\n if (this.#idleCallbackHandle) {\n cancelIdle(this.#idleCallbackHandle);\n this.#idleCallbackHandle = undefined;\n }\n\n // === CRITICAL PATH (synchronous) - needed for first paint ===\n\n // Parse light DOM shell elements BEFORE merging config\n parseLightDomShell(this, this.#shellState);\n // Parse light DOM tool buttons container\n parseLightDomToolButtons(this, this.#shellState);\n // Parse light DOM tool panels (framework adapters may not be ready yet, but vanilla JS works)\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n // Parse light DOM columns (must be before merge to pick up templates)\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n\n // Merge all config sources into effectiveConfig (including columns and shell)\n this.#configManager.merge();\n\n // Initialize plugin system (now plugins can access disconnectSignal)\n this.#initializePlugins();\n\n // Track the initial plugins array to avoid unnecessary re-initialization\n const pluginsConfig = this.#effectiveConfig?.plugins;\n this.#lastPluginsArray = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Collect tool panels and header content from plugins (must be before render)\n this.#collectPluginShellContributions();\n\n if (!this.#initialized) {\n this.#render();\n this.#injectAllPluginStyles(); // Inject plugin styles after render\n this.#initialized = true;\n }\n this.#afterConnect();\n\n // === DEFERRED WORK (idle) - not needed for first paint ===\n this.#idleCallbackHandle = scheduleIdle(\n () => {\n // Set up Light DOM observation via ConfigManager\n // This handles frameworks like Angular that project content asynchronously\n this.#setupLightDomHandlers();\n },\n { timeout: 100 },\n );\n }\n\n /** @internal Web component lifecycle - not part of public API */\n disconnectedCallback(): void {\n // Cancel any pending idle work\n if (this.#idleCallbackHandle) {\n cancelIdle(this.#idleCallbackHandle);\n this.#idleCallbackHandle = undefined;\n }\n\n // Clean up plugin states\n this.#destroyPlugins();\n\n // Clean up shell state\n cleanupShellState(this.#shellState);\n this.#shellController.setInitialized(false);\n\n // Clean up tool panel resize handler\n this.#resizeCleanup?.();\n this.#resizeCleanup = undefined;\n\n // Cancel any ongoing touch momentum animation\n cancelMomentum(this.#touchState);\n\n // Abort all event listeners (internal + document-level)\n // This cleans up all listeners added with { signal } option\n if (this.#eventAbortController) {\n this.#eventAbortController.abort();\n this.#eventAbortController = undefined;\n }\n // Also abort scroll-specific listeners (separate controller)\n this.#scrollAbortController?.abort();\n this.#scrollAbortController = undefined;\n this.#eventListenersAdded = false; // Reset so listeners can be re-added on reconnect\n\n if (this._resizeController) {\n this._resizeController.dispose();\n }\n if (this.#resizeObserver) {\n this.#resizeObserver.disconnect();\n this.#resizeObserver = undefined;\n }\n if (this.#rowHeightObserver) {\n this.#rowHeightObserver.disconnect();\n this.#rowHeightObserver = undefined;\n this.#rowHeightObserverSetup = false;\n }\n\n // Clear caches to prevent memory leaks\n invalidateCellCache(this);\n this.#customStyleSheets.clear();\n\n // Clear plugin tracking to allow fresh initialization on reconnect\n this.#lastPluginsArray = undefined;\n\n // Clear row pool - detach from DOM and release references\n for (const rowEl of this._rowPool) {\n rowEl.remove();\n }\n this._rowPool.length = 0;\n\n // Clear cached DOM refs to prevent stale references\n this.__rowsBodyEl = null;\n\n this.#connected = false;\n }\n\n /**\n * Handle HTML attribute changes.\n * Only processes attribute values when SET (non-null).\n * Removing an attribute does NOT clear JS-set properties.\n * @internal Web component lifecycle - not part of public API\n */\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue || !newValue || newValue === 'null' || newValue === 'undefined') return;\n\n // JSON attributes need parsing\n if (name === 'rows' || name === 'columns' || name === 'grid-config') {\n try {\n const parsed = JSON.parse(newValue);\n if (name === 'rows') this.rows = parsed;\n else if (name === 'columns') this.columns = parsed;\n else if (name === 'grid-config') this.gridConfig = parsed;\n } catch {\n console.warn(`[tbw-grid] Invalid JSON for '${name}' attribute:`, newValue);\n }\n } else if (name === 'fit-mode') {\n this.fitMode = newValue as FitMode;\n }\n }\n\n #afterConnect(): void {\n // Shell changes the DOM structure - need to find elements appropriately\n const gridContent = this.#renderRoot.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#renderRoot.querySelector('.tbw-grid-root');\n\n this._headerRowEl = gridRoot?.querySelector('.header-row') as HTMLElement;\n // Faux scrollbar pattern:\n // - .faux-vscroll-spacer sets virtual height\n // - .rows-viewport provides visible height for virtualization calculations\n this._virtualization.totalHeightEl = gridRoot?.querySelector('.faux-vscroll-spacer') as HTMLElement;\n this._virtualization.viewportEl = gridRoot?.querySelector('.rows-viewport') as HTMLElement;\n this._bodyEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Cache DOM refs for hot path (refreshVirtualWindow) - avoid querySelector per scroll\n this.__rowsBodyEl = gridRoot?.querySelector('.rows-body') as HTMLElement;\n\n // Initialize shell header content and custom buttons if shell is active\n if (this.#shellController.isInitialized) {\n // Render plugin header content\n renderHeaderContent(this.#renderRoot, this.#shellState);\n // Render custom toolbar contents (render modes) - all contents unified in effectiveConfig\n renderCustomToolbarContents(this.#renderRoot, this.#effectiveConfig?.shell, this.#shellState);\n // Open default section if configured\n const defaultOpen = this.#effectiveConfig?.shell?.toolPanel?.defaultOpen;\n if (defaultOpen && this.#shellState.toolPanels.has(defaultOpen)) {\n this.openToolPanel();\n this.#shellState.expandedSections.add(defaultOpen);\n }\n }\n\n // Mark for tests that afterConnect ran\n this.setAttribute('data-upgraded', '');\n this.#connected = true;\n\n // Create resize controller BEFORE setup - renderHeader() needs it for resize handle mousedown events\n this._resizeController = createResizeController(this as unknown as InternalGrid<T>);\n\n // Run setup\n this.#setup();\n\n // Set up DOM-element scroll listeners (these need to be re-attached when DOM is recreated)\n this.#setupScrollListeners(gridRoot);\n\n // Only add component-level event listeners once (afterConnect can be called multiple times)\n // When shell state changes or refreshShellHeader is called, we re-run afterConnect\n // but component-level listeners should not be duplicated (they don't depend on DOM elements)\n // Scroll listeners are already set up above and handle DOM recreation via their own AbortController\n if (this.#eventListenersAdded) {\n return;\n }\n this.#eventListenersAdded = true;\n\n // Get the signal for event listener cleanup (AbortController created in connectedCallback)\n const signal = this.disconnectSignal;\n\n // Set up all root-level and document-level event listeners\n // Consolidates keydown, mousedown, mousemove, mouseup in one place (event-delegation.ts)\n setupRootEventDelegation(this as unknown as InternalGrid<T>, this, this.#renderRoot, signal);\n\n // Note: click/dblclick handlers are set up via setupCellEventDelegation in #setupScrollListeners\n // This consolidates all body-level delegated event handlers in one place (event-delegation.ts)\n\n // Determine row height for virtualization:\n // 1. User-configured rowHeight in gridConfig takes precedence\n // 2. Otherwise, measure actual row height from DOM (respects CSS variable --tbw-row-height)\n const userRowHeight = this.#effectiveConfig.rowHeight;\n if (userRowHeight && userRowHeight > 0) {\n this._virtualization.rowHeight = userRowHeight;\n } else {\n // Initial measurement after first paint (CSS is already loaded via Vite)\n // ResizeObserver in #setupScrollListeners handles subsequent dynamic changes\n requestAnimationFrame(() => this.#measureRowHeight());\n }\n\n // Initialize ARIA selection state\n queueMicrotask(() => this.#updateAriaSelection());\n\n // Request initial render through the scheduler.\n // The scheduler resolves ready() after the render cycle completes.\n // Framework adapters (React/Angular) will request COLUMNS phase via refreshColumns(),\n // which will be batched with this request - highest phase wins.\n this.#scheduler.requestPhase(RenderPhase.FULL, 'afterConnect');\n }\n\n /**\n * Measure actual row height from DOM.\n * Finds the tallest cell to account for custom renderers that may push height.\n */\n #measureRowHeight(): void {\n // Skip if a plugin is managing variable row heights (e.g., ResponsivePlugin with groups)\n // In that case, the plugin handles height via getExtraHeight() and we shouldn't\n // override the base row height, which would cause oscillation loops.\n if (this.#pluginManager.hasExtraHeight()) {\n return;\n }\n\n const firstRow = this._bodyEl?.querySelector('.data-grid-row');\n if (!firstRow) return;\n\n // Find the tallest cell in the row (custom renderers may push some cells taller)\n const cells = firstRow.querySelectorAll('.cell');\n let maxCellHeight = 0;\n cells.forEach((cell) => {\n const h = (cell as HTMLElement).offsetHeight;\n if (h > maxCellHeight) maxCellHeight = h;\n });\n\n const rowRect = (firstRow as HTMLElement).getBoundingClientRect();\n\n // Use the larger of row height or max cell height\n const measuredHeight = Math.max(rowRect.height, maxCellHeight);\n // Use a 1px threshold to avoid oscillation from sub-pixel rounding\n if (measuredHeight > 0 && Math.abs(measuredHeight - this._virtualization.rowHeight) > 1) {\n this._virtualization.rowHeight = measuredHeight;\n // Use scheduler to batch with other pending work\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'measureRowHeight');\n }\n }\n\n /**\n * Set up scroll-related event listeners on DOM elements.\n * These need to be re-attached when the DOM is recreated (e.g., shell toggle).\n * Uses a separate AbortController that is recreated each time.\n */\n #setupScrollListeners(gridRoot: Element | null): void {\n // Abort any existing scroll listeners before adding new ones\n this.#scrollAbortController?.abort();\n this.#scrollAbortController = new AbortController();\n const scrollSignal = this.#scrollAbortController.signal;\n\n // Faux scrollbar pattern: scroll events come from the fake scrollbar element\n // Content area doesn't scroll - rows are positioned via transforms\n const fauxScrollbar = gridRoot?.querySelector('.faux-vscroll') as HTMLElement;\n const rowsEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Store reference for scroll position reading in refreshVirtualWindow\n this._virtualization.container = fauxScrollbar ?? this;\n\n // Cache whether any plugin has scroll handlers (checked once during setup)\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n\n if (fauxScrollbar && rowsEl) {\n fauxScrollbar.addEventListener(\n 'scroll',\n () => {\n // Fast exit if no scroll processing needed\n if (!this._virtualization.enabled && !this.#hasScrollPlugins) return;\n\n const currentScrollTop = fauxScrollbar.scrollTop;\n const rowHeight = this._virtualization.rowHeight;\n\n // Bypass mode: all rows are rendered, just translate by scroll position\n // No need for virtual window calculations\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n rowsEl.style.transform = `translateY(${-currentScrollTop}px)`;\n } else {\n // Virtualized mode: calculate sub-pixel offset for smooth scrolling\n // Even-aligned start preserves zebra stripe parity\n // DOM nth-child(even) will always match data row parity\n const rawStart = Math.floor(currentScrollTop / rowHeight);\n const evenAlignedStart = rawStart - (rawStart % 2);\n const subPixelOffset = -(currentScrollTop - evenAlignedStart * rowHeight);\n rowsEl.style.transform = `translateY(${subPixelOffset}px)`;\n }\n\n // Batch content update with requestAnimationFrame\n // Old content stays visible with smooth offset until new content renders\n this.#pendingScrollTop = currentScrollTop;\n if (!this.#scrollRaf) {\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n if (this.#pendingScrollTop !== null) {\n this.#onScrollBatched(this.#pendingScrollTop);\n this.#pendingScrollTop = null;\n }\n });\n }\n },\n { passive: true, signal: scrollSignal },\n );\n\n // Forward wheel events from content area to faux scrollbar\n // Without this, mouse wheel over content wouldn't scroll\n // Listen on .tbw-grid-content to capture wheel events from entire grid area\n // Note: gridRoot may already BE .tbw-grid-content when shell is active, so search from shadow root\n const gridContentEl = this.#renderRoot.querySelector('.tbw-grid-content') as HTMLElement;\n const scrollArea = this.#renderRoot.querySelector('.tbw-scroll-area') as HTMLElement;\n if (gridContentEl) {\n gridContentEl.addEventListener(\n 'wheel',\n (e: WheelEvent) => {\n // SHIFT+wheel or trackpad deltaX = horizontal scroll\n const isHorizontal = e.shiftKey || Math.abs(e.deltaX) > Math.abs(e.deltaY);\n\n if (isHorizontal && scrollArea) {\n const delta = e.shiftKey ? e.deltaY : e.deltaX;\n const { scrollLeft, scrollWidth, clientWidth } = scrollArea;\n const canScroll = (delta > 0 && scrollLeft < scrollWidth - clientWidth) || (delta < 0 && scrollLeft > 0);\n if (canScroll) {\n e.preventDefault();\n scrollArea.scrollLeft += delta;\n }\n } else if (!isHorizontal) {\n const { scrollTop, scrollHeight, clientHeight } = fauxScrollbar;\n const canScroll =\n (e.deltaY > 0 && scrollTop < scrollHeight - clientHeight) || (e.deltaY < 0 && scrollTop > 0);\n if (canScroll) {\n e.preventDefault();\n fauxScrollbar.scrollTop += e.deltaY;\n }\n }\n // If can't scroll, event bubbles to scroll the page\n },\n { passive: false, signal: scrollSignal },\n );\n\n // Touch scrolling support for mobile devices\n // Supports both vertical (via faux scrollbar) and horizontal (via scroll area) scrolling\n // Includes momentum scrolling for natural \"flick\" behavior\n setupTouchScrollListeners(gridContentEl, this.#touchState, { fauxScrollbar, scrollArea }, scrollSignal);\n }\n }\n\n // Set up delegated event handlers for cell interactions (click, dblclick, keydown)\n // This replaces per-cell event listeners with a single set of delegated handlers\n // Dramatically reduces memory usage: 4 listeners total vs. 30,000+ for large grids\n if (this._bodyEl) {\n setupCellEventDelegation(this as unknown as InternalGrid<T>, this._bodyEl, scrollSignal);\n }\n\n // Disconnect existing resize observer before creating new one\n // This ensures we observe the NEW viewport element after DOM recreation\n this.#resizeObserver?.disconnect();\n\n // Resize observer to refresh virtualization when viewport size changes\n // (e.g., when footer is added, window resizes, or shell panel toggles)\n if (this._virtualization.viewportEl) {\n this.#resizeObserver = new ResizeObserver(() => {\n // Use scheduler for viewport resize - batches with other pending work\n // The scheduler already batches multiple requests per RAF\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'resize-observer');\n });\n this.#resizeObserver.observe(this._virtualization.viewportEl);\n }\n\n // Note: We no longer need to schedule init-virtualization here since\n // the initial FULL render from #setup already includes virtualization.\n // Requesting it again here caused duplicate renders on initialization.\n\n // Track focus state via data attribute (shadow DOM doesn't reliably support :focus-within)\n // Listen on shadow root to catch focus events from shadow DOM elements\n // Cast to EventTarget since TypeScript's lib.dom doesn't include focus events on ShadowRoot\n (this.#renderRoot as EventTarget).addEventListener(\n 'focusin',\n () => {\n this.dataset.hasFocus = '';\n },\n { signal: scrollSignal },\n );\n (this.#renderRoot as EventTarget).addEventListener(\n 'focusout',\n (e) => {\n // Only remove if focus is leaving the grid entirely\n // relatedTarget is null when focus leaves the document, or the new focus target\n const newFocus = (e as FocusEvent).relatedTarget as Node | null;\n if (!newFocus || !this.#renderRoot.contains(newFocus)) {\n delete this.dataset.hasFocus;\n }\n },\n { signal: scrollSignal },\n );\n }\n\n /**\n * Set up ResizeObserver on first row to detect height changes.\n * Called after rows are rendered to observe the actual content.\n * Handles dynamic CSS loading, lazy images, font loading, column virtualization, etc.\n */\n #rowHeightObserverSetup = false; // Only set up once per lifecycle\n #setupRowHeightObserver(): void {\n // Only set up once - row height measurement is one-time during initialization\n if (this.#rowHeightObserverSetup) return;\n\n const firstRow = this._bodyEl?.querySelector('.data-grid-row') as HTMLElement | null;\n if (!firstRow) return;\n\n this.#rowHeightObserverSetup = true;\n this.#rowHeightObserver?.disconnect();\n\n // Observe the row element itself, not individual cells.\n // This catches all height changes including:\n // - Custom renderers that push cell height\n // - Column virtualization adding/removing columns\n // - Dynamic content loading (images, fonts)\n // Note: ResizeObserver fires on initial observation in modern browsers,\n // so no separate measurement call is needed.\n this.#rowHeightObserver = new ResizeObserver(() => {\n this.#measureRowHeight();\n });\n this.#rowHeightObserver.observe(firstRow);\n }\n\n // ---------------- Event Emitters ----------------\n #emit<D>(eventName: string, detail: D): void {\n this.dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true, composed: true }));\n }\n\n /** Update ARIA selection attributes on rendered rows/cells */\n #updateAriaSelection(): void {\n // Mark active row and cell with aria-selected\n const rows = this._bodyEl?.querySelectorAll('.data-grid-row');\n rows?.forEach((row, rowIdx) => {\n const isActiveRow = rowIdx === this._focusRow;\n row.setAttribute('aria-selected', String(isActiveRow));\n row.querySelectorAll('.cell').forEach((cell, colIdx) => {\n (cell as HTMLElement).setAttribute('aria-selected', String(isActiveRow && colIdx === this._focusCol));\n });\n });\n }\n\n // ---------------- Batched Update System ----------------\n // Allows multiple property changes within the same microtask to be coalesced\n // into a single update cycle, dramatically reducing redundant renders.\n\n /**\n * Queue an update for a specific property type.\n * All updates queued within the same microtask are batched together.\n */\n #queueUpdate(type: 'rows' | 'columns' | 'gridConfig' | 'fitMode'): void {\n this.#pendingUpdateFlags[type] = true;\n\n // If already queued, skip scheduling\n if (this.#pendingUpdate) return;\n\n this.#pendingUpdate = true;\n // Use queueMicrotask to batch synchronous property sets\n queueMicrotask(() => this.#flushPendingUpdates());\n }\n\n /**\n * Process all pending updates in optimal order.\n * Priority: gridConfig first (may affect all), then columns, rows, fitMode, editMode\n */\n #flushPendingUpdates(): void {\n if (!this.#pendingUpdate || !this.#connected) {\n this.#pendingUpdate = false;\n return;\n }\n\n const flags = this.#pendingUpdateFlags;\n\n // Reset flags before processing to allow new updates during processing\n this.#pendingUpdate = false;\n this.#pendingUpdateFlags = {\n rows: false,\n columns: false,\n gridConfig: false,\n fitMode: false,\n };\n\n // If gridConfig changed, it supersedes columns/fit changes\n // but we still need to handle rows if set separately\n if (flags.gridConfig) {\n this.#applyGridConfigUpdate();\n // Still process rows if set separately (e.g., grid.gridConfig = ...; grid.rows = ...;)\n if (flags.rows) {\n this.#applyRowsUpdate();\n }\n return;\n }\n\n // Process remaining changes in dependency order\n if (flags.columns) {\n this.#applyColumnsUpdate();\n }\n if (flags.rows) {\n this.#applyRowsUpdate();\n }\n if (flags.fitMode) {\n this.#applyFitModeUpdate();\n }\n }\n\n // Individual update applicators - these do the actual work\n #applyRowsUpdate(): void {\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n // Rebuild row ID map for O(1) lookups\n this.#rebuildRowIdMap();\n // Request a ROWS phase render through the scheduler.\n // This batches with any other pending work (e.g., React adapter's refreshColumns).\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'applyRowsUpdate');\n }\n\n /**\n * Rebuild the row ID map for O(1) lookups.\n * Called when rows array changes.\n */\n #rebuildRowIdMap(): void {\n this.#rowIdMap.clear();\n const getRowId = this.#effectiveConfig.getRowId;\n\n this._rows.forEach((row, index) => {\n const id = this.#tryResolveRowId(row, getRowId);\n if (id !== undefined) {\n this.#rowIdMap.set(id, { row, index });\n }\n // Rows without IDs are skipped - they won't be accessible via getRow/updateRow\n });\n }\n\n /**\n * Try to resolve the ID for a row using configured getRowId or fallback.\n * Returns undefined if no ID can be determined (non-throwing).\n * Used internally by #rebuildRowIdMap.\n */\n #tryResolveRowId(row: T, getRowId?: (row: T) => string): string | undefined {\n if (getRowId) {\n return getRowId(row);\n }\n\n // Fallback: common ID fields\n const r = row as Record<string, unknown>;\n if ('id' in r && r.id != null) return String(r.id);\n if ('_id' in r && r._id != null) return String(r._id);\n\n return undefined;\n }\n\n /**\n * Resolve the ID for a row, throwing if not found.\n * Used by public getRowId() method.\n */\n #resolveRowIdOrThrow(row: T, getRowId?: (row: T) => string): string {\n const id = this.#tryResolveRowId(row, getRowId);\n if (id === undefined) {\n throw new Error(\n '[tbw-grid] Cannot determine row ID. ' +\n 'Configure getRowId in gridConfig or ensure rows have an \"id\" property.',\n );\n }\n return id;\n }\n\n #applyColumnsUpdate(): void {\n invalidateCellCache(this);\n this.#configManager.merge();\n this.#setup();\n }\n\n #applyFitModeUpdate(): void {\n this.#configManager.merge();\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed') {\n this.__didInitialAutoSize = false;\n autoSizeColumns(this);\n } else {\n this._columns.forEach((c: any) => {\n if (!c.__userResized && c.__autoSized) delete c.width;\n });\n updateTemplate(this);\n }\n }\n\n #applyGridConfigUpdate(): void {\n // Parse shell config (title, etc.) - needed for React where gridConfig is set after initial render\n parseLightDomShell(this, this.#shellState);\n // Parse tool buttons container before checking shell state\n parseLightDomToolButtons(this, this.#shellState);\n\n const hadShell = !!this.#renderRoot.querySelector('.has-shell');\n const hadToolPanel = !!this.#renderRoot.querySelector('.tbw-tool-panel');\n\n // Count accordion sections before update (to detect new panels added)\n const accordionSectionsBefore = this.#renderRoot.querySelectorAll('.tbw-accordion-section').length;\n\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n this.#configManager.merge();\n this.#updatePluginConfigs();\n\n // Parse light DOM tool panels AFTER plugins are initialized\n // This ensures plugin panels are collected first, then light DOM panels merge in\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Mark sources as changed since parseLightDomToolPanels may have updated shell state maps\n // This ensures the next merge() will pick up any new tool panels\n this.#configManager.markSourcesChanged();\n\n // Re-merge to pick up any light DOM tool panels\n this.#configManager.merge();\n\n const nowNeedsShell = shouldRenderShellHeader(this.#effectiveConfig?.shell);\n const nowHasToolPanels = (this.#effectiveConfig?.shell?.toolPanels?.length ?? 0) > 0;\n\n // Full re-render needed if:\n // 1. Shell state changed (added or removed)\n // 2. Tool panels were added but sidebar doesn't exist in DOM yet\n // 3. Number of tool panels changed (plugin panels added/removed)\n const toolPanelCountChanged = (this.#effectiveConfig?.shell?.toolPanels?.length ?? 0) !== accordionSectionsBefore;\n const needsFullRerender =\n hadShell !== nowNeedsShell ||\n (!hadShell && nowNeedsShell) ||\n (!hadToolPanel && nowHasToolPanels) ||\n (hadToolPanel && toolPanelCountChanged);\n\n if (needsFullRerender) {\n this.#render();\n this.#injectAllPluginStyles(); // Re-inject after render clears shadow DOM\n this.#afterConnect();\n // Rebuild row ID map in case getRowId changed\n this.#rebuildRowIdMap();\n return;\n }\n\n // Update shell header in place if it exists (e.g., title changed)\n // This avoids a full re-render when only shell config changed\n if (hadShell) {\n this.#updateShellHeaderInPlace();\n }\n\n // Rebuild row ID map in case getRowId changed\n this.#rebuildRowIdMap();\n\n // Request a COLUMNS phase render through the scheduler.\n // This batches with any other pending work (e.g., React adapter's refreshColumns).\n // Previously this called rebuildRowModel, processColumns, renderHeader, updateTemplate,\n // and refreshVirtualWindow directly - causing race conditions with framework adapters.\n this.#scheduler.requestPhase(RenderPhase.COLUMNS, 'applyGridConfigUpdate');\n }\n\n /**\n * Update the shell header DOM in place without a full re-render.\n * Handles title, toolbar buttons, and other shell header changes.\n */\n #updateShellHeaderInPlace(): void {\n const shellHeader = this.#renderRoot.querySelector('.tbw-shell-header');\n if (!shellHeader) return;\n\n const title = this.#effectiveConfig.shell?.header?.title ?? this.#shellState.lightDomTitle;\n\n // Update or create title element\n let titleEl = shellHeader.querySelector('.tbw-shell-title') as HTMLElement | null;\n if (title) {\n if (!titleEl) {\n // Create title element if it doesn't exist\n titleEl = document.createElement('h2');\n titleEl.className = 'tbw-shell-title';\n titleEl.setAttribute('part', 'shell-title');\n // Insert at the beginning of the shell header\n shellHeader.insertBefore(titleEl, shellHeader.firstChild);\n }\n titleEl.textContent = title;\n } else if (titleEl) {\n // Remove title element if no title\n titleEl.remove();\n }\n }\n\n // NOTE: Legacy watch handlers have been replaced by the batched update system.\n // The #queueUpdate() method schedules updates which are processed by #flushPendingUpdates()\n // and individual #apply*Update() methods. This coalesces rapid property changes\n // (e.g., setting rows, columns, gridConfig in quick succession) into a single update cycle.\n\n #processColumns(): void {\n // Let plugins process visible columns (column grouping, etc.)\n // Start from base columns (before any plugin transformation) - like #rebuildRowModel uses #rows\n if (this.#pluginManager) {\n // Use base columns as source of truth, falling back to current _columns if not set\n const sourceColumns = this.#baseColumns.length > 0 ? this.#baseColumns : this._columns;\n const visibleCols = sourceColumns.filter((c) => !c.hidden);\n const hiddenCols = sourceColumns.filter((c) => c.hidden);\n const processedColumns = this.#pluginManager.processColumns([...visibleCols]);\n\n // If plugins modified visible columns, update them\n if (processedColumns !== visibleCols) {\n // Build sets for quick lookup\n const sourceFields = new Set(visibleCols.map((c) => c.field));\n const processedFields = new Set(processedColumns.map((c: any) => c.field));\n\n // Check if this is a complete column replacement (e.g., pivot mode)\n // If no processed columns match original columns, use processed columns directly\n const hasMatchingFields = visibleCols.some((c) => processedFields.has(c.field));\n\n if (!hasMatchingFields && processedColumns.length > 0) {\n // Complete replacement: use processed columns directly (pivot mode)\n // Preserve hidden columns at the end\n this._columns = [...processedColumns, ...hiddenCols] as ColumnInternal<T>[];\n } else {\n // Plugins may have:\n // 1. Modified existing columns\n // 2. Added new columns (e.g., expander column)\n // 3. Reordered columns\n // We trust the plugin's output order and include all columns they returned\n // plus any hidden columns at the end\n this._columns = [...processedColumns, ...hiddenCols] as ColumnInternal<T>[];\n }\n } else {\n // Plugins returned columns unchanged, but we may need to restore from base\n this._columns = [...sourceColumns] as ColumnInternal<T>[];\n }\n }\n }\n\n /** Recompute row model via plugin hooks. */\n #rebuildRowModel(): void {\n // Invalidate cell display value cache - rows are changing\n invalidateCellCache(this);\n\n // Start fresh from original rows (plugins will transform them)\n const originalRows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Let plugins process rows (they may add, remove, or transform rows)\n // Plugins can add markers for specialized rendering via the renderRow hook\n const processedRows = this.#pluginManager?.processRows(originalRows) ?? originalRows;\n\n // Store processed rows for rendering\n // Note: processedRows may contain group markers that plugins handle via renderRow hook\n this._rows = processedRows as T[];\n }\n\n /**\n * Apply animation configuration to CSS custom properties on the host element.\n * This makes the grid's animation settings available to plugins via CSS variables.\n * Called by ConfigManager after merge.\n */\n #applyAnimationConfig(gridConfig: GridConfig<T>): void {\n const config: AnimationConfig = {\n ...DEFAULT_ANIMATION_CONFIG,\n ...gridConfig.animation,\n };\n\n // Resolve animation mode\n const mode = config.mode ?? 'reduced-motion';\n let enabled: 0 | 1 = 1;\n\n if (mode === false || mode === 'off') {\n enabled = 0;\n } else if (mode === true || mode === 'on') {\n enabled = 1;\n }\n // For 'reduced-motion', we leave enabled=1 and let CSS @media query handle it\n\n // Set CSS custom properties\n this.style.setProperty('--tbw-animation-duration', `${config.duration}ms`);\n this.style.setProperty('--tbw-animation-easing', config.easing ?? 'ease-out');\n this.style.setProperty('--tbw-animation-enabled', String(enabled));\n\n // Set data attribute for mode-based CSS selectors\n this.dataset.animationMode = typeof mode === 'boolean' ? (mode ? 'on' : 'off') : mode;\n }\n\n // ---------------- Delegate Wrappers ----------------\n #renderVisibleRows(start: number, end: number, epoch = this.__rowRenderEpoch): void {\n // Use cached hook to avoid creating closures on every render (hot path optimization)\n if (!this.#renderRowHook) {\n this.#renderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number): boolean => {\n return this.#pluginManager?.renderRow(row, rowEl, rowIndex) ?? false;\n };\n }\n renderVisibleRows(this as unknown as InternalGrid<T>, start, end, epoch, this.#renderRowHook);\n }\n\n // Cache for ARIA counts to avoid redundant DOM writes on scroll (hot path)\n #lastAriaRowCount = -1;\n #lastAriaColCount = -1;\n\n /**\n * Updates ARIA row/col counts on the grid container.\n * Also sets role=\"rowgroup\" on .rows container only when there are rows.\n * Uses caching to avoid redundant DOM writes on every scroll frame.\n */\n #updateAriaCounts(rowCount: number, colCount: number): void {\n // Skip if nothing changed (hot path optimization for scroll)\n if (rowCount === this.#lastAriaRowCount && colCount === this.#lastAriaColCount) {\n return;\n }\n const prevRowCount = this.#lastAriaRowCount;\n this.#lastAriaRowCount = rowCount;\n this.#lastAriaColCount = colCount;\n\n // Update ARIA counts on inner grid element\n if (this.__rowsBodyEl) {\n this.__rowsBodyEl.setAttribute('aria-rowcount', String(rowCount));\n this.__rowsBodyEl.setAttribute('aria-colcount', String(colCount));\n }\n\n // Set role=\"rowgroup\" on .rows only when there are rows (ARIA compliance)\n if (rowCount !== prevRowCount && this._bodyEl) {\n if (rowCount > 0) {\n this._bodyEl.setAttribute('role', 'rowgroup');\n } else {\n this._bodyEl.removeAttribute('role');\n }\n }\n }\n\n // ---------------- Core Helpers ----------------\n /**\n * Request a full grid re-setup through the render scheduler.\n * This method queues all the config merging, column/row processing, and rendering\n * to happen in the next animation frame via the scheduler.\n *\n * Previously this method executed rendering synchronously, but that caused race\n * conditions with framework adapters that also schedule their own render work.\n */\n #setup(): void {\n if (!this.isConnected) return;\n if (!this._headerRowEl || !this._bodyEl) {\n return;\n }\n\n // Read light DOM column configuration (synchronous DOM read)\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n\n // Apply initial column state synchronously if present\n // (needs to happen before scheduler to avoid flash of unstyled content)\n if (this.#initialColumnState) {\n const state = this.#initialColumnState;\n this.#initialColumnState = undefined; // Clear to avoid re-applying\n // Temporarily merge config so applyState has columns to work with\n this.#configManager.merge();\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.applyState(state, plugins);\n }\n\n // Ensure legacy inline grid styles are cleared from container\n if (this._bodyEl) {\n this._bodyEl.style.display = '';\n this._bodyEl.style.gridTemplateColumns = '';\n }\n\n // Request full render through scheduler - batches with framework adapter work\n this.#scheduler.requestPhase(RenderPhase.FULL, 'setup');\n }\n\n #onScrollBatched(scrollTop: number): void {\n // Faux scrollbar pattern: content never scrolls, just update transforms\n // Old content stays visible until new transforms are applied\n this.refreshVirtualWindow(false);\n\n // Let plugins reapply visual state to recycled DOM elements\n this.#pluginManager?.onScrollRender();\n\n // Dispatch to plugins (using cached flag and pooled event object to avoid GC)\n if (this.#hasScrollPlugins) {\n const fauxScrollbar = this._virtualization.container;\n // Reuse pooled event object - update values in-place instead of allocating new object\n const scrollEvent = this.#pooledScrollEvent;\n scrollEvent.scrollTop = scrollTop;\n scrollEvent.scrollLeft = fauxScrollbar?.scrollLeft ?? 0;\n scrollEvent.scrollHeight = fauxScrollbar?.scrollHeight ?? 0;\n scrollEvent.scrollWidth = fauxScrollbar?.scrollWidth ?? 0;\n scrollEvent.clientHeight = fauxScrollbar?.clientHeight ?? 0;\n scrollEvent.clientWidth = fauxScrollbar?.clientWidth ?? 0;\n // Note: originalEvent removed to avoid allocation - plugins should not rely on it\n this.#pluginManager?.onScroll(scrollEvent);\n }\n }\n\n /**\n * Find the header row element in the shadow DOM.\n * Used by plugins that need to access header cells for styling or measurement.\n * @group DOM Access\n * @internal Plugin API\n */\n findHeaderRow(): HTMLElement {\n return this.#renderRoot.querySelector('.header-row') as HTMLElement;\n }\n\n /**\n * Find a rendered row element by its data row index.\n * Returns null if the row is not currently rendered (virtualized out of view).\n * Used by plugins that need to access specific row elements for styling or measurement.\n * @group DOM Access\n * @internal Plugin API\n * @param rowIndex - The data row index (not the DOM position)\n */\n findRenderedRowElement(rowIndex: number): HTMLElement | null {\n return (\n (Array.from(this._bodyEl.querySelectorAll('.data-grid-row')) as HTMLElement[]).find((r) => {\n const cell = r.querySelector('.cell[data-row]');\n return cell && Number(cell.getAttribute('data-row')) === rowIndex;\n }) || null\n );\n }\n\n /**\n * Dispatch a cell click event to the plugin system, then emit a public event.\n * Plugins get first chance to handle the event. After plugins process it,\n * a `cell-click` CustomEvent is dispatched for external listeners.\n *\n * @returns `true` if any plugin handled (consumed) the event, or if consumer canceled\n * @fires cell-activate - Unified activation event (cancelable) - fires FIRST\n * @fires cell-click - Emitted after plugins process the click, with full cell context\n */\n _dispatchCellClick(event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement): boolean {\n const row = this._rows[rowIndex];\n const col = this._columns[colIndex];\n if (!row || !col) return false;\n\n const field = col.field;\n const value = (row as Record<string, unknown>)[field];\n\n // Emit cell-activate FIRST (cancelable) - consumers can prevent plugin behavior\n const activateEvent = new CustomEvent('cell-activate', {\n cancelable: true,\n bubbles: true,\n composed: true,\n detail: {\n rowIndex,\n colIndex,\n field,\n value,\n row,\n cellEl,\n trigger: 'pointer' as const,\n originalEvent: event,\n },\n });\n this.dispatchEvent(activateEvent);\n\n // If consumer canceled, don't let plugins handle it\n if (activateEvent.defaultPrevented) {\n return true; // Treated as \"handled\"\n }\n\n const cellClickEvent: CellClickEvent = {\n row,\n rowIndex,\n colIndex,\n field,\n value,\n cellEl,\n originalEvent: event,\n };\n\n // Let plugins handle (editing, selection, etc.)\n const handled = this.#pluginManager?.onCellClick(cellClickEvent) ?? false;\n\n // Emit informational cell-click event for external listeners\n this.#emit('cell-click', cellClickEvent);\n\n return handled;\n }\n\n /**\n * Dispatch a row click event to the plugin system, then emit a public event.\n * Plugins get first chance to handle the event. After plugins process it,\n * a `row-click` CustomEvent is dispatched for external listeners.\n *\n * @returns `true` if any plugin handled (consumed) the event\n * @fires row-click - Emitted after plugins process the click, with full row context\n */\n _dispatchRowClick(event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement): boolean {\n if (!row) return false;\n\n const rowClickEvent: RowClickEvent = {\n rowIndex,\n row,\n rowEl,\n originalEvent: event,\n };\n\n // Let plugins handle first\n const handled = this.#pluginManager?.onRowClick(rowClickEvent) ?? false;\n\n // Emit public event for external listeners (reuse same event object)\n this.#emit('row-click', rowClickEvent);\n\n return handled;\n }\n\n /**\n * Dispatch a header click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchHeaderClick(event: MouseEvent, colIndex: number, headerEl: HTMLElement): boolean {\n const col = this._columns[colIndex];\n if (!col) return false;\n\n const headerClickEvent: HeaderClickEvent = {\n colIndex,\n field: col.field,\n column: col,\n headerEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onHeaderClick(headerClickEvent) ?? false;\n }\n\n /**\n * Dispatch a keyboard event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchKeyDown(event: KeyboardEvent): boolean {\n return this.#pluginManager?.onKeyDown(event) ?? false;\n }\n\n /**\n * Get horizontal scroll boundary offsets from plugins.\n * Used by keyboard navigation to ensure focused cells are fully visible\n * when plugins like pinned columns obscure part of the scroll area.\n */\n _getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n return this.#pluginManager?.getHorizontalScrollOffsets(rowEl, focusedCell) ?? { left: 0, right: 0 };\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n * @group Plugin Communication\n * @internal Plugin API\n *\n * @example\n * // Check if any plugin vetoes moving a column\n * const responses = grid.queryPlugins<boolean>({ type: PLUGIN_QUERIES.CAN_MOVE_COLUMN, context: column });\n * const canMove = !responses.includes(false);\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n return this.#pluginManager?.queryPlugins<T>(query) ?? [];\n }\n\n /**\n * Dispatch cell mouse events for drag operations.\n * Returns true if any plugin started a drag.\n * @group Event Dispatching\n * @internal Plugin API - called by event-delegation.ts\n */\n _dispatchCellMouseDown(event: CellMouseEvent): boolean {\n return this.#pluginManager?.onCellMouseDown(event) ?? false;\n }\n\n /**\n * Dispatch cell mouse move during drag.\n * @group Event Dispatching\n * @internal Plugin API - called by event-delegation.ts\n */\n _dispatchCellMouseMove(event: CellMouseEvent): void {\n this.#pluginManager?.onCellMouseMove(event);\n }\n\n /**\n * Dispatch cell mouse up to end drag.\n * @group Event Dispatching\n * @internal Plugin API - called by event-delegation.ts\n */\n _dispatchCellMouseUp(event: CellMouseEvent): void {\n this.#pluginManager?.onCellMouseUp(event);\n }\n\n /**\n * Wait for the grid to be ready.\n * Resolves once the component has finished initial setup, including\n * column inference, plugin initialization, and first render.\n *\n * @group Lifecycle\n * @returns Promise that resolves when the grid is ready\n *\n * @example\n * ```typescript\n * const grid = document.querySelector('tbw-grid');\n * await grid.ready();\n * console.log('Grid is ready, rows:', grid.rows.length);\n * ```\n */\n async ready(): Promise<void> {\n return this.#readyPromise;\n }\n\n /**\n * Force a full layout/render cycle.\n * Use this after programmatic changes that require re-measurement,\n * such as container resize or dynamic style changes.\n *\n * @group Lifecycle\n * @returns Promise that resolves when the render cycle completes\n *\n * @example\n * ```typescript\n * // After resizing the container\n * container.style.width = '800px';\n * await grid.forceLayout();\n * console.log('Grid re-rendered');\n * ```\n */\n async forceLayout(): Promise<void> {\n // Request a full render cycle through the scheduler\n this.#scheduler.requestPhase(RenderPhase.FULL, 'forceLayout');\n // Wait for the render cycle to complete\n return this.#scheduler.whenReady();\n }\n\n /**\n * Get a frozen snapshot of the current effective configuration.\n * The returned object is read-only and reflects all merged config sources.\n *\n * @group Configuration\n * @returns Promise resolving to frozen configuration object\n *\n * @example\n * ```typescript\n * const config = await grid.getConfig();\n * console.log('Columns:', config.columns?.length);\n * console.log('Fit mode:', config.fitMode);\n * ```\n */\n async getConfig(): Promise<Readonly<GridConfig<T>>> {\n return Object.freeze({ ...(this.#effectiveConfig || {}) });\n }\n\n // ---------------- Row Update API ----------------\n\n /**\n * Get the unique ID for a row.\n * Uses the configured `getRowId` function or falls back to `row.id` / `row._id`.\n *\n * @group Data Management\n * @param row - The row object\n * @returns The row's unique identifier\n * @throws Error if no ID can be determined\n *\n * @example\n * ```typescript\n * const id = grid.getRowId(row);\n * console.log('Row ID:', id);\n * ```\n */\n getRowId(row: T): string {\n return this.#resolveRowIdOrThrow(row, this.#effectiveConfig.getRowId);\n }\n\n /**\n * Get a row by its ID.\n * O(1) lookup via internal Map.\n *\n * @group Data Management\n * @param id - Row identifier (from getRowId)\n * @returns The row object, or undefined if not found\n *\n * @example\n * ```typescript\n * const row = grid.getRow('cargo-123');\n * if (row) {\n * console.log('Found:', row.name);\n * }\n * ```\n */\n getRow(id: string): T | undefined {\n return this.#rowIdMap.get(id)?.row;\n }\n\n /**\n * Update a row by ID.\n * Mutates the row in-place and emits `cell-change` for each changed field.\n *\n * @group Data Management\n * @param id - Row identifier (from getRowId)\n * @param changes - Partial row data to merge\n * @param source - Origin of update (default: 'api')\n * @throws Error if row is not found\n * @fires cell-change - For each field that changed\n *\n * @example\n * ```typescript\n * // WebSocket update handler\n * socket.on('cargo-update', (data) => {\n * grid.updateRow(data.id, { status: data.status, eta: data.eta });\n * });\n * ```\n */\n updateRow(id: string, changes: Partial<T>, source: UpdateSource = 'api'): void {\n const entry = this.#rowIdMap.get(id);\n if (!entry) {\n throw new Error(\n `[tbw-grid] Row with ID \"${id}\" not found. ` + `Ensure the row exists and getRowId is correctly configured.`,\n );\n }\n\n const { row, index } = entry;\n const changedFields: Array<{ field: string; oldValue: unknown; newValue: unknown }> = [];\n\n // Compute changes and apply in-place\n for (const [field, newValue] of Object.entries(changes)) {\n const oldValue = (row as Record<string, unknown>)[field];\n if (oldValue !== newValue) {\n changedFields.push({ field, oldValue, newValue });\n (row as Record<string, unknown>)[field] = newValue;\n }\n }\n\n // Emit cell-change for each changed field\n for (const { field, oldValue, newValue } of changedFields) {\n this.#emit('cell-change', {\n row,\n rowId: id,\n rowIndex: index,\n field,\n oldValue,\n newValue,\n changes,\n source,\n } as CellChangeDetail<T>);\n }\n\n // Schedule re-render if anything changed\n if (changedFields.length > 0) {\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'updateRow');\n }\n }\n\n /**\n * Batch update multiple rows.\n * More efficient than multiple `updateRow()` calls - single render cycle.\n *\n * @group Data Management\n * @param updates - Array of { id, changes } objects\n * @param source - Origin of updates (default: 'api')\n * @throws Error if any row is not found\n * @fires cell-change - For each field that changed on each row\n *\n * @example\n * ```typescript\n * // Bulk status update\n * grid.updateRows([\n * { id: 'cargo-1', changes: { status: 'shipped' } },\n * { id: 'cargo-2', changes: { status: 'shipped' } },\n * ]);\n * ```\n */\n updateRows(updates: Array<{ id: string; changes: Partial<T> }>, source: UpdateSource = 'api'): void {\n let anyChanged = false;\n\n for (const { id, changes } of updates) {\n const entry = this.#rowIdMap.get(id);\n if (!entry) {\n throw new Error(\n `[tbw-grid] Row with ID \"${id}\" not found. ` + `Ensure the row exists and getRowId is correctly configured.`,\n );\n }\n\n const { row, index } = entry;\n\n // Compute changes and apply in-place\n for (const [field, newValue] of Object.entries(changes)) {\n const oldValue = (row as Record<string, unknown>)[field];\n if (oldValue !== newValue) {\n anyChanged = true;\n (row as Record<string, unknown>)[field] = newValue;\n\n // Emit cell-change for each changed field\n this.#emit('cell-change', {\n row,\n rowId: id,\n rowIndex: index,\n field,\n oldValue,\n newValue,\n changes,\n source,\n } as CellChangeDetail<T>);\n }\n }\n }\n\n // Schedule single re-render for all changes\n if (anyChanged) {\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'updateRows');\n }\n }\n\n // ---------------- Column Visibility API ----------------\n // Delegates to ConfigManager\n\n /**\n * Show or hide a column by field name.\n *\n * @group Column Visibility\n * @param field - The field name of the column to modify\n * @param visible - Whether the column should be visible\n * @returns `true` if the visibility changed, `false` if unchanged\n * @fires column-state-change - Emitted when the visibility changes\n *\n * @example\n * ```typescript\n * // Hide the email column\n * grid.setColumnVisible('email', false);\n *\n * // Show it again\n * grid.setColumnVisible('email', true);\n * ```\n */\n setColumnVisible(field: string, visible: boolean): boolean {\n const result = this.#configManager.setColumnVisible(field, visible);\n if (result) {\n this.requestStateChange();\n }\n return result;\n }\n\n /**\n * Toggle a column's visibility.\n *\n * @group Column Visibility\n * @param field - The field name of the column to toggle\n * @returns The new visibility state (`true` = visible, `false` = hidden)\n * @fires column-state-change - Emitted when the visibility changes\n *\n * @example\n * ```typescript\n * // Toggle the notes column visibility\n * const isNowVisible = grid.toggleColumnVisibility('notes');\n * console.log(`Notes column is now ${isNowVisible ? 'visible' : 'hidden'}`);\n * ```\n */\n toggleColumnVisibility(field: string): boolean {\n const result = this.#configManager.toggleColumnVisibility(field);\n if (result) {\n this.requestStateChange();\n }\n return result;\n }\n\n /**\n * Check if a column is currently visible.\n *\n * @group Column Visibility\n * @param field - The field name to check\n * @returns `true` if the column is visible, `false` if hidden\n *\n * @example\n * ```typescript\n * if (grid.isColumnVisible('email')) {\n * console.log('Email column is showing');\n * }\n * ```\n */\n isColumnVisible(field: string): boolean {\n return this.#configManager.isColumnVisible(field);\n }\n\n /**\n * Show all columns, resetting any hidden columns to visible.\n *\n * @group Column Visibility\n * @fires column-state-change - Emitted when column visibility changes\n * @example\n * ```typescript\n * // Reset button handler\n * resetButton.onclick = () => grid.showAllColumns();\n * ```\n */\n showAllColumns(): void {\n this.#configManager.showAllColumns();\n this.requestStateChange();\n }\n\n /**\n * Get metadata for all columns including visibility state.\n * Useful for building a column picker UI.\n *\n * @group Column Visibility\n * @returns Array of column info objects\n *\n * @example\n * ```typescript\n * // Build a column visibility menu\n * const columns = grid.getAllColumns();\n * columns.forEach(col => {\n * if (!col.utility) { // Skip utility columns like selection checkbox\n * const menuItem = document.createElement('label');\n * menuItem.innerHTML = `\n * <input type=\"checkbox\" ${col.visible ? 'checked' : ''}>\n * ${col.header}\n * `;\n * menuItem.querySelector('input').onchange = () =>\n * grid.toggleColumnVisibility(col.field);\n * menu.appendChild(menuItem);\n * }\n * });\n * ```\n */\n getAllColumns(): Array<{\n field: string;\n header: string;\n visible: boolean;\n lockVisible?: boolean;\n utility?: boolean;\n }> {\n return this.#configManager.getAllColumns();\n }\n\n /**\n * Set the display order of columns.\n *\n * @group Column Order\n * @param order - Array of field names in desired order\n * @fires column-state-change - Emitted when column order changes\n *\n * @example\n * ```typescript\n * // Move 'status' column to first position\n * const currentOrder = grid.getColumnOrder();\n * const newOrder = ['status', ...currentOrder.filter(f => f !== 'status')];\n * grid.setColumnOrder(newOrder);\n * ```\n */\n setColumnOrder(order: string[]): void {\n this.#configManager.setColumnOrder(order);\n this.requestStateChange();\n }\n\n /**\n * Get the current column display order.\n *\n * @group Column Order\n * @returns Array of field names in display order\n *\n * @example\n * ```typescript\n * const order = grid.getColumnOrder();\n * console.log('Columns:', order.join(', '));\n * ```\n */\n getColumnOrder(): string[] {\n return this.#configManager.getColumnOrder();\n }\n\n // ---------------- Column State API ----------------\n\n /**\n * Get the current column state for persistence.\n * Returns a serializable object including column order, widths, visibility,\n * sort state, and any plugin-specific state.\n *\n * Use this to save user preferences to localStorage or a database.\n *\n * @group State Persistence\n * @returns Serializable column state object\n *\n * @example\n * ```typescript\n * // Save state to localStorage\n * const state = grid.getColumnState();\n * localStorage.setItem('gridState', JSON.stringify(state));\n *\n * // Later, restore the state\n * const saved = localStorage.getItem('gridState');\n * if (saved) {\n * grid.columnState = JSON.parse(saved);\n * }\n * ```\n */\n getColumnState(): GridColumnState {\n const plugins = this.#pluginManager?.getAll() ?? [];\n return this.#configManager.collectState(plugins as BaseGridPlugin[]);\n }\n\n /**\n * Set the column state, restoring all saved preferences.\n * Can be set before or after grid initialization.\n *\n * @group State Persistence\n * @fires column-state-change - Emitted after state is applied (if grid is initialized)\n * @example\n * ```typescript\n * // Restore saved state on page load\n * const grid = document.querySelector('tbw-grid');\n * const saved = localStorage.getItem('myGridState');\n * if (saved) {\n * grid.columnState = JSON.parse(saved);\n * }\n * ```\n */\n set columnState(state: GridColumnState | undefined) {\n if (!state) return;\n\n // Store for use after initialization if called before ready\n this.#initialColumnState = state;\n this.#configManager.initialColumnState = state;\n\n // If already initialized, apply immediately\n if (this.#initialized) {\n this.#applyColumnState(state);\n }\n }\n\n /**\n * Get the current column state.\n * Alias for `getColumnState()` for property-style access.\n * @group State Persistence\n */\n get columnState(): GridColumnState | undefined {\n return this.getColumnState();\n }\n\n /**\n * Apply column state internally.\n */\n #applyColumnState(state: GridColumnState): void {\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.applyState(state, plugins);\n\n // Re-setup to apply changes\n this.#setup();\n }\n\n /**\n * Request a state change event to be emitted.\n * Called internally after resize, reorder, visibility, or sort changes.\n * Plugins should call this after changing their state.\n * The event is debounced to avoid excessive events during drag operations.\n * @group State Persistence\n * @internal Plugin API\n */\n requestStateChange(): void {\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.requestStateChange(plugins);\n }\n\n /**\n * Reset column state to initial configuration.\n * Clears all user modifications including order, widths, visibility, and sort.\n *\n * @group State Persistence\n * @fires column-state-change - Emitted after state is reset\n * @example\n * ```typescript\n * // Reset button handler\n * resetBtn.onclick = () => {\n * grid.resetColumnState();\n * localStorage.removeItem('gridState');\n * };\n * ```\n */\n resetColumnState(): void {\n // Clear initial state\n this.#initialColumnState = undefined;\n this.__originalOrder = [];\n\n // Use ConfigManager to reset state\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.resetState(plugins);\n\n // Re-initialize columns from config\n this.#configManager.merge();\n this.#setup();\n }\n\n // ---------------- Shell / Tool Panel API ----------------\n // These methods delegate to ShellController for implementation.\n // The controller encapsulates all tool panel logic while grid.ts\n // ════════════════════════════════════════════════════════════════════════════\n // Tool Panel API\n // ════════════════════════════════════════════════════════════════════════════\n // The tool panel is a collapsible sidebar that contains accordion sections.\n // Each section can contain plugin UI (e.g., column visibility, filtering).\n\n /**\n * Check if the tool panel sidebar is currently open.\n *\n * The tool panel is an accordion-based sidebar that contains sections\n * registered by plugins or via `registerToolPanel()`.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Conditionally show/hide a \"toggle panel\" button\n * const isPanelOpen = grid.isToolPanelOpen;\n * toggleButton.textContent = isPanelOpen ? 'Close Panel' : 'Open Panel';\n * ```\n */\n get isToolPanelOpen(): boolean {\n return this.#shellController.isPanelOpen;\n }\n\n /**\n * Get the IDs of currently expanded accordion sections in the tool panel.\n *\n * Multiple sections can be expanded simultaneously in the accordion view.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Check which sections are expanded\n * const expanded = grid.expandedToolPanelSections;\n * console.log('Expanded sections:', expanded);\n * // e.g., ['columnVisibility', 'filtering']\n * ```\n */\n get expandedToolPanelSections(): string[] {\n return this.#shellController.expandedSections;\n }\n\n /**\n * Open the tool panel sidebar.\n *\n * The tool panel displays an accordion view with all registered panel sections.\n * Each section can be expanded/collapsed independently.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Open the tool panel when a toolbar button is clicked\n * settingsButton.addEventListener('click', () => {\n * grid.openToolPanel();\n * });\n * ```\n */\n openToolPanel(): void {\n this.#shellController.openToolPanel();\n }\n\n /**\n * Close the tool panel sidebar.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Close the panel after user makes a selection\n * grid.closeToolPanel();\n * ```\n */\n closeToolPanel(): void {\n this.#shellController.closeToolPanel();\n }\n\n /**\n * Toggle the tool panel sidebar open or closed.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Wire up a toggle button\n * toggleButton.addEventListener('click', () => {\n * grid.toggleToolPanel();\n * });\n * ```\n */\n toggleToolPanel(): void {\n this.#shellController.toggleToolPanel();\n }\n\n /**\n * Toggle an accordion section expanded or collapsed within the tool panel.\n *\n * @group Tool Panel\n * @param sectionId - The ID of the section to toggle (matches `ToolPanelDefinition.id`)\n *\n * @example\n * ```typescript\n * // Expand the column visibility section programmatically\n * grid.openToolPanel();\n * grid.toggleToolPanelSection('columnVisibility');\n * ```\n */\n toggleToolPanelSection(sectionId: string): void {\n this.#shellController.toggleToolPanelSection(sectionId);\n }\n\n /**\n * Get all registered tool panel definitions.\n *\n * Returns both plugin-registered panels and panels registered via `registerToolPanel()`.\n *\n * @group Tool Panel\n * @returns Array of tool panel definitions\n *\n * @example\n * ```typescript\n * // List all available panels\n * const panels = grid.getToolPanels();\n * panels.forEach(panel => {\n * console.log(`Panel: ${panel.title} (${panel.id})`);\n * });\n * ```\n */\n getToolPanels(): ToolPanelDefinition[] {\n return this.#shellController.getToolPanels();\n }\n\n /**\n * Register a custom tool panel section.\n *\n * Use this API to add custom UI sections to the tool panel sidebar\n * without creating a full plugin. The panel will appear as an accordion\n * section in the tool panel.\n *\n * @group Tool Panel\n * @param panel - The tool panel definition\n *\n * @example\n * ```typescript\n * // Register a custom \"Export\" panel\n * grid.registerToolPanel({\n * id: 'export',\n * title: 'Export Options',\n * icon: '📥',\n * order: 50, // Lower order = higher in list\n * render: (container) => {\n * container.innerHTML = `\n * <button id=\"export-csv\">Export CSV</button>\n * <button id=\"export-json\">Export JSON</button>\n * `;\n * container.querySelector('#export-csv')?.addEventListener('click', () => {\n * exportToCSV(grid.rows);\n * });\n * }\n * });\n * ```\n */\n registerToolPanel(panel: ToolPanelDefinition): void {\n this.#shellState.apiToolPanelIds.add(panel.id);\n this.#shellController.registerToolPanel(panel);\n }\n\n /**\n * Unregister a custom tool panel section.\n *\n * @group Tool Panel\n * @param panelId - The ID of the panel to remove\n *\n * @example\n * ```typescript\n * // Remove the export panel when no longer needed\n * grid.unregisterToolPanel('export');\n * ```\n */\n unregisterToolPanel(panelId: string): void {\n this.#shellState.apiToolPanelIds.delete(panelId);\n this.#shellController.unregisterToolPanel(panelId);\n }\n\n // ════════════════════════════════════════════════════════════════════════════\n // Header Content API\n // ════════════════════════════════════════════════════════════════════════════\n // Header content appears in the grid's header bar area (above the column headers).\n\n /**\n * Get all registered header content definitions.\n *\n * @group Header Content\n * @returns Array of header content definitions\n *\n * @example\n * ```typescript\n * const contents = grid.getHeaderContents();\n * console.log('Header sections:', contents.map(c => c.id));\n * ```\n */\n getHeaderContents(): HeaderContentDefinition[] {\n return this.#shellController.getHeaderContents();\n }\n\n /**\n * Register custom header content.\n *\n * Header content appears in the grid's header bar area, which is displayed\n * above the column headers. Use this for search boxes, filters, or other\n * controls that should be prominently visible.\n *\n * @group Header Content\n * @param content - The header content definition\n *\n * @example\n * ```typescript\n * // Add a global search box to the header\n * grid.registerHeaderContent({\n * id: 'global-search',\n * order: 10,\n * render: (container) => {\n * const input = document.createElement('input');\n * input.type = 'search';\n * input.placeholder = 'Search all columns...';\n * input.addEventListener('input', (e) => {\n * const term = (e.target as HTMLInputElement).value;\n * filterGrid(term);\n * });\n * container.appendChild(input);\n * }\n * });\n * ```\n */\n registerHeaderContent(content: HeaderContentDefinition): void {\n this.#shellController.registerHeaderContent(content);\n }\n\n /**\n * Unregister custom header content.\n *\n * @group Header Content\n * @param contentId - The ID of the content to remove\n *\n * @example\n * ```typescript\n * grid.unregisterHeaderContent('global-search');\n * ```\n */\n unregisterHeaderContent(contentId: string): void {\n this.#shellController.unregisterHeaderContent(contentId);\n }\n\n // ════════════════════════════════════════════════════════════════════════════\n // Toolbar Content API\n // ════════════════════════════════════════════════════════════════════════════\n // Toolbar content appears in the grid's toolbar area (typically below header,\n // above column headers). Use for action buttons, dropdowns, etc.\n\n /**\n * Get all registered toolbar content definitions.\n *\n * @group Toolbar\n * @returns Array of toolbar content definitions\n *\n * @example\n * ```typescript\n * const contents = grid.getToolbarContents();\n * console.log('Toolbar items:', contents.map(c => c.id));\n * ```\n */\n getToolbarContents(): ToolbarContentDefinition[] {\n return this.#shellController.getToolbarContents();\n }\n\n /**\n * Register custom toolbar content.\n *\n * Toolbar content appears in the grid's toolbar area. Use this for action\n * buttons, dropdowns, or other controls that should be easily accessible.\n * Content is rendered in order of the `order` property (lower = first).\n *\n * @group Toolbar\n * @param content - The toolbar content definition\n *\n * @example\n * ```typescript\n * // Add export buttons to the toolbar\n * grid.registerToolbarContent({\n * id: 'export-buttons',\n * order: 100, // Position in toolbar (lower = first)\n * render: (container) => {\n * const csvBtn = document.createElement('button');\n * csvBtn.textContent = 'Export CSV';\n * csvBtn.className = 'dg-toolbar-btn';\n * csvBtn.addEventListener('click', () => exportToCSV(grid.rows));\n *\n * const jsonBtn = document.createElement('button');\n * jsonBtn.textContent = 'Export JSON';\n * jsonBtn.className = 'dg-toolbar-btn';\n * jsonBtn.addEventListener('click', () => exportToJSON(grid.rows));\n *\n * container.append(csvBtn, jsonBtn);\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Add a dropdown filter to the toolbar\n * grid.registerToolbarContent({\n * id: 'status-filter',\n * order: 50,\n * render: (container) => {\n * const select = document.createElement('select');\n * select.innerHTML = `\n * <option value=\"\">All Statuses</option>\n * <option value=\"active\">Active</option>\n * <option value=\"inactive\">Inactive</option>\n * `;\n * select.addEventListener('change', (e) => {\n * const status = (e.target as HTMLSelectElement).value;\n * applyStatusFilter(status);\n * });\n * container.appendChild(select);\n * }\n * });\n * ```\n */\n registerToolbarContent(content: ToolbarContentDefinition): void {\n this.#shellController.registerToolbarContent(content);\n }\n\n /**\n * Unregister custom toolbar content.\n *\n * @group Toolbar\n * @param contentId - The ID of the content to remove\n *\n * @example\n * ```typescript\n * // Remove export buttons when switching to read-only mode\n * grid.unregisterToolbarContent('export-buttons');\n * ```\n */\n unregisterToolbarContent(contentId: string): void {\n this.#shellController.unregisterToolbarContent(contentId);\n }\n\n /** Pending shell refresh - used to batch multiple rapid calls */\n #pendingShellRefresh = false;\n\n /**\n * Re-parse light DOM shell elements and refresh shell header.\n * Call this after dynamically modifying <tbw-grid-header> children.\n *\n * Multiple calls are batched via microtask to avoid redundant DOM rebuilds\n * when registering multiple tool panels in sequence.\n *\n * @internal Plugin API\n */\n refreshShellHeader(): void {\n // Batch multiple rapid calls into a single microtask\n if (this.#pendingShellRefresh) return;\n this.#pendingShellRefresh = true;\n\n queueMicrotask(() => {\n this.#pendingShellRefresh = false;\n if (!this.isConnected) return;\n\n // Re-parse light DOM (header, tool buttons, and tool panels)\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Mark sources as changed since shell parsing may have updated state maps\n this.#configManager.markSourcesChanged();\n\n // Re-merge config to sync shell state changes into effectiveConfig.shell\n this.#configManager.merge();\n\n // Re-render the entire grid (shell structure may change)\n this.#render();\n this.#injectAllPluginStyles(); // Re-inject after render clears shadow DOM\n\n // Use lighter-weight post-render setup instead of full #afterConnect()\n // This avoids requesting another FULL render since #render() already rebuilt the DOM\n this.#afterShellRefresh();\n });\n }\n\n /**\n * Lighter-weight post-render setup after shell refresh.\n * Unlike #afterConnect(), this skips scheduler FULL request since DOM is already built.\n */\n #afterShellRefresh(): void {\n // Shell changes the DOM structure - need to re-cache element references\n const gridContent = this.#renderRoot.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#renderRoot.querySelector('.tbw-grid-root');\n\n this._headerRowEl = gridRoot?.querySelector('.header-row') as HTMLElement;\n this._virtualization.totalHeightEl = gridRoot?.querySelector('.faux-vscroll-spacer') as HTMLElement;\n this._virtualization.viewportEl = gridRoot?.querySelector('.rows-viewport') as HTMLElement;\n this._bodyEl = gridRoot?.querySelector('.rows') as HTMLElement;\n this.__rowsBodyEl = gridRoot?.querySelector('.rows-body') as HTMLElement;\n\n // Render shell header content and custom toolbar contents\n if (this.#shellController.isInitialized) {\n renderHeaderContent(this.#renderRoot, this.#shellState);\n renderCustomToolbarContents(this.#renderRoot, this.#effectiveConfig?.shell, this.#shellState);\n\n const defaultOpen = this.#effectiveConfig?.shell?.toolPanel?.defaultOpen;\n if (defaultOpen && this.#shellState.toolPanels.has(defaultOpen)) {\n this.openToolPanel();\n this.#shellState.expandedSections.add(defaultOpen);\n }\n }\n\n // Re-create resize controller (DOM elements changed)\n this._resizeController = createResizeController(this as unknown as InternalGrid<T>);\n\n // Re-setup scroll listeners (DOM elements changed)\n this.#setupScrollListeners(gridRoot);\n\n // Request only VIRTUALIZATION phase to recalculate rows - not FULL\n // The DOM is already rebuilt by #render(), we just need to update virtualization\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'shellRefresh');\n }\n\n // #region Custom Styles API\n /** Map of registered custom stylesheets by ID - uses adoptedStyleSheets which survive DOM rebuilds */\n #customStyleSheets = new Map<string, CSSStyleSheet>();\n\n /**\n * Register custom CSS styles to be injected into the grid's shadow DOM.\n * Use this to style custom cell renderers, editors, or detail panels.\n *\n * Uses adoptedStyleSheets for efficiency - styles survive shadow DOM rebuilds.\n *\n * @group Custom Styles\n * @param id - Unique identifier for the style block (for removal/updates)\n * @param css - CSS string to inject\n *\n * @example\n * ```typescript\n * // Register custom styles for a detail panel\n * grid.registerStyles('my-detail-styles', `\n * .my-detail-panel { padding: 16px; }\n * .my-detail-table { width: 100%; }\n * `);\n *\n * // Update styles later\n * grid.registerStyles('my-detail-styles', updatedCss);\n *\n * // Remove styles\n * grid.unregisterStyles('my-detail-styles');\n * ```\n */\n registerStyles(id: string, css: string): void {\n // Create or update the stylesheet\n let sheet = this.#customStyleSheets.get(id);\n if (!sheet) {\n sheet = new CSSStyleSheet();\n this.#customStyleSheets.set(id, sheet);\n }\n sheet.replaceSync(css);\n\n // Update adoptedStyleSheets to include all custom sheets\n this.#updateAdoptedStyleSheets();\n }\n\n /**\n * Remove previously registered custom styles.\n *\n * @group Custom Styles\n * @param id - The ID used when registering the styles\n */\n unregisterStyles(id: string): void {\n if (this.#customStyleSheets.delete(id)) {\n this.#updateAdoptedStyleSheets();\n }\n }\n\n /**\n * Get list of registered custom style IDs.\n *\n * @group Custom Styles\n */\n getRegisteredStyles(): string[] {\n return Array.from(this.#customStyleSheets.keys());\n }\n\n /**\n * Update document.adoptedStyleSheets to include custom sheets.\n * Without Shadow DOM, all custom styles go into the document.\n */\n #updateAdoptedStyleSheets(): void {\n const customSheets = Array.from(this.#customStyleSheets.values());\n\n // Start with document's existing sheets (excluding any we've added before)\n // We track custom sheets by their presence in our map\n const existingSheets = document.adoptedStyleSheets.filter(\n (sheet) => !Array.from(this.#customStyleSheets.values()).includes(sheet),\n );\n\n document.adoptedStyleSheets = [...existingSheets, ...customSheets];\n }\n // #endregion\n\n /**\n * Set up Light DOM handlers via ConfigManager's observer infrastructure.\n * This handles frameworks like Angular that project content asynchronously.\n *\n * The observer is owned by ConfigManager (generic infrastructure).\n * The handlers (parsing logic) are owned here (eventually ShellPlugin).\n *\n * This separation allows plugins to register their own Light DOM elements\n * and handle parsing themselves.\n */\n #setupLightDomHandlers(): void {\n // Handler for shell header element changes\n const handleShellChange = () => {\n const hadTitle = this.#shellState.lightDomTitle;\n const hadToolButtons = this.#shellState.hasToolButtonsContainer;\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n const hasTitle = this.#shellState.lightDomTitle;\n const hasToolButtons = this.#shellState.hasToolButtonsContainer;\n\n if ((hasTitle && !hadTitle) || (hasToolButtons && !hadToolButtons)) {\n this.#configManager.markSourcesChanged();\n this.#configManager.merge();\n const shellHeader = this.#renderRoot.querySelector('.tbw-shell-header');\n if (shellHeader) {\n const newHeaderHtml = renderShellHeader(\n this.#effectiveConfig.shell,\n this.#shellState,\n this.#effectiveConfig.icons?.toolPanel,\n );\n const temp = document.createElement('div');\n temp.innerHTML = newHeaderHtml;\n const newHeader = temp.firstElementChild;\n if (newHeader) {\n shellHeader.replaceWith(newHeader);\n this.#setupShellListeners();\n }\n }\n }\n };\n\n // Handler for column element changes\n const handleColumnChange = () => {\n this.__lightDomColumnsCache = undefined;\n this.#setup();\n };\n\n // Register handlers with ConfigManager\n // Shell-related elements (eventually these move to ShellPlugin)\n this.#configManager.registerLightDomHandler('tbw-grid-header', handleShellChange);\n this.#configManager.registerLightDomHandler('tbw-grid-tool-buttons', handleShellChange);\n this.#configManager.registerLightDomHandler('tbw-grid-tool-panel', handleShellChange);\n\n // Column elements (core grid functionality)\n this.#configManager.registerLightDomHandler('tbw-grid-column', handleColumnChange);\n this.#configManager.registerLightDomHandler('tbw-grid-detail', handleColumnChange);\n\n // Start observing\n this.#configManager.observeLightDOM(this as unknown as HTMLElement);\n }\n\n /**\n * Re-parse light DOM column elements and refresh the grid.\n * Call this after framework adapters have registered their templates.\n * Uses the render scheduler to batch with other pending updates.\n * @category Framework Adapters\n */\n refreshColumns(): void {\n // Clear the column cache to force re-parsing\n this.__lightDomColumnsCache = undefined;\n\n // Invalidate cell cache to reset __hasSpecialColumns flag\n // This is critical for frameworks like React where renderers are registered asynchronously\n // after the initial render (which may have cached __hasSpecialColumns = false)\n invalidateCellCache(this);\n\n // Re-parse light DOM columns SYNCHRONOUSLY to pick up newly registered framework renderers\n // This must happen before the scheduler runs processColumns\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n\n // Re-parse light DOM shell elements (may have been rendered asynchronously by frameworks)\n const hadTitle = this.#shellState.lightDomTitle;\n const hadToolButtons = this.#shellState.hasToolButtonsContainer;\n parseLightDomShell(this, this.#shellState);\n // Also parse tool buttons container that is a direct child (React/Vue pattern)\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n const hasTitle = this.#shellState.lightDomTitle;\n const hasToolButtons = this.#shellState.hasToolButtonsContainer;\n\n // If title or tool buttons were added via light DOM, update the shell header in place\n // The shell may already be rendered (due to plugins/panels), but without the title\n const needsShellRefresh = (hasTitle && !hadTitle) || (hasToolButtons && !hadToolButtons);\n if (needsShellRefresh) {\n // Mark sources as changed since shell parsing may have updated state maps\n this.#configManager.markSourcesChanged();\n // Merge the new title into effectiveConfig\n this.#configManager.merge();\n // Update the existing shell header element with new HTML\n const shellHeader = this.#renderRoot.querySelector('.tbw-shell-header');\n if (shellHeader) {\n const newHeaderHtml = renderShellHeader(\n this.#effectiveConfig.shell,\n this.#shellState,\n this.#effectiveConfig.icons?.toolPanel,\n );\n // Create a temporary container and extract the new header\n const temp = document.createElement('div');\n temp.innerHTML = newHeaderHtml;\n const newHeader = temp.firstElementChild;\n if (newHeader) {\n shellHeader.replaceWith(newHeader);\n // Re-attach event listeners to the new toolbar element\n this.#setupShellListeners();\n }\n }\n }\n\n // Request a COLUMNS phase render through the scheduler\n // This batches with any other pending work (e.g., afterConnect)\n this.#scheduler.requestPhase(RenderPhase.COLUMNS, 'refreshColumns');\n }\n\n // ---------------- Virtual Window ----------------\n /**\n * Calculate total height for the faux scrollbar spacer element.\n * Used by both bypass and virtualized rendering paths to ensure consistent scroll behavior.\n */\n #calculateTotalSpacerHeight(totalRows: number): number {\n const rowHeight = this._virtualization.rowHeight;\n const fauxScrollbar = this._virtualization.container ?? this;\n const viewportEl = this._virtualization.viewportEl ?? fauxScrollbar;\n const fauxScrollHeight = fauxScrollbar.clientHeight;\n const viewportHeight = viewportEl.clientHeight;\n\n // Get scroll-area height (may differ from faux when h-scrollbar present)\n const shadowRoot = (this as unknown as Element).shadowRoot;\n const scrollAreaEl = shadowRoot?.querySelector('.tbw-scroll-area');\n const scrollAreaHeight = scrollAreaEl ? (scrollAreaEl as HTMLElement).clientHeight : fauxScrollHeight;\n\n // Use scroll-area height as reference since it contains the actual content\n const containerHeight = scrollAreaHeight;\n const viewportHeightDiff = containerHeight - viewportHeight;\n\n // Add extra height from plugins (e.g., expanded master-detail rows)\n const pluginExtraHeight = this.#pluginManager?.getExtraHeight() ?? 0;\n\n // Horizontal scrollbar compensation: When a horizontal scrollbar appears inside scroll-area,\n // the faux scrollbar (sibling) is taller than scroll-area. Add the difference as padding.\n const hScrollbarPadding = Math.max(0, fauxScrollHeight - scrollAreaHeight);\n\n return totalRows * rowHeight + viewportHeightDiff + pluginExtraHeight + hScrollbarPadding;\n }\n\n /**\n * Core virtualization routine. Chooses between bypass (small datasets), grouped window rendering,\n * or standard row window rendering.\n * @param force - Whether to force a full refresh (not just scroll update)\n * @param skipAfterRender - When true, skip calling afterRender (used by scheduler which calls it separately)\n * @internal Plugin API\n */\n refreshVirtualWindow(force = false, skipAfterRender = false): void {\n if (!this._bodyEl) return;\n\n const totalRows = this._rows.length;\n\n if (!this._virtualization.enabled) {\n this.#renderVisibleRows(0, totalRows);\n if (!skipAfterRender) {\n this.#pluginManager?.afterRender();\n }\n return;\n }\n\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n this._virtualization.start = 0;\n this._virtualization.end = totalRows;\n // Only reset transform on force refresh (initial render, data change)\n // Don't reset on scroll-triggered updates - the scroll handler manages transforms\n if (force) {\n this._bodyEl.style.transform = 'translateY(0px)';\n }\n this.#renderVisibleRows(0, totalRows, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${this.#calculateTotalSpacerHeight(totalRows)}px`;\n }\n // Update ARIA counts on the grid container\n this.#updateAriaCounts(totalRows, this._visibleColumns.length);\n if (!skipAfterRender) {\n this.#pluginManager?.afterRender();\n }\n return;\n }\n\n // --- Normal virtualization path with faux scrollbar pattern ---\n // Faux scrollbar provides scrollTop, viewport provides visible height\n const fauxScrollbar = this._virtualization.container ?? this;\n const viewportEl = this._virtualization.viewportEl ?? fauxScrollbar;\n const viewportHeight = viewportEl.clientHeight;\n const rowHeight = this._virtualization.rowHeight;\n const scrollTop = fauxScrollbar.scrollTop;\n\n // When plugins add extra height (e.g., expanded details), the scroll position\n // includes that extra height. We need to find the actual row at scrollTop\n // by iteratively accounting for cumulative extra heights.\n // This prevents jumping when scrolling past expanded content.\n let start = Math.floor(scrollTop / rowHeight);\n\n // Iteratively refine: the initial guess may be too high because scrollTop\n // includes extra heights from expanded rows before it. Adjust downward.\n let iterations = 0;\n const maxIterations = 10; // Prevent infinite loop\n while (iterations < maxIterations) {\n const extraHeightBefore = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const adjustedStart = Math.floor((scrollTop - extraHeightBefore) / rowHeight);\n if (adjustedStart >= start || adjustedStart < 0) break;\n start = adjustedStart;\n iterations++;\n }\n\n // Faux scrollbar pattern: calculate effective position for this start\n // With translateY(0), the first rendered row appears at viewport top\n // Round down to even number so DOM nth-child(even) always matches data row parity\n // This prevents zebra stripe flickering during scroll since rows shift in pairs\n start = start - (start % 2);\n if (start < 0) start = 0;\n\n // Allow plugins to extend the start index backwards\n // (e.g., to keep expanded detail rows visible as they scroll out)\n const pluginAdjustedStart = this.#pluginManager?.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginAdjustedStart !== undefined && pluginAdjustedStart < start) {\n start = pluginAdjustedStart;\n // Re-apply even alignment after plugin adjustment\n start = start - (start % 2);\n if (start < 0) start = 0;\n }\n\n // Faux pattern buffer: render 2 extra rows below for smooth edge transition\n // This is smaller than traditional overscan since sub-pixel offset handles smoothness\n // +1 extra to account for the even-alignment above potentially showing 1 more row at top\n const visibleCount = Math.ceil(viewportHeight / rowHeight) + 3;\n let end = start + visibleCount;\n if (end > totalRows) end = totalRows;\n\n this._virtualization.start = start;\n this._virtualization.end = end;\n\n // Height spacer for scrollbar\n // The faux-vscroll is a sibling of .tbw-scroll-area, so it doesn't shrink when\n // elements inside scroll-area (header, column groups, footer, hScrollbar) take vertical space.\n // viewportHeightDiff captures ALL these differences - no extra buffer needed.\n const fauxScrollHeight = fauxScrollbar.clientHeight;\n\n // Guard: Skip height calculation if faux scrollbar has no height but viewport does\n // This indicates stale DOM references during recreation (e.g., shell toggle)\n // When both are 0 (test environment or not in DOM), proceed normally\n if (fauxScrollHeight === 0 && viewportHeight > 0) {\n // Stale refs detected, schedule retry through the scheduler\n // Using scheduler ensures this batches with other pending work\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'stale-refs-retry');\n return;\n }\n\n const totalHeight = this.#calculateTotalSpacerHeight(totalRows);\n\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${totalHeight}px`;\n }\n\n // Smooth scroll: apply offset for fluid motion\n // Since start is even-aligned, offset is distance from that aligned position\n // This creates smooth sliding while preserving zebra stripe parity\n // Account for extra heights (expanded details) before the start row\n const extraHeightBeforeStart = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const subPixelOffset = -(scrollTop - start * rowHeight - extraHeightBeforeStart);\n this._bodyEl.style.transform = `translateY(${subPixelOffset}px)`;\n\n this.#renderVisibleRows(start, end, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n\n // Update ARIA counts on the grid container\n this.#updateAriaCounts(totalRows, this._visibleColumns.length);\n\n // Only run plugin afterRender hooks on force refresh (structural changes)\n // Skip on scroll-triggered renders for maximum performance\n if (force && !skipAfterRender) {\n this.#pluginManager?.afterRender();\n\n // After plugins modify the DOM (e.g., add footer, column groups),\n // heights may have changed. Recalculate spacer height in a microtask\n // to catch these changes before the next paint.\n queueMicrotask(() => {\n const newFauxHeight = fauxScrollbar.clientHeight;\n const newViewportHeight = viewportEl.clientHeight;\n // Skip if faux scrollbar is stale (0 height but viewport has height)\n if (newFauxHeight === 0 && newViewportHeight > 0) return;\n\n // Recalculate using the shared helper\n const newTotalHeight = this.#calculateTotalSpacerHeight(totalRows);\n\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${newTotalHeight}px`;\n }\n });\n }\n }\n\n // ---------------- Render ----------------\n #render(): void {\n // Parse light DOM shell elements before rendering\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Mark sources as changed since shell parsing may have updated state maps\n this.#configManager.markSourcesChanged();\n\n // Re-merge config to pick up any newly parsed light DOM shell settings\n this.#configManager.merge();\n\n const shellConfig = this.#effectiveConfig?.shell;\n\n // Render using direct DOM construction (2-3x faster than innerHTML)\n // Pass only minimal runtime state (isPanelOpen, expandedSections) - config comes from effectiveConfig.shell\n const hasShell = buildGridDOMIntoElement(\n this.#renderRoot,\n shellConfig,\n { isPanelOpen: this.#shellState.isPanelOpen, expandedSections: this.#shellState.expandedSections },\n this.#effectiveConfig?.icons,\n );\n\n if (hasShell) {\n this.#setupShellListeners();\n this.#shellController.setInitialized(true);\n }\n }\n\n /**\n * Set up shell event listeners after render.\n */\n #setupShellListeners(): void {\n setupShellEventListeners(this.#renderRoot, this.#effectiveConfig?.shell, this.#shellState, {\n onPanelToggle: () => this.toggleToolPanel(),\n onSectionToggle: (sectionId: string) => this.toggleToolPanelSection(sectionId),\n });\n\n // Set up tool panel resize\n this.#resizeCleanup?.();\n this.#resizeCleanup = setupToolPanelResize(this.#renderRoot, this.#effectiveConfig?.shell, (width: number) => {\n // Update the CSS variable to persist the new width\n this.style.setProperty('--tbw-tool-panel-width', `${width}px`);\n });\n }\n}\n\n// Self-registering custom element\nif (!customElements.get(DataGridElement.tagName)) {\n customElements.define(DataGridElement.tagName, DataGridElement);\n}\n\n// Make DataGridElement accessible globally for framework adapters\n(globalThis as unknown as { DataGridElement: typeof DataGridElement }).DataGridElement = DataGridElement;\n\n// Type augmentation for querySelector/createElement\ndeclare global {\n interface HTMLElementTagNameMap {\n 'tbw-grid': DataGridElement;\n }\n}\n","/**\n * Shared types for the plugin system.\n *\n * These types are used by both the base plugin class and the grid core.\n * Centralizing them here avoids circular imports and reduces duplication.\n */\n\nimport type { ColumnConfig, GridConfig, ToolPanelDefinition, UpdateSource } from '../types';\n\n/**\n * Keyboard modifier flags\n */\nexport interface KeyboardModifiers {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n}\n\n/**\n * Cell coordinates\n */\nexport interface CellCoords {\n row: number;\n col: number;\n}\n\n/**\n * Cell click event\n */\nexport interface CellClickEvent {\n rowIndex: number;\n colIndex: number;\n field: string;\n value: unknown;\n row: unknown;\n cellEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Row click event\n */\nexport interface RowClickEvent {\n rowIndex: number;\n row: unknown;\n rowEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Header click event\n */\nexport interface HeaderClickEvent {\n colIndex: number;\n field: string;\n column: ColumnConfig;\n headerEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Scroll event\n */\nexport interface ScrollEvent {\n scrollTop: number;\n scrollLeft: number;\n scrollHeight: number;\n scrollWidth: number;\n clientHeight: number;\n clientWidth: number;\n originalEvent?: Event;\n}\n\n/**\n * Cell mouse event (for drag operations, selection, etc.)\n */\nexport interface CellMouseEvent {\n /** Event type: mousedown, mousemove, or mouseup */\n type: 'mousedown' | 'mousemove' | 'mouseup';\n /** Row index, undefined if not over a data cell */\n rowIndex?: number;\n /** Column index, undefined if not over a cell */\n colIndex?: number;\n /** Field name, undefined if not over a cell */\n field?: string;\n /** Cell value, undefined if not over a data cell */\n value?: unknown;\n /** Row data object, undefined if not over a data row */\n row?: unknown;\n /** Column configuration, undefined if not over a column */\n column?: ColumnConfig;\n /** The cell element, undefined if not over a cell */\n cellElement?: HTMLElement;\n /** The row element, undefined if not over a row */\n rowElement?: HTMLElement;\n /** Whether the event is over a header cell */\n isHeader: boolean;\n /** Cell coordinates if over a valid data cell */\n cell?: CellCoords;\n /** The original mouse event */\n originalEvent: MouseEvent;\n}\n\n/**\n * Context menu parameters\n */\nexport interface ContextMenuParams {\n x: number;\n y: number;\n rowIndex?: number;\n colIndex?: number;\n field?: string;\n value?: unknown;\n row?: unknown;\n column?: ColumnConfig;\n isHeader?: boolean;\n}\n\n/**\n * Context menu item (used by context-menu plugin query)\n */\nexport interface ContextMenuItem {\n id: string;\n label: string;\n icon?: string;\n disabled?: boolean;\n separator?: boolean;\n children?: ContextMenuItem[];\n action?: (params: ContextMenuParams) => void;\n}\n\n/**\n * Generic plugin query for inter-plugin communication.\n * Plugins can define their own query types as string constants\n * and respond to queries from other plugins.\n */\nexport interface PluginQuery<T = unknown> {\n /** Query type identifier (e.g., 'canMoveColumn', 'getContextMenuItems') */\n type: string;\n /** Query-specific context/parameters */\n context: T;\n}\n\n/**\n * Well-known plugin query types.\n * Plugins can define additional query types beyond these.\n */\nexport const PLUGIN_QUERIES = {\n /** Ask if a column can be moved. Context: ColumnConfig. Response: boolean | undefined */\n CAN_MOVE_COLUMN: 'canMoveColumn',\n /** Get context menu items. Context: ContextMenuParams. Response: ContextMenuItem[] */\n GET_CONTEXT_MENU_ITEMS: 'getContextMenuItems',\n} as const;\n\n/**\n * Cell render context for plugin cell renderers.\n * Provides full context including position and editing state.\n */\nexport interface PluginCellRenderContext {\n /** The cell value */\n value: unknown;\n /** The row data object */\n row: unknown;\n /** The row index in the data array */\n rowIndex: number;\n /** The column index */\n colIndex: number;\n /** The field name */\n field: string;\n /** The column configuration */\n column: ColumnConfig;\n /** Whether the cell is being edited */\n isEditing: boolean;\n}\n\n/**\n * Cell renderer function type for plugins.\n */\nexport type CellRenderer = (ctx: PluginCellRenderContext) => string | HTMLElement;\n\n/**\n * Header renderer function type for plugins.\n */\nexport type HeaderRenderer = (ctx: { column: ColumnConfig; colIndex: number }) => string | HTMLElement;\n\n/**\n * Cell editor interface for plugins.\n */\nexport interface CellEditor {\n create(ctx: PluginCellRenderContext, commitFn: (value: unknown) => void, cancelFn: () => void): HTMLElement;\n getValue?(element: HTMLElement): unknown;\n focus?(element: HTMLElement): void;\n}\n\n/**\n * Minimal grid interface for plugins.\n * This avoids circular imports with the full DataGridElement.\n *\n * Member prefixes indicate accessibility:\n * - `_underscore` = protected members accessible to plugins (marked @internal in full interface)\n */\nexport interface GridElementRef {\n // =========================================================================\n // HTMLElement-like Properties (avoid casting to HTMLElement)\n // =========================================================================\n\n /** The grid's shadow root. */\n shadowRoot: ShadowRoot | null;\n /** Grid element width in pixels. */\n readonly clientWidth: number;\n /** Grid element height in pixels. */\n readonly clientHeight: number;\n /** Add an event listener to the grid element. */\n addEventListener<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => unknown,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n /** Remove an event listener from the grid element. */\n removeEventListener<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => unknown,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n /** Set an attribute on the grid element. */\n setAttribute(name: string, value: string): void;\n /** Get an attribute from the grid element. */\n getAttribute(name: string): string | null;\n /** Remove an attribute from the grid element. */\n removeAttribute(name: string): void;\n\n // =========================================================================\n // Grid Data & Configuration\n // =========================================================================\n\n /** Current rows (after plugin processing like grouping, filtering). */\n rows: unknown[];\n /** Original unfiltered/unprocessed rows. */\n sourceRows: unknown[];\n /** Column configurations. */\n columns: ColumnConfig[];\n /** Visible columns only (excludes hidden). Use for rendering. @internal */\n _visibleColumns: ColumnConfig[];\n /** Full grid configuration. */\n gridConfig: GridConfig;\n /** Effective (merged) configuration - the single source of truth. */\n effectiveConfig: GridConfig;\n\n // =========================================================================\n // Row Update API\n // =========================================================================\n\n /**\n * Get the unique ID for a row.\n * Uses configured `getRowId` function or falls back to `row.id` / `row._id`.\n * @throws Error if no ID can be determined\n */\n getRowId(row: unknown): string;\n\n /**\n * Get a row by its ID.\n * O(1) lookup via internal Map.\n * @returns The row object, or undefined if not found\n */\n getRow(id: string): unknown | undefined;\n\n /**\n * Update a row by ID.\n * Mutates the row in-place and emits `cell-change` for each changed field.\n * @param id - Row identifier (from getRowId)\n * @param changes - Partial row data to merge\n * @param source - Origin of update (default: 'api')\n * @throws Error if row is not found\n */\n updateRow(id: string, changes: Record<string, unknown>, source?: UpdateSource): void;\n\n /**\n * Batch update multiple rows.\n * More efficient than multiple `updateRow()` calls - single render cycle.\n * @param updates - Array of { id, changes } objects\n * @param source - Origin of updates (default: 'api')\n * @throws Error if any row is not found\n */\n updateRows(updates: Array<{ id: string; changes: Record<string, unknown> }>, source?: UpdateSource): void;\n\n // =========================================================================\n // Focus & Lifecycle\n // =========================================================================\n\n /** Current focused row index. @internal */\n _focusRow: number;\n /** Current focused column index. @internal */\n _focusCol: number;\n /** AbortSignal that is aborted when the grid disconnects from the DOM. */\n disconnectSignal: AbortSignal;\n\n // =========================================================================\n // Rendering\n // =========================================================================\n\n /** Request a full re-render of the grid. */\n requestRender(): void;\n /** Request a full re-render and restore focus styling afterward. */\n requestRenderWithFocus(): void;\n /** Request a lightweight style update without rebuilding DOM. */\n requestAfterRender(): void;\n /** Force a layout pass. */\n forceLayout(): Promise<void>;\n /** Dispatch an event from the grid element. */\n dispatchEvent(event: Event): boolean;\n\n // =========================================================================\n // Inter-plugin Communication\n // =========================================================================\n\n /**\n * Query all plugins with a generic query and collect responses.\n * Used for inter-plugin communication (e.g., asking PinnedColumnsPlugin\n * if a column can be moved).\n *\n * @example\n * const responses = grid.queryPlugins<boolean>({\n * type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n * context: column\n * });\n * const canMove = !responses.includes(false);\n */\n queryPlugins<T>(query: PluginQuery): T[];\n\n // =========================================================================\n // DOM Access\n // =========================================================================\n\n /**\n * Find the rendered DOM element for a row by its data index.\n * Returns null if the row is not currently rendered (virtualized out).\n */\n findRenderedRowElement(rowIndex: number): HTMLElement | null;\n\n // =========================================================================\n // Column Visibility API\n // =========================================================================\n\n /**\n * Get all columns including hidden ones.\n * Returns field, header, visibility status, lock state, and utility flag.\n */\n getAllColumns(): Array<{\n field: string;\n header: string;\n visible: boolean;\n lockVisible?: boolean;\n utility?: boolean;\n }>;\n\n /**\n * Set visibility for a specific column.\n * @returns true if state changed, false if column not found or already in state\n */\n setColumnVisible(field: string, visible: boolean): boolean;\n\n /**\n * Toggle visibility for a specific column.\n * @returns true if state changed, false if column not found\n */\n toggleColumnVisibility(field: string): boolean;\n\n /**\n * Check if a column is currently visible.\n */\n isColumnVisible(field: string): boolean;\n\n /**\n * Show all hidden columns.\n */\n showAllColumns(): void;\n\n // =========================================================================\n // Column Order API\n // =========================================================================\n\n /**\n * Get the current column display order as array of field names.\n */\n getColumnOrder(): string[];\n\n /**\n * Set the column display order.\n * @param order Array of field names in desired order\n */\n setColumnOrder(order: string[]): void;\n\n /**\n * Request emission of column-state-change event (debounced).\n * Call after programmatic column changes that should notify consumers.\n */\n requestStateChange?(): void;\n\n // =========================================================================\n // Tool Panel API (Shell Integration)\n // =========================================================================\n\n /**\n * Whether the tool panel sidebar is currently open.\n */\n readonly isToolPanelOpen: boolean;\n\n /**\n * Get the IDs of expanded accordion sections.\n */\n readonly expandedToolPanelSections: string[];\n\n /**\n * Open the tool panel sidebar (accordion view with all registered panels).\n */\n openToolPanel(): void;\n\n /**\n * Close the tool panel sidebar.\n */\n closeToolPanel(): void;\n\n /**\n * Toggle the tool panel sidebar open/closed.\n */\n toggleToolPanel(): void;\n\n /**\n * Toggle a specific accordion section expanded/collapsed.\n * @param sectionId - The panel ID to toggle (matches ToolPanelDefinition.id)\n */\n toggleToolPanelSection(sectionId: string): void;\n\n /**\n * Get registered tool panel definitions.\n */\n getToolPanels(): ToolPanelDefinition[];\n}\n","/**\n * Base Grid Plugin Class\n *\n * All plugins extend this abstract class.\n * Plugins are instantiated per-grid and manage their own state.\n */\n\n// Injected by Vite at build time from package.json (same as grid.ts)\ndeclare const __GRID_VERSION__: string;\n\nimport type {\n ColumnConfig,\n ColumnState,\n GridPlugin,\n HeaderContentDefinition,\n IconValue,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\n\n// Re-export shared plugin types for convenience\nexport { PLUGIN_QUERIES } from './types';\nexport type {\n CellClickEvent,\n CellCoords,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n ContextMenuItem,\n ContextMenuParams,\n GridElementRef,\n HeaderClickEvent,\n HeaderRenderer,\n KeyboardModifiers,\n PluginCellRenderContext,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './types';\n\nimport type {\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n GridElementRef,\n HeaderClickEvent,\n HeaderRenderer,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './types';\n\n/**\n * Grid element interface for plugins.\n * Extends GridElementRef with plugin-specific methods.\n * Note: effectiveConfig is already available from GridElementRef.\n */\nexport interface GridElement extends GridElementRef {\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined;\n getPluginByName(name: string): BaseGridPlugin | undefined;\n /**\n * Get a plugin's state by plugin name.\n * This is a loose-coupling method for plugins to access other plugins' state\n * without importing the plugin class directly.\n * @internal Plugin API\n */\n getPluginState?(name: string): unknown;\n}\n\n/**\n * Header render context for plugin header renderers.\n */\nexport interface PluginHeaderRenderContext {\n /** Column configuration */\n column: ColumnConfig;\n /** Column index */\n colIndex: number;\n}\n\n// ============================================================================\n// Plugin Dependency Types\n// ============================================================================\n\n/**\n * Declares a dependency on another plugin.\n *\n * @category Plugin Development\n * @example\n * ```typescript\n * export class UndoRedoPlugin extends BaseGridPlugin {\n * static readonly dependencies: PluginDependency[] = [\n * { name: 'editing', required: true },\n * ];\n * }\n * ```\n */\nexport interface PluginDependency {\n /**\n * The name of the required plugin (matches the plugin's `name` property).\n * Use string names for loose coupling - avoids static imports.\n */\n name: string;\n\n /**\n * Whether this dependency is required (hard) or optional (soft).\n * - `true`: Plugin cannot function without it. Throws error if missing.\n * - `false`: Plugin works with reduced functionality. Logs info if missing.\n * @default true\n */\n required?: boolean;\n\n /**\n * Human-readable reason for this dependency.\n * Shown in error/info messages to help users understand why.\n * @example \"UndoRedoPlugin needs EditingPlugin to track cell edits\"\n */\n reason?: string;\n}\n\n/**\n * Declares an incompatibility between plugins.\n * When both plugins are loaded, a warning is logged to help users understand the conflict.\n *\n * @category Plugin Development\n */\nexport interface PluginIncompatibility {\n /**\n * The name of the incompatible plugin (matches the plugin's `name` property).\n */\n name: string;\n\n /**\n * Human-readable reason for the incompatibility.\n * Should explain why the plugins conflict and any workarounds.\n * @example \"Responsive card layout does not support row grouping yet\"\n */\n reason: string;\n}\n\n// ============================================================================\n// Plugin Manifest Types\n// ============================================================================\n\n/**\n * Defines a property that a plugin \"owns\" - used for configuration validation.\n * When this property is used without the owning plugin loaded, an error is thrown.\n *\n * @category Plugin Development\n */\nexport interface PluginPropertyDefinition {\n /** The property name on column or grid config */\n property: string;\n /** Whether this is a column-level or config-level property */\n level: 'column' | 'config';\n /** Human-readable description for error messages (e.g., 'the \"editable\" column property') */\n description: string;\n /** Import path hint for error messages (e.g., \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\") */\n importHint?: string;\n /** Custom check for whether property is considered \"used\" (default: truthy value check) */\n isUsed?: (value: unknown) => boolean;\n}\n\n/**\n * A configuration validation rule for detecting invalid/conflicting settings.\n * Plugins declare rules in their manifest; the validator executes them during grid initialization.\n *\n * @category Plugin Development\n * @template TConfig - The plugin's configuration type\n */\nexport interface PluginConfigRule<TConfig = unknown> {\n /** Rule identifier for debugging (e.g., 'selection/range-dblclick') */\n id: string;\n /** Severity: 'error' throws, 'warn' logs warning */\n severity: 'error' | 'warn';\n /** Human-readable message shown when rule is violated */\n message: string;\n /** Predicate returning true if the rule is VIOLATED (i.e., config is invalid) */\n check: (pluginConfig: TConfig) => boolean;\n}\n\n/**\n * Hook names that can have execution priority configured.\n *\n * @category Plugin Development\n */\nexport type HookName =\n | 'processColumns'\n | 'processRows'\n | 'afterRender'\n | 'onCellClick'\n | 'onCellMouseDown'\n | 'onCellMouseMove'\n | 'onCellMouseUp'\n | 'onKeyDown'\n | 'onScroll'\n | 'onScrollRender';\n\n/**\n * Static metadata about a plugin's capabilities and requirements.\n * Declared as a static property on plugin classes.\n *\n * @category Plugin Development\n * @template TConfig - The plugin's configuration type\n *\n * @example\n * ```typescript\n * export class MyPlugin extends BaseGridPlugin<MyConfig> {\n * static override readonly manifest: PluginManifest<MyConfig> = {\n * ownedProperties: [\n * { property: 'myProp', level: 'column', description: 'the \"myProp\" column property' },\n * ],\n * configRules: [\n * { id: 'my-plugin/invalid-combo', severity: 'warn', message: '...', check: (c) => c.a && c.b },\n * ],\n * };\n * readonly name = 'myPlugin';\n * }\n * ```\n */\nexport interface PluginManifest<TConfig = unknown> {\n /**\n * Properties this plugin owns - validated by validate-config.ts.\n * If a user uses one of these properties without loading the plugin, an error is thrown.\n */\n ownedProperties?: PluginPropertyDefinition[];\n\n /**\n * Hook execution priority (higher = later, default 0).\n * Use negative values to run earlier, positive to run later.\n */\n hookPriority?: Partial<Record<HookName, number>>;\n\n /**\n * Configuration validation rules - checked during grid initialization.\n * Rules with severity 'error' throw, 'warn' logs to console.\n */\n configRules?: PluginConfigRule<TConfig>[];\n\n /**\n * Plugins that are incompatible with this plugin.\n * When both plugins are loaded together, a warning is shown.\n *\n * @example\n * ```typescript\n * incompatibleWith: [\n * { name: 'groupingRows', reason: 'Responsive card layout does not support row grouping yet' },\n * ],\n * ```\n */\n incompatibleWith?: PluginIncompatibility[];\n}\n\n/**\n * Abstract base class for all grid plugins.\n *\n * @category Plugin Development\n * @template TConfig - Configuration type for the plugin\n */\nexport abstract class BaseGridPlugin<TConfig = unknown> implements GridPlugin {\n /**\n * Plugin dependencies - declare other plugins this one requires.\n *\n * Dependencies are validated when the plugin is attached.\n * Required dependencies throw an error if missing.\n * Optional dependencies log an info message if missing.\n *\n * @example\n * ```typescript\n * static readonly dependencies: PluginDependency[] = [\n * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },\n * { name: 'selection', required: false, reason: 'Enables selection-based undo' },\n * ];\n * ```\n */\n static readonly dependencies?: PluginDependency[];\n\n /**\n * Plugin manifest - declares owned properties, config rules, and hook priorities.\n *\n * This is read by the configuration validator to:\n * - Validate that required plugins are loaded when their properties are used\n * - Execute configRules to detect invalid/conflicting settings\n * - Order hook execution based on priority\n *\n * @example\n * ```typescript\n * static override readonly manifest: PluginManifest<MyConfig> = {\n * ownedProperties: [\n * { property: 'myProp', level: 'column', description: 'the \"myProp\" column property' },\n * ],\n * configRules: [\n * { id: 'myPlugin/conflict', severity: 'warn', message: '...', check: (c) => c.a && c.b },\n * ],\n * };\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n static readonly manifest?: PluginManifest<any>;\n\n /** Unique plugin identifier (derived from class name by default) */\n abstract readonly name: string;\n\n /**\n * Plugin version - defaults to grid version for built-in plugins.\n * Third-party plugins can override with their own semver.\n */\n readonly version: string = typeof __GRID_VERSION__ !== 'undefined' ? __GRID_VERSION__ : 'dev';\n\n /** CSS styles to inject into the grid's shadow DOM */\n readonly styles?: string;\n\n /** Custom cell renderers keyed by type name */\n readonly cellRenderers?: Record<string, CellRenderer>;\n\n /** Custom header renderers keyed by type name */\n readonly headerRenderers?: Record<string, HeaderRenderer>;\n\n /** Custom cell editors keyed by type name */\n readonly cellEditors?: Record<string, CellEditor>;\n\n /** The grid instance this plugin is attached to */\n protected grid!: GridElement;\n\n /** Plugin configuration - merged with defaults in attach() */\n protected config!: TConfig;\n\n /** User-provided configuration from constructor */\n protected readonly userConfig: Partial<TConfig>;\n\n /**\n * Plugin-level AbortController for event listener cleanup.\n * Created fresh in attach(), aborted in detach().\n * This ensures event listeners are properly cleaned up when plugins are re-attached.\n */\n #abortController?: AbortController;\n\n /**\n * Default configuration - subclasses should override this getter.\n * Note: This must be a getter (not property initializer) for proper inheritance\n * since property initializers run after parent constructor.\n */\n protected get defaultConfig(): Partial<TConfig> {\n return {};\n }\n\n constructor(config: Partial<TConfig> = {}) {\n this.userConfig = config;\n }\n\n /**\n * Called when the plugin is attached to a grid.\n * Override to set up event listeners, initialize state, etc.\n *\n * @example\n * ```ts\n * attach(grid: GridElement): void {\n * super.attach(grid);\n * // Set up document-level listeners with auto-cleanup\n * document.addEventListener('keydown', this.handleEscape, {\n * signal: this.disconnectSignal\n * });\n * }\n * ```\n */\n attach(grid: GridElement): void {\n // Abort any previous abort controller (in case of re-attach without detach)\n this.#abortController?.abort();\n // Create fresh abort controller for this attachment\n this.#abortController = new AbortController();\n\n this.grid = grid;\n // Merge config here (after subclass construction is complete)\n this.config = { ...this.defaultConfig, ...this.userConfig } as TConfig;\n }\n\n /**\n * Called when the plugin is detached from a grid.\n * Override to clean up event listeners, timers, etc.\n *\n * @example\n * ```ts\n * detach(): void {\n * // Clean up any state not handled by disconnectSignal\n * this.selectedRows.clear();\n * this.cache = null;\n * }\n * ```\n */\n detach(): void {\n // Abort the plugin's abort controller to clean up all event listeners\n // registered with { signal: this.disconnectSignal }\n this.#abortController?.abort();\n this.#abortController = undefined;\n // Override in subclass for additional cleanup\n }\n\n /**\n * Called when another plugin is attached to the same grid.\n * Use for inter-plugin coordination, e.g., to integrate with new plugins.\n *\n * @param name - The name of the plugin that was attached\n * @param plugin - The plugin instance (for direct access if needed)\n *\n * @example\n * ```ts\n * onPluginAttached(name: string, plugin: BaseGridPlugin): void {\n * if (name === 'selection') {\n * // Integrate with selection plugin\n * this.selectionPlugin = plugin as SelectionPlugin;\n * }\n * }\n * ```\n */\n onPluginAttached?(name: string, plugin: BaseGridPlugin): void;\n\n /**\n * Called when another plugin is detached from the same grid.\n * Use to clean up inter-plugin references.\n *\n * @param name - The name of the plugin that was detached\n *\n * @example\n * ```ts\n * onPluginDetached(name: string): void {\n * if (name === 'selection') {\n * this.selectionPlugin = undefined;\n * }\n * }\n * ```\n */\n onPluginDetached?(name: string): void;\n\n /**\n * Get another plugin instance from the same grid.\n * Use for inter-plugin communication.\n *\n * @example\n * ```ts\n * const selection = this.getPlugin(SelectionPlugin);\n * if (selection) {\n * const selectedRows = selection.getSelectedRows();\n * }\n * ```\n */\n protected getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.grid?.getPlugin(PluginClass);\n }\n\n /**\n * Emit a custom event from the grid.\n */\n protected emit<T>(eventName: string, detail: T): void {\n this.grid?.dispatchEvent?.(new CustomEvent(eventName, { detail, bubbles: true }));\n }\n\n /**\n * Emit a cancelable custom event from the grid.\n * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise\n */\n protected emitCancelable<T>(eventName: string, detail: T): boolean {\n const event = new CustomEvent(eventName, { detail, bubbles: true, cancelable: true });\n this.grid?.dispatchEvent?.(event);\n return event.defaultPrevented;\n }\n\n /**\n * Request a re-render of the grid.\n */\n protected requestRender(): void {\n this.grid?.requestRender?.();\n }\n\n /**\n * Request a re-render and restore focus styling afterward.\n * Use this when a plugin action (like expand/collapse) triggers a render\n * but needs to maintain keyboard navigation focus.\n */\n protected requestRenderWithFocus(): void {\n this.grid?.requestRenderWithFocus?.();\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Use this instead of requestRender() when only CSS classes need updating.\n */\n protected requestAfterRender(): void {\n this.grid?.requestAfterRender?.();\n }\n\n /**\n * Get the current rows from the grid.\n */\n protected get rows(): any[] {\n return this.grid?.rows ?? [];\n }\n\n /**\n * Get the original unfiltered/unprocessed rows from the grid.\n * Use this when you need all source data regardless of active filters.\n */\n protected get sourceRows(): any[] {\n return this.grid?.sourceRows ?? [];\n }\n\n /**\n * Get the current columns from the grid.\n */\n protected get columns(): ColumnConfig[] {\n return this.grid?.columns ?? [];\n }\n\n /**\n * Get only visible columns from the grid (excludes hidden).\n * Use this for rendering that needs to match the grid template.\n */\n protected get visibleColumns(): ColumnConfig[] {\n return this.grid?._visibleColumns ?? [];\n }\n\n /**\n * Get the grid as an HTMLElement for direct DOM operations.\n * Use sparingly - prefer the typed GridElementRef API when possible.\n *\n * @example\n * ```ts\n * const width = this.gridElement.clientWidth;\n * this.gridElement.classList.add('my-plugin-active');\n * ```\n */\n protected get gridElement(): HTMLElement {\n return this.grid as unknown as HTMLElement;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Use this when adding event listeners that should be cleaned up automatically.\n *\n * Best for:\n * - Document/window-level listeners added in attach()\n * - Listeners on the grid element itself\n * - Any listener that should persist across renders\n *\n * Not needed for:\n * - Listeners on elements created in afterRender() (removed with element)\n *\n * @example\n * element.addEventListener('click', handler, { signal: this.disconnectSignal });\n * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });\n */\n protected get disconnectSignal(): AbortSignal {\n // Return the plugin's own abort signal for proper cleanup on detach/re-attach\n // Falls back to grid's signal if plugin's controller not yet created\n return this.#abortController?.signal ?? this.grid?.disconnectSignal;\n }\n\n /**\n * Get the grid-level icons configuration.\n * Returns merged icons (user config + defaults).\n */\n protected get gridIcons(): typeof DEFAULT_GRID_ICONS {\n const userIcons = this.grid?.gridConfig?.icons ?? {};\n return { ...DEFAULT_GRID_ICONS, ...userIcons };\n }\n\n // #region Animation Helpers\n\n /**\n * Check if animations are enabled at the grid level.\n * Respects gridConfig.animation.mode and the CSS variable set by the grid.\n *\n * Plugins should use this to skip animations when:\n * - Animation mode is 'off' or `false`\n * - User prefers reduced motion and mode is 'reduced-motion' (default)\n *\n * @example\n * ```ts\n * private get animationStyle(): 'slide' | 'fade' | false {\n * if (!this.isAnimationEnabled) return false;\n * return this.config.animation ?? 'slide';\n * }\n * ```\n */\n protected get isAnimationEnabled(): boolean {\n const mode = this.grid?.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n // Explicit off = disabled\n if (mode === false || mode === 'off') return false;\n\n // Explicit on = always enabled\n if (mode === true || mode === 'on') return true;\n\n // reduced-motion: check CSS variable (set by grid based on media query)\n const host = this.gridElement;\n if (host) {\n const enabled = getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim();\n return enabled !== '0';\n }\n\n return true; // Default to enabled\n }\n\n /**\n * Get the animation duration in milliseconds from CSS variable.\n * Falls back to 200ms if not set.\n *\n * Plugins can use this for their animation timing to stay consistent\n * with the grid-level animation.duration setting.\n *\n * @example\n * ```ts\n * element.animate(keyframes, { duration: this.animationDuration });\n * ```\n */\n protected get animationDuration(): number {\n const host = this.gridElement;\n if (host) {\n const durationStr = getComputedStyle(host).getPropertyValue('--tbw-animation-duration').trim();\n const parsed = parseInt(durationStr, 10);\n if (!isNaN(parsed)) return parsed;\n }\n return 200; // Default\n }\n\n // #endregion\n\n /**\n * Resolve an icon value to string or HTMLElement.\n * Checks plugin config first, then grid-level icons, then defaults.\n *\n * @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')\n * @param pluginOverride - Optional plugin-level override\n * @returns The resolved icon value\n */\n protected resolveIcon(iconKey: keyof typeof DEFAULT_GRID_ICONS, pluginOverride?: IconValue): IconValue {\n // Plugin override takes precedence\n if (pluginOverride !== undefined) {\n return pluginOverride;\n }\n // Then grid-level config\n return this.gridIcons[iconKey];\n }\n\n /**\n * Set an icon value on an element.\n * Handles both string (text/HTML) and HTMLElement values.\n *\n * @param element - The element to set the icon on\n * @param icon - The icon value (string or HTMLElement)\n */\n protected setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.innerHTML = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n }\n\n /**\n * Log a warning message.\n */\n protected warn(message: string): void {\n console.warn(`[tbw-grid:${this.name}] ${message}`);\n }\n\n // #region Lifecycle Hooks\n\n /**\n * Transform rows before rendering.\n * Called during each render cycle before rows are rendered to the DOM.\n * Use this to filter, sort, or add computed properties to rows.\n *\n * @param rows - The current rows array (readonly to encourage returning a new array)\n * @returns The modified rows array to render\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Filter out hidden rows\n * return rows.filter(row => !row._hidden);\n * }\n * ```\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Add computed properties\n * return rows.map(row => ({\n * ...row,\n * _fullName: `${row.firstName} ${row.lastName}`\n * }));\n * }\n * ```\n */\n processRows?(rows: readonly any[]): any[];\n\n /**\n * Transform columns before rendering.\n * Called during each render cycle before column headers and cells are rendered.\n * Use this to add, remove, or modify column definitions.\n *\n * @param columns - The current columns array (readonly to encourage returning a new array)\n * @returns The modified columns array to render\n *\n * @example\n * ```ts\n * processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n * // Add a selection checkbox column\n * return [\n * { field: '_select', header: '', width: 40 },\n * ...columns\n * ];\n * }\n * ```\n */\n processColumns?(columns: readonly ColumnConfig[]): ColumnConfig[];\n\n /**\n * Called before each render cycle begins.\n * Use this to prepare state or cache values needed during rendering.\n *\n * @example\n * ```ts\n * beforeRender(): void {\n * this.visibleRowCount = this.calculateVisibleRows();\n * }\n * ```\n */\n beforeRender?(): void;\n\n /**\n * Called after each render cycle completes.\n * Use this for DOM manipulation, adding event listeners to rendered elements,\n * or applying visual effects like selection highlights.\n *\n * @example\n * ```ts\n * afterRender(): void {\n * // Apply selection styling to rendered rows\n * const rows = this.shadowRoot?.querySelectorAll('.data-row');\n * rows?.forEach((row, i) => {\n * row.classList.toggle('selected', this.selectedRows.has(i));\n * });\n * }\n * ```\n */\n afterRender?(): void;\n\n /**\n * Called after scroll-triggered row rendering completes.\n * This is a lightweight hook for applying visual state to recycled DOM elements.\n * Use this instead of afterRender when you need to reapply styling during scroll.\n *\n * Performance note: This is called frequently during scroll. Keep implementation fast.\n *\n * @example\n * ```ts\n * onScrollRender(): void {\n * // Reapply selection state to visible cells\n * this.applySelectionToVisibleCells();\n * }\n * ```\n */\n onScrollRender?(): void;\n\n /**\n * Return extra height contributed by this plugin (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations for virtualization.\n *\n * @returns Total extra height in pixels\n *\n * @example\n * ```ts\n * getExtraHeight(): number {\n * return this.expandedRows.size * this.detailHeight;\n * }\n * ```\n */\n getExtraHeight?(): number;\n\n /**\n * Return extra height that appears before a given row index.\n * Used by virtualization to correctly calculate scroll positions when\n * there's variable height content (like expanded detail rows) above the viewport.\n *\n * @param beforeRowIndex - The row index to calculate extra height before\n * @returns Extra height in pixels that appears before this row\n *\n * @example\n * ```ts\n * getExtraHeightBefore(beforeRowIndex: number): number {\n * let height = 0;\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * if (expandedRowIndex < beforeRowIndex) {\n * height += this.getDetailHeight(expandedRowIndex);\n * }\n * }\n * return height;\n * }\n * ```\n */\n getExtraHeightBefore?(beforeRowIndex: number): number;\n\n /**\n * Adjust the virtualization start index to render additional rows before the visible range.\n * Use this when expanded content (like detail rows) needs its parent row to remain rendered\n * even when the parent row itself has scrolled above the viewport.\n *\n * @param start - The calculated start row index\n * @param scrollTop - The current scroll position\n * @param rowHeight - The height of a single row\n * @returns The adjusted start index (lower than or equal to original start)\n *\n * @example\n * ```ts\n * adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n * // If row 5 is expanded and scrolled partially, keep it rendered\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * const expandedRowTop = expandedRowIndex * rowHeight;\n * const expandedRowBottom = expandedRowTop + rowHeight + this.detailHeight;\n * if (expandedRowBottom > scrollTop && expandedRowIndex < start) {\n * return expandedRowIndex;\n * }\n * }\n * return start;\n * }\n * ```\n */\n adjustVirtualStart?(start: number, scrollTop: number, rowHeight: number): number;\n\n /**\n * Render a custom row, bypassing the default row rendering.\n * Use this for special row types like group headers, detail rows, or footers.\n *\n * @param row - The row data object\n * @param rowEl - The row DOM element to render into\n * @param rowIndex - The index of the row in the data array\n * @returns `true` if the plugin handled rendering (prevents default), `false`/`void` for default rendering\n *\n * @example\n * ```ts\n * renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void {\n * if (row._isGroupHeader) {\n * rowEl.innerHTML = `<div class=\"group-header\">${row._groupLabel}</div>`;\n * return true; // Handled - skip default rendering\n * }\n * // Return void to let default rendering proceed\n * }\n * ```\n */\n renderRow?(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void;\n\n // #endregion\n\n // #region Inter-Plugin Communication\n\n /**\n * Handle queries from other plugins.\n * This is the generic mechanism for inter-plugin communication.\n * Plugins can respond to well-known query types or define their own.\n *\n * @param query - The query object with type and context\n * @returns Query-specific response, or undefined if not handling this query\n *\n * @example\n * ```ts\n * onPluginQuery(query: PluginQuery): unknown {\n * switch (query.type) {\n * case PLUGIN_QUERIES.CAN_MOVE_COLUMN:\n * // Prevent moving pinned columns\n * const column = query.context as ColumnConfig;\n * if (column.sticky === 'left' || column.sticky === 'right') {\n * return false;\n * }\n * break;\n * case PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS:\n * const params = query.context as ContextMenuParams;\n * return [{ id: 'my-action', label: 'My Action', action: () => {} }];\n * }\n * }\n * ```\n */\n onPluginQuery?(query: PluginQuery): unknown;\n\n // #endregion\n\n // #region Interaction Hooks\n\n /**\n * Handle keyboard events on the grid.\n * Called when a key is pressed while the grid or a cell has focus.\n *\n * @param event - The native KeyboardEvent\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onKeyDown(event: KeyboardEvent): boolean | void {\n * // Handle Ctrl+A for select all\n * if (event.ctrlKey && event.key === 'a') {\n * this.selectAllRows();\n * return true; // Prevent default browser select-all\n * }\n * }\n * ```\n */\n onKeyDown?(event: KeyboardEvent): boolean | void;\n\n /**\n * Handle cell click events.\n * Called when a data cell is clicked (not headers).\n *\n * @param event - Cell click event with row/column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onCellClick(event: CellClickEvent): boolean | void {\n * if (event.field === '_select') {\n * this.toggleRowSelection(event.rowIndex);\n * return true; // Handled\n * }\n * }\n * ```\n */\n onCellClick?(event: CellClickEvent): boolean | void;\n\n /**\n * Handle row click events.\n * Called when any part of a data row is clicked.\n * Note: This is called in addition to onCellClick, not instead of.\n *\n * @param event - Row click event with row context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onRowClick(event: RowClickEvent): boolean | void {\n * if (this.config.mode === 'row') {\n * this.selectRow(event.rowIndex, event.originalEvent);\n * return true;\n * }\n * }\n * ```\n */\n onRowClick?(event: RowClickEvent): boolean | void;\n\n /**\n * Handle header click events.\n * Called when a column header is clicked. Commonly used for sorting.\n *\n * @param event - Header click event with column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onHeaderClick(event: HeaderClickEvent): boolean | void {\n * if (event.column.sortable !== false) {\n * this.toggleSort(event.field);\n * return true;\n * }\n * }\n * ```\n */\n onHeaderClick?(event: HeaderClickEvent): boolean | void;\n\n /**\n * Handle scroll events on the grid viewport.\n * Called during scrolling. Note: This may be called frequently; debounce if needed.\n *\n * @param event - Scroll event with scroll position and viewport dimensions\n *\n * @example\n * ```ts\n * onScroll(event: ScrollEvent): void {\n * // Update sticky column positions\n * this.updateStickyPositions(event.scrollLeft);\n * }\n * ```\n */\n onScroll?(event: ScrollEvent): void;\n\n /**\n * Handle cell mousedown events.\n * Used for initiating drag operations like range selection or column resize.\n *\n * @param event - Mouse event with cell context\n * @returns `true` to indicate drag started (prevents text selection), `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseDown(event: CellMouseEvent): boolean | void {\n * if (event.rowIndex !== undefined && this.config.mode === 'range') {\n * this.startDragSelection(event.rowIndex, event.colIndex);\n * return true; // Prevent text selection\n * }\n * }\n * ```\n */\n onCellMouseDown?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mousemove events during drag operations.\n * Only called when a drag is in progress (after mousedown returned true).\n *\n * @param event - Mouse event with current cell context\n * @returns `true` to continue handling the drag, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseMove(event: CellMouseEvent): boolean | void {\n * if (this.isDragging && event.rowIndex !== undefined) {\n * this.extendSelection(event.rowIndex, event.colIndex);\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseMove?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mouseup events to end drag operations.\n *\n * @param event - Mouse event with final cell context\n * @returns `true` if drag was finalized, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseUp(event: CellMouseEvent): boolean | void {\n * if (this.isDragging) {\n * this.finalizeDragSelection();\n * this.isDragging = false;\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseUp?(event: CellMouseEvent): boolean | void;\n\n // Note: Context menu items are now provided via onPluginQuery with PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS\n // This keeps the core decoupled from the context-menu plugin specifics.\n\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Contribute plugin-specific state for a column.\n * Called by the grid when collecting column state for serialization.\n * Plugins can add their own properties to the column state.\n *\n * @param field - The field name of the column\n * @returns Partial column state with plugin-specific properties, or undefined if no state to contribute\n *\n * @example\n * ```ts\n * getColumnState(field: string): Partial<ColumnState> | undefined {\n * const filterModel = this.filterModels.get(field);\n * if (filterModel) {\n * // Uses module augmentation to add filter property to ColumnState\n * return { filter: filterModel } as Partial<ColumnState>;\n * }\n * return undefined;\n * }\n * ```\n */\n getColumnState?(field: string): Partial<ColumnState> | undefined;\n\n /**\n * Apply plugin-specific state to a column.\n * Called by the grid when restoring column state from serialized data.\n * Plugins should restore their internal state based on the provided state.\n *\n * @param field - The field name of the column\n * @param state - The column state to apply (may contain plugin-specific properties)\n *\n * @example\n * ```ts\n * applyColumnState(field: string, state: ColumnState): void {\n * // Check for filter property added via module augmentation\n * const filter = (state as any).filter;\n * if (filter) {\n * this.filterModels.set(field, filter);\n * this.applyFilter();\n * }\n * }\n * ```\n */\n applyColumnState?(field: string, state: ColumnState): void;\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Report horizontal scroll boundary offsets for this plugin.\n * Plugins that obscure part of the scroll area (e.g., pinned/sticky columns)\n * should return how much space they occupy on each side.\n * The keyboard navigation uses this to ensure focused cells are fully visible.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Object with left/right pixel offsets and optional skipScroll flag, or undefined if plugin has no offsets\n *\n * @example\n * ```ts\n * getHorizontalScrollOffsets(rowEl?: HTMLElement, focusedCell?: HTMLElement): { left: number; right: number; skipScroll?: boolean } | undefined {\n * // Calculate total width of left-pinned columns\n * const leftCells = rowEl?.querySelectorAll('.sticky-left') ?? [];\n * let left = 0;\n * leftCells.forEach(el => { left += (el as HTMLElement).offsetWidth; });\n * // Skip scroll if focused cell is pinned (always visible)\n * const skipScroll = focusedCell?.classList.contains('sticky-left');\n * return { left, right: 0, skipScroll };\n * }\n * ```\n */\n getHorizontalScrollOffsets?(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } | undefined;\n\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Register a tool panel for this plugin.\n * Return undefined if plugin has no tool panel.\n * The shell will create a toolbar toggle button and render the panel content\n * when the user opens the panel.\n *\n * @returns Tool panel definition, or undefined if plugin has no panel\n *\n * @example\n * ```ts\n * getToolPanel(): ToolPanelDefinition | undefined {\n * return {\n * id: 'columns',\n * title: 'Columns',\n * icon: '☰',\n * tooltip: 'Show/hide columns',\n * order: 10,\n * render: (container) => {\n * this.renderColumnList(container);\n * return () => this.cleanup();\n * },\n * };\n * }\n * ```\n */\n getToolPanel?(): ToolPanelDefinition | undefined;\n\n /**\n * Register content for the shell header center section.\n * Return undefined if plugin has no header content.\n * Examples: search input, selection summary, status indicators.\n *\n * @returns Header content definition, or undefined if plugin has no header content\n *\n * @example\n * ```ts\n * getHeaderContent(): HeaderContentDefinition | undefined {\n * return {\n * id: 'quick-filter',\n * order: 10,\n * render: (container) => {\n * const input = document.createElement('input');\n * input.type = 'text';\n * input.placeholder = 'Search...';\n * input.addEventListener('input', this.handleInput);\n * container.appendChild(input);\n * return () => input.removeEventListener('input', this.handleInput);\n * },\n * };\n * }\n * ```\n */\n getHeaderContent?(): HeaderContentDefinition | undefined;\n\n // #endregion\n}\n","/**\n * Grid DOM Constants\n *\n * Centralized constants for CSS classes, data attributes, and selectors\n * used throughout the grid. Use these instead of magic strings.\n *\n * Note: Some constants here are used by plugins but defined in core because:\n * 1. The core CSS needs to style these elements (e.g., sticky positioning)\n * 2. Multiple plugins may share the same class names\n *\n * Plugins can define their own additional constants and export them.\n * See each plugin's index.ts for plugin-specific exports.\n */\n\n// #region CSS Classes\n\n/**\n * CSS class names used in the grid's shadow DOM.\n * Use these when adding/removing classes or querying elements.\n *\n * Classes are organized by:\n * - Core: Used by the grid core for structure and basic functionality\n * - Shared: Used by multiple features/plugins, styled by core CSS\n *\n * @category Plugin Development\n */\nexport const GridClasses = {\n // ─── Core Structure ───────────────────────────────────────────────\n ROOT: 'tbw-grid-root',\n HEADER: 'header',\n HEADER_ROW: 'header-row',\n HEADER_CELL: 'header-cell',\n\n // Body structure\n ROWS_VIEWPORT: 'rows-viewport',\n ROWS_SPACER: 'rows-spacer',\n ROWS_CONTAINER: 'rows',\n\n // Row elements\n DATA_ROW: 'data-row',\n GROUP_ROW: 'group-row',\n\n // Cell elements\n DATA_CELL: 'data-cell',\n\n // ─── Core States ──────────────────────────────────────────────────\n SELECTED: 'selected',\n FOCUSED: 'focused',\n EDITING: 'editing',\n EXPANDED: 'expanded',\n COLLAPSED: 'collapsed',\n DRAGGING: 'dragging',\n RESIZING: 'resizing',\n\n // Sorting (core feature)\n SORTABLE: 'sortable',\n SORTED_ASC: 'sorted-asc',\n SORTED_DESC: 'sorted-desc',\n\n // Visibility\n HIDDEN: 'hidden',\n\n // ─── Shared Classes (used by plugins, styled by core CSS) ────────\n // These are defined here because core CSS provides the base styling.\n // Plugins apply these classes; core CSS defines how they look.\n\n // Sticky positioning (PinnedColumnsPlugin applies, core CSS styles)\n STICKY_LEFT: 'sticky-left',\n STICKY_RIGHT: 'sticky-right',\n\n // Pinned rows (PinnedRowsPlugin applies, core CSS styles)\n PINNED_TOP: 'pinned-top',\n PINNED_BOTTOM: 'pinned-bottom',\n\n // Tree structure (TreePlugin applies, core CSS styles)\n TREE_TOGGLE: 'tree-toggle',\n TREE_INDENT: 'tree-indent',\n\n // Grouping (GroupingRowsPlugin applies, core CSS styles)\n GROUP_TOGGLE: 'group-toggle',\n GROUP_LABEL: 'group-label',\n GROUP_COUNT: 'group-count',\n\n // Selection (SelectionPlugin applies, core CSS styles)\n RANGE_SELECTION: 'range-selection',\n SELECTION_OVERLAY: 'selection-overlay',\n} as const;\n\n// #endregion\n\n// #region Data Attributes\n\n/**\n * Data attribute names used on grid elements.\n * Use these when getting/setting data attributes.\n *\n * @category Plugin Development\n */\nexport const GridDataAttrs = {\n // ─── Core Attributes ──────────────────────────────────────────────\n ROW_INDEX: 'data-row-index',\n COL_INDEX: 'data-col-index',\n FIELD: 'data-field',\n\n // ─── Shared Attributes (used by plugins) ──────────────────────────\n GROUP_KEY: 'data-group-key', // GroupingRowsPlugin\n TREE_LEVEL: 'data-tree-level', // TreePlugin\n STICKY: 'data-sticky', // PinnedColumnsPlugin\n} as const;\n\n// #endregion\n\n// #region Selectors\n\n/**\n * Common CSS selectors for querying grid elements.\n * Built from the class constants for consistency.\n *\n * @category Plugin Development\n */\nexport const GridSelectors = {\n ROOT: `.${GridClasses.ROOT}`,\n HEADER: `.${GridClasses.HEADER}`,\n HEADER_ROW: `.${GridClasses.HEADER_ROW}`,\n HEADER_CELL: `.${GridClasses.HEADER_CELL}`,\n ROWS_VIEWPORT: `.${GridClasses.ROWS_VIEWPORT}`,\n ROWS_CONTAINER: `.${GridClasses.ROWS_CONTAINER}`,\n DATA_ROW: `.${GridClasses.DATA_ROW}`,\n DATA_CELL: `.${GridClasses.DATA_CELL}`,\n GROUP_ROW: `.${GridClasses.GROUP_ROW}`,\n\n // By data attribute\n ROW_BY_INDEX: (index: number) => `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${index}\"]`,\n CELL_BY_FIELD: (field: string) => `.${GridClasses.DATA_CELL}[${GridDataAttrs.FIELD}=\"${field}\"]`,\n CELL_AT: (row: number, col: number) =>\n `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${row}\"] .${GridClasses.DATA_CELL}[${GridDataAttrs.COL_INDEX}=\"${col}\"]`,\n\n // State selectors\n SELECTED_ROWS: `.${GridClasses.DATA_ROW}.${GridClasses.SELECTED}`,\n EDITING_CELL: `.${GridClasses.DATA_CELL}.${GridClasses.EDITING}`,\n} as const;\n\n// #endregion\n\n// #region CSS Custom Properties\n\n/**\n * CSS custom property names for theming.\n * Use these when programmatically setting styles.\n *\n * @category Plugin Development\n */\nexport const GridCSSVars = {\n // Colors\n COLOR_BG: '--tbw-color-bg',\n COLOR_FG: '--tbw-color-fg',\n COLOR_FG_MUTED: '--tbw-color-fg-muted',\n COLOR_BORDER: '--tbw-color-border',\n COLOR_ACCENT: '--tbw-color-accent',\n COLOR_HEADER_BG: '--tbw-color-header-bg',\n COLOR_HEADER_FG: '--tbw-color-header-fg',\n COLOR_SELECTION: '--tbw-color-selection',\n COLOR_ROW_HOVER: '--tbw-color-row-hover',\n COLOR_ROW_ALT: '--tbw-color-row-alt',\n\n // Sizing\n ROW_HEIGHT: '--tbw-row-height',\n HEADER_HEIGHT: '--tbw-header-height',\n CELL_PADDING: '--tbw-cell-padding',\n\n // Typography\n FONT_FAMILY: '--tbw-font-family',\n FONT_SIZE: '--tbw-font-size',\n\n // Borders\n BORDER_RADIUS: '--tbw-border-radius',\n FOCUS_OUTLINE: '--tbw-focus-outline',\n} as const;\n\n// #endregion\n\n// Type helpers\nexport type GridClassName = (typeof GridClasses)[keyof typeof GridClasses];\nexport type GridDataAttr = (typeof GridDataAttrs)[keyof typeof GridDataAttrs];\nexport type GridCSSVar = (typeof GridCSSVars)[keyof typeof GridCSSVars];\n","/**\n * @toolbox-web/grid - A high-performance, framework-agnostic data grid web component.\n *\n * This is the public API surface. Only symbols exported here are considered stable.\n *\n * @packageDocumentation\n * @module Core\n */\n\n// #region Public API surface - only export what consumers need\nexport { DataGridElement, DataGridElement as GridElement } from './lib/core/grid';\n\n// Import types needed for factory functions\nimport type { DataGridElement } from './lib/core/grid';\nimport type { GridConfig } from './lib/core/types';\n\n// #region Factory Functions\n/**\n * Create a new typed grid element programmatically.\n *\n * This avoids the need to cast when creating grids in TypeScript:\n * ```typescript\n * // Before: manual cast required\n * const grid = document.createElement('tbw-grid') as DataGridElement<Employee>;\n *\n * // After: fully typed\n * const grid = createGrid<Employee>({\n * columns: [{ field: 'name' }],\n * plugins: [new SelectionPlugin()],\n * });\n * grid.rows = employees; // ✓ Typed!\n * ```\n *\n * @param config - Optional initial grid configuration\n * @returns A typed DataGridElement instance\n */\nexport function createGrid<TRow = unknown>(config?: Partial<GridConfig<TRow>>): DataGridElement<TRow> {\n const grid = document.createElement('tbw-grid') as DataGridElement<TRow>;\n if (config) {\n grid.gridConfig = config as GridConfig<TRow>;\n }\n return grid;\n}\n\n/**\n * Query an existing grid element from the DOM with proper typing.\n *\n * This avoids the need to cast when querying grids:\n * ```typescript\n * // Before: manual cast required\n * const grid = document.querySelector('tbw-grid') as DataGridElement<Employee>;\n *\n * // After: fully typed\n * const grid = queryGrid<Employee>('#my-grid');\n * if (grid) {\n * grid.rows = employees; // ✓ Typed!\n * }\n * ```\n *\n * @param selector - CSS selector to find the grid element\n * @param parent - Parent node to search within (defaults to document)\n * @returns The typed grid element or null if not found\n */\nexport function queryGrid<TRow = unknown>(\n selector: string,\n parent: ParentNode = document,\n): DataGridElement<TRow> | null {\n return parent.querySelector(selector) as DataGridElement<TRow> | null;\n}\n// #endregion\n\n/**\n * Event name constants for DataGrid (public API).\n *\n * @category Events\n */\nexport const DGEvents = {\n /** Emitted by core after any data mutation */\n CELL_CHANGE: 'cell-change',\n CELL_COMMIT: 'cell-commit',\n ROW_COMMIT: 'row-commit',\n CHANGED_ROWS_RESET: 'changed-rows-reset',\n MOUNT_EXTERNAL_VIEW: 'mount-external-view',\n MOUNT_EXTERNAL_EDITOR: 'mount-external-editor',\n SORT_CHANGE: 'sort-change',\n COLUMN_RESIZE: 'column-resize',\n ACTIVATE_CELL: 'activate-cell',\n /** Unified cell activation event (keyboard or pointer) */\n CELL_ACTIVATE: 'cell-activate',\n GROUP_TOGGLE: 'group-toggle',\n COLUMN_STATE_CHANGE: 'column-state-change',\n} as const;\n\n/**\n * Union type of all DataGrid event names.\n *\n * @category Events\n */\nexport type DGEventName = (typeof DGEvents)[keyof typeof DGEvents];\n\n/**\n * Plugin event constants (mirrors DGEvents pattern).\n *\n * @category Events\n */\nexport const PluginEvents = {\n // Selection plugin\n SELECTION_CHANGE: 'selection-change',\n // Tree plugin\n TREE_EXPAND: 'tree-expand',\n // Filtering plugin\n FILTER_CHANGE: 'filter-change',\n // Sorting plugin\n SORT_MODEL_CHANGE: 'sort-model-change',\n // Export plugin\n EXPORT_START: 'export-start',\n EXPORT_COMPLETE: 'export-complete',\n // Clipboard plugin\n CLIPBOARD_COPY: 'clipboard-copy',\n CLIPBOARD_PASTE: 'clipboard-paste',\n // Context menu plugin\n CONTEXT_MENU_OPEN: 'context-menu-open',\n CONTEXT_MENU_CLOSE: 'context-menu-close',\n // Undo/Redo plugin\n HISTORY_CHANGE: 'history-change',\n // Server-side plugin\n SERVER_LOADING: 'server-loading',\n SERVER_ERROR: 'server-error',\n // Visibility plugin\n COLUMN_VISIBILITY_CHANGE: 'column-visibility-change',\n // Reorder plugin\n COLUMN_REORDER: 'column-reorder',\n // Master-detail plugin\n DETAIL_EXPAND: 'detail-expand',\n // Grouping rows plugin\n GROUP_EXPAND: 'group-expand',\n} as const;\n\n/**\n * Union type of all plugin event names.\n *\n * @category Events\n */\nexport type PluginEventName = (typeof PluginEvents)[keyof typeof PluginEvents];\n\n// Public type exports\nexport type {\n /** @deprecated Use CellActivateDetail instead */\n ActivateCellDetail,\n AggregatorRef,\n // Animation types\n AnimationConfig,\n AnimationMode,\n AnimationStyle,\n BaseColumnConfig,\n // Event detail types\n CellActivateDetail,\n CellActivateTrigger,\n CellChangeDetail,\n CellClickDetail,\n CellCommitDetail,\n CellRenderContext,\n ChangedRowsResetDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnEditorContext,\n // Column features\n ColumnEditorSpec,\n ColumnResizeDetail,\n // Column state types\n ColumnSortState,\n ColumnState,\n // Type-level defaults\n ColumnType,\n ColumnViewRenderer,\n DataGridCustomEvent,\n DataGridElement as DataGridElementInterface,\n DataGridEventMap,\n ExpandCollapseAnimation,\n ExternalMountEditorDetail,\n ExternalMountViewDetail,\n FitMode,\n // Framework adapter interface\n FrameworkAdapter,\n GridColumnState,\n // Core configuration types\n GridConfig,\n // Icons\n GridIcons,\n // Plugin interface (minimal shape for type-checking)\n GridPlugin,\n // Shell types\n HeaderContentDefinition,\n IconValue,\n // Inference types\n InferredColumnResult,\n PrimitiveColumnType,\n // Public interface\n PublicGrid,\n RowClickDetail,\n RowCommitDetail,\n // Grouping & Footer types\n RowGroupRenderConfig,\n // Data update management\n RowUpdate,\n ShellConfig,\n ShellHeaderConfig,\n SortChangeDetail,\n // Sorting types\n SortHandler,\n SortState,\n ToolbarContentDefinition,\n ToolPanelConfig,\n ToolPanelDefinition,\n TypeDefault,\n UpdateSource,\n} from './lib/core/types';\n\n// Re-export FitModeEnum for runtime usage\nexport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS, FitModeEnum } from './lib/core/types';\n\n// Re-export sorting utilities for custom sort handlers\nexport { builtInSort, defaultComparator } from './lib/core/internal/sorting';\n// #endregion\n\n// #region Plugin Development\n// Plugin base class - for creating custom plugins\nexport { BaseGridPlugin, PLUGIN_QUERIES } from './lib/core/plugin';\nexport type { PluginQuery } from './lib/core/plugin';\n\n// DOM constants - for querying grid elements and styling\nexport { GridClasses, GridCSSVars, GridDataAttrs, GridSelectors } from './lib/core/constants';\nexport type { GridClassName, GridCSSVar, GridDataAttr } from './lib/core/constants';\n\n// Note: Plugin-specific types (SelectionConfig, FilterConfig, etc.) are exported\n// from their respective plugin entry points:\n// import { SelectionPlugin, type SelectionConfig } from '@toolbox-web/grid/plugins/selection';\n// import { FilteringPlugin, type FilterConfig } from '@toolbox-web/grid/plugins/filtering';\n// Or import all plugins + types from: '@toolbox-web/grid/all'\n// #endregion\n\n// #region Advanced Types for Custom Plugins & Enterprise Extensions\n/**\n * Internal types for advanced users building custom plugins or enterprise extensions.\n *\n * These types provide access to grid internals that may be needed for deep customization.\n * While not part of the \"stable\" API, they are exported for power users who need them.\n *\n * @remarks\n * Use with caution - these types expose internal implementation details.\n * The underscore-prefixed members they reference are considered less stable\n * than the public API surface.\n *\n * @example\n * ```typescript\n * import { BaseGridPlugin } from '@toolbox-web/grid';\n * import type { InternalGrid, ColumnInternal } from '@toolbox-web/grid';\n *\n * export class MyPlugin extends BaseGridPlugin<MyConfig> {\n * afterRender(): void {\n * // Access grid internals with proper typing\n * const grid = this.grid as InternalGrid;\n * const columns = grid._columns as ColumnInternal[];\n * // ...\n * }\n * }\n * ```\n */\n\n/**\n * Column configuration with internal cache properties.\n * Extends the public ColumnConfig with compiled template caches (__compiledView, __viewTemplate, etc.)\n * @internal\n */\nexport type { ColumnInternal } from './lib/core/types';\n\n/**\n * Compiled template function with __blocked property for error handling.\n * @internal\n */\nexport type { CompiledViewFunction } from './lib/core/types';\n\n/**\n * Full internal grid interface extending PublicGrid with internal state.\n * Provides typed access to _columns, _rows, virtualization state, etc.\n * @internal\n */\nexport type { InternalGrid } from './lib/core/types';\n\n/**\n * Cell context for renderer/editor operations.\n * @internal\n */\nexport type { CellContext } from './lib/core/types';\n\n/**\n * Editor execution context extending CellContext with commit/cancel functions.\n * @internal\n */\nexport type { EditorExecContext } from './lib/core/types';\n\n/**\n * Template evaluation context for dynamic templates.\n * @internal\n */\nexport type { EvalContext } from './lib/core/types';\n\n/**\n * Column resize controller interface.\n * @internal\n */\nexport type { ResizeController } from './lib/core/types';\n\n/**\n * Row virtualization state interface.\n * @internal\n */\nexport type { VirtualState } from './lib/core/types';\n\n/**\n * Row element with internal editing state cache.\n * Used for tracking editing cell count without querySelector.\n * @internal\n */\nexport type { RowElementInternal } from './lib/core/types';\n\n/**\n * Union type for input-like elements that have a `value` property.\n * Covers standard form elements and custom elements with value semantics.\n * @internal\n */\nexport type { InputLikeElement } from './lib/core/types';\n\n/**\n * Utility type to safely cast a grid element to InternalGrid for plugin use.\n *\n * @example\n * ```typescript\n * import type { AsInternalGrid, InternalGrid } from '@toolbox-web/grid';\n *\n * class MyPlugin extends BaseGridPlugin {\n * get internalGrid(): InternalGrid {\n * return this.grid as AsInternalGrid;\n * }\n * }\n * ```\n * @internal\n */\nexport type AsInternalGrid<T = unknown> = import('./lib/core/types').InternalGrid<T>;\n\n/**\n * Render phase enum for debugging and understanding the render pipeline.\n * Higher phases include all lower phase work.\n */\nexport { RenderPhase } from './lib/core/internal/render-scheduler';\n// #endregion\n","/**\n * Aggregators Core Registry\n *\n * Provides a central registry for aggregator functions.\n * Built-in aggregators are provided by default.\n * Plugins can register additional aggregators.\n *\n * The registry is exposed as a singleton object that can be accessed:\n * - By ES module imports: import { aggregatorRegistry } from '@toolbox-web/grid'\n * - By UMD/CDN: TbwGrid.aggregatorRegistry\n * - By plugins via context: ctx.aggregatorRegistry\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type AggregatorFn = (rows: any[], field: string, column?: any) => any;\nexport type AggregatorRef = string | AggregatorFn;\n\n/** Built-in aggregator functions */\nconst builtInAggregators: Record<string, AggregatorFn> = {\n sum: (rows, field) => rows.reduce((acc, row) => acc + (Number(row[field]) || 0), 0),\n avg: (rows, field) => {\n const sum = rows.reduce((acc, row) => acc + (Number(row[field]) || 0), 0);\n return rows.length ? sum / rows.length : 0;\n },\n count: (rows) => rows.length,\n min: (rows, field) => Math.min(...rows.map((r) => Number(r[field]) || Infinity)),\n max: (rows, field) => Math.max(...rows.map((r) => Number(r[field]) || -Infinity)),\n first: (rows, field) => rows[0]?.[field],\n last: (rows, field) => rows[rows.length - 1]?.[field],\n};\n\n/** Custom aggregator registry (for plugins to add to) */\nconst customAggregators: Map<string, AggregatorFn> = new Map();\n\n/**\n * The aggregator registry singleton.\n * Plugins should access this through context or the global namespace.\n */\nexport const aggregatorRegistry = {\n /**\n * Register a custom aggregator function.\n */\n register(name: string, fn: AggregatorFn): void {\n customAggregators.set(name, fn);\n },\n\n /**\n * Unregister a custom aggregator function.\n */\n unregister(name: string): void {\n customAggregators.delete(name);\n },\n\n /**\n * Get an aggregator function by reference.\n */\n get(ref: AggregatorRef | undefined): AggregatorFn | undefined {\n if (ref === undefined) return undefined;\n if (typeof ref === 'function') return ref;\n // Check custom first, then built-in\n return customAggregators.get(ref) ?? builtInAggregators[ref];\n },\n\n /**\n * Run an aggregator on a set of rows.\n */\n run(ref: AggregatorRef | undefined, rows: any[], field: string, column?: any): any {\n const fn = this.get(ref);\n return fn ? fn(rows, field, column) : undefined;\n },\n\n /**\n * Check if an aggregator exists.\n */\n has(name: string): boolean {\n return customAggregators.has(name) || name in builtInAggregators;\n },\n\n /**\n * List all available aggregator names.\n */\n list(): string[] {\n return [...Object.keys(builtInAggregators), ...customAggregators.keys()];\n },\n};\n\n// #region Value-based Aggregators\n// Used by plugins like Pivot that work with pre-extracted numeric values\n\nexport type ValueAggregatorFn = (values: number[]) => number;\n\n/**\n * Built-in value-based aggregators.\n * These operate on arrays of numbers (unlike row-based aggregators).\n */\nconst builtInValueAggregators: Record<string, ValueAggregatorFn> = {\n sum: (vals) => vals.reduce((a, b) => a + b, 0),\n avg: (vals) => (vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0),\n count: (vals) => vals.length,\n min: (vals) => (vals.length ? Math.min(...vals) : 0),\n max: (vals) => (vals.length ? Math.max(...vals) : 0),\n first: (vals) => vals[0] ?? 0,\n last: (vals) => vals[vals.length - 1] ?? 0,\n};\n\n/**\n * Get a value-based aggregator function.\n * Used by Pivot plugin and other features that aggregate pre-extracted values.\n *\n * @param aggFunc - Aggregation function name ('sum', 'avg', 'count', 'min', 'max', 'first', 'last')\n * @returns Aggregator function that takes number[] and returns number\n */\nexport function getValueAggregator(aggFunc: string): ValueAggregatorFn {\n return builtInValueAggregators[aggFunc] ?? builtInValueAggregators.sum;\n}\n\n/**\n * Run a value-based aggregator on a set of values.\n *\n * @param aggFunc - Aggregation function name\n * @param values - Array of numbers to aggregate\n * @returns Aggregated result\n */\nexport function runValueAggregator(aggFunc: string, values: number[]): number {\n return getValueAggregator(aggFunc)(values);\n}\n// #endregion\n\n// Legacy function exports for backward compatibility\nexport const registerAggregator = aggregatorRegistry.register.bind(aggregatorRegistry);\nexport const unregisterAggregator = aggregatorRegistry.unregister.bind(aggregatorRegistry);\nexport const getAggregator = aggregatorRegistry.get.bind(aggregatorRegistry);\nexport const runAggregator = aggregatorRegistry.run.bind(aggregatorRegistry);\nexport const listAggregators = aggregatorRegistry.list.bind(aggregatorRegistry);\n"],"names":["FitModeEnum","DEFAULT_ANIMATION_CONFIG","DEFAULT_GRID_ICONS","parseLightDomColumns","host","el","field","rawType","type","header","sortable","editable","config","widthAttr","numericWidth","minWidthAttr","numericMinWidth","editorName","rendererName","optionsAttr","item","value","label","viewTpl","editorTpl","headerTpl","adapters","viewTarget","viewAdapter","a","renderer","editorTarget","editorAdapter","editor","c","mergeColumns","programmatic","dom","domMap","existing","cRenderer","existingRenderer","merged","d","m","dRenderer","mRenderer","addPart","token","autoSizeColumns","grid","mode","headerCells","changed","col","i","headerCell","max","rowEl","cell","w","updateTemplate","min","inferType","inferColumns","rows","provided","sample","columns","k","v","typeMap","EXPR_RE","EMPTY_SENTINEL","SAFE_EXPR","FORBIDDEN","escapeHtml","text","DANGEROUS_TAGS","DANGEROUS_ATTR_PATTERN","URL_ATTRS","DANGEROUS_URL_PROTOCOL","sanitizeHTML","html","template","sanitizeNode","root","toRemove","elements","tagName","attr","attrsToRemove","attrName","name","evalTemplateString","raw","ctx","parts","evaluated","_m","expr","res","evalSingle","finalStr","postProcess","allEmpty","p","key","dotChain","out","str","s","finalCellScrub","n","compileTemplate","forceBlank","fn","STATE_CHANGE_DEBOUNCE_MS","ConfigManager","#gridConfig","#columns","#fitMode","#lightDomColumnsCache","#originalColumnNodes","#originalConfig","#effectiveConfig","#sourcesChanged","#changeListeners","#lightDomObserver","#stateChangeTimeoutId","#initialColumnState","#callbacks","#lightDomTitle","callbacks","hasColumns","base","#collectAllSources","#cloneConfig","#applyPostMergeOperations","clone","h","configColumns","domCols","#mergeShellConfig","shellLightDomTitle","lightDomHeaderContent","toolPanelsMap","panels","b","headerContentsMap","contents","toolbarContentsMap","apiContents","originalConfigContents","configIds","mergedContents","content","plugins","sortStates","#getSortState","index","state","internalCol","sortState","plugin","pluginState","allColumns","stateMap","updatedColumns","updated","orderA","orderB","sortedByPriority","primarySort","colState","sortMap","visible","allCols","order","columnMap","reordered","#lightDomHandlers","callback","pendingCallbacks","debounceTimer","processPendingCallbacks","mutations","mutation","node","cb","booleanCellHTML","formatDateValue","getRowIndexFromCell","parent","getColIndexFromCell","clearCellFocus","resolveRenderer","columnRenderer","gridTypeDefaults","adapter","appDefault","FOCUSABLE_EDITOR_SELECTOR","hasEditingCells","clearEditingState","cellTemplate","rowTemplate","createCellFromTemplate","createRowFromTemplate","invalidateCellCache","renderVisibleRows","start","end","epoch","renderRowHook","needed","bodyEl","colLen","headerRowCount","hasRenderRowPlugins","rowIndex","rowData","rowEpoch","prevRef","cellCount","structureValid","dataRefChanged","needsExternalRebuild","hasEditing","isActivelyEditedRow","renderInlineRow","fastPatchRow","isChanged","changedRowIds","rowId","hasChangedClass","rowClassFn","prevClasses","cls","newClasses","validClasses","e","children","colsLen","childLen","minLen","focusRow","focusCol","hasSpecialCols","typeDefaults","rowIndexStr","shouldHaveFocus","hasFocus","cellClassFn","cellClasses","cellRenderer","produced","displayStr","formatted","gridEl","fragment","colIndex","compiled","tplHolder","viewRenderer","externalView","needsSanitization","spec","placeholder","context","output","blocked","rawTpl","textContent","cellValue","handleRowClick","firstCell","cellEl","focusChanged","ensureCellVisible","handleGridKeyDown","maxRow","maxCol","editing","colType","path","target","isFormField","tag","column","row","detail","activateEvent","legacyEvent","options","rowHeight","container","viewportEl","scrollEl","visibleHeight","y","isEditing","vStart","vEnd","scrollArea","offsets","cellRect","scrollAreaRect","cellLeft","cellRight","visibleLeft","visibleRight","focusTarget","dragState","handleCellMousedown","buildCellMouseEvent","renderRoot","elAtPoint","headerEl","handleMouseDown","event","handleMouseMove","handleMouseUp","setupCellEventDelegation","signal","setupRootEventDelegation","gridElement","defaultComparator","builtInSort","comparator","direction","rA","rB","finalizeSortResult","sortedRows","dir","r","renderHeader","toggleSort","applySort","result","setIcon","element","icon","headerRow","maybeTpl","span","active","icons","iconValue","handle","hasIdleCallback","scheduleIdle","cancelIdle","RenderPhase","RenderScheduler","#pendingPhase","#rafHandle","#readyPromise","#readyResolve","#initialReadyResolver","#initialReadyFired","phase","_source","#ensureReadyPromise","#flush","resolver","resolve","createResizeController","resizeState","pendingRaf","prevCursor","prevUserSelect","onMove","delta","width","justFinishedResize","onUp","hadResize","colWidth","startWidth","createElement","attrs","div","className","button","gridContentTemplate","cloneGridContent","buildGridDOM","contentWrapper","buildShellHeader","titleEl","toolbar","btn","toggleBtn","buildShellBody","body","hasPanel","isSinglePanel","gridContent","panelEl","resizeHandlePosition","panelContent","accordion","panel","sectionClasses","section","headerBtn","iconSpan","titleSpan","chevronSpan","iconToString","createShellState","shouldRenderShellHeader","renderShellHeader","toolPanelIcon","title","hasTitle","iconStr","configContents","stateContents","allContents","hasCustomContent","hasPanels","showSeparator","sortedContents","toolbarHtml","isOpen","parseLightDomShell","headerContents","parseLightDomToolButtons","rendererFactory","toolButtonsContainer","id","contentDef","parseLightDomToolPanels","tooltip","render","adapterRenderer","wrapper","existingPanel","cleanup","setupShellEventListeners","sectionId","setupToolPanelResize","onResize","shellBody","position","minWidth","startX","maxWidth","isResizing","onMouseMove","newWidth","onMouseUp","finalWidth","onMouseDown","renderCustomToolbarContents","slot","renderHeaderContent","hasLightDomContent","hasPluginContent","contentArea","existingCleanup","renderPanelContent","expandIcon","collapseIcon","panelId","isExpanded","chevron","updateToolbarActiveStates","panelToggle","updatePanelState","cleanupShellState","createShellController","initialized","controller","firstPanel","shadow","updateAccordionSectionState","otherId","otherPanel","contentEl","renderAccordionSectionContent","contentId","expanded","buildGridDOMIntoElement","shellConfig","runtimeState","hasShell","lightDomElements","lightDomSelectors","selector","sortedPanels","headerOptions","bodyOptions","shellHeader","createTouchScrollState","resetTouchState","cancelMomentum","handleTouchStart","touch","handleTouchMove","currentY","currentX","now","deltaY","deltaX","dt","scrollTop","scrollHeight","clientHeight","maxScrollY","canScrollVertically","canScrollHorizontally","scrollLeft","scrollWidth","clientWidth","maxScrollX","handleTouchEnd","startMomentumScroll","animate","scrollY","scrollX","setupTouchScrollListeners","gridContentEl","KNOWN_COLUMN_PROPERTIES","KNOWN_CONFIG_PROPERTIES","PLUGIN_IMPORT_HINTS","getImportHint","pluginName","capitalize","isDevelopment","hostname","hasPlugin","validatePluginProperties","columnProps","configProps","missingPlugins","addError","description","importHint","isConfigProperty","entry","def","errors","fields","fieldList","validatePluginConfigRules","warnings","manifest","rule","pluginConfig","warning","validatePluginDependencies","loadedPlugins","dependencies","dep","requiredPlugin","required","reason","reasonText","validatePluginIncompatibilities","pluginNames","warned","incompatibility","PluginManager","existingPlugin","otherPlugin","PluginClass","total","beforeRowIndex","adjustedStart","pluginStart","query","responses","response","focusedCell","left","right","skipScroll","DataGridElement","#renderRoot","#initialized","#rows","#configManager","#connected","#pendingUpdate","#pendingUpdateFlags","#scheduler","#scrollRaf","#pendingScrollTop","#hasScrollPlugins","#renderRowHook","#touchState","#eventAbortController","#resizeObserver","#rowHeightObserver","#idleCallbackHandle","#pooledScrollEvent","#pluginManager","#lastPluginsArray","#eventListenersAdded","#scrollAbortController","#shellState","#shellController","#resizeCleanup","#rowIdMap","#baseColumns","oldValue","#queueUpdate","#injectStyles","#updatePluginConfigs","#processColumns","#rebuildRowModel","#rowHeightObserverSetup","#setupRowHeightObserver","eventName","#emit","#setup","height","#applyAnimationConfig","#STYLE_ELEMENT_ID","#baseStyles","#pluginStylesMap","#getStyleElement","styleEl","#updateStyleElement","pluginStyles","styles","gridCssText","stylesheet","cssText","err","#initializePlugins","pluginsConfig","#injectAllPluginStyles","hasNewStyles","newPlugins","isLightDom","isApiRegistered","#collectPluginShellContributions","#destroyPlugins","pluginPanels","pluginContents","#getToolPanelRendererFactory","instanceAdapter","#render","#afterConnect","#setupLightDomHandlers","#customStyleSheets","newValue","parsed","gridRoot","defaultOpen","#setupScrollListeners","userRowHeight","#measureRowHeight","#updateAriaSelection","firstRow","cells","maxCellHeight","rowRect","measuredHeight","scrollSignal","fauxScrollbar","rowsEl","currentScrollTop","rawStart","evenAlignedStart","subPixelOffset","#onScrollBatched","isHorizontal","newFocus","rowIdx","isActiveRow","colIdx","#flushPendingUpdates","flags","#applyGridConfigUpdate","#applyRowsUpdate","#applyColumnsUpdate","#applyFitModeUpdate","#rebuildRowIdMap","getRowId","#tryResolveRowId","#resolveRowIdOrThrow","hadShell","hadToolPanel","accordionSectionsBefore","nowNeedsShell","nowHasToolPanels","toolPanelCountChanged","#updateShellHeaderInPlace","sourceColumns","visibleCols","hiddenCols","processedColumns","processedFields","originalRows","processedRows","gridConfig","enabled","#renderVisibleRows","#lastAriaRowCount","#lastAriaColCount","#updateAriaCounts","rowCount","colCount","prevRowCount","scrollEvent","cellClickEvent","handled","rowClickEvent","headerClickEvent","changes","source","changedFields","updates","anyChanged","#applyColumnState","#pendingShellRefresh","#afterShellRefresh","css","sheet","#updateAdoptedStyleSheets","customSheets","existingSheets","handleShellChange","hadTitle","hadToolButtons","hasToolButtons","newHeaderHtml","temp","newHeader","#setupShellListeners","handleColumnChange","#calculateTotalSpacerHeight","totalRows","fauxScrollHeight","viewportHeight","scrollAreaEl","scrollAreaHeight","viewportHeightDiff","pluginExtraHeight","hScrollbarPadding","force","skipAfterRender","iterations","maxIterations","extraHeightBefore","pluginAdjustedStart","visibleCount","totalHeight","extraHeightBeforeStart","newFauxHeight","newViewportHeight","newTotalHeight","PLUGIN_QUERIES","BaseGridPlugin","#abortController","userIcons","durationStr","iconKey","pluginOverride","message","GridClasses","GridDataAttrs","GridSelectors","GridCSSVars","createGrid","queryGrid","DGEvents","PluginEvents","builtInAggregators","acc","sum","customAggregators","aggregatorRegistry","ref","builtInValueAggregators","vals","getValueAggregator","aggFunc","runValueAggregator","values","registerAggregator","unregisterAggregator","getAggregator","runAggregator","listAggregators"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","sources":["../../../libs/grid/src/lib/core/types.ts","../../../libs/grid/src/lib/core/internal/columns.ts","../../../libs/grid/src/lib/core/internal/inference.ts","../../../libs/grid/src/lib/core/internal/sanitize.ts","../../../libs/grid/src/lib/core/internal/config-manager.ts","../../../libs/grid/src/lib/core/internal/utils.ts","../../../libs/grid/src/lib/core/internal/rows.ts","../../../libs/grid/src/lib/core/internal/keyboard.ts","../../../libs/grid/src/lib/core/internal/event-delegation.ts","../../../libs/grid/src/lib/core/internal/sorting.ts","../../../libs/grid/src/lib/core/internal/header.ts","../../../libs/grid/src/lib/core/internal/idle-scheduler.ts","../../../libs/grid/src/lib/core/internal/render-scheduler.ts","../../../libs/grid/src/lib/core/internal/resize.ts","../../../libs/grid/src/lib/core/internal/row-animation.ts","../../../libs/grid/src/lib/core/internal/dom-builder.ts","../../../libs/grid/src/lib/core/internal/shell.ts","../../../libs/grid/src/lib/core/internal/touch-scroll.ts","../../../libs/grid/src/lib/core/internal/validate-config.ts","../../../libs/grid/src/lib/core/plugin/plugin-manager.ts","../../../libs/grid/src/lib/core/grid.ts","../../../libs/grid/src/lib/core/plugin/types.ts","../../../libs/grid/src/lib/core/plugin/base-plugin.ts","../../../libs/grid/src/lib/core/constants.ts","../../../libs/grid/src/public.ts","../../../libs/grid/src/lib/core/internal/aggregators.ts"],"sourcesContent":["import type { PluginQuery } from './plugin/base-plugin';\nimport type { AfterCellRenderContext, AfterRowRenderContext, CellMouseEvent } from './plugin/types';\n\n/**\n * The compiled webcomponent interface for DataGrid\n */\nexport interface DataGridElement extends PublicGrid, HTMLElement {}\n\n/**\n * Public API interface for DataGrid component.\n *\n * **Property Getters vs Setters:**\n *\n * Property getters return the EFFECTIVE (resolved) value after merging all config sources.\n * This is the \"current situation\" - what consumers and plugins need to know.\n *\n * Property setters accept input values which are merged into the effective config.\n * Multiple sources can contribute (gridConfig, columns prop, light DOM, individual props).\n *\n * For example:\n * - `grid.fitMode` returns the resolved fitMode (e.g., 'stretch' even if you set undefined)\n * - `grid.columns` returns the effective columns after merging\n * - `grid.gridConfig` returns the full effective config\n */\nexport interface PublicGrid<T = any> {\n /**\n * Full config object. Setter merges with other inputs per precedence rules.\n * Getter returns the effective (resolved) config.\n */\n gridConfig?: GridConfig<T>;\n /**\n * Column definitions.\n * Getter returns effective columns (after merging config, light DOM, inference).\n */\n columns?: ColumnConfig<T>[];\n /** Current row data (after plugin processing like grouping, filtering). */\n rows?: T[];\n /** Resolves once the component has finished initial work (layout, inference). */\n ready?: () => Promise<void>;\n /** Force a layout / measurement pass (e.g. after container resize). */\n forceLayout?: () => Promise<void>;\n /** Return effective resolved config (after inference & precedence). */\n getConfig?: () => Promise<Readonly<GridConfig<T>>>;\n /** Toggle expansion state of a group row by its generated key. */\n toggleGroup?: (key: string) => Promise<void>;\n\n // Custom Styles API\n /**\n * Register custom CSS styles to be injected into the grid.\n * Use this to style custom cell renderers, editors, or detail panels.\n * @param id - Unique identifier for the style block (for removal/updates)\n * @param css - CSS string to inject\n */\n registerStyles?: (id: string, css: string) => void;\n /**\n * Remove previously registered custom styles.\n * @param id - The ID used when registering the styles\n */\n unregisterStyles?: (id: string) => void;\n /**\n * Get list of registered custom style IDs.\n */\n getRegisteredStyles?: () => string[];\n\n // Plugin API\n /**\n * Get a plugin instance by its class.\n *\n * @example\n * ```typescript\n * const selection = grid.getPlugin(SelectionPlugin);\n * if (selection) {\n * selection.selectAll();\n * }\n * ```\n */\n getPlugin?<P>(PluginClass: new (...args: any[]) => P): P | undefined;\n /**\n * Get a plugin instance by its name.\n * Prefer `getPlugin(PluginClass)` for type safety.\n */\n getPluginByName?(name: string): GridPlugin | undefined;\n\n // Shell API\n /**\n * Re-render the shell header (title, column groups, toolbar).\n * Call this after dynamically adding/removing tool panels or toolbar buttons.\n */\n refreshShellHeader?(): void;\n /**\n * Register a custom tool panel in the sidebar.\n *\n * @example\n * ```typescript\n * grid.registerToolPanel({\n * id: 'analytics',\n * title: 'Analytics',\n * icon: '📊',\n * render: (container) => {\n * container.innerHTML = '<div>Charts here...</div>';\n * }\n * });\n * ```\n */\n registerToolPanel?(panel: ToolPanelDefinition): void;\n /**\n * Unregister a previously registered tool panel.\n */\n unregisterToolPanel?(panelId: string): void;\n}\n\n/**\n * Internal-only augmented interface for DataGrid component.\n *\n * Member prefixes indicate accessibility:\n * - `_underscore` = protected members - private outside core, accessible to plugins. Marked with @internal.\n * - `__doubleUnderscore` = deeply internal members - private outside core, only for internal functions.\n *\n * @category Plugin Development\n */\nexport interface InternalGrid<T = any> extends PublicGrid<T>, GridConfig<T> {\n // Element methods available because DataGridElement extends HTMLElement\n querySelector<K extends keyof HTMLElementTagNameMap>(selectors: K): HTMLElementTagNameMap[K] | null;\n querySelector<E extends Element = Element>(selectors: string): E | null;\n querySelectorAll<K extends keyof HTMLElementTagNameMap>(selectors: K): NodeListOf<HTMLElementTagNameMap[K]>;\n querySelectorAll<E extends Element = Element>(selectors: string): NodeListOf<E>;\n _rows: T[];\n _columns: ColumnInternal<T>[];\n /** Visible columns only (excludes hidden). Use for rendering. */\n _visibleColumns: ColumnInternal<T>[];\n _headerRowEl: HTMLElement;\n _bodyEl: HTMLElement;\n _rowPool: RowElementInternal[];\n _resizeController: ResizeController;\n _sortState: { field: string; direction: 1 | -1 } | null;\n /** Original unfiltered/unprocessed rows. @internal */\n sourceRows: T[];\n /** Framework adapter instance (set by Grid directives). @internal */\n __frameworkAdapter?: FrameworkAdapter;\n __originalOrder: T[];\n __rowRenderEpoch: number;\n __didInitialAutoSize?: boolean;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n /** Cell display value cache. @internal */\n __cellDisplayCache?: Map<number, string[]>;\n /** Cache epoch for cell display values. @internal */\n __cellCacheEpoch?: number;\n /** Cached header row count for virtualization. @internal */\n __cachedHeaderRowCount?: number;\n /** Cached flag for whether grid has special columns (custom renderers, etc.). @internal */\n __hasSpecialColumns?: boolean;\n /** Cached flag for whether any plugin has renderRow hooks. @internal */\n __hasRenderRowPlugins?: boolean;\n _gridTemplate: string;\n _virtualization: VirtualState;\n _focusRow: number;\n _focusCol: number;\n /** Currently active edit row index. Injected by EditingPlugin. @internal */\n _activeEditRows?: number;\n /** Snapshots of row data before editing. Injected by EditingPlugin. @internal */\n _rowEditSnapshots?: Map<number, T>;\n /** Get all changed rows. Injected by EditingPlugin. */\n changedRows?: T[];\n /** Get IDs of all changed rows. Injected by EditingPlugin. */\n changedRowIds?: string[];\n effectiveConfig?: GridConfig<T>;\n findHeaderRow?: () => HTMLElement;\n refreshVirtualWindow: (full: boolean, skipAfterRender?: boolean) => void;\n updateTemplate?: () => void;\n findRenderedRowElement?: (rowIndex: number) => HTMLElement | null;\n /** Get a row by its ID. Implemented in grid.ts */\n getRow?: (id: string) => T | undefined;\n /** Get the unique ID for a row. Implemented in grid.ts */\n getRowId?: (row: T) => string;\n /** Update a row by ID. Implemented in grid.ts */\n updateRow?: (id: string, changes: Partial<T>, source?: UpdateSource) => void;\n /** Animate a single row. Implemented in grid.ts */\n animateRow?: (rowIndex: number, type: RowAnimationType) => void;\n /** Animate multiple rows. Implemented in grid.ts */\n animateRows?: (rowIndices: number[], type: RowAnimationType) => void;\n /** Animate a row by its ID. Implemented in grid.ts */\n animateRowById?: (rowId: string, type: RowAnimationType) => boolean;\n /** Begin bulk edit on a row. Injected by EditingPlugin. */\n beginBulkEdit?: (rowIndex: number) => void;\n /** Commit active row edit. Injected by EditingPlugin. */\n commitActiveRowEdit?: () => void;\n /** Dispatch cell click to plugin system, returns true if handled */\n _dispatchCellClick?: (event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement) => boolean;\n /** Dispatch row click to plugin system, returns true if handled */\n _dispatchRowClick?: (event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement) => boolean;\n /** Dispatch header click to plugin system, returns true if handled */\n _dispatchHeaderClick?: (event: MouseEvent, colIndex: number, headerEl: HTMLElement) => boolean;\n /** Dispatch keydown to plugin system, returns true if handled */\n _dispatchKeyDown?: (event: KeyboardEvent) => boolean;\n /** Dispatch cell mouse events for drag operations. Returns true if any plugin started a drag. */\n _dispatchCellMouseDown?: (event: CellMouseEvent) => boolean;\n /** Dispatch cell mouse move during drag. */\n _dispatchCellMouseMove?: (event: CellMouseEvent) => void;\n /** Dispatch cell mouse up to end drag. */\n _dispatchCellMouseUp?: (event: CellMouseEvent) => void;\n /** Call afterCellRender hook on all plugins. Called from rows.ts after each cell is rendered. @internal */\n _afterCellRender?: (context: AfterCellRenderContext<T>) => void;\n /** Check if any plugin has registered an afterCellRender hook. Used to skip hook call for performance. @internal */\n _hasAfterCellRenderHook?: () => boolean;\n /** Call afterRowRender hook on all plugins. Called from rows.ts after each row is rendered. @internal */\n _afterRowRender?: (context: AfterRowRenderContext<T>) => void;\n /** Check if any plugin has registered an afterRowRender hook. Used to skip hook call for performance. @internal */\n _hasAfterRowRenderHook?: () => boolean;\n /** Get horizontal scroll boundary offsets from plugins */\n _getHorizontalScrollOffsets?: (\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ) => { left: number; right: number; skipScroll?: boolean };\n /** Query all plugins with a generic query and collect responses */\n queryPlugins?: <T>(query: PluginQuery) => T[];\n /** Request emission of column-state-change event (debounced) */\n requestStateChange?: () => void;\n}\n\nexport type PrimitiveColumnType = 'number' | 'string' | 'date' | 'boolean' | 'select';\n\n/**\n * Column type - built-in primitives or custom type strings.\n * Custom types (e.g., 'currency', 'country') can have type-level defaults via `typeDefaults`.\n */\nexport type ColumnType = PrimitiveColumnType | (string & {});\n\n/**\n * Type-level defaults for renderers and editors.\n * Applied to all columns of a given type unless overridden at column level.\n *\n * @example\n * ```typescript\n * typeDefaults: {\n * country: {\n * renderer: (ctx) => {\n * const span = document.createElement('span');\n * span.innerHTML = `<img src=\"/flags/${ctx.value}.svg\" /> ${ctx.value}`;\n * return span;\n * }\n * },\n * date: {\n * editor: (ctx) => createDatePickerEditor(ctx)\n * }\n * }\n * ```\n */\nexport interface TypeDefault<TRow = unknown> {\n /** Renderer template for this type */\n renderer?: ColumnViewRenderer<TRow, unknown>;\n /** Editor template for this type (requires EditingPlugin) */\n editor?: ColumnEditorSpec<TRow, unknown>;\n /** Default editorParams for this type */\n editorParams?: Record<string, unknown>;\n}\n\n/**\n * Base contract for a column. Public; kept intentionally lean so host apps can extend via intersection types.\n * Prefer adding optional properties here only when broadly useful to most grids.\n */\nexport interface BaseColumnConfig<TRow = any, TValue = any> {\n /** Unique field key referencing property in row objects */\n field: keyof TRow & string;\n /** Visible header label; defaults to capitalized field */\n header?: string;\n /**\n * Column data type.\n *\n * Built-in types: `'string'`, `'number'`, `'date'`, `'boolean'`, `'select'`\n *\n * Custom types (e.g., `'currency'`, `'country'`) can have type-level defaults\n * via `gridConfig.typeDefaults` or framework adapter registries.\n *\n * @default Inferred from first row data\n */\n type?: ColumnType;\n /** Column width in pixels; fixed size (no flexibility) */\n width?: string | number;\n /** Minimum column width in pixels (stretch mode only); when set, column uses minmax(minWidth, 1fr) */\n minWidth?: number;\n /** Whether column can be sorted */\n sortable?: boolean;\n /** Whether column can be resized by user */\n resizable?: boolean;\n /** Optional custom comparator for sorting (a,b) -> number */\n sortComparator?: (a: TValue, b: TValue, rowA: TRow, rowB: TRow) => number;\n /** For select type - available options */\n options?: Array<{ label: string; value: unknown }> | (() => Array<{ label: string; value: unknown }>);\n /** For select - allow multi select */\n multi?: boolean;\n /** Optional formatter */\n format?: (value: TValue, row: TRow) => string;\n /** Arbitrary extra metadata */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Full column configuration including optional custom view/renderer & grouping metadata.\n */\nexport interface ColumnConfig<TRow = any> extends BaseColumnConfig<TRow, any> {\n /**\n * Optional custom cell renderer function. Alias for `viewRenderer`.\n * Can return an HTMLElement, a Node, or an HTML string (which will be sanitized).\n *\n * @example\n * ```typescript\n * // Simple string template\n * renderer: (ctx) => `<span class=\"badge\">${ctx.value}</span>`\n *\n * // DOM element\n * renderer: (ctx) => {\n * const el = document.createElement('span');\n * el.textContent = ctx.value;\n * return el;\n * }\n * ```\n */\n renderer?: ColumnViewRenderer<TRow, any>;\n /** Optional custom view renderer used instead of default text rendering */\n viewRenderer?: ColumnViewRenderer<TRow, any>;\n /** External view spec (lets host app mount any framework component) */\n externalView?: {\n component: unknown;\n props?: Record<string, unknown>;\n mount?: (options: {\n placeholder: HTMLElement;\n context: CellRenderContext<TRow, unknown>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n /** Whether the column is initially hidden */\n hidden?: boolean;\n /** Prevent this column from being hidden programmatically */\n lockVisible?: boolean;\n /**\n * Dynamic CSS class(es) for cells in this column.\n * Called for each cell during rendering. Return class names to add to the cell element.\n *\n * @example\n * ```typescript\n * // Highlight negative values\n * cellClass: (value, row, column) => value < 0 ? ['negative', 'text-red'] : []\n *\n * // Status-based styling\n * cellClass: (value) => [`status-${value}`]\n * ```\n */\n cellClass?: (value: unknown, row: TRow, column: ColumnConfig<TRow>) => string[];\n}\n\nexport type ColumnConfigMap<TRow = any> = ColumnConfig<TRow>[];\n\n/** External editor spec: tag name, factory function, or external mount spec */\nexport type ColumnEditorSpec<TRow = unknown, TValue = unknown> =\n | string // custom element tag name\n | ((context: ColumnEditorContext<TRow, TValue>) => HTMLElement | string)\n | {\n /** Arbitrary component reference (class, function, token) */\n component: unknown;\n /** Optional static props passed to mount */\n props?: Record<string, unknown>;\n /** Optional custom mount function; if provided we call it directly instead of emitting an event */\n mount?: (options: {\n placeholder: HTMLElement;\n context: ColumnEditorContext<TRow, TValue>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n\n/**\n * Context object provided to editor factories allowing mutation (commit/cancel) of a cell value.\n */\nexport interface ColumnEditorContext<TRow = any, TValue = any> {\n /** Underlying full row object for the active edit. */\n row: TRow;\n /** Current cell value (mutable only via commit). */\n value: TValue;\n /** Field name being edited. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n /** Accept the edit; triggers change tracking + rerender. */\n commit: (newValue: TValue) => void;\n /** Abort edit without persisting changes. */\n cancel: () => void;\n}\n\n/**\n * Context passed to custom view renderers (pure display – no commit helpers).\n */\nexport interface CellRenderContext<TRow = any, TValue = any> {\n /** Row object for the cell being rendered. */\n row: TRow;\n /** Value at field. */\n value: TValue;\n /** Field key. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n /**\n * The cell DOM element being rendered into.\n * Framework adapters can use this to cache per-cell state (e.g., React roots).\n * @internal\n */\n cellEl?: HTMLElement;\n}\n\nexport type ColumnViewRenderer<TRow = unknown, TValue = unknown> = (\n ctx: CellRenderContext<TRow, TValue>,\n) => Node | string | void | null;\n\n/**\n * Framework adapter interface for handling framework-specific component instantiation.\n * Allows framework libraries (Angular, React, Vue) to register handlers that convert\n * declarative light DOM elements into functional renderers/editors.\n *\n * @example\n * ```typescript\n * // In @toolbox-web/grid-angular\n * class AngularGridAdapter implements FrameworkAdapter {\n * canHandle(element: HTMLElement): boolean {\n * return element.tagName.startsWith('APP-');\n * }\n * createRenderer(element: HTMLElement): ColumnViewRenderer {\n * return (ctx) => {\n * // Angular-specific instantiation logic\n * const componentRef = createComponent(...);\n * componentRef.setInput('value', ctx.value);\n * return componentRef.location.nativeElement;\n * };\n * }\n * createEditor(element: HTMLElement): ColumnEditorSpec {\n * return (ctx) => {\n * // Angular-specific editor with commit/cancel\n * const componentRef = createComponent(...);\n * componentRef.setInput('value', ctx.value);\n * // Subscribe to commit/cancel outputs\n * return componentRef.location.nativeElement;\n * };\n * }\n * }\n *\n * // User registers adapter once in their app\n * GridElement.registerAdapter(new AngularGridAdapter(injector, appRef));\n * ```\n * @category Framework Adapters\n */\nexport interface FrameworkAdapter {\n /**\n * Determines if this adapter can handle the given element.\n * Typically checks tag name, attributes, or other conventions.\n */\n canHandle(element: HTMLElement): boolean;\n\n /**\n * Creates a view renderer function from a light DOM element.\n * The renderer receives cell context and returns DOM or string.\n */\n createRenderer<TRow = unknown, TValue = unknown>(element: HTMLElement): ColumnViewRenderer<TRow, TValue>;\n\n /**\n * Creates an editor spec from a light DOM element.\n * The editor receives context with commit/cancel and returns DOM.\n */\n createEditor<TRow = unknown, TValue = unknown>(element: HTMLElement): ColumnEditorSpec<TRow, TValue>;\n\n /**\n * Creates a tool panel renderer from a light DOM element.\n * The renderer receives a container element and optionally returns a cleanup function.\n */\n createToolPanelRenderer?(element: HTMLElement): ((container: HTMLElement) => void | (() => void)) | undefined;\n\n /**\n * Gets type-level defaults from an application-level registry.\n * Used by Angular's `GridTypeRegistry` and React's `GridTypeProvider`.\n *\n * @param type - The column type (e.g., 'date', 'currency', 'country')\n * @returns Type defaults for renderer/editor, or undefined if not registered\n */\n getTypeDefault?<TRow = unknown>(type: string): TypeDefault<TRow> | undefined;\n}\n\n// #region Internal-only augmented types (not re-exported publicly)\n\n/**\n * Column internal properties used during light DOM parsing.\n * Stores attribute-based names before they're resolved to actual functions.\n * @internal\n */\nexport interface ColumnParsedAttributes {\n /** Editor name from `editor` attribute (resolved later) */\n __editorName?: string;\n /** Renderer name from `renderer` attribute (resolved later) */\n __rendererName?: string;\n}\n\n/**\n * Extended column config used internally.\n * Includes all internal properties needed during grid lifecycle.\n *\n * @category Plugin Development\n * @internal\n */\nexport interface ColumnInternal<T = any> extends ColumnConfig<T>, ColumnParsedAttributes {\n __autoSized?: boolean;\n __userResized?: boolean;\n __renderedWidth?: number;\n /** Original configured width (for reset on double-click) */\n __originalWidth?: number;\n __viewTemplate?: HTMLElement;\n __editorTemplate?: HTMLElement;\n __headerTemplate?: HTMLElement;\n __compiledView?: CompiledViewFunction<T>;\n __compiledEditor?: (ctx: EditorExecContext<T>) => string;\n}\n\n/**\n * Row element with internal tracking properties.\n * Used during virtualization and row pooling.\n *\n * @category Plugin Development\n * @internal\n */\nexport interface RowElementInternal extends HTMLElement {\n /** Epoch marker for row render invalidation */\n __epoch?: number;\n /** Reference to the row data object for change detection */\n __rowDataRef?: unknown;\n /** Count of cells currently in edit mode */\n __editingCellCount?: number;\n /** Flag indicating this is a custom-rendered row (group row, etc.) */\n __isCustomRow?: boolean;\n}\n\n/**\n * Type-safe access to element.part API (DOMTokenList-like).\n * Used for CSS ::part styling support.\n * @internal\n */\nexport interface ElementWithPart {\n part?: DOMTokenList;\n}\n\n/**\n * Compiled view function type with optional blocked flag.\n * The __blocked flag is set when a template contains unsafe expressions.\n *\n * @category Plugin Development\n * @internal\n */\nexport interface CompiledViewFunction<T = any> {\n (ctx: CellContext<T>): string;\n /** Set to true when template was blocked due to unsafe expressions */\n __blocked?: boolean;\n}\n\n/**\n * Runtime cell context used internally for compiled template execution.\n *\n * @category Plugin Development\n */\nexport interface CellContext<T = any> {\n row: T;\n value: unknown;\n field: string;\n column: ColumnInternal<T>;\n}\n\n/**\n * Internal editor execution context extending the generic cell context with commit helpers.\n *\n * @category Plugin Development\n */\nexport interface EditorExecContext<T = any> extends CellContext<T> {\n commit: (newValue: unknown) => void;\n cancel: () => void;\n}\n\n/**\n * Controller managing drag-based column resize lifecycle.\n *\n * @category Plugin Development\n */\nexport interface ResizeController {\n start: (e: MouseEvent, colIndex: number, cell: HTMLElement) => void;\n /** Reset a column to its configured width (or auto-size if none configured). */\n resetColumn: (colIndex: number) => void;\n dispose: () => void;\n /** True while a resize drag is in progress (used to suppress header click/sort). */\n isResizing: boolean;\n}\n\n/**\n * Virtual window bookkeeping; modified in-place as scroll position changes.\n *\n * @category Plugin Development\n */\nexport interface VirtualState {\n enabled: boolean;\n rowHeight: number;\n /** Threshold for bypassing virtualization (renders all rows if totalRows <= bypassThreshold) */\n bypassThreshold: number;\n start: number;\n end: number;\n /** Faux scrollbar element that provides scroll events (AG Grid pattern) */\n container: HTMLElement | null;\n /** Rows viewport element for measuring visible area height */\n viewportEl: HTMLElement | null;\n /** Spacer element inside faux scrollbar for setting virtual height */\n totalHeightEl: HTMLElement | null;\n}\n\n// RowElementInternal is now defined earlier in the file with all internal properties\n\n/**\n * Union type for input-like elements that have a `value` property.\n * Covers standard form elements and custom elements with value semantics.\n *\n * @category Plugin Development\n * @internal\n */\nexport type InputLikeElement = HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement | { value: unknown };\n// #endregion\n\n// #region Grouping & Footer Public Types\n/**\n * Group row rendering customization options.\n * Used within grouping-rows plugin config for presentation of group rows.\n */\nexport interface RowGroupRenderConfig {\n /** If true, group rows span all columns (single full-width cell). Default false. */\n fullWidth?: boolean;\n /** Optional label formatter override. Receives raw group value + depth. */\n formatLabel?: (value: unknown, depth: number, key: string) => string;\n /** Optional aggregate overrides per field for group summary cells (only when not fullWidth). */\n aggregators?: Record<string, AggregatorRef>;\n /** Additional CSS class applied to each group row root element. */\n class?: string;\n}\n\nexport type AggregatorRef = string | ((rows: unknown[], field: string, column?: unknown) => unknown);\n\n/** Result of automatic column inference from sample rows. */\nexport interface InferredColumnResult<TRow = unknown> {\n columns: ColumnConfigMap<TRow>;\n typeMap: Record<string, ColumnType>;\n}\n\nexport const FitModeEnum = {\n STRETCH: 'stretch',\n FIXED: 'fixed',\n} as const;\nexport type FitMode = (typeof FitModeEnum)[keyof typeof FitModeEnum]; // evaluates to 'stretch' | 'fixed'\n// #endregion\n\n// #region Plugin Interface\n/**\n * Minimal plugin interface for type-checking.\n * This interface is defined here to avoid circular imports with BaseGridPlugin.\n * All plugins must satisfy this shape (BaseGridPlugin implements it).\n *\n * @category Plugin Development\n */\nexport interface GridPlugin {\n /** Unique plugin identifier */\n readonly name: string;\n /** Plugin version */\n readonly version: string;\n /** CSS styles to inject into the grid */\n readonly styles?: string;\n}\n// #endregion\n\n// #region Grid Config\n/**\n * Grid configuration object - the **single source of truth** for grid behavior.\n *\n * Users can configure the grid via multiple input methods, all of which converge\n * into an effective `GridConfig` internally:\n *\n * **Configuration Input Methods:**\n * - `gridConfig` property - direct assignment of this object\n * - `columns` property - shorthand for `gridConfig.columns`\n * - `fitMode` property - shorthand for `gridConfig.fitMode`\n * - Light DOM `<tbw-grid-column>` - declarative columns (merged into `columns`)\n * - Light DOM `<tbw-grid-header>` - declarative shell header (merged into `shell.header`)\n *\n * **Precedence (when same property set multiple ways):**\n * Individual props (`fitMode`) > `columns` prop > Light DOM > `gridConfig`\n *\n * @example\n * ```ts\n * // Via gridConfig (recommended for complex setups)\n * grid.gridConfig = {\n * columns: [{ field: 'name' }, { field: 'age' }],\n * fitMode: 'stretch',\n * plugins: [new SelectionPlugin()],\n * shell: { header: { title: 'My Grid' } }\n * };\n *\n * // Via individual props (convenience for simple cases)\n * grid.columns = [{ field: 'name' }, { field: 'age' }];\n * grid.fitMode = 'stretch';\n * ```\n */\nexport interface GridConfig<TRow = any> {\n /** Column definitions. Can also be set via `columns` prop or `<tbw-grid-column>` light DOM. */\n columns?: ColumnConfigMap<TRow>;\n /**\n * Dynamic CSS class(es) for data rows.\n * Called for each row during rendering. Return class names to add to the row element.\n *\n * @example\n * ```typescript\n * // Highlight inactive rows\n * rowClass: (row) => row.active ? [] : ['inactive', 'dimmed']\n *\n * // Status-based row styling\n * rowClass: (row) => [`priority-${row.priority}`]\n * ```\n */\n rowClass?: (row: TRow) => string[];\n /** Sizing mode for columns. Can also be set via `fitMode` prop. */\n fitMode?: FitMode;\n /**\n * Edit trigger mode. Requires `EditingPlugin` to be loaded.\n *\n * Configure via `new EditingPlugin({ editOn: 'click' })` or set on gridConfig.\n * Plugin config takes precedence over gridConfig.\n *\n * - `'click'`: Single click to edit\n * - `'dblclick'`: Double-click to edit (default)\n * - `'manual'`: Only via programmatic API (beginEdit)\n * - `false`: Disable editing entirely\n */\n editOn?: 'click' | 'dblclick' | 'manual' | false;\n /**\n * Row height in pixels for virtualization calculations.\n * The virtualization system assumes uniform row heights for performance.\n *\n * If not specified, the grid measures the first rendered row's height,\n * which respects the CSS variable `--tbw-row-height` set by themes.\n *\n * Set this explicitly when:\n * - Row content may wrap to multiple lines (also set `--tbw-cell-white-space: normal`)\n * - Using custom row templates with variable content\n * - You want to override theme-defined row height\n *\n * @default Auto-measured from first row (respects --tbw-row-height CSS variable)\n *\n * @example\n * ```ts\n * // Fixed height for rows that may wrap to 2 lines\n * gridConfig = { rowHeight: 56 };\n * ```\n */\n rowHeight?: number;\n /**\n * Array of plugin instances.\n * Each plugin is instantiated with its configuration and attached to this grid.\n *\n * @example\n * ```ts\n * plugins: [\n * new SelectionPlugin({ mode: 'range' }),\n * new MultiSortPlugin(),\n * new FilteringPlugin({ debounceMs: 150 }),\n * ]\n * ```\n */\n plugins?: GridPlugin[];\n\n /**\n * Saved column state to restore on initialization.\n * Includes order, width, visibility, sort, and plugin-contributed state.\n */\n columnState?: GridColumnState;\n\n /**\n * Shell configuration for header bar and tool panels.\n * When configured, adds an optional wrapper with title, toolbar, and collapsible side panels.\n */\n shell?: ShellConfig;\n\n /**\n * Grid-wide icon configuration.\n * Provides consistent icons across all plugins (tree, grouping, sorting, etc.).\n * Plugins will use these by default but can override with their own config.\n */\n icons?: GridIcons;\n\n /**\n * Grid-wide animation configuration.\n * Controls animations for expand/collapse, reordering, and other visual transitions.\n * Individual plugins can override these defaults in their own config.\n */\n animation?: AnimationConfig;\n\n /**\n * Custom sort handler for full control over sorting behavior.\n *\n * When provided, this handler is called instead of the built-in sorting logic.\n * Enables custom sorting algorithms, server-side sorting, or plugin-specific sorting.\n *\n * The handler receives:\n * - `rows`: Current row array to sort\n * - `sortState`: Sort field and direction (1 = asc, -1 = desc)\n * - `columns`: Column configurations (for accessing sortComparator)\n *\n * Return the sorted array (sync) or a Promise that resolves to the sorted array (async).\n * For server-side sorting, return a Promise that resolves when data is fetched.\n *\n * @example\n * ```ts\n * // Custom stable sort\n * sortHandler: (rows, state, cols) => {\n * return stableSort(rows, (a, b) => compare(a[state.field], b[state.field]) * state.direction);\n * }\n *\n * // Server-side sorting\n * sortHandler: async (rows, state) => {\n * const response = await fetch(`/api/data?sort=${state.field}&dir=${state.direction}`);\n * return response.json();\n * }\n * ```\n */\n sortHandler?: SortHandler<TRow>;\n\n /**\n * Function to extract a unique identifier from a row.\n * Used by `updateRow()`, `getRow()`, and ID-based tracking.\n *\n * If not provided, falls back to `row.id` or `row._id` if present.\n * Rows without IDs are silently skipped during map building.\n * Only throws when explicitly calling `getRowId()` or `updateRow()` on a row without an ID.\n *\n * @example\n * ```ts\n * // Simple field\n * getRowId: (row) => row.id\n *\n * // Composite key\n * getRowId: (row) => `${row.voyageId}-${row.legNumber}`\n *\n * // UUID field\n * getRowId: (row) => row.uuid\n * ```\n */\n getRowId?: (row: TRow) => string;\n\n /**\n * Type-level renderer and editor defaults.\n *\n * Keys can be:\n * - Built-in types: `'string'`, `'number'`, `'date'`, `'boolean'`, `'select'`\n * - Custom types: `'currency'`, `'country'`, `'status'`, etc.\n *\n * Resolution order (highest priority first):\n * 1. Column-level (`column.renderer` / `column.editor`)\n * 2. Grid-level (`gridConfig.typeDefaults[column.type]`)\n * 3. App-level (Angular `GridTypeRegistry`, React `GridTypeProvider`)\n * 4. Built-in (checkbox for boolean, select for select, etc.)\n * 5. Fallback (plain text / text input)\n *\n * @example\n * ```typescript\n * typeDefaults: {\n * date: { editor: myDatePickerEditor },\n * country: {\n * renderer: (ctx) => {\n * const span = document.createElement('span');\n * span.innerHTML = `<img src=\"/flags/${ctx.value}.svg\" /> ${ctx.value}`;\n * return span;\n * },\n * editor: (ctx) => createCountrySelect(ctx)\n * }\n * }\n * ```\n */\n typeDefaults?: Record<string, TypeDefault<TRow>>;\n}\n// #endregion\n\n// #region Animation\n\n/**\n * Sort state passed to custom sort handlers.\n */\nexport interface SortState {\n /** Field to sort by */\n field: string;\n /** Sort direction: 1 = ascending, -1 = descending */\n direction: 1 | -1;\n}\n\n/**\n * Custom sort handler function signature.\n *\n * @param rows - Current row array to sort\n * @param sortState - Sort field and direction\n * @param columns - Column configurations (for accessing sortComparator)\n * @returns Sorted array (sync) or Promise resolving to sorted array (async)\n */\nexport type SortHandler<TRow = any> = (\n rows: TRow[],\n sortState: SortState,\n columns: ColumnConfig<TRow>[],\n) => TRow[] | Promise<TRow[]>;\n\n// #region Data Update Management\n\n/**\n * Indicates the origin of a data change.\n * Used to prevent infinite loops in cascade update handlers.\n *\n * - `'user'`: Direct user interaction via EditingPlugin (typing, selecting)\n * - `'cascade'`: Triggered by `updateRow()` in an event handler\n * - `'api'`: External programmatic update via `grid.updateRow()`\n *\n * @category Data Management\n */\nexport type UpdateSource = 'user' | 'cascade' | 'api';\n\n/**\n * Detail for cell-change event (emitted by core after mutation).\n * This is an informational event that fires for ALL data mutations.\n *\n * @category Events\n */\nexport interface CellChangeDetail<TRow = unknown> {\n /** The row object (after mutation) */\n row: TRow;\n /** Stable row identifier */\n rowId: string;\n /** Current index in rows array */\n rowIndex: number;\n /** Field that changed */\n field: string;\n /** Value before change */\n oldValue: unknown;\n /** Value after change */\n newValue: unknown;\n /** All changes passed to updateRow/updateRows (for context) */\n changes: Partial<TRow>;\n /** Origin of this change */\n source: UpdateSource;\n}\n\n/**\n * Batch update specification for updateRows().\n *\n * @category Data Management\n */\nexport interface RowUpdate<TRow = unknown> {\n /** Row identifier (from getRowId) */\n id: string;\n /** Fields to update */\n changes: Partial<TRow>;\n}\n\n// #endregion\n\n/**\n * Animation behavior mode.\n * - `true` or `'on'`: Animations always enabled\n * - `false` or `'off'`: Animations always disabled\n * - `'reduced-motion'`: Respects `prefers-reduced-motion` media query (default)\n */\nexport type AnimationMode = boolean | 'on' | 'off' | 'reduced-motion';\n\n/**\n * Animation style for visual transitions.\n * - `'slide'`: Slide/transform animation (e.g., expand down, slide left/right)\n * - `'fade'`: Opacity fade animation\n * - `'flip'`: FLIP technique for position changes (First, Last, Invert, Play)\n * - `false`: No animation for this specific feature\n */\nexport type AnimationStyle = 'slide' | 'fade' | 'flip' | false;\n\n/**\n * Animation style for expand/collapse operations.\n * Subset of AnimationStyle - excludes 'flip' which is for position changes.\n * - `'slide'`: Slide down/up animation for expanding/collapsing content\n * - `'fade'`: Fade in/out animation\n * - `false`: No animation\n */\nexport type ExpandCollapseAnimation = 'slide' | 'fade' | false;\n\n/**\n * Type of row animation.\n * - `'change'`: Flash highlight when row data changes (e.g., after cell edit)\n * - `'insert'`: Slide-in animation for newly added rows\n * - `'remove'`: Fade-out animation for rows being removed\n */\nexport type RowAnimationType = 'change' | 'insert' | 'remove';\n\n/**\n * Grid-wide animation configuration.\n * Controls global animation behavior - individual plugins define their own animation styles.\n * Duration and easing values set corresponding CSS variables on the grid element.\n */\nexport interface AnimationConfig {\n /**\n * Global animation mode.\n * @default 'reduced-motion'\n */\n mode?: AnimationMode;\n\n /**\n * Default animation duration in milliseconds.\n * Sets `--tbw-animation-duration` CSS variable.\n * @default 200\n */\n duration?: number;\n\n /**\n * Default easing function.\n * Sets `--tbw-animation-easing` CSS variable.\n * @default 'ease-out'\n */\n easing?: string;\n}\n\n/** Default animation configuration */\nexport const DEFAULT_ANIMATION_CONFIG: Required<Omit<AnimationConfig, 'sort'>> = {\n mode: 'reduced-motion',\n duration: 200,\n easing: 'ease-out',\n};\n\n// #endregion\n\n// #region Grid Icons\n\n/** Icon value - can be a string (text/HTML) or HTMLElement */\nexport type IconValue = string | HTMLElement;\n\n/**\n * Grid-wide icon configuration.\n * All icons are optional - sensible defaults are used when not specified.\n */\nexport interface GridIcons {\n /** Expand icon for collapsed items (trees, groups, details). Default: '▶' */\n expand?: IconValue;\n /** Collapse icon for expanded items (trees, groups, details). Default: '▼' */\n collapse?: IconValue;\n /** Sort ascending indicator. Default: '▲' */\n sortAsc?: IconValue;\n /** Sort descending indicator. Default: '▼' */\n sortDesc?: IconValue;\n /** Sort neutral/unsorted indicator. Default: '⇅' */\n sortNone?: IconValue;\n /** Submenu arrow for context menus. Default: '▶' */\n submenuArrow?: IconValue;\n /** Drag handle icon for reordering. Default: '⋮⋮' */\n dragHandle?: IconValue;\n /** Tool panel toggle icon in toolbar. Default: '☰' */\n toolPanel?: IconValue;\n}\n\n/** Default icons used when not overridden */\nexport const DEFAULT_GRID_ICONS: Required<GridIcons> = {\n expand: '▶',\n collapse: '▼',\n sortAsc: '▲',\n sortDesc: '▼',\n sortNone: '⇅',\n submenuArrow: '▶',\n dragHandle: '⋮⋮',\n toolPanel: '☰',\n};\n// #endregion\n\n// #region Shell Configuration\n\n/**\n * Shell configuration for the grid's optional header bar and tool panels.\n */\nexport interface ShellConfig {\n /** Shell header bar configuration */\n header?: ShellHeaderConfig;\n /** Tool panel configuration */\n toolPanel?: ToolPanelConfig;\n /**\n * Registered tool panels (from plugins, API, or Light DOM).\n * These are the actual panel definitions that can be opened.\n * @internal Set by ConfigManager during merge\n */\n toolPanels?: ToolPanelDefinition[];\n /**\n * Registered header content sections (from plugins or API).\n * Content rendered in the center of the shell header.\n * @internal Set by ConfigManager during merge\n */\n headerContents?: HeaderContentDefinition[];\n}\n\n/**\n * Shell header bar configuration\n */\nexport interface ShellHeaderConfig {\n /** Grid title displayed on the left (optional) */\n title?: string;\n /** Custom toolbar content (rendered before tool panel toggle) */\n toolbarContents?: ToolbarContentDefinition[];\n /**\n * Light DOM header content elements (parsed from <tbw-grid-header> children).\n * @internal Set by ConfigManager during merge\n */\n lightDomContent?: HTMLElement[];\n /**\n * Whether a tool buttons container was found in light DOM.\n * @internal Set by ConfigManager during merge\n */\n hasToolButtonsContainer?: boolean;\n}\n\n/**\n * Tool panel configuration\n */\nexport interface ToolPanelConfig {\n /** Panel position: 'left' | 'right' (default: 'right') */\n position?: 'left' | 'right';\n /** Default panel width in pixels (default: 280) */\n width?: number;\n /** Panel ID to open by default on load */\n defaultOpen?: string;\n /** Whether to persist open/closed state (requires Column State Events) */\n persistState?: boolean;\n}\n\n/**\n * Toolbar content definition for the shell header toolbar area.\n * Register via `registerToolbarContent()` or use light DOM `<tbw-grid-tool-buttons>`.\n *\n * @example\n * ```typescript\n * grid.registerToolbarContent({\n * id: 'my-toolbar',\n * order: 10,\n * render: (container) => {\n * const btn = document.createElement('button');\n * btn.textContent = 'Refresh';\n * btn.onclick = () => console.log('clicked');\n * container.appendChild(btn);\n * return () => btn.remove();\n * },\n * });\n * ```\n */\nexport interface ToolbarContentDefinition {\n /** Unique content ID */\n id: string;\n /** Content factory - called once when shell header renders */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when content is removed (for cleanup) */\n onDestroy?: () => void;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n}\n\n/**\n * Tool panel definition registered by plugins or consumers.\n */\nexport interface ToolPanelDefinition {\n /** Unique panel ID */\n id: string;\n /** Panel title shown in accordion header */\n title: string;\n /** Icon for accordion section header (optional, emoji or SVG) */\n icon?: string;\n /** Tooltip for accordion section header */\n tooltip?: string;\n /** Panel content factory - called when panel section opens */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when panel closes (for cleanup) */\n onClose?: () => void;\n /** Panel order priority (lower = first, default: 100) */\n order?: number;\n}\n\n/**\n * Header content definition for plugins contributing to shell header center section.\n */\nexport interface HeaderContentDefinition {\n /** Unique content ID */\n id: string;\n /** Content factory - called once when shell header renders */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when content is removed (for cleanup) */\n onDestroy?: () => void;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n}\n// #endregion\n\n// #region Column State (Persistence)\n\n/**\n * State for a single column. Captures user-driven changes at runtime.\n * Plugins can extend this interface via module augmentation to add their own state.\n *\n * @example\n * ```ts\n * // In filtering plugin\n * declare module '@toolbox-web/grid' {\n * interface ColumnState {\n * filter?: FilterValue;\n * }\n * }\n * ```\n */\nexport interface ColumnState {\n /** Column field identifier */\n field: string;\n /** Position index after reordering (0-based) */\n order: number;\n /** Width in pixels (undefined = use default) */\n width?: number;\n /** Visibility state */\n visible: boolean;\n /** Sort state (undefined = not sorted) */\n sort?: ColumnSortState;\n}\n\n/**\n * Sort state for a column\n */\nexport interface ColumnSortState {\n /** Sort direction */\n direction: 'asc' | 'desc';\n /** Priority for multi-sort (0 = primary, 1 = secondary, etc.) */\n priority: number;\n}\n\n/**\n * Complete grid column state for persistence.\n * Contains state for all columns, including plugin-contributed properties.\n */\nexport interface GridColumnState {\n columns: ColumnState[];\n}\n// #endregion\n\n// #region Public Event Detail Interfaces\n/**\n * Event detail for cell value commit.\n *\n * @category Events\n */\nexport interface CellCommitDetail<TRow = unknown> {\n /** The row object (not yet mutated if event is cancelable). */\n row: TRow;\n /** Stable row identifier (from getRowId). */\n rowId: string;\n /** Field name whose value changed. */\n field: string;\n /** Previous value before change. */\n oldValue: unknown;\n /** New value to be stored. */\n value: unknown;\n /** Index of the row in current data set. */\n rowIndex: number;\n /** All rows that have at least one committed change (snapshot list). */\n changedRows: TRow[];\n /** IDs of changed rows. */\n changedRowIds: string[];\n /** True if this row just entered the changed set. */\n firstTimeForRow: boolean;\n /**\n * Update other fields in this row.\n * Convenience wrapper for grid.updateRow(rowId, changes, 'cascade').\n * Useful for cascade updates (e.g., calculating totals).\n */\n updateRow: (changes: Partial<TRow>) => void;\n}\n\n/**\n * Detail payload for a committed row edit (may or may not include changes).\n *\n * @category Events\n */\nexport interface RowCommitDetail<TRow = unknown> {\n /** Row index that lost edit focus. */\n rowIndex: number;\n /** Stable row identifier (from getRowId). */\n rowId: string;\n /** Row object reference. */\n row: TRow;\n /** Whether any cell changes were actually committed in this row during the session. */\n changed: boolean;\n /** Current changed row collection. */\n changedRows: TRow[];\n /** IDs of changed rows. */\n changedRowIds: string[];\n}\n\n/**\n * Emitted when the changed rows tracking set is cleared programmatically.\n *\n * @category Events\n */\nexport interface ChangedRowsResetDetail<TRow = unknown> {\n /** New (empty) changed rows array after reset. */\n rows: TRow[];\n /** IDs of changed rows (likely empty). */\n ids: string[];\n}\n\n/**\n * Detail for a cell click event.\n * Provides full context about the clicked cell including row data.\n *\n * @category Events\n */\nexport interface CellClickDetail<TRow = unknown> {\n /** Zero-based row index of the clicked cell. */\n rowIndex: number;\n /** Zero-based column index of the clicked cell. */\n colIndex: number;\n /** Field name of the clicked column. */\n field: string;\n /** Cell value at the clicked position. */\n value: unknown;\n /** Full row data object. */\n row: TRow;\n /** The clicked cell element. */\n cellEl: HTMLElement;\n /** The original mouse event. */\n originalEvent: MouseEvent;\n}\n\n/**\n * Detail for a row click event.\n * Provides context about the clicked row.\n *\n * @category Events\n */\nexport interface RowClickDetail<TRow = unknown> {\n /** Zero-based row index of the clicked row. */\n rowIndex: number;\n /** Full row data object. */\n row: TRow;\n /** The clicked row element. */\n rowEl: HTMLElement;\n /** The original mouse event. */\n originalEvent: MouseEvent;\n}\n\n/**\n * Detail for a sort change (direction 0 indicates cleared sort).\n *\n * @category Events\n */\nexport interface SortChangeDetail {\n /** Sorted field key. */\n field: string;\n /** Direction: 1 ascending, -1 descending, 0 cleared. */\n direction: 1 | -1 | 0;\n}\n\n/**\n * Column resize event detail containing final pixel width.\n *\n * @category Events\n */\nexport interface ColumnResizeDetail {\n /** Resized column field key. */\n field: string;\n /** New width in pixels. */\n width: number;\n}\n\n/**\n * Trigger type for cell activation.\n * - `'keyboard'`: Enter key pressed on focused cell\n * - `'pointer'`: Mouse/touch/pen click on cell\n *\n * @category Events\n */\nexport type CellActivateTrigger = 'keyboard' | 'pointer';\n\n/**\n * Fired when a cell is activated by user interaction (Enter key or click).\n * Unified event for both keyboard and pointer activation.\n *\n * @category Events\n */\nexport interface CellActivateDetail<TRow = unknown> {\n /** Zero-based row index of the activated cell. */\n rowIndex: number;\n /** Zero-based column index of the activated cell. */\n colIndex: number;\n /** Field name of the activated column. */\n field: string;\n /** Cell value at the activated position. */\n value: unknown;\n /** Full row data object. */\n row: TRow;\n /** The activated cell element. */\n cellEl: HTMLElement;\n /** What triggered the activation. */\n trigger: CellActivateTrigger;\n /** The original event (KeyboardEvent for keyboard, MouseEvent/PointerEvent for pointer). */\n originalEvent: KeyboardEvent | MouseEvent | PointerEvent;\n}\n\n/**\n * @deprecated Use `CellActivateDetail` instead. Will be removed in next major version.\n * Kept for backwards compatibility.\n *\n * @category Events\n */\nexport interface ActivateCellDetail {\n /** Zero-based row index now focused. */\n row: number;\n /** Zero-based column index now focused. */\n col: number;\n}\n\n/**\n * Event detail for mounting external view renderers.\n *\n * @category Events\n */\nexport interface ExternalMountViewDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: { row: TRow; value: unknown; field: string; column: unknown };\n}\n\n/**\n * Event detail for mounting external editor renderers.\n *\n * @category Events\n */\nexport interface ExternalMountEditorDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: {\n row: TRow;\n value: unknown;\n field: string;\n column: unknown;\n commit: (v: unknown) => void;\n cancel: () => void;\n };\n}\n\n/**\n * Maps event names to their detail payload types.\n *\n * @category Events\n */\nexport interface DataGridEventMap<TRow = unknown> {\n 'cell-click': CellClickDetail<TRow>;\n 'row-click': RowClickDetail<TRow>;\n 'cell-activate': CellActivateDetail<TRow>;\n 'cell-change': CellChangeDetail<TRow>;\n 'cell-commit': CellCommitDetail<TRow>;\n 'row-commit': RowCommitDetail<TRow>;\n 'changed-rows-reset': ChangedRowsResetDetail<TRow>;\n 'mount-external-view': ExternalMountViewDetail<TRow>;\n 'mount-external-editor': ExternalMountEditorDetail<TRow>;\n 'sort-change': SortChangeDetail;\n 'column-resize': ColumnResizeDetail;\n /** @deprecated Use 'cell-activate' instead */\n 'activate-cell': ActivateCellDetail;\n 'column-state-change': GridColumnState;\n}\n\n/**\n * Extracts the event detail type for a given event name.\n *\n * @category Events\n */\nexport type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = DataGridEventMap<TRow>[K];\n\n/**\n * Custom event type for DataGrid events with typed detail payload.\n *\n * @category Events\n */\nexport type DataGridCustomEvent<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = CustomEvent<\n DataGridEventMap<TRow>[K]\n>;\n\n// Internal code now reuses the public ColumnEditorContext; provide alias for backward compatibility\nexport type EditorContext<T = unknown> = ColumnEditorContext<T, unknown>;\n\n/**\n * Template evaluation context for dynamic templates.\n *\n * @category Plugin Development\n */\nexport interface EvalContext {\n value: unknown;\n row: Record<string, unknown> | null;\n}\n// #endregion\n","import type { ColumnConfig, ColumnInternal, ElementWithPart, InternalGrid, PrimitiveColumnType } from '../types';\nimport { FitModeEnum } from '../types';\n\n/** Global DataGridElement class (may or may not be registered) */\ninterface DataGridElementClass {\n getAdapters?: () => Array<{\n canHandle: (el: HTMLElement) => boolean;\n createRenderer: (el: HTMLElement) => ((ctx: unknown) => Node | string | void) | undefined;\n createEditor: (el: HTMLElement) => ((ctx: unknown) => HTMLElement | string) | undefined;\n }>;\n}\n\n/**\n * Parse `<tbw-grid-column>` elements from the host light DOM into column config objects,\n * capturing template elements for later cloning / compilation.\n */\nexport function parseLightDomColumns(host: HTMLElement): ColumnInternal[] {\n const domColumns = Array.from(host.querySelectorAll('tbw-grid-column')) as HTMLElement[];\n return domColumns\n .map((el) => {\n const field = el.getAttribute('field') || '';\n if (!field) return null;\n const rawType = el.getAttribute('type') || undefined;\n const allowedTypes = new Set<PrimitiveColumnType>(['number', 'string', 'date', 'boolean', 'select']);\n const type =\n rawType && allowedTypes.has(rawType as PrimitiveColumnType) ? (rawType as PrimitiveColumnType) : undefined;\n const header = el.getAttribute('header') || undefined;\n const sortable = el.hasAttribute('sortable');\n const editable = el.hasAttribute('editable');\n const config: ColumnInternal = { field, type, header, sortable, editable };\n\n // Parse width attribute (supports px values, percentages, or plain numbers)\n const widthAttr = el.getAttribute('width');\n if (widthAttr) {\n const numericWidth = parseFloat(widthAttr);\n if (!isNaN(numericWidth) && /^\\d+(\\.\\d+)?$/.test(widthAttr.trim())) {\n config.width = numericWidth;\n } else {\n config.width = widthAttr; // e.g. \"100px\", \"20%\", \"1fr\"\n }\n }\n\n // Parse minWidth attribute (numeric only)\n const minWidthAttr = el.getAttribute('minWidth') || el.getAttribute('min-width');\n if (minWidthAttr) {\n const numericMinWidth = parseFloat(minWidthAttr);\n if (!isNaN(numericMinWidth)) {\n config.minWidth = numericMinWidth;\n }\n }\n\n if (el.hasAttribute('resizable')) config.resizable = true;\n if (el.hasAttribute('sizable')) config.resizable = true; // legacy attribute support\n\n // Parse editor and renderer attribute names for programmatic lookup\n const editorName = el.getAttribute('editor');\n const rendererName = el.getAttribute('renderer');\n if (editorName) config.__editorName = editorName;\n if (rendererName) config.__rendererName = rendererName;\n\n // Parse options attribute for select/typeahead: \"value1:Label1,value2:Label2\" or \"value1,value2\"\n const optionsAttr = el.getAttribute('options');\n if (optionsAttr) {\n config.options = optionsAttr.split(',').map((item) => {\n const [value, label] = item.includes(':') ? item.split(':') : [item.trim(), item.trim()];\n return { value: value.trim(), label: label?.trim() || value.trim() };\n });\n }\n const viewTpl = el.querySelector('tbw-grid-column-view');\n const editorTpl = el.querySelector('tbw-grid-column-editor');\n const headerTpl = el.querySelector('tbw-grid-column-header');\n if (viewTpl) config.__viewTemplate = viewTpl as HTMLElement;\n if (editorTpl) config.__editorTemplate = editorTpl as HTMLElement;\n if (headerTpl) config.__headerTemplate = headerTpl as HTMLElement;\n\n // Check if framework adapters can handle template wrapper elements or the column element itself\n // React adapter registers on the column element, Angular uses inner template wrappers\n const DataGridElementClassRef = (globalThis as { DataGridElement?: DataGridElementClass }).DataGridElement;\n const adapters = DataGridElementClassRef?.getAdapters?.() ?? [];\n\n // First check inner view template, then column element itself\n const viewTarget = (viewTpl ?? el) as HTMLElement;\n const viewAdapter = adapters.find((a) => a.canHandle(viewTarget));\n if (viewAdapter) {\n // Only assign if adapter returns a truthy renderer\n // Adapters return undefined when only an editor is registered (no view template)\n const renderer = viewAdapter.createRenderer(viewTarget);\n if (renderer) {\n config.viewRenderer = renderer;\n }\n }\n\n // First check inner editor template, then column element itself\n const editorTarget = (editorTpl ?? el) as HTMLElement;\n const editorAdapter = adapters.find((a) => a.canHandle(editorTarget));\n if (editorAdapter) {\n // Only assign if adapter returns a truthy editor\n const editor = editorAdapter.createEditor(editorTarget);\n if (editor) {\n config.editor = editor;\n }\n }\n\n return config;\n })\n .filter((c): c is ColumnInternal => !!c);\n}\n\n/**\n * Merge programmatic columns with light DOM columns by field name, allowing DOM-provided\n * attributes / templates to supplement (not overwrite) programmatic definitions.\n * Any DOM columns without a programmatic counterpart are appended.\n * When multiple DOM columns exist for the same field (e.g., separate renderer and editor),\n * their properties are merged together.\n */\nexport function mergeColumns(\n programmatic: ColumnConfig[] | undefined,\n dom: ColumnConfig[] | undefined,\n): ColumnInternal[] {\n if ((!programmatic || !programmatic.length) && (!dom || !dom.length)) return [];\n if (!programmatic || !programmatic.length) return (dom || []) as ColumnInternal[];\n if (!dom || !dom.length) return programmatic as ColumnInternal[];\n\n // Build domMap by merging multiple DOM columns with the same field\n // This supports React pattern where renderer and editor are in separate GridColumn elements\n const domMap: Record<string, ColumnInternal> = {};\n (dom as ColumnInternal[]).forEach((c) => {\n const existing = domMap[c.field];\n if (existing) {\n // Merge this column's properties into the existing one\n if (c.header && !existing.header) existing.header = c.header;\n if (c.type && !existing.type) existing.type = c.type;\n if (c.sortable) existing.sortable = true;\n if (c.editable) existing.editable = true;\n if (c.resizable) existing.resizable = true;\n if (c.width != null && existing.width == null) existing.width = c.width;\n if (c.minWidth != null && existing.minWidth == null) existing.minWidth = c.minWidth;\n if (c.__viewTemplate) existing.__viewTemplate = c.__viewTemplate;\n if (c.__editorTemplate) existing.__editorTemplate = c.__editorTemplate;\n if (c.__headerTemplate) existing.__headerTemplate = c.__headerTemplate;\n // Support both 'renderer' alias and 'viewRenderer'\n const cRenderer = c.renderer || c.viewRenderer;\n const existingRenderer = existing.renderer || existing.viewRenderer;\n if (cRenderer && !existingRenderer) {\n existing.viewRenderer = cRenderer;\n if (c.renderer) existing.renderer = cRenderer;\n }\n if (c.editor && !existing.editor) existing.editor = c.editor;\n } else {\n domMap[c.field] = { ...c };\n }\n });\n\n const merged: ColumnInternal[] = (programmatic as ColumnInternal[]).map((c) => {\n const d = domMap[c.field];\n if (!d) return c;\n const m: ColumnInternal = { ...c };\n if (d.header && !m.header) m.header = d.header;\n if (d.type && !m.type) m.type = d.type;\n m.sortable = c.sortable || d.sortable;\n if (c.resizable === true || d.resizable === true) m.resizable = true;\n m.editable = c.editable || d.editable;\n // Merge width/minWidth from DOM if not set programmatically\n if (d.width != null && m.width == null) m.width = d.width;\n if (d.minWidth != null && m.minWidth == null) m.minWidth = d.minWidth;\n if (d.__viewTemplate) m.__viewTemplate = d.__viewTemplate;\n if (d.__editorTemplate) m.__editorTemplate = d.__editorTemplate;\n if (d.__headerTemplate) m.__headerTemplate = d.__headerTemplate;\n // Merge framework adapter renderers/editors from DOM (support both 'renderer' alias and 'viewRenderer')\n const dRenderer = d.renderer || d.viewRenderer;\n const mRenderer = m.renderer || m.viewRenderer;\n if (dRenderer && !mRenderer) {\n m.viewRenderer = dRenderer;\n if (d.renderer) m.renderer = dRenderer;\n }\n if (d.editor && !m.editor) m.editor = d.editor;\n delete domMap[c.field];\n return m;\n });\n Object.keys(domMap).forEach((field) => merged.push(domMap[field]));\n return merged;\n}\n\n/**\n * Safely add a token to an element's `part` attribute (supporting the CSS ::part API)\n * without duplicating values. Falls back to string manipulation if `el.part` API isn't present.\n */\nexport function addPart(el: HTMLElement, token: string): void {\n try {\n (el as ElementWithPart).part?.add?.(token);\n } catch {\n /* empty */\n }\n const existing = el.getAttribute('part');\n if (!existing) el.setAttribute('part', token);\n else if (!existing.split(/\\s+/).includes(token)) el.setAttribute('part', existing + ' ' + token);\n}\n\n/**\n * Measure rendered header + visible cell content to assign initial pixel widths\n * to columns when in `content` fit mode. Runs only once unless fit mode changes.\n */\nexport function autoSizeColumns(grid: InternalGrid): void {\n const mode = grid.effectiveConfig?.fitMode || grid.fitMode || FitModeEnum.STRETCH;\n // Run for both stretch (to derive baseline pixel widths before fr distribution) and fixed.\n if (mode !== FitModeEnum.STRETCH && mode !== FitModeEnum.FIXED) return;\n if (grid.__didInitialAutoSize) return;\n if (!(grid as unknown as HTMLElement).isConnected) return;\n const headerCells = Array.from(grid._headerRowEl?.children || []) as HTMLElement[];\n if (!headerCells.length) return;\n let changed = false;\n grid._visibleColumns.forEach((col: ColumnInternal, i: number) => {\n if (col.width) return;\n const headerCell = headerCells[i];\n let max = headerCell ? headerCell.scrollWidth : 0;\n for (const rowEl of grid._rowPool) {\n const cell = rowEl.children[i] as HTMLElement | undefined;\n if (cell) {\n const w = cell.scrollWidth;\n if (w > max) max = w;\n }\n }\n if (max > 0) {\n col.width = max + 2;\n col.__autoSized = true;\n changed = true;\n }\n });\n if (changed) updateTemplate(grid);\n grid.__didInitialAutoSize = true;\n}\n\n/**\n * Compute and apply the CSS grid template string that drives column layout.\n * Uses `fr` units for flexible (non user-resized) columns in stretch mode, otherwise\n * explicit pixel widths or auto sizing.\n */\nexport function updateTemplate(grid: InternalGrid): void {\n // Modes:\n // - 'stretch': columns with explicit width use that width; columns without width are flexible\n // Uses minmax(minWidth, maxWidth) when both min/max specified (bounded flex)\n // Uses minmax(minWidth, 1fr) when only min specified (grows unbounded)\n // Uses minmax(defaultMin, maxWidth) when only max specified (capped growth)\n // - 'fixed': columns with explicit width use that width; columns without width use max-content\n const mode = grid.effectiveConfig?.fitMode || grid.fitMode || FitModeEnum.STRETCH;\n\n if (mode === FitModeEnum.STRETCH) {\n grid._gridTemplate = grid._visibleColumns\n .map((c: ColumnInternal) => {\n if (c.width) return `${c.width}px`;\n // Flexible column: pure 1fr unless minWidth specified\n const min = c.minWidth;\n return min != null ? `minmax(${min}px, 1fr)` : '1fr';\n })\n .join(' ')\n .trim();\n } else {\n // fixed mode: explicit pixel widths or max-content for content-based sizing\n grid._gridTemplate = grid._visibleColumns\n .map((c: ColumnInternal) => (c.width ? `${c.width}px` : 'max-content'))\n .join(' ');\n }\n (grid as unknown as HTMLElement).style.setProperty('--tbw-column-template', grid._gridTemplate);\n}\n","import type { ColumnConfigMap, ColumnType, InferredColumnResult, PrimitiveColumnType } from '../types';\n/**\n * Best-effort primitive type inference for a cell value used during automatic column generation.\n */\nfunction inferType(value: any): PrimitiveColumnType {\n if (value == null) return 'string';\n if (typeof value === 'number') return 'number';\n if (typeof value === 'boolean') return 'boolean';\n if (value instanceof Date) return 'date';\n if (typeof value === 'string' && /\\d{4}-\\d{2}-\\d{2}/.test(value) && !isNaN(Date.parse(value))) return 'date';\n return 'string';\n}\n/**\n * Derive column definitions from provided configuration or by inspecting the first row of data.\n * Returns both the resolved column array and a field->type map.\n */\nexport function inferColumns<TRow extends Record<string, unknown>>(\n rows: TRow[],\n provided?: ColumnConfigMap<TRow>,\n): InferredColumnResult<TRow> {\n if (provided && provided.length) {\n const typeMap: Record<string, ColumnType> = {};\n provided.forEach((col) => {\n if (col.type) typeMap[col.field] = col.type;\n });\n return { columns: provided, typeMap };\n }\n const sample = rows[0] || ({} as TRow);\n const columns: ColumnConfigMap<TRow> = Object.keys(sample).map((k) => {\n const v = (sample as Record<string, unknown>)[k];\n const type = inferType(v);\n return { field: k as keyof TRow & string, header: k.charAt(0).toUpperCase() + k.slice(1), type };\n });\n const typeMap: Record<string, ColumnType> = {};\n columns.forEach((c) => {\n typeMap[c.field] = c.type || 'string';\n });\n return { columns, typeMap };\n}\nexport { inferType };\n","// Centralized template expression evaluation & sanitization utilities.\n// Responsible for safely interpolating {{ }} expressions while blocking\n// access to dangerous globals / reflective capabilities.\nimport type { CompiledViewFunction, EvalContext } from '../types';\n\nconst EXPR_RE = /{{\\s*([^}]+)\\s*}}/g;\nconst EMPTY_SENTINEL = '__DG_EMPTY__';\nconst SAFE_EXPR = /^[\\w$. '?+\\-*/%:()!<>=,&|]+$/;\nconst FORBIDDEN =\n /__(proto|defineGetter|defineSetter)|constructor|window|globalThis|global|process|Function|import|eval|Reflect|Proxy|Error|arguments|document|location|cookie|localStorage|sessionStorage|indexedDB|fetch|XMLHttpRequest|WebSocket|Worker|SharedWorker|ServiceWorker|opener|parent|top|frames|self|this\\b/;\n\n// #region HTML Sanitization\n\n/**\n * Escape a plain text string for safe insertion into HTML.\n * Converts special HTML characters to their entity equivalents.\n *\n * @param text - Plain text string to escape\n * @returns HTML-safe string\n */\nexport function escapeHtml(text: string): string {\n if (!text || typeof text !== 'string') return '';\n return text\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Tags that are considered dangerous and will be completely removed.\n * These can execute scripts, load external resources, or manipulate the page.\n */\nconst DANGEROUS_TAGS = new Set([\n 'script',\n 'iframe',\n 'object',\n 'embed',\n 'form',\n 'input',\n 'button',\n 'textarea',\n 'select',\n 'link',\n 'meta',\n 'base',\n 'style',\n 'template',\n 'slot',\n 'portal',\n 'frame',\n 'frameset',\n 'applet',\n 'noscript',\n 'noembed',\n 'plaintext',\n 'xmp',\n 'listing',\n]);\n\n/**\n * Attributes that are considered dangerous - event handlers and data loading.\n */\nconst DANGEROUS_ATTR_PATTERN = /^on\\w+$/i;\n\n/**\n * Attributes that can contain URLs which might be javascript: or data: URIs.\n */\nconst URL_ATTRS = new Set(['href', 'src', 'action', 'formaction', 'data', 'srcdoc', 'xlink:href', 'poster', 'srcset']);\n\n/**\n * Protocol patterns that are dangerous in URLs.\n */\nconst DANGEROUS_URL_PROTOCOL = /^\\s*(javascript|vbscript|data|blob):/i;\n\n/**\n * Sanitize an HTML string by removing dangerous tags and attributes.\n * This is a defense-in-depth measure for content rendered via innerHTML.\n *\n * @param html - Raw HTML string to sanitize\n * @returns Sanitized HTML string safe for innerHTML\n */\nexport function sanitizeHTML(html: string): string {\n if (!html || typeof html !== 'string') return '';\n\n // Fast path: if no HTML tags at all, return as-is (already safe)\n if (html.indexOf('<') === -1) return html;\n\n const template = document.createElement('template');\n template.innerHTML = html;\n\n sanitizeNode(template.content);\n\n return template.innerHTML;\n}\n\n/**\n * Recursively sanitize a DOM node tree.\n */\nfunction sanitizeNode(root: DocumentFragment | Element): void {\n const toRemove: Element[] = [];\n\n // Use querySelectorAll to find all elements, then filter\n const elements = root.querySelectorAll('*');\n\n for (const el of elements) {\n const tagName = el.tagName.toLowerCase();\n\n // Check if tag is dangerous\n if (DANGEROUS_TAGS.has(tagName)) {\n toRemove.push(el);\n continue;\n }\n\n // SVG elements need special handling - they can contain script-like behavior\n if (tagName === 'svg' || el.namespaceURI === 'http://www.w3.org/2000/svg') {\n // Remove entire SVG if it has any suspicious attributes\n const hasDangerousContent = Array.from(el.attributes).some(\n (attr) => DANGEROUS_ATTR_PATTERN.test(attr.name) || attr.name === 'href' || attr.name === 'xlink:href',\n );\n if (hasDangerousContent) {\n toRemove.push(el);\n continue;\n }\n }\n\n // Check and remove dangerous attributes\n const attrsToRemove: string[] = [];\n for (const attr of el.attributes) {\n const attrName = attr.name.toLowerCase();\n\n // Event handlers (onclick, onerror, onload, etc.)\n if (DANGEROUS_ATTR_PATTERN.test(attrName)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // URL attributes with dangerous protocols\n if (URL_ATTRS.has(attrName) && DANGEROUS_URL_PROTOCOL.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // style attribute can contain expressions (IE) or url() with javascript:\n if (attrName === 'style' && /expression\\s*\\(|javascript:|behavior\\s*:/i.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n }\n\n attrsToRemove.forEach((name) => el.removeAttribute(name));\n }\n\n // Remove dangerous elements (do this after iteration to avoid modifying during traversal)\n toRemove.forEach((el) => el.remove());\n}\n\n// #endregion\n\nexport function evalTemplateString(raw: string, ctx: EvalContext): string {\n if (!raw || raw.indexOf('{{') === -1) return raw; // fast path (no expressions)\n const parts: { expr: string; result: string }[] = [];\n const evaluated = raw.replace(EXPR_RE, (_m, expr) => {\n const res = evalSingle(expr, ctx);\n parts.push({ expr: expr.trim(), result: res });\n return res;\n });\n const finalStr = postProcess(evaluated);\n // If every part evaluated to EMPTY_SENTINEL we treat this as intentionally blank.\n // If any expression was blocked due to forbidden token (EMPTY_SENTINEL) we *still* only output ''\n // but do not escalate to BLOCKED_SENTINEL unless the original contained explicit forbidden tokens.\n const allEmpty = parts.length && parts.every((p) => p.result === '' || p.result === EMPTY_SENTINEL);\n const hadForbidden = /Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(raw);\n if (hadForbidden || allEmpty) return '';\n return finalStr;\n}\n\nfunction evalSingle(expr: string, ctx: EvalContext): string {\n expr = (expr || '').trim();\n if (!expr) return EMPTY_SENTINEL;\n if (/\\b(Reflect|Proxy|ownKeys)\\b/.test(expr)) return EMPTY_SENTINEL;\n if (expr === 'value') return ctx.value == null ? EMPTY_SENTINEL : String(ctx.value);\n if (expr.startsWith('row.') && !/[()?]/.test(expr) && !expr.includes(':')) {\n const key = expr.slice(4);\n const v = ctx.row ? ctx.row[key] : undefined;\n return v == null ? EMPTY_SENTINEL : String(v);\n }\n if (expr.length > 80) return EMPTY_SENTINEL;\n if (!SAFE_EXPR.test(expr) || FORBIDDEN.test(expr)) return EMPTY_SENTINEL;\n const dotChain = expr.match(/\\./g);\n if (dotChain && dotChain.length > 1) return EMPTY_SENTINEL;\n try {\n const fn = new Function('value', 'row', `return (${expr});`);\n const out = fn(ctx.value, ctx.row);\n const str = out == null ? '' : String(out);\n if (/Reflect|Proxy|ownKeys/.test(str)) return EMPTY_SENTINEL;\n return str || EMPTY_SENTINEL;\n } catch {\n return EMPTY_SENTINEL;\n }\n}\n\nfunction postProcess(s: string): string {\n if (!s) return s;\n return s\n .replace(new RegExp(EMPTY_SENTINEL, 'g'), '')\n .replace(/Reflect\\.[^<>{}\\s]+/g, '')\n .replace(/\\bProxy\\b/g, '')\n .replace(/ownKeys\\([^)]*\\)/g, '');\n}\n\nexport function finalCellScrub(cell: HTMLElement): void {\n if (/Reflect|Proxy|ownKeys/.test(cell.textContent || '')) {\n Array.from(cell.childNodes).forEach((n) => {\n if (n.nodeType === Node.TEXT_NODE && /Reflect|Proxy|ownKeys/.test(n.textContent || '')) n.textContent = '';\n });\n if (/Reflect|Proxy|ownKeys/.test(cell.textContent || '')) {\n // If remaining content still includes forbidden tokens inside element nodes, clear children entirely.\n const still = /Reflect|Proxy|ownKeys/.test(cell.textContent || '');\n if (still) {\n while (cell.firstChild) cell.removeChild(cell.firstChild);\n }\n cell.textContent = (cell.textContent || '').replace(/Reflect|Proxy|ownKeys/g, '');\n }\n if ((cell.textContent || '').trim().length === 0) cell.textContent = '';\n }\n}\n\nexport function compileTemplate(raw: string) {\n const forceBlank = /Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(raw);\n const fn = ((ctx: EvalContext) => {\n if (forceBlank) return '';\n const out = evalTemplateString(raw, ctx);\n return out;\n }) as CompiledViewFunction;\n fn.__blocked = forceBlank;\n return fn;\n}\n","/**\n * ConfigManager - Unified Configuration Lifecycle Management\n *\n * Manages all configuration concerns for the grid:\n * - Source collection (gridConfig, columns, attributes, Light DOM)\n * - Two-layer config (frozen original + mutable effective)\n * - State persistence (collect/apply/reset via diff)\n * - Change notification for re-rendering\n *\n * This is an internal module - grid.ts delegates to ConfigManager\n * but the public API remains unchanged.\n */\n\nimport type { BaseGridPlugin } from '../plugin';\nimport type {\n ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n ColumnSortState,\n ColumnState,\n FitMode,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n ToolbarContentDefinition,\n ToolPanelDefinition,\n} from '../types';\nimport { mergeColumns, parseLightDomColumns } from './columns';\nimport { inferColumns } from './inference';\nimport { compileTemplate } from './sanitize';\n\n/** Debounce timeout for state change events */\nconst STATE_CHANGE_DEBOUNCE_MS = 100;\n\n/**\n * Callbacks for ConfigManager to notify the grid of changes.\n */\nexport interface ConfigManagerCallbacks<T> {\n /** Get current rows for column inference */\n getRows: () => T[];\n /** Get current sort state */\n getSortState: () => { field: string; direction: 1 | -1 } | null;\n /** Set sort state */\n setSortState: (state: { field: string; direction: 1 | -1 } | null) => void;\n /** Notify that config changed and re-render is needed */\n onConfigChange: () => void;\n /** Emit a custom event */\n emit: (eventName: string, detail: unknown) => void;\n /** Clear row pool for visibility changes */\n clearRowPool: () => void;\n /** Run setup after state changes */\n setup: () => void;\n /** Render header */\n renderHeader: () => void;\n /** Update column template */\n updateTemplate: () => void;\n /** Refresh virtual window */\n refreshVirtualWindow: () => void;\n /** Get virtualization state for row height application */\n getVirtualization: () => { rowHeight: number };\n /** Set row height on virtualization state */\n setRowHeight: (height: number) => void;\n /** Apply animation config to host element */\n applyAnimationConfig: (config: GridConfig<T>) => void;\n /** Get light DOM title from shell state (synced from parseLightDomShell) */\n getShellLightDomTitle: () => string | null;\n /** Get registered tool panels from shell state */\n getShellToolPanels: () => Map<string, ToolPanelDefinition>;\n /** Get registered header contents from shell state */\n getShellHeaderContents: () => Map<string, HeaderContentDefinition>;\n /** Get registered toolbar contents from shell state */\n getShellToolbarContents: () => Map<string, ToolbarContentDefinition>;\n /** Get light DOM header content elements */\n getShellLightDomHeaderContent: () => HTMLElement[];\n /** Get whether a tool buttons container was found in light DOM */\n getShellHasToolButtonsContainer: () => boolean;\n}\n\n/**\n * ConfigManager handles all configuration lifecycle for the grid.\n *\n * Manages:\n * - Source collection (gridConfig, columns, attributes, Light DOM)\n * - Effective config (merged from all sources)\n * - State persistence (collectState, applyState, resetState)\n * - Column visibility and ordering\n */\nexport class ConfigManager<T = unknown> {\n // ============================================================================\n // Sources (raw input from user)\n // ============================================================================\n #gridConfig?: GridConfig<T>;\n #columns?: ColumnConfig<T>[] | ColumnConfigMap<T>;\n #fitMode?: FitMode;\n\n // Light DOM cache\n #lightDomColumnsCache?: ColumnInternal<T>[];\n #originalColumnNodes?: HTMLElement[];\n\n // ============================================================================\n // Two-Layer Config Architecture\n // ============================================================================\n /**\n * Original config (frozen) - Built from sources, never mutated.\n * This is the \"canonical\" config that sources compile into.\n * Used as the reset point for effectiveConfig.\n */\n #originalConfig: GridConfig<T> = {};\n\n /**\n * Effective config (mutable) - Cloned from original, runtime mutations here.\n * This is what rendering reads from.\n * Runtime changes: hidden, width, sort order, column order.\n */\n #effectiveConfig: GridConfig<T> = {};\n\n // ============================================================================\n // State Tracking\n // ============================================================================\n #sourcesChanged = true;\n #changeListeners: Array<() => void> = [];\n #lightDomObserver?: MutationObserver;\n #stateChangeTimeoutId?: ReturnType<typeof setTimeout>;\n #initialColumnState?: GridColumnState;\n #callbacks: ConfigManagerCallbacks<T>;\n\n // Shell state (Light DOM title)\n #lightDomTitle?: string;\n\n constructor(callbacks: ConfigManagerCallbacks<T>) {\n this.#callbacks = callbacks;\n }\n\n // ============================================================================\n // Getters for Grid Access\n // ============================================================================\n\n /** Get the frozen original config (compiled from sources, immutable) */\n get original(): GridConfig<T> {\n return this.#originalConfig;\n }\n\n /** Get the mutable effective config (current runtime state) */\n get effective(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n\n /** Get columns from effective config */\n get columns(): ColumnInternal<T>[] {\n return (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n }\n\n /** Set columns on effective config */\n set columns(value: ColumnInternal<T>[]) {\n this.#effectiveConfig.columns = value as ColumnConfig<T>[];\n }\n\n /** Get light DOM columns cache */\n get lightDomColumnsCache(): ColumnInternal<T>[] | undefined {\n return this.#lightDomColumnsCache;\n }\n\n /** Set light DOM columns cache */\n set lightDomColumnsCache(value: ColumnInternal<T>[] | undefined) {\n this.#lightDomColumnsCache = value;\n }\n\n /** Get original column nodes */\n get originalColumnNodes(): HTMLElement[] | undefined {\n return this.#originalColumnNodes;\n }\n\n /** Set original column nodes */\n set originalColumnNodes(value: HTMLElement[] | undefined) {\n this.#originalColumnNodes = value;\n }\n\n /** Get light DOM title */\n get lightDomTitle(): string | undefined {\n return this.#lightDomTitle;\n }\n\n /** Set light DOM title */\n set lightDomTitle(value: string | undefined) {\n this.#lightDomTitle = value;\n }\n\n /** Get initial column state */\n get initialColumnState(): GridColumnState | undefined {\n return this.#initialColumnState;\n }\n\n /** Set initial column state */\n set initialColumnState(value: GridColumnState | undefined) {\n this.#initialColumnState = value;\n }\n\n // ============================================================================\n // Source Management\n // ============================================================================\n\n /**\n * Check if sources have changed since last merge.\n */\n get sourcesChanged(): boolean {\n return this.#sourcesChanged;\n }\n\n /**\n * Mark that sources have changed and need re-merging.\n * Call this when external state (shell maps, etc.) that feeds into\n * collectAllSources() has been updated.\n */\n markSourcesChanged(): void {\n this.#sourcesChanged = true;\n }\n\n // ============================================================================\n // Source Setters (mark dirty)\n // ============================================================================\n\n /** Set gridConfig source */\n setGridConfig(config: GridConfig<T> | undefined): void {\n this.#gridConfig = config;\n this.#sourcesChanged = true;\n // Clear light DOM cache for framework async content\n this.#lightDomColumnsCache = undefined;\n }\n\n /** Get the raw gridConfig source */\n getGridConfig(): GridConfig<T> | undefined {\n return this.#gridConfig;\n }\n\n /** Set columns source */\n setColumns(columns: ColumnConfig<T>[] | ColumnConfigMap<T> | undefined): void {\n this.#columns = columns;\n this.#sourcesChanged = true;\n }\n\n /** Get the raw columns source */\n getColumns(): ColumnConfig<T>[] | ColumnConfigMap<T> | undefined {\n return this.#columns;\n }\n\n /** Set fitMode source */\n setFitMode(mode: FitMode | undefined): void {\n this.#fitMode = mode;\n this.#sourcesChanged = true;\n }\n\n /** Get the raw fitMode source */\n getFitMode(): FitMode | undefined {\n return this.#fitMode;\n }\n\n // ============================================================================\n // Config Lifecycle\n // ============================================================================\n\n /**\n * Merge all sources into effective config.\n * Also applies post-merge operations (rowHeight, fixed mode widths, animation).\n *\n * Called by RenderScheduler's mergeConfig phase.\n *\n * Two-layer architecture:\n * 1. Sources → #originalConfig (frozen, immutable)\n * 2. Clone → #effectiveConfig (mutable, runtime changes)\n *\n * When sources change, both layers are rebuilt.\n * When sources haven't changed AND columns exist, this is a no-op.\n * Runtime mutations only affect effectiveConfig.\n * resetState() clones originalConfig back to effectiveConfig.\n */\n merge(): void {\n // Only rebuild when sources have actually changed.\n // Exception: always rebuild if we don't have columns yet (inference may be needed)\n const hasColumns = (this.#effectiveConfig.columns?.length ?? 0) > 0;\n if (!this.#sourcesChanged && hasColumns) {\n return; // effectiveConfig is already valid\n }\n\n // Build config from all sources\n const base = this.#collectAllSources();\n\n // Mark sources as processed\n this.#sourcesChanged = false;\n\n // Freeze as the new original config (immutable reference point)\n this.#originalConfig = base;\n Object.freeze(this.#originalConfig);\n if (this.#originalConfig.columns) {\n // Deep freeze columns array (but not the column objects themselves,\n // as we need effectiveConfig columns to be mutable)\n Object.freeze(this.#originalConfig.columns);\n }\n\n // Clone to effective config (mutable copy for runtime changes)\n this.#effectiveConfig = this.#cloneConfig(this.#originalConfig);\n\n // Apply post-merge operations to effectiveConfig\n this.#applyPostMergeOperations();\n }\n\n /**\n * Deep clone a config object, handling functions (renderers, editors).\n * Uses structuredClone where possible, with fallback for function properties.\n */\n #cloneConfig(config: GridConfig<T>): GridConfig<T> {\n // Can't use structuredClone because config may contain functions\n const clone: GridConfig<T> = { ...config };\n\n // Deep clone columns (they may have runtime-mutable state)\n if (config.columns) {\n clone.columns = config.columns.map((col) => ({ ...col }));\n }\n\n // Deep clone shell if present\n if (config.shell) {\n clone.shell = {\n ...config.shell,\n header: config.shell.header ? { ...config.shell.header } : undefined,\n toolPanel: config.shell.toolPanel ? { ...config.shell.toolPanel } : undefined,\n toolPanels: config.shell.toolPanels?.map((p) => ({ ...p })),\n headerContents: config.shell.headerContents?.map((h) => ({ ...h })),\n };\n }\n\n return clone;\n }\n\n /**\n * Apply operations that depend on the merged effective config.\n * These were previously in grid.ts #mergeEffectiveConfig().\n */\n #applyPostMergeOperations(): void {\n const config = this.#effectiveConfig;\n\n // Apply rowHeight from config if specified\n if (config.rowHeight && config.rowHeight > 0) {\n this.#callbacks.setRowHeight(config.rowHeight);\n }\n\n // If fixed mode and width not specified: assign default 80px\n if (config.fitMode === 'fixed') {\n const columns = this.columns;\n columns.forEach((c) => {\n if (c.width == null) c.width = 80;\n });\n }\n\n // Apply animation configuration to host element\n this.#callbacks.applyAnimationConfig(config);\n }\n\n /**\n * Collect all sources into a single config object.\n * This is the core merge logic extracted from grid.ts #mergeEffectiveConfig.\n *\n * Collects all sources into a canonical config object.\n * This becomes the frozen #originalConfig.\n *\n * Sources (in order of precedence, low to high):\n * 1. gridConfig.columns\n * 2. Light DOM columns (merged with config columns)\n * 3. columns prop (overrides if set)\n * 4. Inferred columns (if still empty)\n *\n * Runtime state (hidden, width) is NOT preserved here - that's in effectiveConfig.\n */\n #collectAllSources(): GridConfig<T> {\n const base: GridConfig<T> = this.#gridConfig ? { ...this.#gridConfig } : {};\n const configColumns: ColumnConfig<T>[] = Array.isArray(base.columns) ? [...base.columns] : [];\n\n // Light DOM cached parse - clone to avoid mutation\n const domCols: ColumnConfig<T>[] = (this.#lightDomColumnsCache ?? []).map((c) => ({\n ...c,\n })) as ColumnConfig<T>[];\n\n // Use mergeColumns to combine config columns with light DOM columns\n // This handles all the complex merge logic including templates and renderers\n let columns: ColumnInternal<T>[] = mergeColumns(\n configColumns as ColumnInternal<T>[],\n domCols as ColumnInternal<T>[],\n ) as ColumnInternal<T>[];\n\n // Columns prop highest structural precedence (overrides merged result)\n if (this.#columns && (this.#columns as ColumnConfig<T>[]).length) {\n // When columns prop is set, merge with light DOM columns for renderers/templates\n columns = mergeColumns(\n this.#columns as ColumnInternal<T>[],\n domCols as ColumnInternal<T>[],\n ) as ColumnInternal<T>[];\n }\n\n // Inference if still empty\n const rows = this.#callbacks.getRows();\n if (columns.length === 0 && rows.length) {\n const result = inferColumns(rows as Record<string, unknown>[]);\n columns = result.columns as ColumnInternal<T>[];\n }\n\n if (columns.length) {\n // Apply per-column defaults\n columns.forEach((c) => {\n if (c.sortable === undefined) c.sortable = true;\n if (c.resizable === undefined) c.resizable = true;\n if (c.__originalWidth === undefined && typeof c.width === 'number') {\n c.__originalWidth = c.width;\n }\n });\n\n // Compile inline templates (from light DOM <template> elements)\n columns.forEach((c) => {\n if (c.__viewTemplate && !c.__compiledView) {\n c.__compiledView = compileTemplate((c.__viewTemplate as HTMLElement).innerHTML);\n }\n if (c.__editorTemplate && !c.__compiledEditor) {\n c.__compiledEditor = compileTemplate(c.__editorTemplate.innerHTML);\n }\n });\n\n base.columns = columns as ColumnConfig<T>[];\n }\n\n // Individual prop overrides\n if (this.#fitMode) base.fitMode = this.#fitMode;\n if (!base.fitMode) base.fitMode = 'stretch';\n\n // ========================================================================\n // Merge shell configuration from ShellState into effectiveConfig.shell\n // This ensures a single source of truth for all shell config\n // ========================================================================\n this.#mergeShellConfig(base);\n\n // Store columnState from gridConfig if not already set\n if (base.columnState && !this.#initialColumnState) {\n this.#initialColumnState = base.columnState;\n }\n\n return base;\n }\n\n /**\n * Merge shell state into base config's shell property.\n * Ensures effectiveConfig.shell is the single source of truth.\n *\n * IMPORTANT: This method must NOT mutate the original gridConfig.\n * We shallow-clone the shell hierarchy to avoid side effects.\n */\n #mergeShellConfig(base: GridConfig<T>): void {\n // Clone shell hierarchy to avoid mutating original gridConfig\n // base.shell may still reference this.#gridConfig.shell, so we need fresh objects\n base.shell = base.shell ? { ...base.shell } : {};\n base.shell.header = base.shell.header ? { ...base.shell.header } : {};\n\n // Sync light DOM title\n const shellLightDomTitle = this.#callbacks.getShellLightDomTitle();\n if (shellLightDomTitle) {\n this.#lightDomTitle = shellLightDomTitle;\n }\n if (this.#lightDomTitle && !base.shell.header.title) {\n base.shell.header.title = this.#lightDomTitle;\n }\n\n // Sync light DOM header content elements\n const lightDomHeaderContent = this.#callbacks.getShellLightDomHeaderContent();\n if (lightDomHeaderContent?.length > 0) {\n base.shell.header.lightDomContent = lightDomHeaderContent;\n }\n\n // Sync hasToolButtonsContainer from shell state\n if (this.#callbacks.getShellHasToolButtonsContainer()) {\n base.shell.header.hasToolButtonsContainer = true;\n }\n\n // Sync tool panels (from plugins + API + Light DOM)\n const toolPanelsMap = this.#callbacks.getShellToolPanels();\n if (toolPanelsMap.size > 0) {\n const panels = Array.from(toolPanelsMap.values());\n // Sort by order (lower = first, default 100)\n panels.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n base.shell.toolPanels = panels;\n }\n\n // Sync header contents (from plugins + API)\n const headerContentsMap = this.#callbacks.getShellHeaderContents();\n if (headerContentsMap.size > 0) {\n const contents = Array.from(headerContentsMap.values());\n // Sort by order\n contents.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n base.shell.headerContents = contents;\n }\n\n // Sync toolbar contents (from API - config contents are already in base.shell.header.toolbarContents)\n // We need to merge config contents (from gridConfig) with API contents (from registerToolbarContent)\n // API contents can be added/removed dynamically, so we need to rebuild from current state each time\n const toolbarContentsMap = this.#callbacks.getShellToolbarContents();\n const apiContents = Array.from(toolbarContentsMap.values());\n\n // Get ORIGINAL config contents (from gridConfig, not from previous merges)\n // We use a fresh read from gridConfig to avoid accumulating stale API contents\n const originalConfigContents = this.#gridConfig?.shell?.header?.toolbarContents ?? [];\n\n // Merge: config contents + API contents (config takes precedence by id)\n const configIds = new Set(originalConfigContents.map((c) => c.id));\n const mergedContents = [...originalConfigContents];\n for (const content of apiContents) {\n if (!configIds.has(content.id)) {\n mergedContents.push(content);\n }\n }\n\n // Sort by order\n mergedContents.sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n base.shell.header.toolbarContents = mergedContents;\n }\n\n // ============================================================================\n // State Persistence (Replaces column-state.ts)\n // ============================================================================\n\n /**\n * Collect current column state by diffing original vs effective.\n * Returns only the changes from the original configuration.\n */\n collectState(plugins: BaseGridPlugin[]): GridColumnState {\n const columns = this.columns;\n const sortStates = this.#getSortState();\n\n return {\n columns: columns.map((col, index) => {\n const state: ColumnState = {\n field: col.field,\n order: index,\n visible: !col.hidden,\n };\n\n // Include width if set\n const internalCol = col as ColumnInternal<T>;\n if (internalCol.__renderedWidth !== undefined) {\n state.width = internalCol.__renderedWidth;\n } else if (col.width !== undefined) {\n state.width = typeof col.width === 'string' ? parseFloat(col.width) : col.width;\n }\n\n // Include sort state\n const sortState = sortStates.get(col.field);\n if (sortState) {\n state.sort = sortState;\n }\n\n // Collect from plugins\n for (const plugin of plugins) {\n if (plugin.getColumnState) {\n const pluginState = plugin.getColumnState(col.field);\n if (pluginState) {\n Object.assign(state, pluginState);\n }\n }\n }\n\n return state;\n }),\n };\n }\n\n /**\n * Apply column state to the grid.\n */\n applyState(state: GridColumnState, plugins: BaseGridPlugin[]): void {\n if (!state.columns || state.columns.length === 0) return;\n\n const allColumns = this.columns;\n const stateMap = new Map(state.columns.map((s) => [s.field, s]));\n\n // Apply width and visibility\n const updatedColumns = allColumns.map((col) => {\n const s = stateMap.get(col.field);\n if (!s) return col;\n\n const updated: ColumnInternal<T> = { ...col };\n\n if (s.width !== undefined) {\n updated.width = s.width;\n updated.__renderedWidth = s.width;\n }\n\n if (s.visible !== undefined) {\n updated.hidden = !s.visible;\n }\n\n return updated;\n });\n\n // Reorder columns\n updatedColumns.sort((a, b) => {\n const orderA = stateMap.get(a.field)?.order ?? Infinity;\n const orderB = stateMap.get(b.field)?.order ?? Infinity;\n return orderA - orderB;\n });\n\n this.columns = updatedColumns;\n\n // Apply sort state\n const sortedByPriority = state.columns\n .filter((s) => s.sort !== undefined)\n .sort((a, b) => (a.sort?.priority ?? 0) - (b.sort?.priority ?? 0));\n\n if (sortedByPriority.length > 0) {\n const primarySort = sortedByPriority[0];\n if (primarySort.sort) {\n this.#callbacks.setSortState({\n field: primarySort.field,\n direction: primarySort.sort.direction === 'asc' ? 1 : -1,\n });\n }\n } else {\n this.#callbacks.setSortState(null);\n }\n\n // Let plugins apply their state\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n for (const colState of state.columns) {\n plugin.applyColumnState(colState.field, colState);\n }\n }\n }\n }\n\n /**\n * Reset state to original configuration.\n *\n * Two-layer architecture: Clones #originalConfig back to #effectiveConfig.\n * This discards all runtime changes (hidden, width, order) and restores\n * the state to what was compiled from sources.\n */\n resetState(plugins: BaseGridPlugin[]): void {\n // Clear initial state\n this.#initialColumnState = undefined;\n\n // Reset sort state\n this.#callbacks.setSortState(null);\n\n // Clone original config back to effective (discards all runtime changes)\n this.#effectiveConfig = this.#cloneConfig(this.#originalConfig);\n\n // Apply post-merge operations (rowHeight, fixed mode widths, animation)\n this.#applyPostMergeOperations();\n\n // Notify plugins to reset\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n for (const col of this.columns) {\n plugin.applyColumnState(col.field, {\n field: col.field,\n order: 0,\n visible: true,\n });\n }\n }\n }\n\n // Request state change notification\n this.requestStateChange(plugins);\n }\n\n /**\n * Get sort state as a map.\n */\n #getSortState(): Map<string, ColumnSortState> {\n const sortMap = new Map<string, ColumnSortState>();\n const sortState = this.#callbacks.getSortState();\n\n if (sortState) {\n sortMap.set(sortState.field, {\n direction: sortState.direction === 1 ? 'asc' : 'desc',\n priority: 0,\n });\n }\n\n return sortMap;\n }\n\n /**\n * Request a debounced state change event.\n */\n requestStateChange(plugins: BaseGridPlugin[]): void {\n if (this.#stateChangeTimeoutId) {\n clearTimeout(this.#stateChangeTimeoutId);\n }\n\n this.#stateChangeTimeoutId = setTimeout(() => {\n this.#stateChangeTimeoutId = undefined;\n const state = this.collectState(plugins);\n this.#callbacks.emit('column-state-change', state);\n }, STATE_CHANGE_DEBOUNCE_MS);\n }\n\n // ============================================================================\n // Column Visibility API\n // ============================================================================\n\n /**\n * Set the visibility of a column.\n * @returns true if visibility changed, false otherwise\n */\n setColumnVisible(field: string, visible: boolean): boolean {\n const allCols = this.columns;\n const col = allCols.find((c) => c.field === field);\n\n if (!col) return false;\n if (!visible && col.lockVisible) return false;\n\n // Ensure at least one column remains visible\n if (!visible) {\n const remainingVisible = allCols.filter((c) => !c.hidden && c.field !== field).length;\n if (remainingVisible === 0) return false;\n }\n\n const wasHidden = !!col.hidden;\n if (wasHidden === !visible) return false; // No change\n\n col.hidden = !visible;\n\n this.#callbacks.emit('column-visibility', {\n field,\n visible,\n visibleColumns: allCols.filter((c) => !c.hidden).map((c) => c.field),\n });\n\n this.#callbacks.clearRowPool();\n this.#callbacks.setup();\n\n return true;\n }\n\n /**\n * Toggle column visibility.\n */\n toggleColumnVisibility(field: string): boolean {\n const col = this.columns.find((c) => c.field === field);\n return col ? this.setColumnVisible(field, !!col.hidden) : false;\n }\n\n /**\n * Check if a column is visible.\n */\n isColumnVisible(field: string): boolean {\n const col = this.columns.find((c) => c.field === field);\n return col ? !col.hidden : false;\n }\n\n /**\n * Show all columns.\n */\n showAllColumns(): void {\n const allCols = this.columns;\n if (!allCols.some((c) => c.hidden)) return;\n\n allCols.forEach((c) => (c.hidden = false));\n\n this.#callbacks.emit('column-visibility', {\n visibleColumns: allCols.map((c) => c.field),\n });\n\n this.#callbacks.clearRowPool();\n this.#callbacks.setup();\n }\n\n /**\n * Get all columns with visibility info.\n */\n getAllColumns(): Array<{\n field: string;\n header: string;\n visible: boolean;\n lockVisible?: boolean;\n utility?: boolean;\n }> {\n return this.columns.map((c) => ({\n field: c.field,\n header: c.header || c.field,\n visible: !c.hidden,\n lockVisible: c.lockVisible,\n utility: c.meta?.utility === true,\n }));\n }\n\n /**\n * Get current column order.\n */\n getColumnOrder(): string[] {\n return this.columns.map((c) => c.field);\n }\n\n /**\n * Set column order.\n */\n setColumnOrder(order: string[]): void {\n if (!order.length) return;\n\n const columnMap = new Map(this.columns.map((c) => [c.field as string, c]));\n const reordered: ColumnInternal<T>[] = [];\n\n for (const field of order) {\n const col = columnMap.get(field);\n if (col) {\n reordered.push(col);\n columnMap.delete(field);\n }\n }\n\n // Add remaining columns not in order\n for (const col of columnMap.values()) {\n reordered.push(col);\n }\n\n this.columns = reordered;\n\n this.#callbacks.renderHeader();\n this.#callbacks.updateTemplate();\n this.#callbacks.refreshVirtualWindow();\n }\n\n // ============================================================================\n // Light DOM Observer\n // ============================================================================\n\n /**\n * Parse light DOM columns from host element.\n */\n parseLightDomColumns(host: HTMLElement): void {\n if (!this.#lightDomColumnsCache) {\n this.#originalColumnNodes = Array.from(host.querySelectorAll('tbw-grid-column')) as HTMLElement[];\n this.#lightDomColumnsCache = this.#originalColumnNodes.length ? parseLightDomColumns(host) : [];\n }\n }\n\n /**\n * Clear the light DOM columns cache.\n */\n clearLightDomCache(): void {\n this.#lightDomColumnsCache = undefined;\n }\n\n /**\n * Registered Light DOM element handlers.\n * Maps element tag names to callbacks that are invoked when those elements change.\n *\n * This is a generic mechanism - plugins (or future ShellPlugin) register\n * what elements they care about and handle parsing themselves.\n */\n #lightDomHandlers: Map<string, () => void> = new Map();\n\n /**\n * Register a handler for Light DOM element changes.\n * When elements matching the tag name are added/removed/changed,\n * the callback will be invoked (debounced).\n *\n * @param tagName - The lowercase tag name to watch (e.g., 'tbw-grid-header')\n * @param callback - Called when matching elements change\n */\n registerLightDomHandler(tagName: string, callback: () => void): void {\n this.#lightDomHandlers.set(tagName.toLowerCase(), callback);\n }\n\n /**\n * Unregister a Light DOM element handler.\n */\n unregisterLightDomHandler(tagName: string): void {\n this.#lightDomHandlers.delete(tagName.toLowerCase());\n }\n\n /**\n * Set up MutationObserver to watch for Light DOM changes.\n * This is generic infrastructure - specific handling is done via registered handlers.\n *\n * When Light DOM elements are added/removed/changed, the observer:\n * 1. Identifies which registered tag names were affected\n * 2. Debounces multiple mutations into a single callback per handler\n * 3. Invokes the registered callbacks\n *\n * This mechanism allows plugins to register their own Light DOM elements\n * and handle parsing themselves, then hand config to ConfigManager.\n *\n * @param host - The host element to observe (the grid element)\n */\n observeLightDOM(host: HTMLElement): void {\n // Clean up any existing observer\n if (this.#lightDomObserver) {\n this.#lightDomObserver.disconnect();\n }\n\n // Track which handlers need to be called (debounced)\n const pendingCallbacks = new Set<string>();\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n\n const processPendingCallbacks = () => {\n debounceTimer = null;\n for (const tagName of pendingCallbacks) {\n const handler = this.#lightDomHandlers.get(tagName);\n handler?.();\n }\n pendingCallbacks.clear();\n };\n\n this.#lightDomObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n // Check added nodes\n for (const node of mutation.addedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\n const el = node as Element;\n const tagName = el.tagName.toLowerCase();\n\n // Check if any handler is interested in this element\n if (this.#lightDomHandlers.has(tagName)) {\n pendingCallbacks.add(tagName);\n }\n }\n\n // Check for attribute changes\n if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {\n const el = mutation.target as Element;\n const tagName = el.tagName.toLowerCase();\n if (this.#lightDomHandlers.has(tagName)) {\n pendingCallbacks.add(tagName);\n }\n }\n }\n\n // Debounce - batch all mutations into single callbacks\n if (pendingCallbacks.size > 0 && !debounceTimer) {\n debounceTimer = setTimeout(processPendingCallbacks, 0);\n }\n });\n\n // Observe children and their attributes\n this.#lightDomObserver.observe(host, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['title', 'field', 'header', 'width', 'hidden', 'id', 'icon', 'tooltip', 'order'],\n });\n }\n\n // ============================================================================\n // Change Notification\n // ============================================================================\n\n /**\n * Register a change listener.\n */\n onChange(callback: () => void): void {\n this.#changeListeners.push(callback);\n }\n\n /**\n * Notify all change listeners.\n */\n notifyChange(): void {\n for (const cb of this.#changeListeners) {\n cb();\n }\n }\n\n // ============================================================================\n // Cleanup\n // ============================================================================\n\n /**\n * Dispose of the ConfigManager and clean up resources.\n */\n dispose(): void {\n this.#lightDomObserver?.disconnect();\n this.#changeListeners = [];\n if (this.#stateChangeTimeoutId) {\n clearTimeout(this.#stateChangeTimeoutId);\n }\n }\n}\n","// ────────────────────────────────────────────────────────────────────────────\n// Cell Rendering Helpers (reduces duplicate code in rows.ts)\n// ────────────────────────────────────────────────────────────────────────────\n\n/** Unicode checkmark for true boolean values */\nconst BOOL_TRUE = '\\u{1F5F9}';\n/** Unicode empty checkbox for false boolean values */\nconst BOOL_FALSE = '\\u2610';\n\n/**\n * Generate accessible HTML for a boolean cell.\n * Uses role=\"checkbox\" with proper aria attributes.\n */\nexport function booleanCellHTML(value: boolean): string {\n return `<span role=\"checkbox\" aria-checked=\"${value}\" aria-label=\"${value}\">${value ? '🗹' : '☐'}</span>`;\n}\n\n/**\n * Format a date value for display.\n * Handles Date objects, timestamps, and date strings.\n * Returns empty string for invalid dates.\n */\nexport function formatDateValue(value: unknown): string {\n if (value == null || value === '') return '';\n if (value instanceof Date) {\n return isNaN(value.getTime()) ? '' : value.toLocaleDateString();\n }\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return isNaN(d.getTime()) ? '' : d.toLocaleDateString();\n }\n return '';\n}\n\n/**\n * Format a boolean value for text display (not HTML).\n */\nexport function formatBooleanValue(value: unknown): string {\n return value ? BOOL_TRUE : BOOL_FALSE;\n}\n\n/**\n * Get the row index from a cell element's data-row attribute.\n * Falls back to calculating from parent row's DOM position if data-row is missing.\n * Returns -1 if no valid row index is found.\n */\nexport function getRowIndexFromCell(cell: Element | null): number {\n if (!cell) return -1;\n const attr = cell.getAttribute('data-row');\n if (attr) return parseInt(attr, 10);\n\n // Fallback: find the parent .data-grid-row and calculate index from siblings\n const rowEl = cell.closest('.data-grid-row');\n if (!rowEl) return -1;\n\n const parent = rowEl.parentElement;\n if (!parent) return -1;\n\n // Get all data-grid-row siblings and find this row's index\n const rows = parent.querySelectorAll(':scope > .data-grid-row');\n for (let i = 0; i < rows.length; i++) {\n if (rows[i] === rowEl) return i;\n }\n return -1;\n}\n\n/**\n * Get the column index from a cell element's data-col attribute.\n * Returns -1 if no valid column index is found.\n */\nexport function getColIndexFromCell(cell: Element | null): number {\n if (!cell) return -1;\n const attr = cell.getAttribute('data-col');\n return attr ? parseInt(attr, 10) : -1;\n}\n\n/**\n * Clear all cell-focus styling from a root element (shadowRoot or bodyEl).\n * Used when changing focus or when selection plugin takes over focus management.\n */\nexport function clearCellFocus(root: Element | ShadowRoot | null): void {\n if (!root) return;\n root.querySelectorAll('.cell-focus').forEach((el) => el.classList.remove('cell-focus'));\n}\n","import type { ColumnInternal, ColumnViewRenderer, InternalGrid, RowElementInternal } from '../types';\nimport { ensureCellVisible } from './keyboard';\nimport { evalTemplateString, finalCellScrub, sanitizeHTML } from './sanitize';\nimport { booleanCellHTML, clearCellFocus, formatDateValue, getRowIndexFromCell } from './utils';\n\n/** Callback type for plugin row rendering hook */\nexport type RenderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number) => boolean;\n\n// ============================================================================\n// Type Defaults Resolution\n// ============================================================================\n\n/**\n * Resolves the renderer for a column using the priority chain:\n * 1. Column-level (`column.renderer` / `column.viewRenderer`)\n * 2. Grid-level (`gridConfig.typeDefaults[column.type]`)\n * 3. App-level (framework adapter's `getTypeDefault`)\n * 4. Returns undefined (caller uses built-in or fallback)\n */\nexport function resolveRenderer<TRow>(\n grid: InternalGrid<TRow>,\n col: ColumnInternal<TRow>,\n): ColumnViewRenderer<TRow, unknown> | undefined {\n // 1. Column-level renderer (highest priority)\n const columnRenderer = col.renderer || col.viewRenderer;\n if (columnRenderer) return columnRenderer;\n\n // No type specified - no type defaults to check\n if (!col.type) return undefined;\n\n // 2. Grid-level typeDefaults (access via effectiveConfig)\n const gridTypeDefaults = (grid as any).effectiveConfig?.typeDefaults;\n if (gridTypeDefaults?.[col.type]?.renderer) {\n return gridTypeDefaults[col.type].renderer;\n }\n\n // 3. App-level registry (via framework adapter)\n const adapter = grid.__frameworkAdapter;\n if (adapter?.getTypeDefault) {\n const appDefault = adapter.getTypeDefault<TRow>(col.type);\n if (appDefault?.renderer) {\n return appDefault.renderer;\n }\n }\n\n // 4. No custom renderer - caller uses built-in/fallback\n return undefined;\n}\n\n// ============================================================================\n// DOM State Helpers (used for virtualization cleanup)\n// ============================================================================\n\n/**\n * CSS selector for focusable editor elements within a cell.\n * Used by EditingPlugin and keyboard navigation.\n */\nexport const FOCUSABLE_EDITOR_SELECTOR =\n 'input,select,textarea,[contenteditable=\"true\"],[contenteditable=\"\"],[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Check if a row element has any cells in editing mode.\n * This is a DOM-level check used for virtualization recycling.\n */\nfunction hasEditingCells(rowEl: RowElementInternal): boolean {\n return (rowEl.__editingCellCount ?? 0) > 0;\n}\n\n/**\n * Clear all editing state from a row element.\n * Called when a row element is recycled for a different data row.\n */\nfunction clearEditingState(rowEl: RowElementInternal): void {\n rowEl.__editingCellCount = 0;\n rowEl.removeAttribute('data-has-editing');\n // Clear editing class from all cells\n const cells = rowEl.querySelectorAll('.cell.editing');\n cells.forEach((cell) => cell.classList.remove('editing'));\n}\n\n// ============== Template Cloning System ==============\n// Using template cloning is 3-4x faster than document.createElement + setAttribute\n// for repetitive element creation because the browser can skip parsing.\n\n/**\n * Cell template for cloning. Pre-configured with static attributes.\n * Dynamic attributes (data-col, data-row, etc.) are set after cloning.\n */\nconst cellTemplate = document.createElement('template');\ncellTemplate.innerHTML = '<div class=\"cell\" role=\"gridcell\" part=\"cell\"></div>';\n\n/**\n * Row template for cloning. Pre-configured with static attributes.\n * Dynamic attributes (data-row) and children (cells) are set after cloning.\n */\nconst rowTemplate = document.createElement('template');\nrowTemplate.innerHTML = '<div class=\"data-grid-row\" role=\"row\" part=\"row\"></div>';\n\n/**\n * Create a cell element from template. Significantly faster than createElement + setAttribute.\n */\nfunction createCellFromTemplate(): HTMLDivElement {\n return cellTemplate.content.firstElementChild!.cloneNode(true) as HTMLDivElement;\n}\n\n/**\n * Create a row element from template. Significantly faster than createElement + setAttribute.\n */\nexport function createRowFromTemplate(): HTMLDivElement {\n return rowTemplate.content.firstElementChild!.cloneNode(true) as HTMLDivElement;\n}\n\n// ============== End Template Cloning System ==============\n\n/**\n * Invalidate the cell cache (call when rows or columns change).\n */\nexport function invalidateCellCache(grid: InternalGrid): void {\n grid.__cellDisplayCache = undefined;\n grid.__cellCacheEpoch = undefined;\n grid.__hasSpecialColumns = undefined; // Reset fast-path check\n}\n\n/**\n * Render / patch the visible window of rows [start, end) using a recyclable DOM pool.\n * Newly required row elements are created and appended; excess are detached.\n * Uses an epoch counter to force full row rebuilds when structural changes (like columns) occur.\n * @param renderRowHook - Optional callback that plugins can use to render custom rows (e.g., group rows).\n * If it returns true, default rendering is skipped for that row.\n */\nexport function renderVisibleRows(\n grid: InternalGrid,\n start: number,\n end: number,\n epoch?: number,\n renderRowHook?: RenderRowHook,\n): void {\n const needed = Math.max(0, end - start);\n const bodyEl = grid._bodyEl;\n const columns = grid._visibleColumns;\n const colLen = columns.length;\n\n // Cache header row count once (check for group header row existence)\n let headerRowCount = grid.__cachedHeaderRowCount;\n if (headerRowCount === undefined) {\n headerRowCount = grid.querySelector('.header-group-row') ? 2 : 1;\n grid.__cachedHeaderRowCount = headerRowCount;\n }\n\n // Pool management: grow pool if needed\n // Note: click/dblclick handlers are delegated at grid level for efficiency\n while (grid._rowPool.length < needed) {\n // Use template cloning - 3-4x faster than createElement + setAttribute\n const rowEl = createRowFromTemplate();\n grid._rowPool.push(rowEl);\n }\n\n // Remove excess pool elements from DOM and shrink pool\n if (grid._rowPool.length > needed) {\n for (let i = needed; i < grid._rowPool.length; i++) {\n const el = grid._rowPool[i];\n if (el.parentNode === bodyEl) el.remove();\n }\n grid._rowPool.length = needed;\n }\n\n // Check if any plugin has a renderRow hook (cache this)\n const hasRenderRowPlugins = renderRowHook && grid.__hasRenderRowPlugins !== false;\n\n // Check if any plugin wants row-level hooks (avoid overhead when not needed)\n const hasRowHook = grid._hasAfterRowRenderHook?.() ?? false;\n\n for (let i = 0; i < needed; i++) {\n const rowIndex = start + i;\n const rowData = grid._rows[rowIndex];\n const rowEl = grid._rowPool[i] as RowElementInternal;\n\n // Always set aria-rowindex (1-based, accounting for header rows)\n rowEl.setAttribute('aria-rowindex', String(rowIndex + headerRowCount + 1));\n\n // Let plugins handle custom row rendering (e.g., group rows)\n if (hasRenderRowPlugins && renderRowHook!(rowData, rowEl, rowIndex)) {\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n continue;\n }\n\n const rowEpoch = rowEl.__epoch;\n const prevRef = rowEl.__rowDataRef;\n const cellCount = rowEl.children.length;\n\n // Check if we need a full rebuild vs fast update\n const epochMatch = rowEpoch === epoch;\n const structureValid = epochMatch && cellCount === colLen;\n const dataRefChanged = prevRef !== rowData;\n\n // Need external view rebuild check when structure is valid but data changed\n let needsExternalRebuild = false;\n if (structureValid && dataRefChanged) {\n for (let c = 0; c < colLen; c++) {\n const col = columns[c];\n if (col.externalView) {\n const cellCheck = rowEl.querySelector(`.cell[data-col=\"${c}\"] [data-external-view]`);\n if (!cellCheck) {\n needsExternalRebuild = true;\n break;\n }\n }\n }\n }\n\n if (!structureValid || needsExternalRebuild) {\n // Full rebuild needed - epoch changed, cell count mismatch, or external view missing\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n // (This happens when virtualization recycles the DOM element for a different row)\n if (hasEditing && !isActivelyEditedRow) {\n // Force full rebuild to clear stale editors\n if (rowEl.__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n rowEl.__isCustomRow = false;\n }\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n } else if (hasEditing && isActivelyEditedRow) {\n // Row is in editing mode AND this is the correct row - preserve editors\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n rowEl.__rowDataRef = rowData;\n } else {\n if (rowEl.__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n rowEl.__isCustomRow = false;\n }\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n // NOTE: If this is the actively edited row, EditingPlugin's onScrollRender() will inject editors\n }\n } else if (dataRefChanged) {\n // Same structure, different row data - fast update\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditing && !isActivelyEditedRow) {\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n rowEl.__rowDataRef = rowData;\n // NOTE: If this is the actively edited row, EditingPlugin's onScrollRender() will inject editors\n }\n } else {\n // Same row data reference - just patch if any values changed\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditing && !isActivelyEditedRow) {\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n rowEl.__epoch = epoch;\n rowEl.__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n // NOTE: If this is the actively edited row, EditingPlugin's onScrollRender() will inject editors\n }\n }\n\n // Changed class toggle - check if row ID is in changedRowIds Set (EditingPlugin)\n let isChanged = false;\n const changedRowIds = grid.changedRowIds;\n if (changedRowIds && changedRowIds.length > 0) {\n try {\n const rowId = grid.getRowId?.(rowData);\n if (rowId) {\n isChanged = changedRowIds.includes(rowId);\n }\n } catch {\n // Row has no ID - not tracked as changed\n }\n }\n const hasChangedClass = rowEl.classList.contains('changed');\n if (isChanged !== hasChangedClass) {\n rowEl.classList.toggle('changed', isChanged);\n }\n\n // Apply rowClass callback if configured\n const rowClassFn = grid.effectiveConfig?.rowClass;\n if (rowClassFn) {\n // Remove previous dynamic classes (stored in data attribute)\n const prevClasses = rowEl.getAttribute('data-dynamic-classes');\n if (prevClasses) {\n prevClasses.split(' ').forEach((cls) => cls && rowEl.classList.remove(cls));\n }\n try {\n const newClasses = rowClassFn(rowData);\n if (newClasses && newClasses.length > 0) {\n const validClasses = newClasses.filter((c) => c && typeof c === 'string');\n validClasses.forEach((cls) => rowEl.classList.add(cls));\n rowEl.setAttribute('data-dynamic-classes', validClasses.join(' '));\n } else {\n rowEl.removeAttribute('data-dynamic-classes');\n }\n } catch (e) {\n console.warn(`[tbw-grid] rowClass callback error:`, e);\n rowEl.removeAttribute('data-dynamic-classes');\n }\n }\n\n // Call row-level plugin hook if any plugin registered it\n if (hasRowHook) {\n grid._afterRowRender?.({\n row: rowData,\n rowIndex,\n rowElement: rowEl,\n });\n }\n\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n }\n}\n\n/**\n * Fast patch path for an already-rendered row: updates plain text cells whose data changed\n * while skipping cells with external views, templates, or active editors.\n *\n * Optimized for scroll performance - avoids querySelectorAll in favor of children access.\n */\nfunction fastPatchRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n const children = rowEl.children;\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const childLen = children.length;\n const minLen = colsLen < childLen ? colsLen : childLen;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n\n // Check if any plugin wants cell-level hooks (avoid overhead when not needed)\n const hasCellHook = grid._hasAfterCellRenderHook?.() ?? false;\n\n // Ultra-fast path: if no special columns (templates, formatters, etc.), use direct assignment\n // Check is cached on grid to avoid repeated iteration\n let hasSpecialCols = grid.__hasSpecialColumns;\n if (hasSpecialCols === undefined) {\n hasSpecialCols = false;\n const typeDefaults = (grid as any).effectiveConfig?.typeDefaults;\n const adapter = grid.__frameworkAdapter;\n for (let i = 0; i < colsLen; i++) {\n const col = columns[i];\n if (\n col.__viewTemplate ||\n col.__compiledView ||\n col.renderer ||\n col.viewRenderer ||\n col.externalView ||\n col.format ||\n col.type === 'date' ||\n col.type === 'boolean' ||\n // Check for type-level renderers (grid-level or adapter-level)\n (col.type && typeDefaults?.[col.type]?.renderer) ||\n (col.type && adapter?.getTypeDefault?.(col.type)?.renderer)\n ) {\n hasSpecialCols = true;\n break;\n }\n }\n grid.__hasSpecialColumns = hasSpecialCols;\n }\n\n const rowIndexStr = String(rowIndex);\n\n // Ultra-fast path for plain text grids - just set textContent directly\n if (!hasSpecialCols) {\n for (let i = 0; i < minLen; i++) {\n const cell = children[i] as HTMLElement;\n\n // Skip cells in edit mode - they have editors that must be preserved\n if (cell.classList.contains('editing')) continue;\n\n const col = columns[i];\n const value = rowData[col.field];\n cell.textContent = value == null ? '' : String(value);\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n // aria-selected only valid for gridcell, not checkbox (but ultra-fast path has no special cols)\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n\n // Call cell-level plugin hook if any plugin registered it\n if (hasCellHook) {\n grid._afterCellRender?.({\n row: rowData,\n rowIndex,\n column: col,\n colIndex: i,\n value,\n cellElement: cell,\n rowElement: rowEl,\n });\n }\n }\n return;\n }\n\n // Check if any external view placeholder is missing - if so, do full rebuild\n for (let i = 0; i < minLen; i++) {\n const col = columns[i];\n if (col.externalView) {\n const cell = children[i] as HTMLElement;\n if (!cell.querySelector('[data-external-view]')) {\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n return;\n }\n }\n }\n\n // Standard path for grids with special columns\n for (let i = 0; i < minLen; i++) {\n const col = columns[i];\n const cell = children[i] as HTMLElement;\n\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n\n // Apply cellClass callback if configured\n const cellClassFn = col.cellClass;\n if (cellClassFn) {\n // Remove previous dynamic classes\n const prevClasses = cell.getAttribute('data-dynamic-classes');\n if (prevClasses) {\n prevClasses.split(' ').forEach((cls) => cls && cell.classList.remove(cls));\n }\n try {\n const value = rowData[col.field];\n const cellClasses = cellClassFn(value, rowData, col);\n if (cellClasses && cellClasses.length > 0) {\n const validClasses = cellClasses.filter((c: string) => c && typeof c === 'string');\n validClasses.forEach((cls: string) => cell.classList.add(cls));\n cell.setAttribute('data-dynamic-classes', validClasses.join(' '));\n } else {\n cell.removeAttribute('data-dynamic-classes');\n }\n } catch (e) {\n console.warn(`[tbw-grid] cellClass callback error for column '${col.field}':`, e);\n cell.removeAttribute('data-dynamic-classes');\n }\n }\n\n // Skip cells in edit mode\n if (cell.classList.contains('editing')) continue;\n\n // Handle viewRenderer/renderer - must re-invoke to get updated content\n // Uses priority chain: column → typeDefaults → adapter → built-in\n const cellRenderer = resolveRenderer(grid, col);\n if (cellRenderer) {\n const renderedValue = rowData[col.field];\n // Pass cellEl for framework adapters that want to cache per-cell\n const produced = cellRenderer({\n row: rowData,\n value: renderedValue,\n field: col.field,\n column: col,\n cellEl: cell,\n });\n if (typeof produced === 'string') {\n cell.innerHTML = sanitizeHTML(produced);\n } else if (produced instanceof Node) {\n // Check if this container is already a child of the cell (reused by framework adapter)\n if (produced.parentElement !== cell) {\n cell.innerHTML = '';\n cell.appendChild(produced);\n }\n // If already a child, the framework adapter has re-rendered in place\n } else if (produced == null) {\n // Renderer returned null/undefined - show raw value\n cell.textContent = renderedValue == null ? '' : String(renderedValue);\n }\n // If produced is truthy but not a string or Node, the framework handles it\n // Call cell-level plugin hook - cell was rendered\n if (hasCellHook) {\n grid._afterCellRender?.({\n row: rowData,\n rowIndex,\n column: col,\n colIndex: i,\n value: renderedValue,\n cellElement: cell,\n rowElement: rowEl,\n });\n }\n continue;\n }\n\n // Skip templated / external cells (these need full rebuild to remount)\n if (col.__viewTemplate || col.__compiledView || col.externalView) {\n continue;\n }\n\n // Compute and set display value\n const value = rowData[col.field];\n let displayStr: string;\n\n if (col.format) {\n try {\n const formatted = col.format(value, rowData);\n displayStr = formatted == null ? '' : String(formatted);\n } catch (e) {\n // Log format errors as warnings (user configuration issue)\n console.warn(`[tbw-grid] Format error in column '${col.field}':`, e);\n displayStr = value == null ? '' : String(value);\n }\n } else if (col.type === 'date') {\n displayStr = formatDateValue(value);\n cell.textContent = displayStr;\n } else if (col.type === 'boolean') {\n // Boolean cells have inner span with checkbox role for ARIA compliance\n cell.innerHTML = booleanCellHTML(!!value);\n } else {\n displayStr = value == null ? '' : String(value);\n cell.textContent = displayStr;\n }\n\n // Call cell-level plugin hook - cell was rendered\n if (hasCellHook) {\n grid._afterCellRender?.({\n row: rowData,\n rowIndex,\n column: col,\n colIndex: i,\n value,\n cellElement: cell,\n rowElement: rowEl,\n });\n }\n }\n}\n\n/**\n * Full reconstruction of a row's set of cells including templated, external view, and formatted content.\n * Attaches event handlers for editing and accessibility per cell.\n */\nexport function renderInlineRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n rowEl.innerHTML = '';\n\n // Pre-cache values used in the loop\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n const gridEl = grid as unknown as HTMLElement;\n\n // Check if any plugin wants cell-level hooks (avoid overhead when not needed)\n const hasCellHook = grid._hasAfterCellRenderHook?.() ?? false;\n\n // Use DocumentFragment for batch DOM insertion\n const fragment = document.createDocumentFragment();\n\n for (let colIndex = 0; colIndex < colsLen; colIndex++) {\n const col = columns[colIndex];\n // Use template cloning - 3-4x faster than createElement + setAttribute\n const cell = createCellFromTemplate();\n\n // Only set dynamic attributes (role, class, part are already set in template)\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(colIndex + 1));\n cell.setAttribute('data-col', String(colIndex));\n cell.setAttribute('data-row', String(rowIndex));\n cell.setAttribute('data-field', col.field); // Field name for column identification\n cell.setAttribute('data-header', col.header ?? col.field); // Header text for responsive CSS\n if (col.type) cell.setAttribute('data-type', col.type);\n\n let value = (rowData as Record<string, unknown>)[col.field];\n if (col.format) {\n try {\n value = col.format(value, rowData);\n } catch (e) {\n // Log format errors as warnings (user configuration issue)\n console.warn(`[tbw-grid] Format error in column '${col.field}':`, e);\n }\n }\n\n const compiled = col.__compiledView;\n const tplHolder = col.__viewTemplate;\n // Resolve renderer using priority chain: column → typeDefaults → adapter → built-in\n const viewRenderer = resolveRenderer(grid, col);\n const externalView = col.externalView;\n\n // Track if we used a template that needs sanitization\n let needsSanitization = false;\n\n if (viewRenderer) {\n // Pass cellEl for framework adapters that want to cache per-cell\n const produced = viewRenderer({ row: rowData, value, field: col.field, column: col, cellEl: cell });\n if (typeof produced === 'string') {\n // Sanitize HTML from viewRenderer to prevent XSS from user-controlled data\n cell.innerHTML = sanitizeHTML(produced);\n needsSanitization = true;\n } else if (produced instanceof Node) {\n // Check if this container is already a child of the cell (reused by framework adapter)\n if (produced.parentElement !== cell) {\n // Clear any existing content before appending new container\n cell.textContent = '';\n cell.appendChild(produced);\n }\n // If already a child, the framework adapter has re-rendered in place\n } else if (produced == null) {\n // Renderer returned null/undefined - show raw value\n cell.textContent = value == null ? '' : String(value);\n }\n // If produced is truthy but not a string or Node (e.g., framework placeholder),\n // don't modify the cell - the framework adapter handles rendering\n } else if (externalView) {\n const spec = externalView;\n const placeholder = document.createElement('div');\n placeholder.setAttribute('data-external-view', '');\n placeholder.setAttribute('data-field', col.field);\n cell.appendChild(placeholder);\n const context = { row: rowData, value, field: col.field, column: col };\n if (spec.mount) {\n try {\n spec.mount({ placeholder, context, spec });\n } catch (e) {\n // Log mount errors as warnings (user configuration issue)\n console.warn(`[tbw-grid] External view mount error for column '${col.field}':`, e);\n }\n } else {\n queueMicrotask(() => {\n try {\n gridEl.dispatchEvent(\n new CustomEvent('mount-external-view', {\n bubbles: true,\n composed: true,\n detail: { placeholder, spec, context },\n }),\n );\n } catch (e) {\n // Log dispatch errors as warnings\n console.warn(`[tbw-grid] External view event dispatch error for column '${col.field}':`, e);\n }\n });\n }\n placeholder.setAttribute('data-mounted', '');\n } else if (compiled) {\n const output = compiled({ row: rowData, value, field: col.field, column: col });\n const blocked = compiled.__blocked;\n // Sanitize compiled template output to prevent XSS\n cell.innerHTML = blocked ? '' : sanitizeHTML(output);\n needsSanitization = true;\n if (blocked) {\n // Forcefully clear any residual whitespace text nodes for deterministic emptiness\n cell.textContent = '';\n cell.setAttribute('data-blocked-template', '');\n }\n } else if (tplHolder) {\n const rawTpl = tplHolder.innerHTML;\n if (/Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(rawTpl)) {\n cell.textContent = '';\n cell.setAttribute('data-blocked-template', '');\n } else {\n // Sanitize inline template output to prevent XSS\n cell.innerHTML = sanitizeHTML(evalTemplateString(rawTpl, { row: rowData, value }));\n needsSanitization = true;\n }\n } else {\n // Plain value rendering - compute display directly (matches Stencil performance)\n if (col.type === 'date') {\n cell.textContent = formatDateValue(value);\n } else if (col.type === 'boolean') {\n // Wrap checkbox in span to satisfy ARIA: gridcell can contain checkbox\n cell.innerHTML = booleanCellHTML(!!value);\n } else {\n cell.textContent = value == null ? '' : String(value);\n }\n }\n\n // Only run expensive sanitization when we used innerHTML with user content\n if (needsSanitization) {\n finalCellScrub(cell);\n // Defensive: if forbidden tokens leaked via async or framework hydration, scrub again.\n const textContent = cell.textContent || '';\n if (/Proxy|Reflect\\.ownKeys/.test(textContent)) {\n cell.textContent = textContent.replace(/Proxy|Reflect\\.ownKeys/g, '').trim();\n if (/Proxy|Reflect\\.ownKeys/.test(cell.textContent || '')) cell.textContent = '';\n }\n }\n\n if (cell.hasAttribute('data-blocked-template')) {\n // If anything at all remains (e.g., 'function () { [native code] }'), blank it completely.\n if ((cell.textContent || '').trim().length) cell.textContent = '';\n }\n // Mark editable cells with tabindex for keyboard navigation\n // Event handlers are set up via delegation in setupCellEventDelegation()\n if (col.editable) {\n cell.tabIndex = 0;\n } else if (col.type === 'boolean') {\n // Non-editable boolean cells should NOT toggle on space key\n // They are read-only, only set tabindex for focus navigation\n if (!cell.hasAttribute('tabindex')) cell.tabIndex = 0;\n }\n\n // Initialize focus state (must match fastPatchRow for consistent behavior)\n if (focusRow === rowIndex && focusCol === colIndex) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n } else {\n cell.setAttribute('aria-selected', 'false');\n }\n\n // Apply cellClass callback if configured\n const cellClassFn = col.cellClass;\n if (cellClassFn) {\n try {\n const cellValue = (rowData as Record<string, unknown>)[col.field];\n const cellClasses = cellClassFn(cellValue, rowData, col);\n if (cellClasses && cellClasses.length > 0) {\n const validClasses = cellClasses.filter((c) => c && typeof c === 'string');\n validClasses.forEach((cls) => cell.classList.add(cls));\n cell.setAttribute('data-dynamic-classes', validClasses.join(' '));\n }\n } catch (e) {\n console.warn(`[tbw-grid] cellClass callback error for column '${col.field}':`, e);\n }\n }\n\n // Call cell-level plugin hook if any plugin registered it\n if (hasCellHook) {\n grid._afterCellRender?.({\n row: rowData,\n rowIndex,\n column: col,\n colIndex,\n value,\n cellElement: cell,\n rowElement: rowEl,\n });\n }\n\n fragment.appendChild(cell);\n }\n\n // Single DOM operation to append all cells\n rowEl.appendChild(fragment);\n}\n\n/**\n * Handle click / double click interaction to focus cells.\n * Edit triggering is handled by EditingPlugin via onCellClick hook.\n */\nexport function handleRowClick(grid: InternalGrid, e: MouseEvent, rowEl: HTMLElement): void {\n if ((e.target as HTMLElement)?.closest('.resize-handle')) return;\n const firstCell = rowEl.querySelector('.cell[data-row]') as HTMLElement | null;\n const rowIndex = getRowIndexFromCell(firstCell);\n if (rowIndex < 0) return;\n const rowData = grid._rows[rowIndex];\n if (!rowData) return;\n\n // Dispatch row click to plugin system first (e.g., for master-detail expansion)\n if (grid._dispatchRowClick?.(e, rowIndex, rowData, rowEl)) {\n return;\n }\n\n const cellEl = (e.target as HTMLElement)?.closest('.cell[data-col]') as HTMLElement | null;\n if (cellEl) {\n const colIndex = Number(cellEl.getAttribute('data-col'));\n if (!isNaN(colIndex)) {\n // Dispatch to plugin system first - if handled (e.g., edit triggered), stop propagation\n if (grid._dispatchCellClick?.(e, rowIndex, colIndex, cellEl)) {\n return;\n }\n\n // Always update focus to the clicked cell\n const focusChanged = grid._focusRow !== rowIndex || grid._focusCol !== colIndex;\n grid._focusRow = rowIndex;\n grid._focusCol = colIndex;\n\n // If clicking an already-editing cell, just update focus styling and return\n if (cellEl.classList.contains('editing')) {\n if (focusChanged) {\n // Update .cell-focus class to reflect new focus (clear from grid element)\n clearCellFocus(grid._bodyEl ?? grid);\n cellEl.classList.add('cell-focus');\n }\n // Focus the editor in the cell\n const editor = cellEl.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n return;\n }\n\n ensureCellVisible(grid);\n }\n }\n}\n","/**\n * Central keyboard handler attached to the host element. Manages navigation, paging,\n * and edit lifecycle triggers while respecting active form field interactions.\n */\nimport type { InternalGrid } from '../types';\nimport { FOCUSABLE_EDITOR_SELECTOR } from './rows';\nimport { clearCellFocus } from './utils';\n\nexport function handleGridKeyDown(grid: InternalGrid, e: KeyboardEvent): void {\n // Dispatch to plugin system first - if any plugin handles it, stop here\n if (grid._dispatchKeyDown?.(e)) {\n return;\n }\n\n const maxRow = grid._rows.length - 1;\n const maxCol = grid._visibleColumns.length - 1;\n const editing = grid._activeEditRows !== undefined && grid._activeEditRows !== -1;\n const col = grid._visibleColumns[grid._focusCol];\n const colType = col?.type;\n const path = e.composedPath?.() ?? [];\n const target = (path.length ? path[0] : e.target) as HTMLElement | null;\n const isFormField = (el: HTMLElement | null) => {\n if (!el) return false;\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') return true;\n if (el.isContentEditable) return true;\n return false;\n };\n if (isFormField(target) && (e.key === 'Home' || e.key === 'End')) return;\n if (isFormField(target) && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {\n if ((target as HTMLInputElement).tagName === 'INPUT' && (target as HTMLInputElement).type === 'number') return;\n }\n // Let arrow left/right navigate within text inputs instead of moving cells\n if (isFormField(target) && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) return;\n // Let Enter/Escape be handled by the input's own handlers first\n if (isFormField(target) && (e.key === 'Enter' || e.key === 'Escape')) return;\n if (editing && colType === 'select' && (e.key === 'ArrowDown' || e.key === 'ArrowUp')) return;\n switch (e.key) {\n case 'Tab': {\n e.preventDefault();\n const forward = !e.shiftKey;\n if (forward) {\n if (grid._focusCol < maxCol) grid._focusCol += 1;\n else {\n if (typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n if (grid._focusRow < maxRow) {\n grid._focusRow += 1;\n grid._focusCol = 0;\n }\n }\n } else {\n if (grid._focusCol > 0) grid._focusCol -= 1;\n else if (grid._focusRow > 0) {\n if (typeof grid.commitActiveRowEdit === 'function' && grid._activeEditRows === grid._focusRow)\n grid.commitActiveRowEdit();\n grid._focusRow -= 1;\n grid._focusCol = maxCol;\n }\n }\n ensureCellVisible(grid);\n return;\n }\n case 'ArrowDown':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.min(maxRow, grid._focusRow + 1);\n e.preventDefault();\n break;\n case 'ArrowUp':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.max(0, grid._focusRow - 1);\n e.preventDefault();\n break;\n case 'ArrowRight':\n grid._focusCol = Math.min(maxCol, grid._focusCol + 1);\n e.preventDefault();\n break;\n case 'ArrowLeft':\n grid._focusCol = Math.max(0, grid._focusCol - 1);\n e.preventDefault();\n break;\n case 'Home':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+Home: navigate to first row, first cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = 0;\n grid._focusCol = 0;\n } else {\n // Home: navigate to first cell in current row\n grid._focusCol = 0;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollLeft: true });\n return;\n case 'End':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+End: navigate to last row, last cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = maxRow;\n grid._focusCol = maxCol;\n } else {\n // End: navigate to last cell in current row\n grid._focusCol = maxCol;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollRight: true });\n return;\n case 'PageDown':\n grid._focusRow = Math.min(maxRow, grid._focusRow + 20);\n e.preventDefault();\n break;\n case 'PageUp':\n grid._focusRow = Math.max(0, grid._focusRow - 20);\n e.preventDefault();\n break;\n // NOTE: Enter key is handled by EditingPlugin. If no plugin handles it,\n // we dispatch the unified cell-activate event for custom handling.\n case 'Enter': {\n const rowIndex = grid._focusRow;\n const colIndex = grid._focusCol;\n const column = grid._visibleColumns[colIndex];\n const row = grid._rows[rowIndex];\n const field = column?.field ?? '';\n const value = field && row ? (row as Record<string, unknown>)[field] : undefined;\n const cellEl = (grid as unknown as HTMLElement).querySelector(\n `[data-row=\"${rowIndex}\"][data-col=\"${colIndex}\"]`,\n ) as HTMLElement | undefined;\n\n const detail = {\n rowIndex,\n colIndex,\n field,\n value,\n row,\n cellEl,\n trigger: 'keyboard' as const,\n originalEvent: e,\n };\n\n // Emit unified cell-activate event\n const activateEvent = new CustomEvent('cell-activate', {\n cancelable: true,\n detail,\n });\n (grid as unknown as HTMLElement).dispatchEvent(activateEvent);\n\n // Also emit deprecated activate-cell for backwards compatibility\n const legacyEvent = new CustomEvent('activate-cell', {\n cancelable: true,\n detail: { row: rowIndex, col: colIndex },\n });\n (grid as unknown as HTMLElement).dispatchEvent(legacyEvent);\n\n // If either event was prevented, block further keyboard processing\n if (activateEvent.defaultPrevented || legacyEvent.defaultPrevented) {\n e.preventDefault();\n return;\n }\n // Otherwise allow normal keyboard processing\n break;\n }\n default:\n return;\n }\n ensureCellVisible(grid);\n}\n\n/**\n * Options for ensureCellVisible to control scroll behavior.\n */\ninterface EnsureCellVisibleOptions {\n /** Force scroll to the leftmost position (for Home key) */\n forceScrollLeft?: boolean;\n /** Force scroll to the rightmost position (for End key) */\n forceScrollRight?: boolean;\n}\n\n/**\n * Scroll the viewport (virtualized or static) so the focused cell's row is visible\n * and apply visual focus styling / tabindex management.\n */\nexport function ensureCellVisible(grid: InternalGrid, options?: EnsureCellVisibleOptions): void {\n if (grid._virtualization?.enabled) {\n const { rowHeight, container, viewportEl } = grid._virtualization;\n // container is the faux scrollbar element that handles actual scrolling\n // viewportEl is the visible area element that has the correct height\n const scrollEl = container as HTMLElement | undefined;\n const visibleHeight = viewportEl?.clientHeight ?? scrollEl?.clientHeight ?? 0;\n if (scrollEl && visibleHeight > 0) {\n const y = grid._focusRow * rowHeight;\n if (y < scrollEl.scrollTop) {\n scrollEl.scrollTop = y;\n } else if (y + rowHeight > scrollEl.scrollTop + visibleHeight) {\n scrollEl.scrollTop = y - visibleHeight + rowHeight;\n }\n }\n }\n // Skip refreshVirtualWindow when in edit mode to avoid wiping editors\n const isEditing = grid._activeEditRows !== undefined && grid._activeEditRows !== -1;\n if (!isEditing) {\n grid.refreshVirtualWindow(false);\n }\n clearCellFocus(grid._bodyEl);\n // Clear previous aria-selected markers\n Array.from(grid._bodyEl.querySelectorAll('[aria-selected=\"true\"]')).forEach((el) => {\n el.setAttribute('aria-selected', 'false');\n });\n const rowIndex = grid._focusRow;\n const vStart = grid._virtualization.start ?? 0;\n const vEnd = grid._virtualization.end ?? grid._rows.length;\n if (rowIndex >= vStart && rowIndex < vEnd) {\n const rowEl = grid._bodyEl.querySelectorAll('.data-grid-row')[rowIndex - vStart] as HTMLElement | null;\n // Try exact column match first, then query by data-col, then fallback to first cell (for full-width group rows)\n let cell = rowEl?.children[grid._focusCol] as HTMLElement | undefined;\n if (!cell || !cell.classList?.contains('cell')) {\n cell = (rowEl?.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`) ??\n rowEl?.querySelector('.cell[data-col]')) as HTMLElement | undefined;\n }\n if (cell) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n\n // Horizontal scroll: ensure focused cell is visible in the horizontal scroll area\n // The .tbw-scroll-area element handles horizontal scrolling\n // Skip horizontal scrolling when in edit mode to prevent scroll jumps when editors are created\n const scrollArea = grid.querySelector('.tbw-scroll-area') as HTMLElement | null;\n if (scrollArea && cell && !isEditing) {\n // Handle forced scroll for Home/End keys - always scroll to edge\n if (options?.forceScrollLeft) {\n scrollArea.scrollLeft = 0;\n } else if (options?.forceScrollRight) {\n scrollArea.scrollLeft = scrollArea.scrollWidth - scrollArea.clientWidth;\n } else {\n // Get scroll boundary offsets from plugins (e.g., pinned columns)\n // This allows plugins to report how much of the scroll area they obscure\n // and whether the focused cell should skip scrolling (e.g., pinned cells are always visible)\n const offsets = grid._getHorizontalScrollOffsets?.(rowEl ?? undefined, cell) ?? { left: 0, right: 0 };\n\n if (!offsets.skipScroll) {\n // Get cell position relative to the scroll area\n const cellRect = cell.getBoundingClientRect();\n const scrollAreaRect = scrollArea.getBoundingClientRect();\n // Calculate the cell's position relative to scroll area's visible region\n const cellLeft = cellRect.left - scrollAreaRect.left + scrollArea.scrollLeft;\n const cellRight = cellLeft + cellRect.width;\n // Adjust visible boundaries to account for plugin-reported offsets\n const visibleLeft = scrollArea.scrollLeft + offsets.left;\n const visibleRight = scrollArea.scrollLeft + scrollArea.clientWidth - offsets.right;\n // Scroll horizontally if needed\n if (cellLeft < visibleLeft) {\n scrollArea.scrollLeft = cellLeft - offsets.left;\n } else if (cellRight > visibleRight) {\n scrollArea.scrollLeft = cellRight - scrollArea.clientWidth + offsets.right;\n }\n }\n }\n }\n\n if (grid._activeEditRows !== undefined && grid._activeEditRows !== -1 && cell.classList.contains('editing')) {\n const focusTarget = cell.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n if (focusTarget && document.activeElement !== focusTarget) {\n try {\n focusTarget.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n } else if (!cell.contains(document.activeElement)) {\n if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '-1');\n try {\n cell.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n }\n }\n}\n","/**\n * Event Delegation Module\n *\n * Consolidates all delegated event handling for the grid.\n * Uses event delegation (single listener on container) rather than per-cell/per-row\n * listeners to minimize memory usage.\n *\n * This module provides:\n * - setupCellEventDelegation: Body-level handlers (mousedown, click, dblclick on cells/rows)\n * - setupRootEventDelegation: Root-level handlers (keydown, mousedown for plugins, drag tracking)\n *\n * Edit triggering is handled separately by the EditingPlugin via\n * onCellClick and onKeyDown hooks.\n */\n\nimport type { CellMouseEvent } from '../plugin/types';\nimport type { InternalGrid } from '../types';\nimport { handleGridKeyDown } from './keyboard';\nimport { handleRowClick } from './rows';\nimport { clearCellFocus, getColIndexFromCell, getRowIndexFromCell } from './utils';\n\n// Track drag state per grid instance (avoids polluting InternalGrid interface)\nconst dragState = new WeakMap<InternalGrid, boolean>();\n\n/**\n * Handle delegated mousedown on cells.\n * Updates focus position for navigation.\n *\n * IMPORTANT: This must NOT call refreshVirtualWindow or any function that\n * re-renders DOM elements. Doing so would replace the element the user clicked on,\n * causing the subsequent click event to fire on a detached element and not bubble\n * to parent handlers (like handleRowClick).\n *\n * For mouse interactions, the cell is already visible (user clicked on it),\n * so we only need to update focus state without scrolling or re-rendering.\n */\nfunction handleCellMousedown(grid: InternalGrid, cell: HTMLElement): void {\n const rowIndex = getRowIndexFromCell(cell);\n const colIndex = getColIndexFromCell(cell);\n if (rowIndex < 0 || colIndex < 0) return;\n\n grid._focusRow = rowIndex;\n grid._focusCol = colIndex;\n\n // Update focus styling directly without triggering re-render.\n // ensureCellVisible() would call refreshVirtualWindow() which replaces DOM elements,\n // breaking the click event that follows this mousedown.\n clearCellFocus(grid._bodyEl);\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n}\n\n/**\n * Build a CellMouseEvent from a native MouseEvent.\n * Extracts cell/row information from the event target.\n */\nfunction buildCellMouseEvent(\n grid: InternalGrid,\n renderRoot: HTMLElement,\n e: MouseEvent,\n type: 'mousedown' | 'mousemove' | 'mouseup',\n): CellMouseEvent {\n // For document-level events (mousemove/mouseup during drag), e.target won't be inside shadow DOM.\n // Use composedPath to find elements inside shadow roots, or fall back to elementFromPoint.\n let target: Element | null = null;\n\n // composedPath gives us the full path including shadow DOM elements\n const path = e.composedPath?.() as Element[] | undefined;\n if (path && path.length > 0) {\n target = path[0];\n } else {\n target = e.target as Element;\n }\n\n // If target is not inside our element (e.g., for document-level events),\n // use elementFromPoint to find the actual element under the mouse\n if (target && !renderRoot.contains(target)) {\n const elAtPoint = document.elementFromPoint(e.clientX, e.clientY);\n if (elAtPoint) {\n target = elAtPoint;\n }\n }\n\n // Cells have data-col and data-row attributes\n const cellEl = target?.closest?.('[data-col]') as HTMLElement | null;\n const rowEl = target?.closest?.('.data-grid-row') as HTMLElement | null;\n const headerEl = target?.closest?.('.header-row') as HTMLElement | null;\n\n let rowIndex: number | undefined;\n let colIndex: number | undefined;\n let row: unknown;\n let field: string | undefined;\n let value: unknown;\n let column: unknown;\n\n if (cellEl) {\n // Get indices from cell attributes\n rowIndex = parseInt(cellEl.getAttribute('data-row') ?? '-1', 10);\n colIndex = parseInt(cellEl.getAttribute('data-col') ?? '-1', 10);\n if (rowIndex >= 0 && colIndex >= 0) {\n row = grid._rows[rowIndex];\n column = grid._columns[colIndex];\n field = (column as { field?: string })?.field;\n value = row && field ? (row as Record<string, unknown>)[field] : undefined;\n }\n }\n\n return {\n type,\n row,\n rowIndex: rowIndex !== undefined && rowIndex >= 0 ? rowIndex : undefined,\n colIndex: colIndex !== undefined && colIndex >= 0 ? colIndex : undefined,\n field,\n value,\n column: column as CellMouseEvent['column'],\n originalEvent: e,\n cellElement: cellEl ?? undefined,\n rowElement: rowEl ?? undefined,\n isHeader: !!headerEl,\n cell:\n rowIndex !== undefined && colIndex !== undefined && rowIndex >= 0 && colIndex >= 0\n ? { row: rowIndex, col: colIndex }\n : undefined,\n };\n}\n\n/**\n * Handle mousedown events and dispatch to plugin system.\n */\nfunction handleMouseDown(grid: InternalGrid, renderRoot: HTMLElement, e: MouseEvent): void {\n const event = buildCellMouseEvent(grid, renderRoot, e, 'mousedown');\n const handled = grid._dispatchCellMouseDown?.(event) ?? false;\n\n // If any plugin handled mousedown, start tracking for drag\n if (handled) {\n dragState.set(grid, true);\n }\n}\n\n/**\n * Handle mousemove events (only when dragging).\n */\nfunction handleMouseMove(grid: InternalGrid, renderRoot: HTMLElement, e: MouseEvent): void {\n if (!dragState.get(grid)) return;\n\n const event = buildCellMouseEvent(grid, renderRoot, e, 'mousemove');\n grid._dispatchCellMouseMove?.(event);\n}\n\n/**\n * Handle mouseup events.\n */\nfunction handleMouseUp(grid: InternalGrid, renderRoot: HTMLElement, e: MouseEvent): void {\n if (!dragState.get(grid)) return;\n\n const event = buildCellMouseEvent(grid, renderRoot, e, 'mouseup');\n grid._dispatchCellMouseUp?.(event);\n dragState.set(grid, false);\n}\n\n/**\n * Set up delegated event listeners on the grid body.\n * Consolidates all row/cell mouse event handling into a single set of listeners.\n * Call once during grid initialization.\n *\n * Benefits:\n * - 3 listeners total vs N*2 listeners (where N = pool size)\n * - Consistent event handling across all rows\n * - Automatic cleanup via AbortController signal\n *\n * @param grid - The grid instance\n * @param bodyEl - The .rows element containing all data rows\n * @param signal - AbortSignal for cleanup\n */\nexport function setupCellEventDelegation(grid: InternalGrid, bodyEl: HTMLElement, signal: AbortSignal): void {\n // Mousedown - update focus on any cell (not just editable)\n bodyEl.addEventListener(\n 'mousedown',\n (e) => {\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n // Skip if clicking inside an editing cell (let the editor handle it)\n if (cell.classList.contains('editing')) return;\n\n handleCellMousedown(grid, cell);\n },\n { signal },\n );\n\n // Click - handle row/cell click interactions\n bodyEl.addEventListener(\n 'click',\n (e) => {\n const rowEl = (e.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n if (rowEl) handleRowClick(grid, e as MouseEvent, rowEl);\n },\n { signal },\n );\n\n // Dblclick - same handler as click (edit triggering handled by EditingPlugin)\n bodyEl.addEventListener(\n 'dblclick',\n (e) => {\n const rowEl = (e.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n if (rowEl) handleRowClick(grid, e as MouseEvent, rowEl);\n },\n { signal },\n );\n}\n\n/**\n * Set up root-level and document-level event listeners.\n * These are added once per grid lifetime (not re-attached on DOM recreation).\n *\n * Includes:\n * - keydown: Keyboard navigation (arrows, Enter, Escape)\n * - mousedown: Plugin dispatch for cell interactions\n * - mousemove/mouseup: Global drag tracking\n *\n * @param grid - The grid instance\n * @param gridElement - The grid element (for keydown)\n * @param renderRoot - The render root element (for mousedown)\n * @param signal - AbortSignal for cleanup\n */\nexport function setupRootEventDelegation(\n grid: InternalGrid,\n gridElement: HTMLElement,\n renderRoot: HTMLElement,\n signal: AbortSignal,\n): void {\n // Element-level keydown handler for keyboard navigation\n gridElement.addEventListener('keydown', (e) => handleGridKeyDown(grid, e), { signal });\n\n // Central mouse event handling for plugins\n renderRoot.addEventListener('mousedown', (e) => handleMouseDown(grid, renderRoot, e as MouseEvent), { signal });\n\n // Track global mousemove/mouseup for drag operations (column resize, selection, etc.)\n document.addEventListener('mousemove', (e: MouseEvent) => handleMouseMove(grid, renderRoot, e), { signal });\n document.addEventListener('mouseup', (e: MouseEvent) => handleMouseUp(grid, renderRoot, e), { signal });\n}\n","/**\n * Sorting Module\n *\n * Handles column sorting state transitions and row ordering.\n */\n\nimport type { ColumnConfig, InternalGrid, SortHandler, SortState } from '../types';\nimport { renderHeader } from './header';\n\n/**\n * Default comparator for sorting values.\n * Handles nulls (pushed to end), numbers, and string fallback.\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n return a > b ? 1 : a < b ? -1 : 0;\n}\n\n/**\n * Built-in sort implementation using column comparator or default.\n * This is the default sortHandler when none is configured.\n */\nexport function builtInSort<T>(rows: T[], sortState: SortState, columns: ColumnConfig<T>[]): T[] {\n const col = columns.find((c) => c.field === sortState.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n const { field, direction } = sortState;\n\n return [...rows].sort((rA: any, rB: any) => {\n return comparator(rA[field], rB[field], rA, rB) * direction;\n });\n}\n\n/**\n * Apply sort result to grid and update UI.\n * Called after sync or async sort completes.\n */\nfunction finalizeSortResult<T>(grid: InternalGrid<T>, sortedRows: T[], col: ColumnConfig<T>, dir: 1 | -1): void {\n grid._rows = sortedRows;\n // Bump epoch so renderVisibleRows triggers full inline rebuild\n grid.__rowRenderEpoch++;\n // Invalidate pooled rows to guarantee rebuild\n grid._rowPool.forEach((r) => (r.__epoch = -1));\n renderHeader(grid);\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: dir } }),\n );\n // Trigger state change after sort applied\n grid.requestStateChange?.();\n}\n\n/**\n * Cycle sort state for a column: none -> ascending -> descending -> none.\n * Restores original row order when clearing sort.\n */\nexport function toggleSort(grid: InternalGrid, col: ColumnConfig<any>): void {\n if (!grid._sortState || grid._sortState.field !== col.field) {\n if (!grid._sortState) grid.__originalOrder = grid._rows.slice();\n applySort(grid, col, 1);\n } else if (grid._sortState.direction === 1) {\n applySort(grid, col, -1);\n } else {\n grid._sortState = null;\n // Force full row rebuild after clearing sort so templated cells reflect original order\n grid.__rowRenderEpoch++;\n // Invalidate existing pooled row epochs so virtualization triggers a full inline rebuild\n grid._rowPool.forEach((r) => (r.__epoch = -1));\n grid._rows = grid.__originalOrder.slice();\n renderHeader(grid);\n // After re-render ensure cleared column shows aria-sort=\"none\" baseline.\n const headers = grid._headerRowEl?.querySelectorAll('[role=\"columnheader\"].sortable');\n headers?.forEach((h) => {\n if (!h.getAttribute('aria-sort')) h.setAttribute('aria-sort', 'none');\n else if (h.getAttribute('aria-sort') === 'ascending' || h.getAttribute('aria-sort') === 'descending') {\n // The active column was re-rendered already, but normalize any missing ones.\n if (!grid._sortState) h.setAttribute('aria-sort', 'none');\n }\n });\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: 0 } }),\n );\n // Trigger state change after sort is cleared\n grid.requestStateChange?.();\n }\n}\n\n/**\n * Apply a concrete sort direction to rows.\n *\n * Uses custom sortHandler from gridConfig if provided, otherwise uses built-in sorting.\n * Supports both sync and async handlers (for server-side sorting).\n */\nexport function applySort(grid: InternalGrid, col: ColumnConfig<any>, dir: 1 | -1): void {\n grid._sortState = { field: col.field, direction: dir };\n\n const sortState: SortState = { field: col.field, direction: dir };\n const columns = grid._columns as ColumnConfig<any>[];\n\n // Get custom handler from effectiveConfig, or use built-in\n const handler: SortHandler<any> = grid.effectiveConfig?.sortHandler ?? builtInSort;\n\n const result = handler(grid._rows, sortState, columns);\n\n // Handle async (Promise) or sync result\n if (result && typeof (result as Promise<unknown[]>).then === 'function') {\n // Async handler - wait for result\n (result as Promise<unknown[]>).then((sortedRows) => {\n finalizeSortResult(grid, sortedRows, col, dir);\n });\n } else {\n // Sync handler - apply immediately\n finalizeSortResult(grid, result as unknown[], col, dir);\n }\n}\n","/**\n * Header Rendering Module\n *\n * Handles rendering of the grid header row with sorting and resize affordances.\n */\n\nimport type { ColumnInternal, IconValue, InternalGrid } from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\nimport { addPart } from './columns';\nimport { toggleSort } from './sorting';\n\n/**\n * Set an icon value on an element. Handles both string and HTMLElement icons.\n */\nfunction setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.textContent = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n}\n\n/**\n * Rebuild the header row DOM based on current column configuration, attaching\n * sorting and resize affordances where enabled.\n */\nexport function renderHeader(grid: InternalGrid): void {\n grid._headerRowEl = grid.findHeaderRow!();\n const headerRow = grid._headerRowEl as HTMLElement;\n\n // Guard: DOM may not be built yet\n if (!headerRow) {\n return;\n }\n\n headerRow.innerHTML = '';\n\n grid._visibleColumns.forEach((col: ColumnInternal, i: number) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n addPart(cell, 'header-cell');\n cell.setAttribute('role', 'columnheader');\n\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(i + 1));\n cell.setAttribute('data-field', col.field);\n cell.setAttribute('data-col', String(i)); // Add data-col for consistency with body cells\n\n // Column grouping styling is handled by the grouping-columns plugin via afterRender\n const maybeTpl = col.__headerTemplate;\n if (maybeTpl) Array.from(maybeTpl.childNodes).forEach((n) => cell.appendChild(n.cloneNode(true)));\n else {\n // Use header if defined (including empty string), otherwise fall back to field name\n const label = col.header ?? col.field;\n const span = document.createElement('span');\n span.textContent = label;\n cell.appendChild(span);\n }\n if (col.sortable) {\n cell.classList.add('sortable');\n cell.tabIndex = 0;\n const icon = document.createElement('span');\n addPart(icon, 'sort-indicator');\n const active = grid._sortState?.field === col.field ? grid._sortState.direction : 0;\n // Use grid-level icons (fall back to defaults)\n const icons = { ...DEFAULT_GRID_ICONS, ...grid.icons };\n const iconValue = active === 1 ? icons.sortAsc : active === -1 ? icons.sortDesc : icons.sortNone;\n setIcon(icon, iconValue);\n cell.appendChild(icon);\n // Always set a baseline aria-sort for sortable headers for assistive tech clarity.\n cell.setAttribute('aria-sort', active === 0 ? 'none' : active === 1 ? 'ascending' : 'descending');\n cell.addEventListener('click', (e) => {\n // Ignore clicks that are the result of a resize drag ending\n if (grid._resizeController?.isResizing) return;\n // Let plugins handle the click first (e.g., multi-sort)\n if (grid._dispatchHeaderClick?.(e, i, cell)) return;\n toggleSort(grid, col);\n });\n cell.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n // Let plugins handle the keydown first\n if (grid._dispatchHeaderClick?.(e as unknown as MouseEvent, i, cell)) return;\n toggleSort(grid, col);\n }\n });\n }\n if (col.resizable) {\n // Add class for resize handle positioning context (CSS provides position: relative)\n // Note: If a plugin applies position: sticky (e.g., PinnedColumnsPlugin), it will override this\n cell.classList.add('resizable');\n const handle = document.createElement('div');\n handle.className = 'resize-handle';\n handle.setAttribute('aria-hidden', 'true');\n handle.addEventListener('mousedown', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.start(e, i, cell);\n });\n // Double-click to reset column width to default\n handle.addEventListener('dblclick', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.resetColumn(i);\n });\n cell.appendChild(handle);\n }\n headerRow.appendChild(cell);\n });\n\n // Ensure every sortable header has a baseline aria-sort if not already set during construction.\n headerRow.querySelectorAll('.cell.sortable').forEach((el) => {\n if (!el.getAttribute('aria-sort')) el.setAttribute('aria-sort', 'none');\n });\n\n // Set ARIA role only if header has children (role=\"row\" requires columnheader children)\n // When grid is cleared with 0 columns, the header row should not have role=\"row\"\n if (headerRow.children.length > 0) {\n headerRow.setAttribute('role', 'row');\n headerRow.setAttribute('aria-rowindex', '1');\n } else {\n headerRow.removeAttribute('role');\n headerRow.removeAttribute('aria-rowindex');\n }\n}\n","/**\n * Idle Scheduler - Defer non-critical work to browser idle periods.\n *\n * Uses requestIdleCallback where available, with fallback to setTimeout.\n * This allows the main thread to remain responsive during startup.\n */\n\n/**\n * Check if requestIdleCallback is available (not in Safari < 17.4).\n */\nconst hasIdleCallback = typeof requestIdleCallback === 'function';\n\n/**\n * IdleDeadline-compatible interface for fallback.\n */\ninterface IdleDeadlineLike {\n didTimeout: boolean;\n timeRemaining(): number;\n}\n\n/**\n * Schedule work to run during browser idle time.\n * Falls back to setTimeout(0) if requestIdleCallback is not available.\n *\n * @param callback - Work to run when idle\n * @param options - Optional timeout configuration\n * @returns Handle for cancellation\n */\nexport function scheduleIdle(callback: (deadline: IdleDeadlineLike) => void, options?: { timeout?: number }): number {\n if (hasIdleCallback) {\n return requestIdleCallback(callback, options);\n }\n\n // Fallback for Safari (before 17.4) and older browsers\n return window.setTimeout(() => {\n const start = Date.now();\n callback({\n didTimeout: false,\n timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),\n });\n }, 1) as unknown as number;\n}\n\n/**\n * Cancel a scheduled idle callback.\n */\nexport function cancelIdle(handle: number): void {\n if (hasIdleCallback) {\n cancelIdleCallback(handle);\n } else {\n clearTimeout(handle);\n }\n}\n\n/**\n * Queue of deferred tasks to run during idle periods.\n */\ninterface DeferredTask {\n fn: () => void;\n priority: number; // Lower = higher priority\n}\n\n/**\n * Deferred work queue that runs tasks during idle periods.\n * Groups related work and processes in priority order.\n */\nexport class IdleQueue {\n private tasks: DeferredTask[] = [];\n private scheduled = false;\n private handle: number | null = null;\n\n /**\n * Add a task to the queue.\n * @param fn - Function to execute\n * @param priority - Priority (lower = run sooner). Default 10.\n */\n add(fn: () => void, priority = 10): void {\n this.tasks.push({ fn, priority });\n\n if (!this.scheduled) {\n this.scheduled = true;\n this.handle = scheduleIdle((deadline) => this.process(deadline), { timeout: 100 });\n }\n }\n\n /**\n * Process tasks until we run out of time or tasks.\n */\n private process(deadline: IdleDeadlineLike): void {\n // Sort by priority (stable sort for same priority)\n this.tasks.sort((a, b) => a.priority - b.priority);\n\n // Process tasks while we have time\n while (this.tasks.length > 0 && (deadline.timeRemaining() > 0 || deadline.didTimeout)) {\n const task = this.tasks.shift();\n if (task) {\n try {\n task.fn();\n } catch (error) {\n console.error('[IdleQueue] Task error:', error);\n }\n }\n }\n\n // If tasks remain, schedule another idle callback\n if (this.tasks.length > 0) {\n this.handle = scheduleIdle((d) => this.process(d), { timeout: 100 });\n } else {\n this.scheduled = false;\n this.handle = null;\n }\n }\n\n /**\n * Cancel all pending tasks.\n */\n cancel(): void {\n this.tasks = [];\n if (this.handle !== null) {\n cancelIdle(this.handle);\n this.handle = null;\n }\n this.scheduled = false;\n }\n\n /**\n * Check if the queue is empty.\n */\n get isEmpty(): boolean {\n return this.tasks.length === 0;\n }\n}\n","/**\n * Centralized Render Scheduler for the Grid component.\n *\n * This scheduler batches all rendering work into a single requestAnimationFrame,\n * eliminating race conditions between different parts of the grid (ResizeObserver,\n * framework adapters, virtualization, etc.) that previously scheduled independent RAFs.\n *\n * ## Design Principles\n *\n * 1. **Single RAF per frame**: All render requests are batched into one RAF callback\n * 2. **Phase-based execution**: Work is organized into ordered phases that run sequentially\n * 3. **Highest-phase wins**: Multiple requests merge to the highest requested phase\n * 4. **Framework-agnostic timing**: Eliminates need for \"double RAF\" hacks\n *\n * ## Render Phases (execute in order)\n *\n * - STYLE (1): Lightweight style/class updates, plugin afterRender hooks\n * - VIRTUALIZATION (2): Virtual window recalculation (scroll, resize)\n * - HEADER (3): Header re-render only\n * - ROWS (4): Row model rebuild + header + template + virtual window\n * - COLUMNS (5): Column processing + rows phase work\n * - FULL (6): Complete render including config merge\n *\n * @example\n * ```typescript\n * const scheduler = new RenderScheduler({\n * mergeConfig: () => this.#mergeEffectiveConfig(),\n * processColumns: () => this.#processColumns(),\n * processRows: () => this.#rebuildRowModel(),\n * renderHeader: () => renderHeader(this),\n * updateTemplate: () => updateTemplate(this),\n * renderVirtualWindow: () => this.refreshVirtualWindow(true),\n * afterRender: () => this.#pluginManager?.afterRender(),\n * isConnected: () => this.isConnected && this.#connected,\n * });\n *\n * // Request a full render\n * scheduler.requestPhase(RenderPhase.FULL, 'initial-setup');\n *\n * // Wait for render to complete\n * await scheduler.whenReady();\n * ```\n */\n\n/**\n * Render phases in order of execution.\n * Higher phases include all lower phase work.\n *\n * @category Plugin Development\n */\nexport enum RenderPhase {\n /** Lightweight style updates only (plugin afterRender hooks) */\n STYLE = 1,\n /** Virtual window recalculation (includes STYLE) */\n VIRTUALIZATION = 2,\n /** Header re-render (includes VIRTUALIZATION) */\n HEADER = 3,\n /** Row model rebuild (includes HEADER) */\n ROWS = 4,\n /** Column processing (includes ROWS) */\n COLUMNS = 5,\n /** Full render including config merge (includes COLUMNS) */\n FULL = 6,\n}\n\n/**\n/**\n * Callbacks that the scheduler invokes during flush.\n * Each callback corresponds to work done in a specific phase.\n */\nexport interface RenderCallbacks {\n /** Merge effective config (FULL phase) */\n mergeConfig: () => void;\n /** Process columns through plugins (COLUMNS phase) */\n processColumns: () => void;\n /** Rebuild row model through plugins (ROWS phase) */\n processRows: () => void;\n /** Render header DOM (HEADER phase) */\n renderHeader: () => void;\n /** Update CSS grid template (ROWS phase) */\n updateTemplate: () => void;\n /** Recalculate virtual window (VIRTUALIZATION phase) */\n renderVirtualWindow: () => void;\n /** Run plugin afterRender hooks (STYLE phase) */\n afterRender: () => void;\n /** Check if grid is still connected to DOM */\n isConnected: () => boolean;\n}\n\n/**\n * Centralized render scheduler that batches all grid rendering into single RAF.\n */\nexport class RenderScheduler {\n readonly #callbacks: RenderCallbacks;\n\n /** Current pending phase (0 = none pending) */\n #pendingPhase: RenderPhase | 0 = 0;\n\n /** RAF handle for cancellation */\n #rafHandle = 0;\n\n /** Promise that resolves when current render completes */\n #readyPromise: Promise<void> | null = null;\n #readyResolve: (() => void) | null = null;\n\n /** Initial ready resolver (for component's initial ready() promise) */\n #initialReadyResolver: (() => void) | null = null;\n #initialReadyFired = false;\n\n constructor(callbacks: RenderCallbacks) {\n this.#callbacks = callbacks;\n }\n\n /**\n * Request a render at the specified phase.\n * Multiple requests are batched - the highest phase wins.\n *\n * @param phase - The render phase to execute\n * @param _source - Debug identifier for what triggered this request (unused, kept for API compatibility)\n */\n requestPhase(phase: RenderPhase, _source: string): void {\n // Merge to highest phase\n if (phase > this.#pendingPhase) {\n this.#pendingPhase = phase;\n }\n\n // Schedule RAF if not already scheduled\n if (this.#rafHandle === 0) {\n this.#ensureReadyPromise();\n this.#rafHandle = requestAnimationFrame(() => this.#flush());\n }\n }\n\n /**\n * Returns a promise that resolves when the current render cycle completes.\n * If no render is pending, returns an already-resolved promise.\n */\n whenReady(): Promise<void> {\n if (this.#readyPromise) {\n return this.#readyPromise;\n }\n return Promise.resolve();\n }\n\n /**\n * Set the initial ready resolver (called once on first render).\n * This connects to the grid's `ready()` promise.\n */\n setInitialReadyResolver(resolver: () => void): void {\n this.#initialReadyResolver = resolver;\n }\n\n /**\n * Cancel any pending render.\n * Useful for cleanup when component disconnects.\n */\n cancel(): void {\n if (this.#rafHandle !== 0) {\n cancelAnimationFrame(this.#rafHandle);\n this.#rafHandle = 0;\n }\n this.#pendingPhase = 0;\n\n // Resolve any pending ready promise (don't leave it hanging)\n if (this.#readyResolve) {\n this.#readyResolve();\n this.#readyResolve = null;\n this.#readyPromise = null;\n }\n }\n\n /**\n * Check if a render is currently pending.\n */\n get isPending(): boolean {\n return this.#pendingPhase !== 0;\n }\n\n /**\n * Get the current pending phase (0 if none).\n */\n get pendingPhase(): RenderPhase | 0 {\n return this.#pendingPhase;\n }\n\n // ─────────────────────────────────────────────────────────────────────────\n // Private Methods\n // ─────────────────────────────────────────────────────────────────────────\n\n #ensureReadyPromise(): void {\n if (!this.#readyPromise) {\n this.#readyPromise = new Promise<void>((resolve) => {\n this.#readyResolve = resolve;\n });\n }\n }\n\n /**\n * Execute all pending render work in phase order.\n * This is the single RAF callback that does all rendering.\n */\n #flush(): void {\n this.#rafHandle = 0;\n\n // Bail if component disconnected\n if (!this.#callbacks.isConnected()) {\n this.#pendingPhase = 0;\n if (this.#readyResolve) {\n this.#readyResolve();\n this.#readyResolve = null;\n this.#readyPromise = null;\n }\n return;\n }\n\n const phase = this.#pendingPhase;\n this.#pendingPhase = 0;\n\n // Execute phases in order (higher phases include lower phase work)\n // The execution order respects data dependencies:\n // mergeConfig → processRows → processColumns → renderHeader → virtualWindow → afterRender\n\n // mergeConfig runs for FULL phase OR COLUMNS phase (to pick up framework adapter renderers)\n // IMPORTANT: mergeConfig must run BEFORE processRows because the row model depends on\n // column configuration, and framework adapters (React/Angular) may register renderers\n // asynchronously after the initial gridConfig is set.\n if (phase >= RenderPhase.COLUMNS) {\n this.#callbacks.mergeConfig();\n }\n\n // Phase 4 (ROWS): Rebuild row model\n // NOTE: processRows MUST run before processColumns because tree plugin's\n // processColumns depends on flattenedRows populated by processRows\n if (phase >= RenderPhase.ROWS) {\n this.#callbacks.processRows();\n }\n\n // Phase 5 (COLUMNS): Process columns + update template\n if (phase >= RenderPhase.COLUMNS) {\n this.#callbacks.processColumns();\n this.#callbacks.updateTemplate();\n }\n\n // Phase 3 (HEADER): Render header\n if (phase >= RenderPhase.HEADER) {\n this.#callbacks.renderHeader();\n }\n\n // Phase 2 (VIRTUALIZATION): Recalculate virtual window\n if (phase >= RenderPhase.VIRTUALIZATION) {\n this.#callbacks.renderVirtualWindow();\n }\n\n // Phase 1 (STYLE): Run afterRender hooks (always runs)\n if (phase >= RenderPhase.STYLE) {\n this.#callbacks.afterRender();\n }\n\n // Fire initial ready resolver once\n if (!this.#initialReadyFired && this.#initialReadyResolver) {\n this.#initialReadyFired = true;\n this.#initialReadyResolver();\n }\n\n // Resolve the ready promise\n if (this.#readyResolve) {\n this.#readyResolve();\n this.#readyResolve = null;\n this.#readyPromise = null;\n }\n }\n}\n","import type { InternalGrid, ResizeController } from '../types';\n\nexport function createResizeController(grid: InternalGrid): ResizeController {\n let resizeState: { startX: number; colIndex: number; startWidth: number } | null = null;\n let pendingRaf: number | null = null;\n let prevCursor: string | null = null;\n let prevUserSelect: string | null = null;\n const onMove = (e: MouseEvent) => {\n if (!resizeState) return;\n const delta = e.clientX - resizeState.startX;\n const width = Math.max(40, resizeState.startWidth + delta);\n const col = grid._visibleColumns[resizeState.colIndex];\n col.width = width;\n col.__userResized = true;\n col.__renderedWidth = width;\n if (pendingRaf == null) {\n pendingRaf = requestAnimationFrame(() => {\n pendingRaf = null;\n grid.updateTemplate?.();\n });\n }\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize', { detail: { field: col.field, width } }),\n );\n };\n let justFinishedResize = false;\n const onUp = () => {\n const hadResize = resizeState !== null;\n // Set flag to suppress click events that fire immediately after mouseup\n if (hadResize) {\n justFinishedResize = true;\n requestAnimationFrame(() => {\n justFinishedResize = false;\n });\n }\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n if (prevCursor !== null) {\n document.documentElement.style.cursor = prevCursor;\n prevCursor = null;\n }\n if (prevUserSelect !== null) {\n document.body.style.userSelect = prevUserSelect;\n prevUserSelect = null;\n }\n resizeState = null;\n // Trigger state change after resize completes\n if (hadResize && grid.requestStateChange) {\n grid.requestStateChange();\n }\n };\n return {\n get isResizing() {\n return resizeState !== null || justFinishedResize;\n },\n start(e, colIndex, cell) {\n e.preventDefault();\n // Use the column's configured/rendered width, not the cell's bounding rect.\n // The bounding rect can be incorrect if CSS grid-column spanning is in effect\n // (e.g., when previous columns are display:none and this cell spans multiple tracks).\n const col = grid._visibleColumns[colIndex];\n // Only use numeric widths; string widths (e.g., \"100px\", \"20%\") fall back to bounding rect\n const colWidth = typeof col?.width === 'number' ? col.width : undefined;\n const startWidth = col?.__renderedWidth ?? colWidth ?? cell.getBoundingClientRect().width;\n resizeState = { startX: e.clientX, colIndex, startWidth };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n if (prevCursor === null) prevCursor = document.documentElement.style.cursor;\n document.documentElement.style.cursor = 'e-resize';\n if (prevUserSelect === null) prevUserSelect = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n },\n resetColumn(colIndex) {\n const col = grid._visibleColumns[colIndex];\n if (!col) return;\n\n // Reset to original configured width (or undefined for auto-sizing)\n col.__userResized = false;\n col.__renderedWidth = undefined;\n col.width = col.__originalWidth;\n\n grid.updateTemplate?.();\n grid.requestStateChange?.();\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize-reset', { detail: { field: col.field, width: col.width } }),\n );\n },\n dispose() {\n onUp();\n },\n };\n}\n","/**\n * Row Animation Module\n *\n * Provides row-level animation utilities for the grid.\n * Animations are CSS-based and triggered via data attributes.\n *\n * Supported animations:\n * - `change`: Flash highlight for modified rows (e.g., after editing)\n * - `insert`: Slide-in animation for newly added rows\n * - `remove`: Fade-out animation for rows being removed\n *\n * @module internal/row-animation\n */\n\nimport type { InternalGrid, RowAnimationType } from '../types';\n\n/**\n * Data attribute used to trigger row animations via CSS.\n */\nconst ANIMATION_ATTR = 'data-animating';\n\n/**\n * Map of animation types to their CSS custom property duration names.\n */\nconst DURATION_PROPS: Record<RowAnimationType, string> = {\n change: '--tbw-row-change-duration',\n insert: '--tbw-row-insert-duration',\n remove: '--tbw-row-remove-duration',\n};\n\n/**\n * Default animation durations in milliseconds.\n */\nconst DEFAULT_DURATIONS: Record<RowAnimationType, number> = {\n change: 500,\n insert: 300,\n remove: 200,\n};\n\n/**\n * Parse a CSS duration string (e.g., \"500ms\", \"0.5s\") to milliseconds.\n */\nfunction parseDuration(value: string): number {\n const trimmed = value.trim().toLowerCase();\n if (trimmed.endsWith('ms')) {\n return parseFloat(trimmed);\n }\n if (trimmed.endsWith('s')) {\n return parseFloat(trimmed) * 1000;\n }\n return parseFloat(trimmed);\n}\n\n/**\n * Get the animation duration for a row element.\n * Reads from CSS custom property or falls back to default.\n */\nfunction getAnimationDuration(rowEl: HTMLElement, animationType: RowAnimationType): number {\n const prop = DURATION_PROPS[animationType];\n const computed = getComputedStyle(rowEl).getPropertyValue(prop);\n if (computed) {\n const parsed = parseDuration(computed);\n if (!isNaN(parsed) && parsed > 0) {\n return parsed;\n }\n }\n return DEFAULT_DURATIONS[animationType];\n}\n\n/**\n * Animate a single row element.\n *\n * @param rowEl - The row DOM element to animate\n * @param animationType - The type of animation to apply\n * @param onComplete - Optional callback when animation completes\n */\nexport function animateRowElement(rowEl: HTMLElement, animationType: RowAnimationType, onComplete?: () => void): void {\n // Remove any existing animation first (allows re-triggering)\n rowEl.removeAttribute(ANIMATION_ATTR);\n\n // Force a reflow to restart the animation\n void rowEl.offsetWidth;\n\n // Apply the animation\n rowEl.setAttribute(ANIMATION_ATTR, animationType);\n\n // Get duration and schedule cleanup\n const duration = getAnimationDuration(rowEl, animationType);\n\n setTimeout(() => {\n // For 'remove' animations, skip removing the attribute since the element\n // will be destroyed by the onComplete callback. This prevents a visual\n // flash where the element snaps back to its original state.\n if (animationType !== 'remove') {\n rowEl.removeAttribute(ANIMATION_ATTR);\n }\n onComplete?.();\n }, duration);\n}\n\n/**\n * Animate a row by its data index.\n *\n * @param grid - The grid instance\n * @param rowIndex - The data row index (not DOM position)\n * @param animationType - The type of animation to apply\n * @returns true if the row was found and animated, false otherwise\n */\nexport function animateRow<T>(grid: InternalGrid<T>, rowIndex: number, animationType: RowAnimationType): boolean {\n // Guard against invalid indices\n if (rowIndex < 0) {\n return false;\n }\n\n const rowEl = grid.findRenderedRowElement?.(rowIndex);\n if (!rowEl) {\n // Row is virtualized out of view - nothing to animate\n return false;\n }\n\n animateRowElement(rowEl, animationType);\n return true;\n}\n\n/**\n * Animate multiple rows by their data indices.\n *\n * @param grid - The grid instance\n * @param rowIndices - Array of data row indices to animate\n * @param animationType - The type of animation to apply\n * @returns Number of rows that were actually animated (visible in viewport)\n */\nexport function animateRows<T>(grid: InternalGrid<T>, rowIndices: number[], animationType: RowAnimationType): number {\n let animatedCount = 0;\n for (const rowIndex of rowIndices) {\n if (animateRow(grid, rowIndex, animationType)) {\n animatedCount++;\n }\n }\n return animatedCount;\n}\n\n/**\n * Animate a row by its ID.\n *\n * @param grid - The grid instance\n * @param rowId - The row ID (requires getRowId to be configured)\n * @param animationType - The type of animation to apply\n * @returns true if the row was found and animated, false otherwise\n */\nexport function animateRowById<T>(grid: InternalGrid<T>, rowId: string, animationType: RowAnimationType): boolean {\n // Find row index by searching _rows\n const rows = grid._rows ?? [];\n const getRowId = grid.getRowId;\n if (!getRowId) {\n return false;\n }\n\n const rowIndex = rows.findIndex((row) => getRowId(row) === rowId);\n if (rowIndex < 0) {\n return false;\n }\n return animateRow(grid, rowIndex, animationType);\n}\n","/**\n * DOM Builder - Direct DOM construction for performance.\n *\n * Using direct DOM APIs instead of innerHTML is significantly faster:\n * - No HTML parsing by the browser\n * - No template string concatenation\n * - Immediate element creation without serialization/deserialization\n *\n * Benchmark: DOM construction is ~2-3x faster than innerHTML for complex structures.\n */\n\n/**\n * Create an element with attributes and optional children.\n * Optimized helper that avoids repeated function calls.\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string | null | undefined)[],\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n\n if (children) {\n for (const child of children) {\n if (child == null) continue;\n if (typeof child === 'string') {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n }\n\n return el;\n}\n\n/**\n * Create a text node (shorthand).\n */\nexport function text(content: string): Text {\n return document.createTextNode(content);\n}\n\n/**\n * Create an element with class (common pattern).\n */\nexport function div(className?: string, attrs?: Record<string, string>): HTMLDivElement {\n const el = document.createElement('div');\n if (className) el.className = className;\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n return el;\n}\n\n/**\n * Create a button element.\n */\nexport function button(className?: string, attrs?: Record<string, string>, content?: string | Node): HTMLButtonElement {\n const el = document.createElement('button');\n if (className) el.className = className;\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n if (content) {\n if (typeof content === 'string') {\n el.textContent = content;\n } else {\n el.appendChild(content);\n }\n }\n return el;\n}\n\n/**\n * Append multiple children to a parent element.\n */\nexport function appendChildren(parent: Element, children: (Node | null | undefined)[]): void {\n for (const child of children) {\n if (child) parent.appendChild(child);\n }\n}\n\n/**\n * Set multiple attributes on an element.\n */\nexport function setAttrs(el: Element, attrs: Record<string, string | undefined>): void {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n}\n\n// ============================================================================\n// Grid Structure Templates (Pre-built for cloning)\n// ============================================================================\n\n/**\n * Template for grid content (the core scrollable grid area).\n * Pre-built once, then cloned for each grid instance.\n */\nconst gridContentTemplate = document.createElement('template');\ngridContentTemplate.innerHTML = `\n <div class=\"tbw-scroll-area\">\n <div class=\"rows-body-wrapper\">\n <div class=\"rows-body\" role=\"grid\">\n <div class=\"header\" role=\"rowgroup\">\n <div class=\"header-row\" role=\"row\" part=\"header-row\"></div>\n </div>\n <div class=\"rows-container\" role=\"presentation\">\n <div class=\"rows-viewport\" role=\"presentation\">\n <div class=\"rows\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"faux-vscroll\">\n <div class=\"faux-vscroll-spacer\"></div>\n </div>\n`;\n\n/**\n * Clone the grid content structure.\n * Using template cloning is faster than createElement for nested structures.\n */\nexport function cloneGridContent(): DocumentFragment {\n return gridContentTemplate.content.cloneNode(true) as DocumentFragment;\n}\n\n/**\n * Build the grid root structure using direct DOM construction.\n * This is called once per grid instance during initial render.\n */\nexport interface GridDOMOptions {\n hasShell: boolean;\n /** Shell header element (pre-built) */\n shellHeader?: Element;\n /** Shell body element with tool panel (pre-built) */\n shellBody?: Element;\n}\n\n/**\n * Build the complete grid DOM structure.\n * Returns a DocumentFragment ready to be appended to shadow root.\n */\nexport function buildGridDOM(options: GridDOMOptions): DocumentFragment {\n const fragment = document.createDocumentFragment();\n\n const root = div(options.hasShell ? 'tbw-grid-root has-shell' : 'tbw-grid-root');\n\n if (options.hasShell && options.shellHeader && options.shellBody) {\n // Shell mode: header + body (with grid content inside)\n root.appendChild(options.shellHeader);\n root.appendChild(options.shellBody);\n } else {\n // No shell: just grid content in wrapper\n const contentWrapper = div('tbw-grid-content');\n contentWrapper.appendChild(cloneGridContent());\n root.appendChild(contentWrapper);\n }\n\n fragment.appendChild(root);\n return fragment;\n}\n\n/**\n * Build shell header using direct DOM construction.\n *\n * Note: The grid no longer creates buttons from config (icon/action).\n * Config/API buttons only use element or render function.\n * Light DOM buttons are slotted directly.\n * The ONLY button the grid creates is the panel toggle.\n */\nexport interface ShellHeaderOptions {\n title?: string;\n hasPanels: boolean;\n isPanelOpen: boolean;\n toolPanelIcon: string;\n /** Config toolbar contents with render function (pre-sorted by order) */\n configButtons: Array<{\n id: string;\n hasRender?: boolean;\n }>;\n /** API toolbar contents with render function (pre-sorted by order) */\n apiButtons: Array<{\n id: string;\n hasRender?: boolean;\n }>;\n}\n\n/**\n * Build shell header element directly without innerHTML.\n */\nexport function buildShellHeader(options: ShellHeaderOptions): HTMLDivElement {\n const header = div('tbw-shell-header', { part: 'shell-header', role: 'presentation' });\n\n // Title\n if (options.title) {\n const titleEl = div('tbw-shell-title');\n titleEl.textContent = options.title;\n header.appendChild(titleEl);\n }\n\n // Shell content with placeholder for light DOM header content\n const content = div('tbw-shell-content', {\n part: 'shell-content',\n role: 'presentation',\n 'data-light-dom-header-content': '',\n });\n header.appendChild(content);\n\n // Toolbar\n const toolbar = div('tbw-shell-toolbar', { part: 'shell-toolbar', role: 'presentation' });\n\n // Placeholders for config toolbar contents with render function\n for (const btn of options.configButtons) {\n if (btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-content-slot', { 'data-toolbar-content': btn.id }));\n }\n }\n // Placeholders for API toolbar contents with render function\n for (const btn of options.apiButtons) {\n if (btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-content-slot', { 'data-toolbar-content': btn.id }));\n }\n }\n\n // Separator between custom content and panel toggle\n const hasCustomContent =\n options.configButtons.some((b) => b.hasRender) || options.apiButtons.some((b) => b.hasRender);\n if (hasCustomContent && options.hasPanels) {\n toolbar.appendChild(div('tbw-toolbar-separator'));\n }\n\n // Panel toggle button\n if (options.hasPanels) {\n const toggleBtn = button(options.isPanelOpen ? 'tbw-toolbar-btn active' : 'tbw-toolbar-btn', {\n 'data-panel-toggle': '',\n title: 'Settings',\n 'aria-label': 'Toggle settings panel',\n 'aria-pressed': String(options.isPanelOpen),\n 'aria-controls': 'tbw-tool-panel',\n });\n toggleBtn.innerHTML = options.toolPanelIcon;\n toolbar.appendChild(toggleBtn);\n }\n\n header.appendChild(toolbar);\n return header;\n}\n\n/**\n * Build shell body (grid content + optional tool panel).\n */\nexport interface ShellBodyOptions {\n position: 'left' | 'right';\n isPanelOpen: boolean;\n expandIcon: string;\n collapseIcon: string;\n /** Sorted panels for accordion */\n panels: Array<{\n id: string;\n title: string;\n icon?: string;\n isExpanded: boolean;\n }>;\n}\n\n/**\n * Build shell body element directly without innerHTML.\n */\nexport function buildShellBody(options: ShellBodyOptions): HTMLDivElement {\n const body = div('tbw-shell-body');\n const hasPanel = options.panels.length > 0;\n const isSinglePanel = options.panels.length === 1;\n\n // Grid content wrapper with cloned grid structure\n const gridContent = div('tbw-grid-content');\n gridContent.appendChild(cloneGridContent());\n\n // Tool panel\n let panelEl: HTMLElement | null = null;\n if (hasPanel) {\n panelEl = createElement('aside', {\n class: options.isPanelOpen ? 'tbw-tool-panel open' : 'tbw-tool-panel',\n part: 'tool-panel',\n 'data-position': options.position,\n role: 'presentation',\n id: 'tbw-tool-panel',\n });\n\n // Resize handle\n const resizeHandlePosition = options.position === 'left' ? 'right' : 'left';\n panelEl.appendChild(\n div('tbw-tool-panel-resize', {\n 'data-resize-handle': '',\n 'data-handle-position': resizeHandlePosition,\n 'aria-hidden': 'true',\n }),\n );\n\n // Panel content with accordion\n const panelContent = div('tbw-tool-panel-content', { role: 'presentation' });\n const accordion = div('tbw-accordion');\n\n for (const panel of options.panels) {\n const sectionClasses = `tbw-accordion-section${panel.isExpanded ? ' expanded' : ''}${isSinglePanel ? ' single' : ''}`;\n const section = div(sectionClasses, { 'data-section': panel.id });\n\n // Accordion header button\n const headerBtn = button('tbw-accordion-header', {\n 'aria-expanded': String(panel.isExpanded),\n 'aria-controls': `tbw-section-${panel.id}`,\n });\n if (isSinglePanel) headerBtn.setAttribute('aria-disabled', 'true');\n\n // Icon\n if (panel.icon) {\n const iconSpan = createElement('span', { class: 'tbw-accordion-icon' });\n iconSpan.innerHTML = panel.icon;\n headerBtn.appendChild(iconSpan);\n }\n\n // Title\n const titleSpan = createElement('span', { class: 'tbw-accordion-title' });\n titleSpan.textContent = panel.title;\n headerBtn.appendChild(titleSpan);\n\n // Chevron (hidden for single panel)\n if (!isSinglePanel) {\n const chevronSpan = createElement('span', { class: 'tbw-accordion-chevron' });\n chevronSpan.innerHTML = panel.isExpanded ? options.collapseIcon : options.expandIcon;\n headerBtn.appendChild(chevronSpan);\n }\n\n section.appendChild(headerBtn);\n\n // Accordion content (empty, will be filled by panel render functions)\n section.appendChild(\n div('tbw-accordion-content', {\n id: `tbw-section-${panel.id}`,\n role: 'presentation',\n }),\n );\n\n accordion.appendChild(section);\n }\n\n panelContent.appendChild(accordion);\n panelEl.appendChild(panelContent);\n }\n\n // Append in correct order based on position\n if (options.position === 'left' && panelEl) {\n body.appendChild(panelEl);\n body.appendChild(gridContent);\n } else {\n body.appendChild(gridContent);\n if (panelEl) body.appendChild(panelEl);\n }\n\n return body;\n}\n","/**\n * Shell infrastructure for grid header bar and tool panels.\n *\n * The shell is an optional wrapper layer that provides:\n * - Header bar with title, plugin content, and toolbar buttons\n * - Tool panels that plugins can register content into\n * - Light DOM parsing for framework-friendly configuration\n */\n\nimport type {\n HeaderContentDefinition,\n IconValue,\n ShellConfig,\n ToolbarContentDefinition,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\nimport { escapeHtml } from './sanitize';\n\n/**\n * Convert an IconValue to a string for rendering in HTML.\n */\nfunction iconToString(icon: IconValue | undefined): string {\n if (!icon) return '';\n if (typeof icon === 'string') return icon;\n // For HTMLElement, get the outerHTML\n return icon.outerHTML;\n}\n\n/**\n * State for managing shell UI.\n *\n * This interface holds both configuration-like properties (toolPanels, headerContents)\n * and runtime state (isPanelOpen, expandedSections). The Maps allow for efficient\n * registration/unregistration of panels and content.\n */\nexport interface ShellState {\n /** Registered tool panels (from plugins + consumer API) */\n toolPanels: Map<string, ToolPanelDefinition>;\n /** Registered header content (from plugins + consumer API) */\n headerContents: Map<string, HeaderContentDefinition>;\n /** Toolbar content registered via API or light DOM */\n toolbarContents: Map<string, ToolbarContentDefinition>;\n /** Whether a <tbw-grid-tool-buttons> container was found in light DOM */\n hasToolButtonsContainer: boolean;\n /** Light DOM header content elements */\n lightDomHeaderContent: HTMLElement[];\n /** Light DOM header title from <tbw-grid-header title=\"...\"> */\n lightDomTitle: string | null;\n /** IDs of tool panels registered from light DOM (to avoid re-parsing) */\n lightDomToolPanelIds: Set<string>;\n /** IDs of toolbar content registered from light DOM (to avoid re-parsing) */\n lightDomToolbarContentIds: Set<string>;\n /** IDs of tool panels registered via registerToolPanel API */\n apiToolPanelIds: Set<string>;\n /** Whether the tool panel sidebar is open */\n isPanelOpen: boolean;\n /** Which accordion sections are expanded (by panel ID) */\n expandedSections: Set<string>;\n /** Whether light DOM header content has been moved to placeholder (perf optimization) */\n lightDomContentMoved: boolean;\n /** Cleanup functions for header content render returns */\n headerContentCleanups: Map<string, () => void>;\n /** Cleanup functions for each panel section's render return */\n panelCleanups: Map<string, () => void>;\n /** Cleanup functions for toolbar content render returns */\n toolbarContentCleanups: Map<string, () => void>;\n}\n\n/**\n * Runtime-only shell state (not configuration).\n *\n * Configuration (toolPanels, headerContents, toolbarContents, title) lives in\n * effectiveConfig.shell. This state holds runtime UI state and cleanup functions.\n */\nexport interface ShellRuntimeState {\n /** Whether the tool panel sidebar is currently open */\n isPanelOpen: boolean;\n /** Which accordion sections are currently expanded (by panel ID) */\n expandedSections: Set<string>;\n /** Cleanup functions for header content render returns */\n headerContentCleanups: Map<string, () => void>;\n /** Cleanup functions for each panel section's render return */\n panelCleanups: Map<string, () => void>;\n /** Cleanup functions for toolbar content render returns */\n toolbarContentCleanups: Map<string, () => void>;\n /** IDs of tool panels registered from light DOM (to avoid re-parsing) */\n lightDomToolPanelIds: Set<string>;\n /** IDs of tool panels registered via registerToolPanel API */\n apiToolPanelIds: Set<string>;\n /** Whether a <tbw-grid-tool-buttons> container was found in light DOM */\n hasToolButtonsContainer: boolean;\n}\n\n/**\n * Create initial shell runtime state.\n */\nexport function createShellRuntimeState(): ShellRuntimeState {\n return {\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarContentCleanups: new Map(),\n lightDomToolPanelIds: new Set(),\n apiToolPanelIds: new Set(),\n hasToolButtonsContainer: false,\n };\n}\n\n/**\n * Create initial shell state.\n */\nexport function createShellState(): ShellState {\n return {\n toolPanels: new Map(),\n headerContents: new Map(),\n toolbarContents: new Map(),\n hasToolButtonsContainer: false,\n lightDomHeaderContent: [],\n lightDomTitle: null,\n lightDomToolPanelIds: new Set(),\n lightDomToolbarContentIds: new Set(),\n apiToolPanelIds: new Set(),\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarContentCleanups: new Map(),\n lightDomContentMoved: false,\n };\n}\n\n/**\n * Determine if shell header should be rendered.\n * Reads only from effectiveConfig.shell (single source of truth).\n */\nexport function shouldRenderShellHeader(config: ShellConfig | undefined): boolean {\n // Check if title is configured\n if (config?.header?.title) return true;\n\n // Check if config has toolbar contents\n if (config?.header?.toolbarContents?.length) return true;\n\n // Check if any tool panels are registered\n if (config?.toolPanels?.length) return true;\n\n // Check if any header content is registered\n if (config?.headerContents?.length) return true;\n\n // Check if light DOM has header elements\n if (config?.header?.lightDomContent?.length) return true;\n\n // Check if a toolbar buttons container was found\n if (config?.header?.hasToolButtonsContainer) return true;\n\n return false;\n}\n\n/**\n * Render the shell header HTML.\n *\n * Toolbar contents come from two sources:\n * 1. Light DOM slot (users provide their own HTML in <tbw-grid-tool-buttons>)\n * 2. Config/API with render function (programmatic insertion)\n *\n * Users have full control over toolbar HTML, styling, and behavior.\n * The only button the grid creates is the tool panel toggle.\n *\n * @param toolPanelIcon - Icon for the tool panel toggle (from grid icon config)\n */\nexport function renderShellHeader(\n config: ShellConfig | undefined,\n state: ShellState,\n toolPanelIcon: IconValue = '☰',\n): string {\n const title = config?.header?.title ?? state.lightDomTitle ?? '';\n const hasTitle = !!title;\n const iconStr = iconToString(toolPanelIcon);\n\n // Get all toolbar contents from effectiveConfig (already merged: config + API + light DOM)\n // The config-manager merges state.toolbarContents into effectiveConfig.shell.header.toolbarContents\n // Also include state.toolbarContents directly for cases where renderShellHeader is called\n // before config-manager has merged (e.g., unit tests, initial render)\n const configContents = config?.header?.toolbarContents ?? [];\n const stateContents = [...state.toolbarContents.values()];\n\n // Merge: use config contents, add state contents that aren't in config\n const configIds = new Set(configContents.map((c) => c.id));\n const allContents = [...configContents];\n for (const content of stateContents) {\n if (!configIds.has(content.id)) {\n allContents.push(content);\n }\n }\n\n const hasCustomContent = allContents.length > 0;\n const hasPanels = state.toolPanels.size > 0;\n const showSeparator = hasCustomContent && hasPanels;\n\n // Sort contents by order for slot placement\n const sortedContents = [...allContents].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n\n // Build toolbar HTML\n let toolbarHtml = '';\n\n // Create slots for all contents (unified: config + API + light DOM)\n for (const content of sortedContents) {\n toolbarHtml += `<div class=\"tbw-toolbar-content-slot\" data-toolbar-content=\"${content.id}\"></div>`;\n }\n\n // Separator between custom content and panel toggle\n if (showSeparator) {\n toolbarHtml += '<div class=\"tbw-toolbar-separator\"></div>';\n }\n\n // Single panel toggle button (the ONLY button the grid creates)\n if (hasPanels) {\n const isOpen = state.isPanelOpen;\n const toggleClass = isOpen ? 'tbw-toolbar-btn active' : 'tbw-toolbar-btn';\n toolbarHtml += `<button class=\"${toggleClass}\" data-panel-toggle title=\"Settings\" aria-label=\"Toggle settings panel\" aria-pressed=\"${isOpen}\" aria-controls=\"tbw-tool-panel\">${iconStr}</button>`;\n }\n\n return `\n <div class=\"tbw-shell-header\" part=\"shell-header\" role=\"presentation\">\n ${hasTitle ? `<div class=\"tbw-shell-title\">${escapeHtml(title)}</div>` : ''}\n <div class=\"tbw-shell-content\" part=\"shell-content\" role=\"presentation\" data-light-dom-header-content></div>\n <div class=\"tbw-shell-toolbar\" part=\"shell-toolbar\" role=\"presentation\">\n ${toolbarHtml}\n </div>\n </div>\n `;\n}\n\n/**\n * Render the shell body wrapper HTML (contains grid content + accordion-style tool panel).\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderShellBody(\n config: ShellConfig | undefined,\n state: ShellState,\n gridContentHtml: string,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): string {\n const position = config?.toolPanel?.position ?? 'right';\n const hasPanel = state.toolPanels.size > 0;\n const isOpen = state.isPanelOpen;\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n // Sort panels by order for accordion sections\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const isSinglePanel = sortedPanels.length === 1;\n\n // Build accordion sections HTML\n let accordionHtml = '';\n for (const panel of sortedPanels) {\n const isExpanded = state.expandedSections.has(panel.id);\n const iconHtml = panel.icon ? `<span class=\"tbw-accordion-icon\">${panel.icon}</span>` : '';\n // Hide chevron for single panel (no toggling needed)\n const chevronHtml = isSinglePanel\n ? ''\n : `<span class=\"tbw-accordion-chevron\">${isExpanded ? collapseIcon : expandIcon}</span>`;\n // Disable accordion toggle for single panel\n const sectionClasses = `tbw-accordion-section${isExpanded ? ' expanded' : ''}${isSinglePanel ? ' single' : ''}`;\n accordionHtml += `\n <div class=\"${sectionClasses}\" data-section=\"${panel.id}\">\n <button class=\"tbw-accordion-header\" aria-expanded=\"${isExpanded}\" aria-controls=\"tbw-section-${panel.id}\"${isSinglePanel ? ' aria-disabled=\"true\"' : ''}>\n ${iconHtml}\n <span class=\"tbw-accordion-title\">${panel.title}</span>\n ${chevronHtml}\n </button>\n <div class=\"tbw-accordion-content\" id=\"tbw-section-${panel.id}\" role=\"presentation\"></div>\n </div>\n `;\n }\n\n // Resize handle position depends on panel position\n const resizeHandlePosition = position === 'left' ? 'right' : 'left';\n\n const panelHtml = hasPanel\n ? `\n <aside class=\"tbw-tool-panel${isOpen ? ' open' : ''}\" part=\"tool-panel\" data-position=\"${position}\" role=\"presentation\" id=\"tbw-tool-panel\">\n <div class=\"tbw-tool-panel-resize\" data-resize-handle data-handle-position=\"${resizeHandlePosition}\" aria-hidden=\"true\"></div>\n <div class=\"tbw-tool-panel-content\" role=\"presentation\">\n <div class=\"tbw-accordion\">\n ${accordionHtml}\n </div>\n </div>\n </aside>\n `\n : '';\n\n // For left position, panel comes before content in DOM order\n if (position === 'left') {\n return `\n <div class=\"tbw-shell-body\">\n ${panelHtml}\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n </div>\n `;\n }\n\n return `\n <div class=\"tbw-shell-body\">\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n ${panelHtml}\n </div>\n `;\n}\n\n/**\n * Parse light DOM shell elements (tbw-grid-header, etc.).\n * Safe to call multiple times - will only parse once when elements are available.\n */\nexport function parseLightDomShell(host: HTMLElement, state: ShellState): void {\n const headerEl = host.querySelector('tbw-grid-header');\n if (!headerEl) return;\n\n // Parse title attribute (only if not already parsed)\n if (!state.lightDomTitle) {\n const title = headerEl.getAttribute('title');\n if (title) {\n state.lightDomTitle = title;\n }\n }\n\n // Parse header content elements - store references but don't set slot (light DOM doesn't use slots)\n const headerContents = headerEl.querySelectorAll('tbw-grid-header-content');\n if (headerContents.length > 0 && state.lightDomHeaderContent.length === 0) {\n state.lightDomHeaderContent = Array.from(headerContents) as HTMLElement[];\n }\n\n // Hide the light DOM header container (it was just for declarative config)\n (headerEl as HTMLElement).style.display = 'none';\n}\n\n/**\n * Callback type for creating a toolbar content renderer from a light DOM container.\n * This is used by framework adapters (Angular, React, etc.) to create renderers\n * from their template syntax.\n */\nexport type ToolbarContentRendererFactory = (\n container: HTMLElement,\n) => ((target: HTMLElement) => void | (() => void)) | undefined;\n\n/**\n * Parse toolbar buttons container element (<tbw-grid-tool-buttons>).\n * This is a content container - we don't parse individual children.\n * The entire container content is registered as a single toolbar content entry.\n *\n * Example:\n * ```html\n * <tbw-grid>\n * <tbw-grid-tool-buttons>\n * <button>My button</button>\n * <button>My other button</button>\n * </tbw-grid-tool-buttons>\n * </tbw-grid>\n * ```\n *\n * The container's children are moved to the toolbar area during render.\n * We treat this as opaque content - users control what goes inside.\n *\n * @param host - The grid host element\n * @param state - Shell state to update\n * @param rendererFactory - Optional factory for creating renderers (used by framework adapters)\n */\nexport function parseLightDomToolButtons(\n host: HTMLElement,\n state: ShellState,\n rendererFactory?: ToolbarContentRendererFactory,\n): void {\n // Look for the toolbar buttons container element\n const toolButtonsContainer = host.querySelector(':scope > tbw-grid-tool-buttons') as HTMLElement | null;\n if (!toolButtonsContainer) return;\n\n // Mark that we found the container (for shouldRenderShellHeader)\n state.hasToolButtonsContainer = true;\n\n // Skip if already registered\n const id = 'light-dom-toolbar-content';\n if (state.lightDomToolbarContentIds.has(id)) return;\n\n // Register as a single content entry with a render function\n const adapterRenderer = rendererFactory?.(toolButtonsContainer);\n\n const contentDef: ToolbarContentDefinition = {\n id,\n order: 0, // Light DOM content comes first\n render:\n adapterRenderer ??\n ((target: HTMLElement) => {\n // Move all children from the light DOM container to the target\n while (toolButtonsContainer.firstChild) {\n target.appendChild(toolButtonsContainer.firstChild);\n }\n // No cleanup needed - elements are just moved\n }),\n };\n\n state.toolbarContents.set(id, contentDef);\n state.lightDomToolbarContentIds.add(id);\n\n // Hide the original container\n toolButtonsContainer.style.display = 'none';\n}\n\n/**\n * Callback type for creating a tool panel renderer from a light DOM element.\n * This is used by framework adapters (Angular, React, etc.) to create renderers\n * from their template syntax.\n */\nexport type ToolPanelRendererFactory = (\n element: HTMLElement,\n) => ((container: HTMLElement) => void | (() => void)) | undefined;\n\n/**\n * Parse light DOM tool panel elements (<tbw-grid-tool-panel>).\n * These can appear as direct children of <tbw-grid> for declarative tool panel configuration.\n *\n * Attributes:\n * - `id` (required): Unique panel identifier\n * - `title` (required): Panel title shown in accordion header\n * - `icon`: Icon for accordion section header (emoji or text)\n * - `tooltip`: Tooltip for accordion section header\n * - `order`: Panel order priority (lower = first, default: 100)\n *\n * For vanilla JS, the element's innerHTML is used as the panel content.\n * For framework adapters, the adapter can provide a custom renderer factory.\n *\n * @param host - The grid host element\n * @param state - Shell state to update\n * @param rendererFactory - Optional factory for creating renderers (used by framework adapters)\n */\nexport function parseLightDomToolPanels(\n host: HTMLElement,\n state: ShellState,\n rendererFactory?: ToolPanelRendererFactory,\n): void {\n const toolPanelElements = host.querySelectorAll(':scope > tbw-grid-tool-panel');\n\n toolPanelElements.forEach((element) => {\n const panelEl = element as HTMLElement;\n const id = panelEl.getAttribute('id');\n const title = panelEl.getAttribute('title');\n\n // Skip if required attributes are missing\n if (!id || !title) {\n console.warn(\n `[parseLightDomToolPanels] Tool panel missing required id or title attribute: id=\"${id ?? ''}\", title=\"${title ?? ''}\"`,\n );\n return;\n }\n\n const icon = panelEl.getAttribute('icon') ?? undefined;\n const tooltip = panelEl.getAttribute('tooltip') ?? undefined;\n const order = parseInt(panelEl.getAttribute('order') ?? '100', 10);\n\n // Try framework adapter first, then fall back to innerHTML\n let render: (container: HTMLElement) => void | (() => void);\n\n const adapterRenderer = rendererFactory?.(panelEl);\n if (adapterRenderer) {\n render = adapterRenderer;\n } else {\n // Vanilla fallback: use innerHTML as static content\n const content = panelEl.innerHTML.trim();\n render = (container: HTMLElement) => {\n const wrapper = document.createElement('div');\n wrapper.innerHTML = content;\n container.appendChild(wrapper);\n return () => wrapper.remove();\n };\n }\n\n // Check if panel was already parsed\n const existingPanel = state.toolPanels.get(id);\n\n // If already parsed and we have an adapter renderer, update the render function\n // and re-read attributes from DOM (Angular may have updated them after initial parse)\n // This handles the case where Angular templates register after initial parsing\n if (existingPanel) {\n if (adapterRenderer) {\n // Update render function with framework adapter renderer\n existingPanel.render = render;\n\n // Re-read attributes from DOM - framework may have set them after initial parse\n // (e.g., Angular directive sets attributes in an effect after template is available)\n existingPanel.order = order;\n existingPanel.icon = icon;\n existingPanel.tooltip = tooltip;\n // Note: title and id are required and shouldn't change\n\n // Clear existing cleanup to force re-render with new renderer\n const cleanup = state.panelCleanups.get(id);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(id);\n }\n }\n return;\n }\n\n // Register the tool panel\n const panel: ToolPanelDefinition = {\n id,\n title,\n icon,\n tooltip,\n order,\n render,\n };\n\n state.toolPanels.set(id, panel);\n state.lightDomToolPanelIds.add(id);\n\n // Hide the light DOM element\n panelEl.style.display = 'none';\n });\n}\n\n/**\n * Set up event listeners for shell toolbar buttons and accordion.\n */\nexport function setupShellEventListeners(\n renderRoot: Element,\n config: ShellConfig | undefined,\n state: ShellState,\n callbacks: {\n onPanelToggle: () => void;\n onSectionToggle: (sectionId: string) => void;\n },\n): void {\n const toolbar = renderRoot.querySelector('.tbw-shell-toolbar');\n if (toolbar) {\n toolbar.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n\n // Handle single panel toggle button\n const panelToggle = target.closest('[data-panel-toggle]') as HTMLElement | null;\n if (panelToggle) {\n callbacks.onPanelToggle();\n return;\n }\n });\n }\n\n // Accordion header clicks\n const accordion = renderRoot.querySelector('.tbw-accordion');\n if (accordion) {\n accordion.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n const header = target.closest('.tbw-accordion-header') as HTMLElement | null;\n if (header) {\n const section = header.closest('[data-section]') as HTMLElement | null;\n const sectionId = section?.getAttribute('data-section');\n if (sectionId) {\n callbacks.onSectionToggle(sectionId);\n }\n }\n });\n }\n}\n\n/**\n * Set up resize handle for tool panel.\n * Returns a cleanup function to remove event listeners.\n */\nexport function setupToolPanelResize(\n renderRoot: Element,\n config: ShellConfig | undefined,\n onResize: (width: number) => void,\n): () => void {\n const panel = renderRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n const handle = renderRoot.querySelector('[data-resize-handle]') as HTMLElement | null;\n const shellBody = renderRoot.querySelector('.tbw-shell-body') as HTMLElement | null;\n if (!panel || !handle || !shellBody) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const position = config?.toolPanel?.position ?? 'right';\n const minWidth = 200;\n\n let startX = 0;\n let startWidth = 0;\n let maxWidth = 0;\n let isResizing = false;\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n e.preventDefault();\n\n // For right-positioned panel: dragging left (negative clientX change) should expand\n // For left-positioned panel: dragging right (positive clientX change) should expand\n const delta = position === 'left' ? e.clientX - startX : startX - e.clientX;\n const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidth + delta));\n\n panel.style.width = `${newWidth}px`;\n };\n\n const onMouseUp = () => {\n if (!isResizing) return;\n isResizing = false;\n handle.classList.remove('resizing');\n panel.style.transition = ''; // Re-enable transition\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n\n // Get final width and notify\n const finalWidth = panel.getBoundingClientRect().width;\n onResize(finalWidth);\n\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n isResizing = true;\n startX = e.clientX;\n startWidth = panel.getBoundingClientRect().width;\n // Calculate max width dynamically based on grid container width\n maxWidth = shellBody.getBoundingClientRect().width - 20; // Leave 20px margin\n handle.classList.add('resizing');\n panel.style.transition = 'none'; // Disable transition for smooth resize\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n\n // Return cleanup function\n return () => {\n handle.removeEventListener('mousedown', onMouseDown);\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n}\n\n/**\n * Render toolbar content (render functions) into toolbar slots.\n * All contents (config + API + light DOM) are now unified.\n */\nexport function renderCustomToolbarContents(\n renderRoot: Element,\n config: ShellConfig | undefined,\n state: ShellState,\n): void {\n // Merge config contents with state contents (same logic as renderShellHeader)\n const configContents = config?.header?.toolbarContents ?? [];\n const stateContents = [...state.toolbarContents.values()];\n const configIds = new Set(configContents.map((c) => c.id));\n const allContents = [...configContents];\n for (const content of stateContents) {\n if (!configIds.has(content.id)) {\n allContents.push(content);\n }\n }\n\n // Only process contents that need rendering (have render and cleanup not already set)\n for (const content of allContents) {\n // Skip if already rendered (cleanup exists)\n if (state.toolbarContentCleanups.has(content.id)) continue;\n if (!content.render) continue;\n\n const slot = renderRoot.querySelector(`[data-toolbar-content=\"${content.id}\"]`);\n if (!slot) continue;\n\n const cleanup = content.render(slot as HTMLElement);\n if (cleanup) {\n state.toolbarContentCleanups.set(content.id, cleanup);\n }\n }\n}\n\n/**\n * Render header content from plugins into the shell content area.\n * Also moves light DOM header content to the placeholder (once).\n */\nexport function renderHeaderContent(renderRoot: Element, state: ShellState): void {\n // Early exit if nothing to do (most common path after initial render)\n const hasLightDomContent = state.lightDomHeaderContent.length > 0 && !state.lightDomContentMoved;\n const hasPluginContent = state.headerContents.size > 0;\n if (!hasLightDomContent && !hasPluginContent) return;\n\n const contentArea = renderRoot.querySelector('.tbw-shell-content');\n if (!contentArea) return;\n\n // Move light DOM header content to placeholder - only once (perf optimization)\n if (hasLightDomContent) {\n for (const el of state.lightDomHeaderContent) {\n el.style.display = ''; // Show it (was hidden in the original container)\n contentArea.appendChild(el);\n }\n state.lightDomContentMoved = true;\n }\n\n // Sort by order\n const sortedContents = [...state.headerContents.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n for (const content of sortedContents) {\n // Clean up previous render if any\n const existingCleanup = state.headerContentCleanups.get(content.id);\n if (existingCleanup) {\n existingCleanup();\n state.headerContentCleanups.delete(content.id);\n }\n\n // Check if container already exists\n let container = contentArea.querySelector(`[data-header-content=\"${content.id}\"]`) as HTMLElement | null;\n if (!container) {\n container = document.createElement('div');\n container.setAttribute('data-header-content', content.id);\n contentArea.appendChild(container);\n }\n\n const cleanup = content.render(container);\n if (cleanup) {\n state.headerContentCleanups.set(content.id, cleanup);\n }\n }\n}\n\n/**\n * Render content for expanded accordion sections.\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderPanelContent(\n renderRoot: Element,\n state: ShellState,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): void {\n if (!state.isPanelOpen) return;\n\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n for (const [panelId, panel] of state.toolPanels) {\n const isExpanded = state.expandedSections.has(panelId);\n const section = renderRoot.querySelector(`[data-section=\"${panelId}\"]`);\n const contentArea = section?.querySelector('.tbw-accordion-content') as HTMLElement | null;\n\n if (!section || !contentArea) continue;\n\n // Update expanded state\n section.classList.toggle('expanded', isExpanded);\n const header = section.querySelector('.tbw-accordion-header');\n if (header) {\n header.setAttribute('aria-expanded', String(isExpanded));\n }\n const chevron = section.querySelector('.tbw-accordion-chevron');\n if (chevron) {\n chevron.innerHTML = isExpanded ? collapseIcon : expandIcon;\n }\n\n if (isExpanded) {\n // Check if content is already rendered\n if (contentArea.children.length === 0) {\n // Render panel content\n const cleanup = panel.render(contentArea);\n if (cleanup) {\n state.panelCleanups.set(panelId, cleanup);\n }\n }\n } else {\n // Clean up and clear content when collapsed\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n contentArea.innerHTML = '';\n }\n }\n}\n\n/**\n * Update toolbar button active states.\n */\nexport function updateToolbarActiveStates(renderRoot: Element, state: ShellState): void {\n // Update single panel toggle button\n const panelToggle = renderRoot.querySelector('[data-panel-toggle]');\n if (panelToggle) {\n panelToggle.classList.toggle('active', state.isPanelOpen);\n panelToggle.setAttribute('aria-pressed', String(state.isPanelOpen));\n }\n}\n\n/**\n * Update tool panel open/close state.\n */\nexport function updatePanelState(renderRoot: Element, state: ShellState): void {\n const panel = renderRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n if (!panel) return;\n\n panel.classList.toggle('open', state.isPanelOpen);\n\n // Clear inline width when closing (resize sets inline style that overrides CSS)\n if (!state.isPanelOpen) {\n panel.style.width = '';\n }\n}\n\n/**\n * Cleanup all shell state when grid disconnects.\n */\nexport function cleanupShellState(state: ShellState): void {\n // Clean up header content\n for (const cleanup of state.headerContentCleanups.values()) {\n cleanup();\n }\n state.headerContentCleanups.clear();\n\n // Clean up panel content\n for (const cleanup of state.panelCleanups.values()) {\n cleanup();\n }\n state.panelCleanups.clear();\n\n // Clean up toolbar contents\n for (const cleanup of state.toolbarContentCleanups.values()) {\n cleanup();\n }\n state.toolbarContentCleanups.clear();\n\n // Call onDestroy for all toolbar contents\n for (const content of state.toolbarContents.values()) {\n content.onDestroy?.();\n }\n\n // Invoke onClose for all open panels\n if (state.isPanelOpen) {\n for (const sectionId of state.expandedSections) {\n const panel = state.toolPanels.get(sectionId);\n panel?.onClose?.();\n }\n }\n\n // Reset panel state\n state.isPanelOpen = false;\n state.expandedSections.clear();\n\n // Clear registrations\n state.toolPanels.clear();\n state.headerContents.clear();\n state.toolbarContents.clear();\n state.lightDomHeaderContent = [];\n\n // Clear light DOM tracking sets (allow re-parsing)\n state.lightDomToolPanelIds.clear();\n state.lightDomToolbarContentIds.clear();\n\n // Reset move tracking flag (allow re-initialization)\n state.lightDomContentMoved = false;\n}\n\n// ============================================================================\n// ShellController - Encapsulates tool panel orchestration logic\n// ============================================================================\n\n/**\n * Callbacks for ShellController to communicate with the grid.\n * This interface decouples the controller from grid internals.\n */\nexport interface ShellControllerCallbacks {\n /** Get the render root for DOM queries (the grid element) */\n getShadow: () => Element;\n /** Get the current shell config */\n getShellConfig: () => ShellConfig | undefined;\n /** Get accordion expand/collapse icons */\n getAccordionIcons: () => { expand: IconValue; collapse: IconValue };\n /** Emit an event from the grid */\n emit: (eventName: string, detail: unknown) => void;\n /** Refresh the shell header (re-parse light DOM and re-render) */\n refreshShellHeader: () => void;\n}\n\n/**\n * Controller interface for managing shell/tool panel behavior.\n */\nexport interface ShellController {\n /** Whether the shell has been initialized */\n readonly isInitialized: boolean;\n /** Set the initialized state */\n setInitialized(value: boolean): void;\n /** Whether the tool panel is currently open */\n readonly isPanelOpen: boolean;\n /** Get the currently active panel ID (deprecated) */\n readonly activePanel: string | null;\n /** Get IDs of expanded accordion sections */\n readonly expandedSections: string[];\n /** Open the tool panel */\n openToolPanel(): void;\n /** Close the tool panel */\n closeToolPanel(): void;\n /** Toggle the tool panel */\n toggleToolPanel(): void;\n /** Toggle an accordion section */\n toggleToolPanelSection(sectionId: string): void;\n /** Get registered tool panels */\n getToolPanels(): ToolPanelDefinition[];\n /** Register a tool panel */\n registerToolPanel(panel: ToolPanelDefinition): void;\n /** Unregister a tool panel */\n unregisterToolPanel(panelId: string): void;\n /** Get registered header contents */\n getHeaderContents(): HeaderContentDefinition[];\n /** Register header content */\n registerHeaderContent(content: HeaderContentDefinition): void;\n /** Unregister header content */\n unregisterHeaderContent(contentId: string): void;\n /** Get all registered toolbar contents */\n getToolbarContents(): ToolbarContentDefinition[];\n /** Register toolbar content */\n registerToolbarContent(content: ToolbarContentDefinition): void;\n /** Unregister toolbar content */\n unregisterToolbarContent(contentId: string): void;\n}\n\n/**\n * Create a ShellController instance.\n * The controller encapsulates all tool panel orchestration logic.\n */\nexport function createShellController(state: ShellState, callbacks: ShellControllerCallbacks): ShellController {\n let initialized = false;\n\n const controller: ShellController = {\n get isInitialized() {\n return initialized;\n },\n setInitialized(value: boolean) {\n initialized = value;\n },\n\n get isPanelOpen() {\n return state.isPanelOpen;\n },\n\n get activePanel() {\n // For backward compatibility, return first expanded section if panel is open\n if (state.isPanelOpen && state.expandedSections.size > 0) {\n return [...state.expandedSections][0];\n }\n return null;\n },\n\n get expandedSections() {\n return [...state.expandedSections];\n },\n\n openToolPanel() {\n if (state.isPanelOpen) return;\n if (state.toolPanels.size === 0) {\n console.warn('[tbw-grid] No tool panels registered');\n return;\n }\n\n state.isPanelOpen = true;\n\n // Auto-expand first section if none expanded\n if (state.expandedSections.size === 0 && state.toolPanels.size > 0) {\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const firstPanel = sortedPanels[0];\n if (firstPanel) {\n state.expandedSections.add(firstPanel.id);\n }\n }\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Render accordion sections\n renderPanelContent(shadow, state, callbacks.getAccordionIcons());\n\n // Emit event\n callbacks.emit('tool-panel-open', { sections: controller.expandedSections });\n },\n\n closeToolPanel() {\n if (!state.isPanelOpen) return;\n\n // Clean up all panel content\n for (const cleanup of state.panelCleanups.values()) {\n cleanup();\n }\n state.panelCleanups.clear();\n\n // Call onClose for all panels\n for (const panel of state.toolPanels.values()) {\n panel.onClose?.();\n }\n\n state.isPanelOpen = false;\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Emit event\n callbacks.emit('tool-panel-close', {});\n },\n\n toggleToolPanel() {\n if (state.isPanelOpen) {\n controller.closeToolPanel();\n } else {\n controller.openToolPanel();\n }\n },\n\n toggleToolPanelSection(sectionId: string) {\n const panel = state.toolPanels.get(sectionId);\n if (!panel) {\n console.warn(`[tbw-grid] Tool panel section \"${sectionId}\" not found`);\n return;\n }\n\n // Don't allow toggling when there's only one panel (it should stay expanded)\n if (state.toolPanels.size === 1) {\n return;\n }\n\n const shadow = callbacks.getShadow();\n const isExpanded = state.expandedSections.has(sectionId);\n\n if (isExpanded) {\n // Collapsing current section\n const cleanup = state.panelCleanups.get(sectionId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(sectionId);\n }\n panel.onClose?.();\n state.expandedSections.delete(sectionId);\n updateAccordionSectionState(shadow, sectionId, false);\n } else {\n // Expanding - first collapse all others (exclusive accordion)\n for (const [otherId, otherPanel] of state.toolPanels) {\n if (otherId !== sectionId && state.expandedSections.has(otherId)) {\n const cleanup = state.panelCleanups.get(otherId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(otherId);\n }\n otherPanel.onClose?.();\n state.expandedSections.delete(otherId);\n updateAccordionSectionState(shadow, otherId, false);\n // Clear content of collapsed section\n const contentEl = shadow.querySelector(`[data-section=\"${otherId}\"] .tbw-accordion-content`);\n if (contentEl) contentEl.innerHTML = '';\n }\n }\n // Now expand the target section\n state.expandedSections.add(sectionId);\n updateAccordionSectionState(shadow, sectionId, true);\n renderAccordionSectionContent(shadow, state, sectionId);\n }\n\n // Emit event\n callbacks.emit('tool-panel-section-toggle', { id: sectionId, expanded: !isExpanded });\n },\n\n getToolPanels() {\n return [...state.toolPanels.values()];\n },\n\n registerToolPanel(panel: ToolPanelDefinition) {\n if (state.toolPanels.has(panel.id)) {\n console.warn(`[tbw-grid] Tool panel \"${panel.id}\" already registered`);\n return;\n }\n state.toolPanels.set(panel.id, panel);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolPanel(panelId: string) {\n // Close panel if open and this section is expanded\n if (state.expandedSections.has(panelId)) {\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n state.expandedSections.delete(panelId);\n }\n\n state.toolPanels.delete(panelId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n getHeaderContents() {\n return [...state.headerContents.values()];\n },\n\n registerHeaderContent(content: HeaderContentDefinition) {\n if (state.headerContents.has(content.id)) {\n console.warn(`[tbw-grid] Header content \"${content.id}\" already registered`);\n return;\n }\n state.headerContents.set(content.id, content);\n\n if (initialized) {\n renderHeaderContent(callbacks.getShadow(), state);\n }\n },\n\n unregisterHeaderContent(contentId: string) {\n // Clean up\n const cleanup = state.headerContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n state.headerContentCleanups.delete(contentId);\n }\n\n // Call onDestroy\n const content = state.headerContents.get(contentId);\n content?.onDestroy?.();\n\n state.headerContents.delete(contentId);\n\n // Remove DOM element\n const el = callbacks.getShadow().querySelector(`[data-header-content=\"${contentId}\"]`);\n el?.remove();\n },\n\n getToolbarContents() {\n return [...state.toolbarContents.values()].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n },\n\n registerToolbarContent(content: ToolbarContentDefinition) {\n if (state.toolbarContents.has(content.id)) {\n console.warn(`[tbw-grid] Toolbar content \"${content.id}\" already registered`);\n return;\n }\n state.toolbarContents.set(content.id, content);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolbarContent(contentId: string) {\n // Clean up\n const cleanup = state.toolbarContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n state.toolbarContentCleanups.delete(contentId);\n }\n\n // Call onDestroy if defined\n const content = state.toolbarContents.get(contentId);\n if (content?.onDestroy) {\n content.onDestroy();\n }\n\n state.toolbarContents.delete(contentId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n };\n\n return controller;\n}\n\n/**\n * Update accordion section visual state.\n */\nfunction updateAccordionSectionState(renderRoot: Element, sectionId: string, expanded: boolean): void {\n const section = renderRoot.querySelector(`[data-section=\"${sectionId}\"]`);\n if (section) {\n section.classList.toggle('expanded', expanded);\n }\n}\n\n/**\n * Render content for a single accordion section.\n */\nfunction renderAccordionSectionContent(renderRoot: Element, state: ShellState, sectionId: string): void {\n const panel = state.toolPanels.get(sectionId);\n if (!panel?.render) return;\n\n const contentEl = renderRoot.querySelector(`[data-section=\"${sectionId}\"] .tbw-accordion-content`);\n if (!contentEl) return;\n\n const cleanup = panel.render(contentEl as HTMLElement);\n if (cleanup) {\n state.panelCleanups.set(sectionId, cleanup);\n }\n}\n\n// ============================================================================\n// Grid Render HTML\n// ============================================================================\n\n/**\n * Core grid content HTML template.\n * Uses faux scrollbar pattern for smooth virtualized scrolling.\n */\nexport const GRID_CONTENT_HTML = `\n <div class=\"tbw-scroll-area\">\n <div class=\"rows-body-wrapper\">\n <div class=\"rows-body\" role=\"grid\">\n <div class=\"header\" role=\"rowgroup\">\n <div class=\"header-row\" role=\"row\" part=\"header-row\"></div>\n </div>\n <div class=\"rows-container\" role=\"presentation\">\n <div class=\"rows-viewport\" role=\"presentation\">\n <div class=\"rows\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"faux-vscroll\">\n <div class=\"faux-vscroll-spacer\"></div>\n </div>\n`;\n\n// ============================================================================\n// DOM-based Grid Rendering (Performance Optimized)\n// ============================================================================\n\nimport {\n buildGridDOM,\n buildShellBody,\n buildShellHeader,\n type ShellBodyOptions,\n type ShellHeaderOptions,\n} from './dom-builder';\n\n/**\n * Build the complete grid DOM structure using direct DOM construction.\n * This is 2-3x faster than innerHTML for initial render.\n *\n * @param renderRoot - The element to render into (will be cleared)\n * @param shellConfig - Shell configuration\n * @param state - Shell state\n * @param icons - Optional icons\n * @returns Whether shell is active (for post-render setup)\n */\nexport function buildGridDOMIntoElement(\n renderRoot: Element,\n shellConfig: ShellConfig | undefined,\n runtimeState: { isPanelOpen: boolean; expandedSections: Set<string> },\n icons?: { toolPanel?: IconValue; expand?: IconValue; collapse?: IconValue },\n): boolean {\n const hasShell = shouldRenderShellHeader(shellConfig);\n\n // Preserve light DOM elements before clearing (they contain user content)\n // These are custom elements used for declarative configuration\n const lightDomElements: Element[] = [];\n const lightDomSelectors = [\n 'tbw-grid-header',\n 'tbw-grid-tool-buttons',\n 'tbw-grid-tool-panel',\n 'tbw-grid-column',\n 'tbw-grid-detail',\n 'tbw-grid-responsive-card',\n ];\n for (const selector of lightDomSelectors) {\n const elements = renderRoot.querySelectorAll(`:scope > ${selector}`);\n elements.forEach((el) => lightDomElements.push(el));\n }\n\n // Clear existing content (this would delete light DOM elements, so we preserved them first)\n renderRoot.replaceChildren();\n\n // Re-append preserved light DOM elements (hidden, they're used for config)\n for (const el of lightDomElements) {\n renderRoot.appendChild(el);\n }\n\n if (hasShell) {\n const toolPanelIcon = iconToString(icons?.toolPanel ?? DEFAULT_GRID_ICONS.toolPanel);\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n // All toolbar contents now come from shellConfig (merged by ConfigManager)\n const allContents = shellConfig?.header?.toolbarContents ?? [];\n const sortedContents = [...allContents].sort((a, b) => (a.order ?? 0) - (b.order ?? 0));\n\n // All panels now come from shellConfig (merged by ConfigManager)\n const allPanels = shellConfig?.toolPanels ?? [];\n const sortedPanels = [...allPanels].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build header options\n const headerOptions: ShellHeaderOptions = {\n title: shellConfig?.header?.title ?? undefined,\n hasPanels: sortedPanels.length > 0,\n isPanelOpen: runtimeState.isPanelOpen,\n toolPanelIcon,\n // All contents are now in config (no more separate config vs API distinction for rendering)\n configButtons: sortedContents.map((c) => ({\n id: c.id,\n hasElement: false,\n hasRender: !!c.render,\n })),\n apiButtons: [], // No longer needed - all contents merged into configButtons\n };\n\n // Build body options\n const bodyOptions: ShellBodyOptions = {\n position: shellConfig?.toolPanel?.position ?? 'right',\n isPanelOpen: runtimeState.isPanelOpen,\n expandIcon,\n collapseIcon,\n panels: sortedPanels.map((p) => ({\n id: p.id,\n title: p.title,\n icon: iconToString(p.icon),\n isExpanded: runtimeState.expandedSections.has(p.id),\n })),\n };\n\n // Build shell elements\n const shellHeader = buildShellHeader(headerOptions);\n const shellBody = buildShellBody(bodyOptions);\n\n // Build and append complete DOM\n const fragment = buildGridDOM({\n hasShell: true,\n shellHeader,\n shellBody,\n });\n renderRoot.appendChild(fragment);\n } else {\n // No shell - just grid content\n const fragment = buildGridDOM({ hasShell: false });\n renderRoot.appendChild(fragment);\n }\n\n return hasShell;\n}\n","/**\n * Touch scrolling controller for mobile devices.\n *\n * Handles touch events for scrolling with momentum physics.\n * Supports both vertical (faux scrollbar) and horizontal (scroll area) scrolling.\n */\n\nexport interface TouchScrollState {\n startY: number | null;\n startX: number | null;\n scrollTop: number | null;\n scrollLeft: number | null;\n lastY: number | null;\n lastX: number | null;\n lastTime: number | null;\n velocityY: number;\n velocityX: number;\n momentumRaf: number;\n}\n\nexport interface TouchScrollElements {\n fauxScrollbar: HTMLElement;\n scrollArea: HTMLElement | null;\n}\n\n/**\n * Create initial touch scroll state.\n */\nexport function createTouchScrollState(): TouchScrollState {\n return {\n startY: null,\n startX: null,\n scrollTop: null,\n scrollLeft: null,\n lastY: null,\n lastX: null,\n lastTime: null,\n velocityY: 0,\n velocityX: 0,\n momentumRaf: 0,\n };\n}\n\n/**\n * Reset touch scroll state (called on touchend or cleanup).\n */\nexport function resetTouchState(state: TouchScrollState): void {\n state.startY = null;\n state.startX = null;\n state.scrollTop = null;\n state.scrollLeft = null;\n state.lastY = null;\n state.lastX = null;\n state.lastTime = null;\n}\n\n/**\n * Cancel any ongoing momentum animation.\n */\nexport function cancelMomentum(state: TouchScrollState): void {\n if (state.momentumRaf) {\n cancelAnimationFrame(state.momentumRaf);\n state.momentumRaf = 0;\n }\n}\n\n/**\n * Handle touchstart event.\n */\nexport function handleTouchStart(e: TouchEvent, state: TouchScrollState, elements: TouchScrollElements): void {\n if (e.touches.length !== 1) return;\n\n // Cancel any ongoing momentum animation\n cancelMomentum(state);\n\n const touch = e.touches[0];\n state.startY = touch.clientY;\n state.startX = touch.clientX;\n state.lastY = touch.clientY;\n state.lastX = touch.clientX;\n state.lastTime = performance.now();\n state.scrollTop = elements.fauxScrollbar.scrollTop;\n state.scrollLeft = elements.scrollArea?.scrollLeft ?? 0;\n state.velocityY = 0;\n state.velocityX = 0;\n}\n\n/**\n * Handle touchmove event.\n * Returns true if the event should be prevented (grid scrolled).\n */\nexport function handleTouchMove(e: TouchEvent, state: TouchScrollState, elements: TouchScrollElements): boolean {\n if (\n e.touches.length !== 1 ||\n state.startY === null ||\n state.startX === null ||\n state.scrollTop === null ||\n state.scrollLeft === null\n ) {\n return false;\n }\n\n const touch = e.touches[0];\n const currentY = touch.clientY;\n const currentX = touch.clientX;\n const now = performance.now();\n\n const deltaY = state.startY - currentY;\n const deltaX = state.startX - currentX;\n\n // Calculate velocity for momentum scrolling\n if (state.lastTime !== null && state.lastY !== null && state.lastX !== null) {\n const dt = now - state.lastTime;\n if (dt > 0) {\n // Velocity in pixels per millisecond\n state.velocityY = (state.lastY - currentY) / dt;\n state.velocityX = (state.lastX - currentX) / dt;\n }\n }\n state.lastY = currentY;\n state.lastX = currentX;\n state.lastTime = now;\n\n // Check if grid can scroll in the requested directions\n const { scrollTop, scrollHeight, clientHeight } = elements.fauxScrollbar;\n const maxScrollY = scrollHeight - clientHeight;\n const canScrollVertically = (deltaY > 0 && scrollTop < maxScrollY) || (deltaY < 0 && scrollTop > 0);\n\n let canScrollHorizontally = false;\n if (elements.scrollArea) {\n const { scrollLeft, scrollWidth, clientWidth } = elements.scrollArea;\n const maxScrollX = scrollWidth - clientWidth;\n canScrollHorizontally = (deltaX > 0 && scrollLeft < maxScrollX) || (deltaX < 0 && scrollLeft > 0);\n }\n\n // Apply scroll if grid can scroll in that direction\n if (canScrollVertically) {\n elements.fauxScrollbar.scrollTop = state.scrollTop + deltaY;\n }\n if (canScrollHorizontally && elements.scrollArea) {\n elements.scrollArea.scrollLeft = state.scrollLeft + deltaX;\n }\n\n // Return true to prevent page scroll when we scrolled the grid\n return canScrollVertically || canScrollHorizontally;\n}\n\n/**\n * Handle touchend event.\n * Starts momentum scrolling if velocity is significant.\n */\nexport function handleTouchEnd(state: TouchScrollState, elements: TouchScrollElements): void {\n const minVelocity = 0.1; // pixels per ms threshold\n\n // Start momentum scrolling if there's significant velocity\n if (Math.abs(state.velocityY) > minVelocity || Math.abs(state.velocityX) > minVelocity) {\n startMomentumScroll(state, elements);\n }\n\n resetTouchState(state);\n}\n\n/**\n * Start momentum scrolling animation.\n */\nfunction startMomentumScroll(state: TouchScrollState, elements: TouchScrollElements): void {\n const friction = 0.95; // Deceleration factor per frame\n const minVelocity = 0.01; // Stop threshold in px/ms\n\n const animate = () => {\n // Apply friction\n state.velocityY *= friction;\n state.velocityX *= friction;\n\n // Convert velocity (px/ms) to per-frame scroll amount (~16ms per frame)\n const scrollY = state.velocityY * 16;\n const scrollX = state.velocityX * 16;\n\n // Apply scroll if above threshold\n if (Math.abs(state.velocityY) > minVelocity) {\n elements.fauxScrollbar.scrollTop += scrollY;\n }\n if (Math.abs(state.velocityX) > minVelocity && elements.scrollArea) {\n elements.scrollArea.scrollLeft += scrollX;\n }\n\n // Continue animation if still moving\n if (Math.abs(state.velocityY) > minVelocity || Math.abs(state.velocityX) > minVelocity) {\n state.momentumRaf = requestAnimationFrame(animate);\n } else {\n state.momentumRaf = 0;\n }\n };\n\n state.momentumRaf = requestAnimationFrame(animate);\n}\n\n/**\n * Set up touch scroll event listeners on the grid content element.\n * Returns a cleanup function that removes all listeners.\n */\nexport function setupTouchScrollListeners(\n gridContentEl: HTMLElement,\n state: TouchScrollState,\n elements: TouchScrollElements,\n signal: AbortSignal,\n): void {\n gridContentEl.addEventListener('touchstart', (e: TouchEvent) => handleTouchStart(e, state, elements), {\n passive: true,\n signal,\n });\n\n gridContentEl.addEventListener(\n 'touchmove',\n (e: TouchEvent) => {\n const shouldPrevent = handleTouchMove(e, state, elements);\n if (shouldPrevent) {\n e.preventDefault();\n }\n },\n { passive: false, signal },\n );\n\n gridContentEl.addEventListener('touchend', () => handleTouchEnd(state, elements), { passive: true, signal });\n}\n","/**\n * Configuration Validation\n *\n * Runtime validators that check for plugin-specific properties in config\n * and throw helpful errors if the required plugin is not loaded.\n *\n * This catches common mistakes like using `editable: true` without EditingPlugin.\n *\n * Uses a static registry of known plugin-owned properties to detect when users\n * configure features that require plugins they haven't loaded.\n */\n\nimport type { BaseGridPlugin, PluginManifest, PluginPropertyDefinition } from '../plugin';\nimport type { ColumnConfig, GridConfig } from '../types';\n\n/**\n * Internal property definition with plugin name attached.\n * Extends PluginPropertyDefinition with required pluginName for validation.\n */\ninterface InternalPropertyDefinition extends PluginPropertyDefinition {\n pluginName: string;\n}\n\n// ============================================================================\n// Known Plugin-Owned Properties (Static Registry)\n// ============================================================================\n\n/**\n * Static registry of known plugin-owned column properties.\n * This enables detection of plugin properties even when the plugin isn't loaded.\n * Properties defined here allow helpful error messages when plugins are missing.\n *\n * ## Why This Exists (The Validation Paradox)\n *\n * We need to detect when a developer uses a plugin-owned property (like `editable`)\n * but forgets to add the plugin. However, if the plugin isn't loaded, we can't\n * read its manifest! The manifest only exists when the plugin class is imported.\n *\n * This static registry solves that: it's a \"well-known properties\" list that exists\n * independently of whether plugins are loaded.\n *\n * ## When Adding New Plugin-Owned Properties\n *\n * 1. **Always**: Add to the plugin's manifest `ownedProperties` (documentation, lives with plugin)\n * 2. **Optionally**: Add here if you want \"forgot to add plugin\" detection for that property\n *\n * Not every property needs to be here - only high-value ones where developers commonly\n * forget to add the plugin. Third-party plugins can't be listed here anyway.\n *\n * ## Future Improvement\n *\n * A build-time script could generate these arrays from plugin manifests,\n * creating a single source of truth. For now, they're maintained manually.\n */\nconst KNOWN_COLUMN_PROPERTIES: InternalPropertyDefinition[] = [\n // EditingPlugin\n {\n property: 'editable',\n pluginName: 'editing',\n level: 'column',\n description: 'the \"editable\" column property',\n importHint: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n isUsed: (v) => v === true,\n },\n {\n property: 'editor',\n pluginName: 'editing',\n level: 'column',\n description: 'the \"editor\" column property',\n importHint: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n },\n {\n property: 'editorParams',\n pluginName: 'editing',\n level: 'column',\n description: 'the \"editorParams\" column property',\n importHint: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n },\n // GroupingColumnsPlugin\n {\n property: 'group',\n pluginName: 'groupingColumns',\n level: 'column',\n description: 'the \"group\" column property',\n importHint: \"import { GroupingColumnsPlugin } from '@toolbox-web/grid/plugins/grouping-columns';\",\n },\n // PinnedColumnsPlugin\n {\n property: 'sticky',\n pluginName: 'pinnedColumns',\n level: 'column',\n description: 'the \"sticky\" column property',\n importHint: \"import { PinnedColumnsPlugin } from '@toolbox-web/grid/plugins/pinned-columns';\",\n isUsed: (v) => v === 'left' || v === 'right',\n },\n];\n\n/**\n * Static registry of known plugin-owned grid config properties.\n */\nconst KNOWN_CONFIG_PROPERTIES: InternalPropertyDefinition[] = [\n // GroupingColumnsPlugin\n {\n property: 'columnGroups',\n pluginName: 'groupingColumns',\n level: 'config',\n description: 'the \"columnGroups\" config property',\n importHint: \"import { GroupingColumnsPlugin } from '@toolbox-web/grid/plugins/grouping-columns';\",\n isUsed: (v) => Array.isArray(v) && v.length > 0,\n },\n];\n\n// ============================================================================\n// Import Hints (for error messages)\n// ============================================================================\n\n/**\n * Map of known plugin names to their npm import paths.\n * Used to generate helpful error messages with import hints.\n */\nconst PLUGIN_IMPORT_HINTS: Record<string, string> = {\n editing: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n selection: \"import { SelectionPlugin } from '@toolbox-web/grid/plugins/selection';\",\n reorder: \"import { ReorderPlugin } from '@toolbox-web/grid/plugins/reorder';\",\n clipboard: \"import { ClipboardPlugin } from '@toolbox-web/grid/plugins/clipboard';\",\n filtering: \"import { FilteringPlugin } from '@toolbox-web/grid/plugins/filtering';\",\n multiSort: \"import { MultiSortPlugin } from '@toolbox-web/grid/plugins/multi-sort';\",\n groupingRows: \"import { GroupingRowsPlugin } from '@toolbox-web/grid/plugins/grouping-rows';\",\n groupingColumns: \"import { GroupingColumnsPlugin } from '@toolbox-web/grid/plugins/grouping-columns';\",\n tree: \"import { TreePlugin } from '@toolbox-web/grid/plugins/tree';\",\n masterDetail: \"import { MasterDetailPlugin } from '@toolbox-web/grid/plugins/master-detail';\",\n pinnedColumns: \"import { PinnedColumnsPlugin } from '@toolbox-web/grid/plugins/pinned-columns';\",\n pinnedRows: \"import { PinnedRowsPlugin } from '@toolbox-web/grid/plugins/pinned-rows';\",\n visibility: \"import { VisibilityPlugin } from '@toolbox-web/grid/plugins/visibility';\",\n undoRedo: \"import { UndoRedoPlugin } from '@toolbox-web/grid/plugins/undo-redo';\",\n export: \"import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\",\n contextMenu: \"import { ContextMenuPlugin } from '@toolbox-web/grid/plugins/context-menu';\",\n pivot: \"import { PivotPlugin } from '@toolbox-web/grid/plugins/pivot';\",\n serverSide: \"import { ServerSidePlugin } from '@toolbox-web/grid/plugins/server-side';\",\n columnVirtualization: \"import { ColumnVirtualizationPlugin } from '@toolbox-web/grid/plugins/column-virtualization';\",\n};\n\n/**\n * Get the import hint for a plugin, with a fallback for unknown plugins.\n */\nfunction getImportHint(pluginName: string): string {\n return (\n PLUGIN_IMPORT_HINTS[pluginName] ??\n `import { ${capitalize(pluginName)}Plugin } from '@toolbox-web/grid/plugins/${pluginName}';`\n );\n}\n\n// ============================================================================\n// Development Mode Detection\n// ============================================================================\n\n/**\n * Check if we're running in a development environment.\n * Warnings are only shown in development to avoid polluting production logs.\n */\nfunction isDevelopment(): boolean {\n // Check for localhost (browser environment)\n if (typeof window !== 'undefined' && window.location) {\n const hostname = window.location.hostname;\n if (hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1') {\n return true;\n }\n }\n // Check for NODE_ENV (build-time or SSR)\n if (typeof process !== 'undefined' && process.env?.NODE_ENV !== 'production') {\n return true;\n }\n return false;\n}\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Helper to capitalize a plugin name for display.\n */\nfunction capitalize(s: string): string {\n return s.charAt(0).toUpperCase() + s.slice(1);\n}\n\n/**\n * Check if a plugin with the given name is present in the plugins array.\n */\nfunction hasPlugin(plugins: readonly BaseGridPlugin[], pluginName: string): boolean {\n return plugins.some((p) => p.name === pluginName);\n}\n\n// ============================================================================\n// Property Validation\n// ============================================================================\n\n/**\n * Validate that column properties requiring plugins have those plugins loaded.\n *\n * @param config - The merged grid configuration\n * @param plugins - The array of loaded plugins\n * @throws Error if a plugin-owned property is used without the plugin\n */\nexport function validatePluginProperties<T>(config: GridConfig<T>, plugins: readonly BaseGridPlugin[]): void {\n // Use static registries of known plugin-owned properties\n const columnProps = KNOWN_COLUMN_PROPERTIES;\n const configProps = KNOWN_CONFIG_PROPERTIES;\n\n // Group errors by plugin to avoid spamming multiple errors\n const missingPlugins = new Map<\n string,\n { description: string; importHint: string; fields: string[]; isConfigProperty?: boolean }\n >();\n\n // Helper to add an error for a missing plugin\n function addError(\n pluginName: string,\n description: string,\n importHint: string,\n field: string,\n isConfigProperty = false,\n ) {\n if (!missingPlugins.has(pluginName)) {\n missingPlugins.set(pluginName, { description, importHint, fields: [], isConfigProperty });\n }\n // Entry is guaranteed to exist after the set above\n const entry = missingPlugins.get(pluginName)!;\n if (!entry.fields.includes(field)) {\n entry.fields.push(field);\n }\n }\n\n // Validate grid config properties\n for (const def of configProps) {\n const value = (config as Record<string, unknown>)[def.property];\n const isUsed = def.isUsed ? def.isUsed(value) : value !== undefined;\n\n if (isUsed && !hasPlugin(plugins, def.pluginName)) {\n addError(def.pluginName, def.description, def.importHint ?? getImportHint(def.pluginName), def.property, true);\n }\n }\n\n // Validate column properties\n const columns = config.columns;\n if (columns && columns.length > 0) {\n for (const column of columns) {\n for (const def of columnProps) {\n const value = (column as unknown as Record<string, unknown>)[def.property];\n // Use custom isUsed check if provided, otherwise check for defined value\n const isUsed = def.isUsed ? def.isUsed(value) : value !== undefined;\n\n if (isUsed && !hasPlugin(plugins, def.pluginName)) {\n const field = (column as ColumnConfig).field || '<unknown>';\n addError(def.pluginName, def.description, def.importHint ?? getImportHint(def.pluginName), field);\n }\n }\n }\n }\n\n // Throw a single consolidated error if any missing plugins\n if (missingPlugins.size > 0) {\n const errors: string[] = [];\n for (const [pluginName, { description, importHint, fields, isConfigProperty }] of missingPlugins) {\n if (isConfigProperty) {\n // Config-level property error\n errors.push(\n `Config uses ${description}, but the required plugin is not loaded.\\n` +\n ` → Add the plugin to your gridConfig.plugins array:\\n` +\n ` ${importHint}\\n` +\n ` plugins: [new ${pluginName.charAt(0).toUpperCase() + pluginName.slice(1)}Plugin(), ...]`,\n );\n } else {\n // Column-level property error\n const fieldList = fields.slice(0, 3).join(', ') + (fields.length > 3 ? `, ... (${fields.length} total)` : '');\n errors.push(\n `Column(s) [${fieldList}] use ${description}, but the required plugin is not loaded.\\n` +\n ` → Add the plugin to your gridConfig.plugins array:\\n` +\n ` ${importHint}\\n` +\n ` plugins: [new ${pluginName.charAt(0).toUpperCase() + pluginName.slice(1)}Plugin(), ...]`,\n );\n }\n }\n\n throw new Error(\n `[tbw-grid] Configuration error:\\n\\n${errors.join('\\n\\n')}\\n\\n` +\n `This validation helps catch misconfigurations early. ` +\n `The properties listed above require their respective plugins to function.`,\n );\n }\n}\n\n// ============================================================================\n// Plugin Config Rules Validation\n// ============================================================================\n\n/**\n * Validate plugin configuration rules declared in manifests.\n * Called after plugins are attached to check for invalid/conflicting configurations.\n *\n * Rules with severity 'error' throw an error.\n * Rules with severity 'warn' log a warning to console.\n *\n * @param plugins - The array of attached plugins (with config already merged)\n */\nexport function validatePluginConfigRules(plugins: readonly BaseGridPlugin[]): void {\n const errors: string[] = [];\n const warnings: string[] = [];\n\n for (const plugin of plugins) {\n const PluginClass = plugin.constructor as typeof BaseGridPlugin;\n const manifest = PluginClass.manifest as PluginManifest | undefined;\n if (!manifest?.configRules) continue;\n\n for (const rule of manifest.configRules) {\n // Access plugin's merged config via protected property\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const pluginConfig = (plugin as any).config;\n if (rule.check(pluginConfig)) {\n const prefix = `[tbw-grid:${capitalize(plugin.name)}Plugin]`;\n const formatted = `${prefix} Configuration warning: ${rule.message}`;\n if (rule.severity === 'error') {\n errors.push(formatted);\n } else {\n warnings.push(formatted);\n }\n }\n }\n }\n\n // Log warnings only in development (don't pollute production logs)\n if (warnings.length > 0 && isDevelopment()) {\n for (const warning of warnings) {\n console.warn(warning);\n }\n }\n\n // Throw consolidated error if any (always, regardless of environment)\n if (errors.length > 0) {\n throw new Error(`[tbw-grid] Configuration error:\\n\\n${errors.join('\\n\\n')}`);\n }\n}\n\n// ============================================================================\n// Plugin Dependency Validation\n// ============================================================================\n\n/**\n * Validate plugin-to-plugin dependencies.\n * Called by PluginManager when attaching a new plugin.\n *\n * Dependencies are read from the plugin's static `dependencies` property.\n *\n * For hard dependencies (required: true), throws an error if the dependency is not loaded.\n * For soft dependencies (required: false), logs an info message but continues.\n *\n * @param plugin - The plugin instance being attached\n * @param loadedPlugins - The array of already-loaded plugins\n * @throws Error if a required dependency is missing\n */\nexport function validatePluginDependencies(plugin: BaseGridPlugin, loadedPlugins: readonly BaseGridPlugin[]): void {\n const pluginName = plugin.name;\n const PluginClass = plugin.constructor as typeof BaseGridPlugin;\n\n // Get dependencies from plugin's static property\n const dependencies = PluginClass.dependencies ?? [];\n\n // Validate each dependency\n for (const dep of dependencies) {\n const requiredPlugin = dep.name;\n const required = dep.required ?? true; // Default to required\n const reason = dep.reason;\n const hasRequired = loadedPlugins.some((p) => p.name === requiredPlugin);\n\n if (!hasRequired) {\n const reasonText = reason ?? `${capitalize(pluginName)}Plugin requires ${capitalize(requiredPlugin)}Plugin`;\n const importHint = getImportHint(requiredPlugin);\n\n if (required) {\n throw new Error(\n `[tbw-grid] Plugin dependency error:\\n\\n` +\n `${reasonText}.\\n\\n` +\n ` → Add the plugin to your gridConfig.plugins array BEFORE ${capitalize(pluginName)}Plugin:\\n` +\n ` ${importHint}\\n` +\n ` plugins: [new ${capitalize(requiredPlugin)}Plugin(), new ${capitalize(pluginName)}Plugin()]`,\n );\n } else {\n // Soft dependency - log info message but continue\n console.info(\n `[tbw-grid] ${capitalize(pluginName)}Plugin: Optional \"${requiredPlugin}\" plugin not found. ` +\n `Some features may be unavailable.`,\n );\n }\n }\n }\n}\n// ============================================================================\n// Plugin Incompatibility Validation\n// ============================================================================\n\n/**\n * Validate that no incompatible plugins are loaded together.\n * Called after all plugins are attached to the grid.\n *\n * Incompatibilities are read from each plugin's manifest `incompatibleWith` property.\n * When a conflict is detected, a warning is logged (in development mode).\n *\n * @param plugins - All attached plugins\n */\nexport function validatePluginIncompatibilities(plugins: readonly BaseGridPlugin[]): void {\n // Only warn in development mode to avoid polluting production logs\n if (!isDevelopment()) return;\n\n const pluginNames = new Set(plugins.map((p) => p.name));\n const warned = new Set<string>(); // Avoid duplicate warnings for symmetric conflicts\n\n for (const plugin of plugins) {\n const PluginClass = plugin.constructor as typeof BaseGridPlugin;\n const manifest = PluginClass.manifest as PluginManifest | undefined;\n if (!manifest?.incompatibleWith) continue;\n\n for (const incompatibility of manifest.incompatibleWith) {\n if (pluginNames.has(incompatibility.name)) {\n // Create a symmetric key to avoid warning twice (A→B and B→A)\n const key = [plugin.name, incompatibility.name].sort().join('↔');\n if (warned.has(key)) continue;\n warned.add(key);\n\n console.warn(\n `[tbw-grid] Plugin incompatibility warning:\\n\\n` +\n `${capitalize(plugin.name)}Plugin and ${capitalize(incompatibility.name)}Plugin are both loaded, ` +\n `but they are currently incompatible.\\n\\n` +\n ` → ${incompatibility.reason}\\n\\n` +\n ` Consider removing one of these plugins to avoid unexpected behavior.`,\n );\n }\n }\n }\n}","/**\n * Plugin Manager\n *\n * Manages plugin instances for a single grid.\n * Each grid has its own PluginManager with its own set of plugin instances.\n *\n * **Plugin Order Matters**: Plugins are executed in the order they appear in the\n * `plugins` array. This affects the order of hook execution (processRows, processColumns,\n * afterRender, etc.). For example, if you want filtering to run before grouping,\n * add FilteringPlugin before GroupingRowsPlugin in the array.\n */\n\nimport { validatePluginDependencies } from '../internal/validate-config';\nimport type { ColumnConfig } from '../types';\nimport type {\n AfterCellRenderContext,\n AfterRowRenderContext,\n BaseGridPlugin,\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n GridElement,\n HeaderClickEvent,\n HeaderRenderer,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './base-plugin';\n\n/**\n * Manages plugins for a single grid instance.\n *\n * Plugins are executed in array order. This is intentional and documented behavior.\n * Place plugins in the order you want their hooks to execute.\n */\nexport class PluginManager {\n /** Plugin instances in order of attachment */\n private plugins: BaseGridPlugin[] = [];\n\n /** Get all registered plugins (read-only) */\n getPlugins(): readonly BaseGridPlugin[] {\n return this.plugins;\n }\n\n /** Map from plugin class to instance for fast lookup */\n private pluginMap: Map<new (...args: unknown[]) => BaseGridPlugin, BaseGridPlugin> = new Map();\n\n /** Cell renderers registered by plugins */\n private cellRenderers: Map<string, CellRenderer> = new Map();\n\n /** Header renderers registered by plugins */\n private headerRenderers: Map<string, HeaderRenderer> = new Map();\n\n /** Cell editors registered by plugins */\n private cellEditors: Map<string, CellEditor> = new Map();\n\n constructor(private grid: GridElement) {}\n\n /**\n * Attach all plugins from the config.\n */\n attachAll(plugins: BaseGridPlugin[]): void {\n for (const plugin of plugins) {\n this.attach(plugin);\n }\n }\n\n /**\n * Attach a plugin to this grid.\n * Validates dependencies and notifies other plugins of the new attachment.\n */\n attach(plugin: BaseGridPlugin): void {\n // Validate plugin dependencies BEFORE attaching\n // This throws if a required dependency is missing\n validatePluginDependencies(plugin, this.plugins);\n\n // Store by constructor for type-safe lookup\n this.pluginMap.set(plugin.constructor as new (...args: unknown[]) => BaseGridPlugin, plugin);\n this.plugins.push(plugin);\n\n // Register renderers/editors\n if (plugin.cellRenderers) {\n for (const [type, renderer] of Object.entries(plugin.cellRenderers)) {\n this.cellRenderers.set(type, renderer);\n }\n }\n if (plugin.headerRenderers) {\n for (const [type, renderer] of Object.entries(plugin.headerRenderers)) {\n this.headerRenderers.set(type, renderer);\n }\n }\n if (plugin.cellEditors) {\n for (const [type, editor] of Object.entries(plugin.cellEditors)) {\n this.cellEditors.set(type, editor);\n }\n }\n\n // Call attach lifecycle method\n plugin.attach(this.grid);\n\n // Notify existing plugins of the new attachment\n for (const existingPlugin of this.plugins) {\n if (existingPlugin !== plugin && existingPlugin.onPluginAttached) {\n existingPlugin.onPluginAttached(plugin.name, plugin);\n }\n }\n }\n\n /**\n * Detach all plugins and clean up.\n * Notifies plugins of detachment via onPluginDetached hook.\n */\n detachAll(): void {\n // Notify all plugins before detaching (in forward order)\n for (const plugin of this.plugins) {\n for (const otherPlugin of this.plugins) {\n if (otherPlugin !== plugin && otherPlugin.onPluginDetached) {\n otherPlugin.onPluginDetached(plugin.name);\n }\n }\n }\n\n // Detach in reverse order\n for (let i = this.plugins.length - 1; i >= 0; i--) {\n this.plugins[i].detach();\n }\n this.plugins = [];\n this.pluginMap.clear();\n this.cellRenderers.clear();\n this.headerRenderers.clear();\n this.cellEditors.clear();\n }\n\n /**\n * Get a plugin instance by its class.\n */\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.pluginMap.get(PluginClass) as T | undefined;\n }\n\n /**\n * Get a plugin instance by its name.\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.plugins.find((p) => p.name === name);\n }\n\n /**\n * Check if a plugin is attached.\n */\n hasPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): boolean {\n return this.pluginMap.has(PluginClass);\n }\n\n /**\n * Get all attached plugins.\n */\n getAll(): readonly BaseGridPlugin[] {\n return this.plugins;\n }\n\n /**\n * Get names of all registered plugins (for debugging).\n */\n getRegisteredPluginNames(): string[] {\n return this.plugins.map((p) => p.name);\n }\n\n /**\n * Get a cell renderer by type name.\n */\n getCellRenderer(type: string): CellRenderer | undefined {\n return this.cellRenderers.get(type);\n }\n\n /**\n * Get a header renderer by type name.\n */\n getHeaderRenderer(type: string): HeaderRenderer | undefined {\n return this.headerRenderers.get(type);\n }\n\n /**\n * Get a cell editor by type name.\n */\n getCellEditor(type: string): CellEditor | undefined {\n return this.cellEditors.get(type);\n }\n\n /**\n * Get all CSS styles from all plugins as structured data.\n * Returns an array of { name, styles } for each plugin with styles.\n */\n getPluginStyles(): Array<{ name: string; styles: string }> {\n return this.plugins.filter((p) => p.styles).map((p) => ({ name: p.name, styles: p.styles! }));\n }\n\n // #region Hook execution methods\n\n /**\n * Execute processRows hook on all plugins.\n */\n processRows(rows: readonly any[]): any[] {\n let result = [...rows];\n for (const plugin of this.plugins) {\n if (plugin.processRows) {\n result = plugin.processRows(result);\n }\n }\n return result;\n }\n\n /**\n * Execute processColumns hook on all plugins.\n */\n processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n let result = [...columns];\n for (const plugin of this.plugins) {\n if (plugin.processColumns) {\n result = plugin.processColumns(result);\n }\n }\n return result;\n }\n\n /**\n * Execute beforeRender hook on all plugins.\n */\n beforeRender(): void {\n for (const plugin of this.plugins) {\n plugin.beforeRender?.();\n }\n }\n\n /**\n * Execute afterRender hook on all plugins.\n */\n afterRender(): void {\n for (const plugin of this.plugins) {\n plugin.afterRender?.();\n }\n }\n\n /**\n * Execute afterCellRender hook on all plugins for a single cell.\n * Called during cell rendering for efficient cell-level modifications.\n *\n * @param context - The cell render context\n */\n afterCellRender(context: AfterCellRenderContext): void {\n for (const plugin of this.plugins) {\n plugin.afterCellRender?.(context);\n }\n }\n\n /**\n * Check if any plugin has the afterCellRender hook implemented.\n * Used to skip the hook call overhead when no plugins need it.\n */\n hasAfterCellRenderHook(): boolean {\n return this.plugins.some((p) => typeof p.afterCellRender === 'function');\n }\n\n /**\n * Execute afterRowRender hook on all plugins for a single row.\n * Called after all cells in a row are rendered for efficient row-level modifications.\n *\n * @param context - The row render context\n */\n afterRowRender(context: AfterRowRenderContext): void {\n for (const plugin of this.plugins) {\n plugin.afterRowRender?.(context);\n }\n }\n\n /**\n * Check if any plugin has the afterRowRender hook implemented.\n * Used to skip the hook call overhead when no plugins need it.\n */\n hasAfterRowRenderHook(): boolean {\n return this.plugins.some((p) => typeof p.afterRowRender === 'function');\n }\n\n /**\n * Execute onScrollRender hook on all plugins.\n * Called after scroll-triggered row rendering for lightweight visual state updates.\n */\n onScrollRender(): void {\n for (const plugin of this.plugins) {\n plugin.onScrollRender?.();\n }\n }\n\n /**\n * Get total extra height contributed by plugins (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations.\n */\n getExtraHeight(): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeight === 'function') {\n total += plugin.getExtraHeight();\n }\n }\n return total;\n }\n\n /**\n * Check if any plugin is contributing extra height.\n * When true, plugins are managing variable row heights and the grid should\n * not override the base row height via #measureRowHeight().\n */\n hasExtraHeight(): boolean {\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeight === 'function' && plugin.getExtraHeight() > 0) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Get extra height from plugins that appears before a given row index.\n * Used by virtualization to correctly position the scroll window.\n */\n getExtraHeightBefore(beforeRowIndex: number): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeightBefore === 'function') {\n total += plugin.getExtraHeightBefore(beforeRowIndex);\n }\n }\n return total;\n }\n\n /**\n * Adjust the virtualization start index based on plugin needs.\n * Returns the minimum start index from all plugins.\n */\n adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n let adjustedStart = start;\n for (const plugin of this.plugins) {\n if (typeof plugin.adjustVirtualStart === 'function') {\n const pluginStart = plugin.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginStart < adjustedStart) {\n adjustedStart = pluginStart;\n }\n }\n }\n return adjustedStart;\n }\n\n /**\n * Execute renderRow hook on all plugins.\n * Returns true if any plugin handled the row.\n */\n renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean {\n for (const plugin of this.plugins) {\n if (plugin.renderRow?.(row, rowEl, rowIndex)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n *\n * Common query types are defined in PLUGIN_QUERIES, but plugins can define their own.\n *\n * @param query - The query object containing type and context\n * @returns Array of non-undefined responses from plugins\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n const responses: T[] = [];\n for (const plugin of this.plugins) {\n const response = plugin.onPluginQuery?.(query);\n if (response !== undefined) {\n responses.push(response as T);\n }\n }\n return responses;\n }\n\n /**\n * Execute onKeyDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onKeyDown(event: KeyboardEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onKeyDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellClick(event: CellClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onRowClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onRowClick(event: RowClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onRowClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onHeaderClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onHeaderClick(event: HeaderClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onHeaderClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onScroll hook on all plugins.\n */\n onScroll(event: ScrollEvent): void {\n for (const plugin of this.plugins) {\n plugin.onScroll?.(event);\n }\n }\n\n /**\n * Execute onCellMouseDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseDown(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseMove hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseMove(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseMove?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseUp hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseUp(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseUp?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Collect horizontal scroll boundary offsets from all plugins.\n * Combines offsets from all plugins that report them.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Combined left and right pixel offsets, plus skipScroll if any plugin requests it\n */\n getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n let left = 0;\n let right = 0;\n let skipScroll = false;\n for (const plugin of this.plugins) {\n const offsets = plugin.getHorizontalScrollOffsets?.(rowEl, focusedCell);\n if (offsets) {\n left += offsets.left;\n right += offsets.right;\n if (offsets.skipScroll) {\n skipScroll = true;\n }\n }\n }\n return { left, right, skipScroll };\n }\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Collect tool panels from all plugins.\n * Returns panels sorted by order (ascending).\n */\n getToolPanels(): {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] {\n const panels: {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const panel = plugin.getToolPanel?.();\n if (panel) {\n panels.push({ plugin, panel });\n }\n }\n // Sort by order (ascending), default to 0\n return panels.sort((a, b) => (a.panel.order ?? 0) - (b.panel.order ?? 0));\n }\n\n /**\n * Collect header contents from all plugins.\n * Returns contents sorted by order (ascending).\n */\n getHeaderContents(): {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] {\n const contents: {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const content = plugin.getHeaderContent?.();\n if (content) {\n contents.push({ plugin, content });\n }\n }\n // Sort by order (ascending), default to 0\n return contents.sort((a, b) => (a.content.order ?? 0) - (b.content.order ?? 0));\n }\n // #endregion\n}\n","import styles from './grid.css?inline';\nimport { autoSizeColumns, updateTemplate } from './internal/columns';\nimport { ConfigManager } from './internal/config-manager';\nimport { setupCellEventDelegation, setupRootEventDelegation } from './internal/event-delegation';\nimport { renderHeader } from './internal/header';\nimport { cancelIdle, scheduleIdle } from './internal/idle-scheduler';\nimport { ensureCellVisible } from './internal/keyboard';\nimport { RenderPhase, RenderScheduler } from './internal/render-scheduler';\nimport { createResizeController } from './internal/resize';\nimport { animateRow, animateRowById, animateRows } from './internal/row-animation';\nimport { invalidateCellCache, renderVisibleRows } from './internal/rows';\nimport {\n buildGridDOMIntoElement,\n cleanupShellState,\n createShellController,\n createShellState,\n parseLightDomShell,\n parseLightDomToolButtons,\n parseLightDomToolPanels,\n renderCustomToolbarContents,\n renderHeaderContent,\n renderShellHeader,\n setupShellEventListeners,\n setupToolPanelResize,\n shouldRenderShellHeader,\n type ShellController,\n type ShellState,\n type ToolPanelRendererFactory,\n} from './internal/shell';\nimport {\n cancelMomentum,\n createTouchScrollState,\n setupTouchScrollListeners,\n type TouchScrollState,\n} from './internal/touch-scroll';\nimport {\n validatePluginConfigRules,\n validatePluginIncompatibilities,\n validatePluginProperties,\n} from './internal/validate-config';\nimport type { AfterCellRenderContext, AfterRowRenderContext, CellMouseEvent, ScrollEvent } from './plugin';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n HeaderClickEvent,\n PluginQuery,\n RowClickEvent,\n} from './plugin/base-plugin';\nimport { PluginManager } from './plugin/plugin-manager';\nimport type {\n AnimationConfig,\n CellChangeDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n FitMode,\n FrameworkAdapter,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n InternalGrid,\n ResizeController,\n RowAnimationType,\n ToolbarContentDefinition,\n ToolPanelDefinition,\n UpdateSource,\n VirtualState,\n} from './types';\nimport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS } from './types';\n\n/**\n * High-performance data grid web component.\n *\n * ## Instantiation\n *\n * **Do not call the constructor directly.** Web components must be created via\n * the DOM API. Use one of these approaches:\n *\n * ```typescript\n * // Recommended: Use the createGrid() factory for TypeScript type safety\n * import { createGrid, SelectionPlugin } from '@toolbox-web/grid/all';\n *\n * const grid = createGrid<Employee>({\n * columns: [\n * { field: 'name', header: 'Name' },\n * { field: 'email', header: 'Email' }\n * ],\n * plugins: [new SelectionPlugin()]\n * });\n * grid.rows = employees;\n * document.body.appendChild(grid);\n *\n * // Alternative: Query existing element from DOM\n * import { queryGrid } from '@toolbox-web/grid';\n * const grid = queryGrid<Employee>('#my-grid');\n *\n * // Alternative: Use document.createElement (loses type inference)\n * const grid = document.createElement('tbw-grid');\n * ```\n *\n * ## Configuration Architecture\n *\n * The grid follows a **single source of truth** pattern where all configuration\n * is managed by ConfigManager. Users can set configuration via multiple inputs:\n *\n * **Input Sources (precedence low → high):**\n * 1. `gridConfig` property - base configuration object\n * 2. Light DOM elements:\n * - `<tbw-grid-column>` → `effectiveConfig.columns`\n * - `<tbw-grid-header title=\"...\">` → `effectiveConfig.shell.header.title`\n * - `<tbw-grid-header-content>` → `effectiveConfig.shell.header.content`\n * 3. `columns` property → merged into `effectiveConfig.columns`\n * 4. `fitMode` property → merged into `effectiveConfig.fitMode`\n * 5. Column inference from first row (if no columns defined)\n *\n * **Derived State:**\n * - `_columns` - processed columns from `effectiveConfig.columns` after plugin hooks\n * - `_rows` - processed rows after plugin hooks (grouping, filtering, etc.)\n *\n * ConfigManager.merge() is the single place where all inputs converge.\n * All rendering and logic should read from `effectiveConfig` or derived state.\n *\n * @element tbw-grid\n *\n * @csspart container - The main grid container\n * @csspart header - The header row container\n * @csspart body - The body/rows container\n *\n * @cssprop --tbw-color-bg - Background color\n * @cssprop --tbw-color-fg - Foreground/text color\n */\n// Injected by Vite at build time from package.json\ndeclare const __GRID_VERSION__: string;\n\nexport class DataGridElement<T = any> extends HTMLElement implements InternalGrid<T> {\n // TODO: Rename to 'data-grid' when migration is complete\n static readonly tagName = 'tbw-grid';\n /** Version of the grid component, injected at build time from package.json */\n static readonly version = typeof __GRID_VERSION__ !== 'undefined' ? __GRID_VERSION__ : 'dev';\n\n // ---------------- Framework Adapters ----------------\n /**\n * Registry of framework adapters that handle converting light DOM elements\n * to functional renderers/editors. Framework libraries (Angular, React, Vue)\n * register adapters to enable zero-boilerplate component integration.\n */\n private static adapters: FrameworkAdapter[] = [];\n\n /**\n * Register a framework adapter for handling framework-specific components.\n * Adapters are checked in registration order when processing light DOM templates.\n *\n * @example\n * ```typescript\n * // In @toolbox-web/grid-angular\n * import { AngularGridAdapter } from '@toolbox-web/grid-angular';\n *\n * // One-time setup in app\n * GridElement.registerAdapter(new AngularGridAdapter(injector, appRef));\n * ```\n * @category Framework Adapters\n */\n static registerAdapter(adapter: FrameworkAdapter): void {\n this.adapters.push(adapter);\n }\n\n /**\n * Get all registered framework adapters.\n * Used internally by light DOM parsing to find adapters that can handle templates.\n * @category Framework Adapters\n */\n static getAdapters(): readonly FrameworkAdapter[] {\n return this.adapters;\n }\n\n /**\n * Clear all registered adapters (primarily for testing).\n * @category Framework Adapters\n */\n static clearAdapters(): void {\n this.adapters = [];\n }\n\n // ---------------- Observed Attributes ----------------\n static get observedAttributes(): string[] {\n return ['rows', 'columns', 'grid-config', 'fit-mode'];\n }\n\n /**\n * The render root for the grid. Without Shadow DOM, this is the element itself.\n * This abstraction allows internal code to work the same way regardless of DOM mode.\n */\n get #renderRoot(): HTMLElement {\n return this;\n }\n\n #initialized = false;\n\n // ---------------- Ready Promise ----------------\n #readyPromise: Promise<void>;\n #readyResolve?: () => void;\n\n // #region Input Properties\n // Raw rows are stored here. Config sources (gridConfig, columns, fitMode)\n // are owned by ConfigManager. Grid.ts property setters delegate to ConfigManager.\n #rows: T[] = [];\n // #endregion\n\n // #region Private properties\n // effectiveConfig is owned by ConfigManager - access via getter\n get #effectiveConfig(): GridConfig<T> {\n return this.#configManager?.effective ?? {};\n }\n\n #connected = false;\n\n // ---------------- Batched Updates ----------------\n // When multiple properties are set in rapid succession (within same microtask),\n // we batch them into a single update to avoid redundant re-renders.\n #pendingUpdate = false;\n #pendingUpdateFlags = {\n rows: false,\n columns: false,\n gridConfig: false,\n fitMode: false,\n };\n\n // ---------------- Render Scheduler ----------------\n // Centralizes all rendering through a single RAF-based pipeline\n #scheduler!: RenderScheduler;\n\n #scrollRaf = 0;\n #pendingScrollTop: number | null = null;\n #hasScrollPlugins = false; // Cached flag for plugin scroll handlers\n #renderRowHook?: (row: any, rowEl: HTMLElement, rowIndex: number) => boolean; // Cached hook to avoid closures\n #touchState: TouchScrollState = createTouchScrollState();\n #eventAbortController?: AbortController;\n #resizeObserver?: ResizeObserver;\n #rowHeightObserver?: ResizeObserver; // Watches first row for size changes (CSS loading, custom renderers)\n #idleCallbackHandle?: number; // Handle for cancelling deferred idle work\n\n // Pooled scroll event object (reused to avoid GC pressure during scroll)\n #pooledScrollEvent: ScrollEvent = {\n scrollTop: 0,\n scrollLeft: 0,\n scrollHeight: 0,\n scrollWidth: 0,\n clientHeight: 0,\n clientWidth: 0,\n };\n\n // ---------------- Plugin System ----------------\n #pluginManager!: PluginManager;\n #lastPluginsArray?: BaseGridPlugin[]; // Track last attached plugins to avoid unnecessary re-initialization\n\n // ---------------- Event Listeners ----------------\n #eventListenersAdded = false; // Guard against adding duplicate component-level listeners\n #scrollAbortController?: AbortController; // Separate controller for DOM scroll listeners (recreated on DOM changes)\n\n // ---------------- Column State ----------------\n #initialColumnState?: GridColumnState;\n\n // ---------------- Config Manager ----------------\n #configManager!: ConfigManager<T>;\n\n // ---------------- Shell State ----------------\n #shellState: ShellState = createShellState();\n #shellController!: ShellController;\n #resizeCleanup?: () => void;\n\n // ---------------- Row ID Map ----------------\n // O(1) lookup for rows by ID. Rebuilt when rows change.\n #rowIdMap = new Map<string, { row: T; index: number }>();\n // #endregion\n\n // #region Derived State\n // _rows: result of applying plugin processRows hooks\n _rows: T[] = [];\n\n // _baseColumns: columns before plugin transformation (analogous to #rows for row processing)\n // This is the source of truth for processColumns - plugins transform these\n #baseColumns: ColumnInternal<T>[] = [];\n\n // _columns is a getter/setter that operates on effectiveConfig.columns\n // This ensures effectiveConfig.columns is the single source of truth for columns\n // _columns always contains ALL columns (including hidden)\n get _columns(): ColumnInternal<T>[] {\n return (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n }\n set _columns(value: ColumnInternal<T>[]) {\n this.#effectiveConfig.columns = value as ColumnConfig<T>[];\n }\n\n // visibleColumns returns only visible columns for rendering\n // This is what header/row rendering should use\n get _visibleColumns(): ColumnInternal<T>[] {\n return this._columns.filter((c) => !c.hidden);\n }\n // #endregion\n\n // #region Runtime State (Plugin-accessible)\n // DOM references\n _headerRowEl!: HTMLElement;\n _bodyEl!: HTMLElement;\n _rowPool: HTMLElement[] = [];\n _resizeController!: ResizeController;\n\n // Virtualization & scroll state\n _virtualization: VirtualState = {\n enabled: true,\n rowHeight: 28,\n bypassThreshold: 24,\n start: 0,\n end: 0,\n container: null,\n viewportEl: null,\n totalHeightEl: null,\n };\n\n // Focus & navigation\n _focusRow = 0;\n _focusCol = 0;\n /** Flag to restore focus styling after next render. @internal */\n _restoreFocusAfterRender = false;\n\n // Sort state\n _sortState: { field: string; direction: 1 | -1 } | null = null;\n\n // Layout\n _gridTemplate = '';\n // #endregion\n\n // #region Implementation Details (Internal only)\n __rowRenderEpoch = 0;\n __didInitialAutoSize = false;\n\n /** Light DOM columns cache - delegates to ConfigManager */\n get __lightDomColumnsCache(): ColumnInternal[] | undefined {\n return this.#configManager?.lightDomColumnsCache as ColumnInternal[] | undefined;\n }\n set __lightDomColumnsCache(value: ColumnInternal[] | undefined) {\n if (this.#configManager) {\n this.#configManager.lightDomColumnsCache = value as ColumnInternal<T>[] | undefined;\n }\n }\n\n /** Original column nodes - delegates to ConfigManager */\n get __originalColumnNodes(): HTMLElement[] | undefined {\n return this.#configManager?.originalColumnNodes;\n }\n set __originalColumnNodes(value: HTMLElement[] | undefined) {\n if (this.#configManager) {\n this.#configManager.originalColumnNodes = value;\n }\n }\n\n __originalOrder: T[] = [];\n\n /**\n * Framework adapter instance set by framework directives (Angular Grid, React DataGrid).\n * Used to handle framework-specific component rendering.\n * @internal\n */\n __frameworkAdapter?: FrameworkAdapter;\n\n // Cached DOM refs for hot path (refreshVirtualWindow) - avoid querySelector per scroll\n __rowsBodyEl: HTMLElement | null = null;\n // #endregion\n\n // #region Public API Props (getters/setters)\n // Getters return the EFFECTIVE value (after merging), not the raw input.\n // This is what consumers and plugins need - the current resolved state.\n // Setters update input properties which trigger re-merge into effectiveConfig.\n\n /**\n * Get or set the row data displayed in the grid.\n *\n * The getter returns processed rows (after filtering, sorting, grouping by plugins).\n * The setter accepts new source data and triggers a re-render.\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Set initial data\n * grid.rows = employees;\n *\n * // Update with new data (triggers re-render)\n * grid.rows = [...employees, newEmployee];\n *\n * // Read current (processed) rows\n * console.log(`Displaying ${grid.rows.length} rows`);\n * ```\n */\n get rows(): T[] {\n return this._rows;\n }\n set rows(value: T[]) {\n const oldValue = this.#rows;\n this.#rows = value;\n if (oldValue !== value) {\n this.#queueUpdate('rows');\n }\n }\n\n /**\n * Get the original unfiltered/unprocessed source rows.\n *\n * Use this when you need access to all source data regardless of active\n * filters, sorting, or grouping applied by plugins. The `rows` property\n * returns processed data, while `sourceRows` returns the original input.\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Get total count including filtered-out rows\n * console.log(`${grid.rows.length} of ${grid.sourceRows.length} rows visible`);\n *\n * // Export all data, not just visible\n * exportToCSV(grid.sourceRows);\n * ```\n */\n get sourceRows(): T[] {\n return this.#rows;\n }\n\n /**\n * Get or set the column configurations.\n *\n * The getter returns processed columns (after plugin transformations).\n * The setter accepts an array of column configs or a column config map.\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Set columns as array\n * grid.columns = [\n * { field: 'name', header: 'Name', width: 200 },\n * { field: 'email', header: 'Email' },\n * { field: 'role', header: 'Role', width: 120 }\n * ];\n *\n * // Set columns as map (keyed by field)\n * grid.columns = {\n * name: { header: 'Name', width: 200 },\n * email: { header: 'Email' },\n * role: { header: 'Role', width: 120 }\n * };\n *\n * // Read current columns\n * grid.columns.forEach(col => {\n * console.log(`${col.field}: ${col.width ?? 'auto'}`);\n * });\n * ```\n */\n get columns(): ColumnConfig<T>[] {\n return [...this._columns] as ColumnConfig<T>[];\n }\n set columns(value: ColumnConfig<T>[] | ColumnConfigMap<T> | undefined) {\n const oldValue = this.#configManager?.getColumns();\n this.#configManager?.setColumns(value);\n if (oldValue !== value) {\n this.#queueUpdate('columns');\n }\n }\n\n /**\n * Get or set the full grid configuration object.\n *\n * The getter returns the effective (merged) configuration.\n * The setter accepts a new configuration and triggers a full re-render.\n *\n * @group Configuration\n * @example\n * ```typescript\n * import { SelectionPlugin, SortingPlugin } from '@toolbox-web/grid/all';\n *\n * // Set full configuration\n * grid.gridConfig = {\n * columns: [\n * { field: 'name', header: 'Name' },\n * { field: 'status', header: 'Status' }\n * ],\n * fitMode: 'stretch',\n * plugins: [\n * new SelectionPlugin({ mode: 'row' }),\n * new SortingPlugin()\n * ]\n * };\n *\n * // Read current configuration\n * console.log('Fit mode:', grid.gridConfig.fitMode);\n * console.log('Columns:', grid.gridConfig.columns?.length);\n * ```\n */\n get gridConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n set gridConfig(value: GridConfig<T> | undefined) {\n const oldValue = this.#configManager?.getGridConfig();\n this.#configManager?.setGridConfig(value);\n if (oldValue !== value) {\n // Clear light DOM column cache so columns are re-parsed from light DOM\n // This is needed for frameworks like Angular that project content asynchronously\n this.#configManager.clearLightDomCache();\n this.#queueUpdate('gridConfig');\n }\n }\n\n /**\n * Get or set the column sizing mode.\n *\n * - `'stretch'` (default): Columns stretch to fill available width\n * - `'fixed'`: Columns use explicit widths; horizontal scroll if needed\n * - `'auto'`: Columns auto-size to content on initial render\n *\n * @group Configuration\n * @example\n * ```typescript\n * // Use fixed widths with horizontal scroll\n * grid.fitMode = 'fixed';\n *\n * // Stretch columns to fill container\n * grid.fitMode = 'stretch';\n *\n * // Auto-size columns based on content\n * grid.fitMode = 'auto';\n * ```\n */\n get fitMode(): FitMode {\n return this.#effectiveConfig.fitMode ?? 'stretch';\n }\n set fitMode(value: FitMode | undefined) {\n const oldValue = this.#configManager?.getFitMode();\n this.#configManager?.setFitMode(value);\n if (oldValue !== value) {\n this.#queueUpdate('fitMode');\n }\n }\n\n /**\n * Effective config accessor for internal modules and plugins.\n * Returns the merged config (single source of truth) before plugin processing.\n * Use this when you need the raw merged config (e.g., for column definitions including hidden).\n * @group State Access\n * @internal Plugin API\n */\n get effectiveConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Plugins and internal code can use this for automatic listener cleanup.\n * @group State Access\n * @internal Plugin API\n * @example\n * element.addEventListener('click', handler, { signal: this.grid.disconnectSignal });\n */\n get disconnectSignal(): AbortSignal {\n // Ensure AbortController exists (created in connectedCallback before plugins attach)\n if (!this.#eventAbortController) {\n this.#eventAbortController = new AbortController();\n }\n return this.#eventAbortController.signal;\n }\n // #endregion\n\n /**\n * @internal Do not call directly. Use `createGrid()` or `document.createElement('tbw-grid')`.\n */\n constructor() {\n super();\n // No Shadow DOM - render directly into the element\n void this.#injectStyles(); // Fire and forget - styles load asynchronously\n this.#readyPromise = new Promise((res) => (this.#readyResolve = res));\n\n // Initialize render scheduler with callbacks\n this.#scheduler = new RenderScheduler({\n mergeConfig: () => {\n // Re-parse light DOM columns to pick up framework adapter renderers\n // This is essential for React/Angular where renderers register asynchronously\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n this.#configManager.merge();\n this.#updatePluginConfigs(); // Sync plugin configs (including auto-detection) before processing\n // Validate that plugin-specific column properties have their required plugins loaded\n // This runs after plugins are loaded and config is merged\n validatePluginProperties(this.#effectiveConfig, this.#pluginManager?.getPlugins() ?? []);\n // Validate plugin configRules (errors/warnings for invalid config combinations)\n validatePluginConfigRules(this.#pluginManager?.getPlugins() ?? []);\n // Validate plugin incompatibilities (warnings for conflicting plugin combinations)\n validatePluginIncompatibilities(this.#pluginManager?.getPlugins() ?? []);\n // Store base columns before plugin transformation\n this.#baseColumns = [...this._columns];\n },\n processColumns: () => this.#processColumns(),\n processRows: () => this.#rebuildRowModel(),\n renderHeader: () => renderHeader(this),\n updateTemplate: () => updateTemplate(this),\n renderVirtualWindow: () => this.refreshVirtualWindow(true, true),\n afterRender: () => {\n this.#pluginManager?.afterRender();\n // Auto-size columns on first render if fitMode is 'fixed'\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed' && !this.__didInitialAutoSize) {\n this.__didInitialAutoSize = true;\n autoSizeColumns(this);\n }\n // Restore focus styling if requested by a plugin\n if (this._restoreFocusAfterRender) {\n this._restoreFocusAfterRender = false;\n ensureCellVisible(this);\n }\n // Set up row height observer after first render (rows are now in DOM)\n if (this._virtualization.enabled && !this.#rowHeightObserverSetup) {\n this.#setupRowHeightObserver();\n }\n },\n isConnected: () => this.isConnected && this.#connected,\n });\n // Connect ready promise to scheduler\n this.#scheduler.setInitialReadyResolver(() => this.#readyResolve?.());\n\n // Initialize shell controller with callbacks\n this.#shellController = createShellController(this.#shellState, {\n getShadow: () => this.#renderRoot,\n getShellConfig: () => this.#effectiveConfig?.shell,\n getAccordionIcons: () => ({\n expand: this.#effectiveConfig?.icons?.expand ?? DEFAULT_GRID_ICONS.expand,\n collapse: this.#effectiveConfig?.icons?.collapse ?? DEFAULT_GRID_ICONS.collapse,\n }),\n emit: (eventName, detail) => this.#emit(eventName, detail),\n refreshShellHeader: () => this.refreshShellHeader(),\n });\n\n // Initialize config manager with callbacks\n this.#configManager = new ConfigManager<T>({\n getRows: () => this.#rows,\n getSortState: () => this._sortState,\n setSortState: (state) => {\n this._sortState = state;\n },\n onConfigChange: () => {\n this.#scheduler.requestPhase(RenderPhase.FULL, 'configChange');\n },\n emit: (eventName, detail) => this.#emit(eventName, detail),\n clearRowPool: () => {\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n },\n setup: () => this.#setup(),\n renderHeader: () => renderHeader(this),\n updateTemplate: () => updateTemplate(this),\n refreshVirtualWindow: () => this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'configManager'),\n getVirtualization: () => this._virtualization,\n setRowHeight: (height) => {\n this._virtualization.rowHeight = height;\n },\n applyAnimationConfig: (config) => this.#applyAnimationConfig(config),\n getShellLightDomTitle: () => this.#shellState.lightDomTitle,\n getShellToolPanels: () => this.#shellState.toolPanels,\n getShellHeaderContents: () => this.#shellState.headerContents,\n getShellToolbarContents: () => this.#shellState.toolbarContents,\n getShellLightDomHeaderContent: () => this.#shellState.lightDomHeaderContent,\n getShellHasToolButtonsContainer: () => this.#shellState.hasToolButtonsContainer,\n });\n }\n\n /** ID for the consolidated grid stylesheet in document.head */\n static readonly #STYLE_ELEMENT_ID = 'tbw-grid-styles';\n\n /** Track injected base styles CSS text */\n static #baseStyles = '';\n\n /** Track injected plugin styles by plugin name (accumulates across all grid instances) */\n static #pluginStylesMap = new Map<string, string>();\n\n /**\n * Get or create the consolidated style element in document.head.\n * All grid and plugin styles are combined into this single element.\n */\n static #getStyleElement(): HTMLStyleElement {\n let styleEl = document.getElementById(this.#STYLE_ELEMENT_ID) as HTMLStyleElement | null;\n if (!styleEl) {\n styleEl = document.createElement('style');\n styleEl.id = this.#STYLE_ELEMENT_ID;\n styleEl.setAttribute('data-tbw-grid', 'true');\n document.head.appendChild(styleEl);\n }\n return styleEl;\n }\n\n /**\n * Update the consolidated stylesheet with current base + plugin styles.\n */\n static #updateStyleElement(): void {\n const styleEl = this.#getStyleElement();\n // Combine base styles and all accumulated plugin styles\n const pluginStyles = Array.from(this.#pluginStylesMap.values()).join('\\n');\n styleEl.textContent = `${this.#baseStyles}\\n\\n/* Plugin Styles */\\n${pluginStyles}`;\n }\n\n /**\n * Inject grid styles into the document.\n * All styles go into a single <style id=\"tbw-grid-styles\"> element in document.head.\n * Uses a singleton pattern to avoid duplicate injection across multiple grid instances.\n */\n async #injectStyles(): Promise<void> {\n // If base styles already injected, nothing to do\n if (DataGridElement.#baseStyles) {\n return;\n }\n\n // If styles is a string (from ?inline import in Vite builds), use it directly\n if (typeof styles === 'string' && styles.length > 0) {\n DataGridElement.#baseStyles = styles;\n DataGridElement.#updateStyleElement();\n return;\n }\n\n // Fallback: styles is undefined (e.g., when imported in Angular from source without Vite processing)\n // Angular includes grid.css in global styles - extract it from document.styleSheets\n // Wait a bit for Angular to finish loading styles\n await new Promise((resolve) => setTimeout(resolve, 50));\n\n try {\n let gridCssText = '';\n\n // Try to find the stylesheet containing grid CSS\n // Angular bundles all CSS files from angular.json styles array into one stylesheet\n // We need to find the stylesheet with grid CSS and extract ALL of it (including plugin CSS)\n for (const stylesheet of Array.from(document.styleSheets)) {\n try {\n // For inline/bundled stylesheets, check if it contains grid CSS\n const rules = Array.from(stylesheet.cssRules || []);\n const cssText = rules.map((rule) => rule.cssText).join('\\n');\n\n // Check if this stylesheet contains grid CSS by looking for distinctive selectors\n // Without Shadow DOM, we look for tbw-grid nesting selectors\n if (cssText.includes('.tbw-grid-root') && cssText.includes('tbw-grid')) {\n // Found the bundled stylesheet with grid CSS - use ALL of it\n // This includes core grid.css + all plugin CSS files\n gridCssText = cssText;\n break;\n }\n } catch (e) {\n // CORS or access restriction - skip\n continue;\n }\n }\n\n if (gridCssText) {\n DataGridElement.#baseStyles = gridCssText;\n DataGridElement.#updateStyleElement();\n } else if (typeof process === 'undefined' || process.env?.['NODE_ENV'] !== 'test') {\n // Only warn in non-test environments - test environments (happy-dom, jsdom) don't load stylesheets\n console.warn(\n '[tbw-grid] Could not find grid.css in document.styleSheets. Grid styling will not work.',\n 'Available stylesheets:',\n Array.from(document.styleSheets).map((s) => s.href || '(inline)'),\n );\n }\n } catch (err) {\n console.warn('[tbw-grid] Failed to extract grid.css from document stylesheets:', err);\n }\n }\n\n // ---------------- Plugin System ----------------\n\n /**\n * Get a plugin instance by its class.\n * Used by plugins for inter-plugin communication.\n * @group Plugin Communication\n * @internal Plugin API\n */\n getPlugin<P>(PluginClass: new (...args: any[]) => P): P | undefined {\n return this.#pluginManager?.getPlugin(PluginClass as new (...args: any[]) => BaseGridPlugin) as P | undefined;\n }\n\n /**\n * Get a plugin instance by its name.\n * Used for loose coupling between plugins (avoids static imports).\n * @group Plugin Communication\n * @internal Plugin API\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.#pluginManager?.getPluginByName(name);\n }\n\n /**\n * Request a full re-render of the grid.\n * Called by plugins when they need the grid to update.\n * Note: This does NOT reset plugin state - just re-processes rows/columns and renders.\n * @group Rendering\n * @internal Plugin API\n */\n requestRender(): void {\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'plugin:requestRender');\n }\n\n /**\n * Request a full re-render and restore focus styling afterward.\n * Use this when a plugin action (like expand/collapse) triggers a render\n * but needs to maintain keyboard navigation focus.\n * @group Rendering\n * @internal Plugin API\n */\n requestRenderWithFocus(): void {\n this._restoreFocusAfterRender = true;\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'plugin:requestRenderWithFocus');\n }\n\n /**\n * Update the grid's column template CSS.\n * Called by resize controller during column resize operations.\n * @group Rendering\n * @internal Plugin API\n */\n updateTemplate(): void {\n updateTemplate(this);\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Called by plugins when they only need to update CSS classes/styles.\n * This runs all plugin afterRender hooks without rebuilding row/column DOM.\n * @group Rendering\n * @internal Plugin API\n */\n requestAfterRender(): void {\n this.#scheduler.requestPhase(RenderPhase.STYLE, 'plugin:requestAfterRender');\n }\n\n /**\n * Initialize plugin system with instances from config.\n * Plugins are class instances passed in gridConfig.plugins[].\n */\n #initializePlugins(): void {\n // Create plugin manager for this grid\n this.#pluginManager = new PluginManager(this);\n\n // Get plugin instances from config - ensure it's an array\n const pluginsConfig = this.#effectiveConfig?.plugins;\n const plugins = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Attach all plugins\n this.#pluginManager.attachAll(plugins);\n }\n\n /**\n * Inject all plugin styles into the consolidated style element.\n * Plugin styles are appended after base grid styles in the same <style> element.\n * Uses a Map to accumulate styles from all grid instances on the page.\n */\n #injectAllPluginStyles(): void {\n const pluginStyles = this.#pluginManager?.getPluginStyles() ?? [];\n let hasNewStyles = false;\n\n for (const { name, styles } of pluginStyles) {\n if (!DataGridElement.#pluginStylesMap.has(name)) {\n DataGridElement.#pluginStylesMap.set(name, styles);\n hasNewStyles = true;\n }\n }\n\n if (hasNewStyles) {\n DataGridElement.#updateStyleElement();\n }\n }\n\n /**\n * Update plugins when grid config changes.\n * With class-based plugins, we need to detach old and attach new.\n * Skips re-initialization if the plugins array hasn't changed.\n */\n #updatePluginConfigs(): void {\n // Get the new plugins array from config\n const pluginsConfig = this.#effectiveConfig?.plugins;\n const newPlugins = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Check if plugins have actually changed (same array reference or same contents)\n // This avoids unnecessary detach/attach cycles on every render\n if (this.#lastPluginsArray === newPlugins) {\n return; // Same array reference - no change\n }\n\n // Check if the arrays have the same plugin instances\n if (\n this.#lastPluginsArray &&\n this.#lastPluginsArray.length === newPlugins.length &&\n this.#lastPluginsArray.every((p, i) => p === newPlugins[i])\n ) {\n // Same plugins in same order - just update the reference tracking\n this.#lastPluginsArray = newPlugins;\n return;\n }\n\n // Plugins have changed - detach old and attach new\n if (this.#pluginManager) {\n this.#pluginManager.detachAll();\n }\n\n // Clear plugin-contributed panels BEFORE re-initializing plugins\n // This is critical: when plugins are re-initialized, they create NEW instances\n // with NEW render functions. The old panel definitions have stale closures.\n // We preserve light DOM panels (tracked in lightDomToolPanelIds) and\n // API-registered panels (tracked in apiToolPanelIds).\n for (const panelId of this.#shellState.toolPanels.keys()) {\n const isLightDom = this.#shellState.lightDomToolPanelIds.has(panelId);\n const isApiRegistered = this.#shellState.apiToolPanelIds.has(panelId);\n if (!isLightDom && !isApiRegistered) {\n // Clean up any active panel cleanup function\n const cleanup = this.#shellState.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n this.#shellState.panelCleanups.delete(panelId);\n }\n this.#shellState.toolPanels.delete(panelId);\n }\n }\n\n // Similarly clear plugin-contributed header contents\n // Header contents don't have a light DOM tracking set, so clear all and re-collect\n for (const contentId of this.#shellState.headerContents.keys()) {\n const cleanup = this.#shellState.headerContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n this.#shellState.headerContentCleanups.delete(contentId);\n }\n this.#shellState.headerContents.delete(contentId);\n }\n\n this.#initializePlugins();\n this.#injectAllPluginStyles();\n\n // Track the new plugins array\n this.#lastPluginsArray = newPlugins;\n\n // Re-collect plugin shell contributions (tool panels, header content)\n // Now the new plugin instances will add their fresh panel definitions\n this.#collectPluginShellContributions();\n\n // Update cached scroll plugin flag\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n }\n\n /**\n * Clean up plugin states when grid disconnects.\n */\n #destroyPlugins(): void {\n this.#pluginManager?.detachAll();\n }\n\n /**\n * Collect tool panels and header content from all plugins.\n * Called after plugins are attached but before render.\n */\n #collectPluginShellContributions(): void {\n if (!this.#pluginManager) return;\n\n // Collect tool panels from plugins\n const pluginPanels = this.#pluginManager.getToolPanels();\n for (const { panel } of pluginPanels) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.toolPanels.has(panel.id)) {\n this.#shellState.toolPanels.set(panel.id, panel);\n }\n }\n\n // Collect header contents from plugins\n const pluginContents = this.#pluginManager.getHeaderContents();\n for (const { content } of pluginContents) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.headerContents.has(content.id)) {\n this.#shellState.headerContents.set(content.id, content);\n }\n }\n }\n\n /**\n * Gets a renderer factory for tool panels from registered framework adapters.\n * Returns a factory function that tries each adapter in order until one handles the element.\n */\n #getToolPanelRendererFactory(): ToolPanelRendererFactory | undefined {\n const adapters = DataGridElement.getAdapters();\n if (adapters.length === 0 && !this.__frameworkAdapter) return undefined;\n\n // Also check for instance-level adapter (e.g., __frameworkAdapter from Angular Grid directive)\n const instanceAdapter = this.__frameworkAdapter;\n\n return (element: HTMLElement) => {\n // Try instance adapter first (from Grid directive)\n if (instanceAdapter?.createToolPanelRenderer) {\n const renderer = instanceAdapter.createToolPanelRenderer(element);\n if (renderer) return renderer;\n }\n\n // Try global adapters\n for (const adapter of adapters) {\n if (adapter.createToolPanelRenderer) {\n const renderer = adapter.createToolPanelRenderer(element);\n if (renderer) return renderer;\n }\n }\n\n return undefined;\n };\n }\n\n // ---------------- Lifecycle ----------------\n /** @internal Web component lifecycle - not part of public API */\n connectedCallback(): void {\n if (!this.hasAttribute('tabindex')) this.tabIndex = 0;\n if (!this.hasAttribute('version')) this.setAttribute('version', DataGridElement.version);\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Create AbortController for all event listeners (grid internal + plugins)\n // This must happen BEFORE plugins attach so they can use disconnectSignal\n // Abort any previous controller first (in case of re-connect)\n if (this.#eventAbortController) {\n this.#eventAbortController.abort();\n this.#eventListenersAdded = false; // Reset so listeners can be re-added\n }\n this.#eventAbortController = new AbortController();\n\n // Cancel any pending idle work from previous connection\n if (this.#idleCallbackHandle) {\n cancelIdle(this.#idleCallbackHandle);\n this.#idleCallbackHandle = undefined;\n }\n\n // === CRITICAL PATH (synchronous) - needed for first paint ===\n\n // Parse light DOM shell elements BEFORE merging config\n parseLightDomShell(this, this.#shellState);\n // Parse light DOM tool buttons container\n parseLightDomToolButtons(this, this.#shellState);\n // Parse light DOM tool panels (framework adapters may not be ready yet, but vanilla JS works)\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n // Parse light DOM columns (must be before merge to pick up templates)\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n\n // Merge all config sources into effectiveConfig (including columns and shell)\n this.#configManager.merge();\n\n // Initialize plugin system (now plugins can access disconnectSignal)\n this.#initializePlugins();\n\n // Track the initial plugins array to avoid unnecessary re-initialization\n const pluginsConfig = this.#effectiveConfig?.plugins;\n this.#lastPluginsArray = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Collect tool panels and header content from plugins (must be before render)\n this.#collectPluginShellContributions();\n\n if (!this.#initialized) {\n this.#render();\n this.#injectAllPluginStyles(); // Inject plugin styles after render\n this.#initialized = true;\n }\n this.#afterConnect();\n\n // === DEFERRED WORK (idle) - not needed for first paint ===\n this.#idleCallbackHandle = scheduleIdle(\n () => {\n // Set up Light DOM observation via ConfigManager\n // This handles frameworks like Angular that project content asynchronously\n this.#setupLightDomHandlers();\n },\n { timeout: 100 },\n );\n }\n\n /** @internal Web component lifecycle - not part of public API */\n disconnectedCallback(): void {\n // Cancel any pending idle work\n if (this.#idleCallbackHandle) {\n cancelIdle(this.#idleCallbackHandle);\n this.#idleCallbackHandle = undefined;\n }\n\n // Clean up plugin states\n this.#destroyPlugins();\n\n // Clean up shell state\n cleanupShellState(this.#shellState);\n this.#shellController.setInitialized(false);\n\n // Clean up tool panel resize handler\n this.#resizeCleanup?.();\n this.#resizeCleanup = undefined;\n\n // Cancel any ongoing touch momentum animation\n cancelMomentum(this.#touchState);\n\n // Abort all event listeners (internal + document-level)\n // This cleans up all listeners added with { signal } option\n if (this.#eventAbortController) {\n this.#eventAbortController.abort();\n this.#eventAbortController = undefined;\n }\n // Also abort scroll-specific listeners (separate controller)\n this.#scrollAbortController?.abort();\n this.#scrollAbortController = undefined;\n this.#eventListenersAdded = false; // Reset so listeners can be re-added on reconnect\n\n if (this._resizeController) {\n this._resizeController.dispose();\n }\n if (this.#resizeObserver) {\n this.#resizeObserver.disconnect();\n this.#resizeObserver = undefined;\n }\n if (this.#rowHeightObserver) {\n this.#rowHeightObserver.disconnect();\n this.#rowHeightObserver = undefined;\n this.#rowHeightObserverSetup = false;\n }\n\n // Clear caches to prevent memory leaks\n invalidateCellCache(this);\n this.#customStyleSheets.clear();\n\n // Clear plugin tracking to allow fresh initialization on reconnect\n this.#lastPluginsArray = undefined;\n\n // Clear row pool - detach from DOM and release references\n for (const rowEl of this._rowPool) {\n rowEl.remove();\n }\n this._rowPool.length = 0;\n\n // Clear cached DOM refs to prevent stale references\n this.__rowsBodyEl = null;\n\n this.#connected = false;\n }\n\n /**\n * Handle HTML attribute changes.\n * Only processes attribute values when SET (non-null).\n * Removing an attribute does NOT clear JS-set properties.\n * @internal Web component lifecycle - not part of public API\n */\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue || !newValue || newValue === 'null' || newValue === 'undefined') return;\n\n // JSON attributes need parsing\n if (name === 'rows' || name === 'columns' || name === 'grid-config') {\n try {\n const parsed = JSON.parse(newValue);\n if (name === 'rows') this.rows = parsed;\n else if (name === 'columns') this.columns = parsed;\n else if (name === 'grid-config') this.gridConfig = parsed;\n } catch {\n console.warn(`[tbw-grid] Invalid JSON for '${name}' attribute:`, newValue);\n }\n } else if (name === 'fit-mode') {\n this.fitMode = newValue as FitMode;\n }\n }\n\n #afterConnect(): void {\n // Shell changes the DOM structure - need to find elements appropriately\n const gridContent = this.#renderRoot.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#renderRoot.querySelector('.tbw-grid-root');\n\n this._headerRowEl = gridRoot?.querySelector('.header-row') as HTMLElement;\n // Faux scrollbar pattern:\n // - .faux-vscroll-spacer sets virtual height\n // - .rows-viewport provides visible height for virtualization calculations\n this._virtualization.totalHeightEl = gridRoot?.querySelector('.faux-vscroll-spacer') as HTMLElement;\n this._virtualization.viewportEl = gridRoot?.querySelector('.rows-viewport') as HTMLElement;\n this._bodyEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Cache DOM refs for hot path (refreshVirtualWindow) - avoid querySelector per scroll\n this.__rowsBodyEl = gridRoot?.querySelector('.rows-body') as HTMLElement;\n\n // Initialize shell header content and custom buttons if shell is active\n if (this.#shellController.isInitialized) {\n // Render plugin header content\n renderHeaderContent(this.#renderRoot, this.#shellState);\n // Render custom toolbar contents (render modes) - all contents unified in effectiveConfig\n renderCustomToolbarContents(this.#renderRoot, this.#effectiveConfig?.shell, this.#shellState);\n // Open default section if configured\n const defaultOpen = this.#effectiveConfig?.shell?.toolPanel?.defaultOpen;\n if (defaultOpen && this.#shellState.toolPanels.has(defaultOpen)) {\n this.openToolPanel();\n this.#shellState.expandedSections.add(defaultOpen);\n }\n }\n\n // Mark for tests that afterConnect ran\n this.setAttribute('data-upgraded', '');\n this.#connected = true;\n\n // Create resize controller BEFORE setup - renderHeader() needs it for resize handle mousedown events\n this._resizeController = createResizeController(this as unknown as InternalGrid<T>);\n\n // Run setup\n this.#setup();\n\n // Set up DOM-element scroll listeners (these need to be re-attached when DOM is recreated)\n this.#setupScrollListeners(gridRoot);\n\n // Only add component-level event listeners once (afterConnect can be called multiple times)\n // When shell state changes or refreshShellHeader is called, we re-run afterConnect\n // but component-level listeners should not be duplicated (they don't depend on DOM elements)\n // Scroll listeners are already set up above and handle DOM recreation via their own AbortController\n if (this.#eventListenersAdded) {\n return;\n }\n this.#eventListenersAdded = true;\n\n // Get the signal for event listener cleanup (AbortController created in connectedCallback)\n const signal = this.disconnectSignal;\n\n // Set up all root-level and document-level event listeners\n // Consolidates keydown, mousedown, mousemove, mouseup in one place (event-delegation.ts)\n setupRootEventDelegation(this as unknown as InternalGrid<T>, this, this.#renderRoot, signal);\n\n // Note: click/dblclick handlers are set up via setupCellEventDelegation in #setupScrollListeners\n // This consolidates all body-level delegated event handlers in one place (event-delegation.ts)\n\n // Determine row height for virtualization:\n // 1. User-configured rowHeight in gridConfig takes precedence\n // 2. Otherwise, measure actual row height from DOM (respects CSS variable --tbw-row-height)\n const userRowHeight = this.#effectiveConfig.rowHeight;\n if (userRowHeight && userRowHeight > 0) {\n this._virtualization.rowHeight = userRowHeight;\n } else {\n // Initial measurement after first paint (CSS is already loaded via Vite)\n // ResizeObserver in #setupScrollListeners handles subsequent dynamic changes\n requestAnimationFrame(() => this.#measureRowHeight());\n }\n\n // Initialize ARIA selection state\n queueMicrotask(() => this.#updateAriaSelection());\n\n // Request initial render through the scheduler.\n // The scheduler resolves ready() after the render cycle completes.\n // Framework adapters (React/Angular) will request COLUMNS phase via refreshColumns(),\n // which will be batched with this request - highest phase wins.\n this.#scheduler.requestPhase(RenderPhase.FULL, 'afterConnect');\n }\n\n /**\n * Measure actual row height from DOM.\n * Finds the tallest cell to account for custom renderers that may push height.\n */\n #measureRowHeight(): void {\n // Skip if a plugin is managing variable row heights (e.g., ResponsivePlugin with groups)\n // In that case, the plugin handles height via getExtraHeight() and we shouldn't\n // override the base row height, which would cause oscillation loops.\n if (this.#pluginManager.hasExtraHeight()) {\n return;\n }\n\n const firstRow = this._bodyEl?.querySelector('.data-grid-row');\n if (!firstRow) return;\n\n // Find the tallest cell in the row (custom renderers may push some cells taller)\n const cells = firstRow.querySelectorAll('.cell');\n let maxCellHeight = 0;\n cells.forEach((cell) => {\n const h = (cell as HTMLElement).offsetHeight;\n if (h > maxCellHeight) maxCellHeight = h;\n });\n\n const rowRect = (firstRow as HTMLElement).getBoundingClientRect();\n\n // Use the larger of row height or max cell height\n const measuredHeight = Math.max(rowRect.height, maxCellHeight);\n // Use a 1px threshold to avoid oscillation from sub-pixel rounding\n if (measuredHeight > 0 && Math.abs(measuredHeight - this._virtualization.rowHeight) > 1) {\n this._virtualization.rowHeight = measuredHeight;\n // Use scheduler to batch with other pending work\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'measureRowHeight');\n }\n }\n\n /**\n * Set up scroll-related event listeners on DOM elements.\n * These need to be re-attached when the DOM is recreated (e.g., shell toggle).\n * Uses a separate AbortController that is recreated each time.\n */\n #setupScrollListeners(gridRoot: Element | null): void {\n // Abort any existing scroll listeners before adding new ones\n this.#scrollAbortController?.abort();\n this.#scrollAbortController = new AbortController();\n const scrollSignal = this.#scrollAbortController.signal;\n\n // Faux scrollbar pattern: scroll events come from the fake scrollbar element\n // Content area doesn't scroll - rows are positioned via transforms\n const fauxScrollbar = gridRoot?.querySelector('.faux-vscroll') as HTMLElement;\n const rowsEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Store reference for scroll position reading in refreshVirtualWindow\n this._virtualization.container = fauxScrollbar ?? this;\n\n // Cache whether any plugin has scroll handlers (checked once during setup)\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n\n if (fauxScrollbar && rowsEl) {\n fauxScrollbar.addEventListener(\n 'scroll',\n () => {\n // Fast exit if no scroll processing needed\n if (!this._virtualization.enabled && !this.#hasScrollPlugins) return;\n\n const currentScrollTop = fauxScrollbar.scrollTop;\n const rowHeight = this._virtualization.rowHeight;\n\n // Bypass mode: all rows are rendered, just translate by scroll position\n // No need for virtual window calculations\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n rowsEl.style.transform = `translateY(${-currentScrollTop}px)`;\n } else {\n // Virtualized mode: calculate sub-pixel offset for smooth scrolling\n // Even-aligned start preserves zebra stripe parity\n // DOM nth-child(even) will always match data row parity\n const rawStart = Math.floor(currentScrollTop / rowHeight);\n const evenAlignedStart = rawStart - (rawStart % 2);\n const subPixelOffset = -(currentScrollTop - evenAlignedStart * rowHeight);\n rowsEl.style.transform = `translateY(${subPixelOffset}px)`;\n }\n\n // Batch content update with requestAnimationFrame\n // Old content stays visible with smooth offset until new content renders\n this.#pendingScrollTop = currentScrollTop;\n if (!this.#scrollRaf) {\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n if (this.#pendingScrollTop !== null) {\n this.#onScrollBatched(this.#pendingScrollTop);\n this.#pendingScrollTop = null;\n }\n });\n }\n },\n { passive: true, signal: scrollSignal },\n );\n\n // Forward wheel events from content area to faux scrollbar\n // Without this, mouse wheel over content wouldn't scroll\n // Listen on .tbw-grid-content to capture wheel events from entire grid area\n // Note: gridRoot may already BE .tbw-grid-content when shell is active, so search from shadow root\n const gridContentEl = this.#renderRoot.querySelector('.tbw-grid-content') as HTMLElement;\n const scrollArea = this.#renderRoot.querySelector('.tbw-scroll-area') as HTMLElement;\n if (gridContentEl) {\n gridContentEl.addEventListener(\n 'wheel',\n (e: WheelEvent) => {\n // SHIFT+wheel or trackpad deltaX = horizontal scroll\n const isHorizontal = e.shiftKey || Math.abs(e.deltaX) > Math.abs(e.deltaY);\n\n if (isHorizontal && scrollArea) {\n const delta = e.shiftKey ? e.deltaY : e.deltaX;\n const { scrollLeft, scrollWidth, clientWidth } = scrollArea;\n const canScroll = (delta > 0 && scrollLeft < scrollWidth - clientWidth) || (delta < 0 && scrollLeft > 0);\n if (canScroll) {\n e.preventDefault();\n scrollArea.scrollLeft += delta;\n }\n } else if (!isHorizontal) {\n const { scrollTop, scrollHeight, clientHeight } = fauxScrollbar;\n const canScroll =\n (e.deltaY > 0 && scrollTop < scrollHeight - clientHeight) || (e.deltaY < 0 && scrollTop > 0);\n if (canScroll) {\n e.preventDefault();\n fauxScrollbar.scrollTop += e.deltaY;\n }\n }\n // If can't scroll, event bubbles to scroll the page\n },\n { passive: false, signal: scrollSignal },\n );\n\n // Touch scrolling support for mobile devices\n // Supports both vertical (via faux scrollbar) and horizontal (via scroll area) scrolling\n // Includes momentum scrolling for natural \"flick\" behavior\n setupTouchScrollListeners(gridContentEl, this.#touchState, { fauxScrollbar, scrollArea }, scrollSignal);\n }\n }\n\n // Set up delegated event handlers for cell interactions (click, dblclick, keydown)\n // This replaces per-cell event listeners with a single set of delegated handlers\n // Dramatically reduces memory usage: 4 listeners total vs. 30,000+ for large grids\n if (this._bodyEl) {\n setupCellEventDelegation(this as unknown as InternalGrid<T>, this._bodyEl, scrollSignal);\n }\n\n // Disconnect existing resize observer before creating new one\n // This ensures we observe the NEW viewport element after DOM recreation\n this.#resizeObserver?.disconnect();\n\n // Resize observer to refresh virtualization when viewport size changes\n // (e.g., when footer is added, window resizes, or shell panel toggles)\n if (this._virtualization.viewportEl) {\n this.#resizeObserver = new ResizeObserver(() => {\n // Use scheduler for viewport resize - batches with other pending work\n // The scheduler already batches multiple requests per RAF\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'resize-observer');\n });\n this.#resizeObserver.observe(this._virtualization.viewportEl);\n }\n\n // Note: We no longer need to schedule init-virtualization here since\n // the initial FULL render from #setup already includes virtualization.\n // Requesting it again here caused duplicate renders on initialization.\n\n // Track focus state via data attribute (shadow DOM doesn't reliably support :focus-within)\n // Listen on shadow root to catch focus events from shadow DOM elements\n // Cast to EventTarget since TypeScript's lib.dom doesn't include focus events on ShadowRoot\n (this.#renderRoot as EventTarget).addEventListener(\n 'focusin',\n () => {\n this.dataset.hasFocus = '';\n },\n { signal: scrollSignal },\n );\n (this.#renderRoot as EventTarget).addEventListener(\n 'focusout',\n (e) => {\n // Only remove if focus is leaving the grid entirely\n // relatedTarget is null when focus leaves the document, or the new focus target\n const newFocus = (e as FocusEvent).relatedTarget as Node | null;\n if (!newFocus || !this.#renderRoot.contains(newFocus)) {\n delete this.dataset.hasFocus;\n }\n },\n { signal: scrollSignal },\n );\n }\n\n /**\n * Set up ResizeObserver on first row to detect height changes.\n * Called after rows are rendered to observe the actual content.\n * Handles dynamic CSS loading, lazy images, font loading, column virtualization, etc.\n */\n #rowHeightObserverSetup = false; // Only set up once per lifecycle\n #setupRowHeightObserver(): void {\n // Only set up once - row height measurement is one-time during initialization\n if (this.#rowHeightObserverSetup) return;\n\n const firstRow = this._bodyEl?.querySelector('.data-grid-row') as HTMLElement | null;\n if (!firstRow) return;\n\n this.#rowHeightObserverSetup = true;\n this.#rowHeightObserver?.disconnect();\n\n // Observe the row element itself, not individual cells.\n // This catches all height changes including:\n // - Custom renderers that push cell height\n // - Column virtualization adding/removing columns\n // - Dynamic content loading (images, fonts)\n // Note: ResizeObserver fires on initial observation in modern browsers,\n // so no separate measurement call is needed.\n this.#rowHeightObserver = new ResizeObserver(() => {\n this.#measureRowHeight();\n });\n this.#rowHeightObserver.observe(firstRow);\n }\n\n // ---------------- Event Emitters ----------------\n #emit<D>(eventName: string, detail: D): void {\n this.dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true, composed: true }));\n }\n\n /** Update ARIA selection attributes on rendered rows/cells */\n #updateAriaSelection(): void {\n // Mark active row and cell with aria-selected\n const rows = this._bodyEl?.querySelectorAll('.data-grid-row');\n rows?.forEach((row, rowIdx) => {\n const isActiveRow = rowIdx === this._focusRow;\n row.setAttribute('aria-selected', String(isActiveRow));\n row.querySelectorAll('.cell').forEach((cell, colIdx) => {\n (cell as HTMLElement).setAttribute('aria-selected', String(isActiveRow && colIdx === this._focusCol));\n });\n });\n }\n\n // ---------------- Batched Update System ----------------\n // Allows multiple property changes within the same microtask to be coalesced\n // into a single update cycle, dramatically reducing redundant renders.\n\n /**\n * Queue an update for a specific property type.\n * All updates queued within the same microtask are batched together.\n */\n #queueUpdate(type: 'rows' | 'columns' | 'gridConfig' | 'fitMode'): void {\n this.#pendingUpdateFlags[type] = true;\n\n // If already queued, skip scheduling\n if (this.#pendingUpdate) return;\n\n this.#pendingUpdate = true;\n // Use queueMicrotask to batch synchronous property sets\n queueMicrotask(() => this.#flushPendingUpdates());\n }\n\n /**\n * Process all pending updates in optimal order.\n * Priority: gridConfig first (may affect all), then columns, rows, fitMode, editMode\n */\n #flushPendingUpdates(): void {\n if (!this.#pendingUpdate || !this.#connected) {\n this.#pendingUpdate = false;\n return;\n }\n\n const flags = this.#pendingUpdateFlags;\n\n // Reset flags before processing to allow new updates during processing\n this.#pendingUpdate = false;\n this.#pendingUpdateFlags = {\n rows: false,\n columns: false,\n gridConfig: false,\n fitMode: false,\n };\n\n // If gridConfig changed, it supersedes columns/fit changes\n // but we still need to handle rows if set separately\n if (flags.gridConfig) {\n this.#applyGridConfigUpdate();\n // Still process rows if set separately (e.g., grid.gridConfig = ...; grid.rows = ...;)\n if (flags.rows) {\n this.#applyRowsUpdate();\n }\n return;\n }\n\n // Process remaining changes in dependency order\n if (flags.columns) {\n this.#applyColumnsUpdate();\n }\n if (flags.rows) {\n this.#applyRowsUpdate();\n }\n if (flags.fitMode) {\n this.#applyFitModeUpdate();\n }\n }\n\n // Individual update applicators - these do the actual work\n #applyRowsUpdate(): void {\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n // Rebuild row ID map for O(1) lookups\n this.#rebuildRowIdMap();\n // Request a ROWS phase render through the scheduler.\n // This batches with any other pending work (e.g., React adapter's refreshColumns).\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'applyRowsUpdate');\n }\n\n /**\n * Rebuild the row ID map for O(1) lookups.\n * Called when rows array changes.\n */\n #rebuildRowIdMap(): void {\n this.#rowIdMap.clear();\n const getRowId = this.#effectiveConfig.getRowId;\n\n this._rows.forEach((row, index) => {\n const id = this.#tryResolveRowId(row, getRowId);\n if (id !== undefined) {\n this.#rowIdMap.set(id, { row, index });\n }\n // Rows without IDs are skipped - they won't be accessible via getRow/updateRow\n });\n }\n\n /**\n * Try to resolve the ID for a row using configured getRowId or fallback.\n * Returns undefined if no ID can be determined (non-throwing).\n * Used internally by #rebuildRowIdMap.\n */\n #tryResolveRowId(row: T, getRowId?: (row: T) => string): string | undefined {\n if (getRowId) {\n return getRowId(row);\n }\n\n // Fallback: common ID fields\n const r = row as Record<string, unknown>;\n if ('id' in r && r.id != null) return String(r.id);\n if ('_id' in r && r._id != null) return String(r._id);\n\n return undefined;\n }\n\n /**\n * Resolve the ID for a row, throwing if not found.\n * Used by public getRowId() method.\n */\n #resolveRowIdOrThrow(row: T, getRowId?: (row: T) => string): string {\n const id = this.#tryResolveRowId(row, getRowId);\n if (id === undefined) {\n throw new Error(\n '[tbw-grid] Cannot determine row ID. ' +\n 'Configure getRowId in gridConfig or ensure rows have an \"id\" property.',\n );\n }\n return id;\n }\n\n #applyColumnsUpdate(): void {\n invalidateCellCache(this);\n this.#configManager.merge();\n this.#setup();\n }\n\n #applyFitModeUpdate(): void {\n this.#configManager.merge();\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed') {\n this.__didInitialAutoSize = false;\n autoSizeColumns(this);\n } else {\n this._columns.forEach((c: any) => {\n if (!c.__userResized && c.__autoSized) delete c.width;\n });\n updateTemplate(this);\n }\n }\n\n #applyGridConfigUpdate(): void {\n // Parse shell config (title, etc.) - needed for React where gridConfig is set after initial render\n parseLightDomShell(this, this.#shellState);\n // Parse tool buttons container before checking shell state\n parseLightDomToolButtons(this, this.#shellState);\n\n const hadShell = !!this.#renderRoot.querySelector('.has-shell');\n const hadToolPanel = !!this.#renderRoot.querySelector('.tbw-tool-panel');\n\n // Count accordion sections before update (to detect new panels added)\n const accordionSectionsBefore = this.#renderRoot.querySelectorAll('.tbw-accordion-section').length;\n\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n this.#configManager.merge();\n this.#updatePluginConfigs();\n\n // Parse light DOM tool panels AFTER plugins are initialized\n // This ensures plugin panels are collected first, then light DOM panels merge in\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Mark sources as changed since parseLightDomToolPanels may have updated shell state maps\n // This ensures the next merge() will pick up any new tool panels\n this.#configManager.markSourcesChanged();\n\n // Re-merge to pick up any light DOM tool panels\n this.#configManager.merge();\n\n const nowNeedsShell = shouldRenderShellHeader(this.#effectiveConfig?.shell);\n const nowHasToolPanels = (this.#effectiveConfig?.shell?.toolPanels?.length ?? 0) > 0;\n\n // Full re-render needed if:\n // 1. Shell state changed (added or removed)\n // 2. Tool panels were added but sidebar doesn't exist in DOM yet\n // 3. Number of tool panels changed (plugin panels added/removed)\n const toolPanelCountChanged = (this.#effectiveConfig?.shell?.toolPanels?.length ?? 0) !== accordionSectionsBefore;\n const needsFullRerender =\n hadShell !== nowNeedsShell ||\n (!hadShell && nowNeedsShell) ||\n (!hadToolPanel && nowHasToolPanels) ||\n (hadToolPanel && toolPanelCountChanged);\n\n if (needsFullRerender) {\n this.#render();\n this.#injectAllPluginStyles(); // Re-inject after render clears shadow DOM\n this.#afterConnect();\n // Rebuild row ID map in case getRowId changed\n this.#rebuildRowIdMap();\n return;\n }\n\n // Update shell header in place if it exists (e.g., title changed)\n // This avoids a full re-render when only shell config changed\n if (hadShell) {\n this.#updateShellHeaderInPlace();\n }\n\n // Rebuild row ID map in case getRowId changed\n this.#rebuildRowIdMap();\n\n // Request a COLUMNS phase render through the scheduler.\n // This batches with any other pending work (e.g., React adapter's refreshColumns).\n // Previously this called rebuildRowModel, processColumns, renderHeader, updateTemplate,\n // and refreshVirtualWindow directly - causing race conditions with framework adapters.\n this.#scheduler.requestPhase(RenderPhase.COLUMNS, 'applyGridConfigUpdate');\n }\n\n /**\n * Update the shell header DOM in place without a full re-render.\n * Handles title, toolbar buttons, and other shell header changes.\n */\n #updateShellHeaderInPlace(): void {\n const shellHeader = this.#renderRoot.querySelector('.tbw-shell-header');\n if (!shellHeader) return;\n\n const title = this.#effectiveConfig.shell?.header?.title ?? this.#shellState.lightDomTitle;\n\n // Update or create title element\n let titleEl = shellHeader.querySelector('.tbw-shell-title') as HTMLElement | null;\n if (title) {\n if (!titleEl) {\n // Create title element if it doesn't exist\n titleEl = document.createElement('h2');\n titleEl.className = 'tbw-shell-title';\n titleEl.setAttribute('part', 'shell-title');\n // Insert at the beginning of the shell header\n shellHeader.insertBefore(titleEl, shellHeader.firstChild);\n }\n titleEl.textContent = title;\n } else if (titleEl) {\n // Remove title element if no title\n titleEl.remove();\n }\n }\n\n // NOTE: Legacy watch handlers have been replaced by the batched update system.\n // The #queueUpdate() method schedules updates which are processed by #flushPendingUpdates()\n // and individual #apply*Update() methods. This coalesces rapid property changes\n // (e.g., setting rows, columns, gridConfig in quick succession) into a single update cycle.\n\n #processColumns(): void {\n // Let plugins process visible columns (column grouping, etc.)\n // Start from base columns (before any plugin transformation) - like #rebuildRowModel uses #rows\n if (this.#pluginManager) {\n // Use base columns as source of truth, falling back to current _columns if not set\n const sourceColumns = this.#baseColumns.length > 0 ? this.#baseColumns : this._columns;\n const visibleCols = sourceColumns.filter((c) => !c.hidden);\n const hiddenCols = sourceColumns.filter((c) => c.hidden);\n const processedColumns = this.#pluginManager.processColumns([...visibleCols]);\n\n // If plugins modified visible columns, update them\n if (processedColumns !== visibleCols) {\n // Build sets for quick lookup\n const sourceFields = new Set(visibleCols.map((c) => c.field));\n const processedFields = new Set(processedColumns.map((c: any) => c.field));\n\n // Check if this is a complete column replacement (e.g., pivot mode)\n // If no processed columns match original columns, use processed columns directly\n const hasMatchingFields = visibleCols.some((c) => processedFields.has(c.field));\n\n if (!hasMatchingFields && processedColumns.length > 0) {\n // Complete replacement: use processed columns directly (pivot mode)\n // Preserve hidden columns at the end\n this._columns = [...processedColumns, ...hiddenCols] as ColumnInternal<T>[];\n } else {\n // Plugins may have:\n // 1. Modified existing columns\n // 2. Added new columns (e.g., expander column)\n // 3. Reordered columns\n // We trust the plugin's output order and include all columns they returned\n // plus any hidden columns at the end\n this._columns = [...processedColumns, ...hiddenCols] as ColumnInternal<T>[];\n }\n } else {\n // Plugins returned columns unchanged, but we may need to restore from base\n this._columns = [...sourceColumns] as ColumnInternal<T>[];\n }\n }\n }\n\n /** Recompute row model via plugin hooks. */\n #rebuildRowModel(): void {\n // Invalidate cell display value cache - rows are changing\n invalidateCellCache(this);\n\n // Start fresh from original rows (plugins will transform them)\n const originalRows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Let plugins process rows (they may add, remove, or transform rows)\n // Plugins can add markers for specialized rendering via the renderRow hook\n const processedRows = this.#pluginManager?.processRows(originalRows) ?? originalRows;\n\n // Store processed rows for rendering\n // Note: processedRows may contain group markers that plugins handle via renderRow hook\n this._rows = processedRows as T[];\n }\n\n /**\n * Apply animation configuration to CSS custom properties on the host element.\n * This makes the grid's animation settings available to plugins via CSS variables.\n * Called by ConfigManager after merge.\n */\n #applyAnimationConfig(gridConfig: GridConfig<T>): void {\n const config: AnimationConfig = {\n ...DEFAULT_ANIMATION_CONFIG,\n ...gridConfig.animation,\n };\n\n // Resolve animation mode\n const mode = config.mode ?? 'reduced-motion';\n let enabled: 0 | 1 = 1;\n\n if (mode === false || mode === 'off') {\n enabled = 0;\n } else if (mode === true || mode === 'on') {\n enabled = 1;\n }\n // For 'reduced-motion', we leave enabled=1 and let CSS @media query handle it\n\n // Set CSS custom properties\n this.style.setProperty('--tbw-animation-duration', `${config.duration}ms`);\n this.style.setProperty('--tbw-animation-easing', config.easing ?? 'ease-out');\n this.style.setProperty('--tbw-animation-enabled', String(enabled));\n\n // Set data attribute for mode-based CSS selectors\n this.dataset.animationMode = typeof mode === 'boolean' ? (mode ? 'on' : 'off') : mode;\n }\n\n // ---------------- Delegate Wrappers ----------------\n #renderVisibleRows(start: number, end: number, epoch = this.__rowRenderEpoch): void {\n // Use cached hook to avoid creating closures on every render (hot path optimization)\n if (!this.#renderRowHook) {\n this.#renderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number): boolean => {\n return this.#pluginManager?.renderRow(row, rowEl, rowIndex) ?? false;\n };\n }\n renderVisibleRows(this as unknown as InternalGrid<T>, start, end, epoch, this.#renderRowHook);\n }\n\n // Cache for ARIA counts to avoid redundant DOM writes on scroll (hot path)\n #lastAriaRowCount = -1;\n #lastAriaColCount = -1;\n\n /**\n * Updates ARIA row/col counts on the grid container.\n * Also sets role=\"rowgroup\" on .rows container only when there are rows.\n * Uses caching to avoid redundant DOM writes on every scroll frame.\n */\n #updateAriaCounts(rowCount: number, colCount: number): void {\n // Skip if nothing changed (hot path optimization for scroll)\n if (rowCount === this.#lastAriaRowCount && colCount === this.#lastAriaColCount) {\n return;\n }\n const prevRowCount = this.#lastAriaRowCount;\n this.#lastAriaRowCount = rowCount;\n this.#lastAriaColCount = colCount;\n\n // Update ARIA counts on inner grid element\n if (this.__rowsBodyEl) {\n this.__rowsBodyEl.setAttribute('aria-rowcount', String(rowCount));\n this.__rowsBodyEl.setAttribute('aria-colcount', String(colCount));\n }\n\n // Set role=\"rowgroup\" on .rows only when there are rows (ARIA compliance)\n if (rowCount !== prevRowCount && this._bodyEl) {\n if (rowCount > 0) {\n this._bodyEl.setAttribute('role', 'rowgroup');\n } else {\n this._bodyEl.removeAttribute('role');\n }\n }\n }\n\n // ---------------- Core Helpers ----------------\n /**\n * Request a full grid re-setup through the render scheduler.\n * This method queues all the config merging, column/row processing, and rendering\n * to happen in the next animation frame via the scheduler.\n *\n * Previously this method executed rendering synchronously, but that caused race\n * conditions with framework adapters that also schedule their own render work.\n */\n #setup(): void {\n if (!this.isConnected) return;\n if (!this._headerRowEl || !this._bodyEl) {\n return;\n }\n\n // Read light DOM column configuration (synchronous DOM read)\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n\n // Apply initial column state synchronously if present\n // (needs to happen before scheduler to avoid flash of unstyled content)\n if (this.#initialColumnState) {\n const state = this.#initialColumnState;\n this.#initialColumnState = undefined; // Clear to avoid re-applying\n // Temporarily merge config so applyState has columns to work with\n this.#configManager.merge();\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.applyState(state, plugins);\n }\n\n // Ensure legacy inline grid styles are cleared from container\n if (this._bodyEl) {\n this._bodyEl.style.display = '';\n this._bodyEl.style.gridTemplateColumns = '';\n }\n\n // Request full render through scheduler - batches with framework adapter work\n this.#scheduler.requestPhase(RenderPhase.FULL, 'setup');\n }\n\n #onScrollBatched(scrollTop: number): void {\n // Faux scrollbar pattern: content never scrolls, just update transforms\n // Old content stays visible until new transforms are applied\n this.refreshVirtualWindow(false);\n\n // Let plugins reapply visual state to recycled DOM elements\n this.#pluginManager?.onScrollRender();\n\n // Dispatch to plugins (using cached flag and pooled event object to avoid GC)\n if (this.#hasScrollPlugins) {\n const fauxScrollbar = this._virtualization.container;\n // Reuse pooled event object - update values in-place instead of allocating new object\n const scrollEvent = this.#pooledScrollEvent;\n scrollEvent.scrollTop = scrollTop;\n scrollEvent.scrollLeft = fauxScrollbar?.scrollLeft ?? 0;\n scrollEvent.scrollHeight = fauxScrollbar?.scrollHeight ?? 0;\n scrollEvent.scrollWidth = fauxScrollbar?.scrollWidth ?? 0;\n scrollEvent.clientHeight = fauxScrollbar?.clientHeight ?? 0;\n scrollEvent.clientWidth = fauxScrollbar?.clientWidth ?? 0;\n // Note: originalEvent removed to avoid allocation - plugins should not rely on it\n this.#pluginManager?.onScroll(scrollEvent);\n }\n }\n\n /**\n * Find the header row element in the shadow DOM.\n * Used by plugins that need to access header cells for styling or measurement.\n * @group DOM Access\n * @internal Plugin API\n */\n findHeaderRow(): HTMLElement {\n return this.#renderRoot.querySelector('.header-row') as HTMLElement;\n }\n\n /**\n * Find a rendered row element by its data row index.\n * Returns null if the row is not currently rendered (virtualized out of view).\n * Used by plugins that need to access specific row elements for styling or measurement.\n * @group DOM Access\n * @internal Plugin API\n * @param rowIndex - The data row index (not the DOM position)\n */\n findRenderedRowElement(rowIndex: number): HTMLElement | null {\n return (\n (Array.from(this._bodyEl.querySelectorAll('.data-grid-row')) as HTMLElement[]).find((r) => {\n const cell = r.querySelector('.cell[data-row]');\n return cell && Number(cell.getAttribute('data-row')) === rowIndex;\n }) || null\n );\n }\n\n /**\n * Dispatch a cell click event to the plugin system, then emit a public event.\n * Plugins get first chance to handle the event. After plugins process it,\n * a `cell-click` CustomEvent is dispatched for external listeners.\n *\n * @returns `true` if any plugin handled (consumed) the event, or if consumer canceled\n * @fires cell-activate - Unified activation event (cancelable) - fires FIRST\n * @fires cell-click - Emitted after plugins process the click, with full cell context\n */\n _dispatchCellClick(event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement): boolean {\n const row = this._rows[rowIndex];\n const col = this._columns[colIndex];\n if (!row || !col) return false;\n\n const field = col.field;\n const value = (row as Record<string, unknown>)[field];\n\n // Emit cell-activate FIRST (cancelable) - consumers can prevent plugin behavior\n const activateEvent = new CustomEvent('cell-activate', {\n cancelable: true,\n bubbles: true,\n composed: true,\n detail: {\n rowIndex,\n colIndex,\n field,\n value,\n row,\n cellEl,\n trigger: 'pointer' as const,\n originalEvent: event,\n },\n });\n this.dispatchEvent(activateEvent);\n\n // If consumer canceled, don't let plugins handle it\n if (activateEvent.defaultPrevented) {\n return true; // Treated as \"handled\"\n }\n\n const cellClickEvent: CellClickEvent = {\n row,\n rowIndex,\n colIndex,\n field,\n value,\n cellEl,\n originalEvent: event,\n };\n\n // Let plugins handle (editing, selection, etc.)\n const handled = this.#pluginManager?.onCellClick(cellClickEvent) ?? false;\n\n // Emit informational cell-click event for external listeners\n this.#emit('cell-click', cellClickEvent);\n\n return handled;\n }\n\n /**\n * Dispatch a row click event to the plugin system, then emit a public event.\n * Plugins get first chance to handle the event. After plugins process it,\n * a `row-click` CustomEvent is dispatched for external listeners.\n *\n * @returns `true` if any plugin handled (consumed) the event\n * @fires row-click - Emitted after plugins process the click, with full row context\n */\n _dispatchRowClick(event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement): boolean {\n if (!row) return false;\n\n const rowClickEvent: RowClickEvent = {\n rowIndex,\n row,\n rowEl,\n originalEvent: event,\n };\n\n // Let plugins handle first\n const handled = this.#pluginManager?.onRowClick(rowClickEvent) ?? false;\n\n // Emit public event for external listeners (reuse same event object)\n this.#emit('row-click', rowClickEvent);\n\n return handled;\n }\n\n /**\n * Dispatch a header click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchHeaderClick(event: MouseEvent, colIndex: number, headerEl: HTMLElement): boolean {\n const col = this._columns[colIndex];\n if (!col) return false;\n\n const headerClickEvent: HeaderClickEvent = {\n colIndex,\n field: col.field,\n column: col,\n headerEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onHeaderClick(headerClickEvent) ?? false;\n }\n\n /**\n * Dispatch a keyboard event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchKeyDown(event: KeyboardEvent): boolean {\n return this.#pluginManager?.onKeyDown(event) ?? false;\n }\n\n /**\n * Get horizontal scroll boundary offsets from plugins.\n * Used by keyboard navigation to ensure focused cells are fully visible\n * when plugins like pinned columns obscure part of the scroll area.\n */\n _getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n return this.#pluginManager?.getHorizontalScrollOffsets(rowEl, focusedCell) ?? { left: 0, right: 0 };\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n * @group Plugin Communication\n * @internal Plugin API\n *\n * @example\n * // Check if any plugin vetoes moving a column\n * const responses = grid.queryPlugins<boolean>({ type: PLUGIN_QUERIES.CAN_MOVE_COLUMN, context: column });\n * const canMove = !responses.includes(false);\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n return this.#pluginManager?.queryPlugins<T>(query) ?? [];\n }\n\n /**\n * Dispatch cell mouse events for drag operations.\n * Returns true if any plugin started a drag.\n * @group Event Dispatching\n * @internal Plugin API - called by event-delegation.ts\n */\n _dispatchCellMouseDown(event: CellMouseEvent): boolean {\n return this.#pluginManager?.onCellMouseDown(event) ?? false;\n }\n\n /**\n * Dispatch cell mouse move during drag.\n * @group Event Dispatching\n * @internal Plugin API - called by event-delegation.ts\n */\n _dispatchCellMouseMove(event: CellMouseEvent): void {\n this.#pluginManager?.onCellMouseMove(event);\n }\n\n /**\n * Dispatch cell mouse up to end drag.\n * @group Event Dispatching\n * @internal Plugin API - called by event-delegation.ts\n */\n _dispatchCellMouseUp(event: CellMouseEvent): void {\n this.#pluginManager?.onCellMouseUp(event);\n }\n\n /**\n * Call afterCellRender hook on all plugins.\n * This is called by rows.ts for each cell after it's rendered,\n * allowing plugins to modify cells during render rather than\n * requiring expensive post-render DOM queries.\n *\n * @group Plugin Hooks\n * @internal Plugin API - called by rows.ts\n */\n _afterCellRender(context: AfterCellRenderContext<T>): void {\n // Cast needed because PluginManager uses unknown for row type\n this.#pluginManager?.afterCellRender(context as AfterCellRenderContext);\n }\n\n /**\n * Check if any plugin has registered an afterCellRender hook.\n * Used to skip the hook call entirely for performance when no plugins need it.\n *\n * @group Plugin Hooks\n * @internal Plugin API - called by rows.ts\n */\n _hasAfterCellRenderHook(): boolean {\n return this.#pluginManager?.hasAfterCellRenderHook() ?? false;\n }\n\n /**\n * Call afterRowRender hook on all plugins that have registered for it.\n * Called by rows.ts after each row is completely rendered.\n *\n * @param context - Context containing row data, index, and DOM element\n *\n * @group Plugin Hooks\n * @internal Plugin API - called by rows.ts\n */\n _afterRowRender(context: AfterRowRenderContext<T>): void {\n // Cast needed because PluginManager uses unknown for row type\n this.#pluginManager?.afterRowRender(context as AfterRowRenderContext);\n }\n\n /**\n * Check if any plugin has registered an afterRowRender hook.\n * Used to skip the hook call entirely for performance when no plugins need it.\n *\n * @group Plugin Hooks\n * @internal Plugin API - called by rows.ts\n */\n _hasAfterRowRenderHook(): boolean {\n return this.#pluginManager?.hasAfterRowRenderHook() ?? false;\n }\n\n /**\n * Wait for the grid to be ready.\n * Resolves once the component has finished initial setup, including\n * column inference, plugin initialization, and first render.\n *\n * @group Lifecycle\n * @returns Promise that resolves when the grid is ready\n *\n * @example\n * ```typescript\n * const grid = document.querySelector('tbw-grid');\n * await grid.ready();\n * console.log('Grid is ready, rows:', grid.rows.length);\n * ```\n */\n async ready(): Promise<void> {\n return this.#readyPromise;\n }\n\n /**\n * Force a full layout/render cycle.\n * Use this after programmatic changes that require re-measurement,\n * such as container resize or dynamic style changes.\n *\n * @group Lifecycle\n * @returns Promise that resolves when the render cycle completes\n *\n * @example\n * ```typescript\n * // After resizing the container\n * container.style.width = '800px';\n * await grid.forceLayout();\n * console.log('Grid re-rendered');\n * ```\n */\n async forceLayout(): Promise<void> {\n // Request a full render cycle through the scheduler\n this.#scheduler.requestPhase(RenderPhase.FULL, 'forceLayout');\n // Wait for the render cycle to complete\n return this.#scheduler.whenReady();\n }\n\n /**\n * Get a frozen snapshot of the current effective configuration.\n * The returned object is read-only and reflects all merged config sources.\n *\n * @group Configuration\n * @returns Promise resolving to frozen configuration object\n *\n * @example\n * ```typescript\n * const config = await grid.getConfig();\n * console.log('Columns:', config.columns?.length);\n * console.log('Fit mode:', config.fitMode);\n * ```\n */\n async getConfig(): Promise<Readonly<GridConfig<T>>> {\n return Object.freeze({ ...(this.#effectiveConfig || {}) });\n }\n\n // ---------------- Row Update API ----------------\n\n /**\n * Get the unique ID for a row.\n * Uses the configured `getRowId` function or falls back to `row.id` / `row._id`.\n *\n * @group Data Management\n * @param row - The row object\n * @returns The row's unique identifier\n * @throws Error if no ID can be determined\n *\n * @example\n * ```typescript\n * const id = grid.getRowId(row);\n * console.log('Row ID:', id);\n * ```\n */\n getRowId(row: T): string {\n return this.#resolveRowIdOrThrow(row, this.#effectiveConfig.getRowId);\n }\n\n /**\n * Get a row by its ID.\n * O(1) lookup via internal Map.\n *\n * @group Data Management\n * @param id - Row identifier (from getRowId)\n * @returns The row object, or undefined if not found\n *\n * @example\n * ```typescript\n * const row = grid.getRow('cargo-123');\n * if (row) {\n * console.log('Found:', row.name);\n * }\n * ```\n */\n getRow(id: string): T | undefined {\n return this.#rowIdMap.get(id)?.row;\n }\n\n /**\n * Update a row by ID.\n * Mutates the row in-place and emits `cell-change` for each changed field.\n *\n * @group Data Management\n * @param id - Row identifier (from getRowId)\n * @param changes - Partial row data to merge\n * @param source - Origin of update (default: 'api')\n * @throws Error if row is not found\n * @fires cell-change - For each field that changed\n *\n * @example\n * ```typescript\n * // WebSocket update handler\n * socket.on('cargo-update', (data) => {\n * grid.updateRow(data.id, { status: data.status, eta: data.eta });\n * });\n * ```\n */\n updateRow(id: string, changes: Partial<T>, source: UpdateSource = 'api'): void {\n const entry = this.#rowIdMap.get(id);\n if (!entry) {\n throw new Error(\n `[tbw-grid] Row with ID \"${id}\" not found. ` + `Ensure the row exists and getRowId is correctly configured.`,\n );\n }\n\n const { row, index } = entry;\n const changedFields: Array<{ field: string; oldValue: unknown; newValue: unknown }> = [];\n\n // Compute changes and apply in-place\n for (const [field, newValue] of Object.entries(changes)) {\n const oldValue = (row as Record<string, unknown>)[field];\n if (oldValue !== newValue) {\n changedFields.push({ field, oldValue, newValue });\n (row as Record<string, unknown>)[field] = newValue;\n }\n }\n\n // Emit cell-change for each changed field\n for (const { field, oldValue, newValue } of changedFields) {\n this.#emit('cell-change', {\n row,\n rowId: id,\n rowIndex: index,\n field,\n oldValue,\n newValue,\n changes,\n source,\n } as CellChangeDetail<T>);\n }\n\n // Schedule re-render if anything changed\n if (changedFields.length > 0) {\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'updateRow');\n }\n }\n\n /**\n * Batch update multiple rows.\n * More efficient than multiple `updateRow()` calls - single render cycle.\n *\n * @group Data Management\n * @param updates - Array of { id, changes } objects\n * @param source - Origin of updates (default: 'api')\n * @throws Error if any row is not found\n * @fires cell-change - For each field that changed on each row\n *\n * @example\n * ```typescript\n * // Bulk status update\n * grid.updateRows([\n * { id: 'cargo-1', changes: { status: 'shipped' } },\n * { id: 'cargo-2', changes: { status: 'shipped' } },\n * ]);\n * ```\n */\n updateRows(updates: Array<{ id: string; changes: Partial<T> }>, source: UpdateSource = 'api'): void {\n let anyChanged = false;\n\n for (const { id, changes } of updates) {\n const entry = this.#rowIdMap.get(id);\n if (!entry) {\n throw new Error(\n `[tbw-grid] Row with ID \"${id}\" not found. ` + `Ensure the row exists and getRowId is correctly configured.`,\n );\n }\n\n const { row, index } = entry;\n\n // Compute changes and apply in-place\n for (const [field, newValue] of Object.entries(changes)) {\n const oldValue = (row as Record<string, unknown>)[field];\n if (oldValue !== newValue) {\n anyChanged = true;\n (row as Record<string, unknown>)[field] = newValue;\n\n // Emit cell-change for each changed field\n this.#emit('cell-change', {\n row,\n rowId: id,\n rowIndex: index,\n field,\n oldValue,\n newValue,\n changes,\n source,\n } as CellChangeDetail<T>);\n }\n }\n }\n\n // Schedule single re-render for all changes\n if (anyChanged) {\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'updateRows');\n }\n }\n\n // ---------------- Row Animation API ----------------\n\n /**\n * Animate a row at the specified index.\n * Applies a visual animation to highlight changes, insertions, or removals.\n *\n * **Animation types:**\n * - `'change'`: Flash highlight (for data changes, e.g., after cell edit)\n * - `'insert'`: Slide-in animation (for newly added rows)\n * - `'remove'`: Fade-out animation (for rows being removed)\n *\n * The animation is purely visual - it does not affect the data.\n * For remove animations, the row remains in the DOM until animation completes.\n *\n * @group Row Animation\n * @param rowIndex - Index of the row in the current row set\n * @param type - Type of animation to apply\n *\n * @example\n * ```typescript\n * // Highlight a row after external data update\n * grid.updateRow('row-123', { status: 'shipped' });\n * grid.animateRow(rowIndex, 'change');\n *\n * // Animate new row insertion\n * grid.rows = [...grid.rows, newRow];\n * grid.animateRow(grid.rows.length - 1, 'insert');\n * ```\n */\n animateRow(rowIndex: number, type: RowAnimationType): void {\n animateRow(this as unknown as InternalGrid, rowIndex, type);\n }\n\n /**\n * Animate multiple rows at once.\n * More efficient than calling `animateRow()` multiple times.\n *\n * @group Row Animation\n * @param rowIndices - Indices of the rows to animate\n * @param type - Type of animation to apply to all rows\n *\n * @example\n * ```typescript\n * // Highlight all changed rows after bulk update\n * const changedIndices = [0, 2, 5];\n * grid.animateRows(changedIndices, 'change');\n * ```\n */\n animateRows(rowIndices: number[], type: RowAnimationType): void {\n animateRows(this as unknown as InternalGrid, rowIndices, type);\n }\n\n /**\n * Animate a row by its ID.\n * Uses the configured `getRowId` function to find the row.\n *\n * @group Row Animation\n * @param rowId - The row's unique identifier (from getRowId)\n * @param type - Type of animation to apply\n * @returns `true` if the row was found and animated, `false` otherwise\n *\n * @example\n * ```typescript\n * // Highlight a row after real-time update\n * socket.on('row-updated', (data) => {\n * grid.updateRow(data.id, data.changes);\n * grid.animateRowById(data.id, 'change');\n * });\n * ```\n */\n animateRowById(rowId: string, type: RowAnimationType): boolean {\n return animateRowById(this as unknown as InternalGrid, rowId, type);\n }\n\n // ---------------- Column Visibility API ----------------\n // Delegates to ConfigManager\n\n /**\n * Show or hide a column by field name.\n *\n * @group Column Visibility\n * @param field - The field name of the column to modify\n * @param visible - Whether the column should be visible\n * @returns `true` if the visibility changed, `false` if unchanged\n * @fires column-state-change - Emitted when the visibility changes\n *\n * @example\n * ```typescript\n * // Hide the email column\n * grid.setColumnVisible('email', false);\n *\n * // Show it again\n * grid.setColumnVisible('email', true);\n * ```\n */\n setColumnVisible(field: string, visible: boolean): boolean {\n const result = this.#configManager.setColumnVisible(field, visible);\n if (result) {\n this.requestStateChange();\n }\n return result;\n }\n\n /**\n * Toggle a column's visibility.\n *\n * @group Column Visibility\n * @param field - The field name of the column to toggle\n * @returns The new visibility state (`true` = visible, `false` = hidden)\n * @fires column-state-change - Emitted when the visibility changes\n *\n * @example\n * ```typescript\n * // Toggle the notes column visibility\n * const isNowVisible = grid.toggleColumnVisibility('notes');\n * console.log(`Notes column is now ${isNowVisible ? 'visible' : 'hidden'}`);\n * ```\n */\n toggleColumnVisibility(field: string): boolean {\n const result = this.#configManager.toggleColumnVisibility(field);\n if (result) {\n this.requestStateChange();\n }\n return result;\n }\n\n /**\n * Check if a column is currently visible.\n *\n * @group Column Visibility\n * @param field - The field name to check\n * @returns `true` if the column is visible, `false` if hidden\n *\n * @example\n * ```typescript\n * if (grid.isColumnVisible('email')) {\n * console.log('Email column is showing');\n * }\n * ```\n */\n isColumnVisible(field: string): boolean {\n return this.#configManager.isColumnVisible(field);\n }\n\n /**\n * Show all columns, resetting any hidden columns to visible.\n *\n * @group Column Visibility\n * @fires column-state-change - Emitted when column visibility changes\n * @example\n * ```typescript\n * // Reset button handler\n * resetButton.onclick = () => grid.showAllColumns();\n * ```\n */\n showAllColumns(): void {\n this.#configManager.showAllColumns();\n this.requestStateChange();\n }\n\n /**\n * Get metadata for all columns including visibility state.\n * Useful for building a column picker UI.\n *\n * @group Column Visibility\n * @returns Array of column info objects\n *\n * @example\n * ```typescript\n * // Build a column visibility menu\n * const columns = grid.getAllColumns();\n * columns.forEach(col => {\n * if (!col.utility) { // Skip utility columns like selection checkbox\n * const menuItem = document.createElement('label');\n * menuItem.innerHTML = `\n * <input type=\"checkbox\" ${col.visible ? 'checked' : ''}>\n * ${col.header}\n * `;\n * menuItem.querySelector('input').onchange = () =>\n * grid.toggleColumnVisibility(col.field);\n * menu.appendChild(menuItem);\n * }\n * });\n * ```\n */\n getAllColumns(): Array<{\n field: string;\n header: string;\n visible: boolean;\n lockVisible?: boolean;\n utility?: boolean;\n }> {\n return this.#configManager.getAllColumns();\n }\n\n /**\n * Set the display order of columns.\n *\n * @group Column Order\n * @param order - Array of field names in desired order\n * @fires column-state-change - Emitted when column order changes\n *\n * @example\n * ```typescript\n * // Move 'status' column to first position\n * const currentOrder = grid.getColumnOrder();\n * const newOrder = ['status', ...currentOrder.filter(f => f !== 'status')];\n * grid.setColumnOrder(newOrder);\n * ```\n */\n setColumnOrder(order: string[]): void {\n this.#configManager.setColumnOrder(order);\n this.requestStateChange();\n }\n\n /**\n * Get the current column display order.\n *\n * @group Column Order\n * @returns Array of field names in display order\n *\n * @example\n * ```typescript\n * const order = grid.getColumnOrder();\n * console.log('Columns:', order.join(', '));\n * ```\n */\n getColumnOrder(): string[] {\n return this.#configManager.getColumnOrder();\n }\n\n // ---------------- Column State API ----------------\n\n /**\n * Get the current column state for persistence.\n * Returns a serializable object including column order, widths, visibility,\n * sort state, and any plugin-specific state.\n *\n * Use this to save user preferences to localStorage or a database.\n *\n * @group State Persistence\n * @returns Serializable column state object\n *\n * @example\n * ```typescript\n * // Save state to localStorage\n * const state = grid.getColumnState();\n * localStorage.setItem('gridState', JSON.stringify(state));\n *\n * // Later, restore the state\n * const saved = localStorage.getItem('gridState');\n * if (saved) {\n * grid.columnState = JSON.parse(saved);\n * }\n * ```\n */\n getColumnState(): GridColumnState {\n const plugins = this.#pluginManager?.getAll() ?? [];\n return this.#configManager.collectState(plugins as BaseGridPlugin[]);\n }\n\n /**\n * Set the column state, restoring all saved preferences.\n * Can be set before or after grid initialization.\n *\n * @group State Persistence\n * @fires column-state-change - Emitted after state is applied (if grid is initialized)\n * @example\n * ```typescript\n * // Restore saved state on page load\n * const grid = document.querySelector('tbw-grid');\n * const saved = localStorage.getItem('myGridState');\n * if (saved) {\n * grid.columnState = JSON.parse(saved);\n * }\n * ```\n */\n set columnState(state: GridColumnState | undefined) {\n if (!state) return;\n\n // Store for use after initialization if called before ready\n this.#initialColumnState = state;\n this.#configManager.initialColumnState = state;\n\n // If already initialized, apply immediately\n if (this.#initialized) {\n this.#applyColumnState(state);\n }\n }\n\n /**\n * Get the current column state.\n * Alias for `getColumnState()` for property-style access.\n * @group State Persistence\n */\n get columnState(): GridColumnState | undefined {\n return this.getColumnState();\n }\n\n /**\n * Apply column state internally.\n */\n #applyColumnState(state: GridColumnState): void {\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.applyState(state, plugins);\n\n // Re-setup to apply changes\n this.#setup();\n }\n\n /**\n * Request a state change event to be emitted.\n * Called internally after resize, reorder, visibility, or sort changes.\n * Plugins should call this after changing their state.\n * The event is debounced to avoid excessive events during drag operations.\n * @group State Persistence\n * @internal Plugin API\n */\n requestStateChange(): void {\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.requestStateChange(plugins);\n }\n\n /**\n * Reset column state to initial configuration.\n * Clears all user modifications including order, widths, visibility, and sort.\n *\n * @group State Persistence\n * @fires column-state-change - Emitted after state is reset\n * @example\n * ```typescript\n * // Reset button handler\n * resetBtn.onclick = () => {\n * grid.resetColumnState();\n * localStorage.removeItem('gridState');\n * };\n * ```\n */\n resetColumnState(): void {\n // Clear initial state\n this.#initialColumnState = undefined;\n this.__originalOrder = [];\n\n // Use ConfigManager to reset state\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n this.#configManager.resetState(plugins);\n\n // Re-initialize columns from config\n this.#configManager.merge();\n this.#setup();\n }\n\n // ---------------- Shell / Tool Panel API ----------------\n // These methods delegate to ShellController for implementation.\n // The controller encapsulates all tool panel logic while grid.ts\n // ════════════════════════════════════════════════════════════════════════════\n // Tool Panel API\n // ════════════════════════════════════════════════════════════════════════════\n // The tool panel is a collapsible sidebar that contains accordion sections.\n // Each section can contain plugin UI (e.g., column visibility, filtering).\n\n /**\n * Check if the tool panel sidebar is currently open.\n *\n * The tool panel is an accordion-based sidebar that contains sections\n * registered by plugins or via `registerToolPanel()`.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Conditionally show/hide a \"toggle panel\" button\n * const isPanelOpen = grid.isToolPanelOpen;\n * toggleButton.textContent = isPanelOpen ? 'Close Panel' : 'Open Panel';\n * ```\n */\n get isToolPanelOpen(): boolean {\n return this.#shellController.isPanelOpen;\n }\n\n /**\n * Get the IDs of currently expanded accordion sections in the tool panel.\n *\n * Multiple sections can be expanded simultaneously in the accordion view.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Check which sections are expanded\n * const expanded = grid.expandedToolPanelSections;\n * console.log('Expanded sections:', expanded);\n * // e.g., ['columnVisibility', 'filtering']\n * ```\n */\n get expandedToolPanelSections(): string[] {\n return this.#shellController.expandedSections;\n }\n\n /**\n * Open the tool panel sidebar.\n *\n * The tool panel displays an accordion view with all registered panel sections.\n * Each section can be expanded/collapsed independently.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Open the tool panel when a toolbar button is clicked\n * settingsButton.addEventListener('click', () => {\n * grid.openToolPanel();\n * });\n * ```\n */\n openToolPanel(): void {\n this.#shellController.openToolPanel();\n }\n\n /**\n * Close the tool panel sidebar.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Close the panel after user makes a selection\n * grid.closeToolPanel();\n * ```\n */\n closeToolPanel(): void {\n this.#shellController.closeToolPanel();\n }\n\n /**\n * Toggle the tool panel sidebar open or closed.\n *\n * @group Tool Panel\n * @example\n * ```typescript\n * // Wire up a toggle button\n * toggleButton.addEventListener('click', () => {\n * grid.toggleToolPanel();\n * });\n * ```\n */\n toggleToolPanel(): void {\n this.#shellController.toggleToolPanel();\n }\n\n /**\n * Toggle an accordion section expanded or collapsed within the tool panel.\n *\n * @group Tool Panel\n * @param sectionId - The ID of the section to toggle (matches `ToolPanelDefinition.id`)\n *\n * @example\n * ```typescript\n * // Expand the column visibility section programmatically\n * grid.openToolPanel();\n * grid.toggleToolPanelSection('columnVisibility');\n * ```\n */\n toggleToolPanelSection(sectionId: string): void {\n this.#shellController.toggleToolPanelSection(sectionId);\n }\n\n /**\n * Get all registered tool panel definitions.\n *\n * Returns both plugin-registered panels and panels registered via `registerToolPanel()`.\n *\n * @group Tool Panel\n * @returns Array of tool panel definitions\n *\n * @example\n * ```typescript\n * // List all available panels\n * const panels = grid.getToolPanels();\n * panels.forEach(panel => {\n * console.log(`Panel: ${panel.title} (${panel.id})`);\n * });\n * ```\n */\n getToolPanels(): ToolPanelDefinition[] {\n return this.#shellController.getToolPanels();\n }\n\n /**\n * Register a custom tool panel section.\n *\n * Use this API to add custom UI sections to the tool panel sidebar\n * without creating a full plugin. The panel will appear as an accordion\n * section in the tool panel.\n *\n * @group Tool Panel\n * @param panel - The tool panel definition\n *\n * @example\n * ```typescript\n * // Register a custom \"Export\" panel\n * grid.registerToolPanel({\n * id: 'export',\n * title: 'Export Options',\n * icon: '📥',\n * order: 50, // Lower order = higher in list\n * render: (container) => {\n * container.innerHTML = `\n * <button id=\"export-csv\">Export CSV</button>\n * <button id=\"export-json\">Export JSON</button>\n * `;\n * container.querySelector('#export-csv')?.addEventListener('click', () => {\n * exportToCSV(grid.rows);\n * });\n * }\n * });\n * ```\n */\n registerToolPanel(panel: ToolPanelDefinition): void {\n this.#shellState.apiToolPanelIds.add(panel.id);\n this.#shellController.registerToolPanel(panel);\n }\n\n /**\n * Unregister a custom tool panel section.\n *\n * @group Tool Panel\n * @param panelId - The ID of the panel to remove\n *\n * @example\n * ```typescript\n * // Remove the export panel when no longer needed\n * grid.unregisterToolPanel('export');\n * ```\n */\n unregisterToolPanel(panelId: string): void {\n this.#shellState.apiToolPanelIds.delete(panelId);\n this.#shellController.unregisterToolPanel(panelId);\n }\n\n // ════════════════════════════════════════════════════════════════════════════\n // Header Content API\n // ════════════════════════════════════════════════════════════════════════════\n // Header content appears in the grid's header bar area (above the column headers).\n\n /**\n * Get all registered header content definitions.\n *\n * @group Header Content\n * @returns Array of header content definitions\n *\n * @example\n * ```typescript\n * const contents = grid.getHeaderContents();\n * console.log('Header sections:', contents.map(c => c.id));\n * ```\n */\n getHeaderContents(): HeaderContentDefinition[] {\n return this.#shellController.getHeaderContents();\n }\n\n /**\n * Register custom header content.\n *\n * Header content appears in the grid's header bar area, which is displayed\n * above the column headers. Use this for search boxes, filters, or other\n * controls that should be prominently visible.\n *\n * @group Header Content\n * @param content - The header content definition\n *\n * @example\n * ```typescript\n * // Add a global search box to the header\n * grid.registerHeaderContent({\n * id: 'global-search',\n * order: 10,\n * render: (container) => {\n * const input = document.createElement('input');\n * input.type = 'search';\n * input.placeholder = 'Search all columns...';\n * input.addEventListener('input', (e) => {\n * const term = (e.target as HTMLInputElement).value;\n * filterGrid(term);\n * });\n * container.appendChild(input);\n * }\n * });\n * ```\n */\n registerHeaderContent(content: HeaderContentDefinition): void {\n this.#shellController.registerHeaderContent(content);\n }\n\n /**\n * Unregister custom header content.\n *\n * @group Header Content\n * @param contentId - The ID of the content to remove\n *\n * @example\n * ```typescript\n * grid.unregisterHeaderContent('global-search');\n * ```\n */\n unregisterHeaderContent(contentId: string): void {\n this.#shellController.unregisterHeaderContent(contentId);\n }\n\n // ════════════════════════════════════════════════════════════════════════════\n // Toolbar Content API\n // ════════════════════════════════════════════════════════════════════════════\n // Toolbar content appears in the grid's toolbar area (typically below header,\n // above column headers). Use for action buttons, dropdowns, etc.\n\n /**\n * Get all registered toolbar content definitions.\n *\n * @group Toolbar\n * @returns Array of toolbar content definitions\n *\n * @example\n * ```typescript\n * const contents = grid.getToolbarContents();\n * console.log('Toolbar items:', contents.map(c => c.id));\n * ```\n */\n getToolbarContents(): ToolbarContentDefinition[] {\n return this.#shellController.getToolbarContents();\n }\n\n /**\n * Register custom toolbar content.\n *\n * Toolbar content appears in the grid's toolbar area. Use this for action\n * buttons, dropdowns, or other controls that should be easily accessible.\n * Content is rendered in order of the `order` property (lower = first).\n *\n * @group Toolbar\n * @param content - The toolbar content definition\n *\n * @example\n * ```typescript\n * // Add export buttons to the toolbar\n * grid.registerToolbarContent({\n * id: 'export-buttons',\n * order: 100, // Position in toolbar (lower = first)\n * render: (container) => {\n * const csvBtn = document.createElement('button');\n * csvBtn.textContent = 'Export CSV';\n * csvBtn.className = 'dg-toolbar-btn';\n * csvBtn.addEventListener('click', () => exportToCSV(grid.rows));\n *\n * const jsonBtn = document.createElement('button');\n * jsonBtn.textContent = 'Export JSON';\n * jsonBtn.className = 'dg-toolbar-btn';\n * jsonBtn.addEventListener('click', () => exportToJSON(grid.rows));\n *\n * container.append(csvBtn, jsonBtn);\n * }\n * });\n * ```\n *\n * @example\n * ```typescript\n * // Add a dropdown filter to the toolbar\n * grid.registerToolbarContent({\n * id: 'status-filter',\n * order: 50,\n * render: (container) => {\n * const select = document.createElement('select');\n * select.innerHTML = `\n * <option value=\"\">All Statuses</option>\n * <option value=\"active\">Active</option>\n * <option value=\"inactive\">Inactive</option>\n * `;\n * select.addEventListener('change', (e) => {\n * const status = (e.target as HTMLSelectElement).value;\n * applyStatusFilter(status);\n * });\n * container.appendChild(select);\n * }\n * });\n * ```\n */\n registerToolbarContent(content: ToolbarContentDefinition): void {\n this.#shellController.registerToolbarContent(content);\n }\n\n /**\n * Unregister custom toolbar content.\n *\n * @group Toolbar\n * @param contentId - The ID of the content to remove\n *\n * @example\n * ```typescript\n * // Remove export buttons when switching to read-only mode\n * grid.unregisterToolbarContent('export-buttons');\n * ```\n */\n unregisterToolbarContent(contentId: string): void {\n this.#shellController.unregisterToolbarContent(contentId);\n }\n\n /** Pending shell refresh - used to batch multiple rapid calls */\n #pendingShellRefresh = false;\n\n /**\n * Re-parse light DOM shell elements and refresh shell header.\n * Call this after dynamically modifying <tbw-grid-header> children.\n *\n * Multiple calls are batched via microtask to avoid redundant DOM rebuilds\n * when registering multiple tool panels in sequence.\n *\n * @internal Plugin API\n */\n refreshShellHeader(): void {\n // Batch multiple rapid calls into a single microtask\n if (this.#pendingShellRefresh) return;\n this.#pendingShellRefresh = true;\n\n queueMicrotask(() => {\n this.#pendingShellRefresh = false;\n if (!this.isConnected) return;\n\n // Re-parse light DOM (header, tool buttons, and tool panels)\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Mark sources as changed since shell parsing may have updated state maps\n this.#configManager.markSourcesChanged();\n\n // Re-merge config to sync shell state changes into effectiveConfig.shell\n this.#configManager.merge();\n\n // Re-render the entire grid (shell structure may change)\n this.#render();\n this.#injectAllPluginStyles(); // Re-inject after render clears shadow DOM\n\n // Use lighter-weight post-render setup instead of full #afterConnect()\n // This avoids requesting another FULL render since #render() already rebuilt the DOM\n this.#afterShellRefresh();\n });\n }\n\n /**\n * Lighter-weight post-render setup after shell refresh.\n * Unlike #afterConnect(), this skips scheduler FULL request since DOM is already built.\n */\n #afterShellRefresh(): void {\n // Shell changes the DOM structure - need to re-cache element references\n const gridContent = this.#renderRoot.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#renderRoot.querySelector('.tbw-grid-root');\n\n this._headerRowEl = gridRoot?.querySelector('.header-row') as HTMLElement;\n this._virtualization.totalHeightEl = gridRoot?.querySelector('.faux-vscroll-spacer') as HTMLElement;\n this._virtualization.viewportEl = gridRoot?.querySelector('.rows-viewport') as HTMLElement;\n this._bodyEl = gridRoot?.querySelector('.rows') as HTMLElement;\n this.__rowsBodyEl = gridRoot?.querySelector('.rows-body') as HTMLElement;\n\n // Render shell header content and custom toolbar contents\n if (this.#shellController.isInitialized) {\n renderHeaderContent(this.#renderRoot, this.#shellState);\n renderCustomToolbarContents(this.#renderRoot, this.#effectiveConfig?.shell, this.#shellState);\n\n const defaultOpen = this.#effectiveConfig?.shell?.toolPanel?.defaultOpen;\n if (defaultOpen && this.#shellState.toolPanels.has(defaultOpen)) {\n this.openToolPanel();\n this.#shellState.expandedSections.add(defaultOpen);\n }\n }\n\n // Re-create resize controller (DOM elements changed)\n this._resizeController = createResizeController(this as unknown as InternalGrid<T>);\n\n // Re-setup scroll listeners (DOM elements changed)\n this.#setupScrollListeners(gridRoot);\n\n // Request COLUMNS phase to reprocess columns (including column groups) and render header\n // #render() rebuilds the DOM with an empty header-row, so we need COLUMNS phase (5)\n // which includes processColumns (for column groups), processRows, and renderHeader\n this.#scheduler.requestPhase(RenderPhase.COLUMNS, 'shellRefresh');\n }\n\n // #region Custom Styles API\n /** Map of registered custom stylesheets by ID - uses adoptedStyleSheets which survive DOM rebuilds */\n #customStyleSheets = new Map<string, CSSStyleSheet>();\n\n /**\n * Register custom CSS styles to be injected into the grid's shadow DOM.\n * Use this to style custom cell renderers, editors, or detail panels.\n *\n * Uses adoptedStyleSheets for efficiency - styles survive shadow DOM rebuilds.\n *\n * @group Custom Styles\n * @param id - Unique identifier for the style block (for removal/updates)\n * @param css - CSS string to inject\n *\n * @example\n * ```typescript\n * // Register custom styles for a detail panel\n * grid.registerStyles('my-detail-styles', `\n * .my-detail-panel { padding: 16px; }\n * .my-detail-table { width: 100%; }\n * `);\n *\n * // Update styles later\n * grid.registerStyles('my-detail-styles', updatedCss);\n *\n * // Remove styles\n * grid.unregisterStyles('my-detail-styles');\n * ```\n */\n registerStyles(id: string, css: string): void {\n // Create or update the stylesheet\n let sheet = this.#customStyleSheets.get(id);\n if (!sheet) {\n sheet = new CSSStyleSheet();\n this.#customStyleSheets.set(id, sheet);\n }\n sheet.replaceSync(css);\n\n // Update adoptedStyleSheets to include all custom sheets\n this.#updateAdoptedStyleSheets();\n }\n\n /**\n * Remove previously registered custom styles.\n *\n * @group Custom Styles\n * @param id - The ID used when registering the styles\n */\n unregisterStyles(id: string): void {\n if (this.#customStyleSheets.delete(id)) {\n this.#updateAdoptedStyleSheets();\n }\n }\n\n /**\n * Get list of registered custom style IDs.\n *\n * @group Custom Styles\n */\n getRegisteredStyles(): string[] {\n return Array.from(this.#customStyleSheets.keys());\n }\n\n /**\n * Update document.adoptedStyleSheets to include custom sheets.\n * Without Shadow DOM, all custom styles go into the document.\n */\n #updateAdoptedStyleSheets(): void {\n const customSheets = Array.from(this.#customStyleSheets.values());\n\n // Start with document's existing sheets (excluding any we've added before)\n // We track custom sheets by their presence in our map\n const existingSheets = document.adoptedStyleSheets.filter(\n (sheet) => !Array.from(this.#customStyleSheets.values()).includes(sheet),\n );\n\n document.adoptedStyleSheets = [...existingSheets, ...customSheets];\n }\n // #endregion\n\n /**\n * Set up Light DOM handlers via ConfigManager's observer infrastructure.\n * This handles frameworks like Angular that project content asynchronously.\n *\n * The observer is owned by ConfigManager (generic infrastructure).\n * The handlers (parsing logic) are owned here (eventually ShellPlugin).\n *\n * This separation allows plugins to register their own Light DOM elements\n * and handle parsing themselves.\n */\n #setupLightDomHandlers(): void {\n // Handler for shell header element changes\n const handleShellChange = () => {\n const hadTitle = this.#shellState.lightDomTitle;\n const hadToolButtons = this.#shellState.hasToolButtonsContainer;\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n const hasTitle = this.#shellState.lightDomTitle;\n const hasToolButtons = this.#shellState.hasToolButtonsContainer;\n\n if ((hasTitle && !hadTitle) || (hasToolButtons && !hadToolButtons)) {\n this.#configManager.markSourcesChanged();\n this.#configManager.merge();\n const shellHeader = this.#renderRoot.querySelector('.tbw-shell-header');\n if (shellHeader) {\n const newHeaderHtml = renderShellHeader(\n this.#effectiveConfig.shell,\n this.#shellState,\n this.#effectiveConfig.icons?.toolPanel,\n );\n const temp = document.createElement('div');\n temp.innerHTML = newHeaderHtml;\n const newHeader = temp.firstElementChild;\n if (newHeader) {\n shellHeader.replaceWith(newHeader);\n this.#setupShellListeners();\n }\n }\n }\n };\n\n // Handler for column element changes\n const handleColumnChange = () => {\n this.__lightDomColumnsCache = undefined;\n this.#setup();\n };\n\n // Register handlers with ConfigManager\n // Shell-related elements (eventually these move to ShellPlugin)\n this.#configManager.registerLightDomHandler('tbw-grid-header', handleShellChange);\n this.#configManager.registerLightDomHandler('tbw-grid-tool-buttons', handleShellChange);\n this.#configManager.registerLightDomHandler('tbw-grid-tool-panel', handleShellChange);\n\n // Column elements (core grid functionality)\n this.#configManager.registerLightDomHandler('tbw-grid-column', handleColumnChange);\n this.#configManager.registerLightDomHandler('tbw-grid-detail', handleColumnChange);\n\n // Start observing\n this.#configManager.observeLightDOM(this as unknown as HTMLElement);\n }\n\n /**\n * Re-parse light DOM column elements and refresh the grid.\n * Call this after framework adapters have registered their templates.\n * Uses the render scheduler to batch with other pending updates.\n * @category Framework Adapters\n */\n refreshColumns(): void {\n // Clear the column cache to force re-parsing\n this.__lightDomColumnsCache = undefined;\n\n // Invalidate cell cache to reset __hasSpecialColumns flag\n // This is critical for frameworks like React where renderers are registered asynchronously\n // after the initial render (which may have cached __hasSpecialColumns = false)\n invalidateCellCache(this);\n\n // Re-parse light DOM columns SYNCHRONOUSLY to pick up newly registered framework renderers\n // This must happen before the scheduler runs processColumns\n this.#configManager.parseLightDomColumns(this as unknown as HTMLElement);\n\n // Re-parse light DOM shell elements (may have been rendered asynchronously by frameworks)\n const hadTitle = this.#shellState.lightDomTitle;\n const hadToolButtons = this.#shellState.hasToolButtonsContainer;\n parseLightDomShell(this, this.#shellState);\n // Also parse tool buttons container that is a direct child (React/Vue pattern)\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n const hasTitle = this.#shellState.lightDomTitle;\n const hasToolButtons = this.#shellState.hasToolButtonsContainer;\n\n // If title or tool buttons were added via light DOM, update the shell header in place\n // The shell may already be rendered (due to plugins/panels), but without the title\n const needsShellRefresh = (hasTitle && !hadTitle) || (hasToolButtons && !hadToolButtons);\n if (needsShellRefresh) {\n // Mark sources as changed since shell parsing may have updated state maps\n this.#configManager.markSourcesChanged();\n // Merge the new title into effectiveConfig\n this.#configManager.merge();\n // Update the existing shell header element with new HTML\n const shellHeader = this.#renderRoot.querySelector('.tbw-shell-header');\n if (shellHeader) {\n const newHeaderHtml = renderShellHeader(\n this.#effectiveConfig.shell,\n this.#shellState,\n this.#effectiveConfig.icons?.toolPanel,\n );\n // Create a temporary container and extract the new header\n const temp = document.createElement('div');\n temp.innerHTML = newHeaderHtml;\n const newHeader = temp.firstElementChild;\n if (newHeader) {\n shellHeader.replaceWith(newHeader);\n // Re-attach event listeners to the new toolbar element\n this.#setupShellListeners();\n }\n }\n }\n\n // Request a COLUMNS phase render through the scheduler\n // This batches with any other pending work (e.g., afterConnect)\n this.#scheduler.requestPhase(RenderPhase.COLUMNS, 'refreshColumns');\n }\n\n // ---------------- Virtual Window ----------------\n /**\n * Calculate total height for the faux scrollbar spacer element.\n * Used by both bypass and virtualized rendering paths to ensure consistent scroll behavior.\n */\n #calculateTotalSpacerHeight(totalRows: number): number {\n const rowHeight = this._virtualization.rowHeight;\n const fauxScrollbar = this._virtualization.container ?? this;\n const viewportEl = this._virtualization.viewportEl ?? fauxScrollbar;\n const fauxScrollHeight = fauxScrollbar.clientHeight;\n const viewportHeight = viewportEl.clientHeight;\n\n // Get scroll-area height (may differ from faux when h-scrollbar present)\n const shadowRoot = (this as unknown as Element).shadowRoot;\n const scrollAreaEl = shadowRoot?.querySelector('.tbw-scroll-area');\n const scrollAreaHeight = scrollAreaEl ? (scrollAreaEl as HTMLElement).clientHeight : fauxScrollHeight;\n\n // Use scroll-area height as reference since it contains the actual content\n const containerHeight = scrollAreaHeight;\n const viewportHeightDiff = containerHeight - viewportHeight;\n\n // Add extra height from plugins (e.g., expanded master-detail rows)\n const pluginExtraHeight = this.#pluginManager?.getExtraHeight() ?? 0;\n\n // Horizontal scrollbar compensation: When a horizontal scrollbar appears inside scroll-area,\n // the faux scrollbar (sibling) is taller than scroll-area. Add the difference as padding.\n const hScrollbarPadding = Math.max(0, fauxScrollHeight - scrollAreaHeight);\n\n return totalRows * rowHeight + viewportHeightDiff + pluginExtraHeight + hScrollbarPadding;\n }\n\n /**\n * Core virtualization routine. Chooses between bypass (small datasets), grouped window rendering,\n * or standard row window rendering.\n * @param force - Whether to force a full refresh (not just scroll update)\n * @param skipAfterRender - When true, skip calling afterRender (used by scheduler which calls it separately)\n * @internal Plugin API\n */\n refreshVirtualWindow(force = false, skipAfterRender = false): void {\n if (!this._bodyEl) return;\n\n const totalRows = this._rows.length;\n\n if (!this._virtualization.enabled) {\n this.#renderVisibleRows(0, totalRows);\n if (!skipAfterRender) {\n this.#pluginManager?.afterRender();\n }\n return;\n }\n\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n this._virtualization.start = 0;\n this._virtualization.end = totalRows;\n // Only reset transform on force refresh (initial render, data change)\n // Don't reset on scroll-triggered updates - the scroll handler manages transforms\n if (force) {\n this._bodyEl.style.transform = 'translateY(0px)';\n }\n this.#renderVisibleRows(0, totalRows, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${this.#calculateTotalSpacerHeight(totalRows)}px`;\n }\n // Update ARIA counts on the grid container\n this.#updateAriaCounts(totalRows, this._visibleColumns.length);\n if (!skipAfterRender) {\n this.#pluginManager?.afterRender();\n }\n return;\n }\n\n // --- Normal virtualization path with faux scrollbar pattern ---\n // Faux scrollbar provides scrollTop, viewport provides visible height\n const fauxScrollbar = this._virtualization.container ?? this;\n const viewportEl = this._virtualization.viewportEl ?? fauxScrollbar;\n const viewportHeight = viewportEl.clientHeight;\n const rowHeight = this._virtualization.rowHeight;\n const scrollTop = fauxScrollbar.scrollTop;\n\n // When plugins add extra height (e.g., expanded details), the scroll position\n // includes that extra height. We need to find the actual row at scrollTop\n // by iteratively accounting for cumulative extra heights.\n // This prevents jumping when scrolling past expanded content.\n let start = Math.floor(scrollTop / rowHeight);\n\n // Iteratively refine: the initial guess may be too high because scrollTop\n // includes extra heights from expanded rows before it. Adjust downward.\n let iterations = 0;\n const maxIterations = 10; // Prevent infinite loop\n while (iterations < maxIterations) {\n const extraHeightBefore = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const adjustedStart = Math.floor((scrollTop - extraHeightBefore) / rowHeight);\n if (adjustedStart >= start || adjustedStart < 0) break;\n start = adjustedStart;\n iterations++;\n }\n\n // Faux scrollbar pattern: calculate effective position for this start\n // With translateY(0), the first rendered row appears at viewport top\n // Round down to even number so DOM nth-child(even) always matches data row parity\n // This prevents zebra stripe flickering during scroll since rows shift in pairs\n start = start - (start % 2);\n if (start < 0) start = 0;\n\n // Allow plugins to extend the start index backwards\n // (e.g., to keep expanded detail rows visible as they scroll out)\n const pluginAdjustedStart = this.#pluginManager?.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginAdjustedStart !== undefined && pluginAdjustedStart < start) {\n start = pluginAdjustedStart;\n // Re-apply even alignment after plugin adjustment\n start = start - (start % 2);\n if (start < 0) start = 0;\n }\n\n // Faux pattern buffer: render 2 extra rows below for smooth edge transition\n // This is smaller than traditional overscan since sub-pixel offset handles smoothness\n // +1 extra to account for the even-alignment above potentially showing 1 more row at top\n const visibleCount = Math.ceil(viewportHeight / rowHeight) + 3;\n let end = start + visibleCount;\n if (end > totalRows) end = totalRows;\n\n this._virtualization.start = start;\n this._virtualization.end = end;\n\n // Height spacer for scrollbar\n // The faux-vscroll is a sibling of .tbw-scroll-area, so it doesn't shrink when\n // elements inside scroll-area (header, column groups, footer, hScrollbar) take vertical space.\n // viewportHeightDiff captures ALL these differences - no extra buffer needed.\n const fauxScrollHeight = fauxScrollbar.clientHeight;\n\n // Guard: Skip height calculation if faux scrollbar has no height but viewport does\n // This indicates stale DOM references during recreation (e.g., shell toggle)\n // When both are 0 (test environment or not in DOM), proceed normally\n if (fauxScrollHeight === 0 && viewportHeight > 0) {\n // Stale refs detected, schedule retry through the scheduler\n // Using scheduler ensures this batches with other pending work\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'stale-refs-retry');\n return;\n }\n\n const totalHeight = this.#calculateTotalSpacerHeight(totalRows);\n\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${totalHeight}px`;\n }\n\n // Smooth scroll: apply offset for fluid motion\n // Since start is even-aligned, offset is distance from that aligned position\n // This creates smooth sliding while preserving zebra stripe parity\n // Account for extra heights (expanded details) before the start row\n const extraHeightBeforeStart = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const subPixelOffset = -(scrollTop - start * rowHeight - extraHeightBeforeStart);\n this._bodyEl.style.transform = `translateY(${subPixelOffset}px)`;\n\n this.#renderVisibleRows(start, end, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n\n // Update ARIA counts on the grid container\n this.#updateAriaCounts(totalRows, this._visibleColumns.length);\n\n // Only run plugin afterRender hooks on force refresh (structural changes)\n // Skip on scroll-triggered renders for maximum performance\n if (force && !skipAfterRender) {\n this.#pluginManager?.afterRender();\n\n // After plugins modify the DOM (e.g., add footer, column groups),\n // heights may have changed. Recalculate spacer height in a microtask\n // to catch these changes before the next paint.\n queueMicrotask(() => {\n const newFauxHeight = fauxScrollbar.clientHeight;\n const newViewportHeight = viewportEl.clientHeight;\n // Skip if faux scrollbar is stale (0 height but viewport has height)\n if (newFauxHeight === 0 && newViewportHeight > 0) return;\n\n // Recalculate using the shared helper\n const newTotalHeight = this.#calculateTotalSpacerHeight(totalRows);\n\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${newTotalHeight}px`;\n }\n });\n }\n }\n\n // ---------------- Render ----------------\n #render(): void {\n // Parse light DOM shell elements before rendering\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolButtons(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Mark sources as changed since shell parsing may have updated state maps\n this.#configManager.markSourcesChanged();\n\n // Re-merge config to pick up any newly parsed light DOM shell settings\n this.#configManager.merge();\n\n const shellConfig = this.#effectiveConfig?.shell;\n\n // Render using direct DOM construction (2-3x faster than innerHTML)\n // Pass only minimal runtime state (isPanelOpen, expandedSections) - config comes from effectiveConfig.shell\n const hasShell = buildGridDOMIntoElement(\n this.#renderRoot,\n shellConfig,\n { isPanelOpen: this.#shellState.isPanelOpen, expandedSections: this.#shellState.expandedSections },\n this.#effectiveConfig?.icons,\n );\n\n if (hasShell) {\n this.#setupShellListeners();\n this.#shellController.setInitialized(true);\n }\n }\n\n /**\n * Set up shell event listeners after render.\n */\n #setupShellListeners(): void {\n setupShellEventListeners(this.#renderRoot, this.#effectiveConfig?.shell, this.#shellState, {\n onPanelToggle: () => this.toggleToolPanel(),\n onSectionToggle: (sectionId: string) => this.toggleToolPanelSection(sectionId),\n });\n\n // Set up tool panel resize\n this.#resizeCleanup?.();\n this.#resizeCleanup = setupToolPanelResize(this.#renderRoot, this.#effectiveConfig?.shell, (width: number) => {\n // Update the CSS variable to persist the new width\n this.style.setProperty('--tbw-tool-panel-width', `${width}px`);\n });\n }\n}\n\n// Self-registering custom element\nif (!customElements.get(DataGridElement.tagName)) {\n customElements.define(DataGridElement.tagName, DataGridElement);\n}\n\n// Make DataGridElement accessible globally for framework adapters\n(globalThis as unknown as { DataGridElement: typeof DataGridElement }).DataGridElement = DataGridElement;\n\n// Type augmentation for querySelector/createElement\ndeclare global {\n interface HTMLElementTagNameMap {\n 'tbw-grid': DataGridElement;\n }\n}\n","/**\n * Shared types for the plugin system.\n *\n * These types are used by both the base plugin class and the grid core.\n * Centralizing them here avoids circular imports and reduces duplication.\n */\n\nimport type { ColumnConfig, GridConfig, ToolPanelDefinition, UpdateSource } from '../types';\n\n/**\n * Keyboard modifier flags\n */\nexport interface KeyboardModifiers {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n}\n\n/**\n * Cell coordinates\n */\nexport interface CellCoords {\n row: number;\n col: number;\n}\n\n/**\n * Cell click event\n */\nexport interface CellClickEvent {\n rowIndex: number;\n colIndex: number;\n field: string;\n value: unknown;\n row: unknown;\n cellEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Row click event\n */\nexport interface RowClickEvent {\n rowIndex: number;\n row: unknown;\n rowEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Header click event\n */\nexport interface HeaderClickEvent {\n colIndex: number;\n field: string;\n column: ColumnConfig;\n headerEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Scroll event\n */\nexport interface ScrollEvent {\n scrollTop: number;\n scrollLeft: number;\n scrollHeight: number;\n scrollWidth: number;\n clientHeight: number;\n clientWidth: number;\n originalEvent?: Event;\n}\n\n/**\n * Cell mouse event (for drag operations, selection, etc.)\n */\nexport interface CellMouseEvent {\n /** Event type: mousedown, mousemove, or mouseup */\n type: 'mousedown' | 'mousemove' | 'mouseup';\n /** Row index, undefined if not over a data cell */\n rowIndex?: number;\n /** Column index, undefined if not over a cell */\n colIndex?: number;\n /** Field name, undefined if not over a cell */\n field?: string;\n /** Cell value, undefined if not over a data cell */\n value?: unknown;\n /** Row data object, undefined if not over a data row */\n row?: unknown;\n /** Column configuration, undefined if not over a column */\n column?: ColumnConfig;\n /** The cell element, undefined if not over a cell */\n cellElement?: HTMLElement;\n /** The row element, undefined if not over a row */\n rowElement?: HTMLElement;\n /** Whether the event is over a header cell */\n isHeader: boolean;\n /** Cell coordinates if over a valid data cell */\n cell?: CellCoords;\n /** The original mouse event */\n originalEvent: MouseEvent;\n}\n\n/**\n * Context passed to the `afterCellRender` plugin hook.\n *\n * This provides efficient cell-level access without requiring DOM queries.\n * Plugins receive this context for each cell as it's rendered, enabling\n * targeted modifications instead of post-render DOM traversal.\n *\n * @category Plugin Development\n * @template TRow - The row data type\n *\n * @example\n * ```typescript\n * afterCellRender(context: AfterCellRenderContext): void {\n * if (this.isSelected(context.rowIndex, context.colIndex)) {\n * context.cellElement.classList.add('selected');\n * }\n * }\n * ```\n */\nexport interface AfterCellRenderContext<TRow = unknown> {\n /** The row data object */\n row: TRow;\n /** Zero-based row index in the visible rows array */\n rowIndex: number;\n /** The column configuration */\n column: ColumnConfig<TRow>;\n /** Zero-based column index in the visible columns array */\n colIndex: number;\n /** The cell value (row[column.field]) */\n value: unknown;\n /** The cell DOM element - can be modified by the plugin */\n cellElement: HTMLElement;\n /** The row DOM element - for context, prefer using cellElement */\n rowElement: HTMLElement;\n}\n\n/**\n * Context passed to the `afterRowRender` plugin hook.\n *\n * This provides efficient row-level access after all cells are rendered.\n * Plugins receive this context for each row, enabling row-level modifications\n * without requiring DOM queries in afterRender.\n *\n * @category Plugin Development\n * @template TRow - The row data type\n *\n * @example\n * ```typescript\n * afterRowRender(context: AfterRowRenderContext): void {\n * if (this.isRowSelected(context.rowIndex)) {\n * context.rowElement.classList.add('selected', 'row-focus');\n * }\n * }\n * ```\n */\nexport interface AfterRowRenderContext<TRow = unknown> {\n /** The row data object */\n row: TRow;\n /** Zero-based row index in the visible rows array */\n rowIndex: number;\n /** The row DOM element - can be modified by the plugin */\n rowElement: HTMLElement;\n}\n\n/**\n * Context menu parameters\n */\nexport interface ContextMenuParams {\n x: number;\n y: number;\n rowIndex?: number;\n colIndex?: number;\n field?: string;\n value?: unknown;\n row?: unknown;\n column?: ColumnConfig;\n isHeader?: boolean;\n}\n\n/**\n * Context menu item (used by context-menu plugin query)\n */\nexport interface ContextMenuItem {\n id: string;\n label: string;\n icon?: string;\n disabled?: boolean;\n separator?: boolean;\n children?: ContextMenuItem[];\n action?: (params: ContextMenuParams) => void;\n}\n\n/**\n * Generic plugin query for inter-plugin communication.\n * Plugins can define their own query types as string constants\n * and respond to queries from other plugins.\n */\nexport interface PluginQuery<T = unknown> {\n /** Query type identifier (e.g., 'canMoveColumn', 'getContextMenuItems') */\n type: string;\n /** Query-specific context/parameters */\n context: T;\n}\n\n/**\n * Well-known plugin query types.\n * Plugins can define additional query types beyond these.\n */\nexport const PLUGIN_QUERIES = {\n /** Ask if a column can be moved. Context: ColumnConfig. Response: boolean | undefined */\n CAN_MOVE_COLUMN: 'canMoveColumn',\n /** Get context menu items. Context: ContextMenuParams. Response: ContextMenuItem[] */\n GET_CONTEXT_MENU_ITEMS: 'getContextMenuItems',\n} as const;\n\n/**\n * Cell render context for plugin cell renderers.\n * Provides full context including position and editing state.\n */\nexport interface PluginCellRenderContext {\n /** The cell value */\n value: unknown;\n /** The row data object */\n row: unknown;\n /** The row index in the data array */\n rowIndex: number;\n /** The column index */\n colIndex: number;\n /** The field name */\n field: string;\n /** The column configuration */\n column: ColumnConfig;\n /** Whether the cell is being edited */\n isEditing: boolean;\n}\n\n/**\n * Cell renderer function type for plugins.\n */\nexport type CellRenderer = (ctx: PluginCellRenderContext) => string | HTMLElement;\n\n/**\n * Header renderer function type for plugins.\n */\nexport type HeaderRenderer = (ctx: { column: ColumnConfig; colIndex: number }) => string | HTMLElement;\n\n/**\n * Cell editor interface for plugins.\n */\nexport interface CellEditor {\n create(ctx: PluginCellRenderContext, commitFn: (value: unknown) => void, cancelFn: () => void): HTMLElement;\n getValue?(element: HTMLElement): unknown;\n focus?(element: HTMLElement): void;\n}\n\n/**\n * Minimal grid interface for plugins.\n * This avoids circular imports with the full DataGridElement.\n *\n * Member prefixes indicate accessibility:\n * - `_underscore` = protected members accessible to plugins (marked @internal in full interface)\n */\nexport interface GridElementRef {\n // =========================================================================\n // HTMLElement-like Properties (avoid casting to HTMLElement)\n // =========================================================================\n\n /** The grid's shadow root. */\n shadowRoot: ShadowRoot | null;\n /** Grid element width in pixels. */\n readonly clientWidth: number;\n /** Grid element height in pixels. */\n readonly clientHeight: number;\n /** Add an event listener to the grid element. */\n addEventListener<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => unknown,\n options?: boolean | AddEventListenerOptions,\n ): void;\n addEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | AddEventListenerOptions,\n ): void;\n /** Remove an event listener from the grid element. */\n removeEventListener<K extends keyof HTMLElementEventMap>(\n type: K,\n listener: (this: HTMLElement, ev: HTMLElementEventMap[K]) => unknown,\n options?: boolean | EventListenerOptions,\n ): void;\n removeEventListener(\n type: string,\n listener: EventListenerOrEventListenerObject,\n options?: boolean | EventListenerOptions,\n ): void;\n /** Set an attribute on the grid element. */\n setAttribute(name: string, value: string): void;\n /** Get an attribute from the grid element. */\n getAttribute(name: string): string | null;\n /** Remove an attribute from the grid element. */\n removeAttribute(name: string): void;\n\n // =========================================================================\n // Grid Data & Configuration\n // =========================================================================\n\n /** Current rows (after plugin processing like grouping, filtering). */\n rows: unknown[];\n /** Original unfiltered/unprocessed rows. */\n sourceRows: unknown[];\n /** Column configurations. */\n columns: ColumnConfig[];\n /** Visible columns only (excludes hidden). Use for rendering. @internal */\n _visibleColumns: ColumnConfig[];\n /** Full grid configuration. */\n gridConfig: GridConfig;\n /** Effective (merged) configuration - the single source of truth. */\n effectiveConfig: GridConfig;\n\n // =========================================================================\n // Row Update API\n // =========================================================================\n\n /**\n * Get the unique ID for a row.\n * Uses configured `getRowId` function or falls back to `row.id` / `row._id`.\n * @throws Error if no ID can be determined\n */\n getRowId(row: unknown): string;\n\n /**\n * Get a row by its ID.\n * O(1) lookup via internal Map.\n * @returns The row object, or undefined if not found\n */\n getRow(id: string): unknown | undefined;\n\n /**\n * Update a row by ID.\n * Mutates the row in-place and emits `cell-change` for each changed field.\n * @param id - Row identifier (from getRowId)\n * @param changes - Partial row data to merge\n * @param source - Origin of update (default: 'api')\n * @throws Error if row is not found\n */\n updateRow(id: string, changes: Record<string, unknown>, source?: UpdateSource): void;\n\n /**\n * Batch update multiple rows.\n * More efficient than multiple `updateRow()` calls - single render cycle.\n * @param updates - Array of { id, changes } objects\n * @param source - Origin of updates (default: 'api')\n * @throws Error if any row is not found\n */\n updateRows(updates: Array<{ id: string; changes: Record<string, unknown> }>, source?: UpdateSource): void;\n\n // =========================================================================\n // Focus & Lifecycle\n // =========================================================================\n\n /** Current focused row index. @internal */\n _focusRow: number;\n /** Current focused column index. @internal */\n _focusCol: number;\n /** AbortSignal that is aborted when the grid disconnects from the DOM. */\n disconnectSignal: AbortSignal;\n\n // =========================================================================\n // Rendering\n // =========================================================================\n\n /** Request a full re-render of the grid. */\n requestRender(): void;\n /** Request a full re-render and restore focus styling afterward. */\n requestRenderWithFocus(): void;\n /** Request a lightweight style update without rebuilding DOM. */\n requestAfterRender(): void;\n /** Force a layout pass. */\n forceLayout(): Promise<void>;\n /** Dispatch an event from the grid element. */\n dispatchEvent(event: Event): boolean;\n\n // =========================================================================\n // Inter-plugin Communication\n // =========================================================================\n\n /**\n * Query all plugins with a generic query and collect responses.\n * Used for inter-plugin communication (e.g., asking PinnedColumnsPlugin\n * if a column can be moved).\n *\n * @example\n * const responses = grid.queryPlugins<boolean>({\n * type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n * context: column\n * });\n * const canMove = !responses.includes(false);\n */\n queryPlugins<T>(query: PluginQuery): T[];\n\n // =========================================================================\n // DOM Access\n // =========================================================================\n\n /**\n * Find the rendered DOM element for a row by its data index.\n * Returns null if the row is not currently rendered (virtualized out).\n */\n findRenderedRowElement(rowIndex: number): HTMLElement | null;\n\n // =========================================================================\n // Column Visibility API\n // =========================================================================\n\n /**\n * Get all columns including hidden ones.\n * Returns field, header, visibility status, lock state, and utility flag.\n */\n getAllColumns(): Array<{\n field: string;\n header: string;\n visible: boolean;\n lockVisible?: boolean;\n utility?: boolean;\n }>;\n\n /**\n * Set visibility for a specific column.\n * @returns true if state changed, false if column not found or already in state\n */\n setColumnVisible(field: string, visible: boolean): boolean;\n\n /**\n * Toggle visibility for a specific column.\n * @returns true if state changed, false if column not found\n */\n toggleColumnVisibility(field: string): boolean;\n\n /**\n * Check if a column is currently visible.\n */\n isColumnVisible(field: string): boolean;\n\n /**\n * Show all hidden columns.\n */\n showAllColumns(): void;\n\n // =========================================================================\n // Column Order API\n // =========================================================================\n\n /**\n * Get the current column display order as array of field names.\n */\n getColumnOrder(): string[];\n\n /**\n * Set the column display order.\n * @param order Array of field names in desired order\n */\n setColumnOrder(order: string[]): void;\n\n /**\n * Request emission of column-state-change event (debounced).\n * Call after programmatic column changes that should notify consumers.\n */\n requestStateChange?(): void;\n\n // =========================================================================\n // Tool Panel API (Shell Integration)\n // =========================================================================\n\n /**\n * Whether the tool panel sidebar is currently open.\n */\n readonly isToolPanelOpen: boolean;\n\n /**\n * Get the IDs of expanded accordion sections.\n */\n readonly expandedToolPanelSections: string[];\n\n /**\n * Open the tool panel sidebar (accordion view with all registered panels).\n */\n openToolPanel(): void;\n\n /**\n * Close the tool panel sidebar.\n */\n closeToolPanel(): void;\n\n /**\n * Toggle the tool panel sidebar open/closed.\n */\n toggleToolPanel(): void;\n\n /**\n * Toggle a specific accordion section expanded/collapsed.\n * @param sectionId - The panel ID to toggle (matches ToolPanelDefinition.id)\n */\n toggleToolPanelSection(sectionId: string): void;\n\n /**\n * Get registered tool panel definitions.\n */\n getToolPanels(): ToolPanelDefinition[];\n}\n","/**\n * Base Grid Plugin Class\n *\n * All plugins extend this abstract class.\n * Plugins are instantiated per-grid and manage their own state.\n */\n\n// Injected by Vite at build time from package.json (same as grid.ts)\ndeclare const __GRID_VERSION__: string;\n\nimport type {\n ColumnConfig,\n ColumnState,\n GridPlugin,\n HeaderContentDefinition,\n IconValue,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\n\n// Re-export shared plugin types for convenience\nexport { PLUGIN_QUERIES } from './types';\nexport type {\n AfterCellRenderContext,\n AfterRowRenderContext,\n CellClickEvent,\n CellCoords,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n ContextMenuItem,\n ContextMenuParams,\n GridElementRef,\n HeaderClickEvent,\n HeaderRenderer,\n KeyboardModifiers,\n PluginCellRenderContext,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './types';\n\nimport type {\n AfterCellRenderContext,\n AfterRowRenderContext,\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n GridElementRef,\n HeaderClickEvent,\n HeaderRenderer,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './types';\n\n/**\n * Grid element interface for plugins.\n * Extends GridElementRef with plugin-specific methods.\n * Note: effectiveConfig is already available from GridElementRef.\n */\nexport interface GridElement extends GridElementRef {\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined;\n getPluginByName(name: string): BaseGridPlugin | undefined;\n /**\n * Get a plugin's state by plugin name.\n * This is a loose-coupling method for plugins to access other plugins' state\n * without importing the plugin class directly.\n * @internal Plugin API\n */\n getPluginState?(name: string): unknown;\n}\n\n/**\n * Header render context for plugin header renderers.\n */\nexport interface PluginHeaderRenderContext {\n /** Column configuration */\n column: ColumnConfig;\n /** Column index */\n colIndex: number;\n}\n\n// ============================================================================\n// Plugin Dependency Types\n// ============================================================================\n\n/**\n * Declares a dependency on another plugin.\n *\n * @category Plugin Development\n * @example\n * ```typescript\n * export class UndoRedoPlugin extends BaseGridPlugin {\n * static readonly dependencies: PluginDependency[] = [\n * { name: 'editing', required: true },\n * ];\n * }\n * ```\n */\nexport interface PluginDependency {\n /**\n * The name of the required plugin (matches the plugin's `name` property).\n * Use string names for loose coupling - avoids static imports.\n */\n name: string;\n\n /**\n * Whether this dependency is required (hard) or optional (soft).\n * - `true`: Plugin cannot function without it. Throws error if missing.\n * - `false`: Plugin works with reduced functionality. Logs info if missing.\n * @default true\n */\n required?: boolean;\n\n /**\n * Human-readable reason for this dependency.\n * Shown in error/info messages to help users understand why.\n * @example \"UndoRedoPlugin needs EditingPlugin to track cell edits\"\n */\n reason?: string;\n}\n\n/**\n * Declares an incompatibility between plugins.\n * When both plugins are loaded, a warning is logged to help users understand the conflict.\n *\n * @category Plugin Development\n */\nexport interface PluginIncompatibility {\n /**\n * The name of the incompatible plugin (matches the plugin's `name` property).\n */\n name: string;\n\n /**\n * Human-readable reason for the incompatibility.\n * Should explain why the plugins conflict and any workarounds.\n * @example \"Responsive card layout does not support row grouping yet\"\n */\n reason: string;\n}\n\n// ============================================================================\n// Plugin Manifest Types\n// ============================================================================\n\n/**\n * Defines a property that a plugin \"owns\" - used for configuration validation.\n * When this property is used without the owning plugin loaded, an error is thrown.\n *\n * @category Plugin Development\n */\nexport interface PluginPropertyDefinition {\n /** The property name on column or grid config */\n property: string;\n /** Whether this is a column-level or config-level property */\n level: 'column' | 'config';\n /** Human-readable description for error messages (e.g., 'the \"editable\" column property') */\n description: string;\n /** Import path hint for error messages (e.g., \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\") */\n importHint?: string;\n /** Custom check for whether property is considered \"used\" (default: truthy value check) */\n isUsed?: (value: unknown) => boolean;\n}\n\n/**\n * A configuration validation rule for detecting invalid/conflicting settings.\n * Plugins declare rules in their manifest; the validator executes them during grid initialization.\n *\n * @category Plugin Development\n * @template TConfig - The plugin's configuration type\n */\nexport interface PluginConfigRule<TConfig = unknown> {\n /** Rule identifier for debugging (e.g., 'selection/range-dblclick') */\n id: string;\n /** Severity: 'error' throws, 'warn' logs warning */\n severity: 'error' | 'warn';\n /** Human-readable message shown when rule is violated */\n message: string;\n /** Predicate returning true if the rule is VIOLATED (i.e., config is invalid) */\n check: (pluginConfig: TConfig) => boolean;\n}\n\n/**\n * Hook names that can have execution priority configured.\n *\n * @category Plugin Development\n */\nexport type HookName =\n | 'processColumns'\n | 'processRows'\n | 'afterRender'\n | 'afterCellRender'\n | 'afterRowRender'\n | 'onCellClick'\n | 'onCellMouseDown'\n | 'onCellMouseMove'\n | 'onCellMouseUp'\n | 'onKeyDown'\n | 'onScroll'\n | 'onScrollRender';\n\n/**\n * Static metadata about a plugin's capabilities and requirements.\n * Declared as a static property on plugin classes.\n *\n * @category Plugin Development\n * @template TConfig - The plugin's configuration type\n *\n * @example\n * ```typescript\n * export class MyPlugin extends BaseGridPlugin<MyConfig> {\n * static override readonly manifest: PluginManifest<MyConfig> = {\n * ownedProperties: [\n * { property: 'myProp', level: 'column', description: 'the \"myProp\" column property' },\n * ],\n * configRules: [\n * { id: 'my-plugin/invalid-combo', severity: 'warn', message: '...', check: (c) => c.a && c.b },\n * ],\n * };\n * readonly name = 'myPlugin';\n * }\n * ```\n */\nexport interface PluginManifest<TConfig = unknown> {\n /**\n * Properties this plugin owns - validated by validate-config.ts.\n * If a user uses one of these properties without loading the plugin, an error is thrown.\n */\n ownedProperties?: PluginPropertyDefinition[];\n\n /**\n * Hook execution priority (higher = later, default 0).\n * Use negative values to run earlier, positive to run later.\n */\n hookPriority?: Partial<Record<HookName, number>>;\n\n /**\n * Configuration validation rules - checked during grid initialization.\n * Rules with severity 'error' throw, 'warn' logs to console.\n */\n configRules?: PluginConfigRule<TConfig>[];\n\n /**\n * Plugins that are incompatible with this plugin.\n * When both plugins are loaded together, a warning is shown.\n *\n * @example\n * ```typescript\n * incompatibleWith: [\n * { name: 'groupingRows', reason: 'Responsive card layout does not support row grouping yet' },\n * ],\n * ```\n */\n incompatibleWith?: PluginIncompatibility[];\n}\n\n/**\n * Abstract base class for all grid plugins.\n *\n * @category Plugin Development\n * @template TConfig - Configuration type for the plugin\n */\nexport abstract class BaseGridPlugin<TConfig = unknown> implements GridPlugin {\n /**\n * Plugin dependencies - declare other plugins this one requires.\n *\n * Dependencies are validated when the plugin is attached.\n * Required dependencies throw an error if missing.\n * Optional dependencies log an info message if missing.\n *\n * @example\n * ```typescript\n * static readonly dependencies: PluginDependency[] = [\n * { name: 'editing', required: true, reason: 'Tracks cell edits for undo/redo' },\n * { name: 'selection', required: false, reason: 'Enables selection-based undo' },\n * ];\n * ```\n */\n static readonly dependencies?: PluginDependency[];\n\n /**\n * Plugin manifest - declares owned properties, config rules, and hook priorities.\n *\n * This is read by the configuration validator to:\n * - Validate that required plugins are loaded when their properties are used\n * - Execute configRules to detect invalid/conflicting settings\n * - Order hook execution based on priority\n *\n * @example\n * ```typescript\n * static override readonly manifest: PluginManifest<MyConfig> = {\n * ownedProperties: [\n * { property: 'myProp', level: 'column', description: 'the \"myProp\" column property' },\n * ],\n * configRules: [\n * { id: 'myPlugin/conflict', severity: 'warn', message: '...', check: (c) => c.a && c.b },\n * ],\n * };\n * ```\n */\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n static readonly manifest?: PluginManifest<any>;\n\n /** Unique plugin identifier (derived from class name by default) */\n abstract readonly name: string;\n\n /**\n * Plugin version - defaults to grid version for built-in plugins.\n * Third-party plugins can override with their own semver.\n */\n readonly version: string = typeof __GRID_VERSION__ !== 'undefined' ? __GRID_VERSION__ : 'dev';\n\n /** CSS styles to inject into the grid's shadow DOM */\n readonly styles?: string;\n\n /** Custom cell renderers keyed by type name */\n readonly cellRenderers?: Record<string, CellRenderer>;\n\n /** Custom header renderers keyed by type name */\n readonly headerRenderers?: Record<string, HeaderRenderer>;\n\n /** Custom cell editors keyed by type name */\n readonly cellEditors?: Record<string, CellEditor>;\n\n /** The grid instance this plugin is attached to */\n protected grid!: GridElement;\n\n /** Plugin configuration - merged with defaults in attach() */\n protected config!: TConfig;\n\n /** User-provided configuration from constructor */\n protected readonly userConfig: Partial<TConfig>;\n\n /**\n * Plugin-level AbortController for event listener cleanup.\n * Created fresh in attach(), aborted in detach().\n * This ensures event listeners are properly cleaned up when plugins are re-attached.\n */\n #abortController?: AbortController;\n\n /**\n * Default configuration - subclasses should override this getter.\n * Note: This must be a getter (not property initializer) for proper inheritance\n * since property initializers run after parent constructor.\n */\n protected get defaultConfig(): Partial<TConfig> {\n return {};\n }\n\n constructor(config: Partial<TConfig> = {}) {\n this.userConfig = config;\n }\n\n /**\n * Called when the plugin is attached to a grid.\n * Override to set up event listeners, initialize state, etc.\n *\n * @example\n * ```ts\n * attach(grid: GridElement): void {\n * super.attach(grid);\n * // Set up document-level listeners with auto-cleanup\n * document.addEventListener('keydown', this.handleEscape, {\n * signal: this.disconnectSignal\n * });\n * }\n * ```\n */\n attach(grid: GridElement): void {\n // Abort any previous abort controller (in case of re-attach without detach)\n this.#abortController?.abort();\n // Create fresh abort controller for this attachment\n this.#abortController = new AbortController();\n\n this.grid = grid;\n // Merge config here (after subclass construction is complete)\n this.config = { ...this.defaultConfig, ...this.userConfig } as TConfig;\n }\n\n /**\n * Called when the plugin is detached from a grid.\n * Override to clean up event listeners, timers, etc.\n *\n * @example\n * ```ts\n * detach(): void {\n * // Clean up any state not handled by disconnectSignal\n * this.selectedRows.clear();\n * this.cache = null;\n * }\n * ```\n */\n detach(): void {\n // Abort the plugin's abort controller to clean up all event listeners\n // registered with { signal: this.disconnectSignal }\n this.#abortController?.abort();\n this.#abortController = undefined;\n // Override in subclass for additional cleanup\n }\n\n /**\n * Called when another plugin is attached to the same grid.\n * Use for inter-plugin coordination, e.g., to integrate with new plugins.\n *\n * @param name - The name of the plugin that was attached\n * @param plugin - The plugin instance (for direct access if needed)\n *\n * @example\n * ```ts\n * onPluginAttached(name: string, plugin: BaseGridPlugin): void {\n * if (name === 'selection') {\n * // Integrate with selection plugin\n * this.selectionPlugin = plugin as SelectionPlugin;\n * }\n * }\n * ```\n */\n onPluginAttached?(name: string, plugin: BaseGridPlugin): void;\n\n /**\n * Called when another plugin is detached from the same grid.\n * Use to clean up inter-plugin references.\n *\n * @param name - The name of the plugin that was detached\n *\n * @example\n * ```ts\n * onPluginDetached(name: string): void {\n * if (name === 'selection') {\n * this.selectionPlugin = undefined;\n * }\n * }\n * ```\n */\n onPluginDetached?(name: string): void;\n\n /**\n * Get another plugin instance from the same grid.\n * Use for inter-plugin communication.\n *\n * @example\n * ```ts\n * const selection = this.getPlugin(SelectionPlugin);\n * if (selection) {\n * const selectedRows = selection.getSelectedRows();\n * }\n * ```\n */\n protected getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.grid?.getPlugin(PluginClass);\n }\n\n /**\n * Emit a custom event from the grid.\n */\n protected emit<T>(eventName: string, detail: T): void {\n this.grid?.dispatchEvent?.(new CustomEvent(eventName, { detail, bubbles: true }));\n }\n\n /**\n * Emit a cancelable custom event from the grid.\n * @returns `true` if the event was cancelled (preventDefault called), `false` otherwise\n */\n protected emitCancelable<T>(eventName: string, detail: T): boolean {\n const event = new CustomEvent(eventName, { detail, bubbles: true, cancelable: true });\n this.grid?.dispatchEvent?.(event);\n return event.defaultPrevented;\n }\n\n /**\n * Request a re-render of the grid.\n */\n protected requestRender(): void {\n this.grid?.requestRender?.();\n }\n\n /**\n * Request a re-render and restore focus styling afterward.\n * Use this when a plugin action (like expand/collapse) triggers a render\n * but needs to maintain keyboard navigation focus.\n */\n protected requestRenderWithFocus(): void {\n this.grid?.requestRenderWithFocus?.();\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Use this instead of requestRender() when only CSS classes need updating.\n */\n protected requestAfterRender(): void {\n this.grid?.requestAfterRender?.();\n }\n\n /**\n * Get the current rows from the grid.\n */\n protected get rows(): any[] {\n return this.grid?.rows ?? [];\n }\n\n /**\n * Get the original unfiltered/unprocessed rows from the grid.\n * Use this when you need all source data regardless of active filters.\n */\n protected get sourceRows(): any[] {\n return this.grid?.sourceRows ?? [];\n }\n\n /**\n * Get the current columns from the grid.\n */\n protected get columns(): ColumnConfig[] {\n return this.grid?.columns ?? [];\n }\n\n /**\n * Get only visible columns from the grid (excludes hidden).\n * Use this for rendering that needs to match the grid template.\n */\n protected get visibleColumns(): ColumnConfig[] {\n return this.grid?._visibleColumns ?? [];\n }\n\n /**\n * Get the grid as an HTMLElement for direct DOM operations.\n * Use sparingly - prefer the typed GridElementRef API when possible.\n *\n * @example\n * ```ts\n * const width = this.gridElement.clientWidth;\n * this.gridElement.classList.add('my-plugin-active');\n * ```\n */\n protected get gridElement(): HTMLElement {\n return this.grid as unknown as HTMLElement;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Use this when adding event listeners that should be cleaned up automatically.\n *\n * Best for:\n * - Document/window-level listeners added in attach()\n * - Listeners on the grid element itself\n * - Any listener that should persist across renders\n *\n * Not needed for:\n * - Listeners on elements created in afterRender() (removed with element)\n *\n * @example\n * element.addEventListener('click', handler, { signal: this.disconnectSignal });\n * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });\n */\n protected get disconnectSignal(): AbortSignal {\n // Return the plugin's own abort signal for proper cleanup on detach/re-attach\n // Falls back to grid's signal if plugin's controller not yet created\n return this.#abortController?.signal ?? this.grid?.disconnectSignal;\n }\n\n /**\n * Get the grid-level icons configuration.\n * Returns merged icons (user config + defaults).\n */\n protected get gridIcons(): typeof DEFAULT_GRID_ICONS {\n const userIcons = this.grid?.gridConfig?.icons ?? {};\n return { ...DEFAULT_GRID_ICONS, ...userIcons };\n }\n\n // #region Animation Helpers\n\n /**\n * Check if animations are enabled at the grid level.\n * Respects gridConfig.animation.mode and the CSS variable set by the grid.\n *\n * Plugins should use this to skip animations when:\n * - Animation mode is 'off' or `false`\n * - User prefers reduced motion and mode is 'reduced-motion' (default)\n *\n * @example\n * ```ts\n * private get animationStyle(): 'slide' | 'fade' | false {\n * if (!this.isAnimationEnabled) return false;\n * return this.config.animation ?? 'slide';\n * }\n * ```\n */\n protected get isAnimationEnabled(): boolean {\n const mode = this.grid?.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n // Explicit off = disabled\n if (mode === false || mode === 'off') return false;\n\n // Explicit on = always enabled\n if (mode === true || mode === 'on') return true;\n\n // reduced-motion: check CSS variable (set by grid based on media query)\n const host = this.gridElement;\n if (host) {\n const enabled = getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim();\n return enabled !== '0';\n }\n\n return true; // Default to enabled\n }\n\n /**\n * Get the animation duration in milliseconds from CSS variable.\n * Falls back to 200ms if not set.\n *\n * Plugins can use this for their animation timing to stay consistent\n * with the grid-level animation.duration setting.\n *\n * @example\n * ```ts\n * element.animate(keyframes, { duration: this.animationDuration });\n * ```\n */\n protected get animationDuration(): number {\n const host = this.gridElement;\n if (host) {\n const durationStr = getComputedStyle(host).getPropertyValue('--tbw-animation-duration').trim();\n const parsed = parseInt(durationStr, 10);\n if (!isNaN(parsed)) return parsed;\n }\n return 200; // Default\n }\n\n // #endregion\n\n /**\n * Resolve an icon value to string or HTMLElement.\n * Checks plugin config first, then grid-level icons, then defaults.\n *\n * @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')\n * @param pluginOverride - Optional plugin-level override\n * @returns The resolved icon value\n */\n protected resolveIcon(iconKey: keyof typeof DEFAULT_GRID_ICONS, pluginOverride?: IconValue): IconValue {\n // Plugin override takes precedence\n if (pluginOverride !== undefined) {\n return pluginOverride;\n }\n // Then grid-level config\n return this.gridIcons[iconKey];\n }\n\n /**\n * Set an icon value on an element.\n * Handles both string (text/HTML) and HTMLElement values.\n *\n * @param element - The element to set the icon on\n * @param icon - The icon value (string or HTMLElement)\n */\n protected setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.innerHTML = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n }\n\n /**\n * Log a warning message.\n */\n protected warn(message: string): void {\n console.warn(`[tbw-grid:${this.name}] ${message}`);\n }\n\n // #region Lifecycle Hooks\n\n /**\n * Transform rows before rendering.\n * Called during each render cycle before rows are rendered to the DOM.\n * Use this to filter, sort, or add computed properties to rows.\n *\n * @param rows - The current rows array (readonly to encourage returning a new array)\n * @returns The modified rows array to render\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Filter out hidden rows\n * return rows.filter(row => !row._hidden);\n * }\n * ```\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Add computed properties\n * return rows.map(row => ({\n * ...row,\n * _fullName: `${row.firstName} ${row.lastName}`\n * }));\n * }\n * ```\n */\n processRows?(rows: readonly any[]): any[];\n\n /**\n * Transform columns before rendering.\n * Called during each render cycle before column headers and cells are rendered.\n * Use this to add, remove, or modify column definitions.\n *\n * @param columns - The current columns array (readonly to encourage returning a new array)\n * @returns The modified columns array to render\n *\n * @example\n * ```ts\n * processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n * // Add a selection checkbox column\n * return [\n * { field: '_select', header: '', width: 40 },\n * ...columns\n * ];\n * }\n * ```\n */\n processColumns?(columns: readonly ColumnConfig[]): ColumnConfig[];\n\n /**\n * Called before each render cycle begins.\n * Use this to prepare state or cache values needed during rendering.\n *\n * @example\n * ```ts\n * beforeRender(): void {\n * this.visibleRowCount = this.calculateVisibleRows();\n * }\n * ```\n */\n beforeRender?(): void;\n\n /**\n * Called after each render cycle completes.\n * Use this for DOM manipulation, adding event listeners to rendered elements,\n * or applying visual effects like selection highlights.\n *\n * @example\n * ```ts\n * afterRender(): void {\n * // Apply selection styling to rendered rows\n * const rows = this.shadowRoot?.querySelectorAll('.data-row');\n * rows?.forEach((row, i) => {\n * row.classList.toggle('selected', this.selectedRows.has(i));\n * });\n * }\n * ```\n */\n afterRender?(): void;\n\n /**\n * Called after each cell is rendered.\n * This hook is more efficient than `afterRender()` for cell-level modifications\n * because you receive the cell context directly - no DOM queries needed.\n *\n * Use cases:\n * - Adding selection/highlight classes to specific cells\n * - Injecting badges or decorations\n * - Applying conditional styling based on cell value\n *\n * Performance note: Called for every visible cell during render. Keep implementation fast.\n * This hook is also called during scroll when cells are recycled.\n *\n * @param context - The cell render context with row, column, value, and elements\n *\n * @example\n * ```ts\n * afterCellRender(context: AfterCellRenderContext): void {\n * // Add selection class without DOM queries\n * if (this.isSelected(context.rowIndex, context.colIndex)) {\n * context.cellElement.classList.add('selected');\n * }\n *\n * // Add validation error styling\n * if (this.hasError(context.row, context.column.field)) {\n * context.cellElement.classList.add('has-error');\n * }\n * }\n * ```\n */\n afterCellRender?(context: AfterCellRenderContext): void;\n\n /**\n * Called after a row is fully rendered (all cells complete).\n * Use this for row-level decorations, styling, or ARIA attributes.\n *\n * Common use cases:\n * - Adding selection classes to entire rows (row-focus, selected)\n * - Setting row-level ARIA attributes\n * - Applying row validation highlighting\n * - Tree indentation styling\n *\n * Performance note: Called for every visible row during render. Keep implementation fast.\n * This hook is also called during scroll when rows are recycled.\n *\n * @param context - The row render context with row data and element\n *\n * @example\n * ```ts\n * afterRowRender(context: AfterRowRenderContext): void {\n * // Add row selection class without DOM queries\n * if (this.isRowSelected(context.rowIndex)) {\n * context.rowElement.classList.add('selected', 'row-focus');\n * }\n *\n * // Add validation error styling\n * if (this.rowHasErrors(context.row)) {\n * context.rowElement.classList.add('has-errors');\n * }\n * }\n * ```\n */\n afterRowRender?(context: AfterRowRenderContext): void;\n\n /**\n * Called after scroll-triggered row rendering completes.\n * This is a lightweight hook for applying visual state to recycled DOM elements.\n * Use this instead of afterRender when you need to reapply styling during scroll.\n *\n * Performance note: This is called frequently during scroll. Keep implementation fast.\n *\n * @example\n * ```ts\n * onScrollRender(): void {\n * // Reapply selection state to visible cells\n * this.applySelectionToVisibleCells();\n * }\n * ```\n */\n onScrollRender?(): void;\n\n /**\n * Return extra height contributed by this plugin (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations for virtualization.\n *\n * @returns Total extra height in pixels\n *\n * @example\n * ```ts\n * getExtraHeight(): number {\n * return this.expandedRows.size * this.detailHeight;\n * }\n * ```\n */\n getExtraHeight?(): number;\n\n /**\n * Return extra height that appears before a given row index.\n * Used by virtualization to correctly calculate scroll positions when\n * there's variable height content (like expanded detail rows) above the viewport.\n *\n * @param beforeRowIndex - The row index to calculate extra height before\n * @returns Extra height in pixels that appears before this row\n *\n * @example\n * ```ts\n * getExtraHeightBefore(beforeRowIndex: number): number {\n * let height = 0;\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * if (expandedRowIndex < beforeRowIndex) {\n * height += this.getDetailHeight(expandedRowIndex);\n * }\n * }\n * return height;\n * }\n * ```\n */\n getExtraHeightBefore?(beforeRowIndex: number): number;\n\n /**\n * Adjust the virtualization start index to render additional rows before the visible range.\n * Use this when expanded content (like detail rows) needs its parent row to remain rendered\n * even when the parent row itself has scrolled above the viewport.\n *\n * @param start - The calculated start row index\n * @param scrollTop - The current scroll position\n * @param rowHeight - The height of a single row\n * @returns The adjusted start index (lower than or equal to original start)\n *\n * @example\n * ```ts\n * adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n * // If row 5 is expanded and scrolled partially, keep it rendered\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * const expandedRowTop = expandedRowIndex * rowHeight;\n * const expandedRowBottom = expandedRowTop + rowHeight + this.detailHeight;\n * if (expandedRowBottom > scrollTop && expandedRowIndex < start) {\n * return expandedRowIndex;\n * }\n * }\n * return start;\n * }\n * ```\n */\n adjustVirtualStart?(start: number, scrollTop: number, rowHeight: number): number;\n\n /**\n * Render a custom row, bypassing the default row rendering.\n * Use this for special row types like group headers, detail rows, or footers.\n *\n * @param row - The row data object\n * @param rowEl - The row DOM element to render into\n * @param rowIndex - The index of the row in the data array\n * @returns `true` if the plugin handled rendering (prevents default), `false`/`void` for default rendering\n *\n * @example\n * ```ts\n * renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void {\n * if (row._isGroupHeader) {\n * rowEl.innerHTML = `<div class=\"group-header\">${row._groupLabel}</div>`;\n * return true; // Handled - skip default rendering\n * }\n * // Return void to let default rendering proceed\n * }\n * ```\n */\n renderRow?(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void;\n\n // #endregion\n\n // #region Inter-Plugin Communication\n\n /**\n * Handle queries from other plugins.\n * This is the generic mechanism for inter-plugin communication.\n * Plugins can respond to well-known query types or define their own.\n *\n * @param query - The query object with type and context\n * @returns Query-specific response, or undefined if not handling this query\n *\n * @example\n * ```ts\n * onPluginQuery(query: PluginQuery): unknown {\n * switch (query.type) {\n * case PLUGIN_QUERIES.CAN_MOVE_COLUMN:\n * // Prevent moving pinned columns\n * const column = query.context as ColumnConfig;\n * if (column.sticky === 'left' || column.sticky === 'right') {\n * return false;\n * }\n * break;\n * case PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS:\n * const params = query.context as ContextMenuParams;\n * return [{ id: 'my-action', label: 'My Action', action: () => {} }];\n * }\n * }\n * ```\n */\n onPluginQuery?(query: PluginQuery): unknown;\n\n // #endregion\n\n // #region Interaction Hooks\n\n /**\n * Handle keyboard events on the grid.\n * Called when a key is pressed while the grid or a cell has focus.\n *\n * @param event - The native KeyboardEvent\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onKeyDown(event: KeyboardEvent): boolean | void {\n * // Handle Ctrl+A for select all\n * if (event.ctrlKey && event.key === 'a') {\n * this.selectAllRows();\n * return true; // Prevent default browser select-all\n * }\n * }\n * ```\n */\n onKeyDown?(event: KeyboardEvent): boolean | void;\n\n /**\n * Handle cell click events.\n * Called when a data cell is clicked (not headers).\n *\n * @param event - Cell click event with row/column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onCellClick(event: CellClickEvent): boolean | void {\n * if (event.field === '_select') {\n * this.toggleRowSelection(event.rowIndex);\n * return true; // Handled\n * }\n * }\n * ```\n */\n onCellClick?(event: CellClickEvent): boolean | void;\n\n /**\n * Handle row click events.\n * Called when any part of a data row is clicked.\n * Note: This is called in addition to onCellClick, not instead of.\n *\n * @param event - Row click event with row context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onRowClick(event: RowClickEvent): boolean | void {\n * if (this.config.mode === 'row') {\n * this.selectRow(event.rowIndex, event.originalEvent);\n * return true;\n * }\n * }\n * ```\n */\n onRowClick?(event: RowClickEvent): boolean | void;\n\n /**\n * Handle header click events.\n * Called when a column header is clicked. Commonly used for sorting.\n *\n * @param event - Header click event with column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onHeaderClick(event: HeaderClickEvent): boolean | void {\n * if (event.column.sortable !== false) {\n * this.toggleSort(event.field);\n * return true;\n * }\n * }\n * ```\n */\n onHeaderClick?(event: HeaderClickEvent): boolean | void;\n\n /**\n * Handle scroll events on the grid viewport.\n * Called during scrolling. Note: This may be called frequently; debounce if needed.\n *\n * @param event - Scroll event with scroll position and viewport dimensions\n *\n * @example\n * ```ts\n * onScroll(event: ScrollEvent): void {\n * // Update sticky column positions\n * this.updateStickyPositions(event.scrollLeft);\n * }\n * ```\n */\n onScroll?(event: ScrollEvent): void;\n\n /**\n * Handle cell mousedown events.\n * Used for initiating drag operations like range selection or column resize.\n *\n * @param event - Mouse event with cell context\n * @returns `true` to indicate drag started (prevents text selection), `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseDown(event: CellMouseEvent): boolean | void {\n * if (event.rowIndex !== undefined && this.config.mode === 'range') {\n * this.startDragSelection(event.rowIndex, event.colIndex);\n * return true; // Prevent text selection\n * }\n * }\n * ```\n */\n onCellMouseDown?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mousemove events during drag operations.\n * Only called when a drag is in progress (after mousedown returned true).\n *\n * @param event - Mouse event with current cell context\n * @returns `true` to continue handling the drag, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseMove(event: CellMouseEvent): boolean | void {\n * if (this.isDragging && event.rowIndex !== undefined) {\n * this.extendSelection(event.rowIndex, event.colIndex);\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseMove?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mouseup events to end drag operations.\n *\n * @param event - Mouse event with final cell context\n * @returns `true` if drag was finalized, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseUp(event: CellMouseEvent): boolean | void {\n * if (this.isDragging) {\n * this.finalizeDragSelection();\n * this.isDragging = false;\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseUp?(event: CellMouseEvent): boolean | void;\n\n // Note: Context menu items are now provided via onPluginQuery with PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS\n // This keeps the core decoupled from the context-menu plugin specifics.\n\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Contribute plugin-specific state for a column.\n * Called by the grid when collecting column state for serialization.\n * Plugins can add their own properties to the column state.\n *\n * @param field - The field name of the column\n * @returns Partial column state with plugin-specific properties, or undefined if no state to contribute\n *\n * @example\n * ```ts\n * getColumnState(field: string): Partial<ColumnState> | undefined {\n * const filterModel = this.filterModels.get(field);\n * if (filterModel) {\n * // Uses module augmentation to add filter property to ColumnState\n * return { filter: filterModel } as Partial<ColumnState>;\n * }\n * return undefined;\n * }\n * ```\n */\n getColumnState?(field: string): Partial<ColumnState> | undefined;\n\n /**\n * Apply plugin-specific state to a column.\n * Called by the grid when restoring column state from serialized data.\n * Plugins should restore their internal state based on the provided state.\n *\n * @param field - The field name of the column\n * @param state - The column state to apply (may contain plugin-specific properties)\n *\n * @example\n * ```ts\n * applyColumnState(field: string, state: ColumnState): void {\n * // Check for filter property added via module augmentation\n * const filter = (state as any).filter;\n * if (filter) {\n * this.filterModels.set(field, filter);\n * this.applyFilter();\n * }\n * }\n * ```\n */\n applyColumnState?(field: string, state: ColumnState): void;\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Report horizontal scroll boundary offsets for this plugin.\n * Plugins that obscure part of the scroll area (e.g., pinned/sticky columns)\n * should return how much space they occupy on each side.\n * The keyboard navigation uses this to ensure focused cells are fully visible.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Object with left/right pixel offsets and optional skipScroll flag, or undefined if plugin has no offsets\n *\n * @example\n * ```ts\n * getHorizontalScrollOffsets(rowEl?: HTMLElement, focusedCell?: HTMLElement): { left: number; right: number; skipScroll?: boolean } | undefined {\n * // Calculate total width of left-pinned columns\n * const leftCells = rowEl?.querySelectorAll('.sticky-left') ?? [];\n * let left = 0;\n * leftCells.forEach(el => { left += (el as HTMLElement).offsetWidth; });\n * // Skip scroll if focused cell is pinned (always visible)\n * const skipScroll = focusedCell?.classList.contains('sticky-left');\n * return { left, right: 0, skipScroll };\n * }\n * ```\n */\n getHorizontalScrollOffsets?(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } | undefined;\n\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Register a tool panel for this plugin.\n * Return undefined if plugin has no tool panel.\n * The shell will create a toolbar toggle button and render the panel content\n * when the user opens the panel.\n *\n * @returns Tool panel definition, or undefined if plugin has no panel\n *\n * @example\n * ```ts\n * getToolPanel(): ToolPanelDefinition | undefined {\n * return {\n * id: 'columns',\n * title: 'Columns',\n * icon: '☰',\n * tooltip: 'Show/hide columns',\n * order: 10,\n * render: (container) => {\n * this.renderColumnList(container);\n * return () => this.cleanup();\n * },\n * };\n * }\n * ```\n */\n getToolPanel?(): ToolPanelDefinition | undefined;\n\n /**\n * Register content for the shell header center section.\n * Return undefined if plugin has no header content.\n * Examples: search input, selection summary, status indicators.\n *\n * @returns Header content definition, or undefined if plugin has no header content\n *\n * @example\n * ```ts\n * getHeaderContent(): HeaderContentDefinition | undefined {\n * return {\n * id: 'quick-filter',\n * order: 10,\n * render: (container) => {\n * const input = document.createElement('input');\n * input.type = 'text';\n * input.placeholder = 'Search...';\n * input.addEventListener('input', this.handleInput);\n * container.appendChild(input);\n * return () => input.removeEventListener('input', this.handleInput);\n * },\n * };\n * }\n * ```\n */\n getHeaderContent?(): HeaderContentDefinition | undefined;\n\n // #endregion\n}\n","/**\n * Grid DOM Constants\n *\n * Centralized constants for CSS classes, data attributes, and selectors\n * used throughout the grid. Use these instead of magic strings.\n *\n * Note: Some constants here are used by plugins but defined in core because:\n * 1. The core CSS needs to style these elements (e.g., sticky positioning)\n * 2. Multiple plugins may share the same class names\n *\n * Plugins can define their own additional constants and export them.\n * See each plugin's index.ts for plugin-specific exports.\n */\n\n// #region CSS Classes\n\n/**\n * CSS class names used in the grid's shadow DOM.\n * Use these when adding/removing classes or querying elements.\n *\n * Classes are organized by:\n * - Core: Used by the grid core for structure and basic functionality\n * - Shared: Used by multiple features/plugins, styled by core CSS\n *\n * @category Plugin Development\n */\nexport const GridClasses = {\n // ─── Core Structure ───────────────────────────────────────────────\n ROOT: 'tbw-grid-root',\n HEADER: 'header',\n HEADER_ROW: 'header-row',\n HEADER_CELL: 'header-cell',\n\n // Body structure\n ROWS_VIEWPORT: 'rows-viewport',\n ROWS_SPACER: 'rows-spacer',\n ROWS_CONTAINER: 'rows',\n\n // Row elements\n DATA_ROW: 'data-row',\n GROUP_ROW: 'group-row',\n\n // Cell elements\n DATA_CELL: 'data-cell',\n\n // ─── Core States ──────────────────────────────────────────────────\n SELECTED: 'selected',\n FOCUSED: 'focused',\n EDITING: 'editing',\n EXPANDED: 'expanded',\n COLLAPSED: 'collapsed',\n DRAGGING: 'dragging',\n RESIZING: 'resizing',\n\n // Sorting (core feature)\n SORTABLE: 'sortable',\n SORTED_ASC: 'sorted-asc',\n SORTED_DESC: 'sorted-desc',\n\n // Visibility\n HIDDEN: 'hidden',\n\n // ─── Shared Classes (used by plugins, styled by core CSS) ────────\n // These are defined here because core CSS provides the base styling.\n // Plugins apply these classes; core CSS defines how they look.\n\n // Sticky positioning (PinnedColumnsPlugin applies, core CSS styles)\n STICKY_LEFT: 'sticky-left',\n STICKY_RIGHT: 'sticky-right',\n\n // Pinned rows (PinnedRowsPlugin applies, core CSS styles)\n PINNED_TOP: 'pinned-top',\n PINNED_BOTTOM: 'pinned-bottom',\n\n // Tree structure (TreePlugin applies, core CSS styles)\n TREE_TOGGLE: 'tree-toggle',\n TREE_INDENT: 'tree-indent',\n\n // Grouping (GroupingRowsPlugin applies, core CSS styles)\n GROUP_TOGGLE: 'group-toggle',\n GROUP_LABEL: 'group-label',\n GROUP_COUNT: 'group-count',\n\n // Selection (SelectionPlugin applies, core CSS styles)\n RANGE_SELECTION: 'range-selection',\n SELECTION_OVERLAY: 'selection-overlay',\n} as const;\n\n// #endregion\n\n// #region Data Attributes\n\n/**\n * Data attribute names used on grid elements.\n * Use these when getting/setting data attributes.\n *\n * @category Plugin Development\n */\nexport const GridDataAttrs = {\n // ─── Core Attributes ──────────────────────────────────────────────\n ROW_INDEX: 'data-row-index',\n COL_INDEX: 'data-col-index',\n FIELD: 'data-field',\n\n // ─── Shared Attributes (used by plugins) ──────────────────────────\n GROUP_KEY: 'data-group-key', // GroupingRowsPlugin\n TREE_LEVEL: 'data-tree-level', // TreePlugin\n STICKY: 'data-sticky', // PinnedColumnsPlugin\n} as const;\n\n// #endregion\n\n// #region Selectors\n\n/**\n * Common CSS selectors for querying grid elements.\n * Built from the class constants for consistency.\n *\n * @category Plugin Development\n */\nexport const GridSelectors = {\n ROOT: `.${GridClasses.ROOT}`,\n HEADER: `.${GridClasses.HEADER}`,\n HEADER_ROW: `.${GridClasses.HEADER_ROW}`,\n HEADER_CELL: `.${GridClasses.HEADER_CELL}`,\n ROWS_VIEWPORT: `.${GridClasses.ROWS_VIEWPORT}`,\n ROWS_CONTAINER: `.${GridClasses.ROWS_CONTAINER}`,\n DATA_ROW: `.${GridClasses.DATA_ROW}`,\n DATA_CELL: `.${GridClasses.DATA_CELL}`,\n GROUP_ROW: `.${GridClasses.GROUP_ROW}`,\n\n // By data attribute\n ROW_BY_INDEX: (index: number) => `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${index}\"]`,\n CELL_BY_FIELD: (field: string) => `.${GridClasses.DATA_CELL}[${GridDataAttrs.FIELD}=\"${field}\"]`,\n CELL_AT: (row: number, col: number) =>\n `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${row}\"] .${GridClasses.DATA_CELL}[${GridDataAttrs.COL_INDEX}=\"${col}\"]`,\n\n // State selectors\n SELECTED_ROWS: `.${GridClasses.DATA_ROW}.${GridClasses.SELECTED}`,\n EDITING_CELL: `.${GridClasses.DATA_CELL}.${GridClasses.EDITING}`,\n} as const;\n\n// #endregion\n\n// #region CSS Custom Properties\n\n/**\n * CSS custom property names for theming.\n * Use these when programmatically setting styles.\n *\n * @category Plugin Development\n */\nexport const GridCSSVars = {\n // Colors\n COLOR_BG: '--tbw-color-bg',\n COLOR_FG: '--tbw-color-fg',\n COLOR_FG_MUTED: '--tbw-color-fg-muted',\n COLOR_BORDER: '--tbw-color-border',\n COLOR_ACCENT: '--tbw-color-accent',\n COLOR_HEADER_BG: '--tbw-color-header-bg',\n COLOR_HEADER_FG: '--tbw-color-header-fg',\n COLOR_SELECTION: '--tbw-color-selection',\n COLOR_ROW_HOVER: '--tbw-color-row-hover',\n COLOR_ROW_ALT: '--tbw-color-row-alt',\n\n // Sizing\n ROW_HEIGHT: '--tbw-row-height',\n HEADER_HEIGHT: '--tbw-header-height',\n CELL_PADDING: '--tbw-cell-padding',\n\n // Typography\n FONT_FAMILY: '--tbw-font-family',\n FONT_SIZE: '--tbw-font-size',\n\n // Borders\n BORDER_RADIUS: '--tbw-border-radius',\n FOCUS_OUTLINE: '--tbw-focus-outline',\n} as const;\n\n// #endregion\n\n// Type helpers\nexport type GridClassName = (typeof GridClasses)[keyof typeof GridClasses];\nexport type GridDataAttr = (typeof GridDataAttrs)[keyof typeof GridDataAttrs];\nexport type GridCSSVar = (typeof GridCSSVars)[keyof typeof GridCSSVars];\n","/**\n * @toolbox-web/grid - A high-performance, framework-agnostic data grid web component.\n *\n * This is the public API surface. Only symbols exported here are considered stable.\n *\n * @packageDocumentation\n * @module Core\n */\n\n// #region Public API surface - only export what consumers need\nexport { DataGridElement, DataGridElement as GridElement } from './lib/core/grid';\n\n/**\n * Clean type alias for the grid element.\n * Use this in place of `DataGridElement<T>` for more concise code.\n *\n * @example\n * ```typescript\n * import { TbwGrid, createGrid } from '@toolbox-web/grid';\n *\n * const grid: TbwGrid<Employee> = createGrid();\n * grid.rows = employees;\n * ```\n */\nexport type { DataGridElement as TbwGrid } from './lib/core/grid';\n\n// Import types needed for factory functions\nimport type { DataGridElement } from './lib/core/grid';\nimport type { GridConfig } from './lib/core/types';\n\n// #region Factory Functions\n/**\n * Create a new typed grid element programmatically.\n *\n * This avoids the need to cast when creating grids in TypeScript:\n * ```typescript\n * // Before: manual cast required\n * const grid = document.createElement('tbw-grid') as DataGridElement<Employee>;\n *\n * // After: fully typed\n * const grid = createGrid<Employee>({\n * columns: [{ field: 'name' }],\n * plugins: [new SelectionPlugin()],\n * });\n * grid.rows = employees; // ✓ Typed!\n * ```\n *\n * @param config - Optional initial grid configuration\n * @returns A typed DataGridElement instance\n */\nexport function createGrid<TRow = unknown>(config?: Partial<GridConfig<TRow>>): DataGridElement<TRow> {\n const grid = document.createElement('tbw-grid') as DataGridElement<TRow>;\n if (config) {\n grid.gridConfig = config as GridConfig<TRow>;\n }\n return grid;\n}\n\n/**\n * Query an existing grid element from the DOM with proper typing.\n *\n * This avoids the need to cast when querying grids:\n * ```typescript\n * // Before: manual cast required\n * const grid = document.querySelector('tbw-grid') as DataGridElement<Employee>;\n *\n * // After: fully typed\n * const grid = queryGrid<Employee>('#my-grid');\n * if (grid) {\n * grid.rows = employees; // ✓ Typed!\n * }\n * ```\n *\n * @param selector - CSS selector to find the grid element\n * @param parent - Parent node to search within (defaults to document)\n * @returns The typed grid element or null if not found\n */\nexport function queryGrid<TRow = unknown>(\n selector: string,\n parent: ParentNode = document,\n): DataGridElement<TRow> | null {\n return parent.querySelector(selector) as DataGridElement<TRow> | null;\n}\n// #endregion\n\n/**\n * Event name constants for DataGrid (public API).\n *\n * @category Events\n */\nexport const DGEvents = {\n /** Emitted by core after any data mutation */\n CELL_CHANGE: 'cell-change',\n CELL_COMMIT: 'cell-commit',\n ROW_COMMIT: 'row-commit',\n CHANGED_ROWS_RESET: 'changed-rows-reset',\n MOUNT_EXTERNAL_VIEW: 'mount-external-view',\n MOUNT_EXTERNAL_EDITOR: 'mount-external-editor',\n SORT_CHANGE: 'sort-change',\n COLUMN_RESIZE: 'column-resize',\n ACTIVATE_CELL: 'activate-cell',\n /** Unified cell activation event (keyboard or pointer) */\n CELL_ACTIVATE: 'cell-activate',\n GROUP_TOGGLE: 'group-toggle',\n COLUMN_STATE_CHANGE: 'column-state-change',\n} as const;\n\n/**\n * Union type of all DataGrid event names.\n *\n * @category Events\n */\nexport type DGEventName = (typeof DGEvents)[keyof typeof DGEvents];\n\n/**\n * Plugin event constants (mirrors DGEvents pattern).\n *\n * @category Events\n */\nexport const PluginEvents = {\n // Selection plugin\n SELECTION_CHANGE: 'selection-change',\n // Tree plugin\n TREE_EXPAND: 'tree-expand',\n // Filtering plugin\n FILTER_CHANGE: 'filter-change',\n // Sorting plugin\n SORT_MODEL_CHANGE: 'sort-model-change',\n // Export plugin\n EXPORT_START: 'export-start',\n EXPORT_COMPLETE: 'export-complete',\n // Clipboard plugin\n CLIPBOARD_COPY: 'clipboard-copy',\n CLIPBOARD_PASTE: 'clipboard-paste',\n // Context menu plugin\n CONTEXT_MENU_OPEN: 'context-menu-open',\n CONTEXT_MENU_CLOSE: 'context-menu-close',\n // Undo/Redo plugin\n HISTORY_CHANGE: 'history-change',\n // Server-side plugin\n SERVER_LOADING: 'server-loading',\n SERVER_ERROR: 'server-error',\n // Visibility plugin\n COLUMN_VISIBILITY_CHANGE: 'column-visibility-change',\n // Reorder plugin\n COLUMN_REORDER: 'column-reorder',\n // Master-detail plugin\n DETAIL_EXPAND: 'detail-expand',\n // Grouping rows plugin\n GROUP_EXPAND: 'group-expand',\n} as const;\n\n/**\n * Union type of all plugin event names.\n *\n * @category Events\n */\nexport type PluginEventName = (typeof PluginEvents)[keyof typeof PluginEvents];\n\n// Public type exports\nexport type {\n /** @deprecated Use CellActivateDetail instead */\n ActivateCellDetail,\n AggregatorRef,\n // Animation types\n AnimationConfig,\n AnimationMode,\n AnimationStyle,\n BaseColumnConfig,\n // Event detail types\n CellActivateDetail,\n CellActivateTrigger,\n CellChangeDetail,\n CellClickDetail,\n CellCommitDetail,\n CellRenderContext,\n ChangedRowsResetDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnEditorContext,\n // Column features\n ColumnEditorSpec,\n ColumnResizeDetail,\n // Column state types\n ColumnSortState,\n ColumnState,\n // Type-level defaults\n ColumnType,\n ColumnViewRenderer,\n DataGridCustomEvent,\n DataGridElement as DataGridElementInterface,\n DataGridEventMap,\n ExpandCollapseAnimation,\n ExternalMountEditorDetail,\n ExternalMountViewDetail,\n FitMode,\n // Framework adapter interface\n FrameworkAdapter,\n GridColumnState,\n // Core configuration types\n GridConfig,\n // Icons\n GridIcons,\n // Plugin interface (minimal shape for type-checking)\n GridPlugin,\n // Shell types\n HeaderContentDefinition,\n IconValue,\n // Inference types\n InferredColumnResult,\n PrimitiveColumnType,\n // Public interface\n PublicGrid,\n // Row animation type\n RowAnimationType,\n RowClickDetail,\n RowCommitDetail,\n // Grouping & Footer types\n RowGroupRenderConfig,\n // Data update management\n RowUpdate,\n ShellConfig,\n ShellHeaderConfig,\n SortChangeDetail,\n // Sorting types\n SortHandler,\n SortState,\n ToolbarContentDefinition,\n ToolPanelConfig,\n ToolPanelDefinition,\n TypeDefault,\n UpdateSource,\n} from './lib/core/types';\n\n// Re-export FitModeEnum for runtime usage\nexport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS, FitModeEnum } from './lib/core/types';\n\n// Re-export sorting utilities for custom sort handlers\nexport { builtInSort, defaultComparator } from './lib/core/internal/sorting';\n// #endregion\n\n// #region Plugin Development\n// Plugin base class - for creating custom plugins\nexport { BaseGridPlugin, PLUGIN_QUERIES } from './lib/core/plugin';\nexport type { AfterCellRenderContext, AfterRowRenderContext, PluginQuery } from './lib/core/plugin';\n\n// DOM constants - for querying grid elements and styling\nexport { GridClasses, GridCSSVars, GridDataAttrs, GridSelectors } from './lib/core/constants';\nexport type { GridClassName, GridCSSVar, GridDataAttr } from './lib/core/constants';\n\n// Note: Plugin-specific types (SelectionConfig, FilterConfig, etc.) are exported\n// from their respective plugin entry points:\n// import { SelectionPlugin, type SelectionConfig } from '@toolbox-web/grid/plugins/selection';\n// import { FilteringPlugin, type FilterConfig } from '@toolbox-web/grid/plugins/filtering';\n// Or import all plugins + types from: '@toolbox-web/grid/all'\n// #endregion\n\n// #region Advanced Types for Custom Plugins & Enterprise Extensions\n/**\n * Internal types for advanced users building custom plugins or enterprise extensions.\n *\n * These types provide access to grid internals that may be needed for deep customization.\n * While not part of the \"stable\" API, they are exported for power users who need them.\n *\n * @remarks\n * Use with caution - these types expose internal implementation details.\n * The underscore-prefixed members they reference are considered less stable\n * than the public API surface.\n *\n * @example\n * ```typescript\n * import { BaseGridPlugin } from '@toolbox-web/grid';\n * import type { InternalGrid, ColumnInternal } from '@toolbox-web/grid';\n *\n * export class MyPlugin extends BaseGridPlugin<MyConfig> {\n * afterRender(): void {\n * // Access grid internals with proper typing\n * const grid = this.grid as InternalGrid;\n * const columns = grid._columns as ColumnInternal[];\n * // ...\n * }\n * }\n * ```\n */\n\n/**\n * Column configuration with internal cache properties.\n * Extends the public ColumnConfig with compiled template caches (__compiledView, __viewTemplate, etc.)\n * @internal\n */\nexport type { ColumnInternal } from './lib/core/types';\n\n/**\n * Compiled template function with __blocked property for error handling.\n * @internal\n */\nexport type { CompiledViewFunction } from './lib/core/types';\n\n/**\n * Full internal grid interface extending PublicGrid with internal state.\n * Provides typed access to _columns, _rows, virtualization state, etc.\n * @internal\n */\nexport type { InternalGrid } from './lib/core/types';\n\n/**\n * Cell context for renderer/editor operations.\n * @internal\n */\nexport type { CellContext } from './lib/core/types';\n\n/**\n * Editor execution context extending CellContext with commit/cancel functions.\n * @internal\n */\nexport type { EditorExecContext } from './lib/core/types';\n\n/**\n * Template evaluation context for dynamic templates.\n * @internal\n */\nexport type { EvalContext } from './lib/core/types';\n\n/**\n * Column resize controller interface.\n * @internal\n */\nexport type { ResizeController } from './lib/core/types';\n\n/**\n * Row virtualization state interface.\n * @internal\n */\nexport type { VirtualState } from './lib/core/types';\n\n/**\n * Row element with internal editing state cache.\n * Used for tracking editing cell count without querySelector.\n * @internal\n */\nexport type { RowElementInternal } from './lib/core/types';\n\n/**\n * Union type for input-like elements that have a `value` property.\n * Covers standard form elements and custom elements with value semantics.\n * @internal\n */\nexport type { InputLikeElement } from './lib/core/types';\n\n/**\n * Utility type to safely cast a grid element to InternalGrid for plugin use.\n *\n * @example\n * ```typescript\n * import type { AsInternalGrid, InternalGrid } from '@toolbox-web/grid';\n *\n * class MyPlugin extends BaseGridPlugin {\n * get internalGrid(): InternalGrid {\n * return this.grid as AsInternalGrid;\n * }\n * }\n * ```\n * @internal\n */\nexport type AsInternalGrid<T = unknown> = import('./lib/core/types').InternalGrid<T>;\n\n/**\n * Render phase enum for debugging and understanding the render pipeline.\n * Higher phases include all lower phase work.\n */\nexport { RenderPhase } from './lib/core/internal/render-scheduler';\n// #endregion\n","/**\n * Aggregators Core Registry\n *\n * Provides a central registry for aggregator functions.\n * Built-in aggregators are provided by default.\n * Plugins can register additional aggregators.\n *\n * The registry is exposed as a singleton object that can be accessed:\n * - By ES module imports: import { aggregatorRegistry } from '@toolbox-web/grid'\n * - By UMD/CDN: TbwGrid.aggregatorRegistry\n * - By plugins via context: ctx.aggregatorRegistry\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type AggregatorFn = (rows: any[], field: string, column?: any) => any;\nexport type AggregatorRef = string | AggregatorFn;\n\n/** Built-in aggregator functions */\nconst builtInAggregators: Record<string, AggregatorFn> = {\n sum: (rows, field) => rows.reduce((acc, row) => acc + (Number(row[field]) || 0), 0),\n avg: (rows, field) => {\n const sum = rows.reduce((acc, row) => acc + (Number(row[field]) || 0), 0);\n return rows.length ? sum / rows.length : 0;\n },\n count: (rows) => rows.length,\n min: (rows, field) => Math.min(...rows.map((r) => Number(r[field]) || Infinity)),\n max: (rows, field) => Math.max(...rows.map((r) => Number(r[field]) || -Infinity)),\n first: (rows, field) => rows[0]?.[field],\n last: (rows, field) => rows[rows.length - 1]?.[field],\n};\n\n/** Custom aggregator registry (for plugins to add to) */\nconst customAggregators: Map<string, AggregatorFn> = new Map();\n\n/**\n * The aggregator registry singleton.\n * Plugins should access this through context or the global namespace.\n */\nexport const aggregatorRegistry = {\n /**\n * Register a custom aggregator function.\n */\n register(name: string, fn: AggregatorFn): void {\n customAggregators.set(name, fn);\n },\n\n /**\n * Unregister a custom aggregator function.\n */\n unregister(name: string): void {\n customAggregators.delete(name);\n },\n\n /**\n * Get an aggregator function by reference.\n */\n get(ref: AggregatorRef | undefined): AggregatorFn | undefined {\n if (ref === undefined) return undefined;\n if (typeof ref === 'function') return ref;\n // Check custom first, then built-in\n return customAggregators.get(ref) ?? builtInAggregators[ref];\n },\n\n /**\n * Run an aggregator on a set of rows.\n */\n run(ref: AggregatorRef | undefined, rows: any[], field: string, column?: any): any {\n const fn = this.get(ref);\n return fn ? fn(rows, field, column) : undefined;\n },\n\n /**\n * Check if an aggregator exists.\n */\n has(name: string): boolean {\n return customAggregators.has(name) || name in builtInAggregators;\n },\n\n /**\n * List all available aggregator names.\n */\n list(): string[] {\n return [...Object.keys(builtInAggregators), ...customAggregators.keys()];\n },\n};\n\n// #region Value-based Aggregators\n// Used by plugins like Pivot that work with pre-extracted numeric values\n\nexport type ValueAggregatorFn = (values: number[]) => number;\n\n/**\n * Built-in value-based aggregators.\n * These operate on arrays of numbers (unlike row-based aggregators).\n */\nconst builtInValueAggregators: Record<string, ValueAggregatorFn> = {\n sum: (vals) => vals.reduce((a, b) => a + b, 0),\n avg: (vals) => (vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0),\n count: (vals) => vals.length,\n min: (vals) => (vals.length ? Math.min(...vals) : 0),\n max: (vals) => (vals.length ? Math.max(...vals) : 0),\n first: (vals) => vals[0] ?? 0,\n last: (vals) => vals[vals.length - 1] ?? 0,\n};\n\n/**\n * Get a value-based aggregator function.\n * Used by Pivot plugin and other features that aggregate pre-extracted values.\n *\n * @param aggFunc - Aggregation function name ('sum', 'avg', 'count', 'min', 'max', 'first', 'last')\n * @returns Aggregator function that takes number[] and returns number\n */\nexport function getValueAggregator(aggFunc: string): ValueAggregatorFn {\n return builtInValueAggregators[aggFunc] ?? builtInValueAggregators.sum;\n}\n\n/**\n * Run a value-based aggregator on a set of values.\n *\n * @param aggFunc - Aggregation function name\n * @param values - Array of numbers to aggregate\n * @returns Aggregated result\n */\nexport function runValueAggregator(aggFunc: string, values: number[]): number {\n return getValueAggregator(aggFunc)(values);\n}\n// #endregion\n\n// Legacy function exports for backward compatibility\nexport const registerAggregator = aggregatorRegistry.register.bind(aggregatorRegistry);\nexport const unregisterAggregator = aggregatorRegistry.unregister.bind(aggregatorRegistry);\nexport const getAggregator = aggregatorRegistry.get.bind(aggregatorRegistry);\nexport const runAggregator = aggregatorRegistry.run.bind(aggregatorRegistry);\nexport const listAggregators = aggregatorRegistry.list.bind(aggregatorRegistry);\n"],"names":["FitModeEnum","DEFAULT_ANIMATION_CONFIG","DEFAULT_GRID_ICONS","parseLightDomColumns","host","el","field","rawType","type","header","sortable","editable","config","widthAttr","numericWidth","minWidthAttr","numericMinWidth","editorName","rendererName","optionsAttr","item","value","label","viewTpl","editorTpl","headerTpl","adapters","viewTarget","viewAdapter","a","renderer","editorTarget","editorAdapter","editor","c","mergeColumns","programmatic","dom","domMap","existing","cRenderer","existingRenderer","merged","d","m","dRenderer","mRenderer","addPart","token","autoSizeColumns","grid","mode","headerCells","changed","col","i","headerCell","max","rowEl","cell","w","updateTemplate","min","inferType","inferColumns","rows","provided","sample","columns","k","v","typeMap","EXPR_RE","EMPTY_SENTINEL","SAFE_EXPR","FORBIDDEN","escapeHtml","text","DANGEROUS_TAGS","DANGEROUS_ATTR_PATTERN","URL_ATTRS","DANGEROUS_URL_PROTOCOL","sanitizeHTML","html","template","sanitizeNode","root","toRemove","elements","tagName","attr","attrsToRemove","attrName","name","evalTemplateString","raw","ctx","parts","evaluated","_m","expr","res","evalSingle","finalStr","postProcess","allEmpty","p","key","dotChain","out","str","s","finalCellScrub","n","compileTemplate","forceBlank","fn","STATE_CHANGE_DEBOUNCE_MS","ConfigManager","#gridConfig","#columns","#fitMode","#lightDomColumnsCache","#originalColumnNodes","#originalConfig","#effectiveConfig","#sourcesChanged","#changeListeners","#lightDomObserver","#stateChangeTimeoutId","#initialColumnState","#callbacks","#lightDomTitle","callbacks","hasColumns","base","#collectAllSources","#cloneConfig","#applyPostMergeOperations","clone","h","configColumns","domCols","#mergeShellConfig","shellLightDomTitle","lightDomHeaderContent","toolPanelsMap","panels","b","headerContentsMap","contents","toolbarContentsMap","apiContents","originalConfigContents","configIds","mergedContents","content","plugins","sortStates","#getSortState","index","state","internalCol","sortState","plugin","pluginState","allColumns","stateMap","updatedColumns","updated","orderA","orderB","sortedByPriority","primarySort","colState","sortMap","visible","allCols","order","columnMap","reordered","#lightDomHandlers","callback","pendingCallbacks","debounceTimer","processPendingCallbacks","mutations","mutation","node","cb","booleanCellHTML","formatDateValue","getRowIndexFromCell","parent","getColIndexFromCell","clearCellFocus","resolveRenderer","columnRenderer","gridTypeDefaults","adapter","appDefault","FOCUSABLE_EDITOR_SELECTOR","hasEditingCells","clearEditingState","cellTemplate","rowTemplate","createCellFromTemplate","createRowFromTemplate","invalidateCellCache","renderVisibleRows","start","end","epoch","renderRowHook","needed","bodyEl","colLen","headerRowCount","hasRenderRowPlugins","hasRowHook","rowIndex","rowData","rowEpoch","prevRef","cellCount","structureValid","dataRefChanged","needsExternalRebuild","hasEditing","isActivelyEditedRow","renderInlineRow","fastPatchRow","isChanged","changedRowIds","rowId","hasChangedClass","rowClassFn","prevClasses","cls","newClasses","validClasses","e","children","colsLen","childLen","minLen","focusRow","focusCol","hasCellHook","hasSpecialCols","typeDefaults","rowIndexStr","shouldHaveFocus","hasFocus","cellClassFn","cellClasses","cellRenderer","renderedValue","produced","displayStr","formatted","gridEl","fragment","colIndex","compiled","tplHolder","viewRenderer","externalView","needsSanitization","spec","placeholder","context","output","blocked","rawTpl","textContent","cellValue","handleRowClick","firstCell","cellEl","focusChanged","ensureCellVisible","handleGridKeyDown","maxRow","maxCol","editing","colType","path","target","isFormField","tag","column","row","detail","activateEvent","legacyEvent","options","rowHeight","container","viewportEl","scrollEl","visibleHeight","y","isEditing","vStart","vEnd","scrollArea","offsets","cellRect","scrollAreaRect","cellLeft","cellRight","visibleLeft","visibleRight","focusTarget","dragState","handleCellMousedown","buildCellMouseEvent","renderRoot","elAtPoint","headerEl","handleMouseDown","event","handleMouseMove","handleMouseUp","setupCellEventDelegation","signal","setupRootEventDelegation","gridElement","defaultComparator","builtInSort","comparator","direction","rA","rB","finalizeSortResult","sortedRows","dir","r","renderHeader","toggleSort","applySort","result","setIcon","element","icon","headerRow","maybeTpl","span","active","icons","iconValue","handle","hasIdleCallback","scheduleIdle","cancelIdle","RenderPhase","RenderScheduler","#pendingPhase","#rafHandle","#readyPromise","#readyResolve","#initialReadyResolver","#initialReadyFired","phase","_source","#ensureReadyPromise","#flush","resolver","resolve","createResizeController","resizeState","pendingRaf","prevCursor","prevUserSelect","onMove","delta","width","justFinishedResize","onUp","hadResize","colWidth","startWidth","ANIMATION_ATTR","DURATION_PROPS","DEFAULT_DURATIONS","parseDuration","trimmed","getAnimationDuration","animationType","prop","computed","parsed","animateRowElement","onComplete","duration","animateRow","animateRows","rowIndices","animatedCount","animateRowById","getRowId","createElement","attrs","div","className","button","gridContentTemplate","cloneGridContent","buildGridDOM","contentWrapper","buildShellHeader","titleEl","toolbar","btn","toggleBtn","buildShellBody","body","hasPanel","isSinglePanel","gridContent","panelEl","resizeHandlePosition","panelContent","accordion","panel","sectionClasses","section","headerBtn","iconSpan","titleSpan","chevronSpan","iconToString","createShellState","shouldRenderShellHeader","renderShellHeader","toolPanelIcon","title","hasTitle","iconStr","configContents","stateContents","allContents","hasCustomContent","hasPanels","showSeparator","sortedContents","toolbarHtml","isOpen","parseLightDomShell","headerContents","parseLightDomToolButtons","rendererFactory","toolButtonsContainer","id","contentDef","parseLightDomToolPanels","tooltip","render","adapterRenderer","wrapper","existingPanel","cleanup","setupShellEventListeners","sectionId","setupToolPanelResize","onResize","shellBody","position","minWidth","startX","maxWidth","isResizing","onMouseMove","newWidth","onMouseUp","finalWidth","onMouseDown","renderCustomToolbarContents","slot","renderHeaderContent","hasLightDomContent","hasPluginContent","contentArea","existingCleanup","renderPanelContent","expandIcon","collapseIcon","panelId","isExpanded","chevron","updateToolbarActiveStates","panelToggle","updatePanelState","cleanupShellState","createShellController","initialized","controller","firstPanel","shadow","updateAccordionSectionState","otherId","otherPanel","contentEl","renderAccordionSectionContent","contentId","expanded","buildGridDOMIntoElement","shellConfig","runtimeState","hasShell","lightDomElements","lightDomSelectors","selector","sortedPanels","headerOptions","bodyOptions","shellHeader","createTouchScrollState","resetTouchState","cancelMomentum","handleTouchStart","touch","handleTouchMove","currentY","currentX","now","deltaY","deltaX","dt","scrollTop","scrollHeight","clientHeight","maxScrollY","canScrollVertically","canScrollHorizontally","scrollLeft","scrollWidth","clientWidth","maxScrollX","handleTouchEnd","startMomentumScroll","animate","scrollY","scrollX","setupTouchScrollListeners","gridContentEl","KNOWN_COLUMN_PROPERTIES","KNOWN_CONFIG_PROPERTIES","PLUGIN_IMPORT_HINTS","getImportHint","pluginName","capitalize","isDevelopment","hostname","hasPlugin","validatePluginProperties","columnProps","configProps","missingPlugins","addError","description","importHint","isConfigProperty","entry","def","errors","fields","fieldList","validatePluginConfigRules","warnings","manifest","rule","pluginConfig","warning","validatePluginDependencies","loadedPlugins","dependencies","dep","requiredPlugin","required","reason","reasonText","validatePluginIncompatibilities","pluginNames","warned","incompatibility","PluginManager","existingPlugin","otherPlugin","PluginClass","total","beforeRowIndex","adjustedStart","pluginStart","query","responses","response","focusedCell","left","right","skipScroll","DataGridElement","#renderRoot","#initialized","#rows","#configManager","#connected","#pendingUpdate","#pendingUpdateFlags","#scheduler","#scrollRaf","#pendingScrollTop","#hasScrollPlugins","#renderRowHook","#touchState","#eventAbortController","#resizeObserver","#rowHeightObserver","#idleCallbackHandle","#pooledScrollEvent","#pluginManager","#lastPluginsArray","#eventListenersAdded","#scrollAbortController","#shellState","#shellController","#resizeCleanup","#rowIdMap","#baseColumns","oldValue","#queueUpdate","#injectStyles","#updatePluginConfigs","#processColumns","#rebuildRowModel","#rowHeightObserverSetup","#setupRowHeightObserver","eventName","#emit","#setup","height","#applyAnimationConfig","#STYLE_ELEMENT_ID","#baseStyles","#pluginStylesMap","#getStyleElement","styleEl","#updateStyleElement","pluginStyles","styles","gridCssText","stylesheet","cssText","err","#initializePlugins","pluginsConfig","#injectAllPluginStyles","hasNewStyles","newPlugins","isLightDom","isApiRegistered","#collectPluginShellContributions","#destroyPlugins","pluginPanels","pluginContents","#getToolPanelRendererFactory","instanceAdapter","#render","#afterConnect","#setupLightDomHandlers","#customStyleSheets","newValue","gridRoot","defaultOpen","#setupScrollListeners","userRowHeight","#measureRowHeight","#updateAriaSelection","firstRow","cells","maxCellHeight","rowRect","measuredHeight","scrollSignal","fauxScrollbar","rowsEl","currentScrollTop","rawStart","evenAlignedStart","subPixelOffset","#onScrollBatched","isHorizontal","newFocus","rowIdx","isActiveRow","colIdx","#flushPendingUpdates","flags","#applyGridConfigUpdate","#applyRowsUpdate","#applyColumnsUpdate","#applyFitModeUpdate","#rebuildRowIdMap","#tryResolveRowId","#resolveRowIdOrThrow","hadShell","hadToolPanel","accordionSectionsBefore","nowNeedsShell","nowHasToolPanels","toolPanelCountChanged","#updateShellHeaderInPlace","sourceColumns","visibleCols","hiddenCols","processedColumns","processedFields","originalRows","processedRows","gridConfig","enabled","#renderVisibleRows","#lastAriaRowCount","#lastAriaColCount","#updateAriaCounts","rowCount","colCount","prevRowCount","scrollEvent","cellClickEvent","handled","rowClickEvent","headerClickEvent","changes","source","changedFields","updates","anyChanged","#applyColumnState","#pendingShellRefresh","#afterShellRefresh","css","sheet","#updateAdoptedStyleSheets","customSheets","existingSheets","handleShellChange","hadTitle","hadToolButtons","hasToolButtons","newHeaderHtml","temp","newHeader","#setupShellListeners","handleColumnChange","#calculateTotalSpacerHeight","totalRows","fauxScrollHeight","viewportHeight","scrollAreaEl","scrollAreaHeight","viewportHeightDiff","pluginExtraHeight","hScrollbarPadding","force","skipAfterRender","iterations","maxIterations","extraHeightBefore","pluginAdjustedStart","visibleCount","totalHeight","extraHeightBeforeStart","newFauxHeight","newViewportHeight","newTotalHeight","PLUGIN_QUERIES","BaseGridPlugin","#abortController","userIcons","durationStr","iconKey","pluginOverride","message","GridClasses","GridDataAttrs","GridSelectors","GridCSSVars","createGrid","queryGrid","DGEvents","PluginEvents","builtInAggregators","acc","sum","customAggregators","aggregatorRegistry","ref","builtInValueAggregators","vals","getValueAggregator","aggFunc","runValueAggregator","values","registerAggregator","unregisterAggregator","getAggregator","runAggregator","listAggregators"],"mappings":"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"}
|