@toolbox-web/grid 0.3.3 → 0.4.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.
Files changed (137) hide show
  1. package/all.d.ts +19 -19
  2. package/all.d.ts.map +1 -1
  3. package/all.js +1775 -1202
  4. package/all.js.map +1 -1
  5. package/index.js +2152 -2028
  6. package/index.js.map +1 -1
  7. package/lib/core/grid.d.ts +22 -12
  8. package/lib/core/grid.d.ts.map +1 -1
  9. package/lib/core/internal/columns.d.ts +0 -9
  10. package/lib/core/internal/columns.d.ts.map +1 -1
  11. package/lib/core/internal/config-manager.d.ts +236 -0
  12. package/lib/core/internal/config-manager.d.ts.map +1 -0
  13. package/lib/core/internal/event-delegation.d.ts.map +1 -1
  14. package/lib/core/internal/header.d.ts.map +1 -1
  15. package/lib/core/internal/keyboard.d.ts.map +1 -1
  16. package/lib/core/internal/render-scheduler.d.ts +123 -0
  17. package/lib/core/internal/render-scheduler.d.ts.map +1 -0
  18. package/lib/core/internal/rows.d.ts +8 -3
  19. package/lib/core/internal/rows.d.ts.map +1 -1
  20. package/lib/core/internal/sanitize.d.ts +2 -2
  21. package/lib/core/internal/sanitize.d.ts.map +1 -1
  22. package/lib/core/internal/shell.d.ts +40 -2
  23. package/lib/core/internal/shell.d.ts.map +1 -1
  24. package/lib/core/internal/validate-config.d.ts +11 -0
  25. package/lib/core/internal/validate-config.d.ts.map +1 -0
  26. package/lib/core/plugin/base-plugin.d.ts +70 -0
  27. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  28. package/lib/core/plugin/plugin-manager.d.ts +13 -2
  29. package/lib/core/plugin/plugin-manager.d.ts.map +1 -1
  30. package/lib/core/plugin/types.d.ts +17 -3
  31. package/lib/core/plugin/types.d.ts.map +1 -1
  32. package/lib/core/types.d.ts +112 -12
  33. package/lib/core/types.d.ts.map +1 -1
  34. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
  35. package/lib/plugins/clipboard/index.js +50 -18
  36. package/lib/plugins/clipboard/index.js.map +1 -1
  37. package/lib/plugins/column-virtualization/index.js +60 -25
  38. package/lib/plugins/column-virtualization/index.js.map +1 -1
  39. package/lib/plugins/context-menu/index.js +51 -16
  40. package/lib/plugins/context-menu/index.js.map +1 -1
  41. package/lib/plugins/editing/EditingPlugin.d.ts +117 -0
  42. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -0
  43. package/lib/{core/internal → plugins/editing}/editors.d.ts +1 -1
  44. package/lib/plugins/editing/editors.d.ts.map +1 -0
  45. package/lib/plugins/editing/index.d.ts +8 -0
  46. package/lib/plugins/editing/index.d.ts.map +1 -0
  47. package/lib/plugins/editing/index.js +705 -0
  48. package/lib/plugins/editing/index.js.map +1 -0
  49. package/lib/plugins/editing/types.d.ts +45 -0
  50. package/lib/plugins/editing/types.d.ts.map +1 -0
  51. package/lib/plugins/export/ExportPlugin.d.ts.map +1 -1
  52. package/lib/plugins/export/index.js +74 -39
  53. package/lib/plugins/export/index.js.map +1 -1
  54. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  55. package/lib/plugins/filtering/index.js +87 -50
  56. package/lib/plugins/filtering/index.js.map +1 -1
  57. package/lib/plugins/grouping-columns/grouping-columns.d.ts +4 -4
  58. package/lib/plugins/grouping-columns/grouping-columns.d.ts.map +1 -1
  59. package/lib/plugins/grouping-columns/index.js +59 -24
  60. package/lib/plugins/grouping-columns/index.js.map +1 -1
  61. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -1
  62. package/lib/plugins/grouping-rows/grouping-rows.d.ts.map +1 -1
  63. package/lib/plugins/grouping-rows/index.js +46 -11
  64. package/lib/plugins/grouping-rows/index.js.map +1 -1
  65. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +2 -2
  66. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -1
  67. package/lib/plugins/master-detail/index.js +140 -102
  68. package/lib/plugins/master-detail/index.js.map +1 -1
  69. package/lib/plugins/master-detail/types.d.ts +12 -2
  70. package/lib/plugins/master-detail/types.d.ts.map +1 -1
  71. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts.map +1 -1
  72. package/lib/plugins/multi-sort/index.js +59 -22
  73. package/lib/plugins/multi-sort/index.js.map +1 -1
  74. package/lib/plugins/pinned-columns/index.js +41 -6
  75. package/lib/plugins/pinned-columns/index.js.map +1 -1
  76. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -1
  77. package/lib/plugins/pinned-rows/index.js +45 -9
  78. package/lib/plugins/pinned-rows/index.js.map +1 -1
  79. package/lib/plugins/pivot/index.js +42 -7
  80. package/lib/plugins/pivot/index.js.map +1 -1
  81. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -1
  82. package/lib/plugins/reorder/index.js +59 -19
  83. package/lib/plugins/reorder/index.js.map +1 -1
  84. package/lib/plugins/selection/index.js +41 -6
  85. package/lib/plugins/selection/index.js.map +1 -1
  86. package/lib/plugins/server-side/index.js +48 -13
  87. package/lib/plugins/server-side/index.js.map +1 -1
  88. package/lib/plugins/tree/TreePlugin.d.ts +3 -3
  89. package/lib/plugins/tree/TreePlugin.d.ts.map +1 -1
  90. package/lib/plugins/tree/index.js +165 -126
  91. package/lib/plugins/tree/index.js.map +1 -1
  92. package/lib/plugins/tree/tree-data.d.ts +6 -6
  93. package/lib/plugins/tree/tree-data.d.ts.map +1 -1
  94. package/lib/plugins/tree/tree-detect.d.ts +5 -9
  95. package/lib/plugins/tree/tree-detect.d.ts.map +1 -1
  96. package/lib/plugins/tree/types.d.ts +16 -4
  97. package/lib/plugins/tree/types.d.ts.map +1 -1
  98. package/lib/plugins/undo-redo/index.js +46 -11
  99. package/lib/plugins/undo-redo/index.js.map +1 -1
  100. package/lib/plugins/visibility/index.js +37 -2
  101. package/lib/plugins/visibility/index.js.map +1 -1
  102. package/package.json +1 -1
  103. package/public.d.ts +104 -13
  104. package/public.d.ts.map +1 -1
  105. package/umd/grid.all.umd.js +31 -19
  106. package/umd/grid.all.umd.js.map +1 -1
  107. package/umd/grid.umd.js +18 -6
  108. package/umd/grid.umd.js.map +1 -1
  109. package/umd/plugins/clipboard.umd.js +1 -1
  110. package/umd/plugins/clipboard.umd.js.map +1 -1
  111. package/umd/plugins/editing.umd.js +2 -0
  112. package/umd/plugins/editing.umd.js.map +1 -0
  113. package/umd/plugins/export.umd.js +2 -2
  114. package/umd/plugins/export.umd.js.map +1 -1
  115. package/umd/plugins/filtering.umd.js +1 -1
  116. package/umd/plugins/filtering.umd.js.map +1 -1
  117. package/umd/plugins/grouping-columns.umd.js +1 -1
  118. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  119. package/umd/plugins/grouping-rows.umd.js +1 -1
  120. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  121. package/umd/plugins/master-detail.umd.js +1 -1
  122. package/umd/plugins/master-detail.umd.js.map +1 -1
  123. package/umd/plugins/multi-sort.umd.js +1 -1
  124. package/umd/plugins/multi-sort.umd.js.map +1 -1
  125. package/umd/plugins/pinned-rows.umd.js +1 -1
  126. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  127. package/umd/plugins/reorder.umd.js +1 -1
  128. package/umd/plugins/reorder.umd.js.map +1 -1
  129. package/umd/plugins/tree.umd.js +1 -1
  130. package/umd/plugins/tree.umd.js.map +1 -1
  131. package/lib/core/internal/column-state.d.ts +0 -124
  132. package/lib/core/internal/column-state.d.ts.map +0 -1
  133. package/lib/core/internal/editing.d.ts +0 -76
  134. package/lib/core/internal/editing.d.ts.map +0 -1
  135. package/lib/core/internal/editors.d.ts.map +0 -1
  136. package/lib/core/internal/grid-internals.d.ts +0 -83
  137. package/lib/core/internal/grid-internals.d.ts.map +0 -1
@@ -1 +1 @@
1
- {"version":3,"file":"grid.umd.js","sources":["../../../../libs/grid/src/lib/core/internal/column-state.ts","../../../../libs/grid/src/lib/core/types.ts","../../../../libs/grid/src/lib/core/internal/inference.ts","../../../../libs/grid/src/lib/core/internal/sanitize.ts","../../../../libs/grid/src/lib/core/internal/columns.ts","../../../../libs/grid/src/lib/core/internal/editors.ts","../../../../libs/grid/src/lib/core/internal/utils.ts","../../../../libs/grid/src/lib/core/internal/keyboard.ts","../../../../libs/grid/src/lib/core/internal/rows.ts","../../../../libs/grid/src/lib/core/internal/editing.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/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/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":["/**\n * Column State Module\n *\n * Handles collection and application of column state for persistence.\n * State includes user-driven changes: order, width, visibility, and sort.\n * Plugins can contribute additional state via getColumnState/applyColumnState hooks.\n */\n\nimport type { BaseGridPlugin } from '../plugin';\nimport type {\n ColumnConfig,\n ColumnInternal,\n ColumnSortState,\n ColumnState,\n GridColumnState,\n InternalGrid,\n} from '../types';\n\n/** Debounce timeout for state change events */\nconst STATE_CHANGE_DEBOUNCE_MS = 100;\n\n/**\n * Get sort state for a column from the grid's sortState.\n */\nfunction getSortState(grid: InternalGrid): Map<string, ColumnSortState> {\n const sortMap = new Map<string, ColumnSortState>();\n\n // Core sort state (single column)\n if (grid._sortState) {\n sortMap.set(grid._sortState.field, {\n direction: grid._sortState.direction === 1 ? 'asc' : 'desc',\n priority: 0,\n });\n }\n\n return sortMap;\n}\n\n/**\n * Collect column state from the grid and all plugins.\n * Returns a complete GridColumnState object ready for serialization.\n */\nexport function collectColumnState<T>(grid: InternalGrid<T>, plugins: BaseGridPlugin[]): GridColumnState {\n const columns = grid._columns;\n const sortStates = getSortState(grid);\n\n return {\n columns: columns.map((col, index) => {\n // 1. Core state\n const state: ColumnState = {\n field: col.field,\n order: index,\n visible: true, // If it's in _columns, it's visible (hidden columns are filtered out)\n };\n\n // Include width if set (either from config or resize)\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 if present\n const sortState = sortStates.get(col.field);\n if (sortState) {\n state.sort = sortState;\n }\n\n // 2. Collect from each plugin\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 and all plugins.\n * Modifies the grid's internal state and triggers plugin state restoration.\n *\n * @param grid - The grid instance\n * @param state - The state to apply\n * @param allColumns - All available columns (including hidden ones)\n * @param plugins - Plugins that may have applyColumnState hooks\n */\nexport function applyColumnState<T>(\n grid: InternalGrid<T>,\n state: GridColumnState,\n allColumns: ColumnConfig<T>[],\n plugins: BaseGridPlugin[],\n): void {\n if (!state.columns || state.columns.length === 0) return;\n\n const stateMap = new Map(state.columns.map((s) => [s.field, s]));\n\n // 1. Apply width and visibility to columns\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 // Apply width\n if (s.width !== undefined) {\n updated.width = s.width;\n updated.__renderedWidth = s.width;\n }\n\n // Apply visibility (hidden is inverse of visible)\n if (s.visible !== undefined) {\n updated.hidden = !s.visible;\n }\n\n return updated;\n });\n\n // 2. Reorder columns based on state\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 // 3. Update grid's internal columns\n grid._columns = updatedColumns as ColumnInternal<T>[];\n\n // 4. Apply sort state (core single-column sort)\n // Find the column with highest sort priority\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 grid._sortState = {\n field: primarySort.field,\n direction: primarySort.sort.direction === 'asc' ? 1 : -1,\n };\n }\n } else {\n grid._sortState = null;\n }\n\n // 5. Let each plugin apply its 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 * Create a state change handler with debouncing.\n * Returns a function that, when called, will eventually emit the state change event.\n */\nexport function createStateChangeHandler<T>(\n grid: InternalGrid<T>,\n getPlugins: () => BaseGridPlugin[],\n emit: (detail: GridColumnState) => void,\n): () => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return () => {\n // Clear any pending timeout\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n // Schedule the emit\n timeoutId = setTimeout(() => {\n timeoutId = null;\n const state = collectColumnState(grid, getPlugins());\n emit(state);\n }, STATE_CHANGE_DEBOUNCE_MS);\n };\n}\n\n/**\n * Compare two column states to check if they are equal.\n * Useful for preventing duplicate state change events.\n */\nexport function areColumnStatesEqual(a: GridColumnState, b: GridColumnState): boolean {\n if (a.columns.length !== b.columns.length) return false;\n\n for (let i = 0; i < a.columns.length; i++) {\n const colA = a.columns[i];\n const colB = b.columns[i];\n\n if (colA.field !== colB.field) return false;\n if (colA.order !== colB.order) return false;\n if (colA.visible !== colB.visible) return false;\n if (colA.width !== colB.width) return false;\n\n // Compare sort state\n const sortA = colA.sort;\n const sortB = colB.sort;\n if ((sortA === undefined) !== (sortB === undefined)) return false;\n if (sortA && sortB) {\n if (sortA.direction !== sortB.direction) return false;\n if (sortA.priority !== sortB.priority) return false;\n }\n }\n\n return true;\n}\n\n// ============================================================================\n// Column State API (High-level functions)\n// ============================================================================\n// These functions are extracted from grid.ts to reduce the god object size.\n// Grid.ts delegates to these functions for all state-related operations.\n\n/**\n * State manager for a grid instance.\n * Encapsulates the state change handler and initial state storage.\n */\nexport interface ColumnStateManager {\n /** The initial state to apply after initialization */\n initialState: GridColumnState | undefined;\n /** Debounced state change handler */\n stateChangeHandler: (() => void) | undefined;\n}\n\n/**\n * Create a column state manager for a grid.\n */\nexport function createColumnStateManager(): ColumnStateManager {\n return {\n initialState: undefined,\n stateChangeHandler: undefined,\n };\n}\n\n/**\n * Get the current column state from the grid.\n * @param grid - The grid instance\n * @param plugins - Array of attached plugins\n * @returns Serializable column state object\n */\nexport function getGridColumnState<T>(grid: InternalGrid<T>, plugins: BaseGridPlugin[]): GridColumnState {\n return collectColumnState(grid, plugins);\n}\n\n/**\n * Set column state on a grid, storing for later if not yet initialized.\n * @param grid - The grid instance\n * @param state - The state to apply\n * @param manager - The column state manager\n * @param isInitialized - Whether the grid is initialized\n * @param applyNow - Function to apply the state immediately\n */\nexport function setGridColumnState<T>(\n state: GridColumnState | undefined,\n manager: ColumnStateManager,\n isInitialized: boolean,\n applyNow: (state: GridColumnState) => void,\n): void {\n if (!state) return;\n\n // Store for use after initialization if called before ready\n manager.initialState = state;\n\n // If already initialized, apply immediately\n if (isInitialized) {\n applyNow(state);\n }\n}\n\n/**\n * Request a state change event emission (debounced).\n * @param grid - The grid instance\n * @param manager - The column state manager\n * @param getPlugins - Function to get attached plugins\n * @param emit - Function to emit the event\n */\nexport function requestGridStateChange<T>(\n grid: InternalGrid<T>,\n manager: ColumnStateManager,\n getPlugins: () => BaseGridPlugin[],\n emit: (state: GridColumnState) => void,\n): void {\n if (!manager.stateChangeHandler) {\n manager.stateChangeHandler = createStateChangeHandler(grid, getPlugins, emit);\n }\n manager.stateChangeHandler();\n}\n\n/**\n * Reset column state to initial configuration.\n * @param grid - The grid instance\n * @param manager - The column state manager\n * @param plugins - Array of attached plugins\n * @param callbacks - Grid callbacks for triggering updates\n */\nexport function resetGridColumnState<T>(\n grid: InternalGrid<T>,\n manager: ColumnStateManager,\n plugins: BaseGridPlugin[],\n callbacks: {\n mergeEffectiveConfig: () => void;\n setup: () => void;\n requestStateChange: () => void;\n },\n): void {\n // Clear initial state\n manager.initialState = undefined;\n\n // Clear hidden flag on all columns\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n // Reset sort state\n grid._sortState = null;\n grid.__originalOrder = [];\n\n // Re-initialize columns from config\n callbacks.mergeEffectiveConfig();\n callbacks.setup();\n\n // Notify plugins to reset their state\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n // Pass empty state to indicate reset\n for (const col of grid._columns) {\n plugin.applyColumnState(col.field, {\n field: col.field,\n order: 0,\n visible: true,\n });\n }\n }\n }\n\n // Emit state change\n callbacks.requestStateChange();\n}\n\n// ============================================================================\n// Column Visibility API\n// ============================================================================\n// Pure functions for column visibility operations.\n\n/** Callbacks for visibility changes that need grid integration */\nexport interface VisibilityCallbacks {\n emit: (eventName: string, detail: unknown) => void;\n clearRowPool: () => void;\n setup: () => void;\n requestStateChange: () => void;\n}\n\n/**\n * Set the visibility of a column.\n * @returns true if visibility changed, false otherwise\n */\nexport function setColumnVisible<T>(\n grid: InternalGrid<T>,\n field: string,\n visible: boolean,\n callbacks: VisibilityCallbacks,\n): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\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 callbacks.emit('column-visibility', {\n field,\n visible,\n visibleColumns: allCols.filter((c) => !c.hidden).map((c) => c.field),\n });\n\n callbacks.clearRowPool();\n callbacks.setup();\n callbacks.requestStateChange();\n return true;\n}\n\n/**\n * Toggle column visibility.\n * @returns true if toggled, false if column not found or locked\n */\nexport function toggleColumnVisibility<T>(\n grid: InternalGrid<T>,\n field: string,\n callbacks: VisibilityCallbacks,\n): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n return col ? setColumnVisible(grid, field, !!col.hidden, callbacks) : false;\n}\n\n/**\n * Check if a column is visible.\n */\nexport function isColumnVisible<T>(grid: InternalGrid<T>, field: string): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n return col ? !col.hidden : false;\n}\n\n/**\n * Show all columns.\n */\nexport function showAllColumns<T>(grid: InternalGrid<T>, callbacks: VisibilityCallbacks): void {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n if (!allCols.some((c) => c.hidden)) return;\n\n allCols.forEach((c) => (c.hidden = false));\n\n callbacks.emit('column-visibility', {\n visibleColumns: allCols.map((c) => c.field),\n });\n\n callbacks.clearRowPool();\n callbacks.setup();\n callbacks.requestStateChange();\n}\n\n/**\n * Get all columns with visibility info.\n */\nexport function getAllColumns<T>(\n grid: InternalGrid<T>,\n): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n return allCols.map((c) => ({\n field: c.field,\n header: c.header || c.field,\n visible: !c.hidden,\n lockVisible: c.lockVisible,\n }));\n}\n\n/**\n * Get current column order.\n */\nexport function getColumnOrder<T>(grid: InternalGrid<T>): string[] {\n return grid._columns.map((c) => c.field);\n}\n\n/**\n * Set column order.\n */\nexport function setColumnOrder<T>(\n grid: InternalGrid<T>,\n order: string[],\n callbacks: { renderHeader: () => void; updateTemplate: () => void; refreshVirtualWindow: () => void },\n): void {\n if (!order.length) return;\n\n const columnMap = new Map(grid._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 grid._columns = reordered;\n\n callbacks.renderHeader();\n callbacks.updateTemplate();\n callbacks.refreshVirtualWindow();\n}\n","import type { PluginQuery } from './plugin/base-plugin';\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's shadow DOM.\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 */\nexport interface InternalGrid<T = any> extends PublicGrid<T>, GridConfig<T> {\n shadowRoot: ShadowRoot | null;\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: HTMLElement[];\n _resizeController: ResizeController;\n _sortState: { field: string; direction: 1 | -1 } | null;\n __originalOrder: T[];\n __rowRenderEpoch: number;\n __didInitialAutoSize?: boolean;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n _gridTemplate: string;\n _virtualization: VirtualState;\n _focusRow: number;\n _focusCol: number;\n _activeEditRows: number;\n _rowEditSnapshots: Map<number, T>;\n _changedRowIndices: Set<number>;\n changedRows?: T[];\n changedRowIndices?: number[];\n effectiveConfig?: GridConfig<T>;\n findHeaderRow?: () => HTMLElement;\n refreshVirtualWindow: (full: boolean) => void;\n updateTemplate?: () => void;\n findRenderedRowElement?: (rowIndex: number) => HTMLElement | null;\n beginBulkEdit?: (rowIndex: number) => void;\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 /** 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' | 'typeahead';\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 /** Column data type; inferred if omitted */\n type?: PrimitiveColumnType;\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 /** Whether the field is editable (enables editors) */\n editable?: boolean;\n /** Optional custom editor factory or element tag name */\n editor?: ColumnEditorSpec<TRow, TValue>;\n /** For select/typeahead types - available options */\n options?: Array<{ label: string; value: unknown }> | (() => Array<{ label: string; value: unknown }>);\n /** For select/typeahead - 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\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;\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 */\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// #region Internal-only augmented types (not re-exported publicly)\nexport interface ColumnInternal<T = any> extends ColumnConfig<T> {\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?: (ctx: CellContext<T>) => string;\n __compiledEditor?: (ctx: EditorExecContext<T>) => string;\n}\n\n/**\n * Runtime cell context used internally for compiled template execution.\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 */\nexport interface EditorExecContext<T = any> extends CellContext<T> {\n commit: (newValue: unknown) => void;\n cancel: () => void;\n}\n\n/** Controller managing drag-based column resize lifecycle. */\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/** Virtual window bookkeeping; modified in-place as scroll position changes. */\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// #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, PrimitiveColumnType>;\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 */\nexport interface GridPlugin {\n /** Unique plugin identifier */\n readonly name: string;\n /** Plugin version */\n readonly version: string;\n /** CSS styles to inject into grid's shadow DOM */\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 * - `editOn` property - shorthand for `gridConfig.editOn`\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`, `editOn`) > `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 /** Sizing mode for columns. Can also be set via `fitMode` prop. */\n fitMode?: FitMode;\n /** Edit activation mode ('click' | 'dblClick' | false). Set to false to disable editing. Can also be set via `editOn` prop. */\n editOn?: string | boolean;\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// #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/**\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\n/**\n * Shell header bar configuration\n */\nexport interface ShellHeaderConfig {\n /** Grid title displayed on the left (optional) */\n title?: string;\n /** Custom toolbar buttons (rendered before tool panel toggles) */\n toolbarButtons?: ToolbarButtonConfig[];\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 button defined via config (programmatic approach).\n *\n * The grid does NOT create buttons - developers have full control over their own buttons.\n * Provide either:\n * - `element`: A ready-made DOM element (grid appends it to toolbar)\n * - `render`: A factory function that receives a container and appends content\n *\n * For declarative HTML buttons, use light-dom instead:\n * ```html\n * <tbw-grid>\n * <tbw-grid-header>\n * <button slot=\"toolbar\">My Button</button>\n * </tbw-grid-header>\n * </tbw-grid>\n * ```\n */\nexport interface ToolbarButtonConfig {\n /** Unique button ID */\n id: string;\n /** Tooltip / aria-label (for accessibility, used when grid generates panel toggle) */\n label?: string;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n\n /**\n * User-provided element. Grid appends it to the toolbar.\n * User is responsible for styling, event handlers, accessibility, etc.\n */\n element?: HTMLElement;\n /**\n * Render function called once. Receives container, user appends their DOM.\n * User is responsible for event handlers.\n * Return a cleanup function (optional).\n */\n render?: (container: HTMLElement) => void | (() => void);\n}\n\n/**\n * Toolbar button info returned by getToolbarButtons().\n */\nexport interface ToolbarButtonInfo {\n id: string;\n label: string;\n /** Source of this button: 'config' | 'light-dom' | 'panel-toggle' */\n source: 'config' | 'light-dom' | 'panel-toggle';\n /** For panel toggles, the associated panel ID */\n panelId?: string;\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\nexport interface CellCommitDetail<TRow = unknown> {\n /** The mutated row after commit. */\n row: TRow;\n /** Field name whose value changed. */\n field: string;\n /** New value 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 /** Indices parallel to changedRows. */\n changedRowIndices: number[];\n /** True if this row just entered the changed set. */\n firstTimeForRow: boolean;\n}\n\n/** Detail payload for a committed row edit (may or may not include changes). */\nexport interface RowCommitDetail<TRow = unknown> {\n /** Row index that lost edit focus. */\n rowIndex: number;\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 /** Indices of changed rows. */\n changedRowIndices: number[];\n}\n\n/** Emitted when the changed rows tracking set is cleared programmatically. */\nexport interface ChangedRowsResetDetail<TRow = unknown> {\n /** New (empty) changed rows array after reset. */\n rows: TRow[];\n /** Parallel indices (likely empty). */\n indices: number[];\n}\n\n/** Detail for a sort change (direction 0 indicates cleared sort). */\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/** Column resize event detail containing final pixel width. */\nexport interface ColumnResizeDetail {\n /** Resized column field key. */\n field: string;\n /** New width in pixels. */\n width: number;\n}\n\n/** Fired when keyboard navigation or programmatic focus changes active cell. */\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\nexport interface ExternalMountViewDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: { row: TRow; value: unknown; field: string; column: unknown };\n}\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\nexport interface DataGridEventMap<TRow = unknown> {\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 'activate-cell': ActivateCellDetail;\n 'column-state-change': GridColumnState;\n}\n\nexport type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = DataGridEventMap<TRow>[K];\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\nexport interface EvalContext {\n value: unknown;\n row: Record<string, unknown> | null;\n}\n// #endregion\n","import type { ColumnConfigMap, 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, PrimitiveColumnType> = {};\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 any)[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, PrimitiveColumnType> = {};\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 { 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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\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 };\n (fn as any).__blocked = forceBlank;\n return fn;\n}\n","import type { ColumnConfig, ColumnInternal, InternalGrid } from '../types';\nimport { FitModeEnum } from '../types';\nimport { inferColumns } from './inference';\nimport { compileTemplate } from './sanitize';\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(['number', 'string', 'date', 'boolean', 'select', 'typeahead']);\n const type = rawType && allowedTypes.has(rawType) ? (rawType as any) : 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 as any).resizable = true;\n if (el.hasAttribute('sizable')) (config as any).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 as any).__editorName = editorName;\n if (rendererName) (config as any).__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 as any).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 DataGridElementClass = (globalThis as any).DataGridElement;\n const adapters = DataGridElementClass?.getAdapters?.() ?? [];\n\n // First check inner view template, then column element itself\n const viewTarget = viewTpl ?? el;\n const viewAdapter = adapters.find((a: any) => 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;\n const editorAdapter = adapters.find((a: any) => 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 as any).resizable) (existing as any).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 as any).__viewTemplate) (existing as any).__viewTemplate = (c as any).__viewTemplate;\n if ((c as any).__editorTemplate) (existing as any).__editorTemplate = (c as any).__editorTemplate;\n if ((c as any).__headerTemplate) (existing as any).__headerTemplate = (c as any).__headerTemplate;\n // Support both 'renderer' alias and 'viewRenderer'\n const cRenderer = (c as any).renderer || c.viewRenderer;\n const existingRenderer = (existing as any).renderer || existing.viewRenderer;\n if (cRenderer && !existingRenderer) {\n existing.viewRenderer = cRenderer;\n if ((c as any).renderer) (existing as any).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 as any).resizable === true || (d as any).resizable === true) (m as any).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 as any).__viewTemplate) (m as any).__viewTemplate = (d as any).__viewTemplate;\n if ((d as any).__editorTemplate) (m as any).__editorTemplate = (d as any).__editorTemplate;\n if ((d as any).__headerTemplate) (m as any).__headerTemplate = (d as any).__headerTemplate;\n // Merge framework adapter renderers/editors from DOM (support both 'renderer' alias and 'viewRenderer')\n const dRenderer = (d as any).renderer || d.viewRenderer;\n const mRenderer = (m as any).renderer || m.viewRenderer;\n if (dRenderer && !mRenderer) {\n m.viewRenderer = dRenderer;\n if ((d as any).renderer) (m as any).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 any).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 * Resolve the effective column list for the grid by combining:\n * 1. Programmatic columns (`grid._columns`)\n * 2. Light DOM `<tbw-grid-column>` definitions (cached)\n * 3. Inferred columns (if none provided)\n * Also compiles inline template expressions into fast functions.\n * Columns with `hidden: true` in config are added to hidden tracking.\n */\nexport function getColumnConfiguration(grid: InternalGrid): void {\n if (!grid.__lightDomColumnsCache) {\n grid.__originalColumnNodes = Array.from(\n (grid as unknown as HTMLElement).querySelectorAll('tbw-grid-column'),\n ) as HTMLElement[];\n grid.__lightDomColumnsCache = grid.__originalColumnNodes.length\n ? parseLightDomColumns(grid as unknown as HTMLElement)\n : [];\n }\n const lightDomColumns = grid.__lightDomColumnsCache;\n const merged = mergeColumns(grid._columns, lightDomColumns);\n merged.forEach((c: ColumnInternal) => {\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 as HTMLElement).innerHTML);\n }\n });\n const { columns } = inferColumns(grid._rows, merged as any);\n grid._columns = columns as ColumnInternal[];\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 as any).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 = (grid._headerRowEl?.children || []) as any;\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] as HTMLElement | undefined;\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 as ColumnInternal).__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 as any).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 as any).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 as any).setProperty('--tbw-column-template', grid._gridTemplate);\n}\n","/**\n * Default Editors Module\n *\n * Provides built-in editor factories for different column types.\n *\n * IMPORTANT: Editor factories should NOT call focus() on elements - they are called\n * before the element is appended to the DOM. The calling code (beginBulkEdit,\n * inlineEnterEdit) is responsible for focusing the correct editor after insertion.\n */\n\nimport type { ColumnConfig, EditorContext } from '../types';\n\n/**\n * Returns a default editor factory function for the given column type.\n * Each editor handles commit on blur/Enter, and cancel on Escape.\n * Note: Focus is NOT called here - the calling code handles focusing after DOM insertion.\n */\nexport function defaultEditorFor(column: ColumnConfig<any>): (ctx: EditorContext) => HTMLElement | string {\n switch (column.type) {\n case 'number':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'number';\n input.value = ctx.value != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value === '' ? null : Number(input.value)));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value === '' ? null : Number(input.value));\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n case 'boolean':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'checkbox';\n input.checked = !!ctx.value;\n input.addEventListener('change', () => ctx.commit(input.checked));\n return input;\n };\n case 'date':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'date';\n if (ctx.value instanceof Date) input.valueAsDate = ctx.value;\n input.addEventListener('change', () => ctx.commit(input.valueAsDate));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n case 'select':\n case 'typeahead':\n return (ctx: EditorContext) => {\n const select = document.createElement('select');\n if ((ctx.column as any).multi) select.multiple = true;\n const options =\n typeof (ctx.column as any).options === 'function'\n ? (ctx.column as any).options()\n : (ctx.column as any).options || [];\n options.forEach((opt: any) => {\n const o = document.createElement('option');\n o.value = String(opt.value);\n o.textContent = opt.label;\n if ((ctx.column as any).multi && Array.isArray(ctx.value) && ctx.value.includes(opt.value)) o.selected = true;\n else if (!(ctx.column as any).multi && ctx.value === opt.value) o.selected = true;\n select.appendChild(o);\n });\n const commitValue = () => {\n if ((ctx.column as any).multi) {\n const values: any[] = [];\n Array.from(select.selectedOptions).forEach((o) => {\n values.push(o.value);\n });\n ctx.commit(values);\n } else {\n ctx.commit(select.value);\n }\n };\n select.addEventListener('change', commitValue);\n select.addEventListener('blur', commitValue);\n select.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') ctx.cancel();\n });\n return select;\n };\n default:\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'text';\n input.value = ctx.value != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value);\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\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 ? '&#x1F5F9;' : '&#9744;'}</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 * 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 return attr ? parseInt(attr, 10) : -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","/**\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 './editing';\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 as any).composedPath ? (e as any).composedPath() : [];\n const target = (path && path.length ? path[0] : (e.target as any)) 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' || colType === 'typeahead') && (e.key === 'ArrowDown' || e.key === 'ArrowUp'))\n 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 case 'Enter':\n if (typeof grid.beginBulkEdit === 'function') {\n grid.beginBulkEdit(grid._focusRow);\n // Don't call ensureCellVisible - beginBulkEdit handles focus\n return;\n } else {\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('activate-cell', { detail: { row: grid._focusRow, col: grid._focusCol } }),\n );\n }\n return ensureCellVisible(grid);\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: any) => {\n el.setAttribute('aria-selected', 'false');\n });\n const rowIndex = grid._focusRow;\n const vStart = (grid._virtualization as any).start ?? 0;\n const vEnd = (grid._virtualization as any).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 const cell = rowEl?.children[grid._focusCol] as HTMLElement | undefined;\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.shadowRoot?.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 as HTMLElement).focus({ preventScroll: true } as any);\n } catch {\n /* empty */\n }\n }\n }\n }\n}\n","import type { ColumnConfig, InternalGrid } from '../types';\nimport {\n clearEditingState,\n FOCUSABLE_EDITOR_SELECTOR,\n hasEditingCells,\n inlineEnterEdit,\n startRowEdit,\n} from './editing';\nimport { ensureCellVisible } from './keyboard';\nimport { evalTemplateString, finalCellScrub, sanitizeHTML } from './sanitize';\nimport { booleanCellHTML, clearCellFocus, formatBooleanValue, 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// ============== 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 * Cell display value cache key on grid instance.\n * Structure: Map<rowIndex, Map<colIndex, displayString>>\n * This cache is invalidated when rows or columns change (epoch bump).\n */\nconst CELL_CACHE_KEY = '__cellDisplayCache';\nconst CELL_CACHE_EPOCH_KEY = '__cellCacheEpoch';\n\n/**\n * Get the cached display value for a cell, computing it if not cached.\n * This is the hot path during scroll - must be as fast as possible.\n */\nfunction getCellDisplayValue(\n grid: InternalGrid,\n rowIndex: number,\n colIndex: number,\n rowData: any,\n col: ColumnConfig<any>,\n epoch: number | undefined,\n): string {\n // Fast path: check cache first\n let cache = (grid as any)[CELL_CACHE_KEY] as Map<number, string[]> | undefined;\n const cacheEpoch = (grid as any)[CELL_CACHE_EPOCH_KEY];\n\n // Invalidate cache if epoch changed\n if (cache && cacheEpoch !== epoch) {\n cache = undefined;\n (grid as any)[CELL_CACHE_KEY] = undefined;\n }\n\n if (!cache) {\n cache = new Map();\n (grid as any)[CELL_CACHE_KEY] = cache;\n (grid as any)[CELL_CACHE_EPOCH_KEY] = epoch;\n }\n\n let rowCache = cache.get(rowIndex);\n if (rowCache && rowCache[colIndex] !== undefined) {\n return rowCache[colIndex];\n }\n\n // Compute the display value\n const displayValue = computeCellDisplayValue(rowData, col);\n\n // Cache it\n if (!rowCache) {\n rowCache = [];\n cache.set(rowIndex, rowCache);\n }\n rowCache[colIndex] = displayValue;\n\n return displayValue;\n}\n\n/**\n * Compute the display string for a cell value.\n * Handles formatting, date conversion, boolean display, etc.\n */\nfunction computeCellDisplayValue(rowData: any, col: ColumnConfig<any>): string {\n let value = rowData[col.field];\n\n // Apply format function if present\n const format = (col as any).format;\n if (format) {\n try {\n value = format(value, rowData);\n } catch {\n // Keep original value on format error\n }\n }\n\n // Type-specific conversion\n if (col.type === 'date') {\n return formatDateValue(value);\n }\n\n if (col.type === 'boolean') {\n return formatBooleanValue(value);\n }\n\n return value == null ? '' : String(value);\n}\n\n/**\n * Invalidate the cell cache (call when rows or columns change).\n */\nexport function invalidateCellCache(grid: InternalGrid): void {\n (grid as any)[CELL_CACHE_KEY] = undefined;\n (grid as any)[CELL_CACHE_EPOCH_KEY] = undefined;\n (grid as any).__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 as any).__cachedHeaderRowCount;\n if (headerRowCount === undefined) {\n headerRowCount = grid.shadowRoot?.querySelector('.header-group-row') ? 2 : 1;\n (grid as any).__cachedHeaderRowCount = headerRowCount;\n }\n\n // Pool management: grow pool if needed\n while (grid._rowPool.length < needed) {\n // Use template cloning - 3-4x faster than createElement + setAttribute\n const rowEl = createRowFromTemplate();\n rowEl.addEventListener('click', (e) => handleRowClick(grid, e, rowEl, false));\n rowEl.addEventListener('dblclick', (e) => handleRowClick(grid, e, rowEl, true));\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 as any).__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];\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 as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n continue;\n }\n\n const rowEpoch = (rowEl as any).__epoch;\n const prevRef = (rowEl as any).__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 as any).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 as any).__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n (rowEl as any).__isCustomRow = false;\n }\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__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 as any).__rowDataRef = rowData;\n } else {\n if ((rowEl as any).__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n (rowEl as any).__isCustomRow = false;\n }\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n // Skip focus - editors are being re-created during virtualization scroll\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement, true);\n }\n }\n }\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 as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__rowDataRef = rowData;\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow && !hasEditing) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n // Skip focus - editors are being re-created during virtualization scroll\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement, true);\n }\n }\n }\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 as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow && !hasEditing) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n // Skip focus - editors are being re-created during virtualization scroll\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement, true);\n }\n }\n }\n }\n }\n\n // Changed class toggle\n const isChanged = grid._changedRowIndices.has(rowIndex);\n const hasChangedClass = rowEl.classList.contains('changed');\n if (isChanged !== hasChangedClass) {\n rowEl.classList.toggle('changed', isChanged);\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 as any).__hasSpecialColumns;\n if (hasSpecialCols === undefined) {\n hasSpecialCols = false;\n for (let i = 0; i < colsLen; i++) {\n const col = columns[i] as any;\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 ) {\n hasSpecialCols = true;\n break;\n }\n }\n (grid as any).__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 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] as any;\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] as any;\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 // 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 const cellRenderer = (col as any).renderer || col.viewRenderer;\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) {\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 {\n cell.textContent = value == null ? '' : String(value);\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 {\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 editMode = (grid as any).effectiveConfig?.editOn || grid.editOn;\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: ColumnConfig<any> = 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 const isCheckbox = col.type === 'boolean';\n if (col.type) cell.setAttribute('data-type', col.type as any);\n\n let value = (rowData as any)[col.field];\n const format = (col as any).format;\n if (format) {\n try {\n value = format(value, rowData);\n } catch {\n /* empty */\n }\n }\n\n const compiled = (col as any).__compiledView as ((ctx: any) => string) | undefined;\n const tplHolder = (col as any).__viewTemplate as HTMLElement | undefined;\n // Support both 'renderer' (ergonomic alias) and 'viewRenderer' (legacy)\n const viewRenderer = (col as any).renderer || (col as any).viewRenderer;\n const externalView = (col as any).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) {\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 cell.textContent = value == null ? '' : String(value);\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 {\n /* empty */\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 {\n /* empty */\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 as any).__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 as any).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 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 and optionally start row editing\n * according to the grid's configured edit activation mode.\n */\nexport function handleRowClick(grid: InternalGrid, e: MouseEvent, rowEl: HTMLElement, isDbl: boolean): 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, 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 entire shadow root)\n clearCellFocus(grid.shadowRoot ?? grid._bodyEl);\n cellEl.classList.add('cell-focus');\n }\n return;\n }\n\n ensureCellVisible(grid);\n }\n }\n\n // If this row is already in edit mode, don't re-render editors\n // Just update focus if clicking an editable cell\n const isRowAlreadyEditing = grid._activeEditRows === rowIndex;\n if (isRowAlreadyEditing) {\n // For single-click on already-editing row, just update focus to the clicked cell\n if (cellEl) {\n // Clear all cell-focus markers and set focus on the clicked cell\n clearCellFocus(grid.shadowRoot ?? grid._bodyEl);\n cellEl.classList.add('cell-focus');\n\n queueMicrotask(() => {\n const colIndex = Number(cellEl.getAttribute('data-col'));\n const col = grid._visibleColumns[colIndex];\n // If clicking an editable cell, focus its editor\n if (col && (col as any).editable && cellEl.classList.contains('editing')) {\n const editor = cellEl.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n });\n }\n return;\n }\n\n // Use cached editing state check (O(1) vs querySelector)\n if (hasEditingCells(rowEl)) {\n // If clicking (not double-clicking) on an already-editing row, do nothing\n if (!isDbl) return;\n // Double-click on editing row - clear editing classes\n const children = rowEl.children;\n for (let i = 0; i < children.length; i++) {\n (children[i] as HTMLElement).classList.remove('editing');\n }\n clearEditingState(rowEl);\n }\n const rawMode = (grid as any).effectiveConfig?.editOn ?? grid.editOn ?? 'dblClick';\n // editOn: false disables all editing\n if (rawMode === false) return;\n // Normalize: accept both 'dblClick' and 'dblclick' (DOM event name)\n const mode = rawMode === 'dblclick' ? 'dblClick' : rawMode;\n if (mode === 'click' || (mode === 'dblClick' && isDbl)) {\n // Use beginBulkEdit if available for consistent behavior with Enter key\n if (typeof grid.beginBulkEdit === 'function') {\n grid.beginBulkEdit(rowIndex);\n return;\n }\n startRowEdit(grid, rowIndex, rowData);\n } else return;\n Array.from(rowEl.children).forEach((c: any, i: number) => {\n const col = grid._visibleColumns[i];\n // Skip focus - we'll focus the correct editor in the queueMicrotask below\n if (col && (col as any).editable) inlineEnterEdit(grid, rowData, rowIndex, col, c as HTMLElement, true);\n });\n if (cellEl) {\n queueMicrotask(() => {\n const targetCell = rowEl.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`);\n if (targetCell?.classList.contains('editing')) {\n const editor = (targetCell as HTMLElement).querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n });\n }\n}\n","/**\n * Editing Lifecycle Module\n *\n * Handles row/cell editing state, commit/cancel operations, and value persistence.\n */\n\nimport type { ColumnConfig, InternalGrid } from '../types';\nimport { defaultEditorFor } from './editors';\nimport { invalidateCellCache, renderInlineRow } from './rows';\n\n/**\n * CSS selector for focusable editor elements within a cell.\n * Used by multiple modules to find and focus the active editor.\n */\nexport const FOCUSABLE_EDITOR_SELECTOR =\n 'input,select,textarea,[contenteditable=\"true\"],[contenteditable=\"\"],[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Returns true if the given property key is safe to use on a plain object without risking\n * prototype pollution via special names like \"__proto__\", \"constructor\", or \"prototype\".\n */\nfunction isSafePropertyKey(key: any): boolean {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') return false;\n return true;\n}\n\n/**\n * Check if a row element has any cells in editing mode.\n * Uses a cached count on the row element for O(1) lookup instead of querySelector.\n */\nexport function hasEditingCells(rowEl: HTMLElement): boolean {\n return ((rowEl as any).__editingCellCount ?? 0) > 0;\n}\n\n/**\n * Increment the editing cell count on a row element.\n * Called when a cell enters edit mode.\n */\nfunction incrementEditingCount(rowEl: HTMLElement): void {\n const count = ((rowEl as any).__editingCellCount ?? 0) + 1;\n (rowEl as any).__editingCellCount = count;\n rowEl.setAttribute('data-has-editing', '');\n}\n\n/**\n * Decrement the editing cell count on a row element.\n * Called when a cell exits edit mode.\n */\nfunction decrementEditingCount(rowEl: HTMLElement): void {\n const count = Math.max(0, ((rowEl as any).__editingCellCount ?? 0) - 1);\n (rowEl as any).__editingCellCount = count;\n if (count === 0) {\n rowEl.removeAttribute('data-has-editing');\n }\n}\n\n/**\n * Clear all editing state from a row element.\n * Called when the row is recycled or fully re-rendered.\n */\nexport function clearEditingState(rowEl: HTMLElement): void {\n (rowEl as any).__editingCellCount = 0;\n rowEl.removeAttribute('data-has-editing');\n}\n\n/**\n * Auto-wire commit/cancel lifecycle for input elements in string-returned editors.\n * This enables the simple syntax: `editor: (ctx) => `<input value=\"${ctx.value}\" />`\n * by automatically handling blur→commit, Enter→commit, Escape→cancel.\n *\n * Note: editFinalized is passed by reference effect (closure) - when the outer\n * code sets editFinalized=true, the handlers here see it.\n */\nfunction wireEditorInputs(\n editorHost: HTMLElement,\n column: ColumnConfig<any>,\n commit: (value: any) => void,\n cancel: () => void,\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n _editFinalizedRef: boolean,\n): void {\n const input = editorHost.querySelector('input,textarea,select') as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement\n | null;\n if (!input) return;\n\n const getInputValue = (): any => {\n if (input instanceof HTMLInputElement) {\n if (input.type === 'checkbox') return input.checked;\n if (input.type === 'number') return input.value === '' ? null : Number(input.value);\n if (input.type === 'date') return input.valueAsDate;\n }\n // For select and textarea, convert to number if column type requires\n if (column.type === 'number' && (input as any).value !== '') {\n return Number((input as any).value);\n }\n return (input as any).value;\n };\n\n // Blur commits value (unless already handled by Enter/Escape)\n input.addEventListener('blur', () => {\n commit(getInputValue());\n });\n\n // Change event for checkboxes and selects\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n input.addEventListener('change', () => commit(input.checked));\n } else if (input instanceof HTMLSelectElement) {\n input.addEventListener('change', () => commit(getInputValue()));\n }\n}\n\n/**\n * Snapshot original row data and mark the row as actively being edited.\n */\nexport function startRowEdit(grid: InternalGrid, rowIndex: number, rowData: any): void {\n if (grid._activeEditRows !== rowIndex) {\n grid._rowEditSnapshots.set(rowIndex, { ...rowData });\n grid._activeEditRows = rowIndex;\n }\n}\n\n/**\n * Finish editing for a row. If `revert` is true restore original snapshot and clear change marks.\n * Otherwise emit a row-commit event describing change status.\n */\nexport function exitRowEdit(grid: InternalGrid, rowIndex: number, revert: boolean): void {\n if (grid._activeEditRows !== rowIndex) return;\n const snapshot = grid._rowEditSnapshots.get(rowIndex);\n const current = grid._rows[rowIndex];\n\n // Before re-rendering, collect and commit values from any active editors\n // This ensures values are persisted even if blur hasn't fired yet\n const rowEl = grid.findRenderedRowElement?.(rowIndex);\n if (!revert && rowEl && current) {\n const editingCells = rowEl.querySelectorAll('.cell.editing');\n editingCells.forEach((cell) => {\n const colIndex = Number((cell as HTMLElement).getAttribute('data-col'));\n if (isNaN(colIndex)) return;\n const col = grid._visibleColumns[colIndex];\n if (!col) return;\n const input = cell.querySelector('input,textarea,select') as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement\n | null;\n if (input) {\n let val: unknown;\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n val = input.checked;\n } else {\n val = input.value;\n // Convert to number for number columns\n if (col.type === 'number' && val !== '') {\n val = Number(val);\n }\n }\n // Only commit if value actually changed\n if (current[col.field] !== val) {\n commitCellValue(grid, rowIndex, col, val, current);\n }\n }\n });\n }\n\n if (revert && snapshot && current) {\n Object.keys(snapshot).forEach((k) => (current[k] = snapshot[k]));\n grid._changedRowIndices.delete(rowIndex);\n // Invalidate cell cache so reverted values display correctly\n invalidateCellCache(grid);\n } else if (!revert) {\n const changed = grid._changedRowIndices.has(rowIndex);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('row-commit', {\n detail: {\n rowIndex,\n row: current,\n changed,\n changedRows: grid.changedRows,\n changedRowIndices: grid.changedRowIndices,\n },\n }),\n );\n }\n grid._rowEditSnapshots.delete(rowIndex);\n grid._activeEditRows = -1;\n if (rowEl) {\n renderInlineRow(grid, rowEl, grid._rows[rowIndex], rowIndex);\n if (grid._changedRowIndices.has(rowIndex)) rowEl.classList.add('changed');\n else rowEl.classList.remove('changed');\n }\n // Restore focus to the cell after exiting edit mode (for both commit and revert)\n queueMicrotask(() => {\n try {\n const rowIdx = grid._focusRow;\n const colIdx = grid._focusCol;\n const rowEl2 = grid.findRenderedRowElement?.(rowIdx);\n if (rowEl2) {\n // Clear all cell-focus markers\n Array.from(grid._bodyEl.querySelectorAll('.cell-focus')).forEach((el: any) =>\n el.classList.remove('cell-focus'),\n );\n // Find and focus the cell\n const cell = rowEl2.querySelector(`.cell[data-row=\"${rowIdx}\"][data-col=\"${colIdx}\"]`) as HTMLElement | null;\n if (cell) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '-1');\n cell.focus({ preventScroll: true });\n }\n }\n } catch {\n /* empty */\n }\n });\n}\n\n/**\n * Commit a single cell value change, updating the row object, marking the row as changed (first-time flag),\n * and emitting a `cell-commit` event with row + field metadata.\n */\nexport function commitCellValue(\n grid: InternalGrid,\n rowIndex: number,\n column: ColumnConfig<any>,\n newValue: any,\n rowData: any,\n): void {\n const field = column.field;\n if (!isSafePropertyKey(field)) return;\n const oldValue = rowData[field];\n if (oldValue === newValue) return;\n rowData[field] = newValue;\n const firstTime = !grid._changedRowIndices.has(rowIndex);\n grid._changedRowIndices.add(rowIndex);\n const rowEl = grid.findRenderedRowElement?.(rowIndex);\n if (rowEl) rowEl.classList.add('changed');\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('cell-commit', {\n detail: {\n row: rowData,\n field,\n value: newValue,\n rowIndex,\n changedRows: grid.changedRows,\n changedRowIndices: grid.changedRowIndices,\n firstTimeForRow: firstTime,\n },\n }),\n );\n}\n\n/**\n * Replace a cell's content with an editor resolved from column configuration (custom editor, template, external\n * mount spec or default editor by type). Manages commit / cancel lifecycle and value restoration.\n *\n * @param skipFocus - When true, don't auto-focus the editor. Used when creating multiple editors\n * at once (e.g., beginBulkEdit) so the caller can control focus.\n */\nexport function inlineEnterEdit(\n grid: InternalGrid,\n rowData: any,\n rowIndex: number,\n column: ColumnConfig<any>,\n cell: HTMLElement,\n skipFocus = false,\n): void {\n if (!column.editable) return;\n if (grid._activeEditRows !== rowIndex) startRowEdit(grid, rowIndex, rowData);\n if (cell.classList.contains('editing')) return;\n const originalValue = isSafePropertyKey(column.field) ? rowData[column.field] : undefined;\n cell.classList.add('editing');\n\n // Track editing state on the row element for fast O(1) lookup\n const rowEl = cell.parentElement;\n if (rowEl) incrementEditingCount(rowEl);\n\n let editFinalized = false; // Flag to prevent blur from committing after explicit Enter/Escape\n const commit = (newValue: any) => {\n // Skip if edit was already finalized by Enter/Escape, or if we've exited edit mode\n // (handles bulk edit case where one cell's exit removes all editors)\n if (editFinalized || grid._activeEditRows === -1) return;\n commitCellValue(grid, rowIndex, column, newValue, rowData);\n };\n const cancel = () => {\n editFinalized = true; // Mark as finalized to prevent blur from re-committing\n rowData[column.field] = isSafePropertyKey(column.field) ? originalValue : undefined;\n const inputLike = cell.querySelector('input,textarea,select') as any;\n if (inputLike) {\n const hasHTMLInput = typeof HTMLInputElement !== 'undefined';\n if (hasHTMLInput && inputLike instanceof HTMLInputElement && inputLike.type === 'checkbox')\n inputLike.checked = !!originalValue;\n else if ('value' in inputLike) inputLike.value = originalValue ?? '';\n }\n };\n const editorHost = document.createElement('div');\n editorHost.style.display = 'contents';\n cell.innerHTML = '';\n cell.appendChild(editorHost);\n\n // Common keydown handler for all editor types to handle Enter/Escape with proper exit\n // This catches events that bubble up from child elements (default editors, custom editors)\n editorHost.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true; // Prevent blur from committing again\n // Value should already be committed by the editor's own handler\n // Just need to exit edit mode\n exitRowEdit(grid, rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel(); // cancel() sets editFinalized = true\n exitRowEdit(grid, rowIndex, true);\n }\n });\n\n const tplHolder = (column as any).__editorTemplate as HTMLElement | undefined;\n const editorSpec = (column as any).editor || (tplHolder ? 'template' : defaultEditorFor(column));\n const value = originalValue;\n if (editorSpec === 'template' && tplHolder) {\n const clone = tplHolder.cloneNode(true) as HTMLElement;\n const compiledEditor = (column as any).__compiledEditor as ((ctx: any) => string) | undefined;\n if (compiledEditor)\n clone.innerHTML = compiledEditor({ row: rowData, value: originalValue, field: column.field, column });\n else\n clone.querySelectorAll<HTMLElement>('*').forEach((node) => {\n if (node.childNodes.length === 1 && node.firstChild?.nodeType === Node.TEXT_NODE) {\n node.textContent =\n node.textContent\n ?.replace(/{{\\s*value\\s*}}/g, originalValue == null ? '' : String(originalValue))\n .replace(/{{\\s*row\\.([a-zA-Z0-9_]+)\\s*}}/g, (_m, g) => {\n const v = (rowData as any)[g];\n return v == null ? '' : String(v);\n }) || '';\n }\n });\n const input = clone.querySelector('input,textarea,select') as HTMLInputElement | HTMLSelectElement | null;\n if (input) {\n const hasHTMLInput = typeof HTMLInputElement !== 'undefined';\n if (hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox')\n input.checked = !!originalValue;\n else if ('value' in input) (input as any).value = originalValue ?? '';\n input.addEventListener('blur', () => {\n // commit() will check editFinalized flag and skip if already handled\n const val =\n hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox'\n ? input.checked\n : (input as any).value;\n commit(val);\n });\n input.addEventListener('keydown', (e: any) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true; // Prevent blur from committing again\n const val =\n hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox'\n ? input.checked\n : (input as any).value;\n commit(val);\n exitRowEdit(grid, rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel(); // cancel() sets editFinalized = true\n exitRowEdit(grid, rowIndex, true);\n }\n });\n if (hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox') {\n input.addEventListener('change', () => {\n const val = input.checked;\n commit(val);\n });\n }\n if (!skipFocus) {\n setTimeout(() => input.focus({ preventScroll: true }), 0);\n }\n }\n editorHost.appendChild(clone);\n } else if (typeof editorSpec === 'string') {\n const el = document.createElement(editorSpec);\n (el as any).value = value;\n el.addEventListener('change', () => commit((el as any).value));\n editorHost.appendChild(el);\n // Focus the custom element editor after DOM insertion\n if (!skipFocus) {\n queueMicrotask(() => {\n const focusable = editorHost.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n focusable?.focus({ preventScroll: true });\n });\n }\n } else if (typeof editorSpec === 'function') {\n const produced = editorSpec({ row: rowData, value, field: column.field, column, commit, cancel });\n if (typeof produced === 'string') {\n editorHost.innerHTML = produced;\n // Auto-wire commit/cancel for inputs in string-returned editors\n wireEditorInputs(editorHost, column, commit, cancel, editFinalized);\n } else {\n editorHost.appendChild(produced);\n }\n // Focus the editor after DOM insertion (editors no longer auto-focus)\n if (!skipFocus) {\n queueMicrotask(() => {\n const focusable = editorHost.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n focusable?.focus({ preventScroll: true });\n });\n }\n } else if (editorSpec && typeof editorSpec === 'object') {\n const placeholder = document.createElement('div');\n placeholder.setAttribute('data-external-editor', '');\n placeholder.setAttribute('data-field', column.field);\n editorHost.appendChild(placeholder);\n const context = { row: rowData, value, field: column.field, column, commit, cancel };\n if (editorSpec.mount) {\n try {\n editorSpec.mount({ placeholder, context, spec: editorSpec });\n } catch {\n /* empty */\n }\n } else {\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('mount-external-editor', { detail: { placeholder, spec: editorSpec, context } }),\n );\n }\n }\n}\n\n// ============================================================================\n// Bulk Editing API\n// ============================================================================\n// These functions are extracted from grid.ts to reduce the god object size.\n// Grid.ts delegates to these functions for all bulk editing operations.\n\n/**\n * Emit a custom event from the grid element.\n */\nfunction emitEvent(grid: InternalGrid, eventName: string, detail: any): void {\n (grid as unknown as HTMLElement).dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true }));\n}\n\n/**\n * Get all changed rows from the grid.\n * @param grid - The grid instance\n * @returns Array of changed row data objects\n */\nexport function getChangedRows<T>(grid: InternalGrid<T>): T[] {\n return Array.from(grid._changedRowIndices).map((i) => grid._rows[i]);\n}\n\n/**\n * Get indices of all changed rows.\n * @param grid - The grid instance\n * @returns Array of row indices that have been modified\n */\nexport function getChangedRowIndices(grid: InternalGrid): number[] {\n return Array.from(grid._changedRowIndices);\n}\n\n/**\n * Reset all changed row markers.\n * @param grid - The grid instance\n * @param silent - If true, don't emit the reset event\n */\nexport function resetChangedRows<T>(grid: InternalGrid<T>, silent?: boolean): void {\n grid._changedRowIndices.clear();\n if (!silent) {\n emitEvent(grid, 'changed-rows-reset', {\n rows: getChangedRows(grid),\n indices: getChangedRowIndices(grid),\n });\n }\n grid._rowPool.forEach((r) => r.classList.remove('changed'));\n}\n\n/**\n * Begin bulk editing for a row. Enters edit mode on all editable cells.\n * @param grid - The grid instance\n * @param rowIndex - The row index to start editing\n * @param callbacks - Grid callbacks for finding row elements\n */\nexport function beginBulkEdit<T>(\n grid: InternalGrid<T>,\n rowIndex: number,\n callbacks: { findRenderedRowElement: (rowIndex: number) => HTMLElement | null },\n): void {\n // editOn: false disables all editing\n if ((grid as any).effectiveConfig?.editOn === false) return;\n\n // Check if any columns are editable - if not, skip edit mode entirely\n const hasEditableColumn = grid._columns.some((col) => col.editable);\n if (!hasEditableColumn) return;\n\n const rowData = grid._rows[rowIndex];\n startRowEdit(grid, rowIndex, rowData);\n\n // Enter edit mode on all editable cells in the row\n const rowEl = callbacks.findRenderedRowElement(rowIndex);\n if (rowEl) {\n Array.from(rowEl.children).forEach((cell, i) => {\n // Use visibleColumns to match the cell index - _columns may include hidden columns\n const col = grid._visibleColumns[i];\n if (col?.editable) {\n const cellEl = cell as HTMLElement;\n if (!cellEl.classList.contains('editing')) {\n // Skip auto-focus - we'll focus the correct editor below\n inlineEnterEdit(grid, rowData, rowIndex, col, cellEl, true);\n }\n }\n });\n\n // Focus the editor in the focused cell, or the first editable cell if focused cell is not editable\n // Use setTimeout to ensure custom editors have time to render their focusable elements\n setTimeout(() => {\n // First try the focused cell\n let targetCell = rowEl.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`);\n if (!targetCell?.classList.contains('editing')) {\n // Focused cell is not editable, find the first editable cell\n targetCell = rowEl.querySelector('.cell.editing');\n }\n if (targetCell?.classList.contains('editing')) {\n const editor = (targetCell as HTMLElement).querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n }, 0);\n }\n}\n\n/**\n * Commit the currently active row edit.\n * @param grid - The grid instance\n */\nexport function commitActiveRowEdit(grid: InternalGrid): void {\n if (grid._activeEditRows !== -1) {\n exitRowEdit(grid, grid._activeEditRows, false);\n }\n}\n\n/**\n * Cancel the currently active row edit, reverting to original values.\n * @param grid - The grid instance\n */\nexport function cancelActiveRowEdit(grid: InternalGrid): void {\n if (grid._activeEditRows !== -1) {\n exitRowEdit(grid, grid._activeEditRows, true);\n }\n}\n","/**\n * Event Delegation Module\n *\n * Provides centralized event handling for grid cells using event delegation.\n * Instead of attaching 3-6 listeners per cell (30,000+ for large grids),\n * we attach a single listener per event type on the container.\n *\n * This dramatically reduces memory usage and improves initialization time.\n */\n\nimport type { ColumnConfig, InternalGrid } from '../types';\nimport { commitCellValue, FOCUSABLE_EDITOR_SELECTOR, inlineEnterEdit, startRowEdit } from './editing';\nimport { ensureCellVisible } from './keyboard';\nimport { booleanCellHTML, getColIndexFromCell, getRowIndexFromCell } from './utils';\n\n/**\n * Extract cell context from a cell element.\n * Returns null if the cell is invalid or indices are missing.\n */\nfunction getCellContext(\n grid: InternalGrid,\n cell: HTMLElement,\n): { rowIndex: number; colIndex: number; rowData: any; col: ColumnConfig<any> } | null {\n const rowIndex = getRowIndexFromCell(cell);\n const colIndex = getColIndexFromCell(cell);\n if (rowIndex < 0 || colIndex < 0) return null;\n\n const rowData = grid._rows[rowIndex];\n const col = grid._visibleColumns[colIndex];\n if (!rowData || !col) return null;\n\n return { rowIndex, colIndex, rowData, col };\n}\n\n/**\n * Handle delegated mousedown on editable cells.\n * Updates focus position without starting edit.\n */\nfunction handleCellMousedown(grid: InternalGrid, cell: HTMLElement): void {\n if (cell.classList.contains('editing')) return;\n\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n grid._focusRow = ctx.rowIndex;\n grid._focusCol = ctx.colIndex;\n ensureCellVisible(grid);\n}\n\n/**\n * Handle delegated click on editable cells (editMode === 'click').\n */\nfunction handleCellClick(grid: InternalGrid, cell: HTMLElement, e: MouseEvent): void {\n if (cell.classList.contains('editing')) return;\n\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n e.stopPropagation();\n grid._focusRow = ctx.rowIndex;\n grid._focusCol = ctx.colIndex;\n inlineEnterEdit(grid, ctx.rowData, ctx.rowIndex, ctx.col, cell);\n}\n\n/**\n * Handle delegated dblclick on editable cells (editMode === 'dblClick').\n */\nfunction handleCellDblclick(grid: InternalGrid, cell: HTMLElement, e: MouseEvent): void {\n e.stopPropagation();\n\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n // Use beginBulkEdit if available for consistent behavior with Enter key\n if (typeof grid.beginBulkEdit === 'function') {\n grid._focusRow = ctx.rowIndex;\n grid._focusCol = ctx.colIndex;\n grid.beginBulkEdit(ctx.rowIndex);\n return;\n }\n\n // Fallback: manual edit initiation\n startRowEdit(grid, ctx.rowIndex, ctx.rowData);\n const rowEl = grid.findRenderedRowElement?.(ctx.rowIndex);\n if (rowEl) {\n const children = rowEl.children;\n for (let i = 0; i < children.length; i++) {\n const col2 = grid._visibleColumns[i];\n if (col2 && (col2 as any).editable) {\n inlineEnterEdit(grid, ctx.rowData, ctx.rowIndex, col2, children[i] as HTMLElement, true);\n }\n }\n // Focus the editor in the clicked cell\n queueMicrotask(() => {\n const targetCell = rowEl.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`);\n if (targetCell?.classList.contains('editing')) {\n const editor = (targetCell as HTMLElement).querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n });\n }\n}\n\n/**\n * Handle delegated keydown on editable cells.\n * Handles Enter, F2, Space (for boolean), and select/typeahead special cases.\n */\nfunction handleCellKeydown(grid: InternalGrid, cell: HTMLElement, e: KeyboardEvent): void {\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n const { rowIndex, colIndex, rowData, col } = ctx;\n const isEditing = cell.classList.contains('editing');\n\n // Select/typeahead: Enter opens picker\n if ((col.type === 'select' || col.type === 'typeahead') && !isEditing && e.key === 'Enter') {\n e.preventDefault();\n if (grid._activeEditRows !== rowIndex) startRowEdit(grid, rowIndex, rowData);\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n setTimeout(() => {\n const selectEl = cell.querySelector('select') as HTMLSelectElement | null;\n try {\n (selectEl as any)?.showPicker?.();\n } catch {\n /* empty */\n }\n selectEl?.focus({ preventScroll: true });\n }, 0);\n return;\n }\n\n // Boolean: Space toggles value\n if (col.type === 'boolean' && e.key === ' ' && !isEditing) {\n e.preventDefault();\n if (grid._activeEditRows !== rowIndex) startRowEdit(grid, rowIndex, rowData);\n const newVal = !rowData[col.field];\n commitCellValue(grid, rowIndex, col, newVal, rowData);\n cell.innerHTML = booleanCellHTML(!!newVal);\n return;\n }\n\n // Enter: Start editing\n if (e.key === 'Enter' && !isEditing) {\n e.preventDefault();\n e.stopPropagation();\n grid._focusRow = rowIndex;\n grid._focusCol = colIndex;\n if (typeof grid.beginBulkEdit === 'function') {\n grid.beginBulkEdit(rowIndex);\n } else {\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n }\n return;\n }\n\n // F2: Start editing (alternative)\n if (e.key === 'F2' && !isEditing) {\n e.preventDefault();\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n return;\n }\n}\n\n/**\n * Set up delegated event listeners on the grid body.\n * Call once during grid initialization.\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 const getEditMode = () => (grid as any).effectiveConfig?.editOn || (grid as any).editOn;\n\n // Mousedown - update focus on editable cells\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 // Check if this cell's column is editable\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellMousedown(grid, cell);\n }\n },\n { signal },\n );\n\n // Click - for editMode === 'click'\n bodyEl.addEventListener(\n 'click',\n (e) => {\n const editMode = getEditMode();\n if (editMode !== 'click') return;\n\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellClick(grid, cell, e);\n }\n },\n { signal },\n );\n\n // Dblclick - for editMode === 'dblClick' (default)\n bodyEl.addEventListener(\n 'dblclick',\n (e) => {\n const editMode = getEditMode();\n // Normalize: accept both 'dblClick' and 'dblclick'\n const normalized = editMode === 'dblclick' ? 'dblClick' : editMode;\n if (normalized === 'click' || editMode === false) return;\n\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellDblclick(grid, cell, e);\n }\n },\n { signal },\n );\n\n // Keydown - for Enter, F2, Space on editable cells\n bodyEl.addEventListener(\n 'keydown',\n (e) => {\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellKeydown(grid, cell, e as KeyboardEvent);\n }\n },\n { signal },\n );\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(grid: InternalGrid, sortedRows: unknown[], col: ColumnConfig<any>, dir: 1 | -1): void {\n grid._rows = sortedRows as any[];\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 as any).__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 as any).__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: any) => {\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 as any).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<any>).then === 'function') {\n // Async handler - wait for result\n (result as Promise<any[]>).then((sortedRows) => {\n finalizeSortResult(grid, sortedRows, col, dir);\n });\n } else {\n // Sync handler - apply immediately\n finalizeSortResult(grid, result as any[], 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 { ColumnConfig, 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! as any)();\n const headerRow = grid._headerRowEl as HTMLElement;\n headerRow.innerHTML = '';\n\n grid._visibleColumns.forEach((col: ColumnConfig<any>, 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 as any).__headerTemplate as HTMLElement | undefined;\n if (maybeTpl) Array.from(maybeTpl.childNodes).forEach((n) => cell.appendChild(n.cloneNode(true)));\n else {\n const label = (col as any).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 as any, '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 // Set position: relative for the resize handle positioning context\n // Note: If a plugin applies position: sticky (e.g., PinnedColumnsPlugin), it will override this\n cell.style.position = 'relative';\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","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 const rect = cell.getBoundingClientRect();\n resizeState = { startX: e.clientX, colIndex, startWidth: rect.width };\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 * Create a slot element for light DOM projection.\n */\nexport function slot(name?: string): HTMLSlotElement {\n const el = document.createElement('slot');\n if (name) el.name = name;\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 buttons with element/render (pre-sorted by order) */\n configButtons: Array<{\n id: string;\n hasElement?: boolean;\n hasRender?: boolean;\n }>;\n /** API toolbar buttons with element/render (pre-sorted by order) */\n apiButtons: Array<{\n id: string;\n hasElement?: boolean;\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 slot for header content\n const content = div('tbw-shell-content', { part: 'shell-content', role: 'presentation' });\n content.appendChild(slot('header-content'));\n header.appendChild(content);\n\n // Toolbar\n const toolbar = div('tbw-shell-toolbar', { part: 'shell-toolbar', role: 'presentation' });\n\n // Placeholders for config buttons with element or render function\n for (const btn of options.configButtons) {\n if (btn.hasElement || btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-btn-slot', { 'data-btn-slot': btn.id }));\n }\n }\n // Placeholders for API buttons with element or render function\n for (const btn of options.apiButtons) {\n if (btn.hasElement || btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-btn-slot', { 'data-btn-slot': btn.id }));\n }\n }\n\n // Light DOM slot for toolbar - always include for async rendering (React)\n toolbar.appendChild(slot('toolbar'));\n\n // Separator between config/API buttons and panel toggle\n const hasCustomButtons =\n options.configButtons.some((b) => b.hasElement || b.hasRender) ||\n options.apiButtons.some((b) => b.hasElement || b.hasRender);\n if (hasCustomButtons && 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 ToolbarButtonConfig,\n ToolbarButtonInfo,\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 */\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 /** Custom toolbar buttons registered via API */\n toolbarButtons: Map<string, ToolbarButtonConfig>;\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 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 /** 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 button render returns */\n toolbarButtonCleanups: Map<string, () => void>;\n}\n\n/**\n * Create initial shell state.\n */\nexport function createShellState(): ShellState {\n return {\n toolPanels: new Map(),\n headerContents: new Map(),\n toolbarButtons: new Map(),\n hasToolButtonsContainer: false,\n lightDomHeaderContent: [],\n lightDomTitle: null,\n lightDomToolPanelIds: new Set(),\n apiToolPanelIds: new Set(),\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarButtonCleanups: new Map(),\n };\n}\n\n/**\n * Determine if shell header should be rendered.\n */\nexport function shouldRenderShellHeader(config: ShellConfig | undefined, state: ShellState): boolean {\n // Check if title is configured (from config or light DOM)\n if (config?.header?.title) return true;\n if (state.lightDomTitle) return true;\n\n // Check if config has toolbar buttons\n if (config?.header?.toolbarButtons?.length) return true;\n\n // Check if any tool panels are registered (need toolbar buttons for them)\n if (state.toolPanels.size > 0) return true;\n\n // Check if any header content is registered\n if (state.headerContents.size > 0) return true;\n\n // Check if any API toolbar buttons registered\n if (state.toolbarButtons.size > 0) return true;\n\n // Check if light DOM has header elements\n if (state.lightDomHeaderContent.length > 0) return true;\n\n // Check if a toolbar buttons container was found\n if (state.hasToolButtonsContainer) return true;\n\n return false;\n}\n\n/**\n * Render the shell header HTML.\n *\n * Toolbar buttons come from two sources:\n * 1. Light DOM slot (users provide their own HTML buttons)\n * 2. Config/API with element or render function (programmatic insertion)\n *\n * The grid does NOT create buttons from config (icon/action).\n * Users have full control over button 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 // Collect toolbar button sources\n const configButtons = config?.header?.toolbarButtons ?? [];\n const hasConfigButtons = configButtons.some((btn) => btn.element || btn.render);\n const hasApiButtons = [...state.toolbarButtons.values()].some((btn) => btn.element || btn.render);\n const hasPanels = state.toolPanels.size > 0;\n const hasCustomButtons = hasConfigButtons || hasApiButtons;\n const showSeparator = hasCustomButtons && hasPanels;\n\n // Sort config/API buttons by order for slot placement\n const sortedConfigButtons = [...configButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const sortedApiButtons = [...state.toolbarButtons.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build toolbar HTML\n let toolbarHtml = '';\n\n // Slots for config/API buttons with element or render function\n for (const btn of sortedConfigButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n for (const btn of sortedApiButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n\n // Light DOM slot - user provides their own button HTML\n // Always include slot so light-dom buttons can be added later (e.g., React async rendering)\n toolbarHtml += '<slot name=\"toolbar\"></slot>';\n\n // Separator between custom buttons 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\">\n <slot name=\"header-content\"></slot>\n </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\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 // Assign slot names for slotting into shadow DOM\n state.lightDomHeaderContent.forEach((el) => {\n el.setAttribute('slot', 'header-content');\n });\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 * Parse toolbar buttons container element (<tbw-grid-tool-buttons>).\n * This is a container element that gets slotted as-is into the toolbar.\n * Users put their buttons inside and have full control over their HTML.\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 is assigned slot=\"toolbar\" and slotted directly.\n */\nexport function parseLightDomToolButtons(host: HTMLElement, state: ShellState): void {\n // Look for the toolbar buttons container element\n const toolButtonsContainer = host.querySelector(':scope > tbw-grid-tool-buttons');\n if (!toolButtonsContainer) return;\n\n // Mark that we found the container (for shouldRenderShellHeader)\n state.hasToolButtonsContainer = true;\n\n // Assign slot so it gets slotted into the toolbar area\n toolButtonsContainer.setAttribute('slot', 'toolbar');\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 shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n callbacks: {\n onPanelToggle: () => void;\n onSectionToggle: (sectionId: string) => void;\n onToolbarButtonClick: (buttonId: string) => void;\n },\n): void {\n const toolbar = shadowRoot.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 // Handle custom toolbar buttons\n const customBtn = target.closest('[data-btn]') as HTMLElement | null;\n if (customBtn) {\n const btnId = customBtn.getAttribute('data-btn');\n if (btnId) {\n callbacks.onToolbarButtonClick(btnId);\n }\n }\n });\n }\n\n // Accordion header clicks\n const accordion = shadowRoot.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 shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n onResize: (width: number) => void,\n): () => void {\n const panel = shadowRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n const handle = shadowRoot.querySelector('[data-resize-handle]') as HTMLElement | null;\n const shellBody = shadowRoot.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 custom button elements/render functions into toolbar slots.\n */\nexport function renderCustomToolbarButtons(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n): void {\n const allButtons = [...(config?.header?.toolbarButtons ?? []), ...state.toolbarButtons.values()];\n\n for (const btn of allButtons) {\n const slot = shadowRoot.querySelector(`[data-btn-slot=\"${btn.id}\"]`);\n if (!slot) continue;\n\n // Clean up previous render if any\n const existingCleanup = state.toolbarButtonCleanups.get(btn.id);\n if (existingCleanup) {\n existingCleanup();\n state.toolbarButtonCleanups.delete(btn.id);\n }\n\n if (btn.element) {\n slot.appendChild(btn.element);\n } else if (btn.render) {\n const cleanup = btn.render(slot as HTMLElement);\n if (cleanup) {\n state.toolbarButtonCleanups.set(btn.id, cleanup);\n }\n }\n }\n}\n\n/**\n * Render header content from plugins into the shell content area.\n */\nexport function renderHeaderContent(shadowRoot: ShadowRoot, state: ShellState): void {\n const contentArea = shadowRoot.querySelector('.tbw-shell-content');\n if (!contentArea) return;\n\n // Sort by order\n const sortedContents = [...state.headerContents.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Create containers for each content piece (before the slot)\n const slot = contentArea.querySelector('slot[name=\"header-content\"]');\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 // Insert before the slot\n if (slot) {\n contentArea.insertBefore(container, slot);\n } else {\n contentArea.appendChild(container);\n }\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 shadowRoot: ShadowRoot,\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 = shadowRoot.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(shadowRoot: ShadowRoot, state: ShellState): void {\n // Update single panel toggle button\n const panelToggle = shadowRoot.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(shadowRoot: ShadowRoot, state: ShellState): void {\n const panel = shadowRoot.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 * Get all toolbar button info (for debugging/introspection only).\n * Since users have full control over their button HTML, this returns limited info.\n */\nexport function getToolbarButtonsInfo(config: ShellConfig | undefined, state: ShellState): ToolbarButtonInfo[] {\n const result: ToolbarButtonInfo[] = [];\n\n // Config buttons (element/render only)\n for (const btn of config?.header?.toolbarButtons ?? []) {\n result.push({\n id: btn.id,\n label: btn.label ?? '',\n source: 'config',\n });\n }\n\n // API buttons\n for (const btn of state.toolbarButtons.values()) {\n result.push({\n id: btn.id,\n label: btn.label ?? '',\n source: 'config',\n });\n }\n\n // Panel toggles (the only buttons the grid creates)\n for (const panel of state.toolPanels.values()) {\n result.push({\n id: `panel-toggle-${panel.id}`,\n label: panel.tooltip ?? panel.title,\n source: 'panel-toggle',\n panelId: panel.id,\n });\n }\n\n return result;\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 buttons\n for (const cleanup of state.toolbarButtonCleanups.values()) {\n cleanup();\n }\n state.toolbarButtonCleanups.clear();\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.toolbarButtons.clear();\n state.lightDomHeaderContent = [];\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 shadow root for DOM queries */\n getShadow: () => ShadowRoot;\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 toolbar buttons info */\n getToolbarButtons(): ToolbarButtonInfo[];\n /** Register a toolbar button */\n registerToolbarButton(button: ToolbarButtonConfig): void;\n /** Unregister a toolbar button */\n unregisterToolbarButton(buttonId: string): void;\n /** Enable/disable a toolbar button */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): 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 getToolbarButtons() {\n return getToolbarButtonsInfo(callbacks.getShellConfig(), state);\n },\n\n registerToolbarButton(button: ToolbarButtonConfig) {\n if (state.toolbarButtons.has(button.id)) {\n console.warn(`[tbw-grid] Toolbar button \"${button.id}\" already registered`);\n return;\n }\n state.toolbarButtons.set(button.id, button);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolbarButton(buttonId: string) {\n // Clean up\n const cleanup = state.toolbarButtonCleanups.get(buttonId);\n if (cleanup) {\n cleanup();\n state.toolbarButtonCleanups.delete(buttonId);\n }\n\n state.toolbarButtons.delete(buttonId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n /**\n * Note: Toolbar button disabled state is now managed by the user.\n * This method is kept for backward compatibility but is a no-op.\n * Users should control their button's disabled state directly in their HTML.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n setToolbarButtonDisabled(_buttonId: string, _disabled: boolean) {\n // No-op: Users control their button disabled state directly.\n // The grid no longer creates or manages toolbar buttons.\n },\n };\n\n return controller;\n}\n\n/**\n * Update accordion section visual state.\n */\nfunction updateAccordionSectionState(shadow: ShadowRoot, sectionId: string, expanded: boolean): void {\n const section = shadow.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(shadow: ShadowRoot, state: ShellState, sectionId: string): void {\n const panel = state.toolPanels.get(sectionId);\n if (!panel?.render) return;\n\n const contentEl = shadow.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 shadowRoot - The shadow root 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 buildGridDOMIntoShadow(\n shadowRoot: ShadowRoot,\n shellConfig: ShellConfig | undefined,\n state: ShellState,\n icons?: { toolPanel?: IconValue; expand?: IconValue; collapse?: IconValue },\n): boolean {\n const hasShell = shouldRenderShellHeader(shellConfig, state);\n\n // Clear existing content\n shadowRoot.replaceChildren();\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 // Prepare button arrays\n const configButtons = shellConfig?.header?.toolbarButtons ?? [];\n const sortedConfigButtons = [...configButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const sortedApiButtons = [...state.toolbarButtons.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build header options\n const headerOptions: ShellHeaderOptions = {\n title: shellConfig?.header?.title ?? state.lightDomTitle ?? undefined,\n hasPanels: state.toolPanels.size > 0,\n isPanelOpen: state.isPanelOpen,\n toolPanelIcon,\n configButtons: sortedConfigButtons.map((b) => ({\n id: b.id,\n hasElement: !!b.element,\n hasRender: !!b.render,\n })),\n apiButtons: sortedApiButtons.map((b) => ({\n id: b.id,\n hasElement: !!b.element,\n hasRender: !!b.render,\n })),\n };\n\n // Build body options\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const bodyOptions: ShellBodyOptions = {\n position: shellConfig?.toolPanel?.position ?? 'right',\n isPanelOpen: state.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: state.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 shadowRoot.appendChild(fragment);\n } else {\n // No shell - just grid content\n const fragment = buildGridDOM({ hasShell: false });\n shadowRoot.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 * 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\nimport type { ColumnConfig } from '../types';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\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 */\nexport class PluginManager {\n /** Plugin instances in order of attachment */\n private plugins: BaseGridPlugin[] = [];\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: any) {}\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 */\n attach(plugin: BaseGridPlugin): void {\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\n /**\n * Detach all plugins and clean up.\n */\n detachAll(): void {\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 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.\n */\n getAllStyles(): string {\n return this.plugins\n .filter((p) => p.styles)\n .map((p) => p.styles)\n .join('\\n');\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 * 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 {\n applyColumnState,\n collectColumnState,\n createStateChangeHandler,\n getAllColumns,\n getColumnOrder,\n isColumnVisible,\n setColumnOrder,\n setColumnVisible,\n showAllColumns,\n toggleColumnVisibility,\n type VisibilityCallbacks,\n} from './internal/column-state';\nimport { autoSizeColumns, getColumnConfiguration, updateTemplate } from './internal/columns';\nimport {\n beginBulkEdit,\n cancelActiveRowEdit,\n commitActiveRowEdit,\n exitRowEdit,\n getChangedRowIndices,\n getChangedRows,\n resetChangedRows,\n} from './internal/editing';\nimport { setupCellEventDelegation } from './internal/event-delegation';\nimport { renderHeader } from './internal/header';\nimport { cancelIdle, scheduleIdle } from './internal/idle-scheduler';\nimport { inferColumns } from './internal/inference';\nimport { ensureCellVisible, handleGridKeyDown } from './internal/keyboard';\nimport { createResizeController } from './internal/resize';\nimport { invalidateCellCache, renderVisibleRows } from './internal/rows';\nimport {\n buildGridDOMIntoShadow,\n cleanupShellState,\n createShellController,\n createShellState,\n parseLightDomShell,\n parseLightDomToolButtons,\n parseLightDomToolPanels,\n renderCustomToolbarButtons,\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 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 ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n FitMode,\n FrameworkAdapter,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n InternalGrid,\n ResizeController,\n ToolbarButtonConfig,\n ToolbarButtonInfo,\n ToolPanelDefinition,\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 * ## Configuration Architecture\n *\n * The grid follows a **single source of truth** pattern where all configuration\n * converges into `#effectiveConfig`. 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. `editOn` property → merged into `effectiveConfig.editOn`\n * 6. 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 * The `#mergeEffectiveConfig()` method 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 * @fires cell-commit - Fired when a cell value is committed\n * @fires row-commit - Fired when a bulk row edit session commits\n * @fires changed-rows-reset - Fired after resetChangedRows() unless silent\n * @fires mount-external-view - Fired to request mounting of an external view renderer\n * @fires mount-external-editor - Fired to request mounting of an external editor renderer\n * @fires sort-change - Fired when sort state changes for a column\n * @fires column-resize - Fired after a column resize drag completes\n * @fires activate-cell - Fired when a cell activation intent occurs\n * @fires group-toggle - Fired when a group row is toggled\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 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 */\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 */\n static getAdapters(): readonly FrameworkAdapter[] {\n return this.adapters;\n }\n\n /**\n * Clear all registered adapters (primarily for testing).\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', 'edit-on'];\n }\n\n readonly #shadow: ShadowRoot;\n #initialized = false;\n\n // ---------------- Ready Promise ----------------\n #readyPromise: Promise<void>;\n #readyResolve?: () => void;\n\n // #region Input Properties\n // These backing fields store raw user input. They are merged into\n // #effectiveConfig by #mergeEffectiveConfig(). Never read directly\n // for rendering logic - always use effectiveConfig or derived state.\n #rows: T[] = [];\n #columns?: ColumnConfig<T>[] | ColumnConfigMap<T>;\n #gridConfig?: GridConfig<T>;\n #fitMode?: FitMode;\n #editOn?: string | boolean;\n // #endregion\n\n // #region Private properties\n // All input sources converge here. This is the canonical config\n // that all rendering and logic should read from.\n #effectiveConfig: GridConfig<T> = {};\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 editMode: false,\n };\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 #isDragging = false;\n #touchState: TouchScrollState = createTouchScrollState();\n #eventAbortController?: AbortController;\n #resizeObserver?: ResizeObserver;\n #rowHeightObserver?: ResizeObserver; // Watches first row for size changes (CSS loading, custom renderers)\n #refreshColumnsRaf = 0; // RAF handle for debounced refreshColumns\n #lightDomObserver?: MutationObserver;\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\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 #stateChangeHandler?: () => void;\n #initialColumnState?: GridColumnState;\n\n // ---------------- Shell State ----------------\n #shellState: ShellState = createShellState();\n #shellController!: ShellController;\n #resizeCleanup?: () => void;\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\n // Sort state\n _sortState: { field: string; direction: 1 | -1 } | null = null;\n\n // Edit state\n _activeEditRows = -1;\n _rowEditSnapshots = new Map<number, T>();\n _changedRowIndices = new Set<number>();\n\n // Layout\n _gridTemplate = '';\n // #endregion\n\n // #region Implementation Details (Internal only)\n __rowRenderEpoch = 0;\n __didInitialAutoSize = false;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n __originalOrder: T[] = [];\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 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 rows.\n * Use this when you need access to all source data regardless of active filters.\n */\n get sourceRows(): T[] {\n return this.#rows;\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.#columns;\n this.#columns = value;\n if (oldValue !== value) {\n this.#queueUpdate('columns');\n }\n }\n\n get gridConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n set gridConfig(value: GridConfig<T> | undefined) {\n const oldValue = this.#gridConfig;\n this.#gridConfig = 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.__lightDomColumnsCache = undefined;\n this.#queueUpdate('gridConfig');\n }\n }\n\n get fitMode(): FitMode {\n return this.#effectiveConfig.fitMode ?? 'stretch';\n }\n set fitMode(value: FitMode | undefined) {\n const oldValue = this.#fitMode;\n this.#fitMode = value;\n if (oldValue !== value) {\n this.#queueUpdate('fitMode');\n }\n }\n\n get editOn(): string | boolean | undefined {\n return this.#effectiveConfig.editOn;\n }\n set editOn(value: string | boolean | undefined) {\n const oldValue = this.#editOn;\n this.#editOn = value;\n if (oldValue !== value) {\n this.#queueUpdate('editMode');\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 * @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 * @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 constructor() {\n super();\n this.#shadow = this.attachShadow({ mode: 'open' });\n void this.#injectStyles(); // Fire and forget - styles load asynchronously\n this.#readyPromise = new Promise((res) => (this.#readyResolve = res));\n\n // Initialize shell controller with callbacks\n this.#shellController = createShellController(this.#shellState, {\n getShadow: () => this.#shadow,\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\n async #injectStyles(): Promise<void> {\n const sheet = new CSSStyleSheet();\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 sheet.replaceSync(styles);\n this.#shadow.adoptedStyleSheets = [sheet];\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 // The grid.css uses :host selectors which appear as-is in cssText\n if (cssText.includes('.tbw-grid-root') && cssText.includes(':host')) {\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 sheet.replaceSync(gridCssText);\n this.#shadow.adoptedStyleSheets = [sheet];\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 * @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 * @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 * @internal Plugin API\n */\n requestRender(): void {\n this.#rebuildRowModel();\n this.#processColumns();\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n }\n\n /**\n * Update the grid's column template CSS.\n * Called by resize controller during column resize operations.\n * @internal\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 * @internal Plugin API\n */\n requestAfterRender(): void {\n this.#pluginManager?.afterRender();\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 shadow DOM.\n * Must be called after #render() since innerHTML wipes existing content.\n */\n #injectAllPluginStyles(): void {\n const allStyles = this.#pluginManager?.getAllStyles() ?? '';\n if (allStyles) {\n const styleEl = document.createElement('style');\n styleEl.setAttribute('data-plugin', 'all');\n styleEl.textContent = allStyles;\n this.#shadow.appendChild(styleEl);\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 */\n #updatePluginConfigs(): void {\n // With class-based plugins, config changes require re-initialization\n // The new plugins are in the new config - detach old, 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 // 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 as any).__frameworkAdapter) return undefined;\n\n // Also check for instance-level adapter (e.g., __frameworkAdapter from Angular Grid directive)\n const instanceAdapter = (this as any).__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 connectedCallback(): void {\n if (!this.hasAttribute('tabindex')) (this as any).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\n // Merge all config sources into effectiveConfig (including columns and shell)\n this.#mergeEffectiveConfig();\n\n // Initialize plugin system (now plugins can access disconnectSignal)\n this.#initializePlugins();\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 MutationObserver to watch for light DOM changes\n // This handles frameworks like Angular that project content asynchronously\n this.#setupLightDomObserver();\n },\n { timeout: 100 },\n );\n }\n\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 if (this.#lightDomObserver) {\n this.#lightDomObserver.disconnect();\n this.#lightDomObserver = undefined;\n }\n\n // Clear caches to prevent memory leaks\n invalidateCellCache(this);\n this._rowEditSnapshots.clear();\n this._changedRowIndices.clear();\n this.#customStyles.clear();\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 */\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue || !newValue || newValue === 'null' || newValue === 'undefined') return;\n\n // Map kebab-case attributes to camelCase properties\n const propMap: Record<string, keyof this> = {\n rows: 'rows',\n columns: 'columns',\n 'grid-config': 'gridConfig',\n 'fit-mode': 'fitMode',\n 'edit-on': 'editOn',\n };\n\n const prop = propMap[name];\n if (!prop) return;\n\n // JSON attributes need parsing\n if (name === 'rows' || name === 'columns' || name === 'grid-config') {\n try {\n (this as any)[prop] = JSON.parse(newValue);\n } catch {\n console.warn(`[tbw-grid] Invalid JSON for '${name}' attribute:`, newValue);\n }\n } else {\n // String attributes (fit-mode, edit-on)\n (this as any)[prop] = newValue;\n }\n }\n\n #afterConnect(): void {\n // Shell changes the DOM structure - need to find elements appropriately\n const gridContent = this.#shadow.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#shadow.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.#shadow, this.#shellState);\n // Render custom toolbar buttons (element/render modes)\n renderCustomToolbarButtons(this.#shadow, 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 any);\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 // Element-level keydown handler (uses signal for automatic cleanup)\n this.addEventListener('keydown', (e) => handleGridKeyDown(this as any, e), { signal });\n\n // Document-level listeners (also use signal for automatic cleanup)\n // Escape key to cancel row editing\n document.addEventListener(\n 'keydown',\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this._activeEditRows !== -1) {\n exitRowEdit(this, this._activeEditRows, true);\n }\n },\n { capture: true, signal },\n );\n\n // Click outside to commit row editing\n document.addEventListener(\n 'mousedown',\n (e: MouseEvent) => {\n if (this._activeEditRows === -1) return;\n const rowEl = this.findRenderedRowElement(this._activeEditRows);\n if (!rowEl) return;\n const path = (e.composedPath && e.composedPath()) || [];\n if (path.includes(rowEl)) return;\n exitRowEdit(this, this._activeEditRows, false);\n },\n { signal },\n );\n\n // Central mouse event handling for plugins (uses signal for automatic cleanup)\n this.#shadow.addEventListener('mousedown', (e) => this.#handleMouseDown(e as MouseEvent), { signal });\n\n // Track global mousemove/mouseup for drag operations (uses signal for automatic cleanup)\n document.addEventListener('mousemove', (e: MouseEvent) => this.#handleMouseMove(e), { signal });\n document.addEventListener('mouseup', (e: MouseEvent) => this.#handleMouseUp(e), { signal });\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 requestAnimationFrame(() => requestAnimationFrame(() => this.#readyResolve?.()));\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 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 if (measuredHeight > 0 && measuredHeight !== this._virtualization.rowHeight) {\n this._virtualization.rowHeight = measuredHeight;\n this.refreshVirtualWindow(true);\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.#shadow.querySelector('.tbw-grid-content') as HTMLElement;\n const scrollArea = this.#shadow.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 any, 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 if (!this.#scrollRaf) {\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n this.refreshVirtualWindow(true);\n ensureCellVisible(this as any);\n });\n }\n });\n this.#resizeObserver.observe(this._virtualization.viewportEl);\n }\n\n if (this._virtualization.enabled) {\n requestAnimationFrame(() => {\n this.refreshVirtualWindow(true);\n // Set up row height observer AFTER rows are rendered (not before)\n // Observing cells before refreshVirtualWindow replaces them is useless\n this.#setupRowHeightObserver();\n });\n }\n }\n\n /**\n * Set up ResizeObserver on first row's cells to detect height changes.\n * Called after rows are rendered to observe the actual content cells.\n * Handles dynamic CSS loading, lazy images, font loading, 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');\n if (!firstRow) return;\n\n this.#rowHeightObserverSetup = true;\n this.#rowHeightObserver?.disconnect();\n\n const cells = firstRow.querySelectorAll('.cell');\n if (cells.length > 0) {\n this.#rowHeightObserver = new ResizeObserver(() => {\n this.#measureRowHeight();\n });\n // Observe all cells - any one might have a custom renderer that changes size\n cells.forEach((cell) => this.#rowHeightObserver!.observe(cell));\n }\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' | 'editMode'): 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 editMode: false,\n };\n\n // If gridConfig changed, it supersedes columns/rows/fit/edit changes\n // because gridConfig includes all of those\n if (flags.gridConfig) {\n this.#applyGridConfigUpdate();\n return; // gridConfig handles everything\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 if (flags.editMode) {\n this.#applyEditModeUpdate();\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 this.#rebuildRowModel();\n const hasColumns =\n this._columns.length > 0 ||\n (Array.isArray(this.#effectiveConfig?.columns) && this.#effectiveConfig.columns.length > 0) ||\n (Array.isArray(this.#columns) && this.#columns.length > 0);\n if (!hasColumns) {\n this.#setup();\n } else {\n this.#processColumns();\n this.refreshVirtualWindow(true);\n }\n }\n\n #applyColumnsUpdate(): void {\n invalidateCellCache(this);\n this.#mergeEffectiveConfig();\n this.#setup();\n }\n\n #applyFitModeUpdate(): void {\n this.#mergeEffectiveConfig();\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 #applyEditModeUpdate(): void {\n this.#mergeEffectiveConfig();\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n this.refreshVirtualWindow(true);\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.#shadow.querySelector('.has-shell');\n const hadToolPanel = !!this.#shadow.querySelector('.tbw-tool-panel');\n\n // Count accordion sections before update (to detect new panels added)\n const accordionSectionsBefore = this.#shadow.querySelectorAll('.tbw-accordion-section').length;\n\n getColumnConfiguration(this);\n this.#mergeEffectiveConfig();\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 const nowNeedsShell = shouldRenderShellHeader(this.#effectiveConfig as any, this.#shellState);\n const nowHasToolPanels = this.#shellState.toolPanels.size > 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.#shellState.toolPanels.size !== 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.#afterConnect();\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 this.#rebuildRowModel();\n this.#processColumns();\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\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.#shadow.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] as any[]);\n\n // If plugins modified visible columns, update them\n if (processedColumns !== visibleCols) {\n // Build a map of processed columns by field for quick lookup\n const processedMap = new Map(processedColumns.map((c: any, i: number) => [c.field, { col: c, order: i }]));\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) => processedMap.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 returned original fields (possibly modified) - merge back\n // Use source columns as base, not current _columns\n const updatedColumns = sourceColumns.map((c) => {\n if (c.hidden) return c; // Keep hidden columns unchanged\n const processed = processedMap.get(c.field);\n return processed ? processed.col : c;\n });\n\n this._columns = updatedColumns 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 * Build the canonical effective configuration by merging all input sources.\n *\n * This is the **single source of truth** for the grid's configuration.\n * All inputs (gridConfig, light DOM, individual props) converge here.\n *\n * **Precedence (lowest → highest):**\n * 1. `gridConfig` property - base config object\n * 2. Light DOM `<tbw-grid-column>` elements - declarative columns\n * 3. `columns` property - programmatic columns override\n * 4. Inferred columns - auto-detected from row data\n * 5. Individual props (`fitMode`, `editOn`) - convenience overrides\n *\n * After this method runs:\n * - `#effectiveConfig` contains the merged result\n * - `_columns` is NOT set here (done by #getColumnConfiguration + #processColumns)\n * - Plugins receive config via their attach() method\n */\n #mergeEffectiveConfig(): void {\n const base: GridConfig<T> = this.#gridConfig ? { ...this.#gridConfig } : {};\n let columns: ColumnConfig<T>[] = Array.isArray(base.columns) ? [...base.columns] : [];\n\n // Light DOM cached parse (if already parsed by columns pipeline); non-invasive merge (fill gaps only)\n const domCols: ColumnConfig<T>[] = ((this as any).__lightDomColumnsCache || []).map((c: ColumnConfig<T>) => ({\n ...c,\n }));\n if (domCols.length) {\n const map: Record<string, ColumnConfig<T>> = {};\n columns.forEach((c) => (map[(c as any).field] = c));\n domCols.forEach((c: any) => {\n const exist = map[c.field];\n if (!exist) {\n columns.push(c);\n map[c.field] = c;\n } else {\n if (c.header && !exist.header) exist.header = c.header;\n if (c.type && !exist.type) exist.type = c.type;\n exist.sortable = exist.sortable || c.sortable;\n if (c.resizable) exist.resizable = true;\n if (c.editable) exist.editable = true;\n // Merge framework adapter renderers/editors from DOM (support both 'renderer' alias and 'viewRenderer')\n const cRenderer = (c as any).renderer || c.viewRenderer;\n const existRenderer = (exist as any).renderer || exist.viewRenderer;\n if (cRenderer && !existRenderer) {\n exist.viewRenderer = cRenderer;\n if ((c as any).renderer) (exist as any).renderer = cRenderer;\n }\n if (c.editor && !exist.editor) exist.editor = c.editor;\n }\n });\n }\n\n // Columns prop highest structural precedence\n if (this.#columns && (this.#columns as ColumnConfig<T>[]).length) {\n columns = [...(this.#columns as ColumnConfig<T>[])];\n }\n\n // Inference if still empty\n if ((!columns || columns.length === 0) && this._rows.length) {\n const result = inferColumns(this._rows as Record<string, unknown>[]);\n columns = result.columns as ColumnConfig<T>[];\n }\n\n if (columns.length) {\n // Apply per-column defaults (sortable/resizable default true unless explicitly false)\n columns.forEach((c) => {\n if (c.sortable === undefined) c.sortable = true;\n if (c.resizable === undefined) c.resizable = true;\n // Store original configured width for reset on double-click (only numeric widths)\n const internal = c as ColumnInternal<T>;\n if (internal.__originalWidth === undefined && typeof c.width === 'number') {\n internal.__originalWidth = c.width;\n }\n });\n // Preserve processed columns (with __compiledView etc.) if already set by #getColumnConfiguration\n // Only set base.columns if effectiveConfig.columns is empty or doesn't have compiled templates\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n const alreadyProcessed = existingCols?.some((c) => c.__compiledView || c.__compiledEditor);\n if (alreadyProcessed) {\n // Keep existing processed columns\n base.columns = existingCols as ColumnConfig<T>[];\n } else {\n base.columns = columns;\n }\n } else {\n // No new columns computed, but preserve existing if processed\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n if (existingCols?.some((c) => c.__compiledView || c.__compiledEditor)) {\n base.columns = existingCols as ColumnConfig<T>[];\n }\n }\n\n // Individual prop overrides (behavioral)\n if (this.#fitMode) base.fitMode = this.#fitMode;\n if (!base.fitMode) base.fitMode = 'stretch';\n if (this.#editOn) base.editOn = this.#editOn;\n\n // Merge light DOM shell configuration\n if (this.#shellState.lightDomTitle) {\n if (!base.shell) base.shell = {};\n if (!base.shell.header) base.shell.header = {};\n if (!base.shell.header.title) {\n base.shell.header.title = this.#shellState.lightDomTitle;\n }\n }\n\n // Apply rowHeight from config if specified\n if (base.rowHeight && base.rowHeight > 0) {\n this._virtualization.rowHeight = base.rowHeight;\n }\n\n // Store columnState from gridConfig if not already set\n if (base.columnState && !this.#initialColumnState) {\n this.#initialColumnState = base.columnState;\n }\n\n this.#effectiveConfig = base;\n // Note: _columns is a getter/setter for effectiveConfig.columns\n // #getColumnConfiguration() populates it, and we preserve those processed columns above\n // Plugins (like ReorderPlugin) modify effectiveConfig.columns via the _columns setter\n\n // If fixed mode and width not specified: assign default 80px\n if (base.fitMode === 'fixed') {\n this._columns.forEach((c) => {\n if (c.width == null) (c as ColumnConfig<T>).width = 80;\n });\n }\n\n // Apply animation configuration to CSS variables\n this.#applyAnimationConfig();\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 */\n #applyAnimationConfig(): void {\n const config: AnimationConfig = {\n ...DEFAULT_ANIMATION_CONFIG,\n ...this.#effectiveConfig.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 any, 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 #setup(): void {\n if (!this.isConnected) return;\n if (!this._headerRowEl || !this._bodyEl) {\n return;\n }\n\n // Seed effectiveConfig.columns from config sources before getColumnConfiguration\n // This ensures columns from gridConfig/columns prop are available for merging with light DOM\n // Preserve hidden state from existing columns (visibility is runtime state)\n const configCols = (this.#gridConfig?.columns || this.#columns || []) as ColumnConfig<T>[];\n if (configCols.length) {\n // Preserve hidden state from existing effectiveConfig.columns\n const existingHiddenMap = new Map(this._columns.filter((c) => c.hidden).map((c) => [c.field, true]));\n const seeded = configCols.map((c) => ({\n ...c,\n hidden: existingHiddenMap.get(c.field) ?? c.hidden,\n }));\n this._columns = seeded as ColumnInternal<T>[];\n }\n\n getColumnConfiguration(this);\n this.#mergeEffectiveConfig();\n this.#updatePluginConfigs(); // Sync plugin configs (including auto-detection) before processing\n\n // Store base columns before plugin transformation (like #rows for row processing)\n this.#baseColumns = [...this._columns];\n\n this.#rebuildRowModel(); // Runs processRows hooks (must run before processColumns for tree plugin)\n this.#processColumns(); // Runs processColumns hooks\n\n // Apply initial column state (from gridConfig.columnState or columnState setter)\n if (this.#initialColumnState) {\n const state = this.#initialColumnState;\n this.#initialColumnState = undefined; // Clear to avoid re-applying\n this.#applyColumnStateInternal(state);\n }\n\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed' && !this.__didInitialAutoSize) {\n requestAnimationFrame(() => autoSizeColumns(this));\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 // Run plugin afterRender hooks (column groups, sticky, etc.)\n queueMicrotask(() => this.#pluginManager?.afterRender());\n }\n\n /** Internal method to apply column state without triggering setup loop */\n #applyColumnStateInternal(state: GridColumnState): void {\n // Get all columns from effectiveConfig (single source of truth)\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n applyColumnState(this, state, allCols, plugins);\n\n // Update hidden property on columns based on state\n for (const colState of state.columns) {\n const col = allCols.find((c) => c.field === colState.field);\n if (col) {\n col.hidden = !colState.visible;\n }\n }\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 * @internal Plugin API\n */\n findHeaderRow(): HTMLElement {\n return this.#shadow.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 * @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.\n * Returns true if any plugin handled the event.\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 cellClickEvent: CellClickEvent = {\n row,\n rowIndex,\n colIndex,\n field: col.field,\n value: (row as any)[col.field],\n cellEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onCellClick(cellClickEvent) ?? false;\n }\n\n /**\n * Dispatch a row click event to the plugin system.\n * Returns true if any plugin handled the event.\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 return this.#pluginManager?.onRowClick(rowClickEvent) ?? false;\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 * @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 * Build a CellMouseEvent from a native MouseEvent.\n * Extracts cell/row information from the event target.\n */\n #buildCellMouseEvent(e: MouseEvent, type: 'mousedown' | 'mousemove' | 'mouseup'): 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 still not inside our shadow root (e.g., for document-level events),\n // use elementFromPoint to find the actual element under the mouse\n if (target && !this.#shadow.contains(target)) {\n const elAtPoint = this.#shadow.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: T | undefined;\n let field: string | undefined;\n let value: unknown;\n let column: any;\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 = this._rows[rowIndex];\n column = this._columns[colIndex];\n field = column?.field;\n value = row && field ? (row as any)[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,\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 */\n #handleMouseDown(e: MouseEvent): void {\n const event = this.#buildCellMouseEvent(e, 'mousedown');\n const handled = this.#pluginManager?.onCellMouseDown(event) ?? false;\n\n // If any plugin handled mousedown, start tracking for drag\n if (handled) {\n this.#isDragging = true;\n }\n }\n\n /**\n * Handle mousemove events (only when dragging).\n */\n #handleMouseMove(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mousemove');\n this.#pluginManager?.onCellMouseMove(event);\n }\n\n /**\n * Handle mouseup events.\n */\n #handleMouseUp(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mouseup');\n this.#pluginManager?.onCellMouseUp(event);\n this.#isDragging = false;\n }\n\n // API consumed by internal utils (rows.ts) - delegates to editing.ts\n get changedRows(): T[] {\n return getChangedRows(this);\n }\n\n get changedRowIndices(): number[] {\n return getChangedRowIndices(this);\n }\n\n async resetChangedRows(silent?: boolean): Promise<void> {\n resetChangedRows(this, silent);\n }\n\n async beginBulkEdit(rowIndex: number): Promise<void> {\n beginBulkEdit(this, rowIndex, {\n findRenderedRowElement: (idx) => this.findRenderedRowElement?.(idx) ?? null,\n });\n }\n\n async commitActiveRowEdit(): Promise<void> {\n commitActiveRowEdit(this);\n }\n\n async cancelActiveRowEdit(): Promise<void> {\n cancelActiveRowEdit(this);\n }\n\n async ready(): Promise<void> {\n return this.#readyPromise;\n }\n\n async forceLayout(): Promise<void> {\n this.#setup();\n await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));\n }\n\n /** Public method: returns a frozen snapshot of the merged effective configuration */\n async getConfig(): Promise<Readonly<GridConfig<T>>> {\n return Object.freeze({ ...(this.#effectiveConfig || {}) });\n }\n\n // ---------------- Column Visibility API ----------------\n // Delegates to column-state.ts pure functions\n\n /** Visibility callbacks for column-state.ts functions */\n #visibilityCallbacks: VisibilityCallbacks = {\n emit: (name, detail) => this.#emit(name, detail),\n clearRowPool: () => {\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n },\n setup: () => this.#setup(),\n requestStateChange: () => this.requestStateChange(),\n };\n\n setColumnVisible(field: string, visible: boolean): boolean {\n return setColumnVisible(this, field, visible, this.#visibilityCallbacks);\n }\n\n toggleColumnVisibility(field: string): boolean {\n return toggleColumnVisibility(this, field, this.#visibilityCallbacks);\n }\n\n isColumnVisible(field: string): boolean {\n return isColumnVisible(this, field);\n }\n\n showAllColumns(): void {\n showAllColumns(this, this.#visibilityCallbacks);\n }\n\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n return getAllColumns(this);\n }\n\n setColumnOrder(order: string[]): void {\n setColumnOrder(this, order, {\n renderHeader: () => renderHeader(this),\n updateTemplate: () => updateTemplate(this),\n refreshVirtualWindow: () => this.refreshVirtualWindow(true),\n });\n }\n\n getColumnOrder(): string[] {\n return getColumnOrder(this);\n }\n\n // ---------------- Column State API ----------------\n\n /**\n * Get the current column state, including order, width, visibility, sort, and plugin state.\n * Returns a serializable object suitable for localStorage or database storage.\n */\n getColumnState(): GridColumnState {\n const plugins = this.#pluginManager?.getAll() ?? [];\n return collectColumnState(this, plugins as BaseGridPlugin[]);\n }\n\n /**\n * Set the column state, restoring order, width, visibility, sort, and plugin state.\n * Use this to restore previously saved column state.\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\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 */\n get columnState(): GridColumnState | undefined {\n return this.getColumnState();\n }\n\n /**\n * Apply column state internally.\n */\n #applyColumnState(state: GridColumnState): void {\n // Clear hidden flags before applying state\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n this.#applyColumnStateInternal(state);\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 * @internal Plugin API\n */\n requestStateChange(): void {\n if (!this.#stateChangeHandler) {\n this.#stateChangeHandler = createStateChangeHandler(\n this,\n () => (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[],\n (state) => this.#emit('column-state-change', state),\n );\n }\n this.#stateChangeHandler();\n }\n\n /**\n * Reset column state to initial configuration.\n * Clears all user modifications (order, width, visibility, sort).\n */\n resetColumnState(): void {\n // Clear initial state\n this.#initialColumnState = undefined;\n\n // Clear hidden flag on all columns\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n // Reset sort state\n this._sortState = null;\n this.__originalOrder = [];\n\n // Re-initialize columns from config\n this.#mergeEffectiveConfig();\n this.#setup();\n\n // Notify plugins to reset their state\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n // Pass empty state to indicate reset\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 // Emit state change\n this.requestStateChange();\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 // exposes the public API surface.\n\n /** Check if the tool panel is currently open. */\n get isToolPanelOpen(): boolean {\n return this.#shellController.isPanelOpen;\n }\n\n /**\n * Get the currently active tool panel ID, or null if none is open.\n * @deprecated Use isToolPanelOpen and expandedToolPanelSections instead.\n */\n get activeToolPanel(): string | null {\n return this.#shellController.activePanel;\n }\n\n /** Get the IDs of expanded accordion sections. */\n get expandedToolPanelSections(): string[] {\n return this.#shellController.expandedSections;\n }\n\n /** Open the tool panel (accordion view with all registered panels). */\n openToolPanel(): void {\n this.#shellController.openToolPanel();\n }\n\n /** Close the tool panel. */\n closeToolPanel(): void {\n this.#shellController.closeToolPanel();\n }\n\n /** Toggle the tool panel open/closed. */\n toggleToolPanel(): void {\n this.#shellController.toggleToolPanel();\n }\n\n /** Toggle an accordion section expanded/collapsed. */\n toggleToolPanelSection(sectionId: string): void {\n this.#shellController.toggleToolPanelSection(sectionId);\n }\n\n /** Get registered tool panel definitions. */\n getToolPanels(): ToolPanelDefinition[] {\n return this.#shellController.getToolPanels();\n }\n\n /** Register a custom tool panel (without creating a plugin). */\n registerToolPanel(panel: ToolPanelDefinition): void {\n this.#shellState.apiToolPanelIds.add(panel.id);\n this.#shellController.registerToolPanel(panel);\n }\n\n /** Unregister a custom tool panel. */\n unregisterToolPanel(panelId: string): void {\n this.#shellState.apiToolPanelIds.delete(panelId);\n this.#shellController.unregisterToolPanel(panelId);\n }\n\n /** Get registered header content definitions. */\n getHeaderContents(): HeaderContentDefinition[] {\n return this.#shellController.getHeaderContents();\n }\n\n /** Register custom header content (without creating a plugin). */\n registerHeaderContent(content: HeaderContentDefinition): void {\n this.#shellController.registerHeaderContent(content);\n }\n\n /** Unregister custom header content. */\n unregisterHeaderContent(contentId: string): void {\n this.#shellController.unregisterHeaderContent(contentId);\n }\n\n /** Get all registered toolbar buttons. */\n getToolbarButtons(): ToolbarButtonInfo[] {\n return this.#shellController.getToolbarButtons();\n }\n\n /** Register a custom toolbar button programmatically. */\n registerToolbarButton(button: ToolbarButtonConfig): void {\n this.#shellController.registerToolbarButton(button);\n }\n\n /** Unregister a custom toolbar button. */\n unregisterToolbarButton(buttonId: string): void {\n this.#shellController.unregisterToolbarButton(buttonId);\n }\n\n /** Enable/disable a toolbar button by ID. */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): void {\n this.#shellController.setToolbarButtonDisabled(buttonId, disabled);\n }\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 refreshShellHeader(): void {\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 // Re-render the entire grid (shell structure may change)\n this.#render();\n this.#afterConnect();\n }\n\n // #region Custom Styles API\n /** Map of registered custom style elements by ID */\n #customStyles = new Map<string, HTMLStyleElement>();\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 * @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 // Remove existing style with same ID\n this.unregisterStyles(id);\n\n // Create and inject new style element\n const styleEl = document.createElement('style');\n styleEl.id = `tbw-custom-${id}`;\n styleEl.textContent = css;\n this.#shadow.appendChild(styleEl);\n this.#customStyles.set(id, styleEl);\n }\n\n /**\n * Remove previously registered custom styles.\n * @param id - The ID used when registering the styles\n */\n unregisterStyles(id: string): void {\n const existing = this.#customStyles.get(id);\n if (existing) {\n existing.remove();\n this.#customStyles.delete(id);\n }\n }\n\n /**\n * Get list of registered custom style IDs.\n */\n getRegisteredStyles(): string[] {\n return Array.from(this.#customStyles.keys());\n }\n // #endregion\n\n /**\n * Set up MutationObserver to watch for light DOM changes.\n * This handles frameworks like Angular that project content asynchronously.\n * When shell-related elements are added/changed, the shell header is updated.\n */\n #setupLightDomObserver(): void {\n // Clean up any existing observer\n if (this.#lightDomObserver) {\n this.#lightDomObserver.disconnect();\n }\n\n // Debounce multiple mutations into a single update\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let needsShellUpdate = false;\n let needsColumnUpdate = false;\n\n const processPendingUpdates = () => {\n debounceTimer = null;\n\n if (needsShellUpdate) {\n // Re-parse shell and update header if title changed\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.#mergeEffectiveConfig();\n const shellHeader = this.#shadow.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 needsShellUpdate = false;\n }\n\n if (needsColumnUpdate) {\n // Clear column cache and re-run setup\n this.__lightDomColumnsCache = undefined;\n this.#setup();\n needsColumnUpdate = false;\n }\n };\n\n this.#lightDomObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n // Check added nodes for shell/column elements\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 if (tagName === 'tbw-grid-header') {\n needsShellUpdate = true;\n } else if (tagName === 'tbw-grid-column' || tagName === 'tbw-grid-detail') {\n needsColumnUpdate = true;\n }\n }\n\n // Check for attribute changes on shell elements\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 (tagName === 'tbw-grid-header') {\n needsShellUpdate = true;\n } else if (tagName === 'tbw-grid-column') {\n needsColumnUpdate = true;\n }\n }\n }\n\n // Debounce updates\n if ((needsShellUpdate || needsColumnUpdate) && !debounceTimer) {\n debounceTimer = setTimeout(processPendingUpdates, 0);\n }\n });\n\n // Observe direct children and their attributes\n this.#lightDomObserver.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['title', 'field', 'header', 'width', 'hidden'],\n });\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 * Debounced to coalesce multiple calls (e.g., from React StrictMode double-mounting).\n * @internal Used by framework integration libraries (Angular, React, Vue)\n */\n refreshColumns(): void {\n // Debounce: if already pending, skip this call\n if (this.#refreshColumnsRaf) {\n return;\n }\n\n this.#refreshColumnsRaf = requestAnimationFrame(() => {\n this.#refreshColumnsRaf = 0;\n this.#doRefreshColumns();\n });\n }\n\n /**\n * Internal implementation of refreshColumns, called after debounce.\n */\n #doRefreshColumns(): 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 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 // Merge the new title into effectiveConfig\n this.#mergeEffectiveConfig();\n // Update the existing shell header element with new HTML\n const shellHeader = this.#shadow.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 // Re-run setup which handles column configuration, headers, and rows\n this.#setup();\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 * @internal Plugin API\n */\n refreshVirtualWindow(force = 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 this.#pluginManager?.afterRender();\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 this.#pluginManager?.afterRender();\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 after layout stabilizes\n requestAnimationFrame(() => this.refreshVirtualWindow(force));\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) {\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 // Re-merge config to pick up any newly parsed light DOM shell settings\n this.#mergeEffectiveConfig();\n\n const shellConfig = this.#effectiveConfig?.shell;\n\n // Render using direct DOM construction (2-3x faster than innerHTML)\n const hasShell = buildGridDOMIntoShadow(this.#shadow, shellConfig, this.#shellState, this.#effectiveConfig?.icons);\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.#shadow, this.#effectiveConfig?.shell, this.#shellState, {\n onPanelToggle: () => this.toggleToolPanel(),\n onSectionToggle: (sectionId: string) => this.toggleToolPanelSection(sectionId),\n onToolbarButtonClick: (buttonId) => this.#handleToolbarButtonClick(buttonId),\n });\n\n // Set up tool panel resize\n this.#resizeCleanup?.();\n this.#resizeCleanup = setupToolPanelResize(this.#shadow, 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 * Handle toolbar button click.\n * Note: Config/API buttons use element or render, so they handle their own clicks.\n * This method is kept for backwards compatibility but may emit an event in the future.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n #handleToolbarButtonClick(_buttonId: string): void {\n // No-op: Config and API buttons now use element/render and handle their own events.\n // Light DOM buttons use slot and handle their own events.\n // This callback may be used for future extensibility (e.g., emitting an event).\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 any).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 } 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 */\nexport interface GridElementRef {\n shadowRoot: ShadowRoot | null;\n rows: unknown[];\n columns: ColumnConfig[];\n gridConfig: GridConfig;\n /** Current focused row index */\n _focusRow: number;\n /** Current focused column index */\n _focusCol: number;\n /** AbortSignal that is aborted when the grid disconnects from the DOM */\n disconnectSignal: AbortSignal;\n requestRender(): void;\n requestAfterRender(): void;\n forceLayout(): Promise<void>;\n dispatchEvent(event: Event): boolean;\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\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 */\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\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 * Abstract base class for all grid plugins.\n *\n * @template TConfig - Configuration type for the plugin\n */\nexport abstract class BaseGridPlugin<TConfig = unknown> implements GridPlugin {\n /** Unique plugin identifier (derived from class name by default) */\n abstract readonly name: string;\n\n /** Plugin version - override in subclass if needed */\n readonly version: string = '1.0.0';\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 * 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 attach(grid: GridElement): void {\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 detach(): void {\n // Override in subclass\n }\n\n /**\n * Get another plugin instance from the same grid.\n * Use for inter-plugin communication.\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 * Request a re-render of the grid.\n */\n protected requestRender(): void {\n this.grid?.requestRender?.();\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 as any)?.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 as any)?._visibleColumns ?? [];\n }\n\n /**\n * Get the shadow root of the grid.\n */\n protected get shadowRoot(): ShadowRoot | null {\n return this.grid?.shadowRoot ?? null;\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 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 /**\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 */\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 */\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 */\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 */\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 * @packageDocumentation\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\n// #region Public API surface - only export what consumers need\nexport { DataGridElement, DataGridElement as GridElement } from './lib/core/grid';\n\n// Event name constants for DataGrid (public API)\nexport const DGEvents = {\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 GROUP_TOGGLE: 'group-toggle',\n COLUMN_STATE_CHANGE: 'column-state-change',\n} as const;\n\nexport type DGEventName = (typeof DGEvents)[keyof typeof DGEvents];\n\n// Plugin event constants (mirrors DGEvents pattern)\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\nexport type PluginEventName = (typeof PluginEvents)[keyof typeof PluginEvents];\n\n// Public type exports\nexport type {\n ActivateCellDetail,\n AggregatorRef,\n // Animation types\n AnimationConfig,\n AnimationMode,\n AnimationStyle,\n BaseColumnConfig,\n // Event detail types\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 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 RowCommitDetail,\n // Grouping & Footer types\n RowGroupRenderConfig,\n ShellConfig,\n ShellHeaderConfig,\n SortChangeDetail,\n // Sorting types\n SortHandler,\n SortState,\n ToolPanelConfig,\n ToolPanelDefinition,\n ToolbarButtonConfig,\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 Types\n// Only export types that consumers need to use plugins\n// Plugin classes are available via @toolbox-web/grid/plugins/<name> or from 'all.ts'\n\n// Selection plugin\nexport type { CellRange, SelectionChangeDetail, SelectionConfig, SelectionMode } from './lib/plugins/selection/types';\n\n// Tree plugin\nexport type { TreeConfig, TreeExpandDetail } from './lib/plugins/tree/types';\n\n// Filtering plugin\nexport type {\n FilterConfig,\n FilterHandler,\n FilterModel,\n FilterOperator,\n FilterType,\n FilterValuesHandler,\n} from './lib/plugins/filtering/types';\n\n// Multi-sort plugin\nexport type { MultiSortConfig, SortModel } from './lib/plugins/multi-sort/types';\n\n// Export plugin\nexport type { ExportFormat, ExportParams } from './lib/plugins/export/types';\n\n// Pinned rows plugin\nexport type { PinnedRowsContext, PinnedRowsPanel } from './lib/plugins/pinned-rows/types';\n\n// Pivot plugin\nexport type { PivotConfig, PivotResult, PivotValueField } from './lib/plugins/pivot/types';\n\n// Server-side plugin\nexport type { GetRowsParams, GetRowsResult, ServerSideDataSource } from './lib/plugins/server-side/types';\n\n// Undo/Redo plugin\nexport type { EditAction } from './lib/plugins/undo-redo/types';\n\n// Grouping rows plugin\nexport type { GroupingRowsConfig } from './lib/plugins/grouping-rows/types';\n\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 { GridCSSVars, GridClasses, GridDataAttrs, GridSelectors } from './lib/core/constants';\nexport type { GridCSSVar, GridClassName, GridDataAttr } from './lib/core/constants';\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":["getSortState","grid","sortMap","collectColumnState","plugins","columns","sortStates","col","index","state","internalCol","sortState","plugin","pluginState","applyColumnState","allColumns","stateMap","updatedColumns","s","updated","a","b","orderA","orderB","sortedByPriority","primarySort","colState","createStateChangeHandler","getPlugins","emit","timeoutId","setColumnVisible","field","visible","callbacks","allCols","c","toggleColumnVisibility","isColumnVisible","showAllColumns","getAllColumns","getColumnOrder","setColumnOrder","order","columnMap","reordered","FitModeEnum","DEFAULT_ANIMATION_CONFIG","DEFAULT_GRID_ICONS","inferType","value","inferColumns","rows","provided","typeMap","sample","k","v","type","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","el","tagName","attr","attrsToRemove","attrName","name","evalTemplateString","raw","ctx","parts","evaluated","_m","expr","res","evalSingle","finalStr","postProcess","allEmpty","p","key","dotChain","out","str","finalCellScrub","cell","n","compileTemplate","forceBlank","fn","parseLightDomColumns","host","rawType","header","sortable","editable","config","widthAttr","numericWidth","minWidthAttr","numericMinWidth","editorName","rendererName","optionsAttr","item","label","viewTpl","editorTpl","headerTpl","adapters","viewTarget","viewAdapter","renderer","editorTarget","editorAdapter","editor","mergeColumns","programmatic","dom","domMap","existing","cRenderer","existingRenderer","merged","d","m","dRenderer","mRenderer","addPart","token","getColumnConfiguration","lightDomColumns","autoSizeColumns","mode","headerCells","changed","i","headerCell","max","rowEl","w","updateTemplate","min","defaultEditorFor","column","input","e","select","opt","o","commitValue","values","booleanCellHTML","formatDateValue","getRowIndexFromCell","getColIndexFromCell","clearCellFocus","handleGridKeyDown","maxRow","maxCol","editing","colType","path","target","isFormField","tag","ensureCellVisible","options","rowHeight","container","viewportEl","scrollEl","visibleHeight","y","isEditing","rowIndex","vStart","vEnd","scrollArea","offsets","cellRect","scrollAreaRect","cellLeft","cellRight","visibleLeft","visibleRight","focusTarget","FOCUSABLE_EDITOR_SELECTOR","cellTemplate","rowTemplate","createCellFromTemplate","createRowFromTemplate","CELL_CACHE_KEY","CELL_CACHE_EPOCH_KEY","invalidateCellCache","renderVisibleRows","start","end","epoch","renderRowHook","needed","bodyEl","colLen","headerRowCount","handleRowClick","hasRenderRowPlugins","rowData","rowEpoch","prevRef","cellCount","structureValid","dataRefChanged","needsExternalRebuild","hasEditing","hasEditingCells","isActivelyEditedRow","clearEditingState","renderInlineRow","fastPatchRow","children","inlineEnterEdit","isChanged","hasChangedClass","colsLen","childLen","minLen","focusRow","focusCol","hasSpecialCols","rowIndexStr","shouldHaveFocus","hasFocus","cellRenderer","produced","displayStr","formatted","gridEl","fragment","colIndex","format","compiled","tplHolder","viewRenderer","externalView","needsSanitization","spec","placeholder","context","output","blocked","rawTpl","textContent","isDbl","firstCell","cellEl","focusChanged","rawMode","startRowEdit","targetCell","isSafePropertyKey","incrementEditingCount","count","wireEditorInputs","editorHost","commit","cancel","_editFinalizedRef","getInputValue","exitRowEdit","revert","snapshot","current","val","commitCellValue","rowIdx","colIdx","rowEl2","newValue","firstTime","skipFocus","originalValue","editFinalized","inputLike","editorSpec","clone","compiledEditor","node","g","hasHTMLInput","emitEvent","eventName","detail","getChangedRows","getChangedRowIndices","resetChangedRows","silent","r","beginBulkEdit","commitActiveRowEdit","cancelActiveRowEdit","getCellContext","handleCellMousedown","handleCellClick","handleCellDblclick","col2","handleCellKeydown","selectEl","newVal","setupCellEventDelegation","signal","getEditMode","editMode","defaultComparator","builtInSort","comparator","direction","rA","rB","finalizeSortResult","sortedRows","dir","renderHeader","toggleSort","applySort","h","result","setIcon","element","icon","headerRow","maybeTpl","span","active","icons","iconValue","handle","hasIdleCallback","scheduleIdle","callback","cancelIdle","createResizeController","resizeState","pendingRaf","prevCursor","prevUserSelect","onMove","delta","width","justFinishedResize","onUp","hadResize","rect","createElement","attrs","div","className","button","content","slot","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","configButtons","hasConfigButtons","hasApiButtons","hasPanels","showSeparator","sortedConfigButtons","sortedApiButtons","toolbarHtml","isOpen","parseLightDomShell","headerEl","headerContents","parseLightDomToolButtons","toolButtonsContainer","parseLightDomToolPanels","rendererFactory","id","tooltip","render","adapterRenderer","wrapper","existingPanel","cleanup","setupShellEventListeners","shadowRoot","customBtn","btnId","sectionId","setupToolPanelResize","onResize","shellBody","position","minWidth","startX","startWidth","maxWidth","isResizing","onMouseMove","newWidth","onMouseUp","finalWidth","onMouseDown","renderCustomToolbarButtons","allButtons","existingCleanup","renderHeaderContent","contentArea","sortedContents","renderPanelContent","expandIcon","collapseIcon","panelId","isExpanded","chevron","updateToolbarActiveStates","panelToggle","updatePanelState","getToolbarButtonsInfo","cleanupShellState","createShellController","initialized","controller","firstPanel","shadow","updateAccordionSectionState","otherId","otherPanel","contentEl","renderAccordionSectionContent","contentId","buttonId","_buttonId","_disabled","expanded","buildGridDOMIntoShadow","shellConfig","hasShell","headerOptions","sortedPanels","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","PluginManager","PluginClass","total","beforeRowIndex","adjustedStart","pluginStart","row","query","responses","response","event","focusedCell","left","right","skipScroll","panels","contents","DataGridElement","adapter","#shadow","#initialized","#readyPromise","#readyResolve","#rows","#columns","#gridConfig","#fitMode","#editOn","#effectiveConfig","#connected","#pendingUpdate","#pendingUpdateFlags","#scrollRaf","#pendingScrollTop","#hasScrollPlugins","#renderRowHook","#isDragging","#touchState","#eventAbortController","#resizeObserver","#rowHeightObserver","#refreshColumnsRaf","#lightDomObserver","#idleCallbackHandle","#pooledScrollEvent","#pluginManager","#eventListenersAdded","#scrollAbortController","#stateChangeHandler","#initialColumnState","#shellState","#shellController","#resizeCleanup","#baseColumns","oldValue","#queueUpdate","#injectStyles","#emit","sheet","styles","resolve","gridCssText","stylesheet","cssText","rule","err","#rebuildRowModel","#processColumns","#initializePlugins","pluginsConfig","#injectAllPluginStyles","allStyles","styleEl","#updatePluginConfigs","isLightDom","isApiRegistered","#collectPluginShellContributions","#destroyPlugins","pluginPanels","pluginContents","#getToolPanelRendererFactory","instanceAdapter","#mergeEffectiveConfig","#render","#afterConnect","#setupLightDomObserver","#rowHeightObserverSetup","#customStyles","prop","gridRoot","defaultOpen","#setup","#setupScrollListeners","#handleMouseDown","#handleMouseMove","#handleMouseUp","userRowHeight","#measureRowHeight","#updateAriaSelection","firstRow","cells","maxCellHeight","rowRect","measuredHeight","scrollSignal","fauxScrollbar","rowsEl","currentScrollTop","rawStart","evenAlignedStart","subPixelOffset","#onScrollBatched","isHorizontal","#setupRowHeightObserver","isActiveRow","#flushPendingUpdates","flags","#applyGridConfigUpdate","#applyColumnsUpdate","#applyRowsUpdate","#applyFitModeUpdate","#applyEditModeUpdate","hadShell","hadToolPanel","accordionSectionsBefore","nowNeedsShell","nowHasToolPanels","toolPanelCountChanged","#updateShellHeaderInPlace","sourceColumns","visibleCols","hiddenCols","processedColumns","processedMap","processed","originalRows","processedRows","base","domCols","map","exist","existRenderer","internal","existingCols","#applyAnimationConfig","enabled","#renderVisibleRows","#lastAriaRowCount","#lastAriaColCount","#updateAriaCounts","rowCount","colCount","prevRowCount","configCols","existingHiddenMap","seeded","#applyColumnStateInternal","scrollEvent","cellClickEvent","rowClickEvent","headerClickEvent","#buildCellMouseEvent","elAtPoint","idx","#visibilityCallbacks","#applyColumnState","disabled","css","debounceTimer","needsShellUpdate","needsColumnUpdate","processPendingUpdates","hadTitle","hadToolButtons","hasToolButtons","newHeaderHtml","temp","newHeader","#setupShellListeners","mutations","mutation","#doRefreshColumns","#calculateTotalSpacerHeight","totalRows","fauxScrollHeight","viewportHeight","scrollAreaEl","scrollAreaHeight","viewportHeightDiff","pluginExtraHeight","hScrollbarPadding","force","iterations","maxIterations","extraHeightBefore","pluginAdjustedStart","visibleCount","totalHeight","extraHeightBeforeStart","newFauxHeight","newViewportHeight","newTotalHeight","#handleToolbarButtonClick","PLUGIN_QUERIES","BaseGridPlugin","userIcons","iconKey","pluginOverride","message","GridClasses","GridDataAttrs","GridSelectors","GridCSSVars","DGEvents","PluginEvents","builtInAggregators","acc","sum","customAggregators","aggregatorRegistry","ref","builtInValueAggregators","vals","getValueAggregator","aggFunc","runValueAggregator","registerAggregator","unregisterAggregator","getAggregator","runAggregator","listAggregators"],"mappings":"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"}
1
+ {"version":3,"file":"grid.umd.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/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/rows.ts","../../../../libs/grid/src/lib/core/internal/keyboard.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';\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's shadow DOM.\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 */\nexport interface InternalGrid<T = any> extends PublicGrid<T>, GridConfig<T> {\n shadowRoot: ShadowRoot | null;\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 /** Set of row indices that have been modified. Injected by EditingPlugin. @internal */\n _changedRowIndices?: Set<number>;\n /** Get all changed rows. Injected by EditingPlugin. */\n changedRows?: T[];\n /** Get indices of all changed rows. Injected by EditingPlugin. */\n changedRowIndices?: number[];\n effectiveConfig?: GridConfig<T>;\n findHeaderRow?: () => HTMLElement;\n refreshVirtualWindow: (full: boolean) => void;\n updateTemplate?: () => void;\n findRenderedRowElement?: (rowIndex: number) => HTMLElement | null;\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 /** 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' | 'typeahead';\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 /** Column data type; inferred if omitted */\n type?: PrimitiveColumnType;\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/typeahead types - available options */\n options?: Array<{ label: string; value: unknown }> | (() => Array<{ label: string; value: unknown }>);\n /** For select/typeahead - 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\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 */\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// #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 * @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 * @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 * @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 */\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 */\nexport interface EditorExecContext<T = any> extends CellContext<T> {\n commit: (newValue: unknown) => void;\n cancel: () => void;\n}\n\n/** Controller managing drag-based column resize lifecycle. */\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/** Virtual window bookkeeping; modified in-place as scroll position changes. */\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 * @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, PrimitiveColumnType>;\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 */\nexport interface GridPlugin {\n /** Unique plugin identifier */\n readonly name: string;\n /** Plugin version */\n readonly version: string;\n /** CSS styles to inject into grid's shadow DOM */\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 * - `editOn` property - shorthand for `gridConfig.editOn`\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`, `editOn`) > `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 /** Sizing mode for columns. Can also be set via `fitMode` prop. */\n fitMode?: FitMode;\n /** Edit activation mode ('click' | 'dblClick' | false). Set to false to disable editing. Can also be set via `editOn` prop. */\n editOn?: string | boolean;\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// #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/**\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 buttons (rendered before tool panel toggles) */\n toolbarButtons?: ToolbarButtonConfig[];\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 button defined via config (programmatic approach).\n *\n * The grid does NOT create buttons - developers have full control over their own buttons.\n * Provide either:\n * - `element`: A ready-made DOM element (grid appends it to toolbar)\n * - `render`: A factory function that receives a container and appends content\n *\n * For declarative HTML buttons, use light-dom instead:\n * ```html\n * <tbw-grid>\n * <tbw-grid-header>\n * <button slot=\"toolbar\">My Button</button>\n * </tbw-grid-header>\n * </tbw-grid>\n * ```\n */\nexport interface ToolbarButtonConfig {\n /** Unique button ID */\n id: string;\n /** Tooltip / aria-label (for accessibility, used when grid generates panel toggle) */\n label?: string;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n\n /**\n * User-provided element. Grid appends it to the toolbar.\n * User is responsible for styling, event handlers, accessibility, etc.\n */\n element?: HTMLElement;\n /**\n * Render function called once. Receives container, user appends their DOM.\n * User is responsible for event handlers.\n * Return a cleanup function (optional).\n */\n render?: (container: HTMLElement) => void | (() => void);\n}\n\n/**\n * Toolbar button info returned by getToolbarButtons().\n */\nexport interface ToolbarButtonInfo {\n id: string;\n label: string;\n /** Source of this button: 'config' | 'light-dom' | 'panel-toggle' */\n source: 'config' | 'light-dom' | 'panel-toggle';\n /** For panel toggles, the associated panel ID */\n panelId?: string;\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\nexport interface CellCommitDetail<TRow = unknown> {\n /** The mutated row after commit. */\n row: TRow;\n /** Field name whose value changed. */\n field: string;\n /** New value 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 /** Indices parallel to changedRows. */\n changedRowIndices: number[];\n /** True if this row just entered the changed set. */\n firstTimeForRow: boolean;\n}\n\n/** Detail payload for a committed row edit (may or may not include changes). */\nexport interface RowCommitDetail<TRow = unknown> {\n /** Row index that lost edit focus. */\n rowIndex: number;\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 /** Indices of changed rows. */\n changedRowIndices: number[];\n}\n\n/** Emitted when the changed rows tracking set is cleared programmatically. */\nexport interface ChangedRowsResetDetail<TRow = unknown> {\n /** New (empty) changed rows array after reset. */\n rows: TRow[];\n /** Parallel indices (likely empty). */\n indices: number[];\n}\n\n/** Detail for a sort change (direction 0 indicates cleared sort). */\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/** Column resize event detail containing final pixel width. */\nexport interface ColumnResizeDetail {\n /** Resized column field key. */\n field: string;\n /** New width in pixels. */\n width: number;\n}\n\n/** Fired when keyboard navigation or programmatic focus changes active cell. */\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\nexport interface ExternalMountViewDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: { row: TRow; value: unknown; field: string; column: unknown };\n}\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\nexport interface DataGridEventMap<TRow = unknown> {\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 'activate-cell': ActivateCellDetail;\n 'column-state-change': GridColumnState;\n}\n\nexport type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = DataGridEventMap<TRow>[K];\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\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', 'typeahead']);\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, 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, PrimitiveColumnType> = {};\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, PrimitiveColumnType> = {};\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, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&#39;');\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 ToolbarButtonConfig,\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 buttons from shell state */\n getShellToolbarButtons: () => Map<string, ToolbarButtonConfig>;\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 #editOn?: string | boolean;\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 /** Set editOn source */\n setEditOn(editOn: string | boolean | undefined): void {\n this.#editOn = editOn;\n this.#sourcesChanged = true;\n }\n\n /** Get the raw editOn source */\n getEditOn(): string | boolean | undefined {\n return this.#editOn;\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 if (this.#editOn) base.editOn = this.#editOn;\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 buttons (from API - config buttons are already in base.shell.header.toolbarButtons)\n // We need to merge config buttons (from gridConfig) with API buttons (from registerToolbarButton)\n // API buttons can be added/removed dynamically, so we need to rebuild from current state each time\n const toolbarButtonsMap = this.#callbacks.getShellToolbarButtons();\n const apiButtons = Array.from(toolbarButtonsMap.values());\n\n // Get ORIGINAL config buttons (from gridConfig, not from previous merges)\n // We use a fresh read from gridConfig to avoid accumulating stale API buttons\n const originalConfigButtons = this.#gridConfig?.shell?.header?.toolbarButtons ?? [];\n\n // Merge: config buttons + API buttons (config takes precedence by id)\n const configIds = new Set(originalConfigButtons.map((b) => b.id));\n const mergedButtons = [...originalConfigButtons];\n for (const btn of apiButtons) {\n if (!configIds.has(btn.id)) {\n mergedButtons.push(btn);\n }\n }\n\n // Sort by order\n mergedButtons.sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n base.shell.header.toolbarButtons = mergedButtons;\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<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\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 }));\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 ? '&#x1F5F9;' : '&#9744;'}</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 * 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 return attr ? parseInt(attr, 10) : -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","/**\n * Event Delegation Module\n *\n * Handles delegated mousedown events on the grid body for focus management.\n * Uses event delegation (single listener on container) rather than per-cell\n * listeners to minimize memory usage.\n *\n * Edit triggering is handled separately by the EditingPlugin via\n * onCellClick and onKeyDown hooks.\n */\n\nimport type { InternalGrid } from '../types';\nimport { clearCellFocus, getColIndexFromCell, getRowIndexFromCell } from './utils';\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 * Set up delegated event listeners on the grid body.\n * Call once during grid initialization.\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","/**\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 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 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 // Set position: relative for the resize handle positioning context\n // Note: If a plugin applies position: sticky (e.g., PinnedColumnsPlugin), it will override this\n cell.style.position = 'relative';\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","import type { 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// 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.shadowRoot?.querySelector('.header-group-row') ? 2 : 1;\n grid.__cachedHeaderRowCount = headerRowCount;\n }\n\n // Pool management: grow pool if needed\n while (grid._rowPool.length < needed) {\n // Use template cloning - 3-4x faster than createElement + setAttribute\n const rowEl = createRowFromTemplate();\n rowEl.addEventListener('click', (e) => handleRowClick(grid, e, rowEl, false));\n rowEl.addEventListener('dblclick', (e) => handleRowClick(grid, e, rowEl, true));\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 - only if EditingPlugin has initialized the Set\n const isChanged = grid._changedRowIndices?.has(rowIndex) ?? false;\n const hasChangedClass = rowEl.classList.contains('changed');\n if (isChanged !== hasChangedClass) {\n rowEl.classList.toggle('changed', isChanged);\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 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 ) {\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 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 // 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 const cellRenderer = col.renderer || col.viewRenderer;\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 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 // Support both 'renderer' (ergonomic alias) and 'viewRenderer' (legacy)\n const viewRenderer = col.renderer || col.viewRenderer;\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 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 */\n// eslint-disable-next-line @typescript-eslint/no-unused-vars\nexport function handleRowClick(grid: InternalGrid, e: MouseEvent, rowEl: HTMLElement, _isDbl: boolean): 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 entire shadow root)\n clearCellFocus(grid.shadowRoot ?? grid._bodyEl);\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' || colType === 'typeahead') && (e.key === 'ArrowDown' || e.key === 'ArrowUp'))\n 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 an activate-cell event for custom handling but don't block navigation.\n case 'Enter':\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('activate-cell', { detail: { row: grid._focusRow, col: grid._focusCol } }),\n );\n // Don't prevent default or return - allow normal keyboard processing\n break;\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 const cell = rowEl?.children[grid._focusCol] as HTMLElement | undefined;\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.shadowRoot?.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 * 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 */\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 const rect = cell.getBoundingClientRect();\n resizeState = { startX: e.clientX, colIndex, startWidth: rect.width };\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 * Create a slot element for light DOM projection.\n */\nexport function slot(name?: string): HTMLSlotElement {\n const el = document.createElement('slot');\n if (name) el.name = name;\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 buttons with element/render (pre-sorted by order) */\n configButtons: Array<{\n id: string;\n hasElement?: boolean;\n hasRender?: boolean;\n }>;\n /** API toolbar buttons with element/render (pre-sorted by order) */\n apiButtons: Array<{\n id: string;\n hasElement?: boolean;\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 slot for header content\n const content = div('tbw-shell-content', { part: 'shell-content', role: 'presentation' });\n content.appendChild(slot('header-content'));\n header.appendChild(content);\n\n // Toolbar\n const toolbar = div('tbw-shell-toolbar', { part: 'shell-toolbar', role: 'presentation' });\n\n // Placeholders for config buttons with element or render function\n for (const btn of options.configButtons) {\n if (btn.hasElement || btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-btn-slot', { 'data-btn-slot': btn.id }));\n }\n }\n // Placeholders for API buttons with element or render function\n for (const btn of options.apiButtons) {\n if (btn.hasElement || btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-btn-slot', { 'data-btn-slot': btn.id }));\n }\n }\n\n // Light DOM slot for toolbar - always include for async rendering (React)\n toolbar.appendChild(slot('toolbar'));\n\n // Separator between config/API buttons and panel toggle\n const hasCustomButtons =\n options.configButtons.some((b) => b.hasElement || b.hasRender) ||\n options.apiButtons.some((b) => b.hasElement || b.hasRender);\n if (hasCustomButtons && 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 ToolbarButtonConfig,\n ToolbarButtonInfo,\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 * NOTE: This interface is being refactored. Config-like properties are moving\n * to effectiveConfig.shell. Only runtime state should remain here.\n *\n * @deprecated Properties that are configuration (toolPanels, headerContents, etc.)\n * are moving to ShellConfig in types.ts. Use ShellRuntimeState for new code.\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 /** Custom toolbar buttons registered via API */\n toolbarButtons: Map<string, ToolbarButtonConfig>;\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 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 /** 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 button render returns */\n toolbarButtonCleanups: Map<string, () => void>;\n}\n\n/**\n * Runtime-only shell state (not configuration).\n *\n * Configuration (toolPanels, headerContents, toolbarButtons, 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 button render returns */\n toolbarButtonCleanups: 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 toolbarButtonCleanups: 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 toolbarButtons: new Map(),\n hasToolButtonsContainer: false,\n lightDomHeaderContent: [],\n lightDomTitle: null,\n lightDomToolPanelIds: new Set(),\n apiToolPanelIds: new Set(),\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarButtonCleanups: new Map(),\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 buttons\n if (config?.header?.toolbarButtons?.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 buttons come from two sources:\n * 1. Light DOM slot (users provide their own HTML buttons)\n * 2. Config/API with element or render function (programmatic insertion)\n *\n * The grid does NOT create buttons from config (icon/action).\n * Users have full control over button 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 // Collect toolbar button sources\n const configButtons = config?.header?.toolbarButtons ?? [];\n const hasConfigButtons = configButtons.some((btn) => btn.element || btn.render);\n const hasApiButtons = [...state.toolbarButtons.values()].some((btn) => btn.element || btn.render);\n const hasPanels = state.toolPanels.size > 0;\n const hasCustomButtons = hasConfigButtons || hasApiButtons;\n const showSeparator = hasCustomButtons && hasPanels;\n\n // Sort config/API buttons by order for slot placement\n const sortedConfigButtons = [...configButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const sortedApiButtons = [...state.toolbarButtons.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build toolbar HTML\n let toolbarHtml = '';\n\n // Slots for config/API buttons with element or render function\n for (const btn of sortedConfigButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n for (const btn of sortedApiButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n\n // Light DOM slot - user provides their own button HTML\n // Always include slot so light-dom buttons can be added later (e.g., React async rendering)\n toolbarHtml += '<slot name=\"toolbar\"></slot>';\n\n // Separator between custom buttons 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\">\n <slot name=\"header-content\"></slot>\n </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\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 // Assign slot names for slotting into shadow DOM\n state.lightDomHeaderContent.forEach((el) => {\n el.setAttribute('slot', 'header-content');\n });\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 * Parse toolbar buttons container element (<tbw-grid-tool-buttons>).\n * This is a container element that gets slotted as-is into the toolbar.\n * Users put their buttons inside and have full control over their HTML.\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 is assigned slot=\"toolbar\" and slotted directly.\n */\nexport function parseLightDomToolButtons(host: HTMLElement, state: ShellState): void {\n // Look for the toolbar buttons container element\n const toolButtonsContainer = host.querySelector(':scope > tbw-grid-tool-buttons');\n if (!toolButtonsContainer) return;\n\n // Mark that we found the container (for shouldRenderShellHeader)\n state.hasToolButtonsContainer = true;\n\n // Assign slot so it gets slotted into the toolbar area\n toolButtonsContainer.setAttribute('slot', 'toolbar');\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 shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n callbacks: {\n onPanelToggle: () => void;\n onSectionToggle: (sectionId: string) => void;\n onToolbarButtonClick: (buttonId: string) => void;\n },\n): void {\n const toolbar = shadowRoot.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 // Handle custom toolbar buttons\n const customBtn = target.closest('[data-btn]') as HTMLElement | null;\n if (customBtn) {\n const btnId = customBtn.getAttribute('data-btn');\n if (btnId) {\n callbacks.onToolbarButtonClick(btnId);\n }\n }\n });\n }\n\n // Accordion header clicks\n const accordion = shadowRoot.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 shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n onResize: (width: number) => void,\n): () => void {\n const panel = shadowRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n const handle = shadowRoot.querySelector('[data-resize-handle]') as HTMLElement | null;\n const shellBody = shadowRoot.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 custom button elements/render functions into toolbar slots.\n */\nexport function renderCustomToolbarButtons(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n): void {\n const allButtons = [...(config?.header?.toolbarButtons ?? []), ...state.toolbarButtons.values()];\n\n for (const btn of allButtons) {\n const slot = shadowRoot.querySelector(`[data-btn-slot=\"${btn.id}\"]`);\n if (!slot) continue;\n\n // Clean up previous render if any\n const existingCleanup = state.toolbarButtonCleanups.get(btn.id);\n if (existingCleanup) {\n existingCleanup();\n state.toolbarButtonCleanups.delete(btn.id);\n }\n\n if (btn.element) {\n slot.appendChild(btn.element);\n } else if (btn.render) {\n const cleanup = btn.render(slot as HTMLElement);\n if (cleanup) {\n state.toolbarButtonCleanups.set(btn.id, cleanup);\n }\n }\n }\n}\n\n/**\n * Render header content from plugins into the shell content area.\n */\nexport function renderHeaderContent(shadowRoot: ShadowRoot, state: ShellState): void {\n const contentArea = shadowRoot.querySelector('.tbw-shell-content');\n if (!contentArea) return;\n\n // Sort by order\n const sortedContents = [...state.headerContents.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Create containers for each content piece (before the slot)\n const slot = contentArea.querySelector('slot[name=\"header-content\"]');\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 // Insert before the slot\n if (slot) {\n contentArea.insertBefore(container, slot);\n } else {\n contentArea.appendChild(container);\n }\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 shadowRoot: ShadowRoot,\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 = shadowRoot.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(shadowRoot: ShadowRoot, state: ShellState): void {\n // Update single panel toggle button\n const panelToggle = shadowRoot.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(shadowRoot: ShadowRoot, state: ShellState): void {\n const panel = shadowRoot.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 * Get all toolbar button info (for debugging/introspection only).\n * Since users have full control over their button HTML, this returns limited info.\n */\nexport function getToolbarButtonsInfo(config: ShellConfig | undefined, state: ShellState): ToolbarButtonInfo[] {\n const result: ToolbarButtonInfo[] = [];\n\n // Config buttons (element/render only)\n for (const btn of config?.header?.toolbarButtons ?? []) {\n result.push({\n id: btn.id,\n label: btn.label ?? '',\n source: 'config',\n });\n }\n\n // API buttons\n for (const btn of state.toolbarButtons.values()) {\n result.push({\n id: btn.id,\n label: btn.label ?? '',\n source: 'config',\n });\n }\n\n // Panel toggles (the only buttons the grid creates)\n for (const panel of state.toolPanels.values()) {\n result.push({\n id: `panel-toggle-${panel.id}`,\n label: panel.tooltip ?? panel.title,\n source: 'panel-toggle',\n panelId: panel.id,\n });\n }\n\n return result;\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 buttons\n for (const cleanup of state.toolbarButtonCleanups.values()) {\n cleanup();\n }\n state.toolbarButtonCleanups.clear();\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.toolbarButtons.clear();\n state.lightDomHeaderContent = [];\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 shadow root for DOM queries */\n getShadow: () => ShadowRoot;\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 toolbar buttons info */\n getToolbarButtons(): ToolbarButtonInfo[];\n /** Register a toolbar button */\n registerToolbarButton(button: ToolbarButtonConfig): void;\n /** Unregister a toolbar button */\n unregisterToolbarButton(buttonId: string): void;\n /** Enable/disable a toolbar button */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): 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 getToolbarButtons() {\n return getToolbarButtonsInfo(callbacks.getShellConfig(), state);\n },\n\n registerToolbarButton(button: ToolbarButtonConfig) {\n if (state.toolbarButtons.has(button.id)) {\n console.warn(`[tbw-grid] Toolbar button \"${button.id}\" already registered`);\n return;\n }\n state.toolbarButtons.set(button.id, button);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolbarButton(buttonId: string) {\n // Clean up\n const cleanup = state.toolbarButtonCleanups.get(buttonId);\n if (cleanup) {\n cleanup();\n state.toolbarButtonCleanups.delete(buttonId);\n }\n\n state.toolbarButtons.delete(buttonId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n /**\n * Note: Toolbar button disabled state is now managed by the user.\n * This method is kept for backward compatibility but is a no-op.\n * Users should control their button's disabled state directly in their HTML.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n setToolbarButtonDisabled(_buttonId: string, _disabled: boolean) {\n // No-op: Users control their button disabled state directly.\n // The grid no longer creates or manages toolbar buttons.\n },\n };\n\n return controller;\n}\n\n/**\n * Update accordion section visual state.\n */\nfunction updateAccordionSectionState(shadow: ShadowRoot, sectionId: string, expanded: boolean): void {\n const section = shadow.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(shadow: ShadowRoot, state: ShellState, sectionId: string): void {\n const panel = state.toolPanels.get(sectionId);\n if (!panel?.render) return;\n\n const contentEl = shadow.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 shadowRoot - The shadow root 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 buildGridDOMIntoShadow(\n shadowRoot: ShadowRoot,\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 // Clear existing content\n shadowRoot.replaceChildren();\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 buttons now come from shellConfig (merged by ConfigManager)\n const allButtons = shellConfig?.header?.toolbarButtons ?? [];\n const sortedButtons = [...allButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\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 buttons are now in config (no more separate config vs API distinction for rendering)\n configButtons: sortedButtons.map((b) => ({\n id: b.id,\n hasElement: !!b.element,\n hasRender: !!b.render,\n })),\n apiButtons: [], // No longer needed - all buttons 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 shadowRoot.appendChild(fragment);\n } else {\n // No shell - just grid content\n const fragment = buildGridDOM({ hasShell: false });\n shadowRoot.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\nimport type { BaseGridPlugin } from '../plugin';\nimport type { ColumnConfig, GridConfig } from '../types';\n\n/**\n * Plugin-owned column property definition.\n * Each entry maps a property name to the plugin that owns it.\n */\ninterface PluginPropertyDefinition {\n /** The property name on column config */\n property: string;\n /** The plugin name (from plugin.name) that owns this property */\n pluginName: string;\n /** Human-readable description for error messages */\n description: string;\n /** Import path hint for the error message */\n importHint: string;\n /** Custom check for whether property is used (default: truthy check) */\n isUsed?: (value: unknown) => boolean;\n}\n\n/**\n * Plugin-owned grid config property definition.\n * For properties on GridConfig (not column-level).\n */\ninterface PluginConfigPropertyDefinition {\n /** The property name on grid config */\n property: string;\n /** The plugin name (from plugin.name) that owns this property */\n pluginName: string;\n /** Human-readable description for error messages */\n description: string;\n /** Import path hint for the error message */\n importHint: string;\n /** Custom check for whether property is used (default: non-empty array or truthy) */\n isUsed?: (value: unknown) => boolean;\n}\n\n/**\n * Registry of plugin-owned column properties.\n * This is the single source of truth for which properties require which plugins.\n */\nconst PLUGIN_OWNED_COLUMN_PROPERTIES: PluginPropertyDefinition[] = [\n // EditingPlugin\n {\n property: 'editable',\n pluginName: 'editing',\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 description: 'the \"editor\" column property',\n importHint: \"import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\",\n },\n // GroupingColumnsPlugin\n {\n property: 'group',\n pluginName: 'groupingColumns',\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 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 * Registry of plugin-owned grid config properties.\n */\nconst PLUGIN_OWNED_CONFIG_PROPERTIES: PluginConfigPropertyDefinition[] = [\n // GroupingColumnsPlugin\n {\n property: 'columnGroups',\n pluginName: 'groupingColumns',\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 * 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 * 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 // 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 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 PLUGIN_OWNED_CONFIG_PROPERTIES) {\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, 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 PLUGIN_OWNED_COLUMN_PROPERTIES) {\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, 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 * 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 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 * Notifies other plugins of the new attachment via onPluginAttached hook.\n */\n attach(plugin: BaseGridPlugin): void {\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.\n */\n getAllStyles(): string {\n return this.plugins\n .filter((p) => p.styles)\n .map((p) => p.styles)\n .join('\\n');\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 * 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 } from './internal/event-delegation';\nimport { renderHeader } from './internal/header';\nimport { cancelIdle, scheduleIdle } from './internal/idle-scheduler';\nimport { handleGridKeyDown } from './internal/keyboard';\nimport { RenderPhase, RenderScheduler } from './internal/render-scheduler';\nimport { createResizeController } from './internal/resize';\nimport { invalidateCellCache, renderVisibleRows } from './internal/rows';\nimport {\n buildGridDOMIntoShadow,\n cleanupShellState,\n createShellController,\n createShellState,\n parseLightDomShell,\n parseLightDomToolButtons,\n parseLightDomToolPanels,\n renderCustomToolbarButtons,\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 { 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 ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n FitMode,\n FrameworkAdapter,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n InternalGrid,\n ResizeController,\n ToolbarButtonConfig,\n ToolbarButtonInfo,\n ToolPanelDefinition,\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 * ## 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. `editOn` property → merged into `effectiveConfig.editOn`\n * 6. 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 * @fires cell-commit - Fired when a cell value is committed\n * @fires row-commit - Fired when a bulk row edit session commits\n * @fires changed-rows-reset - Fired after resetChangedRows() unless silent\n * @fires mount-external-view - Fired to request mounting of an external view renderer\n * @fires mount-external-editor - Fired to request mounting of an external editor renderer\n * @fires sort-change - Fired when sort state changes for a column\n * @fires column-resize - Fired after a column resize drag completes\n * @fires activate-cell - Fired when a cell activation intent occurs\n * @fires group-toggle - Fired when a group row is toggled\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 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 */\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 */\n static getAdapters(): readonly FrameworkAdapter[] {\n return this.adapters;\n }\n\n /**\n * Clear all registered adapters (primarily for testing).\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', 'edit-on'];\n }\n\n readonly #shadow: ShadowRoot;\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, editOn)\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 editMode: 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 #isDragging = false;\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 // #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\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 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 rows.\n * Use this when you need access to all source data regardless of active filters.\n */\n get sourceRows(): T[] {\n return this.#rows;\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 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 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 get editOn(): string | boolean | undefined {\n return this.#effectiveConfig.editOn;\n }\n set editOn(value: string | boolean | undefined) {\n const oldValue = this.#configManager?.getEditOn();\n this.#configManager?.setEditOn(value);\n if (oldValue !== value) {\n this.#queueUpdate('editMode');\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 * @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 * @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 constructor() {\n super();\n this.#shadow = this.attachShadow({ mode: 'open' });\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 // 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),\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 },\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.#shadow,\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 getShellToolbarButtons: () => this.#shellState.toolbarButtons,\n getShellLightDomHeaderContent: () => this.#shellState.lightDomHeaderContent,\n getShellHasToolButtonsContainer: () => this.#shellState.hasToolButtonsContainer,\n });\n }\n\n async #injectStyles(): Promise<void> {\n const sheet = new CSSStyleSheet();\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 sheet.replaceSync(styles);\n this.#shadow.adoptedStyleSheets = [sheet];\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 // The grid.css uses :host selectors which appear as-is in cssText\n if (cssText.includes('.tbw-grid-root') && cssText.includes(':host')) {\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 sheet.replaceSync(gridCssText);\n this.#shadow.adoptedStyleSheets = [sheet];\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 * @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 * @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 * @internal Plugin API\n */\n requestRender(): void {\n this.#scheduler.requestPhase(RenderPhase.ROWS, 'plugin:requestRender');\n }\n\n /**\n * Update the grid's column template CSS.\n * Called by resize controller during column resize operations.\n * @internal\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 * @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 shadow DOM.\n * Must be called after #render() since innerHTML wipes existing content.\n */\n #injectAllPluginStyles(): void {\n const allStyles = this.#pluginManager?.getAllStyles() ?? '';\n if (allStyles) {\n const styleEl = document.createElement('style');\n styleEl.setAttribute('data-plugin', 'all');\n styleEl.textContent = allStyles;\n this.#shadow.appendChild(styleEl);\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 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 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 */\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 } else if (name === 'edit-on') {\n this.editOn = newValue;\n }\n }\n\n #afterConnect(): void {\n // Shell changes the DOM structure - need to find elements appropriately\n const gridContent = this.#shadow.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#shadow.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.#shadow, this.#shellState);\n // Render custom toolbar buttons (element/render modes)\n renderCustomToolbarButtons(this.#shadow, 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 // Element-level keydown handler (uses signal for automatic cleanup)\n this.addEventListener('keydown', (e) => handleGridKeyDown(this as unknown as InternalGrid<T>, e), { signal });\n\n // Central mouse event handling for plugins (uses signal for automatic cleanup)\n this.#shadow.addEventListener('mousedown', (e) => this.#handleMouseDown(e as MouseEvent), { signal });\n\n // Track global mousemove/mouseup for drag operations (uses signal for automatic cleanup)\n document.addEventListener('mousemove', (e: MouseEvent) => this.#handleMouseMove(e), { signal });\n document.addEventListener('mouseup', (e: MouseEvent) => this.#handleMouseUp(e), { signal });\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 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 if (measuredHeight > 0 && measuredHeight !== this._virtualization.rowHeight) {\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.#shadow.querySelector('.tbw-grid-content') as HTMLElement;\n const scrollArea = this.#shadow.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 this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'resize-observer');\n });\n this.#resizeObserver.observe(this._virtualization.viewportEl);\n }\n\n if (this._virtualization.enabled) {\n // Schedule initial virtualization through scheduler\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'init-virtualization');\n // Set up row height observer after first render\n queueMicrotask(() => this.#setupRowHeightObserver());\n }\n }\n\n /**\n * Set up ResizeObserver on first row's cells to detect height changes.\n * Called after rows are rendered to observe the actual content cells.\n * Handles dynamic CSS loading, lazy images, font loading, 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');\n if (!firstRow) return;\n\n this.#rowHeightObserverSetup = true;\n this.#rowHeightObserver?.disconnect();\n\n const cells = firstRow.querySelectorAll('.cell');\n if (cells.length > 0) {\n this.#rowHeightObserver = new ResizeObserver(() => {\n this.#measureRowHeight();\n });\n // Observe all cells - any one might have a custom renderer that changes size\n cells.forEach((cell) => this.#rowHeightObserver!.observe(cell));\n }\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' | 'editMode'): 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 editMode: false,\n };\n\n // If gridConfig changed, it supersedes columns/rows/fit/edit changes\n // because gridConfig includes all of those\n if (flags.gridConfig) {\n this.#applyGridConfigUpdate();\n return; // gridConfig handles everything\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 if (flags.editMode) {\n this.#applyEditModeUpdate();\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 // 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 #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 #applyEditModeUpdate(): void {\n this.#configManager.merge();\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n // Request render through scheduler to batch with other pending work\n this.#scheduler.requestPhase(RenderPhase.VIRTUALIZATION, 'applyEditModeUpdate');\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.#shadow.querySelector('.has-shell');\n const hadToolPanel = !!this.#shadow.querySelector('.tbw-tool-panel');\n\n // Count accordion sections before update (to detect new panels added)\n const accordionSectionsBefore = this.#shadow.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 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 // 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.#shadow.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 a map of processed columns by field for quick lookup\n const processedMap = new Map(processedColumns.map((c: any, i: number) => [c.field, { col: c, order: i }]));\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) => processedMap.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 returned original fields (possibly modified) - merge back\n // Use source columns as base, not current _columns\n const updatedColumns = sourceColumns.map((c) => {\n if (c.hidden) return c; // Keep hidden columns unchanged\n const processed = processedMap.get(c.field);\n return processed ? processed.col : c;\n });\n\n this._columns = updatedColumns 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 * @internal Plugin API\n */\n findHeaderRow(): HTMLElement {\n return this.#shadow.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 * @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.\n * Returns true if any plugin handled the event.\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 cellClickEvent: CellClickEvent = {\n row,\n rowIndex,\n colIndex,\n field: col.field,\n value: (row as Record<string, unknown>)[col.field],\n cellEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onCellClick(cellClickEvent) ?? false;\n }\n\n /**\n * Dispatch a row click event to the plugin system.\n * Returns true if any plugin handled the event.\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 return this.#pluginManager?.onRowClick(rowClickEvent) ?? false;\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 * @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 * Build a CellMouseEvent from a native MouseEvent.\n * Extracts cell/row information from the event target.\n */\n #buildCellMouseEvent(e: MouseEvent, type: 'mousedown' | 'mousemove' | 'mouseup'): 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 still not inside our shadow root (e.g., for document-level events),\n // use elementFromPoint to find the actual element under the mouse\n if (target && !this.#shadow.contains(target)) {\n const elAtPoint = this.#shadow.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: T | undefined;\n let field: string | undefined;\n let value: unknown;\n let column: any;\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 = this._rows[rowIndex];\n column = this._columns[colIndex];\n field = column?.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,\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 */\n #handleMouseDown(e: MouseEvent): void {\n const event = this.#buildCellMouseEvent(e, 'mousedown');\n const handled = this.#pluginManager?.onCellMouseDown(event) ?? false;\n\n // If any plugin handled mousedown, start tracking for drag\n if (handled) {\n this.#isDragging = true;\n }\n }\n\n /**\n * Handle mousemove events (only when dragging).\n */\n #handleMouseMove(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mousemove');\n this.#pluginManager?.onCellMouseMove(event);\n }\n\n /**\n * Handle mouseup events.\n */\n #handleMouseUp(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mouseup');\n this.#pluginManager?.onCellMouseUp(event);\n this.#isDragging = false;\n }\n\n async ready(): Promise<void> {\n return this.#readyPromise;\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 * Trim the internal row pool to match the current visible window size.\n *\n * The grid maintains a pool of reusable row DOM elements for virtualization.\n * When the dataset shrinks significantly (e.g., after filtering or deleting rows),\n * the pool may have excess elements that consume memory unnecessarily.\n *\n /** Public method: returns a frozen snapshot of the merged effective configuration */\n async getConfig(): Promise<Readonly<GridConfig<T>>> {\n return Object.freeze({ ...(this.#effectiveConfig || {}) });\n }\n\n // ---------------- Column Visibility API ----------------\n // Delegates to ConfigManager\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 toggleColumnVisibility(field: string): boolean {\n const result = this.#configManager.toggleColumnVisibility(field);\n if (result) {\n this.requestStateChange();\n }\n return result;\n }\n\n isColumnVisible(field: string): boolean {\n return this.#configManager.isColumnVisible(field);\n }\n\n showAllColumns(): void {\n this.#configManager.showAllColumns();\n this.requestStateChange();\n }\n\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n return this.#configManager.getAllColumns();\n }\n\n setColumnOrder(order: string[]): void {\n this.#configManager.setColumnOrder(order);\n this.requestStateChange();\n }\n\n getColumnOrder(): string[] {\n return this.#configManager.getColumnOrder();\n }\n\n // ---------------- Column State API ----------------\n\n /**\n * Get the current column state, including order, width, visibility, sort, and plugin state.\n * Returns a serializable object suitable for localStorage or database storage.\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 order, width, visibility, sort, and plugin state.\n * Use this to restore previously saved column state.\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 */\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 * @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 (order, width, visibility, sort).\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 // exposes the public API surface.\n\n /** Check if the tool panel is currently open. */\n get isToolPanelOpen(): boolean {\n return this.#shellController.isPanelOpen;\n }\n\n /**\n * Get the currently active tool panel ID, or null if none is open.\n * @deprecated Use isToolPanelOpen and expandedToolPanelSections instead.\n */\n get activeToolPanel(): string | null {\n return this.#shellController.activePanel;\n }\n\n /** Get the IDs of expanded accordion sections. */\n get expandedToolPanelSections(): string[] {\n return this.#shellController.expandedSections;\n }\n\n /** Open the tool panel (accordion view with all registered panels). */\n openToolPanel(): void {\n this.#shellController.openToolPanel();\n }\n\n /** Close the tool panel. */\n closeToolPanel(): void {\n this.#shellController.closeToolPanel();\n }\n\n /** Toggle the tool panel open/closed. */\n toggleToolPanel(): void {\n this.#shellController.toggleToolPanel();\n }\n\n /** Toggle an accordion section expanded/collapsed. */\n toggleToolPanelSection(sectionId: string): void {\n this.#shellController.toggleToolPanelSection(sectionId);\n }\n\n /** Get registered tool panel definitions. */\n getToolPanels(): ToolPanelDefinition[] {\n return this.#shellController.getToolPanels();\n }\n\n /** Register a custom tool panel (without creating a plugin). */\n registerToolPanel(panel: ToolPanelDefinition): void {\n this.#shellState.apiToolPanelIds.add(panel.id);\n this.#shellController.registerToolPanel(panel);\n }\n\n /** Unregister a custom tool panel. */\n unregisterToolPanel(panelId: string): void {\n this.#shellState.apiToolPanelIds.delete(panelId);\n this.#shellController.unregisterToolPanel(panelId);\n }\n\n /** Get registered header content definitions. */\n getHeaderContents(): HeaderContentDefinition[] {\n return this.#shellController.getHeaderContents();\n }\n\n /** Register custom header content (without creating a plugin). */\n registerHeaderContent(content: HeaderContentDefinition): void {\n this.#shellController.registerHeaderContent(content);\n }\n\n /** Unregister custom header content. */\n unregisterHeaderContent(contentId: string): void {\n this.#shellController.unregisterHeaderContent(contentId);\n }\n\n /** Get all registered toolbar buttons. */\n getToolbarButtons(): ToolbarButtonInfo[] {\n return this.#shellController.getToolbarButtons();\n }\n\n /** Register a custom toolbar button programmatically. */\n registerToolbarButton(button: ToolbarButtonConfig): void {\n this.#shellController.registerToolbarButton(button);\n }\n\n /** Unregister a custom toolbar button. */\n unregisterToolbarButton(buttonId: string): void {\n this.#shellController.unregisterToolbarButton(buttonId);\n }\n\n /** Enable/disable a toolbar button by ID. */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): void {\n this.#shellController.setToolbarButtonDisabled(buttonId, disabled);\n }\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 refreshShellHeader(): void {\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 this.#afterConnect();\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 * @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 * @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 getRegisteredStyles(): string[] {\n return Array.from(this.#customStyleSheets.keys());\n }\n\n /**\n * Update the shadow root's adoptedStyleSheets to include base + custom sheets.\n */\n #updateAdoptedStyleSheets(): void {\n // Keep the first stylesheet (base grid styles) and append custom ones\n const baseSheet = this.#shadow.adoptedStyleSheets[0];\n const customSheets = Array.from(this.#customStyleSheets.values());\n this.#shadow.adoptedStyleSheets = baseSheet ? [baseSheet, ...customSheets] : 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.#shadow.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 * @internal Used by framework integration libraries (Angular, React, Vue)\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.#shadow.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 * @internal Plugin API\n */\n refreshVirtualWindow(force = 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 this.#pluginManager?.afterRender();\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 this.#pluginManager?.afterRender();\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) {\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 = buildGridDOMIntoShadow(\n this.#shadow,\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.#shadow, this.#effectiveConfig?.shell, this.#shellState, {\n onPanelToggle: () => this.toggleToolPanel(),\n onSectionToggle: (sectionId: string) => this.toggleToolPanelSection(sectionId),\n onToolbarButtonClick: (buttonId) => this.#handleToolbarButtonClick(buttonId),\n });\n\n // Set up tool panel resize\n this.#resizeCleanup?.();\n this.#resizeCleanup = setupToolPanelResize(this.#shadow, 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 * Handle toolbar button click.\n * Note: Config/API buttons use element or render, so they handle their own clicks.\n * This method is kept for backwards compatibility but may emit an event in the future.\n */\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\n #handleToolbarButtonClick(_buttonId: string): void {\n // No-op: Config and API buttons now use element/render and handle their own events.\n // Light DOM buttons use slot and handle their own events.\n // This callback may be used for future extensibility (e.g., emitting an event).\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 } 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 shadowRoot: ShadowRoot | null;\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 /** 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 /** Request a full re-render of the grid. */\n requestRender(): 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 * 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\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 */\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 * Abstract base class for all grid plugins.\n *\n * @template TConfig - Configuration type for the plugin\n */\nexport abstract class BaseGridPlugin<TConfig = unknown> implements GridPlugin {\n /** Unique plugin identifier (derived from class name by default) */\n abstract readonly name: string;\n\n /** Plugin version - override in subclass if needed */\n readonly version: string = '1.0.0';\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 * Request a re-render of the grid.\n */\n protected requestRender(): void {\n this.grid?.requestRender?.();\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 shadow root of the grid.\n */\n protected get shadowRoot(): ShadowRoot | null {\n return this.grid?.shadowRoot ?? null;\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 /**\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 */\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 */\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 */\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 */\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 * @packageDocumentation\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\n// #region Public API surface - only export what consumers need\nexport { DataGridElement, DataGridElement as GridElement } from './lib/core/grid';\n\n// Event name constants for DataGrid (public API)\nexport const DGEvents = {\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 GROUP_TOGGLE: 'group-toggle',\n COLUMN_STATE_CHANGE: 'column-state-change',\n} as const;\n\nexport type DGEventName = (typeof DGEvents)[keyof typeof DGEvents];\n\n// Plugin event constants (mirrors DGEvents pattern)\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\nexport type PluginEventName = (typeof PluginEvents)[keyof typeof PluginEvents];\n\n// Public type exports\nexport type {\n ActivateCellDetail,\n AggregatorRef,\n // Animation types\n AnimationConfig,\n AnimationMode,\n AnimationStyle,\n BaseColumnConfig,\n // Event detail types\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 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 RowCommitDetail,\n // Grouping & Footer types\n RowGroupRenderConfig,\n ShellConfig,\n ShellHeaderConfig,\n SortChangeDetail,\n // Sorting types\n SortHandler,\n SortState,\n ToolbarButtonConfig,\n ToolPanelConfig,\n ToolPanelDefinition,\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","#editOn","#lightDomColumnsCache","#originalColumnNodes","#originalConfig","#effectiveConfig","#sourcesChanged","#changeListeners","#lightDomObserver","#stateChangeTimeoutId","#initialColumnState","#callbacks","#lightDomTitle","callbacks","editOn","hasColumns","base","#collectAllSources","#cloneConfig","#applyPostMergeOperations","clone","h","configColumns","domCols","#mergeShellConfig","shellLightDomTitle","lightDomHeaderContent","toolPanelsMap","panels","b","headerContentsMap","contents","toolbarButtonsMap","apiButtons","originalConfigButtons","configIds","mergedButtons","btn","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","getColIndexFromCell","clearCellFocus","handleCellMousedown","rowIndex","colIndex","setupCellEventDelegation","bodyEl","signal","e","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","options","start","cancelIdle","FOCUSABLE_EDITOR_SELECTOR","hasEditingCells","clearEditingState","cellTemplate","rowTemplate","createCellFromTemplate","createRowFromTemplate","invalidateCellCache","renderVisibleRows","end","epoch","renderRowHook","needed","colLen","headerRowCount","handleRowClick","hasRenderRowPlugins","rowData","rowEpoch","prevRef","cellCount","structureValid","dataRefChanged","needsExternalRebuild","hasEditing","isActivelyEditedRow","renderInlineRow","fastPatchRow","isChanged","hasChangedClass","children","colsLen","childLen","minLen","focusRow","focusCol","hasSpecialCols","rowIndexStr","shouldHaveFocus","hasFocus","cellRenderer","produced","displayStr","formatted","gridEl","fragment","compiled","tplHolder","viewRenderer","externalView","needsSanitization","spec","placeholder","context","output","blocked","rawTpl","textContent","_isDbl","firstCell","cellEl","focusChanged","ensureCellVisible","handleGridKeyDown","maxRow","maxCol","editing","colType","path","target","isFormField","tag","rowHeight","container","viewportEl","scrollEl","visibleHeight","y","isEditing","vStart","vEnd","scrollArea","offsets","cellRect","scrollAreaRect","cellLeft","cellRight","visibleLeft","visibleRight","focusTarget","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","rect","createElement","attrs","div","className","button","content","slot","gridContentTemplate","cloneGridContent","buildGridDOM","contentWrapper","buildShellHeader","titleEl","toolbar","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","configButtons","hasConfigButtons","hasApiButtons","hasPanels","showSeparator","sortedConfigButtons","sortedApiButtons","toolbarHtml","isOpen","parseLightDomShell","headerEl","headerContents","parseLightDomToolButtons","toolButtonsContainer","parseLightDomToolPanels","rendererFactory","id","tooltip","render","adapterRenderer","wrapper","existingPanel","cleanup","setupShellEventListeners","shadowRoot","customBtn","btnId","sectionId","setupToolPanelResize","onResize","shellBody","position","minWidth","startX","startWidth","maxWidth","isResizing","onMouseMove","newWidth","onMouseUp","finalWidth","onMouseDown","renderCustomToolbarButtons","allButtons","existingCleanup","renderHeaderContent","contentArea","sortedContents","renderPanelContent","expandIcon","collapseIcon","panelId","isExpanded","chevron","updateToolbarActiveStates","panelToggle","updatePanelState","getToolbarButtonsInfo","cleanupShellState","createShellController","initialized","controller","firstPanel","shadow","updateAccordionSectionState","otherId","otherPanel","contentEl","renderAccordionSectionContent","contentId","buttonId","_buttonId","_disabled","expanded","buildGridDOMIntoShadow","shellConfig","runtimeState","hasShell","sortedButtons","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","PLUGIN_OWNED_COLUMN_PROPERTIES","PLUGIN_OWNED_CONFIG_PROPERTIES","hasPlugin","pluginName","validatePluginProperties","missingPlugins","addError","description","importHint","isConfigProperty","entry","def","column","errors","fields","fieldList","PluginManager","existingPlugin","otherPlugin","PluginClass","total","beforeRowIndex","adjustedStart","pluginStart","row","query","responses","response","event","focusedCell","left","right","skipScroll","DataGridElement","adapter","#shadow","#initialized","#rows","#configManager","#connected","#pendingUpdate","#pendingUpdateFlags","#scheduler","#scrollRaf","#pendingScrollTop","#hasScrollPlugins","#renderRowHook","#isDragging","#touchState","#eventAbortController","#resizeObserver","#rowHeightObserver","#idleCallbackHandle","#pooledScrollEvent","#pluginManager","#lastPluginsArray","#eventListenersAdded","#scrollAbortController","#shellState","#shellController","#resizeCleanup","#baseColumns","oldValue","#queueUpdate","#injectStyles","#updatePluginConfigs","#processColumns","#rebuildRowModel","eventName","detail","#emit","#setup","height","#applyAnimationConfig","sheet","styles","gridCssText","stylesheet","cssText","rule","err","#initializePlugins","pluginsConfig","#injectAllPluginStyles","allStyles","styleEl","newPlugins","isLightDom","isApiRegistered","#collectPluginShellContributions","#destroyPlugins","pluginPanels","pluginContents","#getToolPanelRendererFactory","instanceAdapter","#render","#afterConnect","#setupLightDomHandlers","#rowHeightObserverSetup","#customStyleSheets","newValue","parsed","gridRoot","defaultOpen","#setupScrollListeners","#handleMouseDown","#handleMouseMove","#handleMouseUp","userRowHeight","#measureRowHeight","#updateAriaSelection","firstRow","cells","maxCellHeight","rowRect","measuredHeight","scrollSignal","fauxScrollbar","rowsEl","currentScrollTop","rawStart","evenAlignedStart","subPixelOffset","#onScrollBatched","isHorizontal","#setupRowHeightObserver","rowIdx","isActiveRow","colIdx","#flushPendingUpdates","flags","#applyGridConfigUpdate","#applyColumnsUpdate","#applyRowsUpdate","#applyFitModeUpdate","#applyEditModeUpdate","hadShell","hadToolPanel","accordionSectionsBefore","nowNeedsShell","nowHasToolPanels","toolPanelCountChanged","#updateShellHeaderInPlace","sourceColumns","visibleCols","hiddenCols","processedColumns","processedMap","processed","originalRows","processedRows","gridConfig","enabled","#renderVisibleRows","#lastAriaRowCount","#lastAriaColCount","#updateAriaCounts","rowCount","colCount","prevRowCount","scrollEvent","cellClickEvent","rowClickEvent","headerClickEvent","#buildCellMouseEvent","elAtPoint","#applyColumnState","disabled","css","#updateAdoptedStyleSheets","baseSheet","customSheets","handleShellChange","hadTitle","hadToolButtons","hasToolButtons","newHeaderHtml","temp","newHeader","#setupShellListeners","handleColumnChange","#calculateTotalSpacerHeight","totalRows","fauxScrollHeight","viewportHeight","scrollAreaEl","scrollAreaHeight","viewportHeightDiff","pluginExtraHeight","hScrollbarPadding","force","iterations","maxIterations","extraHeightBefore","pluginAdjustedStart","visibleCount","totalHeight","extraHeightBeforeStart","newFauxHeight","newViewportHeight","newTotalHeight","#handleToolbarButtonClick","PLUGIN_QUERIES","BaseGridPlugin","#abortController","userIcons","iconKey","pluginOverride","message","GridClasses","GridDataAttrs","GridSelectors","GridCSSVars","DGEvents","PluginEvents","builtInAggregators","acc","sum","customAggregators","aggregatorRegistry","ref","builtInValueAggregators","vals","getValueAggregator","aggFunc","runValueAggregator","values","registerAggregator","unregisterAggregator","getAggregator","runAggregator","listAggregators"],"mappings":"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"}