@toolbox-web/grid 0.0.1

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 (87) hide show
  1. package/all.d.ts +3518 -0
  2. package/all.js +3762 -0
  3. package/all.js.map +1 -0
  4. package/index.d.ts +2367 -0
  5. package/index.js +3105 -0
  6. package/index.js.map +1 -0
  7. package/lib/plugins/clipboard/index.js +365 -0
  8. package/lib/plugins/clipboard/index.js.map +1 -0
  9. package/lib/plugins/column-virtualization/index.js +255 -0
  10. package/lib/plugins/column-virtualization/index.js.map +1 -0
  11. package/lib/plugins/context-menu/index.js +341 -0
  12. package/lib/plugins/context-menu/index.js.map +1 -0
  13. package/lib/plugins/export/index.js +305 -0
  14. package/lib/plugins/export/index.js.map +1 -0
  15. package/lib/plugins/filtering/index.js +759 -0
  16. package/lib/plugins/filtering/index.js.map +1 -0
  17. package/lib/plugins/grouping-columns/index.js +283 -0
  18. package/lib/plugins/grouping-columns/index.js.map +1 -0
  19. package/lib/plugins/grouping-rows/index.js +494 -0
  20. package/lib/plugins/grouping-rows/index.js.map +1 -0
  21. package/lib/plugins/master-detail/index.js +303 -0
  22. package/lib/plugins/master-detail/index.js.map +1 -0
  23. package/lib/plugins/multi-sort/index.js +270 -0
  24. package/lib/plugins/multi-sort/index.js.map +1 -0
  25. package/lib/plugins/pinned-columns/index.js +221 -0
  26. package/lib/plugins/pinned-columns/index.js.map +1 -0
  27. package/lib/plugins/pinned-rows/index.js +459 -0
  28. package/lib/plugins/pinned-rows/index.js.map +1 -0
  29. package/lib/plugins/pivot/index.js +326 -0
  30. package/lib/plugins/pivot/index.js.map +1 -0
  31. package/lib/plugins/reorder/index.js +260 -0
  32. package/lib/plugins/reorder/index.js.map +1 -0
  33. package/lib/plugins/selection/index.js +426 -0
  34. package/lib/plugins/selection/index.js.map +1 -0
  35. package/lib/plugins/server-side/index.js +241 -0
  36. package/lib/plugins/server-side/index.js.map +1 -0
  37. package/lib/plugins/tree/index.js +383 -0
  38. package/lib/plugins/tree/index.js.map +1 -0
  39. package/lib/plugins/undo-redo/index.js +289 -0
  40. package/lib/plugins/undo-redo/index.js.map +1 -0
  41. package/lib/plugins/visibility/index.js +430 -0
  42. package/lib/plugins/visibility/index.js.map +1 -0
  43. package/package.json +53 -0
  44. package/themes/dg-theme-contrast.css +43 -0
  45. package/themes/dg-theme-large.css +54 -0
  46. package/themes/dg-theme-standard.css +19 -0
  47. package/themes/dg-theme-vibrant.css +16 -0
  48. package/umd/grid.all.umd.js +660 -0
  49. package/umd/grid.all.umd.js.map +1 -0
  50. package/umd/grid.umd.js +105 -0
  51. package/umd/grid.umd.js.map +1 -0
  52. package/umd/plugins/clipboard.umd.js +9 -0
  53. package/umd/plugins/clipboard.umd.js.map +1 -0
  54. package/umd/plugins/column-virtualization.umd.js +2 -0
  55. package/umd/plugins/column-virtualization.umd.js.map +1 -0
  56. package/umd/plugins/context-menu.umd.js +53 -0
  57. package/umd/plugins/context-menu.umd.js.map +1 -0
  58. package/umd/plugins/export.umd.js +14 -0
  59. package/umd/plugins/export.umd.js.map +1 -0
  60. package/umd/plugins/filtering.umd.js +175 -0
  61. package/umd/plugins/filtering.umd.js.map +1 -0
  62. package/umd/plugins/grouping-columns.umd.js +29 -0
  63. package/umd/plugins/grouping-columns.umd.js.map +1 -0
  64. package/umd/plugins/grouping-rows.umd.js +40 -0
  65. package/umd/plugins/grouping-rows.umd.js.map +1 -0
  66. package/umd/plugins/master-detail.umd.js +27 -0
  67. package/umd/plugins/master-detail.umd.js.map +1 -0
  68. package/umd/plugins/multi-sort.umd.js +26 -0
  69. package/umd/plugins/multi-sort.umd.js.map +1 -0
  70. package/umd/plugins/pinned-columns.umd.js +2 -0
  71. package/umd/plugins/pinned-columns.umd.js.map +1 -0
  72. package/umd/plugins/pinned-rows.umd.js +73 -0
  73. package/umd/plugins/pinned-rows.umd.js.map +1 -0
  74. package/umd/plugins/pivot.umd.js +8 -0
  75. package/umd/plugins/pivot.umd.js.map +1 -0
  76. package/umd/plugins/reorder.umd.js +31 -0
  77. package/umd/plugins/reorder.umd.js.map +1 -0
  78. package/umd/plugins/selection.umd.js +34 -0
  79. package/umd/plugins/selection.umd.js.map +1 -0
  80. package/umd/plugins/server-side.umd.js +2 -0
  81. package/umd/plugins/server-side.umd.js.map +1 -0
  82. package/umd/plugins/tree.umd.js +11 -0
  83. package/umd/plugins/tree.umd.js.map +1 -0
  84. package/umd/plugins/undo-redo.umd.js +2 -0
  85. package/umd/plugins/undo-redo.umd.js.map +1 -0
  86. package/umd/plugins/visibility.umd.js +94 -0
  87. package/umd/plugins/visibility.umd.js.map +1 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"grid.all.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/keyboard.ts","../../../../libs/grid/src/lib/core/internal/rows.ts","../../../../libs/grid/src/lib/core/internal/editing.ts","../../../../libs/grid/src/lib/core/internal/sorting.ts","../../../../libs/grid/src/lib/core/internal/header.ts","../../../../libs/grid/src/lib/core/internal/resize.ts","../../../../libs/grid/src/lib/core/internal/shell.ts","../../../../libs/grid/src/lib/core/plugin/plugin-manager.ts","../../../../libs/grid/src/lib/core/grid.ts","../../../../libs/grid/src/lib/core/plugin/base-plugin.ts","../../../../libs/grid/src/lib/plugins/selection/range-selection.ts","../../../../libs/grid/src/lib/plugins/selection/SelectionPlugin.ts","../../../../libs/grid/src/lib/plugins/tree/tree-data.ts","../../../../libs/grid/src/lib/plugins/tree/tree-detect.ts","../../../../libs/grid/src/lib/plugins/tree/TreePlugin.ts","../../../../libs/grid/src/lib/core/constants.ts","../../../../libs/grid/src/public.ts","../../../../libs/grid/src/lib/plugins/clipboard/copy.ts","../../../../libs/grid/src/lib/plugins/clipboard/paste.ts","../../../../libs/grid/src/lib/plugins/clipboard/ClipboardPlugin.ts","../../../../libs/grid/src/lib/plugins/column-virtualization/column-virtualization.ts","../../../../libs/grid/src/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.ts","../../../../libs/grid/src/lib/plugins/context-menu/menu.ts","../../../../libs/grid/src/lib/plugins/context-menu/ContextMenuPlugin.ts","../../../../libs/grid/src/lib/plugins/export/csv.ts","../../../../libs/grid/src/lib/plugins/export/excel.ts","../../../../libs/grid/src/lib/plugins/export/ExportPlugin.ts","../../../../libs/grid/src/lib/core/internal/virtualization.ts","../../../../libs/grid/src/lib/plugins/filtering/filter-model.ts","../../../../libs/grid/src/lib/plugins/filtering/FilteringPlugin.ts","../../../../libs/grid/src/lib/plugins/grouping-columns/grouping-columns.ts","../../../../libs/grid/src/lib/plugins/grouping-columns/GroupingColumnsPlugin.ts","../../../../libs/grid/src/lib/core/internal/aggregators.ts","../../../../libs/grid/src/lib/plugins/grouping-rows/grouping-rows.ts","../../../../libs/grid/src/lib/plugins/grouping-rows/GroupingRowsPlugin.ts","../../../../libs/grid/src/lib/plugins/master-detail/master-detail.ts","../../../../libs/grid/src/lib/plugins/master-detail/MasterDetailPlugin.ts","../../../../libs/grid/src/lib/plugins/multi-sort/multi-sort.ts","../../../../libs/grid/src/lib/plugins/multi-sort/MultiSortPlugin.ts","../../../../libs/grid/src/lib/plugins/pinned-columns/pinned-columns.ts","../../../../libs/grid/src/lib/plugins/pinned-columns/PinnedColumnsPlugin.ts","../../../../libs/grid/src/lib/plugins/pinned-rows/pinned-rows.ts","../../../../libs/grid/src/lib/plugins/pinned-rows/PinnedRowsPlugin.ts","../../../../libs/grid/src/lib/plugins/pivot/pivot-model.ts","../../../../libs/grid/src/lib/plugins/pivot/pivot-engine.ts","../../../../libs/grid/src/lib/plugins/pivot/PivotPlugin.ts","../../../../libs/grid/src/lib/plugins/reorder/column-drag.ts","../../../../libs/grid/src/lib/plugins/reorder/ReorderPlugin.ts","../../../../libs/grid/src/lib/plugins/server-side/datasource.ts","../../../../libs/grid/src/lib/plugins/server-side/ServerSidePlugin.ts","../../../../libs/grid/src/lib/plugins/undo-redo/history.ts","../../../../libs/grid/src/lib/plugins/undo-redo/UndoRedoPlugin.ts","../../../../libs/grid/src/lib/plugins/visibility/VisibilityPlugin.ts"],"sourcesContent":["/**\r\n * Column State Module\r\n *\r\n * Handles collection and application of column state for persistence.\r\n * State includes user-driven changes: order, width, visibility, and sort.\r\n * Plugins can contribute additional state via getColumnState/applyColumnState hooks.\r\n */\r\n\r\nimport type { BaseGridPlugin } from '../plugin';\r\nimport type {\r\n ColumnConfig,\r\n ColumnInternal,\r\n ColumnSortState,\r\n ColumnState,\r\n GridColumnState,\r\n InternalGrid,\r\n} from '../types';\r\n\r\n/** Debounce timeout for state change events */\r\nconst STATE_CHANGE_DEBOUNCE_MS = 100;\r\n\r\n/**\r\n * Get sort state for a column from the grid's sortState.\r\n */\r\nfunction getSortState(grid: InternalGrid): Map<string, ColumnSortState> {\r\n const sortMap = new Map<string, ColumnSortState>();\r\n\r\n // Core sort state (single column)\r\n if (grid.sortState) {\r\n sortMap.set(grid.sortState.field, {\r\n direction: grid.sortState.direction === 1 ? 'asc' : 'desc',\r\n priority: 0,\r\n });\r\n }\r\n\r\n return sortMap;\r\n}\r\n\r\n/**\r\n * Collect column state from the grid and all plugins.\r\n * Returns a complete GridColumnState object ready for serialization.\r\n */\r\nexport function collectColumnState<T>(grid: InternalGrid<T>, plugins: BaseGridPlugin[]): GridColumnState {\r\n const columns = grid._columns;\r\n const sortStates = getSortState(grid);\r\n\r\n return {\r\n columns: columns.map((col, index) => {\r\n // 1. Core state\r\n const state: ColumnState = {\r\n field: col.field,\r\n order: index,\r\n visible: true, // If it's in _columns, it's visible (hidden columns are filtered out)\r\n };\r\n\r\n // Include width if set (either from config or resize)\r\n const internalCol = col as ColumnInternal<T>;\r\n if (internalCol.__renderedWidth !== undefined) {\r\n state.width = internalCol.__renderedWidth;\r\n } else if (col.width !== undefined) {\r\n state.width = typeof col.width === 'string' ? parseFloat(col.width) : col.width;\r\n }\r\n\r\n // Include sort state if present\r\n const sortState = sortStates.get(col.field);\r\n if (sortState) {\r\n state.sort = sortState;\r\n }\r\n\r\n // 2. Collect from each plugin\r\n for (const plugin of plugins) {\r\n if (plugin.getColumnState) {\r\n const pluginState = plugin.getColumnState(col.field);\r\n if (pluginState) {\r\n Object.assign(state, pluginState);\r\n }\r\n }\r\n }\r\n\r\n return state;\r\n }),\r\n };\r\n}\r\n\r\n/**\r\n * Apply column state to the grid and all plugins.\r\n * Modifies the grid's internal state and triggers plugin state restoration.\r\n *\r\n * @param grid - The grid instance\r\n * @param state - The state to apply\r\n * @param allColumns - All available columns (including hidden ones)\r\n * @param plugins - Plugins that may have applyColumnState hooks\r\n */\r\nexport function applyColumnState<T>(\r\n grid: InternalGrid<T>,\r\n state: GridColumnState,\r\n allColumns: ColumnConfig<T>[],\r\n plugins: BaseGridPlugin[]\r\n): void {\r\n if (!state.columns || state.columns.length === 0) return;\r\n\r\n const stateMap = new Map(state.columns.map((s) => [s.field, s]));\r\n\r\n // 1. Apply width and visibility to columns\r\n const updatedColumns = allColumns.map((col) => {\r\n const s = stateMap.get(col.field);\r\n if (!s) return col;\r\n\r\n const updated: ColumnInternal<T> = { ...col };\r\n\r\n // Apply width\r\n if (s.width !== undefined) {\r\n updated.width = s.width;\r\n updated.__renderedWidth = s.width;\r\n }\r\n\r\n // Apply visibility (hidden is inverse of visible)\r\n if (s.visible !== undefined) {\r\n updated.hidden = !s.visible;\r\n }\r\n\r\n return updated;\r\n });\r\n\r\n // 2. Reorder columns based on state\r\n updatedColumns.sort((a, b) => {\r\n const orderA = stateMap.get(a.field)?.order ?? Infinity;\r\n const orderB = stateMap.get(b.field)?.order ?? Infinity;\r\n return orderA - orderB;\r\n });\r\n\r\n // 3. Update grid's internal columns\r\n grid._columns = updatedColumns as ColumnInternal<T>[];\r\n\r\n // 4. Apply sort state (core single-column sort)\r\n // Find the column with highest sort priority\r\n const sortedByPriority = state.columns\r\n .filter((s) => s.sort !== undefined)\r\n .sort((a, b) => (a.sort?.priority ?? 0) - (b.sort?.priority ?? 0));\r\n\r\n if (sortedByPriority.length > 0) {\r\n const primarySort = sortedByPriority[0];\r\n if (primarySort.sort) {\r\n grid.sortState = {\r\n field: primarySort.field,\r\n direction: primarySort.sort.direction === 'asc' ? 1 : -1,\r\n };\r\n }\r\n } else {\r\n grid.sortState = null;\r\n }\r\n\r\n // 5. Let each plugin apply its state\r\n for (const plugin of plugins) {\r\n if (plugin.applyColumnState) {\r\n for (const colState of state.columns) {\r\n plugin.applyColumnState(colState.field, colState);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Create a state change handler with debouncing.\r\n * Returns a function that, when called, will eventually emit the state change event.\r\n */\r\nexport function createStateChangeHandler<T>(\r\n grid: InternalGrid<T>,\r\n getPlugins: () => BaseGridPlugin[],\r\n emit: (detail: GridColumnState) => void\r\n): () => void {\r\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\r\n\r\n return () => {\r\n // Clear any pending timeout\r\n if (timeoutId !== null) {\r\n clearTimeout(timeoutId);\r\n }\r\n\r\n // Schedule the emit\r\n timeoutId = setTimeout(() => {\r\n timeoutId = null;\r\n const state = collectColumnState(grid, getPlugins());\r\n emit(state);\r\n }, STATE_CHANGE_DEBOUNCE_MS);\r\n };\r\n}\r\n\r\n/**\r\n * Compare two column states to check if they are equal.\r\n * Useful for preventing duplicate state change events.\r\n */\r\nexport function areColumnStatesEqual(a: GridColumnState, b: GridColumnState): boolean {\r\n if (a.columns.length !== b.columns.length) return false;\r\n\r\n for (let i = 0; i < a.columns.length; i++) {\r\n const colA = a.columns[i];\r\n const colB = b.columns[i];\r\n\r\n if (colA.field !== colB.field) return false;\r\n if (colA.order !== colB.order) return false;\r\n if (colA.visible !== colB.visible) return false;\r\n if (colA.width !== colB.width) return false;\r\n\r\n // Compare sort state\r\n const sortA = colA.sort;\r\n const sortB = colB.sort;\r\n if ((sortA === undefined) !== (sortB === undefined)) return false;\r\n if (sortA && sortB) {\r\n if (sortA.direction !== sortB.direction) return false;\r\n if (sortA.priority !== sortB.priority) return false;\r\n }\r\n }\r\n\r\n return true;\r\n}\r\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\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, any>;\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 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 /** 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: any }> | (() => Array<{ label: string; value: any }>);\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, any>;\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 /** 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: any;\n props?: Record<string, any>;\n mount?: (options: {\n placeholder: HTMLElement;\n context: CellRenderContext<TRow, any>;\n spec: any;\n }) => void | { dispose?: () => void };\n };\n /** Whether the column is initially hidden */\n hidden?: boolean;\n /** Prevent this column from being hidden by the visibility plugin */\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 = any, TValue = any> =\n | string // custom element tag name\n | ((context: ColumnEditorContext<TRow, TValue>) => HTMLElement | string)\n | {\n /** Arbitrary component reference (class, function, token) */\n component: any;\n /** Optional static props passed to mount */\n props?: Record<string, any>;\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: any;\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\nexport type ColumnViewRenderer<TRow = any, TValue = any> = (\n ctx: CellRenderContext<TRow, TValue>\n) => Node | string | void;\n\n// ================= 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 __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: any;\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: any) => 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 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// ================= 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: any, 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: any[], field: string, column?: any) => any);\n\n/** Result of automatic column inference from sample rows. */\nexport interface InferredColumnResult<TRow = any> {\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\n// ================= 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'). Can also be set via `editOn` prop. */\n editOn?: string;\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?: any[];\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// ================= 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 * Supports three modes:\n * - Simple: provide `icon` + `action` for grid to create button\n * - Element: provide `element` for user-created DOM\n * - Render: provide `render` function for complex widgets\n */\nexport interface ToolbarButtonConfig {\n /** Unique button ID */\n id: string;\n /** Tooltip / aria-label (required for accessibility) */\n label: string;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n /** Whether button is disabled (only applies to grid-rendered buttons) */\n disabled?: boolean;\n\n // ===== Option A: Simple - Grid renders the button =====\n /** Button content: SVG string, emoji, or text. Grid creates <button> with this. */\n icon?: string;\n /** Click handler (required when using icon) */\n action?: () => void;\n\n // ===== Option B: Custom DOM - User provides element or render function =====\n /**\n * User-provided element. Grid wraps it but doesn't modify it.\n * User is responsible for event handlers.\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 disabled: boolean;\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 header */\n title: string;\n /** Icon for toolbar button (SVG string or emoji) */\n icon: string;\n /** Toolbar button tooltip */\n tooltip?: string;\n /** Panel content factory - called when panel 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\n// ================= 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\n// ================= Public Event Detail Interfaces =================\nexport interface CellCommitDetail<TRow = any> {\n /** The mutated row after commit. */\n row: TRow;\n /** Field name whose value changed. */\n field: string;\n /** New value stored. */\n value: any;\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 = any> {\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 = any> {\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 = any> {\n placeholder: HTMLElement;\n spec: any;\n context: { row: TRow; value: any; field: string; column: any };\n}\n\nexport interface ExternalMountEditorDetail<TRow = any> {\n placeholder: HTMLElement;\n spec: any;\n context: { row: TRow; value: any; field: string; column: any; commit: (v: any) => void; cancel: () => void };\n}\n\nexport interface DataGridEventMap<TRow = any> {\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<any>, TRow = any> = DataGridEventMap<TRow>[K];\nexport type DataGridCustomEvent<K extends keyof DataGridEventMap<any>, TRow = any> = CustomEvent<\n DataGridEventMap<TRow>[K]\n>;\n\n// Internal code now reuses the public ColumnEditorContext; provide alias for backward compatibility\nexport type EditorContext<T = any> = ColumnEditorContext<T, any>;\n\nexport interface EvalContext {\n value: any;\n row: any;\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 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__';\n// Distinct sentinel for a template that was forcefully blocked at compile time OR\n// produced only blocked content. This lets downstream rendering logic suppress any\n// fallback value population that might otherwise leak (e.g. the raw row value or a\n// function source string) when a dangerous token is detected.\nexport const BLOCKED_SENTINEL = '__DG_BLOCKED__';\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// ============================================================================\n// HTML SANITIZATION\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\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 // eslint-disable-next-line no-new-func\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 if (el.hasAttribute('resizable')) (config as any).resizable = true;\n if (el.hasAttribute('sizable')) (config as any).resizable = true; // legacy attribute support\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 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 */\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 const domMap: Record<string, ColumnInternal> = {};\n (dom as ColumnInternal[]).forEach((c) => (domMap[c.field] = c));\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 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 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\nimport type { ColumnConfig, EditorContext } from '../types';\n\n/**\n * Returns a default editor factory function for the given column type.\n * Each editor handles focus, commit on blur/Enter, and cancel on Escape.\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 ?? '';\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 input.focus();\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 input.focus();\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 input.focus();\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 select.focus();\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 ?? '';\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 input.focus();\n return input;\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';\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 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 grid.focusCol = 0;\n e.preventDefault();\n break;\n case 'End':\n grid.focusCol = maxCol;\n e.preventDefault();\n break;\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') grid.beginBulkEdit(grid.focusRow);\n else\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('activate-cell', { detail: { row: grid.focusRow, col: grid.focusCol } })\n );\n return ensureCellVisible(grid);\n default:\n return;\n }\n ensureCellVisible(grid);\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): void {\n if (grid.virtualization?.enabled) {\n const { rowHeight } = grid.virtualization;\n const viewport = grid as unknown as HTMLElement; // grid element is the scroll container post-refactor\n const y = grid.focusRow * rowHeight;\n if (y < viewport.scrollTop) viewport.scrollTop = y;\n else if (y + rowHeight > viewport.scrollTop + viewport.clientHeight)\n viewport.scrollTop = y - viewport.clientHeight + rowHeight;\n }\n grid.refreshVirtualWindow(false);\n Array.from(grid.bodyEl.querySelectorAll('.cell-focus')).forEach((el: any) => el.classList.remove('cell-focus'));\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 if (grid.activeEditRows !== undefined && grid.activeEditRows !== -1 && cell.classList.contains('editing')) {\n const focusTarget = cell.querySelector(\n 'input,select,textarea,[contenteditable=\"true\"],[contenteditable=\"\"],[tabindex]:not([tabindex=\"-1\"])'\n ) as HTMLElement | null;\n if (focusTarget && document.activeElement !== focusTarget) {\n try {\n focusTarget.focus();\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 { addPart } from './columns';\nimport { commitCellValue, inlineEnterEdit, startRowEdit } from './editing';\nimport { ensureCellVisible } from './keyboard';\nimport { evalTemplateString, finalCellScrub, sanitizeHTML } from './sanitize';\n\n/** Callback type for plugin row rendering hook */\nexport type RenderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number) => boolean;\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 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 if (col.type === 'boolean') {\n return value ? '\\u{1F5F9}' : '\\u2610';\n }\n\n return value == null ? '' : String(value);\n}\n\n/**\n * Pre-compute display values for a range of rows.\n * Call this after rows change to warm the cache for visible + overscan rows.\n */\nexport function precomputeCellCache(\n grid: InternalGrid,\n startRow: number,\n endRow: number,\n epoch: number | undefined\n): void {\n const columns = grid.visibleColumns;\n const rows = grid._rows;\n\n for (let r = startRow; r < endRow && r < rows.length; r++) {\n const rowData = rows[r];\n if (!rowData) continue;\n for (let c = 0; c < columns.length; c++) {\n // This will compute and cache\n getCellDisplayValue(grid, r, c, rowData, columns[c], epoch);\n }\n }\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 const rowEl = document.createElement('div');\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\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 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 } else if (dataRefChanged) {\n // Same structure, different row data - fast update\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__rowDataRef = rowData;\n } else {\n // Same row data reference - just patch if any values changed\n fastPatchRow(grid, rowEl, rowData, rowIndex);\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 * Ultra-fast row update for plain columns (no templates, renderers, or external views).\n * This is the hottest path during scroll - every microsecond counts.\n * Minimal operations: just update cell textContent directly.\n */\nfunction updateRowDataFast(grid: InternalGrid, rowEl: HTMLElement, rowData: Record<string, unknown>): void {\n const cells = rowEl.children;\n const columns = grid.visibleColumns;\n const len = columns.length;\n\n for (let i = 0; i < len; i++) {\n const cell = cells[i] as HTMLElement;\n if (!cell) continue;\n\n const col = columns[i];\n const value = rowData[col.field];\n\n // Direct textContent update - fastest possible path\n cell.textContent = value == null ? '' : String(value);\n }\n}\n\n/**\n * Update row data in-place without rebuilding DOM structure.\n * Used during scroll when showing different row data in the same row pool slot.\n * Handles special column types (templates, renderers, external views).\n */\nfunction updateRowDataInPlace(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n // Use children for faster access than querySelectorAll\n const cells = rowEl.children;\n const columns = grid.visibleColumns;\n const colsLen = columns.length;\n const focusRow = grid.focusRow;\n const focusCol = grid.focusCol;\n\n // Cache header row count on grid to avoid repeated DOM queries\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 // Update aria-rowindex on the row\n const newRowIndex = String(rowIndex + headerRowCount + 1);\n if (rowEl.getAttribute('aria-rowindex') !== newRowIndex) {\n rowEl.setAttribute('aria-rowindex', newRowIndex);\n }\n\n const rowIndexStr = String(rowIndex);\n\n for (let i = 0; i < colsLen; i++) {\n const col = columns[i];\n const cell = cells[i] as HTMLElement;\n if (!cell) continue;\n\n // Update data-row attribute only if changed\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n\n // Skip cells with active editors\n if (cell.classList.contains('editing')) continue;\n\n // Handle external views - they need remounting for new data\n if ((col as any).externalView) {\n const placeholder = cell.querySelector('[data-external-view]');\n if (placeholder) {\n const spec = (col as any).externalView;\n const value = (rowData as any)[col.field];\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 }\n }\n continue;\n }\n\n // Handle compiled templates\n const compiled = (col as any).__compiledView as ((ctx: any) => string) | undefined;\n if (compiled) {\n const value = (rowData as any)[col.field];\n const output = compiled({ row: rowData, value, field: col.field, column: col });\n const blocked = (compiled as any).__blocked;\n cell.innerHTML = blocked ? '' : sanitizeHTML(output);\n if (blocked) {\n cell.textContent = '';\n cell.setAttribute('data-blocked-template', '');\n } else {\n cell.removeAttribute('data-blocked-template');\n }\n continue;\n }\n\n // Handle inline templates\n const tplHolder = (col as any).__viewTemplate as HTMLElement | undefined;\n if (tplHolder) {\n const rawTpl = tplHolder.innerHTML;\n const value = (rowData as any)[col.field];\n if (/Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(rawTpl)) {\n cell.textContent = '';\n cell.setAttribute('data-blocked-template', '');\n } else {\n cell.innerHTML = sanitizeHTML(evalTemplateString(rawTpl, { row: rowData, value }));\n cell.removeAttribute('data-blocked-template');\n }\n continue;\n }\n\n // Handle viewRenderer\n const viewRenderer = (col as any).viewRenderer;\n if (viewRenderer) {\n const value = (rowData as any)[col.field];\n const produced = viewRenderer({ row: rowData, value, field: col.field, column: col });\n if (typeof produced === 'string') {\n cell.innerHTML = sanitizeHTML(produced);\n } else if (produced) {\n cell.innerHTML = '';\n cell.appendChild(produced);\n } else {\n cell.textContent = value == null ? '' : String(value);\n }\n continue;\n }\n\n // Plain value rendering - update content directly\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 // Convert to display string once\n let displayValue: string;\n if (col.type === 'date') {\n if (value == null || value === '') {\n displayValue = '';\n } else {\n let d: Date | null = null;\n if (value instanceof Date) d = value;\n else if (typeof value === 'number' || typeof value === 'string') {\n const tentative = new Date(value);\n if (!isNaN(tentative.getTime())) d = tentative;\n }\n displayValue = d ? d.toLocaleDateString() : '';\n }\n // Only update if changed\n if (cell.textContent !== displayValue) {\n cell.textContent = displayValue;\n }\n } else if (col.type === 'boolean') {\n const isTrue = !!value;\n const boolHtml = isTrue ? '&#x1F5F9;' : '&#9744;';\n // Boolean cells need innerHTML, check aria-checked to detect change\n const currentChecked = cell.getAttribute('aria-checked');\n if (currentChecked !== String(isTrue)) {\n cell.innerHTML = boolHtml;\n cell.setAttribute('aria-checked', String(isTrue));\n cell.setAttribute('aria-label', String(isTrue));\n }\n } else {\n displayValue = value == null ? '' : String(value);\n // Only update if changed\n if (cell.textContent !== displayValue) {\n cell.textContent = displayValue;\n }\n }\n\n // Update focus state - check before modifying\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n if (shouldHaveFocus) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n } else {\n cell.classList.remove('cell-focus');\n cell.setAttribute('aria-selected', 'false');\n }\n }\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\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.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 }\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 // Skip cells in edit mode\n if (cell.classList.contains('editing')) continue;\n\n // Skip templated / external / custom renderer cells\n if (col.__viewTemplate || col.__compiledView || col.viewRenderer || 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 if (value == null || value === '') {\n displayStr = '';\n } else if (value instanceof Date) {\n displayStr = isNaN(value.getTime()) ? '' : value.toLocaleDateString();\n } else {\n const d = new Date(value);\n displayStr = isNaN(d.getTime()) ? '' : d.toLocaleDateString();\n }\n } else if (col.type === 'boolean') {\n displayStr = value ? '\\u{1F5F9}' : '\\u2610';\n cell.setAttribute('aria-checked', String(!!value));\n } else {\n displayStr = value == null ? '' : String(value);\n }\n\n cell.textContent = displayStr;\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 const cell = document.createElement('div');\n cell.className = 'cell';\n addPart(cell, 'cell');\n\n // Role assigned conditionally; boolean cells later become checkbox.\n if ((col as any).type !== 'boolean') cell.setAttribute('role', 'gridcell');\n cell.setAttribute('data-col', String(colIndex));\n cell.setAttribute('data-row', String(rowIndex));\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(colIndex + 1));\n if (col.type) cell.setAttribute('data-type', col.type as any);\n\n // Apply sticky class if column has sticky property\n const sticky = (col as any).sticky;\n if (sticky === 'left') {\n cell.classList.add('sticky-left');\n } else if (sticky === 'right') {\n cell.classList.add('sticky-right');\n }\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 const viewRenderer = (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 const produced = viewRenderer({ row: rowData, value, field: col.field, column: col });\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) cell.appendChild(produced);\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 if (value == null || value === '') {\n cell.textContent = '';\n } else {\n let d: Date | null = null;\n if (value instanceof Date) d = value;\n else if (typeof value === 'number' || typeof value === 'string') {\n const tentative = new Date(value);\n if (!isNaN(tentative.getTime())) d = tentative;\n }\n cell.textContent = d ? d.toLocaleDateString() : '';\n }\n } else if (col.type === 'boolean') {\n const isTrue = !!value;\n cell.innerHTML = isTrue ? '&#x1F5F9;' : '&#9744;';\n cell.setAttribute('role', 'checkbox');\n cell.setAttribute('aria-checked', String(isTrue));\n cell.setAttribute('aria-label', String(isTrue));\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 if ((col as any).editable) {\n cell.tabIndex = 0;\n cell.addEventListener('mousedown', () => {\n grid.focusRow = rowIndex;\n grid.focusCol = colIndex;\n ensureCellVisible(grid);\n });\n if (editMode === 'click') {\n cell.addEventListener('click', (e) => {\n if (cell.classList.contains('editing')) return;\n e.stopPropagation();\n grid.focusRow = rowIndex;\n grid.focusCol = colIndex;\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n });\n } else {\n cell.addEventListener('dblclick', (e) => {\n e.stopPropagation();\n startRowEdit(grid, rowIndex, rowData);\n const rowElCurrent = grid.findRenderedRowElement?.(rowIndex);\n if (rowElCurrent) {\n const children = rowElCurrent.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, rowData, rowIndex, col2, children[i] as HTMLElement);\n }\n }\n });\n }\n cell.addEventListener('keydown', (e) => {\n if (\n (col.type === 'select' || col.type === 'typeahead') &&\n !cell.classList.contains('editing') &&\n e.key === 'Enter'\n ) {\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();\n }, 0);\n return;\n }\n if (col.type === 'boolean' && e.key === ' ' && !cell.classList.contains('editing')) {\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 = newVal ? '&#x1F5F9;' : '&#9744;';\n cell.setAttribute('aria-label', String(!!newVal));\n return;\n }\n if (e.key === 'Enter' && !cell.classList.contains('editing')) {\n e.preventDefault();\n grid.focusRow = rowIndex;\n grid.focusCol = colIndex;\n if (typeof grid.beginBulkEdit === 'function') grid.beginBulkEdit(rowIndex);\n else inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n return;\n }\n if (e.key === 'F2' && !cell.classList.contains('editing')) {\n e.preventDefault();\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n return;\n }\n });\n } else if (col.type === 'boolean') {\n if (!cell.hasAttribute('tabindex')) cell.tabIndex = 0;\n cell.addEventListener('keydown', (e) => {\n if (e.key === ' ') {\n e.preventDefault();\n const newVal = !rowData[col.field];\n if (grid.activeEditRows !== rowIndex) startRowEdit(grid, rowIndex, rowData);\n commitCellValue(grid, rowIndex, col as any, newVal, rowData);\n cell.innerHTML = newVal ? '&#x1F5F9;' : '&#9744;';\n cell.setAttribute('role', 'checkbox');\n cell.setAttribute('aria-checked', String(!!newVal));\n cell.setAttribute('aria-label', String(!!newVal));\n }\n });\n }\n\n // Initialize selection attributes\n if (focusRow === rowIndex && focusCol === colIndex) cell.setAttribute('aria-selected', 'true');\n else cell.setAttribute('aria-selected', 'false');\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 if (!firstCell) return;\n const rowIndex = Number(firstCell.getAttribute('data-row'));\n if (isNaN(rowIndex)) return;\n const rowData = grid._rows[rowIndex];\n if (!rowData) return;\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 grid.focusRow = rowIndex;\n grid.focusCol = colIndex;\n ensureCellVisible(grid);\n }\n }\n if (rowEl.querySelector('.cell.editing')) {\n const active = rowEl.querySelectorAll('.cell.editing');\n if (!isDbl) return;\n active.forEach((n: any) => n.classList.remove('editing'));\n }\n const mode: 'click' | 'doubleClick' = ((grid as any).effectiveConfig?.editOn || grid.editOn || 'doubleClick') as any;\n if (mode === 'click' || (mode === 'doubleClick' && isDbl)) startRowEdit(grid, rowIndex, rowData);\n else return;\n Array.from(rowEl.children).forEach((c: any, i: number) => {\n const col = grid.visibleColumns[i];\n if (col && (col as any).editable) inlineEnterEdit(grid, rowData, rowIndex, col, c as HTMLElement);\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(\n 'input,select,textarea,[contenteditable=\"true\"],[contenteditable=\"\"],[tabindex]:not([tabindex=\"-1\"])'\n ) as HTMLElement | null;\n try {\n editor?.focus();\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 { renderInlineRow } from './rows';\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 if (revert && snapshot && current) {\n Object.keys(snapshot).forEach((k) => (current[k] = snapshot[k]));\n grid._changedRowIndices.delete(rowIndex);\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 const rowEl = grid.findRenderedRowElement?.(rowIndex);\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 if (revert) {\n queueMicrotask(() => {\n try {\n (grid as unknown as HTMLElement).focus();\n const rowIdx = grid.focusRow;\n const colIdx = grid.focusCol;\n const rowEl2 = grid.findRenderedRowElement?.(rowIdx);\n if (rowEl2) {\n Array.from(grid.bodyEl.querySelectorAll('.cell-focus')).forEach((el: any) =>\n el.classList.remove('cell-focus')\n );\n const cell = rowEl2.querySelector(`.cell[data-row=\"${rowIdx}\"][data-col=\"${colIdx}\"]`);\n if (cell) (cell as HTMLElement).classList.add('cell-focus');\n }\n } catch {\n /* empty */\n }\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 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 */\nexport function inlineEnterEdit(\n grid: InternalGrid,\n rowData: any,\n rowIndex: number,\n column: ColumnConfig<any>,\n cell: HTMLElement\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 = rowData[column.field];\n cell.classList.add('editing');\n const commit = (newValue: any) => {\n commitCellValue(grid, rowIndex, column, newValue, rowData);\n };\n const cancel = () => {\n rowData[column.field] = originalValue;\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 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 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 const val =\n hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox'\n ? input.checked\n : (input as any).value;\n commit(val);\n }\n if (e.key === 'Escape') cancel();\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 setTimeout(() => input.focus(), 0);\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 } else if (typeof editorSpec === 'function') {\n const produced = editorSpec({ row: rowData, value, field: column.field, column, commit, cancel });\n if (typeof produced === 'string') editorHost.innerHTML = produced;\n else editorHost.appendChild(produced);\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","/**\r\n * Sorting Module\r\n *\r\n * Handles column sorting state transitions and row ordering.\r\n */\r\n\r\nimport type { ColumnConfig, InternalGrid } from '../types';\r\nimport { renderHeader } from './header';\r\n\r\n/**\r\n * Cycle sort state for a column: none -> ascending -> descending -> none.\r\n * Restores original row order when clearing sort.\r\n */\r\nexport function toggleSort(grid: InternalGrid, col: ColumnConfig<any>): void {\r\n if (!grid.sortState || grid.sortState.field !== col.field) {\r\n if (!grid.sortState) grid.__originalOrder = grid._rows.slice();\r\n applySort(grid, col, 1);\r\n } else if (grid.sortState.direction === 1) {\r\n applySort(grid, col, -1);\r\n } else {\r\n grid.sortState = null;\r\n // Force full row rebuild after clearing sort so templated cells reflect original order\r\n grid.__rowRenderEpoch++;\r\n // Invalidate existing pooled row epochs so virtualization triggers a full inline rebuild\r\n grid.rowPool.forEach((r) => ((r as any).__epoch = -1));\r\n grid._rows = grid.__originalOrder.slice();\r\n renderHeader(grid);\r\n // After re-render ensure cleared column shows aria-sort=\"none\" baseline.\r\n const headers = grid.headerRowEl?.querySelectorAll('[role=\"columnheader\"].sortable');\r\n headers?.forEach((h: any) => {\r\n if (!h.getAttribute('aria-sort')) h.setAttribute('aria-sort', 'none');\r\n else if (h.getAttribute('aria-sort') === 'ascending' || h.getAttribute('aria-sort') === 'descending') {\r\n // The active column was re-rendered already, but normalize any missing ones.\r\n if (!grid.sortState) h.setAttribute('aria-sort', 'none');\r\n }\r\n });\r\n grid.refreshVirtualWindow(true);\r\n (grid as unknown as HTMLElement).dispatchEvent(\r\n new CustomEvent('sort-change', { detail: { field: col.field, direction: 0 } })\r\n );\r\n // Trigger state change after sort is cleared\r\n grid.requestStateChange?.();\r\n }\r\n}\r\n\r\n/**\r\n * Apply a concrete sort direction to rows using either the column's custom comparator\r\n * or a default comparator aware of null/undefined ordering.\r\n */\r\nexport function applySort(grid: InternalGrid, col: ColumnConfig<any>, dir: 1 | -1): void {\r\n grid.sortState = { field: col.field, direction: dir };\r\n const comparator =\r\n (col as any).sortComparator ||\r\n ((a: any, b: any) => (a == null && b == null ? 0 : a == null ? -1 : b == null ? 1 : a > b ? 1 : a < b ? -1 : 0));\r\n grid._rows.sort((rA: any, rB: any) => comparator(rA[col.field], rB[col.field], rA, rB) * dir);\r\n // Bump epoch so renderVisibleRows triggers full inline rebuild (ensures templated / compiled view cells update)\r\n grid.__rowRenderEpoch++;\r\n // Invalidate pooled rows to guarantee rebuild even if epoch comparison logic changes\r\n grid.rowPool.forEach((r) => ((r as any).__epoch = -1));\r\n renderHeader(grid);\r\n grid.refreshVirtualWindow(true);\r\n (grid as unknown as HTMLElement).dispatchEvent(\r\n new CustomEvent('sort-change', { detail: { field: col.field, direction: dir } })\r\n );\r\n // Trigger state change after sort applied\r\n grid.requestStateChange?.();\r\n}\r\n","/**\n * Header Rendering Module\n *\n * Handles rendering of the grid header row with sorting and resize affordances.\n */\n\nimport type { ColumnConfig, InternalGrid } from '../types';\nimport { addPart } from './columns';\nimport { toggleSort } from './sorting';\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 // ARIA row index for header row is always 1\n headerRow.setAttribute('role', 'row');\n headerRow.setAttribute('aria-rowindex', '1');\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 // Apply sticky class if column has sticky property\n if ((col as any).sticky === 'left') {\n cell.classList.add('sticky-left');\n } else if ((col as any).sticky === 'right') {\n cell.classList.add('sticky-right');\n }\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 icon.style.opacity = '0.6';\n const active = grid.sortState?.field === col.field ? grid.sortState.direction : 0;\n icon.textContent = active === 1 ? '▲' : active === -1 ? '▼' : '⇅';\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 // Only set position: relative if column is not sticky (sticky already creates positioning context)\n if (!(col as any).sticky) {\n cell.style.position = 'relative';\n }\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 cell.appendChild(handle);\n }\n headerRow.appendChild(cell);\n });\n // If a column grouping row exists (handled in component render), we also mirror grouped class onto header-group-row cells in a post-pass\n try {\n const hostRoot = (grid as any).shadowRoot as ShadowRoot | undefined;\n if (hostRoot) {\n const groupCells = hostRoot.querySelectorAll('.header-group-row .cell');\n groupCells.forEach((gc) => {\n const id = gc.getAttribute('data-group');\n if (id) gc.classList.add('grouped');\n });\n }\n } catch {\n /* empty */\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","/** Controller interface for column resize interactions (local minimal typing). */\r\nimport type { InternalGrid } from '../types';\r\n\r\ninterface ResizeController {\r\n start: (e: MouseEvent, colIndex: number, cell: HTMLElement) => void;\r\n dispose: () => void;\r\n /** True while a resize drag is in progress (used to suppress header click/sort). */\r\n isResizing: boolean;\r\n}\r\n\r\nexport function createResizeController(grid: InternalGrid): ResizeController {\r\n let resizeState: { startX: number; colIndex: number; startWidth: number } | null = null;\r\n let pendingRaf: number | null = null;\r\n let prevCursor: string | null = null;\r\n let prevUserSelect: string | null = null;\r\n const onMove = (e: MouseEvent) => {\r\n if (!resizeState) return;\r\n const delta = e.clientX - resizeState.startX;\r\n const width = Math.max(40, resizeState.startWidth + delta);\r\n const col = grid.visibleColumns[resizeState.colIndex];\r\n col.width = width;\r\n col.__userResized = true;\r\n col.__renderedWidth = width;\r\n if (pendingRaf == null) {\r\n pendingRaf = requestAnimationFrame(() => {\r\n pendingRaf = null;\r\n grid.updateTemplate?.();\r\n });\r\n }\r\n (grid as unknown as HTMLElement).dispatchEvent(\r\n new CustomEvent('column-resize', { detail: { field: col.field, width } })\r\n );\r\n };\r\n let justFinishedResize = false;\r\n const onUp = () => {\r\n const hadResize = resizeState !== null;\r\n // Set flag to suppress click events that fire immediately after mouseup\r\n if (hadResize) {\r\n justFinishedResize = true;\r\n requestAnimationFrame(() => {\r\n justFinishedResize = false;\r\n });\r\n }\r\n window.removeEventListener('mousemove', onMove);\r\n window.removeEventListener('mouseup', onUp);\r\n if (prevCursor !== null) {\r\n document.documentElement.style.cursor = prevCursor;\r\n prevCursor = null;\r\n }\r\n if (prevUserSelect !== null) {\r\n document.body.style.userSelect = prevUserSelect;\r\n prevUserSelect = null;\r\n }\r\n resizeState = null;\r\n // Trigger state change after resize completes\r\n if (hadResize && grid.requestStateChange) {\r\n grid.requestStateChange();\r\n }\r\n };\r\n return {\r\n get isResizing() {\r\n return resizeState !== null || justFinishedResize;\r\n },\r\n start(e, colIndex, cell) {\r\n e.preventDefault();\r\n const rect = cell.getBoundingClientRect();\r\n resizeState = { startX: e.clientX, colIndex, startWidth: rect.width };\r\n window.addEventListener('mousemove', onMove);\r\n window.addEventListener('mouseup', onUp);\r\n if (prevCursor === null) prevCursor = document.documentElement.style.cursor;\r\n document.documentElement.style.cursor = 'e-resize';\r\n if (prevUserSelect === null) prevUserSelect = document.body.style.userSelect;\r\n document.body.style.userSelect = 'none';\r\n },\r\n dispose() {\r\n onUp();\r\n },\r\n };\r\n}\r\n","/**\r\n * Shell infrastructure for grid header bar and tool panels.\r\n *\r\n * The shell is an optional wrapper layer that provides:\r\n * - Header bar with title, plugin content, and toolbar buttons\r\n * - Tool panels that plugins can register content into\r\n * - Light DOM parsing for framework-friendly configuration\r\n */\r\n\r\nimport type {\r\n HeaderContentDefinition,\r\n ShellConfig,\r\n ToolbarButtonConfig,\r\n ToolbarButtonInfo,\r\n ToolPanelDefinition,\r\n} from '../types';\r\n\r\n/**\r\n * State for managing shell UI.\r\n */\r\nexport interface ShellState {\r\n /** Registered tool panels (from plugins + consumer API) */\r\n toolPanels: Map<string, ToolPanelDefinition>;\r\n /** Registered header content (from plugins + consumer API) */\r\n headerContents: Map<string, HeaderContentDefinition>;\r\n /** Custom toolbar buttons registered via API */\r\n toolbarButtons: Map<string, ToolbarButtonConfig>;\r\n /** Light DOM toolbar buttons */\r\n lightDomButtons: HTMLElement[];\r\n /** Light DOM header content elements */\r\n lightDomHeaderContent: HTMLElement[];\r\n /** Currently open tool panel ID (null if closed) */\r\n activePanel: string | null;\r\n /** Cleanup functions for header content render returns */\r\n headerContentCleanups: Map<string, () => void>;\r\n /** Cleanup function for active panel render return */\r\n activePanelCleanup: (() => void) | null;\r\n /** Cleanup functions for toolbar button render returns */\r\n toolbarButtonCleanups: Map<string, () => void>;\r\n}\r\n\r\n/**\r\n * Create initial shell state.\r\n */\r\nexport function createShellState(): ShellState {\r\n return {\r\n toolPanels: new Map(),\r\n headerContents: new Map(),\r\n toolbarButtons: new Map(),\r\n lightDomButtons: [],\r\n lightDomHeaderContent: [],\r\n activePanel: null,\r\n headerContentCleanups: new Map(),\r\n activePanelCleanup: null,\r\n toolbarButtonCleanups: new Map(),\r\n };\r\n}\r\n\r\n/**\r\n * Determine if shell header should be rendered.\r\n */\r\nexport function shouldRenderShellHeader(config: ShellConfig | undefined, state: ShellState): boolean {\r\n // Check if title is configured\r\n if (config?.header?.title) return true;\r\n\r\n // Check if config has toolbar buttons\r\n if (config?.header?.toolbarButtons?.length) return true;\r\n\r\n // Check if any tool panels are registered (need toolbar buttons for them)\r\n if (state.toolPanels.size > 0) return true;\r\n\r\n // Check if any header content is registered\r\n if (state.headerContents.size > 0) return true;\r\n\r\n // Check if any API toolbar buttons registered\r\n if (state.toolbarButtons.size > 0) return true;\r\n\r\n // Check if light DOM has header elements\r\n if (state.lightDomButtons.length > 0 || state.lightDomHeaderContent.length > 0) return true;\r\n\r\n return false;\r\n}\r\n\r\n/**\r\n * Render the shell header HTML.\r\n */\r\nexport function renderShellHeader(config: ShellConfig | undefined, state: ShellState): string {\r\n const title = config?.header?.title ?? '';\r\n const hasTitle = !!title;\r\n\r\n // Collect all toolbar buttons in order\r\n // 1. Config buttons (sorted by order)\r\n // 2. API-registered buttons (sorted by order)\r\n // 3. Light DOM buttons via slot\r\n // 4. Panel toggle buttons (sorted by order)\r\n\r\n const configButtons = config?.header?.toolbarButtons ?? [];\r\n const hasConfigButtons = configButtons.length > 0;\r\n const hasApiButtons = state.toolbarButtons.size > 0;\r\n const hasLightDomButtons = state.lightDomButtons.length > 0;\r\n const hasPanelToggles = state.toolPanels.size > 0;\r\n const hasCustomButtons = hasConfigButtons || hasApiButtons || hasLightDomButtons;\r\n const showSeparator = hasCustomButtons && hasPanelToggles;\r\n\r\n // Sort config buttons by order\r\n const sortedConfigButtons = [...configButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\r\n\r\n // Sort API buttons by order\r\n const sortedApiButtons = [...state.toolbarButtons.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\r\n\r\n // Sort panel toggles by order\r\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\r\n\r\n // Build toolbar HTML\r\n let toolbarHtml = '';\r\n\r\n // Config buttons with icon/action (grid renders these)\r\n for (const btn of sortedConfigButtons) {\r\n if (btn.icon && btn.action) {\r\n toolbarHtml += `<button class=\"tbw-toolbar-btn\" data-btn=\"${btn.id}\" title=\"${btn.label}\" aria-label=\"${\r\n btn.label\r\n }\"${btn.disabled ? ' disabled' : ''}>${btn.icon}</button>`;\r\n }\r\n }\r\n\r\n // API buttons with icon/action\r\n for (const btn of sortedApiButtons) {\r\n if (btn.icon && btn.action) {\r\n toolbarHtml += `<button class=\"tbw-toolbar-btn\" data-btn=\"${btn.id}\" title=\"${btn.label}\" aria-label=\"${\r\n btn.label\r\n }\"${btn.disabled ? ' disabled' : ''}>${btn.icon}</button>`;\r\n }\r\n }\r\n\r\n // Placeholders for config/API buttons with element or render function\r\n for (const btn of sortedConfigButtons) {\r\n if (btn.element || btn.render) {\r\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\r\n }\r\n }\r\n for (const btn of sortedApiButtons) {\r\n if (btn.element || btn.render) {\r\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\r\n }\r\n }\r\n\r\n // Light DOM slot\r\n if (hasLightDomButtons) {\r\n toolbarHtml += '<slot name=\"toolbar\"></slot>';\r\n }\r\n\r\n // Separator\r\n if (showSeparator) {\r\n toolbarHtml += '<div class=\"tbw-toolbar-separator\"></div>';\r\n }\r\n\r\n // Panel toggle buttons\r\n for (const panel of sortedPanels) {\r\n const isActive = state.activePanel === panel.id;\r\n toolbarHtml += `<button class=\"tbw-toolbar-btn${isActive ? ' active' : ''}\" data-panel=\"${panel.id}\" title=\"${\r\n panel.tooltip ?? panel.title\r\n }\" aria-label=\"${panel.tooltip ?? panel.title}\" aria-pressed=\"${isActive}\" aria-controls=\"tbw-panel-${panel.id}\">${\r\n panel.icon\r\n }</button>`;\r\n }\r\n\r\n return `\r\n <div class=\"tbw-shell-header\" part=\"shell-header\" role=\"banner\">\r\n ${hasTitle ? `<div class=\"tbw-shell-title\">${title}</div>` : ''}\r\n <div class=\"tbw-shell-content\" part=\"shell-content\" role=\"region\" aria-label=\"Grid information\">\r\n <slot name=\"header-content\"></slot>\r\n </div>\r\n <div class=\"tbw-shell-toolbar\" part=\"shell-toolbar\" role=\"toolbar\" aria-label=\"Grid tools\">\r\n ${toolbarHtml}\r\n </div>\r\n </div>\r\n `;\r\n}\r\n\r\n/**\r\n * Render the shell body wrapper HTML (contains grid content + tool panel).\r\n */\r\nexport function renderShellBody(config: ShellConfig | undefined, state: ShellState, gridContentHtml: string): string {\r\n const position = config?.toolPanel?.position ?? 'right';\r\n const hasPanel = state.toolPanels.size > 0;\r\n const isOpen = state.activePanel !== null;\r\n const activePanel = state.activePanel ? state.toolPanels.get(state.activePanel) : null;\r\n\r\n const panelHtml = hasPanel\r\n ? `\r\n <aside class=\"tbw-tool-panel${\r\n isOpen ? ' open' : ''\r\n }\" part=\"tool-panel\" data-position=\"${position}\" role=\"complementary\" aria-label=\"${\r\n activePanel?.title ?? 'Tool panel'\r\n }\" id=\"tbw-panel-${state.activePanel ?? 'closed'}\">\r\n <div class=\"tbw-tool-panel-header\">\r\n <span class=\"tbw-tool-panel-title\">${activePanel?.title ?? ''}</span>\r\n <button class=\"tbw-tool-panel-close\" aria-label=\"Close panel\">✕</button>\r\n </div>\r\n <div class=\"tbw-tool-panel-content\" role=\"region\"></div>\r\n </aside>\r\n `\r\n : '';\r\n\r\n // For left position, panel comes before content in DOM order\r\n if (position === 'left') {\r\n return `\r\n <div class=\"tbw-shell-body\">\r\n ${panelHtml}\r\n <div class=\"tbw-grid-content\">\r\n ${gridContentHtml}\r\n </div>\r\n </div>\r\n `;\r\n }\r\n\r\n return `\r\n <div class=\"tbw-shell-body\">\r\n <div class=\"tbw-grid-content\">\r\n ${gridContentHtml}\r\n </div>\r\n ${panelHtml}\r\n </div>\r\n `;\r\n}\r\n\r\n/**\r\n * Parse light DOM shell elements (tbw-grid-header, etc.).\r\n */\r\nexport function parseLightDomShell(host: HTMLElement, state: ShellState): void {\r\n const headerEl = host.querySelector('tbw-grid-header');\r\n if (!headerEl) return;\r\n\r\n // Hide the light DOM container\r\n (headerEl as HTMLElement).style.display = 'none';\r\n\r\n // Parse header content elements\r\n const headerContents = headerEl.querySelectorAll('tbw-grid-header-content');\r\n state.lightDomHeaderContent = Array.from(headerContents) as HTMLElement[];\r\n\r\n // Assign slot names for slotting into shadow DOM\r\n state.lightDomHeaderContent.forEach((el, i) => {\r\n el.setAttribute('slot', 'header-content');\r\n });\r\n\r\n // Parse toolbar button elements\r\n const toolButtons = headerEl.querySelectorAll('tbw-grid-tool-button');\r\n state.lightDomButtons = Array.from(toolButtons) as HTMLElement[];\r\n\r\n // Sort by order attribute and assign slots\r\n state.lightDomButtons.sort((a, b) => {\r\n const orderA = parseInt(a.getAttribute('order') ?? '100', 10);\r\n const orderB = parseInt(b.getAttribute('order') ?? '100', 10);\r\n return orderA - orderB;\r\n });\r\n\r\n state.lightDomButtons.forEach((el) => {\r\n el.setAttribute('slot', 'toolbar');\r\n });\r\n}\r\n\r\n/**\r\n * Set up event listeners for shell toolbar buttons.\r\n */\r\nexport function setupShellEventListeners(\r\n shadowRoot: ShadowRoot,\r\n config: ShellConfig | undefined,\r\n state: ShellState,\r\n callbacks: {\r\n onPanelToggle: (panelId: string) => void;\r\n onPanelClose: () => void;\r\n onToolbarButtonClick: (buttonId: string) => void;\r\n }\r\n): void {\r\n const toolbar = shadowRoot.querySelector('.tbw-shell-toolbar');\r\n if (toolbar) {\r\n toolbar.addEventListener('click', (e) => {\r\n const target = e.target as HTMLElement;\r\n const btn = target.closest('[data-panel]') as HTMLElement | null;\r\n if (btn) {\r\n const panelId = btn.getAttribute('data-panel');\r\n if (panelId) {\r\n callbacks.onPanelToggle(panelId);\r\n }\r\n return;\r\n }\r\n\r\n const customBtn = target.closest('[data-btn]') as HTMLElement | null;\r\n if (customBtn) {\r\n const btnId = customBtn.getAttribute('data-btn');\r\n if (btnId) {\r\n callbacks.onToolbarButtonClick(btnId);\r\n }\r\n }\r\n });\r\n }\r\n\r\n const closeBtn = shadowRoot.querySelector('.tbw-tool-panel-close');\r\n if (closeBtn) {\r\n closeBtn.addEventListener('click', () => {\r\n callbacks.onPanelClose();\r\n });\r\n }\r\n}\r\n\r\n/**\r\n * Render custom button elements/render functions into toolbar slots.\r\n */\r\nexport function renderCustomToolbarButtons(\r\n shadowRoot: ShadowRoot,\r\n config: ShellConfig | undefined,\r\n state: ShellState\r\n): void {\r\n const allButtons = [...(config?.header?.toolbarButtons ?? []), ...state.toolbarButtons.values()];\r\n\r\n for (const btn of allButtons) {\r\n const slot = shadowRoot.querySelector(`[data-btn-slot=\"${btn.id}\"]`);\r\n if (!slot) continue;\r\n\r\n // Clean up previous render if any\r\n const existingCleanup = state.toolbarButtonCleanups.get(btn.id);\r\n if (existingCleanup) {\r\n existingCleanup();\r\n state.toolbarButtonCleanups.delete(btn.id);\r\n }\r\n\r\n if (btn.element) {\r\n slot.appendChild(btn.element);\r\n } else if (btn.render) {\r\n const cleanup = btn.render(slot as HTMLElement);\r\n if (cleanup) {\r\n state.toolbarButtonCleanups.set(btn.id, cleanup);\r\n }\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Render header content from plugins into the shell content area.\r\n */\r\nexport function renderHeaderContent(shadowRoot: ShadowRoot, state: ShellState): void {\r\n const contentArea = shadowRoot.querySelector('.tbw-shell-content');\r\n if (!contentArea) return;\r\n\r\n // Sort by order\r\n const sortedContents = [...state.headerContents.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\r\n\r\n // Create containers for each content piece (before the slot)\r\n const slot = contentArea.querySelector('slot[name=\"header-content\"]');\r\n\r\n for (const content of sortedContents) {\r\n // Clean up previous render if any\r\n const existingCleanup = state.headerContentCleanups.get(content.id);\r\n if (existingCleanup) {\r\n existingCleanup();\r\n state.headerContentCleanups.delete(content.id);\r\n }\r\n\r\n // Check if container already exists\r\n let container = contentArea.querySelector(`[data-header-content=\"${content.id}\"]`) as HTMLElement | null;\r\n if (!container) {\r\n container = document.createElement('div');\r\n container.setAttribute('data-header-content', content.id);\r\n // Insert before the slot\r\n if (slot) {\r\n contentArea.insertBefore(container, slot);\r\n } else {\r\n contentArea.appendChild(container);\r\n }\r\n }\r\n\r\n const cleanup = content.render(container);\r\n if (cleanup) {\r\n state.headerContentCleanups.set(content.id, cleanup);\r\n }\r\n }\r\n}\r\n\r\n/**\r\n * Render tool panel content when panel opens.\r\n */\r\nexport function renderPanelContent(shadowRoot: ShadowRoot, state: ShellState): void {\r\n if (!state.activePanel) return;\r\n\r\n const panel = state.toolPanels.get(state.activePanel);\r\n if (!panel) return;\r\n\r\n const contentArea = shadowRoot.querySelector('.tbw-tool-panel-content');\r\n if (!contentArea) return;\r\n\r\n // Clean up previous panel content\r\n if (state.activePanelCleanup) {\r\n state.activePanelCleanup();\r\n state.activePanelCleanup = null;\r\n }\r\n\r\n // Clear content area\r\n contentArea.innerHTML = '';\r\n\r\n // Render new content\r\n const cleanup = panel.render(contentArea as HTMLElement);\r\n if (cleanup) {\r\n state.activePanelCleanup = cleanup;\r\n }\r\n}\r\n\r\n/**\r\n * Update toolbar button active states.\r\n */\r\nexport function updateToolbarActiveStates(shadowRoot: ShadowRoot, state: ShellState): void {\r\n const buttons = shadowRoot.querySelectorAll('[data-panel]');\r\n buttons.forEach((btn) => {\r\n const panelId = btn.getAttribute('data-panel');\r\n const isActive = panelId === state.activePanel;\r\n btn.classList.toggle('active', isActive);\r\n btn.setAttribute('aria-pressed', String(isActive));\r\n });\r\n}\r\n\r\n/**\r\n * Update tool panel open/close state.\r\n */\r\nexport function updatePanelState(shadowRoot: ShadowRoot, state: ShellState): void {\r\n const panel = shadowRoot.querySelector('.tbw-tool-panel');\r\n if (!panel) return;\r\n\r\n const isOpen = state.activePanel !== null;\r\n panel.classList.toggle('open', isOpen);\r\n\r\n if (isOpen && state.activePanel) {\r\n const panelDef = state.toolPanels.get(state.activePanel);\r\n const titleEl = panel.querySelector('.tbw-tool-panel-title');\r\n if (titleEl) {\r\n titleEl.textContent = panelDef?.title ?? '';\r\n }\r\n panel.setAttribute('aria-label', `${panelDef?.title ?? 'Tool'} panel`);\r\n panel.id = `tbw-panel-${state.activePanel}`;\r\n }\r\n}\r\n\r\n/**\r\n * Get all toolbar button info.\r\n */\r\nexport function getToolbarButtonsInfo(config: ShellConfig | undefined, state: ShellState): ToolbarButtonInfo[] {\r\n const result: ToolbarButtonInfo[] = [];\r\n\r\n // Config buttons\r\n for (const btn of config?.header?.toolbarButtons ?? []) {\r\n result.push({\r\n id: btn.id,\r\n label: btn.label,\r\n disabled: btn.disabled ?? false,\r\n source: 'config',\r\n });\r\n }\r\n\r\n // API buttons\r\n for (const btn of state.toolbarButtons.values()) {\r\n result.push({\r\n id: btn.id,\r\n label: btn.label,\r\n disabled: btn.disabled ?? false,\r\n source: 'config',\r\n });\r\n }\r\n\r\n // Light DOM buttons (limited info since user provides DOM)\r\n for (let i = 0; i < state.lightDomButtons.length; i++) {\r\n const el = state.lightDomButtons[i];\r\n const btn = el.querySelector('button');\r\n result.push({\r\n id: `light-dom-${i}`,\r\n label: btn?.getAttribute('title') ?? btn?.getAttribute('aria-label') ?? '',\r\n disabled: btn?.disabled ?? false,\r\n source: 'light-dom',\r\n });\r\n }\r\n\r\n // Panel toggles\r\n for (const panel of state.toolPanels.values()) {\r\n result.push({\r\n id: `panel-toggle-${panel.id}`,\r\n label: panel.tooltip ?? panel.title,\r\n disabled: false,\r\n source: 'panel-toggle',\r\n panelId: panel.id,\r\n });\r\n }\r\n\r\n return result;\r\n}\r\n\r\n/**\r\n * Cleanup all shell state when grid disconnects.\r\n */\r\nexport function cleanupShellState(state: ShellState): void {\r\n // Clean up header content\r\n for (const cleanup of state.headerContentCleanups.values()) {\r\n cleanup();\r\n }\r\n state.headerContentCleanups.clear();\r\n\r\n // Clean up active panel\r\n if (state.activePanelCleanup) {\r\n state.activePanelCleanup();\r\n state.activePanelCleanup = null;\r\n }\r\n\r\n // Clean up toolbar buttons\r\n for (const cleanup of state.toolbarButtonCleanups.values()) {\r\n cleanup();\r\n }\r\n state.toolbarButtonCleanups.clear();\r\n\r\n // Invoke panel onClose if open\r\n if (state.activePanel) {\r\n const panel = state.toolPanels.get(state.activePanel);\r\n panel?.onClose?.();\r\n }\r\n\r\n // Clear registrations\r\n state.toolPanels.clear();\r\n state.headerContents.clear();\r\n state.toolbarButtons.clear();\r\n state.lightDomButtons = [];\r\n state.lightDomHeaderContent = [];\r\n state.activePanel = null;\r\n}\r\n","/**\r\n * Plugin Manager\r\n *\r\n * Manages plugin instances for a single grid.\r\n * Each grid has its own PluginManager with its own set of plugin instances.\r\n */\r\n\r\nimport type { ColumnConfig } from '../types';\r\nimport type {\r\n BaseGridPlugin,\r\n CellClickEvent,\r\n CellEditor,\r\n CellMouseEvent,\r\n CellRenderer,\r\n ContextMenuItem,\r\n ContextMenuParams,\r\n HeaderClickEvent,\r\n HeaderRenderer,\r\n RowClickEvent,\r\n ScrollEvent,\r\n} from './base-plugin';\r\n\r\n/**\r\n * Manages plugins for a single grid instance.\r\n */\r\nexport class PluginManager {\r\n /** Plugin instances in order of attachment */\r\n private plugins: BaseGridPlugin[] = [];\r\n\r\n /** Map from plugin class to instance for fast lookup */\r\n private pluginMap: Map<new (...args: unknown[]) => BaseGridPlugin, BaseGridPlugin> = new Map();\r\n\r\n /** Cell renderers registered by plugins */\r\n private cellRenderers: Map<string, CellRenderer> = new Map();\r\n\r\n /** Header renderers registered by plugins */\r\n private headerRenderers: Map<string, HeaderRenderer> = new Map();\r\n\r\n /** Cell editors registered by plugins */\r\n private cellEditors: Map<string, CellEditor> = new Map();\r\n\r\n constructor(private grid: any) {}\r\n\r\n /**\r\n * Attach all plugins from the config.\r\n */\r\n attachAll(plugins: BaseGridPlugin[]): void {\r\n for (const plugin of plugins) {\r\n this.attach(plugin);\r\n }\r\n }\r\n\r\n /**\r\n * Attach a plugin to this grid.\r\n */\r\n attach(plugin: BaseGridPlugin): void {\r\n // Store by constructor for type-safe lookup\r\n this.pluginMap.set(plugin.constructor as new (...args: unknown[]) => BaseGridPlugin, plugin);\r\n this.plugins.push(plugin);\r\n\r\n // Register renderers/editors\r\n if (plugin.cellRenderers) {\r\n for (const [type, renderer] of Object.entries(plugin.cellRenderers)) {\r\n this.cellRenderers.set(type, renderer);\r\n }\r\n }\r\n if (plugin.headerRenderers) {\r\n for (const [type, renderer] of Object.entries(plugin.headerRenderers)) {\r\n this.headerRenderers.set(type, renderer);\r\n }\r\n }\r\n if (plugin.cellEditors) {\r\n for (const [type, editor] of Object.entries(plugin.cellEditors)) {\r\n this.cellEditors.set(type, editor);\r\n }\r\n }\r\n\r\n // Call attach lifecycle method\r\n plugin.attach(this.grid);\r\n }\r\n\r\n /**\r\n * Detach all plugins and clean up.\r\n */\r\n detachAll(): void {\r\n // Detach in reverse order\r\n for (let i = this.plugins.length - 1; i >= 0; i--) {\r\n this.plugins[i].detach();\r\n }\r\n this.plugins = [];\r\n this.pluginMap.clear();\r\n this.cellRenderers.clear();\r\n this.headerRenderers.clear();\r\n this.cellEditors.clear();\r\n }\r\n\r\n /**\r\n * Get a plugin instance by its class.\r\n */\r\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\r\n return this.pluginMap.get(PluginClass) as T | undefined;\r\n }\r\n\r\n /**\r\n * Get a plugin instance by its name.\r\n */\r\n getPluginByName(name: string): BaseGridPlugin | undefined {\r\n return this.plugins.find((p) => p.name === name);\r\n }\r\n\r\n /**\r\n * Check if a plugin is attached.\r\n */\r\n hasPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): boolean {\r\n return this.pluginMap.has(PluginClass);\r\n }\r\n\r\n /**\r\n * Get all attached plugins.\r\n */\r\n getAll(): readonly BaseGridPlugin[] {\r\n return this.plugins;\r\n }\r\n\r\n /**\r\n * Get a cell renderer by type name.\r\n */\r\n getCellRenderer(type: string): CellRenderer | undefined {\r\n return this.cellRenderers.get(type);\r\n }\r\n\r\n /**\r\n * Get a header renderer by type name.\r\n */\r\n getHeaderRenderer(type: string): HeaderRenderer | undefined {\r\n return this.headerRenderers.get(type);\r\n }\r\n\r\n /**\r\n * Get a cell editor by type name.\r\n */\r\n getCellEditor(type: string): CellEditor | undefined {\r\n return this.cellEditors.get(type);\r\n }\r\n\r\n /**\r\n * Get all CSS styles from all plugins.\r\n */\r\n getAllStyles(): string {\r\n return this.plugins\r\n .filter((p) => p.styles)\r\n .map((p) => p.styles)\r\n .join('\\n');\r\n }\r\n\r\n // ===== Hook execution methods =====\r\n\r\n /**\r\n * Execute processRows hook on all plugins.\r\n */\r\n processRows(rows: readonly any[]): any[] {\r\n let result = [...rows];\r\n for (const plugin of this.plugins) {\r\n if (plugin.processRows) {\r\n result = plugin.processRows(result);\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute processColumns hook on all plugins.\r\n */\r\n processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\r\n let result = [...columns];\r\n for (const plugin of this.plugins) {\r\n if (plugin.processColumns) {\r\n result = plugin.processColumns(result);\r\n }\r\n }\r\n return result;\r\n }\r\n\r\n /**\r\n * Execute beforeRender hook on all plugins.\r\n */\r\n beforeRender(): void {\r\n for (const plugin of this.plugins) {\r\n plugin.beforeRender?.();\r\n }\r\n }\r\n\r\n /**\r\n * Execute afterRender hook on all plugins.\r\n */\r\n afterRender(): void {\r\n for (const plugin of this.plugins) {\r\n plugin.afterRender?.();\r\n }\r\n }\r\n\r\n /**\r\n * Execute renderRow hook on all plugins.\r\n * Returns true if any plugin handled the row.\r\n */\r\n renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.renderRow?.(row, rowEl, rowIndex)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Execute onKeyDown hook on all plugins.\r\n * Returns true if any plugin handled the event.\r\n */\r\n onKeyDown(event: KeyboardEvent): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.onKeyDown?.(event)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Execute onCellClick hook on all plugins.\r\n * Returns true if any plugin handled the event.\r\n */\r\n onCellClick(event: CellClickEvent): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.onCellClick?.(event)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Execute onRowClick hook on all plugins.\r\n * Returns true if any plugin handled the event.\r\n */\r\n onRowClick(event: RowClickEvent): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.onRowClick?.(event)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Execute onHeaderClick hook on all plugins.\r\n * Returns true if any plugin handled the event.\r\n */\r\n onHeaderClick(event: HeaderClickEvent): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.onHeaderClick?.(event)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Execute onScroll hook on all plugins.\r\n */\r\n onScroll(event: ScrollEvent): void {\r\n for (const plugin of this.plugins) {\r\n plugin.onScroll?.(event);\r\n }\r\n }\r\n\r\n /**\r\n * Execute onCellMouseDown hook on all plugins.\r\n * Returns true if any plugin handled the event.\r\n */\r\n onCellMouseDown(event: CellMouseEvent): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.onCellMouseDown?.(event)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Execute onCellMouseMove hook on all plugins.\r\n * Returns true if any plugin handled the event.\r\n */\r\n onCellMouseMove(event: CellMouseEvent): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.onCellMouseMove?.(event)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Execute onCellMouseUp hook on all plugins.\r\n * Returns true if any plugin handled the event.\r\n */\r\n onCellMouseUp(event: CellMouseEvent): boolean {\r\n for (const plugin of this.plugins) {\r\n if (plugin.onCellMouseUp?.(event)) {\r\n return true;\r\n }\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Collect context menu items from all plugins.\r\n */\r\n getContextMenuItems(params: ContextMenuParams): ContextMenuItem[] {\r\n const items: ContextMenuItem[] = [];\r\n for (const plugin of this.plugins) {\r\n const pluginItems = plugin.getContextMenuItems?.(params);\r\n if (pluginItems) {\r\n items.push(...pluginItems);\r\n }\r\n }\r\n return items;\r\n }\r\n\r\n // ===== Shell Integration Hooks =====\r\n\r\n /**\r\n * Collect tool panels from all plugins.\r\n * Returns panels sorted by order (ascending).\r\n */\r\n getToolPanels(): {\r\n plugin: BaseGridPlugin;\r\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\r\n }[] {\r\n const panels: {\r\n plugin: BaseGridPlugin;\r\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\r\n }[] = [];\r\n for (const plugin of this.plugins) {\r\n const panel = plugin.getToolPanel?.();\r\n if (panel) {\r\n panels.push({ plugin, panel });\r\n }\r\n }\r\n // Sort by order (ascending), default to 0\r\n return panels.sort((a, b) => (a.panel.order ?? 0) - (b.panel.order ?? 0));\r\n }\r\n\r\n /**\r\n * Collect header contents from all plugins.\r\n * Returns contents sorted by order (ascending).\r\n */\r\n getHeaderContents(): {\r\n plugin: BaseGridPlugin;\r\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\r\n }[] {\r\n const contents: {\r\n plugin: BaseGridPlugin;\r\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\r\n }[] = [];\r\n for (const plugin of this.plugins) {\r\n const content = plugin.getHeaderContent?.();\r\n if (content) {\r\n contents.push({ plugin, content });\r\n }\r\n }\r\n // Sort by order (ascending), default to 0\r\n return contents.sort((a, b) => (a.content.order ?? 0) - (b.content.order ?? 0));\r\n }\r\n}\r\n","import styles from './grid.css?inline';\r\nimport { applyColumnState, collectColumnState, createStateChangeHandler } from './internal/column-state';\r\nimport { autoSizeColumns, getColumnConfiguration, updateTemplate } from './internal/columns';\r\nimport { exitRowEdit, startRowEdit } from './internal/editing';\r\nimport { renderHeader } from './internal/header';\r\nimport { inferColumns } from './internal/inference';\r\nimport { handleGridKeyDown } from './internal/keyboard';\r\nimport { createResizeController } from './internal/resize';\r\nimport { invalidateCellCache, renderVisibleRows } from './internal/rows';\r\nimport {\r\n cleanupShellState,\r\n createShellState,\r\n getToolbarButtonsInfo,\r\n parseLightDomShell,\r\n renderCustomToolbarButtons,\r\n renderHeaderContent,\r\n renderPanelContent,\r\n renderShellBody,\r\n renderShellHeader,\r\n setupShellEventListeners,\r\n shouldRenderShellHeader,\r\n updatePanelState,\r\n updateToolbarActiveStates,\r\n type ShellState,\r\n} from './internal/shell';\r\nimport type { CellMouseEvent, ScrollEvent } from './plugin';\r\nimport type { BaseGridPlugin, CellClickEvent, HeaderClickEvent } from './plugin/base-plugin';\r\nimport { PluginManager } from './plugin/plugin-manager';\r\nimport type {\r\n ActivateCellDetail,\r\n CellCommitDetail,\r\n ColumnConfig,\r\n ColumnConfigMap,\r\n ColumnInternal,\r\n ColumnResizeDetail,\r\n FitMode,\r\n GridColumnState,\r\n GridConfig,\r\n HeaderContentDefinition,\r\n InternalGrid,\r\n ResizeController,\r\n RowCommitDetail,\r\n SortChangeDetail,\r\n ToolbarButtonConfig,\r\n ToolbarButtonInfo,\r\n ToolPanelDefinition,\r\n VirtualState,\r\n} from './types';\r\n\r\n/**\r\n * High-performance data grid web component.\r\n * During migration, uses tbw-grid tag to avoid conflicts with existing datagrid.\r\n * Will be renamed back to data-grid when migration is complete.\r\n *\r\n * ## Configuration Architecture\r\n *\r\n * The grid follows a **single source of truth** pattern where all configuration\r\n * converges into `#effectiveConfig`. Users can set configuration via multiple inputs:\r\n *\r\n * **Input Sources (precedence low → high):**\r\n * 1. `gridConfig` property - base configuration object\r\n * 2. Light DOM elements:\r\n * - `<tbw-grid-column>` → `effectiveConfig.columns`\r\n * - `<tbw-grid-header title=\"...\">` → `effectiveConfig.shell.header.title`\r\n * - `<tbw-grid-header-content>` → `effectiveConfig.shell.header.content`\r\n * 3. `columns` property → merged into `effectiveConfig.columns`\r\n * 4. `fitMode` property → merged into `effectiveConfig.fitMode`\r\n * 5. `editOn` property → merged into `effectiveConfig.editOn`\r\n * 6. Column inference from first row (if no columns defined)\r\n *\r\n * **Derived State:**\r\n * - `_columns` - processed columns from `effectiveConfig.columns` after plugin hooks\r\n * - `_rows` - processed rows after plugin hooks (grouping, filtering, etc.)\r\n *\r\n * The `#mergeEffectiveConfig()` method is the single place where all inputs converge.\r\n * All rendering and logic should read from `effectiveConfig` or derived state.\r\n *\r\n * @element tbw-grid\r\n *\r\n * @csspart container - The main grid container\r\n * @csspart header - The header row container\r\n * @csspart body - The body/rows container\r\n *\r\n * @fires cell-commit - Fired when a cell value is committed\r\n * @fires row-commit - Fired when a bulk row edit session commits\r\n * @fires changed-rows-reset - Fired after resetChangedRows() unless silent\r\n * @fires mount-external-view - Fired to request mounting of an external view renderer\r\n * @fires mount-external-editor - Fired to request mounting of an external editor renderer\r\n * @fires sort-change - Fired when sort state changes for a column\r\n * @fires column-resize - Fired after a column resize drag completes\r\n * @fires activate-cell - Fired when a cell activation intent occurs\r\n * @fires group-toggle - Fired when a group row is toggled\r\n *\r\n * @cssprop --tbw-color-bg - Background color\r\n * @cssprop --tbw-color-fg - Foreground/text color\r\n */\r\nexport class DataGridElement<T = any> extends HTMLElement implements InternalGrid<T> {\r\n // TODO: Rename to 'data-grid' when migration is complete\r\n static readonly tagName = 'tbw-grid';\r\n\r\n readonly #shadow: ShadowRoot;\r\n #initialized = false;\r\n\r\n // ---------------- Ready Promise ----------------\r\n #readyPromise: Promise<void>;\r\n #readyResolve?: () => void;\r\n\r\n // ================== INPUT PROPERTIES ==================\r\n // These backing fields store raw user input. They are merged into\r\n // #effectiveConfig by #mergeEffectiveConfig(). Never read directly\r\n // for rendering logic - always use effectiveConfig or derived state.\r\n #rows: T[] = [];\r\n #columns?: ColumnConfig<T>[] | ColumnConfigMap<T>;\r\n #gridConfig?: GridConfig<T>;\r\n #fitMode?: FitMode;\r\n #editOn?: string;\r\n\r\n // ================== SINGLE SOURCE OF TRUTH ==================\r\n // All input sources converge here. This is the canonical config\r\n // that all rendering and logic should read from.\r\n #effectiveConfig: GridConfig<T> = {};\r\n #connected = false;\r\n #scrollRaf = 0;\r\n #pendingScrollTop: number | null = null;\r\n #hasScrollPlugins = false; // Cached flag for plugin scroll handlers\r\n #renderRowHook?: (row: any, rowEl: HTMLElement, rowIndex: number) => boolean; // Cached hook to avoid closures\r\n #boundEsc?: (e: KeyboardEvent) => void;\r\n #boundOutside?: (e: MouseEvent) => void;\r\n #isDragging = false;\r\n #boundMouseMove?: (e: MouseEvent) => void;\r\n #boundMouseUp?: (e: MouseEvent) => void;\r\n\r\n // ---------------- Plugin System ----------------\r\n #pluginManager!: PluginManager;\r\n\r\n // ---------------- Column State ----------------\r\n #stateChangeHandler?: () => void;\r\n #initialColumnState?: GridColumnState;\r\n\r\n // ---------------- Shell State ----------------\r\n #shellState: ShellState = createShellState();\r\n #shellInitialized = false;\r\n\r\n // ================== DERIVED STATE ==================\r\n // _rows: result of applying plugin processRows hooks\r\n _rows: T[] = [];\r\n\r\n // _columns is a getter/setter that operates on effectiveConfig.columns\r\n // This ensures effectiveConfig.columns is the single source of truth for columns\r\n // _columns always contains ALL columns (including hidden)\r\n get _columns(): ColumnInternal<T>[] {\r\n return (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\r\n }\r\n set _columns(value: ColumnInternal<T>[]) {\r\n this.#effectiveConfig.columns = value as ColumnConfig<T>[];\r\n }\r\n\r\n // visibleColumns returns only visible columns for rendering\r\n // This is what header/row rendering should use\r\n get visibleColumns(): ColumnInternal<T>[] {\r\n return this._columns.filter((c) => !c.hidden);\r\n }\r\n\r\n // ================== RUNTIME STATE ==================\r\n // User-driven state changes at runtime (sort, etc.)\r\n // Visibility is stored in effectiveConfig.columns[].hidden\r\n rowPool: HTMLElement[] = [];\r\n __rowRenderEpoch = 0;\r\n activeEditRows = -1;\r\n resizeController!: ResizeController;\r\n __didInitialAutoSize = false;\r\n __lightDomColumnsCache?: ColumnInternal[];\r\n __originalColumnNodes?: HTMLElement[];\r\n headerRowEl!: HTMLElement;\r\n bodyEl!: HTMLElement;\r\n virtualization: VirtualState = {\r\n enabled: true,\r\n rowHeight: 28, // Initial state - will recalculate after first render\r\n bypassThreshold: 24, // Skip virtualization if <= this many rows (saves overhead)\r\n start: 0,\r\n end: 0,\r\n container: null, // Faux scrollbar element\r\n viewportEl: null, // Rows viewport for measuring visible height\r\n totalHeightEl: null, // Spacer for virtual height\r\n };\r\n sortState: { field: string; direction: 1 | -1 } | null = null;\r\n __originalOrder: T[] = [];\r\n focusRow = 0;\r\n focusCol = 0;\r\n gridTemplate = '';\r\n rowEditSnapshots = new Map<number, T>();\r\n _changedRowIndices = new Set<number>();\r\n\r\n // ---------------- Public API Props (getters/setters) ----------------\r\n // Getters return the EFFECTIVE value (after merging), not the raw input.\r\n // This is what consumers and plugins need - the current resolved state.\r\n // Setters update input properties which trigger re-merge into effectiveConfig.\r\n\r\n get rows(): T[] {\r\n return this._rows;\r\n }\r\n set rows(value: T[]) {\r\n const oldValue = this.#rows;\r\n this.#rows = value;\r\n if (oldValue !== value) {\r\n this.#onRowsChanged();\r\n }\r\n }\r\n\r\n /**\r\n * Get the original unfiltered/unprocessed rows.\r\n * Use this when you need access to all source data regardless of active filters.\r\n */\r\n get sourceRows(): T[] {\r\n return this.#rows;\r\n }\r\n\r\n get columns(): ColumnConfig<T>[] {\r\n return [...this._columns] as ColumnConfig<T>[];\r\n }\r\n set columns(value: ColumnConfig<T>[] | ColumnConfigMap<T> | undefined) {\r\n const oldValue = this.#columns;\r\n this.#columns = value;\r\n if (oldValue !== value) {\r\n this.#onColsChanged();\r\n }\r\n }\r\n\r\n get gridConfig(): GridConfig<T> {\r\n return this.#effectiveConfig;\r\n }\r\n set gridConfig(value: GridConfig<T> | undefined) {\r\n const oldValue = this.#gridConfig;\r\n this.#gridConfig = value;\r\n if (oldValue !== value) {\r\n this.#onGridConfigChanged();\r\n }\r\n }\r\n\r\n get fitMode(): FitMode {\r\n return this.#effectiveConfig.fitMode ?? 'stretch';\r\n }\r\n set fitMode(value: FitMode | undefined) {\r\n const oldValue = this.#fitMode;\r\n this.#fitMode = value;\r\n if (oldValue !== value) {\r\n this.#onFitChanged();\r\n }\r\n }\r\n\r\n get editOn(): string | undefined {\r\n return this.#effectiveConfig.editOn;\r\n }\r\n set editOn(value: string | undefined) {\r\n const oldValue = this.#editOn;\r\n this.#editOn = value;\r\n if (oldValue !== value) {\r\n this.#onEditModeChanged();\r\n }\r\n }\r\n\r\n // Effective config accessor for internal modules and plugins\r\n // Returns the merged config (single source of truth) before plugin processing\r\n // Use this when you need the raw merged config (e.g., for column definitions including hidden)\r\n get effectiveConfig(): GridConfig<T> {\r\n return this.#effectiveConfig;\r\n }\r\n\r\n constructor() {\r\n super();\r\n this.#shadow = this.attachShadow({ mode: 'open' });\r\n this.#injectStyles();\r\n this.#readyPromise = new Promise((res) => (this.#readyResolve = res));\r\n }\r\n\r\n #injectStyles(): void {\r\n const sheet = new CSSStyleSheet();\r\n sheet.replaceSync(styles);\r\n this.#shadow.adoptedStyleSheets = [sheet];\r\n }\r\n\r\n // ---------------- Plugin System ----------------\r\n\r\n /**\r\n * Get a plugin instance by its class.\r\n * Used by plugins for inter-plugin communication.\r\n */\r\n getPlugin<P extends BaseGridPlugin>(PluginClass: new (...args: any[]) => P): P | undefined {\r\n return this.#pluginManager?.getPlugin(PluginClass);\r\n }\r\n\r\n /**\r\n * Get a plugin instance by its name.\r\n * Used for loose coupling between plugins (avoids static imports).\r\n */\r\n getPluginByName(name: string): BaseGridPlugin | undefined {\r\n return this.#pluginManager?.getPluginByName(name);\r\n }\r\n\r\n /**\r\n * Request a full re-render of the grid.\r\n * Called by plugins when they need the grid to update.\r\n * Note: This does NOT reset plugin state - just re-processes rows/columns and renders.\r\n */\r\n requestRender(): void {\r\n this.#rebuildRowModel();\r\n this.#processColumns();\r\n this.#renderHeader();\r\n this.updateTemplate();\r\n this.refreshVirtualWindow(true);\r\n }\r\n\r\n /**\r\n * Request a lightweight style update without rebuilding DOM.\r\n * Called by plugins when they only need to update CSS classes/styles.\r\n * This runs all plugin afterRender hooks without rebuilding row/column DOM.\r\n */\r\n requestAfterRender(): void {\r\n this.#executeAfterRender();\r\n }\r\n\r\n /**\r\n * Initialize plugin system with instances from config.\r\n * Plugins are class instances passed in gridConfig.plugins[].\r\n */\r\n #initializePlugins(): void {\r\n // Create plugin manager for this grid\r\n this.#pluginManager = new PluginManager(this);\r\n\r\n // Get plugin instances from config - ensure it's an array\r\n const pluginsConfig = this.#effectiveConfig?.plugins;\r\n const plugins = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\r\n\r\n // Attach all plugins\r\n this.#pluginManager.attachAll(plugins);\r\n }\r\n\r\n /**\r\n * Inject all plugin styles into the shadow DOM.\r\n * Must be called after #render() since innerHTML wipes existing content.\r\n */\r\n #injectAllPluginStyles(): void {\r\n const allStyles = this.#pluginManager?.getAllStyles() ?? '';\r\n if (allStyles) {\r\n const styleEl = document.createElement('style');\r\n styleEl.setAttribute('data-plugin', 'all');\r\n styleEl.textContent = allStyles;\r\n this.#shadow.appendChild(styleEl);\r\n }\r\n }\r\n\r\n /**\r\n * Update plugins when grid config changes.\r\n * With class-based plugins, we need to detach old and attach new.\r\n */\r\n #updatePluginConfigs(): void {\r\n // With class-based plugins, config changes require re-initialization\r\n // The new plugins are in the new config - detach old, attach new\r\n if (this.#pluginManager) {\r\n this.#pluginManager.detachAll();\r\n }\r\n this.#initializePlugins();\r\n this.#injectAllPluginStyles();\r\n // Update cached scroll plugin flag\r\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\r\n }\r\n\r\n /**\r\n * Clean up plugin states when grid disconnects.\r\n */\r\n #destroyPlugins(): void {\r\n this.#pluginManager?.detachAll();\r\n }\r\n\r\n /**\r\n * Collect tool panels and header content from all plugins.\r\n * Called after plugins are attached but before render.\r\n */\r\n #collectPluginShellContributions(): void {\r\n if (!this.#pluginManager) return;\r\n\r\n // Collect tool panels from plugins\r\n const pluginPanels = this.#pluginManager.getToolPanels();\r\n for (const { panel } of pluginPanels) {\r\n // Skip if already registered (light DOM or API takes precedence)\r\n if (!this.#shellState.toolPanels.has(panel.id)) {\r\n this.#shellState.toolPanels.set(panel.id, panel);\r\n }\r\n }\r\n\r\n // Collect header contents from plugins\r\n const pluginContents = this.#pluginManager.getHeaderContents();\r\n for (const { content } of pluginContents) {\r\n // Skip if already registered (light DOM or API takes precedence)\r\n if (!this.#shellState.headerContents.has(content.id)) {\r\n this.#shellState.headerContents.set(content.id, content);\r\n }\r\n }\r\n }\r\n\r\n // ---------------- Lifecycle ----------------\r\n connectedCallback(): void {\r\n if (!this.hasAttribute('tabindex')) (this as any).tabIndex = 0;\r\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\r\n\r\n // Merge all config sources into effectiveConfig (including columns)\r\n this.#mergeEffectiveConfig();\r\n\r\n // Initialize plugin system\r\n this.#initializePlugins();\r\n\r\n // Collect tool panels and header content from plugins (must be before render)\r\n this.#collectPluginShellContributions();\r\n\r\n if (!this.#initialized) {\r\n this.#render();\r\n this.#injectAllPluginStyles(); // Inject plugin styles after render\r\n this.#initialized = true;\r\n }\r\n this.#afterConnect();\r\n }\r\n\r\n disconnectedCallback(): void {\r\n // Clean up plugin states\r\n this.#destroyPlugins();\r\n\r\n // Clean up shell state\r\n cleanupShellState(this.#shellState);\r\n this.#shellInitialized = false;\r\n\r\n if (this.#boundEsc) {\r\n document.removeEventListener('keydown', this.#boundEsc, true);\r\n this.#boundEsc = undefined;\r\n }\r\n if (this.#boundOutside) {\r\n document.removeEventListener('mousedown', this.#boundOutside, false);\r\n this.#boundOutside = undefined;\r\n }\r\n if (this.#boundMouseMove) {\r\n document.removeEventListener('mousemove', this.#boundMouseMove);\r\n this.#boundMouseMove = undefined;\r\n }\r\n if (this.#boundMouseUp) {\r\n document.removeEventListener('mouseup', this.#boundMouseUp);\r\n this.#boundMouseUp = undefined;\r\n }\r\n if (this.resizeController) {\r\n this.resizeController.dispose();\r\n }\r\n this.#connected = false;\r\n }\r\n\r\n #afterConnect(): void {\r\n // Shell changes the DOM structure - need to find elements appropriately\r\n const gridContent = this.#shadow.querySelector('.tbw-grid-content');\r\n const gridRoot = gridContent ?? this.#shadow.querySelector('.tbw-grid-root');\r\n\r\n this.headerRowEl = gridRoot?.querySelector('.header-row') as HTMLElement;\r\n // Faux scrollbar pattern:\r\n // - .faux-vscroll-spacer sets virtual height\r\n // - .rows-viewport provides visible height for virtualization calculations\r\n this.virtualization.totalHeightEl = gridRoot?.querySelector('.faux-vscroll-spacer') as HTMLElement;\r\n this.virtualization.viewportEl = gridRoot?.querySelector('.rows-viewport') as HTMLElement;\r\n this.bodyEl = gridRoot?.querySelector('.rows') as HTMLElement;\r\n\r\n // Initialize shell header content and custom buttons if shell is active\r\n if (this.#shellInitialized) {\r\n // Render plugin header content\r\n renderHeaderContent(this.#shadow, this.#shellState);\r\n // Render custom toolbar buttons (element/render modes)\r\n renderCustomToolbarButtons(this.#shadow, this.#effectiveConfig?.shell, this.#shellState);\r\n // Open default panel if configured\r\n const defaultOpen = this.#effectiveConfig?.shell?.toolPanel?.defaultOpen;\r\n if (defaultOpen && this.#shellState.toolPanels.has(defaultOpen)) {\r\n this.openToolPanel(defaultOpen);\r\n }\r\n }\r\n\r\n // Mark for tests that afterConnect ran\r\n this.setAttribute('data-upgraded', '');\r\n if (!this.hasAttribute('role')) this.setAttribute('role', 'grid');\r\n this.#connected = true;\r\n\r\n // Run setup\r\n this.#setup();\r\n\r\n this.addEventListener('keydown', (e) => handleGridKeyDown(this as any, e));\r\n\r\n // Global listeners\r\n if (!this.#boundEsc) {\r\n this.#boundEsc = (e: KeyboardEvent) => {\r\n if (e.key === 'Escape' && this.activeEditRows !== -1) {\r\n this.#exitRowEdit(this.activeEditRows, true);\r\n }\r\n };\r\n document.addEventListener('keydown', this.#boundEsc, true);\r\n }\r\n\r\n if (!this.#boundOutside) {\r\n this.#boundOutside = (e: MouseEvent) => {\r\n if (this.activeEditRows === -1) return;\r\n const rowEl = this.findRenderedRowElement(this.activeEditRows);\r\n if (!rowEl) return;\r\n const path = (e.composedPath && e.composedPath()) || [];\r\n if (path.includes(rowEl)) return;\r\n this.#exitRowEdit(this.activeEditRows, false);\r\n };\r\n document.addEventListener('mousedown', this.#boundOutside, false);\r\n }\r\n\r\n // Faux scrollbar pattern: scroll events come from the fake scrollbar element\r\n // Content area doesn't scroll - rows are positioned via transforms\r\n // This prevents blank viewport: old content stays until transforms are updated\r\n // Reuse gridRoot from earlier in this function\r\n const fauxScrollbar = gridRoot?.querySelector('.faux-vscroll') as HTMLElement;\r\n const rowsEl = gridRoot?.querySelector('.rows') as HTMLElement;\r\n\r\n // Store reference for scroll position reading in refreshVirtualWindow\r\n this.virtualization.container = fauxScrollbar ?? this;\r\n\r\n // Cache whether any plugin has scroll handlers (checked once during setup)\r\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\r\n\r\n if (fauxScrollbar && rowsEl) {\r\n fauxScrollbar.addEventListener(\r\n 'scroll',\r\n () => {\r\n // Fast exit if no scroll processing needed\r\n if (!this.virtualization.enabled && !this.#hasScrollPlugins) return;\r\n\r\n const currentScrollTop = fauxScrollbar.scrollTop;\r\n const rowHeight = this.virtualization.rowHeight;\r\n\r\n // Smooth scroll: apply sub-pixel offset immediately\r\n // This creates fluid motion as user scrolls, before RAF updates content\r\n // The offset is the remainder after dividing by rowHeight\r\n const subPixelOffset = -(currentScrollTop % rowHeight);\r\n rowsEl.style.transform = `translateY(${subPixelOffset}px)`;\r\n\r\n // Batch content update with requestAnimationFrame\r\n // Old content stays visible with smooth offset until new content renders\r\n this.#pendingScrollTop = currentScrollTop;\r\n if (!this.#scrollRaf) {\r\n this.#scrollRaf = requestAnimationFrame(() => {\r\n this.#scrollRaf = 0;\r\n if (this.#pendingScrollTop !== null) {\r\n this.#onScrollBatched(this.#pendingScrollTop);\r\n this.#pendingScrollTop = null;\r\n }\r\n });\r\n }\r\n },\r\n { passive: true }\r\n );\r\n\r\n // Forward wheel events from content area to faux scrollbar\r\n // Without this, mouse wheel over content wouldn't scroll\r\n const rowsBody = gridRoot?.querySelector('.rows-body') as HTMLElement;\r\n if (rowsBody) {\r\n rowsBody.addEventListener(\r\n 'wheel',\r\n (e: WheelEvent) => {\r\n // Prevent default to stop any residual scroll behavior\r\n e.preventDefault();\r\n // Apply wheel delta to faux scrollbar\r\n fauxScrollbar.scrollTop += e.deltaY;\r\n },\r\n { passive: false }\r\n );\r\n }\r\n }\r\n\r\n this.resizeController = createResizeController(this as any);\r\n\r\n // Central mouse event handling for plugins\r\n this.#shadow.addEventListener('mousedown', (e) => this.#handleMouseDown(e as MouseEvent));\r\n\r\n // Track global mousemove/mouseup for drag operations\r\n if (!this.#boundMouseMove) {\r\n this.#boundMouseMove = (e: MouseEvent) => this.#handleMouseMove(e);\r\n document.addEventListener('mousemove', this.#boundMouseMove);\r\n }\r\n if (!this.#boundMouseUp) {\r\n this.#boundMouseUp = (e: MouseEvent) => this.#handleMouseUp(e);\r\n document.addEventListener('mouseup', this.#boundMouseUp);\r\n }\r\n\r\n if (this.virtualization.enabled) {\r\n requestAnimationFrame(() => this.refreshVirtualWindow(true));\r\n }\r\n\r\n // Measure actual row height after first paint for accurate spacer sizing\r\n requestAnimationFrame(() => {\r\n const firstRow = this.bodyEl.querySelector('.data-grid-row');\r\n if (firstRow) {\r\n const h = (firstRow as HTMLElement).getBoundingClientRect().height;\r\n if (h && Math.abs(h - this.virtualization.rowHeight) > 0.1) {\r\n this.virtualization.rowHeight = h;\r\n this.refreshVirtualWindow(true);\r\n }\r\n }\r\n });\r\n\r\n // Initialize ARIA selection state\r\n queueMicrotask(() => this.#updateAriaSelection());\r\n\r\n requestAnimationFrame(() => requestAnimationFrame(() => this.#readyResolve?.()));\r\n }\r\n\r\n // ---------------- Event Emitters ----------------\r\n #emit<D>(eventName: string, detail: D): void {\r\n this.dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true, composed: true }));\r\n }\r\n\r\n emitCellCommit(detail: CellCommitDetail<T>): void {\r\n this.#emit('cell-commit', detail);\r\n }\r\n\r\n emitRowCommit(detail: RowCommitDetail<T>): void {\r\n this.#emit('row-commit', detail);\r\n }\r\n\r\n emitSortChange(detail: SortChangeDetail): void {\r\n this.#emit('sort-change', detail);\r\n }\r\n\r\n emitColumnResize(detail: ColumnResizeDetail): void {\r\n this.#emit('column-resize', detail);\r\n }\r\n\r\n emitActivateCell(detail: ActivateCellDetail): void {\r\n this.#emit('activate-cell', detail);\r\n }\r\n\r\n /** Update ARIA selection attributes on rendered rows/cells */\r\n #updateAriaSelection(): void {\r\n // Mark active row and cell with aria-selected\r\n const rows = this.bodyEl?.querySelectorAll('.data-grid-row');\r\n rows?.forEach((row, rowIdx) => {\r\n const isActiveRow = rowIdx === this.focusRow;\r\n row.setAttribute('aria-selected', String(isActiveRow));\r\n row.querySelectorAll('.cell').forEach((cell, colIdx) => {\r\n (cell as HTMLElement).setAttribute('aria-selected', String(isActiveRow && colIdx === this.focusCol));\r\n });\r\n });\r\n }\r\n\r\n // ---------------- Watch Handlers ----------------\r\n #onFitChanged(): void {\r\n if (!this.#connected) return;\r\n this.#mergeEffectiveConfig();\r\n const mode = this.#effectiveConfig.fitMode;\r\n if (mode === 'fixed') {\r\n this.__didInitialAutoSize = false;\r\n this.#autoSizeColumns();\r\n } else {\r\n this._columns.forEach((c: any) => {\r\n if (!c.__userResized && c.__autoSized) delete c.width;\r\n });\r\n this.updateTemplate();\r\n }\r\n }\r\n\r\n #onEditModeChanged(): void {\r\n if (!this.#connected) return;\r\n this.#mergeEffectiveConfig();\r\n this.rowPool.length = 0;\r\n if (this.bodyEl) this.bodyEl.innerHTML = '';\r\n this.__rowRenderEpoch++;\r\n this.refreshVirtualWindow(true);\r\n }\r\n\r\n #onRowsChanged(): void {\r\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\r\n this.#rebuildRowModel();\r\n // If no explicit columns provided, trigger full setup so inference runs\r\n if (!this.#columns || (Array.isArray(this.#columns) && this.#columns.length === 0)) {\r\n this.#setup();\r\n } else {\r\n this.refreshVirtualWindow(true);\r\n }\r\n }\r\n\r\n #onColsChanged(): void {\r\n // Invalidate caches that depend on column configuration\r\n invalidateCellCache(this as unknown as any);\r\n\r\n // Re-merge config and setup - _columns will be set through effectiveConfig\r\n if (this.#connected) {\r\n this.#mergeEffectiveConfig();\r\n this.#setup();\r\n }\r\n }\r\n\r\n #onGridConfigChanged(): void {\r\n if (!this.#connected) return;\r\n this.#mergeEffectiveConfig();\r\n this.#updatePluginConfigs(); // Sync plugin configs with new grid config\r\n this.#rebuildRowModel();\r\n this.#processColumns(); // Process columns after rows for tree plugin\r\n this.#renderHeader();\r\n this.updateTemplate();\r\n this.refreshVirtualWindow(true);\r\n }\r\n\r\n // ---------------- Helper Wrappers ----------------\r\n #getColumnConfiguration(): void {\r\n getColumnConfiguration(this as unknown as any);\r\n }\r\n\r\n #renderHeader(): void {\r\n renderHeader(this as unknown as any);\r\n // Grouped header cell classes are applied by plugins via afterRender\r\n }\r\n\r\n updateTemplate(): void {\r\n updateTemplate(this as unknown as any);\r\n }\r\n\r\n #autoSizeColumns(): void {\r\n autoSizeColumns(this as unknown as any);\r\n }\r\n\r\n #processColumns(): void {\r\n // Let plugins process visible columns (column grouping, etc.)\r\n // Note: _columns always contains ALL columns; plugins work on visible subset\r\n if (this.#pluginManager) {\r\n // Get visible columns for plugin processing\r\n const visibleCols = this._columns.filter((c) => !c.hidden);\r\n const processedColumns = this.#pluginManager.processColumns([...visibleCols] as any[]);\r\n\r\n // If plugins modified visible columns, update them in place preserving hidden column positions\r\n if (processedColumns !== visibleCols) {\r\n // Build a map of processed columns by field for quick lookup\r\n const processedMap = new Map(processedColumns.map((c: any, i: number) => [c.field, { col: c, order: i }]));\r\n\r\n // Update visible columns in _columns with their processed versions\r\n // Hidden columns keep their original position\r\n const updatedColumns = this._columns.map((c) => {\r\n if (c.hidden) return c; // Keep hidden columns unchanged\r\n const processed = processedMap.get(c.field);\r\n return processed ? processed.col : c;\r\n });\r\n\r\n this._columns = updatedColumns as ColumnInternal<T>[];\r\n }\r\n }\r\n }\r\n\r\n /** Execute all plugin afterRender hooks */\r\n #executeAfterRender(): void {\r\n this.#pluginManager?.afterRender();\r\n }\r\n\r\n /** Recompute row model via plugin hooks (grouping, tree, filtering, etc.). */\r\n #rebuildRowModel(): void {\r\n // Invalidate cell display value cache - rows are changing\r\n invalidateCellCache(this as unknown as any);\r\n\r\n // Start fresh from original rows (plugins will transform them)\r\n const originalRows = Array.isArray(this.#rows) ? [...this.#rows] : [];\r\n\r\n // Let plugins process rows (row grouping, tree, filtering, etc.)\r\n // Plugins can transform the rows array, adding markers like __isGroupRow\r\n // The renderRow hook will handle rendering specialized row types\r\n const processedRows = this.#pluginManager?.processRows(originalRows) ?? originalRows;\r\n\r\n // Store processed rows for rendering\r\n // Note: processedRows may contain group markers that plugins handle via renderRow hook\r\n this._rows = processedRows as T[];\r\n }\r\n\r\n /**\r\n * Build the canonical effective configuration by merging all input sources.\r\n *\r\n * This is the **single source of truth** for the grid's configuration.\r\n * All inputs (gridConfig, light DOM, individual props) converge here.\r\n *\r\n * **Precedence (lowest → highest):**\r\n * 1. `gridConfig` property - base config object\r\n * 2. Light DOM `<tbw-grid-column>` elements - declarative columns\r\n * 3. `columns` property - programmatic columns override\r\n * 4. Inferred columns - auto-detected from row data\r\n * 5. Individual props (`fitMode`, `editOn`) - convenience overrides\r\n *\r\n * After this method runs:\r\n * - `#effectiveConfig` contains the merged result\r\n * - `_columns` is NOT set here (done by #getColumnConfiguration + #processColumns)\r\n * - Plugins receive config via their attach() method\r\n */\r\n #mergeEffectiveConfig(): void {\r\n const base: GridConfig<T> = this.#gridConfig ? { ...this.#gridConfig } : {};\r\n let columns: ColumnConfig<T>[] = Array.isArray(base.columns) ? [...base.columns] : [];\r\n\r\n // Light DOM cached parse (if already parsed by columns pipeline); non-invasive merge (fill gaps only)\r\n const domCols: ColumnConfig<T>[] = ((this as any).__lightDomColumnsCache || []).map((c: ColumnConfig<T>) => ({\r\n ...c,\r\n }));\r\n if (domCols.length) {\r\n const map: Record<string, ColumnConfig<T>> = {};\r\n columns.forEach((c) => (map[(c as any).field] = c));\r\n domCols.forEach((c: any) => {\r\n const exist = map[c.field];\r\n if (!exist) {\r\n columns.push(c);\r\n map[c.field] = c;\r\n } else {\r\n if (c.header && !exist.header) exist.header = c.header;\r\n if (c.type && !exist.type) exist.type = c.type;\r\n exist.sortable = exist.sortable || c.sortable;\r\n if (c.resizable) exist.resizable = true;\r\n if (c.editable) exist.editable = true;\r\n }\r\n });\r\n }\r\n\r\n // Columns prop highest structural precedence\r\n if (this.#columns && (this.#columns as ColumnConfig<T>[]).length) {\r\n columns = [...(this.#columns as ColumnConfig<T>[])];\r\n }\r\n\r\n // Inference if still empty\r\n if ((!columns || columns.length === 0) && this._rows.length) {\r\n const result = inferColumns(this._rows as Record<string, unknown>[]);\r\n columns = result.columns as ColumnConfig<T>[];\r\n }\r\n\r\n if (columns.length) {\r\n // Apply per-column defaults (sortable/resizable default true unless explicitly false)\r\n columns.forEach((c) => {\r\n if (c.sortable === undefined) c.sortable = true;\r\n if (c.resizable === undefined) c.resizable = true;\r\n });\r\n // Preserve processed columns (with __compiledView etc.) if already set by #getColumnConfiguration\r\n // Only set base.columns if effectiveConfig.columns is empty or doesn't have compiled templates\r\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\r\n const alreadyProcessed = existingCols?.some((c) => c.__compiledView || c.__compiledEditor);\r\n if (alreadyProcessed) {\r\n // Keep existing processed columns\r\n base.columns = existingCols as ColumnConfig<T>[];\r\n } else {\r\n base.columns = columns;\r\n }\r\n } else {\r\n // No new columns computed, but preserve existing if processed\r\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\r\n if (existingCols?.some((c) => c.__compiledView || c.__compiledEditor)) {\r\n base.columns = existingCols as ColumnConfig<T>[];\r\n }\r\n }\r\n\r\n // Individual prop overrides (behavioral)\r\n if (this.#fitMode) base.fitMode = this.#fitMode;\r\n if (!base.fitMode) base.fitMode = 'stretch';\r\n if (this.#editOn) base.editOn = this.#editOn;\r\n\r\n // Store columnState from gridConfig if not already set\r\n if (base.columnState && !this.#initialColumnState) {\r\n this.#initialColumnState = base.columnState;\r\n }\r\n\r\n this.#effectiveConfig = base;\r\n // Note: _columns is a getter/setter for effectiveConfig.columns\r\n // #getColumnConfiguration() populates it, and we preserve those processed columns above\r\n // Plugins (like ReorderPlugin) modify effectiveConfig.columns via the _columns setter\r\n\r\n // If fixed mode and width not specified: assign default 80px\r\n if (base.fitMode === 'fixed') {\r\n this._columns.forEach((c) => {\r\n if (c.width == null) (c as ColumnConfig<T>).width = 80;\r\n });\r\n }\r\n }\r\n\r\n // ---------------- Delegate Wrappers ----------------\r\n #renderVisibleRows(start: number, end: number, epoch = this.__rowRenderEpoch): void {\r\n // Use cached hook to avoid creating closures on every render (hot path optimization)\r\n if (!this.#renderRowHook) {\r\n this.#renderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number): boolean => {\r\n return this.#pluginManager?.renderRow(row, rowEl, rowIndex) ?? false;\r\n };\r\n }\r\n renderVisibleRows(this as any, start, end, epoch, this.#renderRowHook);\r\n }\r\n\r\n #startRowEdit(rowIndex: number, rowData: any): void {\r\n startRowEdit(this as unknown as any, rowIndex, rowData);\r\n }\r\n\r\n #exitRowEdit(rowIndex: number, revert: boolean): void {\r\n exitRowEdit(this as unknown as any, rowIndex, revert);\r\n }\r\n\r\n // ---------------- Core Helpers ----------------\r\n #setup(): void {\r\n if (!this.isConnected) return;\r\n if (!this.headerRowEl || !this.bodyEl) {\r\n return;\r\n }\r\n\r\n // Seed effectiveConfig.columns from config sources before getColumnConfiguration\r\n // This ensures columns from gridConfig/columns prop are available for merging with light DOM\r\n // Preserve hidden state from existing columns (visibility is runtime state)\r\n const configCols = (this.#gridConfig?.columns || this.#columns || []) as ColumnConfig<T>[];\r\n if (configCols.length) {\r\n // Preserve hidden state from existing effectiveConfig.columns\r\n const existingHiddenMap = new Map(this._columns.filter((c) => c.hidden).map((c) => [c.field, true]));\r\n const seeded = configCols.map((c) => ({\r\n ...c,\r\n hidden: existingHiddenMap.get(c.field) ?? c.hidden,\r\n }));\r\n this._columns = seeded as ColumnInternal<T>[];\r\n }\r\n\r\n this.#getColumnConfiguration();\r\n this.#mergeEffectiveConfig();\r\n this.#updatePluginConfigs(); // Sync plugin configs (including auto-detection) before processing\r\n this.#rebuildRowModel(); // Runs processRows hooks (must run before processColumns for tree plugin)\r\n this.#processColumns(); // Runs processColumns hooks\r\n\r\n // Apply initial column state (from gridConfig.columnState or columnState setter)\r\n if (this.#initialColumnState) {\r\n const state = this.#initialColumnState;\r\n this.#initialColumnState = undefined; // Clear to avoid re-applying\r\n this.#applyColumnStateInternal(state);\r\n }\r\n\r\n this.#renderHeader();\r\n this.updateTemplate();\r\n this.refreshVirtualWindow(true);\r\n\r\n const mode = this.#effectiveConfig.fitMode;\r\n if (mode === 'fixed' && !this.__didInitialAutoSize) {\r\n requestAnimationFrame(() => this.#autoSizeColumns());\r\n }\r\n\r\n // Ensure legacy inline grid styles are cleared from container\r\n if (this.bodyEl) {\r\n this.bodyEl.style.display = '';\r\n this.bodyEl.style.gridTemplateColumns = '';\r\n }\r\n\r\n // Run plugin afterRender hooks (column groups, sticky, etc.)\r\n queueMicrotask(() => this.#executeAfterRender());\r\n }\r\n\r\n /** Internal method to apply column state without triggering setup loop */\r\n #applyColumnStateInternal(state: GridColumnState): void {\r\n // Get all columns from effectiveConfig (single source of truth)\r\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\r\n\r\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\r\n applyColumnState(this, state, allCols, plugins);\r\n\r\n // Update hidden property on columns based on state\r\n for (const colState of state.columns) {\r\n const col = allCols.find((c) => c.field === colState.field);\r\n if (col) {\r\n col.hidden = !colState.visible;\r\n }\r\n }\r\n }\r\n\r\n #shouldBypassVirtualization(): boolean {\r\n return this._rows.length <= this.virtualization.bypassThreshold;\r\n }\r\n\r\n #onScrollBatched(scrollTop: number): void {\r\n // Faux scrollbar pattern: content never scrolls, just update transforms\r\n // Old content stays visible until new transforms are applied\r\n this.refreshVirtualWindow(false);\r\n\r\n // Dispatch to plugins (using cached flag)\r\n if (this.#hasScrollPlugins) {\r\n const fauxScrollbar = this.virtualization.container;\r\n const scrollEvent: ScrollEvent = {\r\n scrollTop,\r\n scrollLeft: fauxScrollbar?.scrollLeft ?? 0,\r\n scrollHeight: fauxScrollbar?.scrollHeight ?? 0,\r\n scrollWidth: fauxScrollbar?.scrollWidth ?? 0,\r\n clientHeight: fauxScrollbar?.clientHeight ?? 0,\r\n clientWidth: fauxScrollbar?.clientWidth ?? 0,\r\n originalEvent: new Event('scroll'),\r\n };\r\n this.#pluginManager?.onScroll(scrollEvent);\r\n }\r\n }\r\n\r\n findHeaderRow(): HTMLElement {\r\n return this.#shadow.querySelector('.header-row') as HTMLElement;\r\n }\r\n\r\n findRenderedRowElement(rowIndex: number): HTMLElement | null {\r\n return (\r\n (Array.from(this.bodyEl.querySelectorAll('.data-grid-row')) as HTMLElement[]).find((r) => {\r\n const cell = r.querySelector('.cell[data-row]');\r\n return cell && Number(cell.getAttribute('data-row')) === rowIndex;\r\n }) || null\r\n );\r\n }\r\n\r\n /**\r\n * Dispatch a cell click event to the plugin system.\r\n * Returns true if any plugin handled the event.\r\n */\r\n dispatchCellClick(event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement): boolean {\r\n const row = this._rows[rowIndex];\r\n const col = this._columns[colIndex];\r\n if (!row || !col) return false;\r\n\r\n const cellClickEvent: CellClickEvent = {\r\n row,\r\n rowIndex,\r\n colIndex,\r\n field: col.field,\r\n value: (row as any)[col.field],\r\n cellEl,\r\n originalEvent: event,\r\n };\r\n\r\n return this.#pluginManager?.onCellClick(cellClickEvent) ?? false;\r\n }\r\n\r\n /**\r\n * Dispatch a header click event to the plugin system.\r\n * Returns true if any plugin handled the event.\r\n */\r\n dispatchHeaderClick(event: MouseEvent, colIndex: number, headerEl: HTMLElement): boolean {\r\n const col = this._columns[colIndex];\r\n if (!col) return false;\r\n\r\n const headerClickEvent: HeaderClickEvent = {\r\n colIndex,\r\n field: col.field,\r\n column: col,\r\n headerEl,\r\n originalEvent: event,\r\n };\r\n\r\n return this.#pluginManager?.onHeaderClick(headerClickEvent) ?? false;\r\n }\r\n\r\n /**\r\n * Dispatch a keyboard event to the plugin system.\r\n * Returns true if any plugin handled the event.\r\n */\r\n dispatchKeyDown(event: KeyboardEvent): boolean {\r\n return this.#pluginManager?.onKeyDown(event) ?? false;\r\n }\r\n\r\n /**\r\n * Build a CellMouseEvent from a native MouseEvent.\r\n * Extracts cell/row information from the event target.\r\n */\r\n #buildCellMouseEvent(e: MouseEvent, type: 'mousedown' | 'mousemove' | 'mouseup'): CellMouseEvent {\r\n // For document-level events (mousemove/mouseup during drag), e.target won't be inside shadow DOM.\r\n // Use composedPath to find elements inside shadow roots, or fall back to elementFromPoint.\r\n let target: Element | null = null;\r\n\r\n // composedPath gives us the full path including shadow DOM elements\r\n const path = e.composedPath?.() as Element[] | undefined;\r\n if (path && path.length > 0) {\r\n target = path[0];\r\n } else {\r\n target = e.target as Element;\r\n }\r\n\r\n // If target is still not inside our shadow root (e.g., for document-level events),\r\n // use elementFromPoint to find the actual element under the mouse\r\n if (target && !this.#shadow.contains(target)) {\r\n const elAtPoint = this.#shadow.elementFromPoint(e.clientX, e.clientY);\r\n if (elAtPoint) {\r\n target = elAtPoint;\r\n }\r\n }\r\n\r\n // Cells have data-col and data-row attributes\r\n const cellEl = target?.closest?.('[data-col]') as HTMLElement | null;\r\n const rowEl = target?.closest?.('.data-grid-row') as HTMLElement | null;\r\n const headerEl = target?.closest?.('.header-row') as HTMLElement | null;\r\n\r\n let rowIndex: number | undefined;\r\n let colIndex: number | undefined;\r\n let row: T | undefined;\r\n let field: string | undefined;\r\n let value: unknown;\r\n let column: any;\r\n\r\n if (cellEl) {\r\n // Get indices from cell attributes\r\n rowIndex = parseInt(cellEl.getAttribute('data-row') ?? '-1', 10);\r\n colIndex = parseInt(cellEl.getAttribute('data-col') ?? '-1', 10);\r\n if (rowIndex >= 0 && colIndex >= 0) {\r\n row = this._rows[rowIndex];\r\n column = this._columns[colIndex];\r\n field = column?.field;\r\n value = row && field ? (row as any)[field] : undefined;\r\n }\r\n }\r\n\r\n return {\r\n type,\r\n row,\r\n rowIndex: rowIndex !== undefined && rowIndex >= 0 ? rowIndex : undefined,\r\n colIndex: colIndex !== undefined && colIndex >= 0 ? colIndex : undefined,\r\n field,\r\n value,\r\n column,\r\n originalEvent: e,\r\n cellElement: cellEl ?? undefined,\r\n rowElement: rowEl ?? undefined,\r\n isHeader: !!headerEl,\r\n cell:\r\n rowIndex !== undefined && colIndex !== undefined && rowIndex >= 0 && colIndex >= 0\r\n ? { row: rowIndex, col: colIndex }\r\n : undefined,\r\n };\r\n }\r\n\r\n /**\r\n * Handle mousedown events and dispatch to plugin system.\r\n */\r\n #handleMouseDown(e: MouseEvent): void {\r\n const event = this.#buildCellMouseEvent(e, 'mousedown');\r\n const handled = this.#pluginManager?.onCellMouseDown(event) ?? false;\r\n\r\n // If any plugin handled mousedown, start tracking for drag\r\n if (handled) {\r\n this.#isDragging = true;\r\n }\r\n }\r\n\r\n /**\r\n * Handle mousemove events (only when dragging).\r\n */\r\n #handleMouseMove(e: MouseEvent): void {\r\n if (!this.#isDragging) return;\r\n\r\n const event = this.#buildCellMouseEvent(e, 'mousemove');\r\n this.#pluginManager?.onCellMouseMove(event);\r\n }\r\n\r\n /**\r\n * Handle mouseup events.\r\n */\r\n #handleMouseUp(e: MouseEvent): void {\r\n if (!this.#isDragging) return;\r\n\r\n const event = this.#buildCellMouseEvent(e, 'mouseup');\r\n this.#pluginManager?.onCellMouseUp(event);\r\n this.#isDragging = false;\r\n }\r\n\r\n // API consumed by internal utils (rows.ts)\r\n get changedRows(): T[] {\r\n return Array.from(this._changedRowIndices).map((i) => this._rows[i]);\r\n }\r\n\r\n get changedRowIndices(): number[] {\r\n return Array.from(this._changedRowIndices);\r\n }\r\n\r\n async resetChangedRows(silent?: boolean): Promise<void> {\r\n this._changedRowIndices.clear();\r\n if (!silent) {\r\n this.#emit('changed-rows-reset', { rows: this.changedRows, indices: this.changedRowIndices });\r\n }\r\n this.rowPool.forEach((r) => r.classList.remove('changed'));\r\n }\r\n\r\n async beginBulkEdit(rowIndex: number): Promise<void> {\r\n this.#startRowEdit(rowIndex, this._rows[rowIndex]);\r\n }\r\n\r\n async commitActiveRowEdit(): Promise<void> {\r\n if (this.activeEditRows !== -1) {\r\n this.#exitRowEdit(this.activeEditRows, false);\r\n }\r\n }\r\n\r\n async ready(): Promise<void> {\r\n return this.#readyPromise;\r\n }\r\n\r\n async forceLayout(): Promise<void> {\r\n this.#setup();\r\n await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));\r\n }\r\n\r\n /** Public method: returns a frozen snapshot of the merged effective configuration */\r\n async getConfig(): Promise<Readonly<GridConfig<T>>> {\r\n return Object.freeze({ ...(this.#effectiveConfig || {}) });\r\n }\r\n\r\n // ---------------- Column Visibility API ----------------\r\n\r\n /**\r\n * Set the visibility of a column.\r\n * @param field - The field name of the column\r\n * @param visible - Whether the column should be visible\r\n * @returns True if visibility was changed, false if column not found or locked\r\n */\r\n setColumnVisible(field: string, visible: boolean): boolean {\r\n // Find the column in effectiveConfig.columns (includes hidden columns)\r\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\r\n const col = allCols?.find((c) => c.field === field);\r\n\r\n // If column not found, cannot change visibility\r\n if (!col) return false;\r\n\r\n // Check lockVisible - cannot hide locked columns\r\n if (!visible && col.lockVisible) return false;\r\n\r\n // Check if at least one column would remain visible\r\n if (!visible) {\r\n const currentVisible = (allCols ?? []).filter((c) => !c.hidden && c.field !== field).length;\r\n if (currentVisible === 0) return false;\r\n }\r\n\r\n const wasHidden = !!col.hidden;\r\n const willBeHidden = !visible;\r\n\r\n // Only refresh if visibility actually changed\r\n if (wasHidden !== willBeHidden) {\r\n // Update the hidden property on the column in effectiveConfig\r\n col.hidden = willBeHidden;\r\n\r\n // Emit event for consumer preference saving\r\n this.#emit('column-visibility', {\r\n field,\r\n visible,\r\n visibleColumns: (allCols ?? []).filter((c) => !c.hidden).map((c) => c.field),\r\n });\r\n\r\n // Clear row pool to force complete rebuild with new column count\r\n this.rowPool.length = 0;\r\n if (this.bodyEl) this.bodyEl.innerHTML = '';\r\n this.__rowRenderEpoch++;\r\n\r\n // Re-setup to rebuild columns with updated visibility\r\n this.#setup();\r\n\r\n // Trigger state change after visibility change\r\n this.requestStateChange();\r\n return true;\r\n }\r\n return false;\r\n }\r\n\r\n /**\r\n * Toggle the visibility of a column.\r\n * @param field - The field name of the column\r\n * @returns True if visibility was toggled, false if column not found or locked\r\n */\r\n toggleColumnVisibility(field: string): boolean {\r\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\r\n const col = allCols?.find((c) => c.field === field);\r\n const isCurrentlyHidden = !!col?.hidden;\r\n return this.setColumnVisible(field, isCurrentlyHidden);\r\n }\r\n\r\n /**\r\n * Check if a column is currently visible.\r\n * @param field - The field name of the column\r\n * @returns True if visible, false if hidden or not found\r\n */\r\n isColumnVisible(field: string): boolean {\r\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\r\n const col = allCols?.find((c) => c.field === field);\r\n return col ? !col.hidden : false;\r\n }\r\n\r\n /**\r\n * Show all columns.\r\n */\r\n showAllColumns(): void {\r\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\r\n const hasHidden = allCols?.some((c) => c.hidden);\r\n if (!hasHidden) return;\r\n\r\n // Clear hidden flag on all columns\r\n allCols?.forEach((c) => {\r\n c.hidden = false;\r\n });\r\n\r\n this.#emit('column-visibility', {\r\n visibleColumns: (allCols ?? []).map((c) => c.field),\r\n });\r\n\r\n // Clear row pool to force complete rebuild with new column count\r\n this.rowPool.length = 0;\r\n if (this.bodyEl) this.bodyEl.innerHTML = '';\r\n this.__rowRenderEpoch++;\r\n\r\n this.#setup();\r\n\r\n // Trigger state change after visibility change\r\n this.requestStateChange();\r\n }\r\n\r\n /**\r\n * Get list of all column fields (including hidden).\r\n * Returns columns reflecting current display order (after reordering).\r\n * Hidden columns are interleaved at their original relative positions.\r\n * @returns Array of all field names with their visibility status\r\n */\r\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\r\n // effectiveConfig.columns is the single source of truth\r\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\r\n\r\n // Return all columns with their current visibility state\r\n return allCols.map((c) => ({\r\n field: c.field,\r\n header: c.header || c.field,\r\n visible: !c.hidden,\r\n lockVisible: c.lockVisible,\r\n }));\r\n }\r\n\r\n /**\r\n * Reorder columns according to the specified field order.\r\n * This directly updates _columns in place without going through processColumns.\r\n * @param order - Array of field names in the desired order\r\n */\r\n setColumnOrder(order: string[]): void {\r\n if (!order.length) return;\r\n\r\n const columnMap = new Map<string, ColumnInternal<T>>(this._columns.map((c) => [c.field as string, c]));\r\n const reordered: ColumnInternal<T>[] = [];\r\n\r\n // Add columns in specified order\r\n for (const field of order) {\r\n const col = columnMap.get(field);\r\n if (col) {\r\n reordered.push(col);\r\n columnMap.delete(field);\r\n }\r\n }\r\n\r\n // Add any remaining columns not in order\r\n for (const col of columnMap.values()) {\r\n reordered.push(col);\r\n }\r\n\r\n this._columns = reordered;\r\n\r\n // Re-render with new order\r\n this.#renderHeader();\r\n this.updateTemplate();\r\n this.refreshVirtualWindow(true);\r\n }\r\n\r\n /**\r\n * Get the current column order as an array of field names.\r\n * @returns Array of field names in display order\r\n */\r\n getColumnOrder(): string[] {\r\n return this._columns.map((c) => c.field);\r\n }\r\n\r\n // ---------------- Column State API ----------------\r\n\r\n /**\r\n * Get the current column state, including order, width, visibility, sort, and plugin state.\r\n * Returns a serializable object suitable for localStorage or database storage.\r\n */\r\n getColumnState(): GridColumnState {\r\n const plugins = this.#pluginManager?.getAll() ?? [];\r\n return collectColumnState(this, plugins as BaseGridPlugin[]);\r\n }\r\n\r\n /**\r\n * Set the column state, restoring order, width, visibility, sort, and plugin state.\r\n * Use this to restore previously saved column state.\r\n */\r\n set columnState(state: GridColumnState | undefined) {\r\n if (!state) return;\r\n\r\n // Store for use after initialization if called before ready\r\n this.#initialColumnState = state;\r\n\r\n // If already initialized, apply immediately\r\n if (this.#initialized) {\r\n this.#applyColumnState(state);\r\n }\r\n }\r\n\r\n /**\r\n * Get the current column state.\r\n */\r\n get columnState(): GridColumnState | undefined {\r\n return this.getColumnState();\r\n }\r\n\r\n /**\r\n * Apply column state internally.\r\n */\r\n #applyColumnState(state: GridColumnState): void {\r\n // Clear hidden flags before applying state\r\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\r\n allCols.forEach((c) => {\r\n c.hidden = false;\r\n });\r\n\r\n this.#applyColumnStateInternal(state);\r\n\r\n // Re-setup to apply changes\r\n this.#setup();\r\n }\r\n\r\n /**\r\n * Request a state change event to be emitted.\r\n * Called internally after resize, reorder, visibility, or sort changes.\r\n * Plugins should call this after changing their state.\r\n * The event is debounced to avoid excessive events during drag operations.\r\n */\r\n requestStateChange(): void {\r\n if (!this.#stateChangeHandler) {\r\n this.#stateChangeHandler = createStateChangeHandler(\r\n this,\r\n () => (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[],\r\n (state) => this.#emit('column-state-change', state)\r\n );\r\n }\r\n this.#stateChangeHandler();\r\n }\r\n\r\n /**\r\n * Reset column state to initial configuration.\r\n * Clears all user modifications (order, width, visibility, sort).\r\n */\r\n resetColumnState(): void {\r\n // Clear initial state\r\n this.#initialColumnState = undefined;\r\n\r\n // Clear hidden flag on all columns\r\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\r\n allCols.forEach((c) => {\r\n c.hidden = false;\r\n });\r\n\r\n // Reset sort state\r\n this.sortState = null;\r\n this.__originalOrder = [];\r\n\r\n // Re-initialize columns from config\r\n this.#mergeEffectiveConfig();\r\n this.#setup();\r\n\r\n // Notify plugins to reset their state\r\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\r\n for (const plugin of plugins) {\r\n if (plugin.applyColumnState) {\r\n // Pass empty state to indicate reset\r\n for (const col of this._columns) {\r\n plugin.applyColumnState(col.field, {\r\n field: col.field,\r\n order: 0,\r\n visible: true,\r\n });\r\n }\r\n }\r\n }\r\n\r\n // Emit state change\r\n this.requestStateChange();\r\n }\r\n\r\n // ---------------- Shell / Tool Panel API ----------------\r\n\r\n /**\r\n * Get the currently active tool panel ID, or null if none is open.\r\n */\r\n get activeToolPanel(): string | null {\r\n return this.#shellState.activePanel;\r\n }\r\n\r\n /**\r\n * Open a tool panel by ID.\r\n * Closes any currently open panel first.\r\n */\r\n openToolPanel(panelId: string): void {\r\n const panel = this.#shellState.toolPanels.get(panelId);\r\n if (!panel) {\r\n console.warn(`[tbw-grid] Tool panel \"${panelId}\" not found`);\r\n return;\r\n }\r\n\r\n // Close current panel if different\r\n if (this.#shellState.activePanel && this.#shellState.activePanel !== panelId) {\r\n this.closeToolPanel();\r\n }\r\n\r\n // Set active panel\r\n this.#shellState.activePanel = panelId;\r\n\r\n // Update UI\r\n updateToolbarActiveStates(this.#shadow, this.#shellState);\r\n updatePanelState(this.#shadow, this.#shellState);\r\n\r\n // Render panel content\r\n renderPanelContent(this.#shadow, this.#shellState);\r\n\r\n // Emit event\r\n this.#emit('tool-panel-open', { id: panelId });\r\n }\r\n\r\n /**\r\n * Close the currently open tool panel.\r\n */\r\n closeToolPanel(): void {\r\n if (!this.#shellState.activePanel) return;\r\n\r\n const panelId = this.#shellState.activePanel;\r\n const panel = this.#shellState.toolPanels.get(panelId);\r\n\r\n // Clean up panel content\r\n if (this.#shellState.activePanelCleanup) {\r\n this.#shellState.activePanelCleanup();\r\n this.#shellState.activePanelCleanup = null;\r\n }\r\n\r\n // Call panel's onClose callback\r\n panel?.onClose?.();\r\n\r\n // Clear active panel\r\n this.#shellState.activePanel = null;\r\n\r\n // Update UI\r\n updateToolbarActiveStates(this.#shadow, this.#shellState);\r\n updatePanelState(this.#shadow, this.#shellState);\r\n\r\n // Emit event\r\n this.#emit('tool-panel-close', { id: panelId });\r\n }\r\n\r\n /**\r\n * Toggle a tool panel open/closed.\r\n */\r\n toggleToolPanel(panelId: string): void {\r\n if (this.#shellState.activePanel === panelId) {\r\n this.closeToolPanel();\r\n } else {\r\n this.openToolPanel(panelId);\r\n }\r\n }\r\n\r\n /**\r\n * Get registered tool panel definitions.\r\n */\r\n getToolPanels(): ToolPanelDefinition[] {\r\n return [...this.#shellState.toolPanels.values()];\r\n }\r\n\r\n /**\r\n * Register a custom tool panel (without creating a plugin).\r\n */\r\n registerToolPanel(panel: ToolPanelDefinition): void {\r\n if (this.#shellState.toolPanels.has(panel.id)) {\r\n console.warn(`[tbw-grid] Tool panel \"${panel.id}\" already registered`);\r\n return;\r\n }\r\n this.#shellState.toolPanels.set(panel.id, panel);\r\n\r\n // Re-render shell if needed to show new toolbar button\r\n if (this.#shellInitialized) {\r\n this.#refreshShellHeader();\r\n }\r\n }\r\n\r\n /**\r\n * Unregister a custom tool panel.\r\n */\r\n unregisterToolPanel(panelId: string): void {\r\n // Close panel if it's open\r\n if (this.#shellState.activePanel === panelId) {\r\n this.closeToolPanel();\r\n }\r\n\r\n this.#shellState.toolPanels.delete(panelId);\r\n\r\n // Re-render shell if needed to remove toolbar button\r\n if (this.#shellInitialized) {\r\n this.#refreshShellHeader();\r\n }\r\n }\r\n\r\n /**\r\n * Get registered header content definitions.\r\n */\r\n getHeaderContents(): HeaderContentDefinition[] {\r\n return [...this.#shellState.headerContents.values()];\r\n }\r\n\r\n /**\r\n * Register custom header content (without creating a plugin).\r\n */\r\n registerHeaderContent(content: HeaderContentDefinition): void {\r\n if (this.#shellState.headerContents.has(content.id)) {\r\n console.warn(`[tbw-grid] Header content \"${content.id}\" already registered`);\r\n return;\r\n }\r\n this.#shellState.headerContents.set(content.id, content);\r\n\r\n // Render the content if shell is initialized\r\n if (this.#shellInitialized) {\r\n renderHeaderContent(this.#shadow, this.#shellState);\r\n }\r\n }\r\n\r\n /**\r\n * Unregister custom header content.\r\n */\r\n unregisterHeaderContent(contentId: string): void {\r\n // Clean up\r\n const cleanup = this.#shellState.headerContentCleanups.get(contentId);\r\n if (cleanup) {\r\n cleanup();\r\n this.#shellState.headerContentCleanups.delete(contentId);\r\n }\r\n\r\n // Call onDestroy\r\n const content = this.#shellState.headerContents.get(contentId);\r\n content?.onDestroy?.();\r\n\r\n this.#shellState.headerContents.delete(contentId);\r\n\r\n // Remove DOM element\r\n const el = this.#shadow.querySelector(`[data-header-content=\"${contentId}\"]`);\r\n el?.remove();\r\n }\r\n\r\n /**\r\n * Get all registered toolbar buttons.\r\n */\r\n getToolbarButtons(): ToolbarButtonInfo[] {\r\n return getToolbarButtonsInfo(this.#effectiveConfig?.shell, this.#shellState);\r\n }\r\n\r\n /**\r\n * Register a custom toolbar button programmatically.\r\n */\r\n registerToolbarButton(button: ToolbarButtonConfig): void {\r\n if (this.#shellState.toolbarButtons.has(button.id)) {\r\n console.warn(`[tbw-grid] Toolbar button \"${button.id}\" already registered`);\r\n return;\r\n }\r\n this.#shellState.toolbarButtons.set(button.id, button);\r\n\r\n // Re-render shell if needed\r\n if (this.#shellInitialized) {\r\n this.#refreshShellHeader();\r\n }\r\n }\r\n\r\n /**\r\n * Unregister a custom toolbar button.\r\n */\r\n unregisterToolbarButton(buttonId: string): void {\r\n // Clean up\r\n const cleanup = this.#shellState.toolbarButtonCleanups.get(buttonId);\r\n if (cleanup) {\r\n cleanup();\r\n this.#shellState.toolbarButtonCleanups.delete(buttonId);\r\n }\r\n\r\n this.#shellState.toolbarButtons.delete(buttonId);\r\n\r\n // Re-render shell if needed\r\n if (this.#shellInitialized) {\r\n this.#refreshShellHeader();\r\n }\r\n }\r\n\r\n /**\r\n * Enable/disable a toolbar button by ID.\r\n */\r\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): void {\r\n // Check API-registered buttons\r\n const apiBtn = this.#shellState.toolbarButtons.get(buttonId);\r\n if (apiBtn) {\r\n apiBtn.disabled = disabled;\r\n }\r\n\r\n // Update DOM\r\n const btn = this.#shadow.querySelector(`[data-btn=\"${buttonId}\"]`) as HTMLButtonElement | null;\r\n if (btn) {\r\n btn.disabled = disabled;\r\n }\r\n }\r\n\r\n /**\r\n * Re-parse light DOM shell elements and refresh shell header.\r\n * Call this after dynamically modifying <tbw-grid-header> children.\r\n */\r\n refreshShellHeader(): void {\r\n this.#refreshShellHeader();\r\n }\r\n\r\n /**\r\n * Internal shell header refresh.\r\n */\r\n #refreshShellHeader(): void {\r\n // Re-parse light DOM\r\n parseLightDomShell(this, this.#shellState);\r\n\r\n // Re-render the entire grid (shell structure may change)\r\n this.#render();\r\n this.#afterConnect();\r\n }\r\n\r\n // ---------------- Virtual Window ----------------\r\n /**\r\n * Core virtualization routine. Chooses between bypass (small datasets), grouped window rendering,\r\n * or standard row window rendering.\r\n */\r\n refreshVirtualWindow(force = false): void {\r\n if (!this.bodyEl) return;\r\n\r\n const totalRows = this._rows.length;\r\n\r\n if (!this.virtualization.enabled) {\r\n this.#renderVisibleRows(0, totalRows);\r\n this.#executeAfterRender();\r\n return;\r\n }\r\n\r\n if (this.#shouldBypassVirtualization()) {\r\n this.virtualization.start = 0;\r\n this.virtualization.end = totalRows;\r\n this.bodyEl.style.transform = 'translateY(0px)';\r\n this.#renderVisibleRows(0, totalRows, this.__rowRenderEpoch);\r\n if (this.virtualization.totalHeightEl) {\r\n this.virtualization.totalHeightEl.style.height = `${totalRows * this.virtualization.rowHeight}px`;\r\n }\r\n this.setAttribute('aria-rowcount', String(totalRows));\r\n this.setAttribute('aria-colcount', String(this.visibleColumns.length));\r\n this.#executeAfterRender();\r\n return;\r\n }\r\n\r\n // --- Normal virtualization path with faux scrollbar pattern ---\r\n // Faux scrollbar provides scrollTop, viewport provides visible height\r\n const fauxScrollbar = this.virtualization.container ?? this;\r\n const viewportEl = this.virtualization.viewportEl ?? fauxScrollbar;\r\n const viewportHeight = viewportEl.clientHeight;\r\n const rowHeight = this.virtualization.rowHeight;\r\n const scrollTop = fauxScrollbar.scrollTop;\r\n\r\n // Faux scrollbar pattern: no overscan adjustment for start\r\n // With translateY(0), the first rendered row appears at viewport top\r\n // (Overscan before start would make extra rows visible, not hidden)\r\n let start = Math.floor(scrollTop / rowHeight);\r\n if (start < 0) start = 0;\r\n\r\n // Faux pattern buffer: render 2 extra rows below for smooth edge transition\r\n // This is smaller than traditional overscan since sub-pixel offset handles smoothness\r\n const visibleCount = Math.ceil(viewportHeight / rowHeight) + 2;\r\n let end = start + visibleCount;\r\n if (end > totalRows) end = totalRows;\r\n\r\n this.virtualization.start = start;\r\n this.virtualization.end = end;\r\n\r\n // Height spacer for scrollbar\r\n if (this.virtualization.totalHeightEl) {\r\n this.virtualization.totalHeightEl.style.height = `${totalRows * rowHeight}px`;\r\n }\r\n\r\n // Smooth scroll: apply sub-pixel offset for fluid motion\r\n // This is the remainder after dividing scrollTop by rowHeight\r\n // Creates smooth sliding effect as rows scroll between row boundaries\r\n const subPixelOffset = -(scrollTop % rowHeight);\r\n this.bodyEl.style.transform = `translateY(${subPixelOffset}px)`;\r\n\r\n this.#renderVisibleRows(start, end, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\r\n\r\n this.setAttribute('aria-rowcount', String(totalRows));\r\n this.setAttribute('aria-colcount', String(this.visibleColumns.length));\r\n\r\n // Only run plugin afterRender hooks on force refresh (structural changes)\r\n // Skip on scroll-triggered renders for maximum performance\r\n if (force) {\r\n this.#executeAfterRender();\r\n }\r\n }\r\n\r\n // ---------------- Render ----------------\r\n #render(): void {\r\n // Parse light DOM shell elements before rendering\r\n parseLightDomShell(this, this.#shellState);\r\n\r\n // Get shell config\r\n const shellConfig = this.#effectiveConfig?.shell;\r\n\r\n // Determine if shell should be rendered\r\n const hasShell = shouldRenderShellHeader(shellConfig, this.#shellState);\r\n\r\n // Core grid content HTML\r\n // Uses faux scrollbar pattern (like AG Grid) for smooth virtualized scrolling:\r\n // - .tbw-grid-content: outer container (row layout: scroll-area + faux-vscroll)\r\n // - .tbw-scroll-area: horizontal scroll container (overflow-x: auto) - footer appends here\r\n // - .rows-body-wrapper: header + rows in column layout\r\n // - .faux-vscroll: vertical scrollbar at inline-end, sticky during horizontal scroll\r\n // - .rows-viewport: visible rows area (no scroll, overflow hidden)\r\n // - Scroll events come from faux scrollbar, content positioned via transforms\r\n // This prevents blank viewport during fast scroll - old content stays until new renders\r\n const gridContentHtml = `\r\n <div class=\"tbw-scroll-area\">\r\n <div class=\"rows-body-wrapper\">\r\n <div class=\"rows-body\">\r\n <div class=\"header\">\r\n <div class=\"header-row\" part=\"header-row\"></div>\r\n </div>\r\n <div class=\"rows-container\">\r\n <div class=\"rows-viewport\">\r\n <div class=\"rows\"></div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n </div>\r\n <div class=\"faux-vscroll\">\r\n <div class=\"faux-vscroll-spacer\"></div>\r\n </div>\r\n `;\r\n\r\n if (hasShell) {\r\n // Build shell DOM structure\r\n const shellHeaderHtml = renderShellHeader(shellConfig, this.#shellState);\r\n const shellBodyHtml = renderShellBody(shellConfig, this.#shellState, gridContentHtml);\r\n\r\n this.#shadow.innerHTML = `\r\n <div class=\"tbw-grid-root has-shell\">\r\n ${shellHeaderHtml}\r\n ${shellBodyHtml}\r\n </div>\r\n `;\r\n\r\n // Set up shell event listeners\r\n this.#setupShellListeners();\r\n\r\n // Mark shell as initialized\r\n this.#shellInitialized = true;\r\n } else {\r\n // Build minimal DOM structure (no shell)\r\n // Wrap in .tbw-grid-content for consistent horizontal scroll behavior\r\n this.#shadow.innerHTML = `\r\n <div class=\"tbw-grid-root\">\r\n <div class=\"tbw-grid-content\">\r\n ${gridContentHtml}\r\n </div>\r\n </div>\r\n `;\r\n }\r\n }\r\n\r\n /**\r\n * Set up shell event listeners after render.\r\n */\r\n #setupShellListeners(): void {\r\n setupShellEventListeners(this.#shadow, this.#effectiveConfig?.shell, this.#shellState, {\r\n onPanelToggle: (panelId) => this.toggleToolPanel(panelId),\r\n onPanelClose: () => this.closeToolPanel(),\r\n onToolbarButtonClick: (buttonId) => this.#handleToolbarButtonClick(buttonId),\r\n });\r\n }\r\n\r\n /**\r\n * Handle toolbar button click (for config buttons with action).\r\n */\r\n #handleToolbarButtonClick(buttonId: string): void {\r\n // Check config buttons\r\n const configButtons = this.#effectiveConfig?.shell?.header?.toolbarButtons ?? [];\r\n const configBtn = configButtons.find((b) => b.id === buttonId);\r\n if (configBtn?.action) {\r\n configBtn.action();\r\n return;\r\n }\r\n\r\n // Check API-registered buttons\r\n const apiBtn = this.#shellState.toolbarButtons.get(buttonId);\r\n if (apiBtn?.action) {\r\n apiBtn.action();\r\n }\r\n }\r\n}\r\n\r\n// Self-registering custom element\r\nif (!customElements.get(DataGridElement.tagName)) {\r\n customElements.define(DataGridElement.tagName, DataGridElement);\r\n}\r\n\r\n// Type augmentation for querySelector/createElement\r\ndeclare global {\r\n interface HTMLElementTagNameMap {\r\n 'tbw-grid': DataGridElement;\r\n }\r\n}\r\n","/**\r\n * Base Grid Plugin Class\r\n *\r\n * All plugins extend this abstract class.\r\n * Plugins are instantiated per-grid and manage their own state.\r\n */\r\n\r\nimport type { ColumnConfig, ColumnState, HeaderContentDefinition, ToolPanelDefinition } from '../types';\r\n\r\n// Forward declare to avoid circular imports\r\nexport interface GridElement {\r\n shadowRoot: ShadowRoot | null;\r\n rows: any[];\r\n columns: ColumnConfig[];\r\n gridConfig: any;\r\n requestRender(): void;\r\n requestAfterRender(): void;\r\n forceLayout(): Promise<void>;\r\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined;\r\n getPluginByName(name: string): BaseGridPlugin | undefined;\r\n dispatchEvent(event: Event): boolean;\r\n}\r\n\r\n/**\r\n * Keyboard modifier flags\r\n */\r\nexport interface KeyboardModifiers {\r\n ctrl?: boolean;\r\n shift?: boolean;\r\n alt?: boolean;\r\n meta?: boolean;\r\n}\r\n\r\n/**\r\n * Cell coordinates\r\n */\r\nexport interface CellCoords {\r\n row: number;\r\n col: number;\r\n}\r\n\r\n/**\r\n * Cell click event\r\n */\r\nexport interface CellClickEvent {\r\n rowIndex: number;\r\n colIndex: number;\r\n field: string;\r\n value: any;\r\n row: any;\r\n cellEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Row click event\r\n */\r\nexport interface RowClickEvent {\r\n rowIndex: number;\r\n row: any;\r\n rowEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Header click event\r\n */\r\nexport interface HeaderClickEvent {\r\n colIndex: number;\r\n field: string;\r\n column: ColumnConfig;\r\n headerEl: HTMLElement;\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Scroll event\r\n */\r\nexport interface ScrollEvent {\r\n scrollTop: number;\r\n scrollLeft: number;\r\n scrollHeight: number;\r\n scrollWidth: number;\r\n clientHeight: number;\r\n clientWidth: number;\r\n originalEvent?: Event;\r\n}\r\n\r\n/**\r\n * Cell mouse event (for drag operations, selection, etc.)\r\n */\r\nexport interface CellMouseEvent {\r\n /** Event type: mousedown, mousemove, or mouseup */\r\n type: 'mousedown' | 'mousemove' | 'mouseup';\r\n /** Row index, undefined if not over a data cell */\r\n rowIndex?: number;\r\n /** Column index, undefined if not over a cell */\r\n colIndex?: number;\r\n /** Field name, undefined if not over a cell */\r\n field?: string;\r\n /** Cell value, undefined if not over a data cell */\r\n value?: unknown;\r\n /** Row data object, undefined if not over a data row */\r\n row?: unknown;\r\n /** Column configuration, undefined if not over a column */\r\n column?: ColumnConfig;\r\n /** The cell element, undefined if not over a cell */\r\n cellElement?: HTMLElement;\r\n /** The row element, undefined if not over a row */\r\n rowElement?: HTMLElement;\r\n /** Whether the event is over a header cell */\r\n isHeader: boolean;\r\n /** Cell coordinates if over a valid data cell */\r\n cell?: CellCoords;\r\n /** The original mouse event */\r\n originalEvent: MouseEvent;\r\n}\r\n\r\n/**\r\n * Context menu parameters\r\n */\r\nexport interface ContextMenuParams {\r\n x: number;\r\n y: number;\r\n rowIndex?: number;\r\n colIndex?: number;\r\n field?: string;\r\n value?: any;\r\n row?: any;\r\n column?: ColumnConfig;\r\n isHeader?: boolean;\r\n}\r\n\r\n/**\r\n * Context menu item\r\n */\r\nexport interface ContextMenuItem {\r\n id: string;\r\n label: string;\r\n icon?: string;\r\n disabled?: boolean;\r\n separator?: boolean;\r\n children?: ContextMenuItem[];\r\n action?: (params: ContextMenuParams) => void;\r\n}\r\n\r\n/**\r\n * Cell render context for plugin cell renderers.\r\n * Provides full context including position and editing state.\r\n *\r\n * Note: This differs from the core `CellRenderContext` in types.ts which is\r\n * simpler and used for column view renderers. This version provides additional\r\n * context needed by plugins that register custom cell renderers.\r\n */\r\nexport interface PluginCellRenderContext {\r\n /** The cell value */\r\n value: any;\r\n /** The field/column key */\r\n field: string;\r\n /** The row data object */\r\n row: any;\r\n /** Row index in the data array */\r\n rowIndex: number;\r\n /** Column index */\r\n colIndex: number;\r\n /** Column configuration */\r\n column: ColumnConfig;\r\n /** Whether the cell is currently in edit mode */\r\n isEditing: boolean;\r\n}\r\n\r\n/**\r\n * Header render context for plugin header renderers.\r\n */\r\nexport interface PluginHeaderRenderContext {\r\n /** Column configuration */\r\n column: ColumnConfig;\r\n /** Column index */\r\n colIndex: number;\r\n}\r\n\r\n/**\r\n * Cell renderer function type for plugins.\r\n */\r\nexport type CellRenderer = (ctx: PluginCellRenderContext) => string | HTMLElement;\r\n\r\n/**\r\n * Header renderer function type for plugins.\r\n */\r\nexport type HeaderRenderer = (ctx: PluginHeaderRenderContext) => string | HTMLElement;\r\n\r\n/**\r\n * Cell editor interface for plugins.\r\n */\r\nexport interface CellEditor {\r\n create(ctx: PluginCellRenderContext, commitFn: (value: any) => void, cancelFn: () => void): HTMLElement;\r\n getValue?(element: HTMLElement): any;\r\n focus?(element: HTMLElement): void;\r\n}\r\n\r\n/**\r\n * Abstract base class for all grid plugins.\r\n *\r\n * @template TConfig - Configuration type for the plugin\r\n */\r\nexport abstract class BaseGridPlugin<TConfig = unknown> {\r\n /** Unique plugin identifier (derived from class name by default) */\r\n abstract readonly name: string;\r\n\r\n /** Plugin version - override in subclass if needed */\r\n readonly version: string = '1.0.0';\r\n\r\n /** CSS styles to inject into the grid's shadow DOM */\r\n readonly styles?: string;\r\n\r\n /** Custom cell renderers keyed by type name */\r\n readonly cellRenderers?: Record<string, CellRenderer>;\r\n\r\n /** Custom header renderers keyed by type name */\r\n readonly headerRenderers?: Record<string, HeaderRenderer>;\r\n\r\n /** Custom cell editors keyed by type name */\r\n readonly cellEditors?: Record<string, CellEditor>;\r\n\r\n /** The grid instance this plugin is attached to */\r\n protected grid!: GridElement;\r\n\r\n /** Plugin configuration - merged with defaults in attach() */\r\n protected config!: TConfig;\r\n\r\n /** User-provided configuration from constructor */\r\n private readonly userConfig: Partial<TConfig>;\r\n\r\n /**\r\n * Default configuration - subclasses should override this getter.\r\n * Note: This must be a getter (not property initializer) for proper inheritance\r\n * since property initializers run after parent constructor.\r\n */\r\n protected get defaultConfig(): Partial<TConfig> {\r\n return {};\r\n }\r\n\r\n constructor(config: Partial<TConfig> = {}) {\r\n this.userConfig = config;\r\n }\r\n\r\n /**\r\n * Called when the plugin is attached to a grid.\r\n * Override to set up event listeners, initialize state, etc.\r\n */\r\n attach(grid: GridElement): void {\r\n this.grid = grid;\r\n // Merge config here (after subclass construction is complete)\r\n this.config = { ...this.defaultConfig, ...this.userConfig } as TConfig;\r\n }\r\n\r\n /**\r\n * Called when the plugin is detached from a grid.\r\n * Override to clean up event listeners, timers, etc.\r\n */\r\n detach(): void {\r\n // Override in subclass\r\n }\r\n\r\n /**\r\n * Get another plugin instance from the same grid.\r\n * Use for inter-plugin communication.\r\n */\r\n protected getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\r\n return this.grid?.getPlugin(PluginClass);\r\n }\r\n\r\n /**\r\n * Emit a custom event from the grid.\r\n */\r\n protected emit<T>(eventName: string, detail: T): void {\r\n this.grid?.dispatchEvent?.(new CustomEvent(eventName, { detail, bubbles: true }));\r\n }\r\n\r\n /**\r\n * Request a re-render of the grid.\r\n */\r\n protected requestRender(): void {\r\n this.grid?.requestRender?.();\r\n }\r\n\r\n /**\r\n * Request a lightweight style update without rebuilding DOM.\r\n * Use this instead of requestRender() when only CSS classes need updating.\r\n */\r\n protected requestAfterRender(): void {\r\n this.grid?.requestAfterRender?.();\r\n }\r\n\r\n /**\r\n * Get the current rows from the grid.\r\n */\r\n protected get rows(): any[] {\r\n return this.grid?.rows ?? [];\r\n }\r\n\r\n /**\r\n * Get the original unfiltered/unprocessed rows from the grid.\r\n * Use this when you need all source data regardless of active filters.\r\n */\r\n protected get sourceRows(): any[] {\r\n return (this.grid as any)?.sourceRows ?? [];\r\n }\r\n\r\n /**\r\n * Get the current columns from the grid.\r\n */\r\n protected get columns(): ColumnConfig[] {\r\n return this.grid?.columns ?? [];\r\n }\r\n\r\n /**\r\n * Get only visible columns from the grid (excludes hidden).\r\n * Use this for rendering that needs to match the grid template.\r\n */\r\n protected get visibleColumns(): ColumnConfig[] {\r\n return (this.grid as any)?.visibleColumns ?? [];\r\n }\r\n\r\n /**\r\n * Get the shadow root of the grid.\r\n */\r\n protected get shadowRoot(): ShadowRoot | null {\r\n return this.grid?.shadowRoot ?? null;\r\n }\r\n\r\n /**\r\n * Log a warning message.\r\n */\r\n protected warn(message: string): void {\r\n console.warn(`[tbw-grid:${this.name}] ${message}`);\r\n }\r\n\r\n // ===== Lifecycle Hooks (override as needed) =====\r\n\r\n /**\r\n * Transform rows before rendering.\r\n * Called during each render cycle before rows are rendered to the DOM.\r\n * Use this to filter, sort, or add computed properties to rows.\r\n *\r\n * @param rows - The current rows array (readonly to encourage returning a new array)\r\n * @returns The modified rows array to render\r\n *\r\n * @example\r\n * ```ts\r\n * processRows(rows: readonly any[]): any[] {\r\n * // Filter out hidden rows\r\n * return rows.filter(row => !row._hidden);\r\n * }\r\n * ```\r\n *\r\n * @example\r\n * ```ts\r\n * processRows(rows: readonly any[]): any[] {\r\n * // Add computed properties\r\n * return rows.map(row => ({\r\n * ...row,\r\n * _fullName: `${row.firstName} ${row.lastName}`\r\n * }));\r\n * }\r\n * ```\r\n */\r\n processRows?(rows: readonly any[]): any[];\r\n\r\n /**\r\n * Transform columns before rendering.\r\n * Called during each render cycle before column headers and cells are rendered.\r\n * Use this to add, remove, or modify column definitions.\r\n *\r\n * @param columns - The current columns array (readonly to encourage returning a new array)\r\n * @returns The modified columns array to render\r\n *\r\n * @example\r\n * ```ts\r\n * processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\r\n * // Add a selection checkbox column\r\n * return [\r\n * { field: '_select', header: '', width: 40 },\r\n * ...columns\r\n * ];\r\n * }\r\n * ```\r\n */\r\n processColumns?(columns: readonly ColumnConfig[]): ColumnConfig[];\r\n\r\n /**\r\n * Called before each render cycle begins.\r\n * Use this to prepare state or cache values needed during rendering.\r\n *\r\n * @example\r\n * ```ts\r\n * beforeRender(): void {\r\n * this.visibleRowCount = this.calculateVisibleRows();\r\n * }\r\n * ```\r\n */\r\n beforeRender?(): void;\r\n\r\n /**\r\n * Called after each render cycle completes.\r\n * Use this for DOM manipulation, adding event listeners to rendered elements,\r\n * or applying visual effects like selection highlights.\r\n *\r\n * @example\r\n * ```ts\r\n * afterRender(): void {\r\n * // Apply selection styling to rendered rows\r\n * const rows = this.shadowRoot?.querySelectorAll('.data-row');\r\n * rows?.forEach((row, i) => {\r\n * row.classList.toggle('selected', this.selectedRows.has(i));\r\n * });\r\n * }\r\n * ```\r\n */\r\n afterRender?(): void;\r\n\r\n /**\r\n * Render a custom row, bypassing the default row rendering.\r\n * Use this for special row types like group headers, detail rows, or footers.\r\n *\r\n * @param row - The row data object\r\n * @param rowEl - The row DOM element to render into\r\n * @param rowIndex - The index of the row in the data array\r\n * @returns `true` if the plugin handled rendering (prevents default), `false`/`void` for default rendering\r\n *\r\n * @example\r\n * ```ts\r\n * renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void {\r\n * if (row._isGroupHeader) {\r\n * rowEl.innerHTML = `<div class=\"group-header\">${row._groupLabel}</div>`;\r\n * return true; // Handled - skip default rendering\r\n * }\r\n * // Return void to let default rendering proceed\r\n * }\r\n * ```\r\n */\r\n renderRow?(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void;\r\n\r\n // ===== Interaction Hooks (override as needed) =====\r\n\r\n /**\r\n * Handle keyboard events on the grid.\r\n * Called when a key is pressed while the grid or a cell has focus.\r\n *\r\n * @param event - The native KeyboardEvent\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onKeyDown(event: KeyboardEvent): boolean | void {\r\n * // Handle Ctrl+A for select all\r\n * if (event.ctrlKey && event.key === 'a') {\r\n * this.selectAllRows();\r\n * return true; // Prevent default browser select-all\r\n * }\r\n * }\r\n * ```\r\n */\r\n onKeyDown?(event: KeyboardEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell click events.\r\n * Called when a data cell is clicked (not headers).\r\n *\r\n * @param event - Cell click event with row/column context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onCellClick(event: CellClickEvent): boolean | void {\r\n * if (event.field === '_select') {\r\n * this.toggleRowSelection(event.rowIndex);\r\n * return true; // Handled\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellClick?(event: CellClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle row click events.\r\n * Called when any part of a data row is clicked.\r\n * Note: This is called in addition to onCellClick, not instead of.\r\n *\r\n * @param event - Row click event with row context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onRowClick(event: RowClickEvent): boolean | void {\r\n * if (this.config.mode === 'row') {\r\n * this.selectRow(event.rowIndex, event.originalEvent);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onRowClick?(event: RowClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle header click events.\r\n * Called when a column header is clicked. Commonly used for sorting.\r\n *\r\n * @param event - Header click event with column context\r\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\r\n *\r\n * @example\r\n * ```ts\r\n * onHeaderClick(event: HeaderClickEvent): boolean | void {\r\n * if (event.column.sortable !== false) {\r\n * this.toggleSort(event.field);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onHeaderClick?(event: HeaderClickEvent): boolean | void;\r\n\r\n /**\r\n * Handle scroll events on the grid viewport.\r\n * Called during scrolling. Note: This may be called frequently; debounce if needed.\r\n *\r\n * @param event - Scroll event with scroll position and viewport dimensions\r\n *\r\n * @example\r\n * ```ts\r\n * onScroll(event: ScrollEvent): void {\r\n * // Update sticky column positions\r\n * this.updateStickyPositions(event.scrollLeft);\r\n * }\r\n * ```\r\n */\r\n onScroll?(event: ScrollEvent): void;\r\n\r\n /**\r\n * Handle cell mousedown events.\r\n * Used for initiating drag operations like range selection or column resize.\r\n *\r\n * @param event - Mouse event with cell context\r\n * @returns `true` to indicate drag started (prevents text selection), `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseDown(event: CellMouseEvent): boolean | void {\r\n * if (event.rowIndex !== undefined && this.config.mode === 'range') {\r\n * this.startDragSelection(event.rowIndex, event.colIndex);\r\n * return true; // Prevent text selection\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseDown?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell mousemove events during drag operations.\r\n * Only called when a drag is in progress (after mousedown returned true).\r\n *\r\n * @param event - Mouse event with current cell context\r\n * @returns `true` to continue handling the drag, `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseMove(event: CellMouseEvent): boolean | void {\r\n * if (this.isDragging && event.rowIndex !== undefined) {\r\n * this.extendSelection(event.rowIndex, event.colIndex);\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseMove?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Handle cell mouseup events to end drag operations.\r\n *\r\n * @param event - Mouse event with final cell context\r\n * @returns `true` if drag was finalized, `false`/`void` otherwise\r\n *\r\n * @example\r\n * ```ts\r\n * onCellMouseUp(event: CellMouseEvent): boolean | void {\r\n * if (this.isDragging) {\r\n * this.finalizeDragSelection();\r\n * this.isDragging = false;\r\n * return true;\r\n * }\r\n * }\r\n * ```\r\n */\r\n onCellMouseUp?(event: CellMouseEvent): boolean | void;\r\n\r\n /**\r\n * Provide context menu items when right-clicking on the grid.\r\n * Multiple plugins can contribute items; they are merged into a single menu.\r\n *\r\n * @param params - Context about where the menu was triggered (row, column, etc.)\r\n * @returns Array of menu items to display\r\n *\r\n * @example\r\n * ```ts\r\n * getContextMenuItems(params: ContextMenuParams): ContextMenuItem[] {\r\n * if (params.isHeader) {\r\n * return [\r\n * { id: 'sort-asc', label: 'Sort Ascending', action: () => this.sortAsc(params.field) },\r\n * { id: 'sort-desc', label: 'Sort Descending', action: () => this.sortDesc(params.field) },\r\n * ];\r\n * }\r\n * return [\r\n * { id: 'copy', label: 'Copy Cell', action: () => this.copyCell(params) },\r\n * ];\r\n * }\r\n * ```\r\n */\r\n getContextMenuItems?(params: ContextMenuParams): ContextMenuItem[];\r\n\r\n // ===== Column State Hooks (override as needed) =====\r\n\r\n /**\r\n * Contribute plugin-specific state for a column.\r\n * Called by the grid when collecting column state for serialization.\r\n * Plugins can add their own properties to the column state.\r\n *\r\n * @param field - The field name of the column\r\n * @returns Partial column state with plugin-specific properties, or undefined if no state to contribute\r\n *\r\n * @example\r\n * ```ts\r\n * getColumnState(field: string): Partial<ColumnState> | undefined {\r\n * const filterModel = this.filterModels.get(field);\r\n * if (filterModel) {\r\n * // Uses module augmentation to add filter property to ColumnState\r\n * return { filter: filterModel } as Partial<ColumnState>;\r\n * }\r\n * return undefined;\r\n * }\r\n * ```\r\n */\r\n getColumnState?(field: string): Partial<ColumnState> | undefined;\r\n\r\n /**\r\n * Apply plugin-specific state to a column.\r\n * Called by the grid when restoring column state from serialized data.\r\n * Plugins should restore their internal state based on the provided state.\r\n *\r\n * @param field - The field name of the column\r\n * @param state - The column state to apply (may contain plugin-specific properties)\r\n *\r\n * @example\r\n * ```ts\r\n * applyColumnState(field: string, state: ColumnState): void {\r\n * // Check for filter property added via module augmentation\r\n * const filter = (state as any).filter;\r\n * if (filter) {\r\n * this.filterModels.set(field, filter);\r\n * this.applyFilter();\r\n * }\r\n * }\r\n * ```\r\n */\r\n applyColumnState?(field: string, state: ColumnState): void;\r\n\r\n // ===== Shell Integration Hooks (override as needed) =====\r\n\r\n /**\r\n * Register a tool panel for this plugin.\r\n * Return undefined if plugin has no tool panel.\r\n * The shell will create a toolbar toggle button and render the panel content\r\n * when the user opens the panel.\r\n *\r\n * @returns Tool panel definition, or undefined if plugin has no panel\r\n *\r\n * @example\r\n * ```ts\r\n * getToolPanel(): ToolPanelDefinition | undefined {\r\n * return {\r\n * id: 'columns',\r\n * title: 'Columns',\r\n * icon: '☰',\r\n * tooltip: 'Show/hide columns',\r\n * order: 10,\r\n * render: (container) => {\r\n * this.renderColumnList(container);\r\n * return () => this.cleanup();\r\n * },\r\n * };\r\n * }\r\n * ```\r\n */\r\n getToolPanel?(): ToolPanelDefinition | undefined;\r\n\r\n /**\r\n * Register content for the shell header center section.\r\n * Return undefined if plugin has no header content.\r\n * Examples: search input, selection summary, status indicators.\r\n *\r\n * @returns Header content definition, or undefined if plugin has no header content\r\n *\r\n * @example\r\n * ```ts\r\n * getHeaderContent(): HeaderContentDefinition | undefined {\r\n * return {\r\n * id: 'quick-filter',\r\n * order: 10,\r\n * render: (container) => {\r\n * const input = document.createElement('input');\r\n * input.type = 'text';\r\n * input.placeholder = 'Search...';\r\n * input.addEventListener('input', this.handleInput);\r\n * container.appendChild(input);\r\n * return () => input.removeEventListener('input', this.handleInput);\r\n * },\r\n * };\r\n * }\r\n * ```\r\n */\r\n getHeaderContent?(): HeaderContentDefinition | undefined;\r\n}\r\n","/**\n * Cell Range Selection Core Logic\n *\n * Pure functions for cell range selection operations.\n */\n\nimport type { InternalCellRange, CellRange } from './types';\n\n/**\n * Normalize a range so startRow/startCol are always <= endRow/endCol.\n * This handles cases where user drags from bottom-right to top-left.\n *\n * @param range - The range to normalize\n * @returns Normalized range with start <= end for both dimensions\n */\nexport function normalizeRange(range: InternalCellRange): InternalCellRange {\n return {\n startRow: Math.min(range.startRow, range.endRow),\n startCol: Math.min(range.startCol, range.endCol),\n endRow: Math.max(range.startRow, range.endRow),\n endCol: Math.max(range.startCol, range.endCol),\n };\n}\n\n/**\n * Convert an internal range to the public event format.\n *\n * @param range - The internal range to convert\n * @returns Public CellRange format with from/to coordinates\n */\nexport function toPublicRange(range: InternalCellRange): CellRange {\n const normalized = normalizeRange(range);\n return {\n from: { row: normalized.startRow, col: normalized.startCol },\n to: { row: normalized.endRow, col: normalized.endCol },\n };\n}\n\n/**\n * Convert multiple internal ranges to public format.\n *\n * @param ranges - Array of internal ranges\n * @returns Array of public CellRange format\n */\nexport function toPublicRanges(ranges: InternalCellRange[]): CellRange[] {\n return ranges.map(toPublicRange);\n}\n\n/**\n * Check if a cell is within a specific range.\n *\n * @param row - The row index to check\n * @param col - The column index to check\n * @param range - The range to check against\n * @returns True if the cell is within the range\n */\nexport function isCellInRange(row: number, col: number, range: InternalCellRange): boolean {\n const normalized = normalizeRange(range);\n return (\n row >= normalized.startRow && row <= normalized.endRow && col >= normalized.startCol && col <= normalized.endCol\n );\n}\n\n/**\n * Check if a cell is within any of the provided ranges.\n *\n * @param row - The row index to check\n * @param col - The column index to check\n * @param ranges - Array of ranges to check against\n * @returns True if the cell is within any range\n */\nexport function isCellInAnyRange(row: number, col: number, ranges: InternalCellRange[]): boolean {\n return ranges.some((range) => isCellInRange(row, col, range));\n}\n\n/**\n * Get all cells within a range as an array of {row, col} objects.\n *\n * @param range - The range to enumerate\n * @returns Array of all cell coordinates in the range\n */\nexport function getCellsInRange(range: InternalCellRange): Array<{ row: number; col: number }> {\n const cells: Array<{ row: number; col: number }> = [];\n const normalized = normalizeRange(range);\n\n for (let row = normalized.startRow; row <= normalized.endRow; row++) {\n for (let col = normalized.startCol; col <= normalized.endCol; col++) {\n cells.push({ row, col });\n }\n }\n\n return cells;\n}\n\n/**\n * Get all unique cells across multiple ranges.\n * Deduplicates cells that appear in overlapping ranges.\n *\n * @param ranges - Array of ranges to enumerate\n * @returns Array of unique cell coordinates\n */\nexport function getAllCellsInRanges(ranges: InternalCellRange[]): Array<{ row: number; col: number }> {\n const cellMap = new Map<string, { row: number; col: number }>();\n\n for (const range of ranges) {\n for (const cell of getCellsInRange(range)) {\n cellMap.set(`${cell.row},${cell.col}`, cell);\n }\n }\n\n return [...cellMap.values()];\n}\n\n/**\n * Merge overlapping or adjacent ranges into fewer ranges.\n * Simple implementation - returns ranges as-is for now.\n * More complex merging logic can be added later for optimization.\n *\n * @param ranges - Array of ranges to merge\n * @returns Merged array of ranges\n */\nexport function mergeRanges(ranges: InternalCellRange[]): InternalCellRange[] {\n // Simple implementation - more complex merging can be added later\n return ranges;\n}\n\n/**\n * Create a range from an anchor cell to a current cell position.\n * The range is not normalized - it preserves the direction of selection.\n *\n * @param anchor - The anchor cell (where selection started)\n * @param current - The current cell (where selection ends)\n * @returns An InternalCellRange from anchor to current\n */\nexport function createRangeFromAnchor(\n anchor: { row: number; col: number },\n current: { row: number; col: number }\n): InternalCellRange {\n return {\n startRow: anchor.row,\n startCol: anchor.col,\n endRow: current.row,\n endCol: current.col,\n };\n}\n\n/**\n * Calculate the number of cells in a range.\n *\n * @param range - The range to measure\n * @returns Total number of cells in the range\n */\nexport function getRangeCellCount(range: InternalCellRange): number {\n const normalized = normalizeRange(range);\n const rowCount = normalized.endRow - normalized.startRow + 1;\n const colCount = normalized.endCol - normalized.startCol + 1;\n return rowCount * colCount;\n}\n\n/**\n * Check if two ranges are equal (same boundaries).\n *\n * @param a - First range\n * @param b - Second range\n * @returns True if ranges have same boundaries after normalization\n */\nexport function rangesEqual(a: InternalCellRange, b: InternalCellRange): boolean {\n const normA = normalizeRange(a);\n const normB = normalizeRange(b);\n return (\n normA.startRow === normB.startRow &&\n normA.startCol === normB.startCol &&\n normA.endRow === normB.endRow &&\n normA.endCol === normB.endCol\n );\n}\n\n/**\n * Check if a range is a single cell (1x1).\n *\n * @param range - The range to check\n * @returns True if the range is exactly one cell\n */\nexport function isSingleCell(range: InternalCellRange): boolean {\n const normalized = normalizeRange(range);\n return normalized.startRow === normalized.endRow && normalized.startCol === normalized.endCol;\n}\n","/**\n * Selection Plugin (Class-based)\n *\n * Provides selection functionality for tbw-grid.\n * Supports three modes:\n * - 'cell': Single cell selection (default). No border, just focus highlight.\n * - 'row': Row selection. Clicking a cell selects the entire row.\n * - 'range': Range selection. Shift+click or drag to select rectangular cell ranges.\n */\n\nimport { BaseGridPlugin, CellClickEvent, CellMouseEvent } from '../../core/plugin/base-plugin';\nimport {\n createRangeFromAnchor,\n getAllCellsInRanges,\n isCellInAnyRange,\n normalizeRange,\n toPublicRanges,\n} from './range-selection';\nimport type { CellRange, InternalCellRange, SelectionChangeDetail, SelectionConfig, SelectionMode } from './types';\n\n/**\n * Build the selection change event detail for the current state.\n */\nfunction buildSelectionEvent(\n mode: SelectionMode,\n state: {\n selectedCell: { row: number; col: number } | null;\n selected: Set<number>;\n ranges: InternalCellRange[];\n },\n colCount: number\n): SelectionChangeDetail {\n if (mode === 'cell' && state.selectedCell) {\n return {\n mode,\n ranges: [\n {\n from: { row: state.selectedCell.row, col: state.selectedCell.col },\n to: { row: state.selectedCell.row, col: state.selectedCell.col },\n },\n ],\n };\n }\n\n if (mode === 'row' && state.selected.size > 0) {\n const ranges = [...state.selected].map((rowIndex) => ({\n from: { row: rowIndex, col: 0 },\n to: { row: rowIndex, col: colCount - 1 },\n }));\n return { mode, ranges };\n }\n\n if (mode === 'range' && state.ranges.length > 0) {\n return { mode, ranges: toPublicRanges(state.ranges) };\n }\n\n return { mode, ranges: [] };\n}\n\n/**\n * Selection Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new SelectionPlugin({ mode: 'range' })\n * ```\n */\nexport class SelectionPlugin extends BaseGridPlugin<SelectionConfig> {\n readonly name = 'selection';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<SelectionConfig> {\n return {\n mode: 'cell',\n };\n }\n\n // ===== Internal State =====\n /** Row selection state (row mode) */\n private selected = new Set<number>();\n private lastSelected: number | null = null;\n private anchor: number | null = null;\n\n /** Range selection state (range mode) */\n private ranges: InternalCellRange[] = [];\n private activeRange: InternalCellRange | null = null;\n private cellAnchor: { row: number; col: number } | null = null;\n private isDragging = false;\n\n /** Cell selection state (cell mode) */\n private selectedCell: { row: number; col: number } | null = null;\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.selected.clear();\n this.ranges = [];\n this.activeRange = null;\n this.cellAnchor = null;\n this.isDragging = false;\n this.selectedCell = null;\n }\n\n // ===== Event Handlers =====\n\n override onCellClick(event: CellClickEvent): boolean {\n const { rowIndex, colIndex, originalEvent } = event;\n const { mode } = this.config;\n\n // ===== CELL MODE: Single cell selection =====\n if (mode === 'cell') {\n this.selectedCell = { row: rowIndex, col: colIndex };\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n return false;\n }\n\n // ===== ROW MODE: Select entire row =====\n if (mode === 'row') {\n this.selected.clear();\n this.selected.add(rowIndex);\n this.lastSelected = rowIndex;\n\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n return false;\n }\n\n // ===== RANGE MODE: Shift+click extends selection, click starts new =====\n if (mode === 'range') {\n const shiftKey = originalEvent.shiftKey;\n const ctrlKey = originalEvent.ctrlKey || originalEvent.metaKey;\n\n if (shiftKey && this.cellAnchor) {\n // Extend selection from anchor\n const newRange = createRangeFromAnchor(this.cellAnchor, { row: rowIndex, col: colIndex });\n\n if (ctrlKey) {\n if (this.ranges.length > 0) {\n this.ranges[this.ranges.length - 1] = newRange;\n } else {\n this.ranges.push(newRange);\n }\n } else {\n this.ranges = [newRange];\n }\n this.activeRange = newRange;\n } else if (ctrlKey) {\n const newRange: InternalCellRange = {\n startRow: rowIndex,\n startCol: colIndex,\n endRow: rowIndex,\n endCol: colIndex,\n };\n this.ranges.push(newRange);\n this.activeRange = newRange;\n this.cellAnchor = { row: rowIndex, col: colIndex };\n } else {\n const newRange: InternalCellRange = {\n startRow: rowIndex,\n startCol: colIndex,\n endRow: rowIndex,\n endCol: colIndex,\n };\n this.ranges = [newRange];\n this.activeRange = newRange;\n this.cellAnchor = { row: rowIndex, col: colIndex };\n }\n\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n\n this.requestAfterRender();\n return false;\n }\n\n return false;\n }\n\n override onKeyDown(event: KeyboardEvent): boolean {\n const { mode } = this.config;\n\n // Escape clears selection in all modes\n if (event.key === 'Escape') {\n if (mode === 'cell') {\n this.selectedCell = null;\n } else if (mode === 'row') {\n this.selected.clear();\n this.anchor = null;\n } else if (mode === 'range') {\n this.ranges = [];\n this.activeRange = null;\n this.cellAnchor = null;\n }\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n return true;\n }\n\n // Ctrl+A selects all in range mode\n if (mode === 'range' && event.key === 'a' && (event.ctrlKey || event.metaKey)) {\n const rowCount = this.rows.length;\n const colCount = this.columns.length;\n if (rowCount > 0 && colCount > 0) {\n const allRange: InternalCellRange = {\n startRow: 0,\n startCol: 0,\n endRow: rowCount - 1,\n endCol: colCount - 1,\n };\n this.ranges = [allRange];\n this.activeRange = allRange;\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n return true;\n }\n }\n\n return false;\n }\n\n override onCellMouseDown(event: CellMouseEvent): boolean | void {\n if (this.config.mode !== 'range') return;\n if (event.rowIndex === undefined || event.colIndex === undefined) return;\n if (event.rowIndex < 0) return; // Header\n\n // Let onCellClick handle shift+click for range extension\n if (event.originalEvent.shiftKey && this.cellAnchor) {\n return;\n }\n\n // Start drag selection\n this.isDragging = true;\n const rowIndex = event.rowIndex;\n const colIndex = event.colIndex;\n this.cellAnchor = { row: rowIndex, col: colIndex };\n\n const ctrlKey = event.originalEvent.ctrlKey || event.originalEvent.metaKey;\n if (!ctrlKey) {\n this.ranges = [];\n }\n\n const newRange: InternalCellRange = {\n startRow: rowIndex,\n startCol: colIndex,\n endRow: rowIndex,\n endCol: colIndex,\n };\n this.ranges.push(newRange);\n this.activeRange = newRange;\n\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n return true;\n }\n\n override onCellMouseMove(event: CellMouseEvent): boolean | void {\n if (this.config.mode !== 'range') return;\n if (!this.isDragging || !this.cellAnchor) return;\n if (event.rowIndex === undefined || event.colIndex === undefined) return;\n if (event.rowIndex < 0) return;\n\n const newRange = createRangeFromAnchor(this.cellAnchor, { row: event.rowIndex, col: event.colIndex });\n\n if (this.ranges.length > 0) {\n this.ranges[this.ranges.length - 1] = newRange;\n } else {\n this.ranges.push(newRange);\n }\n this.activeRange = newRange;\n\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n return true;\n }\n\n override onCellMouseUp(_event: CellMouseEvent): boolean | void {\n if (this.config.mode !== 'range') return;\n if (this.isDragging) {\n this.isDragging = false;\n return true;\n }\n }\n\n override afterRender(): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const container = shadowRoot.children[0];\n const { mode } = this.config;\n\n // Set data attribute on host for CSS variable scoping\n (this.grid as unknown as Element).setAttribute('data-selection-mode', mode);\n\n // Toggle .selecting class during drag to prevent text selection\n if (container) {\n container.classList.toggle('selecting', this.isDragging);\n }\n\n // Clear all selection classes first\n const allCells = shadowRoot.querySelectorAll('.cell');\n allCells.forEach((cell) => {\n cell.classList.remove('selected', 'top', 'bottom', 'first', 'last');\n });\n\n const allRows = shadowRoot.querySelectorAll('.data-grid-row');\n allRows.forEach((row) => {\n row.classList.remove('selected');\n });\n\n // ===== ROW MODE: Add row-focus class to selected rows =====\n if (mode === 'row') {\n allRows.forEach((row) => row.classList.remove('row-focus'));\n\n allRows.forEach((row) => {\n const firstCell = row.querySelector('.cell[data-row]');\n const rowIndex = parseInt(firstCell?.getAttribute('data-row') ?? '-1', 10);\n if (rowIndex >= 0 && this.selected.has(rowIndex)) {\n row.classList.add('selected', 'row-focus');\n }\n });\n }\n\n // ===== RANGE MODE: Add selected and edge classes to cells =====\n if (mode === 'range' && this.ranges.length > 0) {\n const normalized = this.activeRange ? normalizeRange(this.activeRange) : null;\n\n const cells = shadowRoot.querySelectorAll('.cell[data-row][data-col]');\n cells.forEach((cell) => {\n const rowIndex = parseInt(cell.getAttribute('data-row') ?? '-1', 10);\n const colIndex = parseInt(cell.getAttribute('data-col') ?? '-1', 10);\n if (rowIndex >= 0 && colIndex >= 0) {\n const inRange = isCellInAnyRange(rowIndex, colIndex, this.ranges);\n\n if (inRange) {\n cell.classList.add('selected');\n\n if (normalized) {\n if (rowIndex === normalized.startRow) cell.classList.add('top');\n if (rowIndex === normalized.endRow) cell.classList.add('bottom');\n if (colIndex === normalized.startCol) cell.classList.add('first');\n if (colIndex === normalized.endCol) cell.classList.add('last');\n }\n }\n }\n });\n }\n }\n\n // ===== Public API =====\n\n /**\n * Get the selected cell (cell mode only).\n */\n getSelectedCell(): { row: number; col: number } | null {\n return this.selectedCell;\n }\n\n /**\n * Get all selected row indices (row mode).\n */\n getSelectedRows(): number[] {\n return [...this.selected];\n }\n\n /**\n * Get all selected cell ranges in public format.\n */\n getRanges(): CellRange[] {\n return toPublicRanges(this.ranges);\n }\n\n /**\n * Get all selected cells across all ranges.\n */\n getSelectedCells(): Array<{ row: number; col: number }> {\n return getAllCellsInRanges(this.ranges);\n }\n\n /**\n * Check if a specific cell is in range selection.\n */\n isCellSelected(row: number, col: number): boolean {\n return isCellInAnyRange(row, col, this.ranges);\n }\n\n /**\n * Clear all selection.\n */\n clearSelection(): void {\n this.selectedCell = null;\n this.selected.clear();\n this.anchor = null;\n this.ranges = [];\n this.activeRange = null;\n this.cellAnchor = null;\n this.emit<SelectionChangeDetail>('selection-change', { mode: this.config.mode, ranges: [] });\n this.requestAfterRender();\n }\n\n /**\n * Set selected ranges programmatically.\n */\n setRanges(ranges: CellRange[]): void {\n this.ranges = ranges.map((r) => ({\n startRow: r.from.row,\n startCol: r.from.col,\n endRow: r.to.row,\n endCol: r.to.col,\n }));\n this.activeRange = this.ranges.length > 0 ? this.ranges[this.ranges.length - 1] : null;\n this.emit<SelectionChangeDetail>('selection-change', {\n mode: this.config.mode,\n ranges: toPublicRanges(this.ranges),\n });\n this.requestAfterRender();\n }\n\n // ===== Private Helpers =====\n\n #buildEvent(): SelectionChangeDetail {\n return buildSelectionEvent(\n this.config.mode,\n {\n selectedCell: this.selectedCell,\n selected: this.selected,\n ranges: this.ranges,\n },\n this.columns.length\n );\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n /* Prevent text selection during range drag */\n :host .selecting .data-grid-row > .cell {\n user-select: none;\n }\n\n /* Row selection - use accent color for row focus */\n :host .data-grid-row.row-focus {\n background-color: var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%));\n }\n\n /* Disable cell-focus outline in row mode - row is the focus unit */\n :host([data-selection-mode=\"row\"]) .cell-focus {\n outline: none;\n }\n\n /* Selection cell styles - for range mode */\n :host .data-grid-row > .cell.selected {\n background-color: var(--tbw-range-selection-bg);\n }\n :host .data-grid-row > .cell.selected.top {\n border-top: 2px solid var(--tbw-range-border-color);\n }\n :host .data-grid-row > .cell.selected.bottom {\n border-bottom: 2px solid var(--tbw-range-border-color);\n }\n :host .data-grid-row > .cell.selected.first {\n border-left: 2px solid var(--tbw-range-border-color);\n }\n :host .data-grid-row > .cell.selected.last {\n border-right: 2px solid var(--tbw-range-border-color);\n }\n `;\n}\n","/**\n * Core Tree Data Logic\n *\n * Pure functions for tree flattening, expansion, and traversal.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// The tree plugin intentionally uses `any` for maximum flexibility with user-defined row types.\n\nimport type { FlattenedTreeRow, TreeConfig } from './types';\n\n/**\n * Generates a unique key for a row.\n * Uses row.id if available, otherwise generates from path.\n */\nexport function generateRowKey(row: any, index: number, parentKey: string | null): string {\n if (row.id !== undefined) return String(row.id);\n return parentKey ? `${parentKey}-${index}` : String(index);\n}\n\n/**\n * Flattens a hierarchical tree into a flat array of rows with metadata.\n * Only includes children of expanded nodes.\n */\nexport function flattenTree(\n rows: any[],\n config: TreeConfig,\n expandedKeys: Set<string>,\n parentKey: string | null = null,\n depth = 0\n): FlattenedTreeRow[] {\n const childrenField = config.childrenField ?? 'children';\n const result: FlattenedTreeRow[] = [];\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n const key = generateRowKey(row, i, parentKey);\n const children = row[childrenField];\n const hasChildren = Array.isArray(children) && children.length > 0;\n const isExpanded = expandedKeys.has(key);\n\n result.push({\n key,\n data: row,\n depth,\n hasChildren,\n isExpanded,\n parentKey,\n });\n\n // Recursively add children if expanded\n if (hasChildren && isExpanded) {\n const childRows = flattenTree(children, config, expandedKeys, key, depth + 1);\n result.push(...childRows);\n }\n }\n\n return result;\n}\n\n/**\n * Toggles the expansion state of a row.\n * Returns a new Set with the toggled state.\n */\nexport function toggleExpand(expandedKeys: Set<string>, key: string): Set<string> {\n const newExpanded = new Set(expandedKeys);\n if (newExpanded.has(key)) {\n newExpanded.delete(key);\n } else {\n newExpanded.add(key);\n }\n return newExpanded;\n}\n\n/**\n * Expands all nodes in the tree.\n * Returns a Set of all parent row keys.\n */\nexport function expandAll(rows: any[], config: TreeConfig, parentKey: string | null = null, depth = 0): Set<string> {\n const childrenField = config.childrenField ?? 'children';\n const keys = new Set<string>();\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n const key = generateRowKey(row, i, parentKey);\n const children = row[childrenField];\n\n if (Array.isArray(children) && children.length > 0) {\n keys.add(key);\n const childKeys = expandAll(children, config, key, depth + 1);\n for (const k of childKeys) keys.add(k);\n }\n }\n\n return keys;\n}\n\n/**\n * Collapses all nodes.\n * Returns an empty Set.\n */\nexport function collapseAll(): Set<string> {\n return new Set();\n}\n\n/**\n * Gets all descendants of a node from the flattened row list.\n * Useful for operations that need to affect an entire subtree.\n */\nexport function getDescendants(flattenedRows: FlattenedTreeRow[], parentKey: string): FlattenedTreeRow[] {\n const descendants: FlattenedTreeRow[] = [];\n let collecting = false;\n let parentDepth = -1;\n\n for (const row of flattenedRows) {\n if (row.key === parentKey) {\n collecting = true;\n parentDepth = row.depth;\n continue;\n }\n\n if (collecting) {\n if (row.depth > parentDepth) {\n descendants.push(row);\n } else {\n break; // No longer a descendant\n }\n }\n }\n\n return descendants;\n}\n\n/**\n * Finds the path from root to a specific row key.\n * Returns an array of keys from root to the target (inclusive).\n */\nexport function getPathToKey(\n rows: any[],\n targetKey: string,\n config: TreeConfig,\n parentKey: string | null = null,\n depth = 0\n): string[] | null {\n const childrenField = config.childrenField ?? 'children';\n\n for (let i = 0; i < rows.length; i++) {\n const row = rows[i];\n const key = generateRowKey(row, i, parentKey);\n\n if (key === targetKey) {\n return [key];\n }\n\n const children = row[childrenField];\n if (Array.isArray(children) && children.length > 0) {\n const childPath = getPathToKey(children, targetKey, config, key, depth + 1);\n if (childPath) {\n return [key, ...childPath];\n }\n }\n }\n\n return null;\n}\n\n/**\n * Expands all ancestors of a specific row to make it visible.\n * Returns a new Set with the required keys added.\n */\nexport function expandToKey(\n rows: any[],\n targetKey: string,\n config: TreeConfig,\n existingExpanded: Set<string>\n): Set<string> {\n const path = getPathToKey(rows, targetKey, config);\n if (!path) return existingExpanded;\n\n const newExpanded = new Set(existingExpanded);\n // Add all keys except the last one (the target itself)\n for (let i = 0; i < path.length - 1; i++) {\n newExpanded.add(path[i]);\n }\n return newExpanded;\n}\n","/**\n * Tree Structure Auto-Detection\n *\n * Utilities for detecting hierarchical tree data structures.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// The tree plugin intentionally uses `any` for maximum flexibility with user-defined row types.\n\n/**\n * Detects if the data has a tree structure by checking for children arrays.\n */\nexport function detectTreeStructure(rows: any[], childrenField = 'children'): boolean {\n if (!Array.isArray(rows) || rows.length === 0) return false;\n\n // Check if any row has a non-empty children array\n for (const row of rows) {\n if (row && Array.isArray(row[childrenField]) && row[childrenField].length > 0) {\n return true;\n }\n }\n\n return false;\n}\n\n/**\n * Attempts to infer the children field name from common patterns.\n * Returns the first field that contains an array with items.\n */\nexport function inferChildrenField(rows: any[]): string | null {\n if (!Array.isArray(rows) || rows.length === 0) return null;\n\n const commonArrayFields = ['children', 'items', 'nodes', 'subRows', 'nested'];\n\n for (const row of rows) {\n if (!row || typeof row !== 'object') continue;\n\n for (const field of commonArrayFields) {\n if (Array.isArray(row[field]) && row[field].length > 0) {\n return field;\n }\n }\n }\n\n return null;\n}\n\n/**\n * Calculates the maximum depth of the tree.\n * Useful for layout calculations and virtualization.\n */\nexport function getMaxDepth(rows: any[], childrenField = 'children', currentDepth = 0): number {\n if (!Array.isArray(rows) || rows.length === 0) return currentDepth;\n\n let maxDepth = currentDepth;\n\n for (const row of rows) {\n if (!row) continue;\n const children = row[childrenField];\n if (Array.isArray(children) && children.length > 0) {\n const childDepth = getMaxDepth(children, childrenField, currentDepth + 1);\n if (childDepth > maxDepth) {\n maxDepth = childDepth;\n }\n }\n }\n\n return maxDepth;\n}\n\n/**\n * Counts total nodes in the tree (including all descendants).\n */\nexport function countNodes(rows: any[], childrenField = 'children'): number {\n if (!Array.isArray(rows)) return 0;\n\n let count = 0;\n for (const row of rows) {\n if (!row) continue;\n count++;\n const children = row[childrenField];\n if (Array.isArray(children)) {\n count += countNodes(children, childrenField);\n }\n }\n\n return count;\n}\n","/**\n * Tree Data Plugin (Class-based)\n *\n * Enables hierarchical tree data with expand/collapse and auto-detection.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// The tree plugin intentionally uses `any` for maximum flexibility with user-defined row types.\n\nimport { BaseGridPlugin, CellClickEvent } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { collapseAll, expandAll, expandToKey, flattenTree, toggleExpand } from './tree-data';\nimport { detectTreeStructure, inferChildrenField } from './tree-detect';\nimport type { FlattenedTreeRow, TreeConfig, TreeExpandDetail } from './types';\n\n/**\n * Tree Data Plugin for tbw-grid\n *\n * Provides hierarchical tree data display with expand/collapse functionality.\n *\n * @example\n * ```ts\n * new TreePlugin({ defaultExpanded: true, indentWidth: 24 })\n * ```\n */\nexport class TreePlugin extends BaseGridPlugin<TreeConfig> {\n readonly name = 'tree';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<TreeConfig> {\n return {\n enabled: true,\n childrenField: 'children',\n autoDetect: true,\n defaultExpanded: false,\n indentWidth: 20,\n showExpandIcons: true,\n };\n }\n\n // ===== Internal State =====\n\n /** Set of expanded row keys */\n private expandedKeys = new Set<string>();\n\n /** Whether initial expansion (based on defaultExpanded config) has been applied */\n private initialExpansionDone = false;\n\n /** Flattened tree rows for rendering */\n private flattenedRows: FlattenedTreeRow[] = [];\n\n /** Map from key to flattened row for quick lookup */\n private rowKeyMap = new Map<string, FlattenedTreeRow>();\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.expandedKeys.clear();\n this.initialExpansionDone = false;\n this.flattenedRows = [];\n this.rowKeyMap.clear();\n }\n\n // ===== Auto-Detection =====\n\n /**\n * Detects if tree functionality should be enabled based on data structure.\n * Called by the grid during plugin initialization.\n */\n detect(rows: readonly unknown[]): boolean {\n if (!this.config.autoDetect) return false;\n const childrenField = this.config.childrenField ?? inferChildrenField(rows as any[]) ?? 'children';\n return detectTreeStructure(rows as any[], childrenField);\n }\n\n // ===== Data Processing =====\n\n override processRows(rows: readonly unknown[]): any[] {\n const childrenField = this.config.childrenField ?? 'children';\n\n // Check if data is actually a tree\n if (!detectTreeStructure(rows as any[], childrenField)) {\n this.flattenedRows = [];\n this.rowKeyMap.clear();\n return [...rows];\n }\n\n // Initialize expansion state if needed (only once per grid lifecycle)\n if (this.config.defaultExpanded && !this.initialExpansionDone) {\n this.expandedKeys = expandAll(rows as any[], this.config);\n this.initialExpansionDone = true;\n }\n\n // Flatten tree\n this.flattenedRows = flattenTree(rows as any[], this.config, this.expandedKeys);\n\n // Build key map\n this.rowKeyMap.clear();\n for (const flatRow of this.flattenedRows) {\n this.rowKeyMap.set(flatRow.key, flatRow);\n }\n\n // Return flattened data for rendering with tree metadata\n return this.flattenedRows.map((fr) => ({\n ...fr.data,\n __treeKey: fr.key,\n __treeDepth: fr.depth,\n __treeHasChildren: fr.hasChildren,\n __treeExpanded: fr.isExpanded,\n }));\n }\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (this.flattenedRows.length === 0) return [...columns];\n\n const indentWidth = this.config.indentWidth ?? 20;\n const showExpandIcons = this.config.showExpandIcons ?? true;\n\n // Wrap first column's renderer to add tree indentation\n const cols = [...columns] as ColumnConfig[];\n if (cols.length > 0) {\n const firstCol = { ...cols[0] };\n const originalRenderer = firstCol.viewRenderer;\n\n firstCol.viewRenderer = (renderCtx) => {\n const { value, row, column: colConfig } = renderCtx;\n const depth = row.__treeDepth ?? 0;\n const hasChildren = row.__treeHasChildren ?? false;\n const isExpanded = row.__treeExpanded ?? false;\n\n const container = document.createElement('span');\n container.style.display = 'flex';\n container.style.alignItems = 'center';\n container.style.paddingLeft = `${depth * indentWidth}px`;\n\n // Expand/collapse icon\n if (hasChildren && showExpandIcons) {\n const icon = document.createElement('span');\n icon.className = 'tree-toggle';\n icon.textContent = isExpanded ? '▼' : '▶';\n icon.style.cursor = 'pointer';\n icon.style.marginRight = '4px';\n icon.style.fontSize = '10px';\n icon.setAttribute('data-tree-key', row.__treeKey);\n container.appendChild(icon);\n } else if (showExpandIcons) {\n // Spacer for alignment\n const spacer = document.createElement('span');\n spacer.style.width = '14px';\n spacer.style.display = 'inline-block';\n container.appendChild(spacer);\n }\n\n // Cell content\n const content = document.createElement('span');\n if (originalRenderer) {\n const rendered = originalRenderer(renderCtx);\n if (rendered instanceof Node) {\n content.appendChild(rendered);\n } else {\n content.textContent = String(rendered ?? value ?? '');\n }\n } else {\n content.textContent = String(value ?? '');\n }\n container.appendChild(content);\n\n return container;\n };\n\n cols[0] = firstCol;\n }\n\n return cols;\n }\n\n // ===== Event Handlers =====\n\n override onCellClick(event: CellClickEvent): boolean {\n const target = event.originalEvent?.target as HTMLElement;\n if (!target?.classList.contains('tree-toggle')) return false;\n\n const key = target.getAttribute('data-tree-key');\n if (!key) return false;\n\n const flatRow = this.rowKeyMap.get(key);\n if (!flatRow) return false;\n\n this.expandedKeys = toggleExpand(this.expandedKeys, key);\n\n this.emit<TreeExpandDetail>('tree-expand', {\n key,\n row: flatRow.data,\n expanded: this.expandedKeys.has(key),\n depth: flatRow.depth,\n });\n\n this.requestRender();\n return true;\n }\n\n // ===== Public API =====\n\n /**\n * Expand a specific node by key.\n */\n expand(key: string): void {\n this.expandedKeys.add(key);\n this.requestRender();\n }\n\n /**\n * Collapse a specific node by key.\n */\n collapse(key: string): void {\n this.expandedKeys.delete(key);\n this.requestRender();\n }\n\n /**\n * Toggle the expansion state of a node.\n */\n toggle(key: string): void {\n this.expandedKeys = toggleExpand(this.expandedKeys, key);\n this.requestRender();\n }\n\n /**\n * Expand all nodes in the tree.\n */\n expandAll(): void {\n this.expandedKeys = expandAll(this.rows as any[], this.config);\n this.requestRender();\n }\n\n /**\n * Collapse all nodes in the tree.\n */\n collapseAll(): void {\n this.expandedKeys = collapseAll();\n this.requestRender();\n }\n\n /**\n * Check if a node is currently expanded.\n */\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n /**\n * Get all currently expanded keys.\n */\n getExpandedKeys(): string[] {\n return [...this.expandedKeys];\n }\n\n /**\n * Get the flattened tree rows with metadata.\n */\n getFlattenedRows(): FlattenedTreeRow[] {\n return [...this.flattenedRows];\n }\n\n /**\n * Get a row's original data by its key.\n */\n getRowByKey(key: string): any | undefined {\n return this.rowKeyMap.get(key)?.data;\n }\n\n /**\n * Expand all ancestors of a node to make it visible.\n */\n expandToKey(key: string): void {\n this.expandedKeys = expandToKey(this.rows as any[], key, this.config, this.expandedKeys);\n this.requestRender();\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n .tree-toggle {\n cursor: pointer;\n user-select: none;\n transition: transform 0.2s;\n }\n .tree-toggle:hover {\n color: var(--tbw-tree-accent, var(--tbw-color-accent));\n }\n `;\n}\n","/**\r\n * Grid DOM Constants\r\n *\r\n * Centralized constants for CSS classes, data attributes, and selectors\r\n * used throughout the grid. Use these instead of magic strings.\r\n */\r\n\r\n// ================= CSS Classes =================\r\n\r\n/**\r\n * CSS class names used in the grid's shadow DOM.\r\n * Use these when adding/removing classes or querying elements.\r\n */\r\nexport const GridClasses = {\r\n // Root structure\r\n ROOT: 'tbw-grid-root',\r\n HEADER: 'header',\r\n HEADER_ROW: 'header-row',\r\n HEADER_CELL: 'header-cell',\r\n\r\n // Body structure\r\n ROWS_VIEWPORT: 'rows-viewport',\r\n ROWS_SPACER: 'rows-spacer',\r\n ROWS_CONTAINER: 'rows',\r\n\r\n // Row elements\r\n DATA_ROW: 'data-row',\r\n GROUP_ROW: 'group-row',\r\n\r\n // Cell elements\r\n DATA_CELL: 'data-cell',\r\n\r\n // States\r\n SELECTED: 'selected',\r\n FOCUSED: 'focused',\r\n EDITING: 'editing',\r\n EXPANDED: 'expanded',\r\n COLLAPSED: 'collapsed',\r\n DRAGGING: 'dragging',\r\n RESIZING: 'resizing',\r\n\r\n // Sorting\r\n SORTABLE: 'sortable',\r\n SORTED_ASC: 'sorted-asc',\r\n SORTED_DESC: 'sorted-desc',\r\n\r\n // Visibility\r\n HIDDEN: 'hidden',\r\n\r\n // Sticky/pinned\r\n STICKY_LEFT: 'sticky-left',\r\n STICKY_RIGHT: 'sticky-right',\r\n\r\n // Special rows\r\n PINNED_TOP: 'pinned-top',\r\n PINNED_BOTTOM: 'pinned-bottom',\r\n\r\n // Tree\r\n TREE_TOGGLE: 'tree-toggle',\r\n TREE_INDENT: 'tree-indent',\r\n\r\n // Grouping\r\n GROUP_TOGGLE: 'group-toggle',\r\n GROUP_LABEL: 'group-label',\r\n GROUP_COUNT: 'group-count',\r\n\r\n // Selection\r\n RANGE_SELECTION: 'range-selection',\r\n SELECTION_OVERLAY: 'selection-overlay',\r\n} as const;\r\n\r\n// ================= Data Attributes =================\r\n\r\n/**\r\n * Data attribute names used on grid elements.\r\n * Use these when getting/setting data attributes.\r\n */\r\nexport const GridDataAttrs = {\r\n ROW_INDEX: 'data-row-index',\r\n COL_INDEX: 'data-col-index',\r\n FIELD: 'data-field',\r\n GROUP_KEY: 'data-group-key',\r\n TREE_LEVEL: 'data-tree-level',\r\n STICKY: 'data-sticky',\r\n} as const;\r\n\r\n// ================= Selectors =================\r\n\r\n/**\r\n * Common CSS selectors for querying grid elements.\r\n * Built from the class constants for consistency.\r\n */\r\nexport const GridSelectors = {\r\n ROOT: `.${GridClasses.ROOT}`,\r\n HEADER: `.${GridClasses.HEADER}`,\r\n HEADER_ROW: `.${GridClasses.HEADER_ROW}`,\r\n HEADER_CELL: `.${GridClasses.HEADER_CELL}`,\r\n ROWS_VIEWPORT: `.${GridClasses.ROWS_VIEWPORT}`,\r\n ROWS_CONTAINER: `.${GridClasses.ROWS_CONTAINER}`,\r\n DATA_ROW: `.${GridClasses.DATA_ROW}`,\r\n DATA_CELL: `.${GridClasses.DATA_CELL}`,\r\n GROUP_ROW: `.${GridClasses.GROUP_ROW}`,\r\n\r\n // By data attribute\r\n ROW_BY_INDEX: (index: number) => `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${index}\"]`,\r\n CELL_BY_FIELD: (field: string) => `.${GridClasses.DATA_CELL}[${GridDataAttrs.FIELD}=\"${field}\"]`,\r\n CELL_AT: (row: number, col: number) =>\r\n `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${row}\"] .${GridClasses.DATA_CELL}[${GridDataAttrs.COL_INDEX}=\"${col}\"]`,\r\n\r\n // State selectors\r\n SELECTED_ROWS: `.${GridClasses.DATA_ROW}.${GridClasses.SELECTED}`,\r\n EDITING_CELL: `.${GridClasses.DATA_CELL}.${GridClasses.EDITING}`,\r\n} as const;\r\n\r\n// ================= CSS Custom Properties =================\r\n\r\n/**\r\n * CSS custom property names for theming.\r\n * Use these when programmatically setting styles.\r\n */\r\nexport const GridCSSVars = {\r\n // Colors\r\n COLOR_BG: '--tbw-color-bg',\r\n COLOR_FG: '--tbw-color-fg',\r\n COLOR_FG_MUTED: '--tbw-color-fg-muted',\r\n COLOR_BORDER: '--tbw-color-border',\r\n COLOR_ACCENT: '--tbw-color-accent',\r\n COLOR_HEADER_BG: '--tbw-color-header-bg',\r\n COLOR_HEADER_FG: '--tbw-color-header-fg',\r\n COLOR_SELECTION: '--tbw-color-selection',\r\n COLOR_ROW_HOVER: '--tbw-color-row-hover',\r\n COLOR_ROW_ALT: '--tbw-color-row-alt',\r\n\r\n // Sizing\r\n ROW_HEIGHT: '--tbw-row-height',\r\n HEADER_HEIGHT: '--tbw-header-height',\r\n CELL_PADDING: '--tbw-cell-padding',\r\n\r\n // Typography\r\n FONT_FAMILY: '--tbw-font-family',\r\n FONT_SIZE: '--tbw-font-size',\r\n\r\n // Borders\r\n BORDER_RADIUS: '--tbw-border-radius',\r\n FOCUS_OUTLINE: '--tbw-focus-outline',\r\n} as const;\r\n\r\n// Type helpers\r\nexport type GridClassName = (typeof GridClasses)[keyof typeof GridClasses];\r\nexport type GridDataAttr = (typeof GridDataAttrs)[keyof typeof GridDataAttrs];\r\nexport type GridCSSVar = (typeof GridCSSVars)[keyof typeof GridCSSVars];\r\n","// Public API surface - only export what consumers need\r\nexport { DataGridElement, DataGridElement as GridElement } from './lib/core/grid';\r\n\r\n// Event name constants for DataGrid (public API)\r\nexport const DGEvents = {\r\n CELL_COMMIT: 'cell-commit',\r\n ROW_COMMIT: 'row-commit',\r\n CHANGED_ROWS_RESET: 'changed-rows-reset',\r\n MOUNT_EXTERNAL_VIEW: 'mount-external-view',\r\n MOUNT_EXTERNAL_EDITOR: 'mount-external-editor',\r\n SORT_CHANGE: 'sort-change',\r\n COLUMN_RESIZE: 'column-resize',\r\n ACTIVATE_CELL: 'activate-cell',\r\n GROUP_TOGGLE: 'group-toggle',\r\n COLUMN_STATE_CHANGE: 'column-state-change',\r\n} as const;\r\n\r\nexport type DGEventName = (typeof DGEvents)[keyof typeof DGEvents];\r\n\r\n// Plugin event constants (mirrors DGEvents pattern)\r\nexport const PluginEvents = {\r\n // Selection plugin\r\n SELECTION_CHANGE: 'selection-change',\r\n // Tree plugin\r\n TREE_EXPAND: 'tree-expand',\r\n // Filtering plugin\r\n FILTER_CHANGE: 'filter-change',\r\n // Sorting plugin\r\n SORT_MODEL_CHANGE: 'sort-model-change',\r\n // Export plugin\r\n EXPORT_START: 'export-start',\r\n EXPORT_COMPLETE: 'export-complete',\r\n // Clipboard plugin\r\n CLIPBOARD_COPY: 'clipboard-copy',\r\n CLIPBOARD_PASTE: 'clipboard-paste',\r\n // Context menu plugin\r\n CONTEXT_MENU_OPEN: 'context-menu-open',\r\n CONTEXT_MENU_CLOSE: 'context-menu-close',\r\n // Undo/Redo plugin\r\n HISTORY_CHANGE: 'history-change',\r\n // Server-side plugin\r\n SERVER_LOADING: 'server-loading',\r\n SERVER_ERROR: 'server-error',\r\n // Visibility plugin\r\n COLUMN_VISIBILITY_CHANGE: 'column-visibility-change',\r\n // Reorder plugin\r\n COLUMN_REORDER: 'column-reorder',\r\n // Master-detail plugin\r\n DETAIL_EXPAND: 'detail-expand',\r\n // Grouping rows plugin\r\n GROUP_EXPAND: 'group-expand',\r\n} as const;\r\n\r\nexport type PluginEventName = (typeof PluginEvents)[keyof typeof PluginEvents];\r\n\r\n// Public type exports\r\nexport type {\r\n ActivateCellDetail,\r\n AggregatorRef,\r\n BaseColumnConfig,\r\n // Event detail types\r\n CellCommitDetail,\r\n CellRenderContext,\r\n ChangedRowsResetDetail,\r\n ColumnConfig,\r\n ColumnConfigMap,\r\n ColumnEditorContext,\r\n // Column features\r\n ColumnEditorSpec,\r\n ColumnResizeDetail,\r\n // Column state types\r\n ColumnSortState,\r\n ColumnState,\r\n ColumnViewRenderer,\r\n DataGridCustomEvent,\r\n DataGridElement as DataGridElementInterface,\r\n DataGridEventMap,\r\n ExternalMountEditorDetail,\r\n ExternalMountViewDetail,\r\n FitMode,\r\n GridColumnState,\r\n // Core configuration types\r\n GridConfig,\r\n // Shell types\r\n HeaderContentDefinition,\r\n // Inference types\r\n InferredColumnResult,\r\n PrimitiveColumnType,\r\n // Public interface\r\n PublicGrid,\r\n RowCommitDetail,\r\n // Grouping & Footer types\r\n RowGroupRenderConfig,\r\n ShellConfig,\r\n ShellHeaderConfig,\r\n SortChangeDetail,\r\n ToolPanelConfig,\r\n ToolPanelDefinition,\r\n ToolbarButtonConfig,\r\n} from './lib/core/types';\r\n\r\n// Re-export FitModeEnum for runtime usage\r\nexport { FitModeEnum } from './lib/core/types';\r\n\r\n// ================= Plugin Types =================\r\n// Only export types that consumers need to use plugins\r\n\r\n// Selection plugin\r\nexport { SelectionPlugin } from './lib/plugins/selection/SelectionPlugin';\r\nexport type { CellRange, SelectionChangeDetail, SelectionConfig, SelectionMode } from './lib/plugins/selection/types';\r\n\r\n// Tree plugin\r\nexport { TreePlugin } from './lib/plugins/tree/TreePlugin';\r\nexport type { TreeConfig, TreeExpandDetail } from './lib/plugins/tree/types';\r\n\r\n// Filtering plugin\r\nexport type { FilterConfig, FilterModel, FilterOperator, FilterType } from './lib/plugins/filtering/types';\r\n\r\n// Multi-sort plugin\r\nexport type { MultiSortConfig, SortModel } from './lib/plugins/multi-sort/types';\r\n\r\n// Export plugin\r\nexport type { ExportFormat, ExportParams } from './lib/plugins/export/types';\r\n\r\n// Pinned rows plugin\r\nexport type { PinnedRowsContext, PinnedRowsPanel } from './lib/plugins/pinned-rows/types';\r\n\r\n// Pivot plugin\r\nexport type { PivotConfig, PivotResult, PivotValueField } from './lib/plugins/pivot/types';\r\n\r\n// Server-side plugin\r\nexport type { GetRowsParams, GetRowsResult, ServerSideDataSource } from './lib/plugins/server-side/types';\r\n\r\n// Undo/Redo plugin\r\nexport type { EditAction } from './lib/plugins/undo-redo/types';\r\n\r\n// Grouping rows plugin\r\nexport type { GroupingRowsConfig } from './lib/plugins/grouping-rows/types';\r\n\r\n// Plugin base class - for creating custom plugins\r\nexport { BaseGridPlugin } from './lib/core/plugin';\r\n\r\n// DOM constants - for querying grid elements and styling\r\nexport { GridCSSVars, GridClasses, GridDataAttrs, GridSelectors } from './lib/core/constants';\r\nexport type { GridCSSVar, GridClassName, GridDataAttr } from './lib/core/constants';\r\n","/**\n * Clipboard Copy Logic\n *\n * Pure functions for copying grid data to clipboard.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ClipboardConfig } from './types';\n\n/** Parameters for building clipboard text */\nexport interface CopyParams {\n /** All grid rows */\n rows: unknown[];\n /** Column configurations */\n columns: ColumnConfig[];\n /** Selected row indices */\n selectedIndices: Set<number> | number[];\n /** Clipboard configuration */\n config: ClipboardConfig;\n}\n\n/**\n * Format a cell value for clipboard output.\n *\n * Uses custom processCell if provided, otherwise applies default formatting:\n * - null/undefined → empty string\n * - Date → ISO string\n * - Object → JSON string\n * - Other → String conversion with optional quoting\n *\n * @param value - The cell value to format\n * @param field - The field name\n * @param row - The full row object\n * @param config - Clipboard configuration\n * @returns Formatted string value\n */\nexport function formatCellValue(value: unknown, field: string, row: unknown, config: ClipboardConfig): string {\n if (config.processCell) {\n return config.processCell(value, field, row);\n }\n\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Quote if contains delimiter, newline, or quotes (or if quoteStrings is enabled)\n if (config.quoteStrings || str.includes(delimiter) || str.includes(newline) || str.includes('\"')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build clipboard text from selected rows and columns.\n *\n * @param params - Copy parameters including rows, columns, selection, and config\n * @returns Tab-separated (or custom delimiter) text ready for clipboard\n */\nexport function buildClipboardText(params: CopyParams): string {\n const { rows, columns, selectedIndices, config } = params;\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Filter to visible columns (not hidden, not internal __ prefixed)\n const visibleColumns = columns.filter((c) => !c.hidden && !c.field.startsWith('__'));\n\n const lines: string[] = [];\n\n // Add header row if configured\n if (config.includeHeaders) {\n const headerCells = visibleColumns.map((c) => {\n const header = c.header || c.field;\n // Quote headers if they contain special characters\n if (header.includes(delimiter) || header.includes(newline) || header.includes('\"')) {\n return `\"${header.replace(/\"/g, '\"\"')}\"`;\n }\n return header;\n });\n lines.push(headerCells.join(delimiter));\n }\n\n // Convert indices to sorted array\n const indices = selectedIndices instanceof Set ? [...selectedIndices] : selectedIndices;\n const sortedIndices = [...indices].sort((a, b) => a - b);\n\n // Build data rows\n for (const idx of sortedIndices) {\n const row = rows[idx];\n if (!row) continue;\n\n const cells = visibleColumns.map((col) =>\n formatCellValue((row as Record<string, unknown>)[col.field], col.field, row, config)\n );\n lines.push(cells.join(delimiter));\n }\n\n return lines.join(newline);\n}\n\n/**\n * Copy text to the system clipboard.\n *\n * Uses the modern Clipboard API when available, with fallback\n * to execCommand for older browsers.\n *\n * @param text - The text to copy\n * @returns Promise resolving to true if successful, false otherwise\n */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n // Fallback for older browsers or when Clipboard API is not available\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.position = 'fixed';\n textarea.style.opacity = '0';\n textarea.style.pointerEvents = 'none';\n document.body.appendChild(textarea);\n textarea.select();\n const success = document.execCommand('copy');\n document.body.removeChild(textarea);\n return success;\n }\n}\n","/**\n * Clipboard Paste Logic\n *\n * Pure functions for reading and parsing clipboard data.\n */\n\nimport type { ClipboardConfig } from './types';\n\n/**\n * Parse clipboard text into a 2D array of cell values.\n *\n * Handles:\n * - Quoted values (with escaped double quotes \"\")\n * - Multi-line quoted values (newlines within quotes are preserved)\n * - Custom delimiters and newlines\n * - Empty lines (filtered out only if they contain no data)\n *\n * @param text - Raw clipboard text\n * @param config - Clipboard configuration\n * @returns 2D array where each sub-array is a row of cell values\n */\nexport function parseClipboardText(text: string, config: ClipboardConfig): string[][] {\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Handle Windows CRLF line endings\n const normalizedText = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n\n // Parse the entire text handling quoted fields that may span multiple lines\n const rows: string[][] = [];\n let currentRow: string[] = [];\n let currentCell = '';\n let inQuotes = false;\n\n for (let i = 0; i < normalizedText.length; i++) {\n const char = normalizedText[i];\n\n if (char === '\"' && !inQuotes) {\n // Start of quoted field\n inQuotes = true;\n } else if (char === '\"' && inQuotes) {\n // Check for escaped quote (\"\")\n if (normalizedText[i + 1] === '\"') {\n currentCell += '\"';\n i++; // Skip the second quote\n } else {\n // End of quoted field\n inQuotes = false;\n }\n } else if (char === delimiter && !inQuotes) {\n // Field separator\n currentRow.push(currentCell);\n currentCell = '';\n } else if (char === newline && !inQuotes) {\n // Row separator (only if not inside quotes)\n currentRow.push(currentCell);\n currentCell = '';\n // Only add non-empty rows (at least one non-empty cell or multiple cells)\n if (currentRow.length > 1 || currentRow.some((c) => c.trim() !== '')) {\n rows.push(currentRow);\n }\n currentRow = [];\n } else {\n currentCell += char;\n }\n }\n\n // Handle the last cell and row\n currentRow.push(currentCell);\n if (currentRow.length > 1 || currentRow.some((c) => c.trim() !== '')) {\n rows.push(currentRow);\n }\n\n return rows;\n}\n\n/**\n * Read text from the system clipboard.\n *\n * Uses the modern Clipboard API. Returns empty string if\n * the API is not available or permission is denied.\n *\n * @returns Promise resolving to clipboard text or empty string\n */\nexport async function readFromClipboard(): Promise<string> {\n try {\n return await navigator.clipboard.readText();\n } catch {\n // Permission denied or API not available\n return '';\n }\n}\n","/**\n * Clipboard Plugin (Class-based)\n *\n * Provides copy/paste functionality for tbw-grid.\n * Supports Ctrl+C/Cmd+C for copying and Ctrl+V/Cmd+V for pasting.\n *\n * **With Selection plugin:** Copy selected rows as a range\n * - includeHeaders: true → adds header row at top\n * - includeHeaders: false → data rows only\n *\n * **Without Selection plugin:** Copy focused cell only\n * - includeHeaders: true → \"Header: value\" format\n * - includeHeaders: false → value only\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport { buildClipboardText, copyToClipboard } from './copy';\nimport { parseClipboardText, readFromClipboard } from './paste';\nimport type { ClipboardConfig, CopyDetail, PasteDetail } from './types';\n\n/**\n * Clipboard Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ClipboardPlugin({ includeHeaders: true })\n * ```\n */\nexport class ClipboardPlugin extends BaseGridPlugin<ClipboardConfig> {\n readonly name = 'clipboard';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ClipboardConfig> {\n return {\n enabled: true,\n includeHeaders: false,\n delimiter: '\\t',\n newline: '\\n',\n quoteStrings: false,\n };\n }\n\n // ===== Internal State =====\n /** The last copied text (for reference/debugging) */\n private lastCopied: { text: string; timestamp: number } | null = null;\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.lastCopied = null;\n }\n\n // ===== Event Handlers =====\n\n override onKeyDown(event: KeyboardEvent): boolean {\n if (this.config.enabled === false) return false;\n\n const isCopy = (event.ctrlKey || event.metaKey) && event.key === 'c';\n const isPaste = (event.ctrlKey || event.metaKey) && event.key === 'v';\n\n if (isCopy) {\n this.#handleCopy(event.target as HTMLElement);\n return true; // Prevent default browser behavior\n }\n\n if (isPaste) {\n this.#handlePaste();\n return true; // Prevent default browser behavior\n }\n\n return false;\n }\n\n // ===== Private Methods =====\n\n /**\n * Handle copy operation\n */\n #handleCopy(target: HTMLElement): void {\n // Try to get selection from selection plugin (if available)\n // Use dynamic import to avoid circular dependency issues\n const selectionPlugin = this.#getSelectionPlugin();\n\n // Check for different selection types\n const selectedRows = selectionPlugin?.getSelectedRows() ?? [];\n const hasRowSelection = selectedRows.length > 0;\n const ranges = selectionPlugin?.getRanges() ?? [];\n const hasRangeSelection = ranges.length > 0;\n const hasCellSelection = selectionPlugin?.getSelectedCell() != null;\n\n let text: string;\n let rowCount: number;\n let columnCount: number;\n\n if (hasRowSelection && selectionPlugin) {\n // Row selection mode - copy full rows\n text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: selectedRows,\n config: this.config,\n });\n rowCount = selectedRows.length;\n columnCount = this.columns.filter((c) => !c.hidden && !c.field.startsWith('__')).length;\n } else if (hasRangeSelection && selectionPlugin) {\n // Range selection mode - copy rectangular range (use last range as active)\n const range = ranges[ranges.length - 1];\n const result = this.#buildRangeText({\n startRow: range.from.row,\n startCol: range.from.col,\n endRow: range.to.row,\n endCol: range.to.col,\n });\n text = result.text;\n rowCount = result.rowCount;\n columnCount = result.columnCount;\n } else if (hasCellSelection && selectionPlugin) {\n // Cell selection mode - copy single cell\n const cell = selectionPlugin.getSelectedCell()!;\n const result = this.#buildCellText(cell.row, cell.col);\n if (!result) return;\n text = result.text;\n rowCount = 1;\n columnCount = 1;\n } else {\n // Fallback: try to find focused cell from DOM\n const result = this.#buildSingleCellText(target);\n if (!result) return;\n text = result.text;\n rowCount = 1;\n columnCount = 1;\n }\n\n copyToClipboard(text).then(() => {\n this.lastCopied = { text, timestamp: Date.now() };\n this.emit<CopyDetail>('copy', { text, rowCount, columnCount });\n });\n }\n\n /**\n * Handle paste operation\n */\n #handlePaste(): void {\n readFromClipboard().then((text) => {\n if (!text) return;\n const parsed = parseClipboardText(text, this.config);\n this.emit<PasteDetail>('paste', { rows: parsed, text });\n });\n }\n\n /**\n * Get the selection plugin instance if available.\n */\n #getSelectionPlugin(): SelectionPluginInterface | undefined {\n // Dynamically get the SelectionPlugin class to avoid import order issues\n try {\n // Use getPlugin with the class - this requires the class to be imported\n // For now, we'll use a duck-typing approach via the grid's plugin registry\n const grid = this.grid as any;\n if (grid?._plugins) {\n for (const plugin of grid._plugins) {\n if (plugin.name === 'selection') {\n return plugin as SelectionPluginInterface;\n }\n }\n }\n } catch {\n // Selection plugin not available\n }\n return undefined;\n }\n\n /**\n * Build text for a single cell by row/col index.\n */\n #buildCellText(rowIndex: number, colIndex: number): { text: string } | null {\n const rowData = this.rows[rowIndex] as Record<string, unknown> | undefined;\n if (!rowData) return null;\n\n const column = this.columns[colIndex];\n if (!column) return null;\n\n const value = rowData[column.field];\n const header = column.header || column.field;\n\n let text: string;\n if (this.config.includeHeaders) {\n const formattedValue = value == null ? '' : String(value);\n text = `${header}: ${formattedValue}`;\n } else {\n text = value == null ? '' : String(value);\n }\n\n return { text };\n }\n\n /**\n * Build text for a rectangular range of cells.\n */\n #buildRangeText(range: { startRow: number; startCol: number; endRow: number; endCol: number }): {\n text: string;\n rowCount: number;\n columnCount: number;\n } {\n const { startRow, startCol, endRow, endCol } = range;\n const minRow = Math.min(startRow, endRow);\n const maxRow = Math.max(startRow, endRow);\n const minCol = Math.min(startCol, endCol);\n const maxCol = Math.max(startCol, endCol);\n\n const delimiter = this.config.delimiter ?? '\\t';\n const newline = this.config.newline ?? '\\n';\n const lines: string[] = [];\n\n // Get columns in the range\n const rangeColumns = this.columns.slice(minCol, maxCol + 1);\n\n // Add header row if configured\n if (this.config.includeHeaders) {\n const headerCells = rangeColumns.map((c) => c.header || c.field);\n lines.push(headerCells.join(delimiter));\n }\n\n // Add data rows\n for (let r = minRow; r <= maxRow; r++) {\n const rowData = this.rows[r] as Record<string, unknown> | undefined;\n if (!rowData) continue;\n\n const cells = rangeColumns.map((col) => {\n const value = rowData[col.field];\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n return String(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return {\n text: lines.join(newline),\n rowCount: maxRow - minRow + 1,\n columnCount: maxCol - minCol + 1,\n };\n }\n\n /**\n * Build text for a single focused cell from DOM.\n * Used when selection plugin is not available or no rows are selected.\n */\n #buildSingleCellText(target: HTMLElement): { text: string; field: string; value: unknown } | null {\n // Find the cell element - cells use data-field-cache for the field name\n const cell = target.closest('[data-field-cache]') as HTMLElement | null;\n if (!cell) return null;\n\n const field = cell.dataset.fieldCache;\n if (!field) return null;\n\n // Get row index from data-row attribute on the cell\n const rowIndexStr = cell.dataset.row;\n if (!rowIndexStr) return null;\n\n const rowIndex = parseInt(rowIndexStr, 10);\n if (isNaN(rowIndex)) return null;\n\n const rowData = this.rows[rowIndex] as Record<string, unknown> | undefined;\n if (!rowData) return null;\n\n const value = rowData[field];\n const column = this.columns.find((c) => c.field === field);\n const header = column?.header || field;\n\n // Format the text based on includeHeaders config\n let text: string;\n if (this.config.includeHeaders) {\n // Format: \"{header}: {value}\"\n const formattedValue = value == null ? '' : String(value);\n text = `${header}: ${formattedValue}`;\n } else {\n // Just the value\n text = value == null ? '' : String(value);\n }\n\n return { text, field, value };\n }\n\n // ===== Public API =====\n\n /**\n * Copy currently selected rows to clipboard.\n * @returns The copied text\n */\n async copy(): Promise<string> {\n const selectionPlugin = this.#getSelectionPlugin();\n const indices = selectionPlugin?.getSelectedRows() ?? [];\n\n const text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: indices,\n config: this.config,\n });\n\n await copyToClipboard(text);\n this.lastCopied = { text, timestamp: Date.now() };\n return text;\n }\n\n /**\n * Copy specific rows by index to clipboard.\n * @param indices - Array of row indices to copy\n * @returns The copied text\n */\n async copyRows(indices: number[]): Promise<string> {\n const text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: indices,\n config: this.config,\n });\n\n await copyToClipboard(text);\n this.lastCopied = { text, timestamp: Date.now() };\n return text;\n }\n\n /**\n * Read and parse clipboard content.\n * @returns Parsed 2D array of cell values, or null if clipboard is empty\n */\n async paste(): Promise<string[][] | null> {\n const text = await readFromClipboard();\n if (!text) return null;\n return parseClipboardText(text, this.config);\n }\n\n /**\n * Get the last copied text and timestamp.\n * @returns The last copied info or null\n */\n getLastCopied(): { text: string; timestamp: number } | null {\n return this.lastCopied;\n }\n}\n\n// ===== Internal Types =====\n\n/**\n * Interface for SelectionPlugin methods we need.\n * This avoids circular imports while providing type safety.\n */\ninterface SelectionPluginInterface {\n name: string;\n /** Get selected row indices (row mode) */\n getSelectedRows(): number[];\n /** Get all selected cell ranges (range mode) */\n getRanges(): Array<{ from: { row: number; col: number }; to: { row: number; col: number } }>;\n /** Get selected cell (cell mode) */\n getSelectedCell(): { row: number; col: number } | null;\n}\n\n// Re-export types\nexport type { ClipboardConfig, CopyDetail, PasteDetail } from './types';\n","/**\n * Column Virtualization Core Logic\n *\n * Pure functions for horizontal column virtualization operations.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ColumnVirtualizationViewport } from './types';\n\n/** Default column width when not specified */\nconst DEFAULT_COLUMN_WIDTH = 100;\n\n/**\n * Parse a column width value to pixels.\n * Handles number (px) and string formats.\n *\n * @param width - The width value from column config\n * @returns Width in pixels\n */\nexport function parseColumnWidth(width: string | number | undefined): number {\n if (width === undefined || width === null) {\n return DEFAULT_COLUMN_WIDTH;\n }\n\n if (typeof width === 'number') {\n return width;\n }\n\n // Handle string values - extract numeric part\n const numeric = parseFloat(width);\n if (!isNaN(numeric)) {\n return numeric;\n }\n\n return DEFAULT_COLUMN_WIDTH;\n}\n\n/**\n * Get array of column widths in pixels.\n *\n * @param columns - Column configurations\n * @returns Array of widths in pixels\n */\nexport function getColumnWidths(columns: readonly ColumnConfig[]): number[] {\n return columns.map((col) => parseColumnWidth(col.width));\n}\n\n/**\n * Compute cumulative left offsets for each column.\n *\n * @param columns - Column configurations\n * @returns Array of left offsets in pixels\n */\nexport function computeColumnOffsets(columns: readonly ColumnConfig[]): number[] {\n const offsets: number[] = [];\n let offset = 0;\n\n for (const col of columns) {\n offsets.push(offset);\n offset += parseColumnWidth(col.width);\n }\n\n return offsets;\n}\n\n/**\n * Compute total width of all columns.\n *\n * @param columns - Column configurations\n * @returns Total width in pixels\n */\nexport function computeTotalWidth(columns: readonly ColumnConfig[]): number {\n return columns.reduce((sum, col) => sum + parseColumnWidth(col.width), 0);\n}\n\n/**\n * Find the visible column range based on scroll position.\n * Uses binary search for efficient lookup in large column sets.\n *\n * @param scrollLeft - Current horizontal scroll position\n * @param viewportWidth - Width of the visible viewport\n * @param columnOffsets - Array of column left offsets\n * @param columnWidths - Array of column widths\n * @param overscan - Number of extra columns to render on each side\n * @returns Viewport information with visible column indices\n */\nexport function getVisibleColumnRange(\n scrollLeft: number,\n viewportWidth: number,\n columnOffsets: number[],\n columnWidths: number[],\n overscan: number\n): ColumnVirtualizationViewport {\n const columnCount = columnOffsets.length;\n\n if (columnCount === 0) {\n return { startCol: 0, endCol: 0, visibleColumns: [] };\n }\n\n // Binary search for first visible column\n let startCol = binarySearchFirstVisible(scrollLeft, columnOffsets, columnWidths);\n startCol = Math.max(0, startCol - overscan);\n\n // Find last visible column (without overscan first)\n const rightEdge = scrollLeft + viewportWidth;\n let endCol = startCol;\n\n for (let i = startCol; i < columnCount; i++) {\n if (columnOffsets[i] >= rightEdge) {\n endCol = i - 1;\n break;\n }\n endCol = i;\n }\n\n // Apply overscan to end (only once)\n endCol = Math.min(columnCount - 1, endCol + overscan);\n\n // Build array of visible column indices\n const visibleColumns: number[] = [];\n for (let i = startCol; i <= endCol; i++) {\n visibleColumns.push(i);\n }\n\n return { startCol, endCol, visibleColumns };\n}\n\n/**\n * Binary search to find the first column that is visible.\n *\n * @param scrollLeft - Current scroll position\n * @param columnOffsets - Array of column offsets\n * @param columnWidths - Array of column widths\n * @returns Index of first visible column\n */\nfunction binarySearchFirstVisible(scrollLeft: number, columnOffsets: number[], columnWidths: number[]): number {\n let low = 0;\n let high = columnOffsets.length - 1;\n\n while (low < high) {\n const mid = Math.floor((low + high) / 2);\n const colRight = columnOffsets[mid] + columnWidths[mid];\n\n if (colRight <= scrollLeft) {\n low = mid + 1;\n } else {\n high = mid;\n }\n }\n\n return low;\n}\n\n/**\n * Determine if column virtualization should be active.\n *\n * @param columnCount - Number of columns\n * @param threshold - Column count threshold\n * @param autoEnable - Whether auto-enable is configured\n * @returns True if virtualization should be active\n */\nexport function shouldVirtualize(columnCount: number, threshold: number, autoEnable: boolean): boolean {\n if (!autoEnable) return false;\n return columnCount > threshold;\n}\n","/**\n * Column Virtualization Plugin (Class-based)\n *\n * Provides horizontal column virtualization for grids with many columns.\n * Significantly improves rendering performance when dealing with >30 columns.\n */\n\nimport { BaseGridPlugin, ScrollEvent } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport {\n computeColumnOffsets,\n computeTotalWidth,\n getColumnWidths,\n getVisibleColumnRange,\n shouldVirtualize,\n} from './column-virtualization';\nimport type { ColumnVirtualizationConfig } from './types';\n\n/**\n * Column Virtualization Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ColumnVirtualizationPlugin({ threshold: 30, overscan: 3 })\n * ```\n */\nexport class ColumnVirtualizationPlugin extends BaseGridPlugin<ColumnVirtualizationConfig> {\n readonly name = 'columnVirtualization';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ColumnVirtualizationConfig> {\n return {\n enabled: true,\n autoEnable: true,\n threshold: 30,\n overscan: 3,\n };\n }\n\n // ===== Internal State =====\n private isVirtualized = false;\n private startCol = 0;\n private endCol = 0;\n private scrollLeft = 0;\n private totalWidth = 0;\n private columnWidths: number[] = [];\n private columnOffsets: number[] = [];\n\n // ===== Lifecycle =====\n\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\n super.attach(grid);\n\n // Initialize state from current columns\n const columns = this.columns;\n this.columnWidths = getColumnWidths(columns);\n this.columnOffsets = computeColumnOffsets(columns);\n this.totalWidth = computeTotalWidth(columns);\n this.endCol = columns.length - 1;\n }\n\n override detach(): void {\n this.columnWidths = [];\n this.columnOffsets = [];\n this.isVirtualized = false;\n this.startCol = 0;\n this.endCol = 0;\n this.scrollLeft = 0;\n this.totalWidth = 0;\n }\n\n // ===== Hooks =====\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n const isVirtualized =\n this.config.enabled &&\n shouldVirtualize(columns.length, this.config.threshold ?? 30, this.config.autoEnable ?? true);\n\n // Update state with current column metrics\n this.isVirtualized = isVirtualized ?? false;\n this.columnWidths = getColumnWidths(columns);\n this.columnOffsets = computeColumnOffsets(columns);\n this.totalWidth = computeTotalWidth(columns);\n\n if (!isVirtualized) {\n this.startCol = 0;\n this.endCol = columns.length - 1;\n return [...columns];\n }\n\n // Get viewport width from grid element\n const viewportWidth = (this.grid as unknown as HTMLElement).clientWidth || 800;\n const viewport = getVisibleColumnRange(\n this.scrollLeft,\n viewportWidth,\n this.columnOffsets,\n this.columnWidths,\n this.config.overscan ?? 3\n );\n\n this.startCol = viewport.startCol;\n this.endCol = viewport.endCol;\n\n // Return only visible columns\n return viewport.visibleColumns.map((i) => columns[i]);\n }\n\n override afterRender(): void {\n if (!this.isVirtualized) return;\n\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n // Apply left padding to offset scrolled-out columns\n const leftPadding = this.columnOffsets[this.startCol] ?? 0;\n\n const headerRow = shadowRoot.querySelector('.header-row');\n const bodyRows = shadowRoot.querySelectorAll('.data-grid-row');\n\n if (headerRow) {\n (headerRow as HTMLElement).style.paddingLeft = `${leftPadding}px`;\n }\n\n bodyRows.forEach((row) => {\n (row as HTMLElement).style.paddingLeft = `${leftPadding}px`;\n });\n\n // Set total width for horizontal scrolling on the rows container\n const rowsContainer = shadowRoot.querySelector('.rows-viewport .rows');\n if (rowsContainer) {\n (rowsContainer as HTMLElement).style.width = `${this.totalWidth}px`;\n }\n }\n\n override onScroll(event: ScrollEvent): void {\n if (!this.isVirtualized) return;\n\n // Check if horizontal scroll position changed significantly\n const scrollDelta = Math.abs(event.scrollLeft - this.scrollLeft);\n if (scrollDelta < 1) return;\n\n // Update scroll position\n this.scrollLeft = event.scrollLeft;\n\n // Recalculate visible columns and request re-render\n this.requestRender();\n }\n\n // ===== Public API =====\n\n /**\n * Check if column virtualization is currently active.\n */\n getIsVirtualized(): boolean {\n return this.isVirtualized;\n }\n\n /**\n * Get the current visible column range.\n */\n getVisibleColumnRange(): { start: number; end: number } {\n return { start: this.startCol, end: this.endCol };\n }\n\n /**\n * Scroll the grid to bring a specific column into view.\n * @param columnIndex - Index of the column to scroll to\n */\n scrollToColumn(columnIndex: number): void {\n const offset = this.columnOffsets[columnIndex] ?? 0;\n const gridEl = this.grid as unknown as HTMLElement;\n // Scroll the grid element itself (it's the scroll container)\n gridEl.scrollLeft = offset;\n }\n\n /**\n * Get the left offset for a specific column.\n * @param columnIndex - Index of the column\n */\n getColumnOffset(columnIndex: number): number {\n return this.columnOffsets[columnIndex] ?? 0;\n }\n\n /**\n * Get the total width of all columns.\n */\n getTotalWidth(): number {\n return this.totalWidth;\n }\n}\n","/**\n * Context Menu Rendering Logic\n *\n * Pure functions for building and positioning context menus.\n */\n\nimport type { ContextMenuItem, ContextMenuParams } from './types';\n\n/**\n * Build the visible menu items by resolving dynamic items and filtering hidden ones.\n *\n * @param items - Menu items configuration (array or factory function)\n * @param params - Context menu parameters for evaluating dynamic properties\n * @returns Filtered array of visible menu items\n */\nexport function buildMenuItems(\n items: ContextMenuItem[] | ((params: ContextMenuParams) => ContextMenuItem[]),\n params: ContextMenuParams\n): ContextMenuItem[] {\n const menuItems = typeof items === 'function' ? items(params) : items;\n\n return menuItems.filter((item) => {\n if (item.hidden === true) return false;\n if (typeof item.hidden === 'function' && item.hidden(params)) return false;\n return true;\n });\n}\n\n/**\n * Check if a menu item is disabled.\n *\n * @param item - The menu item to check\n * @param params - Context menu parameters for evaluating dynamic disabled state\n * @returns True if the item is disabled\n */\nexport function isItemDisabled(item: ContextMenuItem, params: ContextMenuParams): boolean {\n if (item.disabled === true) return true;\n if (typeof item.disabled === 'function') return item.disabled(params);\n return false;\n}\n\n/**\n * Create the menu DOM element from a list of menu items.\n *\n * @param items - Array of menu items to render\n * @param params - Context menu parameters for evaluating dynamic properties\n * @param onAction - Callback when a menu item action is triggered\n * @returns The created menu element\n */\nexport function createMenuElement(\n items: ContextMenuItem[],\n params: ContextMenuParams,\n onAction: (item: ContextMenuItem) => void\n): HTMLElement {\n const menu = document.createElement('div');\n menu.className = 'tbw-context-menu';\n menu.setAttribute('role', 'menu');\n\n for (const item of items) {\n if (item.separator) {\n const separator = document.createElement('div');\n separator.className = 'tbw-context-menu-separator';\n separator.setAttribute('role', 'separator');\n menu.appendChild(separator);\n continue;\n }\n\n const menuItem = document.createElement('div');\n menuItem.className = 'tbw-context-menu-item';\n if (item.cssClass) menuItem.classList.add(item.cssClass);\n menuItem.setAttribute('role', 'menuitem');\n menuItem.setAttribute('data-id', item.id);\n\n const disabled = isItemDisabled(item, params);\n if (disabled) {\n menuItem.classList.add('disabled');\n menuItem.setAttribute('aria-disabled', 'true');\n }\n\n if (item.icon) {\n const icon = document.createElement('span');\n icon.className = 'tbw-context-menu-icon';\n icon.innerHTML = item.icon;\n menuItem.appendChild(icon);\n }\n\n const label = document.createElement('span');\n label.className = 'tbw-context-menu-label';\n label.textContent = item.name;\n menuItem.appendChild(label);\n\n if (item.shortcut) {\n const shortcut = document.createElement('span');\n shortcut.className = 'tbw-context-menu-shortcut';\n shortcut.textContent = item.shortcut;\n menuItem.appendChild(shortcut);\n }\n\n if (item.subMenu?.length) {\n const arrow = document.createElement('span');\n arrow.className = 'tbw-context-menu-arrow';\n arrow.textContent = '▶';\n menuItem.appendChild(arrow);\n\n // Add submenu on hover\n menuItem.addEventListener('mouseenter', () => {\n const existingSubMenu = menuItem.querySelector('.tbw-context-menu');\n if (existingSubMenu) return;\n if (!item.subMenu) return;\n\n const subMenuItems = buildMenuItems(item.subMenu, params);\n const subMenu = createMenuElement(subMenuItems, params, onAction);\n subMenu.classList.add('tbw-context-submenu');\n subMenu.style.position = 'absolute';\n subMenu.style.left = '100%';\n subMenu.style.top = '0';\n menuItem.style.position = 'relative';\n menuItem.appendChild(subMenu);\n });\n\n menuItem.addEventListener('mouseleave', () => {\n const subMenu = menuItem.querySelector('.tbw-context-menu');\n if (subMenu) subMenu.remove();\n });\n }\n\n if (!disabled && item.action && !item.subMenu) {\n menuItem.addEventListener('click', (e) => {\n e.stopPropagation();\n onAction(item);\n });\n }\n\n menu.appendChild(menuItem);\n }\n\n return menu;\n}\n\n/**\n * Position the menu at the given viewport coordinates.\n * Menu is rendered in document.body with fixed positioning for top-layer behavior.\n *\n * @param menu - The menu element to position\n * @param x - Desired X coordinate (viewport)\n * @param y - Desired Y coordinate (viewport)\n */\nexport function positionMenu(menu: HTMLElement, x: number, y: number): void {\n // Use fixed positioning for top-layer behavior\n menu.style.position = 'fixed';\n menu.style.left = `${x}px`;\n menu.style.top = `${y}px`;\n menu.style.visibility = 'hidden';\n menu.style.zIndex = '10000';\n\n // Force layout to get dimensions\n const menuRect = menu.getBoundingClientRect();\n\n // Calculate visible area within viewport\n const viewportWidth = window.innerWidth;\n const viewportHeight = window.innerHeight;\n\n let left = x;\n let top = y;\n\n // Check if menu overflows right edge of viewport\n if (x + menuRect.width > viewportWidth) {\n left = x - menuRect.width;\n }\n // Check if menu overflows bottom edge of viewport\n if (y + menuRect.height > viewportHeight) {\n top = y - menuRect.height;\n }\n\n // Ensure we don't go negative\n left = Math.max(0, left);\n top = Math.max(0, top);\n\n menu.style.left = `${left}px`;\n menu.style.top = `${top}px`;\n menu.style.visibility = 'visible';\n}\n","/**\n * Context Menu Plugin (Class-based)\n *\n * Provides right-click context menu functionality for tbw-grid.\n * Supports custom menu items, submenus, icons, shortcuts, and dynamic item generation.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport { buildMenuItems, createMenuElement, positionMenu } from './menu';\nimport type { ContextMenuConfig, ContextMenuItem, ContextMenuParams } from './types';\n\n/** Global click handler reference for cleanup */\nlet globalClickHandler: ((e: Event) => void) | null = null;\n/** Global keydown handler reference for cleanup */\nlet globalKeydownHandler: ((e: KeyboardEvent) => void) | null = null;\n/** Global stylesheet for context menu (injected once) */\nlet globalStyleSheet: HTMLStyleElement | null = null;\n\n/** Context menu styles for light DOM rendering */\nconst contextMenuStyles = `\n .tbw-context-menu {\n position: fixed;\n background: light-dark(#f5f5f5, #2a2a2a);\n color: light-dark(#222, #eee);\n border: 1px solid light-dark(#d0d0d4, #454545);\n border-radius: 4px;\n box-shadow: 0 2px 10px rgba(0, 0, 0, 0.15);\n min-width: 160px;\n padding: 4px 0;\n z-index: 10000;\n font-size: 13px;\n font-family: system-ui, sans-serif;\n }\n .tbw-context-menu-item {\n display: flex;\n align-items: center;\n padding: 6px 12px;\n cursor: pointer;\n gap: 8px;\n }\n .tbw-context-menu-item:hover:not(.disabled) {\n background: light-dark(#e8e8e8, #3a3a3a);\n }\n .tbw-context-menu-item.disabled {\n opacity: 0.5;\n cursor: default;\n }\n .tbw-context-menu-item.danger {\n color: light-dark(#c00, #f66);\n }\n .tbw-context-menu-icon {\n width: 16px;\n text-align: center;\n }\n .tbw-context-menu-label {\n flex: 1;\n }\n .tbw-context-menu-shortcut {\n color: light-dark(#888, #888);\n font-size: 11px;\n }\n .tbw-context-menu-arrow {\n font-size: 10px;\n color: light-dark(#888, #888);\n }\n .tbw-context-menu-separator {\n height: 1px;\n background: light-dark(#d0d0d4, #454545);\n margin: 4px 0;\n }\n`;\n\n/** Default menu items when none are configured */\nconst defaultItems: ContextMenuItem[] = [\n {\n id: 'copy',\n name: 'Copy',\n shortcut: 'Ctrl+C',\n action: (params) => {\n const grid = (params as ContextMenuParams & { grid?: { plugins?: { clipboard?: { copy?: () => void } } } }).grid;\n grid?.plugins?.clipboard?.copy?.();\n },\n },\n { separator: true, id: 'sep1', name: '' },\n {\n id: 'export-csv',\n name: 'Export CSV',\n action: (params) => {\n const grid = (params as ContextMenuParams & { grid?: { plugins?: { export?: { exportCsv?: () => void } } } })\n .grid;\n grid?.plugins?.export?.exportCsv?.();\n },\n },\n];\n\n/**\n * Context Menu Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ContextMenuPlugin({\n * enabled: true,\n * items: [\n * { id: 'edit', name: 'Edit', action: (params) => console.log('Edit', params) },\n * { separator: true, id: 'sep', name: '' },\n * { id: 'delete', name: 'Delete', action: (params) => console.log('Delete', params) },\n * ],\n * })\n * ```\n */\nexport class ContextMenuPlugin extends BaseGridPlugin<ContextMenuConfig> {\n readonly name = 'contextMenu';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ContextMenuConfig> {\n return {\n enabled: true,\n items: defaultItems,\n };\n }\n\n // ===== Internal State =====\n private isOpen = false;\n private position = { x: 0, y: 0 };\n private params: ContextMenuParams | null = null;\n private menuElement: HTMLElement | null = null;\n\n // ===== Lifecycle =====\n\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\n super.attach(grid);\n this.installGlobalHandlers();\n }\n\n override detach(): void {\n if (this.menuElement) {\n this.menuElement.remove();\n this.menuElement = null;\n }\n this.isOpen = false;\n this.params = null;\n }\n\n // ===== Private Methods =====\n\n private installGlobalHandlers(): void {\n // Inject global stylesheet for context menu (once)\n if (!globalStyleSheet && typeof document !== 'undefined') {\n globalStyleSheet = document.createElement('style');\n globalStyleSheet.id = 'tbw-context-menu-styles';\n globalStyleSheet.textContent = contextMenuStyles;\n document.head.appendChild(globalStyleSheet);\n }\n\n // Close menu on click outside\n if (!globalClickHandler) {\n globalClickHandler = () => {\n const menus = document.querySelectorAll('.tbw-context-menu');\n menus.forEach((menu) => menu.remove());\n };\n document.addEventListener('click', globalClickHandler);\n }\n\n // Close on escape\n if (!globalKeydownHandler) {\n globalKeydownHandler = (e: KeyboardEvent) => {\n if (e.key === 'Escape') {\n const menus = document.querySelectorAll('.tbw-context-menu');\n menus.forEach((menu) => menu.remove());\n }\n };\n document.addEventListener('keydown', globalKeydownHandler);\n }\n }\n\n // ===== Hooks =====\n\n override afterRender(): void {\n if (!this.config.enabled) return;\n\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const container = shadowRoot.children[0];\n if (!container) return;\n\n // Check if handler already attached\n if (container.getAttribute('data-context-menu-bound') === 'true') return;\n container.setAttribute('data-context-menu-bound', 'true');\n\n container.addEventListener('contextmenu', (e: Event) => {\n if (!this.config.enabled) return;\n\n const event = e as MouseEvent;\n event.preventDefault();\n\n const target = event.target as HTMLElement;\n const cell = target.closest('[data-row][data-col]');\n const header = target.closest('.header-cell');\n\n let params: ContextMenuParams;\n\n if (cell) {\n const rowIndex = parseInt(cell.getAttribute('data-row') ?? '-1', 10);\n const colIndex = parseInt(cell.getAttribute('data-col') ?? '-1', 10);\n const column = this.columns[colIndex];\n const row = this.rows[rowIndex];\n\n params = {\n row,\n rowIndex,\n column,\n columnIndex: colIndex,\n field: column?.field ?? '',\n value: row?.[column?.field as keyof typeof row] ?? null,\n isHeader: false,\n event,\n };\n } else if (header) {\n const colIndex = parseInt(header.getAttribute('data-col') ?? '-1', 10);\n const column = this.columns[colIndex];\n\n params = {\n row: null,\n rowIndex: -1,\n column,\n columnIndex: colIndex,\n field: column?.field ?? '',\n value: null,\n isHeader: true,\n event,\n };\n } else {\n return;\n }\n\n this.params = params;\n this.position = { x: event.clientX, y: event.clientY };\n\n const items = buildMenuItems(this.config.items ?? defaultItems, params);\n if (!items.length) return;\n\n if (this.menuElement) {\n this.menuElement.remove();\n }\n\n this.menuElement = createMenuElement(items, params, (item) => {\n if (item.action) {\n item.action(params);\n }\n this.menuElement?.remove();\n this.menuElement = null;\n this.isOpen = false;\n });\n\n document.body.appendChild(this.menuElement);\n positionMenu(this.menuElement, event.clientX, event.clientY);\n this.isOpen = true;\n\n this.emit('context-menu-open', { params, items });\n });\n }\n\n // ===== Public API =====\n\n /**\n * Programmatically show the context menu at the specified position.\n * @param x - X coordinate\n * @param y - Y coordinate\n * @param params - Partial context menu parameters\n */\n showMenu(x: number, y: number, params: Partial<ContextMenuParams>): void {\n const fullParams: ContextMenuParams = {\n row: params.row ?? null,\n rowIndex: params.rowIndex ?? -1,\n column: params.column ?? null,\n columnIndex: params.columnIndex ?? -1,\n field: params.field ?? '',\n value: params.value ?? null,\n isHeader: params.isHeader ?? false,\n event: params.event ?? new MouseEvent('contextmenu'),\n };\n\n const items = buildMenuItems(this.config.items ?? defaultItems, fullParams);\n\n if (this.menuElement) {\n this.menuElement.remove();\n }\n\n this.menuElement = createMenuElement(items, fullParams, (item) => {\n if (item.action) item.action(fullParams);\n this.menuElement?.remove();\n this.menuElement = null;\n this.isOpen = false;\n });\n\n document.body.appendChild(this.menuElement);\n positionMenu(this.menuElement, x, y);\n this.isOpen = true;\n }\n\n /**\n * Hide the context menu.\n */\n hideMenu(): void {\n if (this.menuElement) {\n this.menuElement.remove();\n this.menuElement = null;\n this.isOpen = false;\n }\n }\n\n /**\n * Check if the context menu is currently open.\n * @returns Whether the menu is open\n */\n isMenuOpen(): boolean {\n return this.isOpen;\n }\n\n // Styles are injected globally via installGlobalHandlers() since menu renders in document.body\n}\n","/**\n * CSV Export Utilities\n *\n * Functions for building and downloading CSV content.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\n\n/** CSV export options */\nexport interface CsvOptions {\n /** Field delimiter (default: ',') */\n delimiter?: string;\n /** Line separator (default: '\\n') */\n newline?: string;\n /** Whether to quote strings containing special characters (default: true) */\n quoteStrings?: boolean;\n /** Add UTF-8 BOM for Excel compatibility (default: false) */\n bom?: boolean;\n}\n\n/**\n * Format a value for CSV output.\n * Handles null, Date, objects, and strings with special characters.\n */\nexport function formatCsvValue(value: any, quote = true): string {\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n\n // Quote if contains special characters (comma, quote, newline)\n if (quote && (str.includes(',') || str.includes('\"') || str.includes('\\n') || str.includes('\\r'))) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build CSV content from rows and columns.\n */\nexport function buildCsv(rows: any[], columns: ColumnConfig[], params: ExportParams, options: CsvOptions = {}): string {\n const delimiter = options.delimiter ?? ',';\n const newline = options.newline ?? '\\n';\n const lines: string[] = [];\n\n // UTF-8 BOM for Excel compatibility\n const bom = options.bom ? '\\uFEFF' : '';\n\n // Build header row\n if (params.includeHeaders !== false) {\n const headerRow = columns.map((col) => {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n return formatCsvValue(processed);\n });\n lines.push(headerRow.join(delimiter));\n }\n\n // Build data rows\n for (const row of rows) {\n const cells = columns.map((col) => {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n return formatCsvValue(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return bom + lines.join(newline);\n}\n\n/**\n * Download a Blob as a file.\n */\nexport function downloadBlob(blob: Blob, fileName: string): void {\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n link.style.display = 'none';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Download CSV content as a file.\n */\nexport function downloadCsv(content: string, fileName: string): void {\n const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });\n downloadBlob(blob, fileName);\n}\n","/**\n * Excel Export Utilities\n *\n * Simple Excel XML format export (no external dependencies).\n * Produces XML Spreadsheet 2003 format which opens in Excel.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\nimport { downloadBlob } from './csv';\n\n/**\n * Escape XML special characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n\n/**\n * Build Excel XML content from rows and columns.\n * Uses XML Spreadsheet 2003 format for broad compatibility.\n */\nexport function buildExcelXml(rows: any[], columns: ColumnConfig[], params: ExportParams): string {\n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?mso-application progid=\"Excel.Sheet\"?>\n<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"\n xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\">\n<Worksheet ss:Name=\"Sheet1\">\n<Table>`;\n\n // Build header row\n if (params.includeHeaders !== false) {\n xml += '\\n<Row>';\n for (const col of columns) {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n xml += `<Cell><Data ss:Type=\"String\">${escapeXml(processed)}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n // Build data rows\n for (const row of rows) {\n xml += '\\n<Row>';\n for (const col of columns) {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n\n // Determine cell type based on value\n let type: 'Number' | 'String' | 'DateTime' = 'String';\n let displayValue = '';\n\n if (value == null) {\n displayValue = '';\n } else if (typeof value === 'number' && !isNaN(value)) {\n type = 'Number';\n displayValue = String(value);\n } else if (value instanceof Date) {\n type = 'DateTime';\n displayValue = value.toISOString();\n } else {\n displayValue = escapeXml(String(value));\n }\n\n xml += `<Cell><Data ss:Type=\"${type}\">${displayValue}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n xml += '\\n</Table>\\n</Worksheet>\\n</Workbook>';\n return xml;\n}\n\n/**\n * Download Excel XML content as a file.\n */\nexport function downloadExcel(content: string, fileName: string): void {\n const finalName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n const blob = new Blob([content], {\n type: 'application/vnd.ms-excel;charset=utf-8;',\n });\n downloadBlob(blob, finalName);\n}\n","/**\n * Export Plugin (Class-based)\n *\n * Provides data export functionality for tbw-grid.\n * Supports CSV, Excel (XML), and JSON formats.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildCsv, downloadBlob, downloadCsv } from './csv';\nimport { buildExcelXml, downloadExcel } from './excel';\nimport type { ExportCompleteDetail, ExportConfig, ExportFormat, ExportParams } from './types';\n\n/** Selection plugin state interface for type safety */\ninterface SelectionPluginState {\n selected: Set<number>;\n}\n\n/**\n * Export Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ExportPlugin({\n * enabled: true,\n * fileName: 'my-data',\n * includeHeaders: true,\n * onlyVisible: true,\n * })\n * ```\n */\nexport class ExportPlugin extends BaseGridPlugin<ExportConfig> {\n readonly name = 'export';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ExportConfig> {\n return {\n enabled: true,\n fileName: 'export',\n includeHeaders: true,\n onlyVisible: true,\n onlySelected: false,\n };\n }\n\n // ===== Internal State =====\n private isExportingFlag = false;\n private lastExportInfo: { format: ExportFormat; timestamp: Date } | null = null;\n\n // ===== Private Methods =====\n\n private performExport(format: ExportFormat, params?: Partial<ExportParams>): void {\n const config = this.config;\n\n // Build full params with defaults\n const fullParams: ExportParams = {\n format,\n fileName: params?.fileName ?? config.fileName ?? 'export',\n includeHeaders: params?.includeHeaders ?? config.includeHeaders,\n processCell: params?.processCell,\n processHeader: params?.processHeader,\n columns: params?.columns,\n rowIndices: params?.rowIndices,\n };\n\n // Get columns to export\n let columns = [...this.columns] as ColumnConfig[];\n if (config.onlyVisible) {\n columns = columns.filter((c) => !c.hidden && !c.field.startsWith('__'));\n }\n if (params?.columns) {\n const colSet = new Set(params.columns);\n columns = columns.filter((c) => colSet.has(c.field));\n }\n\n // Get rows to export\n let rows = [...this.rows] as any[];\n if (config.onlySelected) {\n const selectionState = this.getSelectionState();\n if (selectionState?.selected?.size) {\n const sortedIndices = [...selectionState.selected].sort((a, b) => a - b);\n rows = sortedIndices.map((i) => this.rows[i]).filter(Boolean);\n }\n }\n if (params?.rowIndices) {\n rows = params.rowIndices.map((i) => this.rows[i]).filter(Boolean);\n }\n\n this.isExportingFlag = true;\n let fileName = fullParams.fileName!;\n\n try {\n switch (format) {\n case 'csv': {\n const content = buildCsv(rows, columns, fullParams, { bom: true });\n fileName = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`;\n downloadCsv(content, fileName);\n break;\n }\n\n case 'excel': {\n const content = buildExcelXml(rows, columns, fullParams);\n fileName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n downloadExcel(content, fileName);\n break;\n }\n\n case 'json': {\n const jsonData = rows.map((row) => {\n const obj: Record<string, any> = {};\n for (const col of columns) {\n let value = row[col.field];\n if (fullParams.processCell) {\n value = fullParams.processCell(value, col.field, row);\n }\n obj[col.field] = value;\n }\n return obj;\n });\n const content = JSON.stringify(jsonData, null, 2);\n fileName = fileName.endsWith('.json') ? fileName : `${fileName}.json`;\n const blob = new Blob([content], { type: 'application/json' });\n downloadBlob(blob, fileName);\n break;\n }\n }\n\n this.lastExportInfo = { format, timestamp: new Date() };\n\n this.emit<ExportCompleteDetail>('export-complete', {\n format,\n fileName,\n rowCount: rows.length,\n columnCount: columns.length,\n });\n } finally {\n this.isExportingFlag = false;\n }\n }\n\n private getSelectionState(): SelectionPluginState | null {\n try {\n const grid = this.grid as any;\n return grid?.getPluginState?.('selection') ?? null;\n } catch {\n return null;\n }\n }\n\n // ===== Public API =====\n\n /**\n * Export data to CSV format.\n * @param params - Optional export parameters\n */\n exportCsv(params?: Partial<ExportParams>): void {\n this.performExport('csv', params);\n }\n\n /**\n * Export data to Excel format (XML Spreadsheet).\n * @param params - Optional export parameters\n */\n exportExcel(params?: Partial<ExportParams>): void {\n this.performExport('excel', params);\n }\n\n /**\n * Export data to JSON format.\n * @param params - Optional export parameters\n */\n exportJson(params?: Partial<ExportParams>): void {\n this.performExport('json', params);\n }\n\n /**\n * Check if an export is currently in progress.\n * @returns Whether export is in progress\n */\n isExporting(): boolean {\n return this.isExportingFlag;\n }\n\n /**\n * Get information about the last export.\n * @returns Export info or null if no export has occurred\n */\n getLastExport(): { format: ExportFormat; timestamp: Date } | null {\n return this.lastExportInfo;\n }\n}\n","/**\n * Row Virtualization Core Logic\n *\n * Pure functions for vertical row virtualization operations.\n * Manages which rows are rendered based on scroll position and viewport size.\n */\n\n/** Result of computing a virtual window */\nexport interface VirtualWindow {\n /** First row index to render (inclusive) */\n start: number;\n /** Last row index to render (exclusive) */\n end: number;\n /** Pixel offset to apply to the rows container (translateY) */\n offsetY: number;\n /** Total height of the scrollable content */\n totalHeight: number;\n}\n\n/** Parameters for computing the virtual window */\nexport interface VirtualWindowParams {\n /** Total number of rows */\n totalRows: number;\n /** Height of the viewport in pixels */\n viewportHeight: number;\n /** Current scroll top position */\n scrollTop: number;\n /** Height of each row in pixels */\n rowHeight: number;\n /** Number of extra rows to render above/below viewport */\n overscan: number;\n /** Previous scroll position for velocity calculation */\n prevScrollTop?: number;\n}\n\n/**\n * Compute the virtual row window based on scroll position and viewport.\n * Uses directional overscan - renders more rows in the scroll direction.\n *\n * @param params - Parameters for computing the window\n * @returns VirtualWindow with start/end indices and transforms\n */\nexport function computeVirtualWindow(params: VirtualWindowParams): VirtualWindow {\n const { totalRows, viewportHeight, scrollTop, rowHeight, overscan } = params;\n\n // Simple approach: render a large fixed window centered on current position\n // This is more predictable than velocity-based sizing\n const visibleRows = Math.ceil(viewportHeight / rowHeight);\n\n // Render overscan rows in each direction (total window = visible + 2*overscan)\n let start = Math.floor(scrollTop / rowHeight) - overscan;\n if (start < 0) start = 0;\n\n let end = start + visibleRows + overscan * 2;\n if (end > totalRows) end = totalRows;\n\n // Ensure start is adjusted if we hit the end\n if (end === totalRows && start > 0) {\n start = Math.max(0, end - visibleRows - overscan * 2);\n }\n\n return {\n start,\n end,\n offsetY: start * rowHeight,\n totalHeight: totalRows * rowHeight,\n };\n}\n\n/**\n * Determine if virtualization should be bypassed for small datasets.\n * When there are very few items, the overhead of virtualization isn't worth it.\n *\n * @param totalRows - Total number of items\n * @param threshold - Bypass threshold (skip virtualization if totalRows <= threshold)\n * @returns True if virtualization should be bypassed\n */\nexport function shouldBypassVirtualization(totalRows: number, threshold: number): boolean {\n return totalRows <= threshold;\n}\n\n/**\n * Compute the row index from a Y pixel position.\n *\n * @param y - Y position in pixels (relative to content top)\n * @param rowHeight - Height of each row\n * @returns Row index at that position\n */\nexport function getRowIndexFromY(y: number, rowHeight: number): number {\n return Math.floor(y / rowHeight);\n}\n\n/**\n * Compute the Y offset for a given row index.\n *\n * @param rowIndex - Row index\n * @param rowHeight - Height of each row\n * @returns Y position in pixels\n */\nexport function getRowOffsetY(rowIndex: number, rowHeight: number): number {\n return rowIndex * rowHeight;\n}\n\n/**\n * Compute the range of rows that are fully or partially visible in the viewport.\n * This is the range without overscan - just what the user can actually see.\n *\n * @param scrollTop - Current scroll position\n * @param viewportHeight - Height of the viewport\n * @param rowHeight - Height of each row\n * @param totalRows - Total number of rows\n * @returns Object with first and last visible row indices\n */\nexport function getVisibleRowRange(\n scrollTop: number,\n viewportHeight: number,\n rowHeight: number,\n totalRows: number\n): { first: number; last: number } {\n const first = Math.floor(scrollTop / rowHeight);\n let last = Math.ceil((scrollTop + viewportHeight) / rowHeight) - 1;\n if (last >= totalRows) last = totalRows - 1;\n if (first > last) return { first: 0, last: 0 };\n return { first, last };\n}\n\n/**\n * Check if a row is currently rendered in the virtual window.\n *\n * @param rowIndex - Row index to check\n * @param windowStart - Start of the render window\n * @param windowEnd - End of the render window\n * @returns True if the row is rendered\n */\nexport function isRowRendered(rowIndex: number, windowStart: number, windowEnd: number): boolean {\n return rowIndex >= windowStart && rowIndex < windowEnd;\n}\n\n/**\n * Clamp a row index to valid bounds.\n *\n * @param rowIndex - Row index to clamp\n * @param totalRows - Total number of rows\n * @returns Clamped row index (0 to totalRows - 1)\n */\nexport function clampRowIndex(rowIndex: number, totalRows: number): number {\n if (totalRows === 0) return 0;\n if (rowIndex < 0) return 0;\n if (rowIndex >= totalRows) return totalRows - 1;\n return rowIndex;\n}\n\n/**\n * Compute the scroll position needed to bring a row into view.\n *\n * @param rowIndex - Row index to scroll to\n * @param rowHeight - Height of each row\n * @param viewportHeight - Height of the viewport\n * @param currentScrollTop - Current scroll position\n * @returns New scroll position, or null if row is already visible\n */\nexport function computeScrollToRow(\n rowIndex: number,\n rowHeight: number,\n viewportHeight: number,\n currentScrollTop: number\n): number | null {\n const rowTop = rowIndex * rowHeight;\n const rowBottom = rowTop + rowHeight;\n\n // Row is above viewport\n if (rowTop < currentScrollTop) {\n return rowTop;\n }\n\n // Row is below viewport\n if (rowBottom > currentScrollTop + viewportHeight) {\n return rowBottom - viewportHeight;\n }\n\n // Row is already visible\n return null;\n}\n","/**\n * Filter Model Core Logic\n *\n * Pure functions for filtering operations.\n */\n\nimport type { FilterModel } from './types';\n\n/**\n * Check if a single row matches a filter condition.\n *\n * @param row - The row data object\n * @param filter - The filter to apply\n * @param caseSensitive - Whether text comparisons are case sensitive\n * @returns True if the row matches the filter\n */\nexport function matchesFilter(row: Record<string, unknown>, filter: FilterModel, caseSensitive = false): boolean {\n const rawValue = row[filter.field];\n\n // Handle blank/notBlank first - these work on null/undefined/empty\n if (filter.operator === 'blank') {\n return rawValue == null || rawValue === '';\n }\n if (filter.operator === 'notBlank') {\n return rawValue != null && rawValue !== '';\n }\n\n // Null/undefined values don't match other filters\n if (rawValue == null) return false;\n\n // Prepare values for comparison\n const stringValue = String(rawValue);\n const compareValue = caseSensitive ? stringValue : stringValue.toLowerCase();\n const filterValue = caseSensitive ? String(filter.value) : String(filter.value).toLowerCase();\n\n switch (filter.operator) {\n // Text operators\n case 'contains':\n return compareValue.includes(filterValue);\n\n case 'notContains':\n return !compareValue.includes(filterValue);\n\n case 'equals':\n return compareValue === filterValue;\n\n case 'notEquals':\n return compareValue !== filterValue;\n\n case 'startsWith':\n return compareValue.startsWith(filterValue);\n\n case 'endsWith':\n return compareValue.endsWith(filterValue);\n\n // Number/Date operators (use raw numeric values)\n case 'lessThan':\n return Number(rawValue) < Number(filter.value);\n\n case 'lessThanOrEqual':\n return Number(rawValue) <= Number(filter.value);\n\n case 'greaterThan':\n return Number(rawValue) > Number(filter.value);\n\n case 'greaterThanOrEqual':\n return Number(rawValue) >= Number(filter.value);\n\n case 'between':\n return Number(rawValue) >= Number(filter.value) && Number(rawValue) <= Number(filter.valueTo);\n\n // Set operators\n case 'in':\n return Array.isArray(filter.value) && filter.value.includes(rawValue);\n\n case 'notIn':\n return Array.isArray(filter.value) && !filter.value.includes(rawValue);\n\n default:\n return true;\n }\n}\n\n/**\n * Filter rows based on multiple filter conditions (AND logic).\n * All filters must match for a row to be included.\n *\n * @param rows - The rows to filter\n * @param filters - Array of filters to apply\n * @param caseSensitive - Whether text comparisons are case sensitive\n * @returns Filtered rows\n */\nexport function filterRows<T extends Record<string, unknown>>(\n rows: T[],\n filters: FilterModel[],\n caseSensitive = false\n): T[] {\n if (!filters.length) return rows;\n return rows.filter((row) => filters.every((f) => matchesFilter(row, f, caseSensitive)));\n}\n\n/**\n * Compute a cache key for a set of filters.\n * Used for memoization of filter results.\n *\n * @param filters - Array of filters\n * @returns Stable string key for the filter set\n */\nexport function computeFilterCacheKey(filters: FilterModel[]): string {\n return JSON.stringify(\n filters.map((f) => ({\n field: f.field,\n operator: f.operator,\n value: f.value,\n valueTo: f.valueTo,\n }))\n );\n}\n\n/**\n * Extract unique values from a field across all rows.\n * Useful for populating \"set\" filter dropdowns.\n *\n * @param rows - The rows to extract values from\n * @param field - The field name\n * @returns Sorted array of unique non-null values\n */\nexport function getUniqueValues<T extends Record<string, unknown>>(rows: T[], field: string): unknown[] {\n const values = new Set<unknown>();\n for (const row of rows) {\n const value = row[field];\n if (value != null) {\n values.add(value);\n }\n }\n return [...values].sort((a, b) => {\n // Handle mixed types gracefully\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n return String(a).localeCompare(String(b));\n });\n}\n","/**\n * Filtering Plugin (Class-based)\n *\n * Provides comprehensive filtering functionality for tbw-grid.\n * Supports text, number, date, set, and boolean filters with caching.\n * Includes UI with filter buttons in headers and dropdown filter panels.\n */\n\nimport { BaseGridPlugin, type GridElement } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, ColumnState } from '../../core/types';\nimport { computeVirtualWindow, shouldBypassVirtualization } from '../../core/internal/virtualization';\nimport { computeFilterCacheKey, filterRows, getUniqueValues } from './filter-model';\nimport type { FilterChangeDetail, FilterConfig, FilterModel, FilterPanelParams } from './types';\n\n/** Global styles for filter panel (rendered in document.body) */\nconst filterPanelStyles = `\n.tbw-filter-panel {\n position: fixed;\n background: var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));\n color: var(--tbw-filter-panel-fg, var(--tbw-color-fg, light-dark(#222222, #eeeeee)));\n border: 1px solid var(--tbw-filter-panel-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));\n border-radius: var(--tbw-filter-panel-radius, var(--tbw-border-radius, 4px));\n box-shadow: 0 4px 16px var(--tbw-filter-panel-shadow, var(--tbw-color-shadow, light-dark(rgba(0,0,0,0.1), rgba(0,0,0,0.3))));\n padding: 12px;\n z-index: 10000;\n min-width: 200px;\n max-width: 280px;\n max-height: 350px;\n display: flex;\n flex-direction: column;\n font-family: var(--tbw-font-family, system-ui, sans-serif);\n font-size: var(--tbw-font-size, 13px);\n}\n\n.tbw-filter-search {\n margin-bottom: 8px;\n}\n\n.tbw-filter-search-input {\n width: 100%;\n padding: 6px 10px;\n background: var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));\n color: inherit;\n border: 1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));\n border-radius: var(--tbw-filter-input-radius, 4px);\n font-size: inherit;\n box-sizing: border-box;\n}\n\n.tbw-filter-search-input:focus {\n outline: none;\n border-color: var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));\n box-shadow: 0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%);\n}\n\n.tbw-filter-actions {\n display: flex;\n padding: 4px 2px;\n margin-bottom: 8px;\n border-bottom: 1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));\n}\n\n.tbw-filter-action-btn {\n background: transparent;\n border: none;\n color: var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));\n cursor: pointer;\n font-size: 12px;\n padding: 2px 0;\n}\n\n.tbw-filter-action-btn:hover {\n text-decoration: underline;\n}\n\n.tbw-filter-values {\n flex: 1;\n overflow-y: auto;\n margin-bottom: 8px;\n max-height: 180px;\n position: relative;\n}\n\n.tbw-filter-values-spacer {\n width: 1px;\n}\n\n.tbw-filter-values-content {\n position: absolute;\n top: 0;\n left: 0;\n right: 0;\n}\n\n.tbw-filter-value-item {\n display: flex;\n align-items: center;\n gap: 8px;\n padding: 4px 2px;\n cursor: pointer;\n border-radius: 3px;\n}\n\n.tbw-filter-value-item:hover {\n background: var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)));\n}\n\n.tbw-filter-checkbox {\n margin: 0;\n cursor: pointer;\n accent-color: var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));\n}\n\n.tbw-filter-no-match {\n color: var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));\n padding: 8px 0;\n text-align: center;\n font-style: italic;\n}\n\n.tbw-filter-buttons {\n display: flex;\n gap: 8px;\n padding-top: 8px;\n border-top: 1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));\n}\n\n.tbw-filter-apply-btn {\n flex: 1;\n padding: 6px 12px;\n background: var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));\n color: var(--tbw-filter-accent-fg, var(--tbw-color-accent-fg, light-dark(#ffffff, #000000)));\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n}\n\n.tbw-filter-apply-btn:hover {\n filter: brightness(0.9);\n}\n\n.tbw-filter-clear-btn {\n flex: 1;\n padding: 6px 12px;\n background: transparent;\n color: var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));\n border: 1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));\n border-radius: 4px;\n cursor: pointer;\n font-size: 13px;\n}\n\n.tbw-filter-clear-btn:hover {\n background: var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)));\n}\n`;\n\n/**\n * Filtering Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new FilteringPlugin({ enabled: true, debounceMs: 300 })\n * ```\n */\nexport class FilteringPlugin extends BaseGridPlugin<FilterConfig> {\n readonly name = 'filtering';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<FilterConfig> {\n return {\n enabled: true,\n debounceMs: 300,\n caseSensitive: false,\n trimInput: true,\n useWorker: true,\n };\n }\n\n // ===== Internal State =====\n private filters: Map<string, FilterModel> = new Map();\n private cachedResult: unknown[] | null = null;\n private cacheKey: string | null = null;\n private openPanelField: string | null = null;\n private panelElement: HTMLElement | null = null;\n private searchText: Map<string, string> = new Map();\n private excludedValues: Map<string, Set<unknown>> = new Map();\n private documentClickHandler: ((e: MouseEvent) => void) | null = null;\n private globalStylesInjected = false;\n\n // Virtualization constants for filter value list\n private static readonly LIST_ITEM_HEIGHT = 28;\n private static readonly LIST_OVERSCAN = 3;\n private static readonly LIST_BYPASS_THRESHOLD = 50; // Don't virtualize if < 50 items\n\n // ===== Lifecycle =====\n\n override attach(grid: GridElement): void {\n super.attach(grid);\n this.injectGlobalStyles();\n }\n\n override detach(): void {\n this.filters.clear();\n this.cachedResult = null;\n this.cacheKey = null;\n this.openPanelField = null;\n if (this.panelElement) {\n this.panelElement.remove();\n this.panelElement = null;\n }\n this.searchText.clear();\n this.excludedValues.clear();\n this.removeDocumentClickHandler();\n }\n\n // ===== Hooks =====\n\n override processRows(rows: readonly unknown[]): unknown[] {\n const filterList = [...this.filters.values()];\n if (!filterList.length) return [...rows];\n\n // Check cache\n const newCacheKey = computeFilterCacheKey(filterList);\n if (this.cacheKey === newCacheKey && this.cachedResult) {\n return this.cachedResult;\n }\n\n // Filter rows synchronously (worker support can be added later)\n const result = filterRows([...rows] as Record<string, unknown>[], filterList, this.config.caseSensitive);\n\n // Update cache\n this.cachedResult = result;\n this.cacheKey = newCacheKey;\n\n return result;\n }\n\n override afterRender(): void {\n if (!this.config.enabled) return;\n\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n // Find all header cells (using part attribute, not class)\n const headerCells = shadowRoot.querySelectorAll('[part~=\"header-cell\"]');\n headerCells.forEach((cell) => {\n const colIndex = cell.getAttribute('data-col');\n if (colIndex === null) return;\n\n const col = this.columns[parseInt(colIndex, 10)] as ColumnConfig;\n if (!col || col.filterable === false) return;\n\n // Skip if button already exists\n if (cell.querySelector('.tbw-filter-btn')) return;\n\n const field = col.field;\n if (!field) return;\n\n // Create filter button\n const filterBtn = document.createElement('button');\n filterBtn.className = 'tbw-filter-btn';\n filterBtn.setAttribute('aria-label', `Filter ${col.header ?? field}`);\n filterBtn.innerHTML = `<svg viewBox=\"0 0 16 16\" width=\"12\" height=\"12\"><path fill=\"currentColor\" d=\"M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z\"/></svg>`;\n\n // Mark button as active if filter exists\n if (this.filters.has(field)) {\n filterBtn.classList.add('active');\n cell.classList.add('filtered');\n }\n\n filterBtn.addEventListener('click', (e) => {\n e.stopPropagation();\n this.toggleFilterPanel(field, col, filterBtn);\n });\n\n // Append to header cell\n cell.appendChild(filterBtn);\n });\n }\n\n // ===== Public API =====\n\n /**\n * Set a filter on a specific field.\n * Pass null to remove the filter.\n */\n setFilter(field: string, filter: Omit<FilterModel, 'field'> | null): void {\n if (filter === null) {\n this.filters.delete(field);\n } else {\n this.filters.set(field, { ...filter, field });\n }\n // Invalidate cache\n this.cachedResult = null;\n this.cacheKey = null;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: [...this.filters.values()],\n filteredRowCount: 0, // Will be accurate after processRows\n });\n this.requestRender();\n }\n\n /**\n * Get the current filter for a field.\n */\n getFilter(field: string): FilterModel | undefined {\n return this.filters.get(field);\n }\n\n /**\n * Get all active filters.\n */\n getFilters(): FilterModel[] {\n return [...this.filters.values()];\n }\n\n /**\n * Alias for getFilters() to match functional API naming.\n */\n getFilterModel(): FilterModel[] {\n return this.getFilters();\n }\n\n /**\n * Set filters from an array (replaces all existing filters).\n */\n setFilterModel(filters: FilterModel[]): void {\n this.filters.clear();\n for (const filter of filters) {\n this.filters.set(filter.field, filter);\n }\n this.cachedResult = null;\n this.cacheKey = null;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: [...this.filters.values()],\n filteredRowCount: 0,\n });\n this.requestRender();\n }\n\n /**\n * Clear all filters.\n */\n clearAllFilters(): void {\n this.filters.clear();\n this.excludedValues.clear();\n this.searchText.clear();\n this.cachedResult = null;\n this.cacheKey = null;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: [],\n filteredRowCount: this.rows.length,\n });\n this.requestRender();\n }\n\n /**\n * Clear filter for a specific field.\n */\n clearFieldFilter(field: string): void {\n this.filters.delete(field);\n this.excludedValues.delete(field);\n this.searchText.delete(field);\n\n this.cachedResult = null;\n this.cacheKey = null;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: [...this.filters.values()],\n filteredRowCount: 0,\n });\n this.requestRender();\n }\n\n /**\n * Check if a field has an active filter.\n */\n isFieldFiltered(field: string): boolean {\n return this.filters.has(field);\n }\n\n /**\n * Get the count of filtered rows (from cache).\n */\n getFilteredRowCount(): number {\n return this.cachedResult?.length ?? this.rows.length;\n }\n\n /**\n * Get all active filters (alias for getFilters).\n */\n getActiveFilters(): FilterModel[] {\n return this.getFilters();\n }\n\n /**\n * Get unique values for a field (for set filter dropdowns).\n * Uses sourceRows to include all values regardless of current filter.\n */\n getUniqueValues(field: string): unknown[] {\n return getUniqueValues(this.sourceRows as Record<string, unknown>[], field);\n }\n\n // ===== Private Methods =====\n\n /**\n * Inject global styles for filter panel (rendered in document.body)\n */\n private injectGlobalStyles(): void {\n if (this.globalStylesInjected) return;\n if (document.getElementById('tbw-filter-panel-styles')) {\n this.globalStylesInjected = true;\n return;\n }\n const style = document.createElement('style');\n style.id = 'tbw-filter-panel-styles';\n style.textContent = filterPanelStyles;\n document.head.appendChild(style);\n this.globalStylesInjected = true;\n }\n\n /**\n * Toggle the filter panel for a field\n */\n private toggleFilterPanel(field: string, column: ColumnConfig, buttonEl: HTMLElement): void {\n // Close if already open\n if (this.openPanelField === field) {\n this.closeFilterPanel();\n return;\n }\n\n // Close any existing panel\n this.closeFilterPanel();\n\n // Create panel\n const panel = document.createElement('div');\n panel.className = 'tbw-filter-panel';\n this.panelElement = panel;\n this.openPanelField = field;\n\n // Get unique values for this field (from source rows, not filtered)\n const uniqueValues = getUniqueValues(this.sourceRows as Record<string, unknown>[], field);\n\n // Get current excluded values or initialize empty\n let excludedSet = this.excludedValues.get(field);\n if (!excludedSet) {\n excludedSet = new Set();\n this.excludedValues.set(field, excludedSet);\n }\n\n // Get current search text\n const currentSearchText = this.searchText.get(field) ?? '';\n\n // Create panel params for custom renderer\n const params: FilterPanelParams = {\n field,\n column,\n uniqueValues,\n excludedValues: excludedSet,\n searchText: currentSearchText,\n applySetFilter: (excluded: unknown[]) => {\n this.applySetFilter(field, excluded);\n this.closeFilterPanel();\n },\n applyTextFilter: (operator, value, valueTo) => {\n this.applyTextFilter(field, operator, value, valueTo);\n this.closeFilterPanel();\n },\n clearFilter: () => {\n this.clearFieldFilter(field);\n this.closeFilterPanel();\n },\n closePanel: () => this.closeFilterPanel(),\n };\n\n // Use custom renderer or default\n // Custom renderer can return undefined to fall back to default panel for specific columns\n let usedCustomRenderer = false;\n if (this.config.filterPanelRenderer) {\n const result = this.config.filterPanelRenderer(panel, params);\n // If renderer added content to panel, it handled rendering\n usedCustomRenderer = panel.children.length > 0;\n }\n if (!usedCustomRenderer) {\n this.renderDefaultFilterPanel(panel, params, uniqueValues, excludedSet);\n }\n\n // Position and append to body\n document.body.appendChild(panel);\n this.positionPanel(panel, buttonEl);\n\n // Add global click handler to close on outside click\n const handler = (e: MouseEvent) => {\n if (!panel.contains(e.target as Node) && e.target !== buttonEl) {\n this.closeFilterPanel();\n }\n };\n this.documentClickHandler = handler;\n // Defer to next tick to avoid immediate close\n setTimeout(() => {\n document.addEventListener('click', handler);\n }, 0);\n }\n\n /**\n * Close the filter panel\n */\n private closeFilterPanel(): void {\n if (this.panelElement) {\n this.panelElement.remove();\n this.panelElement = null;\n }\n this.openPanelField = null;\n this.removeDocumentClickHandler();\n }\n\n /**\n * Remove the document click handler\n */\n private removeDocumentClickHandler(): void {\n if (this.documentClickHandler) {\n document.removeEventListener('click', this.documentClickHandler);\n this.documentClickHandler = null;\n }\n }\n\n /**\n * Position the panel below the button\n */\n private positionPanel(panel: HTMLElement, buttonEl: HTMLElement): void {\n const rect = buttonEl.getBoundingClientRect();\n panel.style.position = 'fixed';\n panel.style.top = `${rect.bottom + 4}px`;\n panel.style.left = `${rect.left}px`;\n\n // Adjust if overflows right edge\n requestAnimationFrame(() => {\n const panelRect = panel.getBoundingClientRect();\n if (panelRect.right > window.innerWidth - 8) {\n panel.style.left = `${window.innerWidth - panelRect.width - 8}px`;\n }\n // Adjust if overflows bottom\n if (panelRect.bottom > window.innerHeight - 8) {\n panel.style.top = `${rect.top - panelRect.height - 4}px`;\n }\n });\n }\n\n /**\n * Render the default filter panel content\n */\n private renderDefaultFilterPanel(\n panel: HTMLElement,\n params: FilterPanelParams,\n uniqueValues: unknown[],\n excludedValues: Set<unknown>\n ): void {\n const { field } = params;\n\n // Search input\n const searchContainer = document.createElement('div');\n searchContainer.className = 'tbw-filter-search';\n\n const searchInput = document.createElement('input');\n searchInput.type = 'text';\n searchInput.placeholder = 'Search...';\n searchInput.className = 'tbw-filter-search-input';\n searchInput.value = this.searchText.get(field) ?? '';\n searchContainer.appendChild(searchInput);\n panel.appendChild(searchContainer);\n\n // Select All tristate checkbox\n const actionsRow = document.createElement('div');\n actionsRow.className = 'tbw-filter-actions';\n\n const selectAllLabel = document.createElement('label');\n selectAllLabel.className = 'tbw-filter-value-item';\n selectAllLabel.style.padding = '0';\n selectAllLabel.style.margin = '0';\n\n const selectAllCheckbox = document.createElement('input');\n selectAllCheckbox.type = 'checkbox';\n selectAllCheckbox.className = 'tbw-filter-checkbox';\n\n const selectAllText = document.createElement('span');\n selectAllText.textContent = 'Select All';\n\n selectAllLabel.appendChild(selectAllCheckbox);\n selectAllLabel.appendChild(selectAllText);\n actionsRow.appendChild(selectAllLabel);\n\n // Update tristate checkbox based on checkState\n const updateSelectAllState = () => {\n const values = [...checkState.values()];\n const allChecked = values.every((v) => v);\n const noneChecked = values.every((v) => !v);\n\n selectAllCheckbox.checked = allChecked;\n selectAllCheckbox.indeterminate = !allChecked && !noneChecked;\n };\n\n // Toggle all on click\n selectAllCheckbox.addEventListener('change', () => {\n const newState = selectAllCheckbox.checked;\n for (const key of checkState.keys()) {\n checkState.set(key, newState);\n }\n updateSelectAllState();\n renderVisibleItems();\n });\n\n panel.appendChild(actionsRow);\n\n // Values container with virtualization support\n const valuesContainer = document.createElement('div');\n valuesContainer.className = 'tbw-filter-values';\n\n // Spacer for virtual height\n const spacer = document.createElement('div');\n spacer.className = 'tbw-filter-values-spacer';\n valuesContainer.appendChild(spacer);\n\n // Content container positioned absolutely\n const contentContainer = document.createElement('div');\n contentContainer.className = 'tbw-filter-values-content';\n valuesContainer.appendChild(contentContainer);\n\n // Track current check state for values (persists across virtualizations)\n const checkState = new Map<string, boolean>();\n uniqueValues.forEach((value) => {\n const key = value == null ? '__null__' : String(value);\n checkState.set(key, !excludedValues.has(value));\n });\n\n // Initialize select all state\n updateSelectAllState();\n\n // Filtered values cache\n let filteredValues: unknown[] = [];\n\n // Create a single checkbox item element\n const createItem = (value: unknown, index: number): HTMLElement => {\n const strValue = value == null ? '(Blank)' : String(value);\n const key = value == null ? '__null__' : String(value);\n\n const item = document.createElement('label');\n item.className = 'tbw-filter-value-item';\n item.style.position = 'absolute';\n item.style.top = `${index * FilteringPlugin.LIST_ITEM_HEIGHT}px`;\n item.style.left = '0';\n item.style.right = '0';\n item.style.height = `${FilteringPlugin.LIST_ITEM_HEIGHT}px`;\n item.style.boxSizing = 'border-box';\n\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.className = 'tbw-filter-checkbox';\n checkbox.checked = checkState.get(key) ?? true;\n checkbox.dataset.value = key;\n\n // Sync check state on change and update tristate checkbox\n checkbox.addEventListener('change', () => {\n checkState.set(key, checkbox.checked);\n updateSelectAllState();\n });\n\n const label = document.createElement('span');\n label.textContent = strValue;\n\n item.appendChild(checkbox);\n item.appendChild(label);\n return item;\n };\n\n // Render visible items using virtualization\n const renderVisibleItems = () => {\n const totalItems = filteredValues.length;\n const viewportHeight = valuesContainer.clientHeight;\n const scrollTop = valuesContainer.scrollTop;\n\n // Set total height for scrollbar\n spacer.style.height = `${totalItems * FilteringPlugin.LIST_ITEM_HEIGHT}px`;\n\n // Bypass virtualization for small lists\n if (shouldBypassVirtualization(totalItems, FilteringPlugin.LIST_BYPASS_THRESHOLD / 3)) {\n contentContainer.innerHTML = '';\n contentContainer.style.transform = 'translateY(0px)';\n filteredValues.forEach((value, idx) => {\n contentContainer.appendChild(createItem(value, idx));\n });\n return;\n }\n\n // Use computeVirtualWindow for real-scroll virtualization\n const window = computeVirtualWindow({\n totalRows: totalItems,\n viewportHeight,\n scrollTop,\n rowHeight: FilteringPlugin.LIST_ITEM_HEIGHT,\n overscan: FilteringPlugin.LIST_OVERSCAN,\n });\n\n // Position content container\n contentContainer.style.transform = `translateY(${window.offsetY}px)`;\n\n // Clear and render visible items\n contentContainer.innerHTML = '';\n for (let i = window.start; i < window.end; i++) {\n contentContainer.appendChild(createItem(filteredValues[i], i - window.start));\n }\n };\n\n // Filter and re-render values\n const renderValues = (filterText: string) => {\n const lowerFilter = filterText.toLowerCase();\n\n // Filter the unique values\n filteredValues = uniqueValues.filter((value) => {\n const strValue = value == null ? '(Blank)' : String(value);\n return !filterText || strValue.toLowerCase().includes(lowerFilter);\n });\n\n if (filteredValues.length === 0) {\n spacer.style.height = '0px';\n contentContainer.innerHTML = '';\n const noMatch = document.createElement('div');\n noMatch.className = 'tbw-filter-no-match';\n noMatch.textContent = 'No matching values';\n contentContainer.appendChild(noMatch);\n return;\n }\n\n renderVisibleItems();\n };\n\n // Scroll handler for virtualization\n valuesContainer.addEventListener(\n 'scroll',\n () => {\n if (filteredValues.length > 0) {\n renderVisibleItems();\n }\n },\n { passive: true }\n );\n\n renderValues(searchInput.value);\n panel.appendChild(valuesContainer);\n\n // Debounced search\n let debounceTimer: ReturnType<typeof setTimeout>;\n searchInput.addEventListener('input', () => {\n clearTimeout(debounceTimer);\n debounceTimer = setTimeout(() => {\n this.searchText.set(field, searchInput.value);\n renderValues(searchInput.value);\n }, this.config.debounceMs ?? 150);\n });\n\n // Apply/Clear buttons\n const buttonRow = document.createElement('div');\n buttonRow.className = 'tbw-filter-buttons';\n\n const applyBtn = document.createElement('button');\n applyBtn.className = 'tbw-filter-apply-btn';\n applyBtn.textContent = 'Apply';\n applyBtn.addEventListener('click', () => {\n // Read from checkState map (works with virtualization)\n const excluded: unknown[] = [];\n for (const [key, isChecked] of checkState) {\n if (!isChecked) {\n if (key === '__null__') {\n excluded.push(null);\n } else {\n // Try to match original value type\n const original = uniqueValues.find((v) => String(v) === key);\n excluded.push(original !== undefined ? original : key);\n }\n }\n }\n params.applySetFilter(excluded);\n });\n buttonRow.appendChild(applyBtn);\n\n const clearBtn = document.createElement('button');\n clearBtn.className = 'tbw-filter-clear-btn';\n clearBtn.textContent = 'Clear Filter';\n clearBtn.addEventListener('click', () => {\n params.clearFilter();\n });\n buttonRow.appendChild(clearBtn);\n\n panel.appendChild(buttonRow);\n }\n\n /**\n * Apply a set filter (exclude values)\n */\n private applySetFilter(field: string, excluded: unknown[]): void {\n // Store excluded values\n this.excludedValues.set(field, new Set(excluded));\n\n if (excluded.length === 0) {\n // No exclusions = no filter\n this.filters.delete(field);\n } else {\n // Create \"notIn\" filter\n this.filters.set(field, {\n field,\n type: 'set',\n operator: 'notIn',\n value: excluded,\n });\n }\n\n this.cachedResult = null;\n this.cacheKey = null;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: [...this.filters.values()],\n filteredRowCount: 0,\n });\n this.requestRender();\n }\n\n /**\n * Apply a text filter\n */\n private applyTextFilter(field: string, operator: FilterModel['operator'], value: string, valueTo?: string): void {\n this.filters.set(field, {\n field,\n type: 'text',\n operator,\n value,\n valueTo,\n });\n\n this.cachedResult = null;\n this.cacheKey = null;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: [...this.filters.values()],\n filteredRowCount: 0,\n });\n this.requestRender();\n }\n\n // ===== Column State Hooks =====\n\n /**\n * Return filter state for a column if it has an active filter.\n */\n override getColumnState(field: string): Partial<ColumnState> | undefined {\n const filterModel = this.filters.get(field);\n if (!filterModel) return undefined;\n\n return {\n filter: {\n type: filterModel.type,\n operator: filterModel.operator,\n value: filterModel.value,\n valueTo: filterModel.valueTo,\n },\n };\n }\n\n /**\n * Apply filter state from column state.\n */\n override applyColumnState(field: string, state: ColumnState): void {\n // Only process if the column has filter state\n if (!state.filter) {\n this.filters.delete(field);\n return;\n }\n\n // Reconstruct the FilterModel from the stored state\n const filterModel: FilterModel = {\n field,\n type: state.filter.type,\n operator: state.filter.operator as FilterModel['operator'],\n value: state.filter.value,\n valueTo: state.filter.valueTo,\n };\n\n this.filters.set(field, filterModel);\n // Invalidate cache so filter is reapplied\n this.cachedResult = null;\n this.cacheKey = null;\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n .header-cell.filtered::before {\n content: '';\n position: absolute;\n top: 4px;\n right: 4px;\n width: 6px;\n height: 6px;\n background: var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));\n border-radius: 50%;\n }\n .tbw-filter-btn {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n background: transparent;\n border: none;\n cursor: pointer;\n padding: 2px;\n margin-left: 4px;\n opacity: 0.4;\n transition: opacity 0.15s;\n color: inherit;\n vertical-align: middle;\n }\n .tbw-filter-btn:hover,\n .tbw-filter-btn.active {\n opacity: 1;\n }\n .tbw-filter-btn.active {\n color: var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));\n }\n `;\n}\n","/**\n * Column Groups Core Logic\n *\n * Pure functions for computing and managing column header groups.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ColumnGroup, ColumnGroupInternal } from './types';\n\n/**\n * Compute column groups from column configuration.\n * Handles explicit groups (via column.group) and creates implicit groups for ungrouped columns.\n *\n * @param columns - Array of column configurations\n * @returns Array of column groups, or empty if no meaningful groups\n */\nexport function computeColumnGroups<T>(columns: ColumnConfig<T>[]): ColumnGroup<T>[] {\n if (!columns.length) return [];\n\n const explicitMap = new Map<string, ColumnGroupInternal<T>>();\n const groupsOrdered: ColumnGroupInternal<T>[] = [];\n\n // Helper to push unnamed implicit group for a run of ungrouped columns\n const pushImplicit = (startIdx: number, cols: ColumnConfig<T>[]) => {\n if (!cols.length) return;\n // Merge with previous implicit group if adjacent to reduce noise\n const prev = groupsOrdered[groupsOrdered.length - 1];\n if (prev && prev.implicit && prev.firstIndex + prev.columns.length === startIdx) {\n prev.columns.push(...cols);\n return;\n }\n groupsOrdered.push({\n id: '__implicit__' + startIdx,\n label: undefined,\n columns: cols,\n firstIndex: startIdx,\n implicit: true,\n });\n };\n\n let run: ColumnConfig<T>[] = [];\n let runStart = 0;\n\n columns.forEach((col, idx) => {\n const g: any = (col as any).group;\n if (!g) {\n if (run.length === 0) runStart = idx;\n run.push(col);\n return;\n }\n // Close any pending implicit run\n if (run.length) {\n pushImplicit(runStart, run.slice());\n run = [];\n }\n const id = typeof g === 'string' ? g : g.id;\n let group = explicitMap.get(id);\n if (!group) {\n group = {\n id,\n label: typeof g === 'string' ? undefined : g.label,\n columns: [],\n firstIndex: idx,\n };\n explicitMap.set(id, group);\n groupsOrdered.push(group);\n }\n group.columns.push(col);\n });\n\n // Trailing implicit run\n if (run.length) pushImplicit(runStart, run);\n\n // If we only have a single implicit group covering all columns, treat as no groups\n if (\n groupsOrdered.length === 1 &&\n groupsOrdered[0].implicit &&\n groupsOrdered[0].columns.length === columns.length\n ) {\n return [];\n }\n\n return groupsOrdered as ColumnGroup<T>[];\n}\n\n/**\n * Apply CSS classes to header cells based on their group membership.\n *\n * @param headerRowEl - The header row element\n * @param groups - The computed column groups\n * @param columns - The column configurations\n */\nexport function applyGroupedHeaderCellClasses(\n headerRowEl: HTMLElement | null,\n groups: ColumnGroup[],\n columns: ColumnConfig<any>[]\n): void {\n if (!groups.length || !headerRowEl) return;\n\n const fieldToGroup = new Map<string, string>();\n for (const g of groups) {\n for (const c of g.columns) {\n if ((c as any)?.field) {\n fieldToGroup.set((c as any).field, g.id);\n }\n }\n }\n\n const headerCells = Array.from(headerRowEl.querySelectorAll('.cell[data-field]')) as HTMLElement[];\n headerCells.forEach((cell) => {\n const f = cell.getAttribute('data-field') || '';\n const gid = fieldToGroup.get(f);\n if (gid) {\n cell.classList.add('grouped');\n if (!cell.getAttribute('data-group')) {\n cell.setAttribute('data-group', gid);\n }\n }\n });\n\n // Mark group end cells for styling\n for (const g of groups) {\n const last = g.columns[g.columns.length - 1];\n const cell = headerCells.find((c) => c.getAttribute('data-field') === (last as any).field);\n if (cell) cell.classList.add('group-end');\n }\n}\n\n/**\n * Build the group header row element.\n *\n * @param groups - The computed column groups\n * @param columns - The column configurations\n * @returns The group header row element, or null if no groups\n */\nexport function buildGroupHeaderRow(\n groups: ColumnGroup[],\n columns: ColumnConfig<any>[]\n): HTMLElement | null {\n if (groups.length === 0) return null;\n\n const groupRow = document.createElement('div');\n groupRow.className = 'header-group-row';\n groupRow.setAttribute('role', 'row');\n\n for (const g of groups) {\n const startIndex =\n g.firstIndex != null\n ? g.firstIndex\n : columns.findIndex((c) => (g.columns as any[]).includes(c));\n\n const isImplicit = String(g.id).startsWith('__implicit__');\n const label = isImplicit ? '' : g.label || g.id;\n\n const cell = document.createElement('div');\n cell.className = 'cell header-group-cell';\n if (isImplicit) cell.classList.add('implicit-group');\n cell.setAttribute('data-group', String(g.id));\n cell.style.gridColumn = `${startIndex + 1} / span ${g.columns.length}`;\n cell.textContent = label;\n groupRow.appendChild(cell);\n }\n\n return groupRow;\n}\n\n/**\n * Check if any columns have group configuration.\n *\n * @param columns - The column configurations\n * @returns True if at least one column has a group\n */\nexport function hasColumnGroups(columns: ColumnConfig<any>[]): boolean {\n return columns.some((col) => (col as any).group != null);\n}\n\n/**\n * Get group ID for a specific column.\n *\n * @param column - The column configuration\n * @returns The group ID, or undefined if not grouped\n */\nexport function getColumnGroupId(column: ColumnConfig<any>): string | undefined {\n const g = (column as any).group;\n if (!g) return undefined;\n return typeof g === 'string' ? g : g.id;\n}\n","/**\n * Column Groups Plugin (Class-based)\n *\n * Enables multi-level column header grouping.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport {\n applyGroupedHeaderCellClasses,\n buildGroupHeaderRow,\n computeColumnGroups,\n hasColumnGroups,\n} from './grouping-columns';\nimport type { ColumnGroup, GroupingColumnsConfig } from './types';\n\n/**\n * Column Groups Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new GroupingColumnsPlugin({\n * enabled: true,\n * showGroupBorders: true,\n * })\n * ```\n */\nexport class GroupingColumnsPlugin extends BaseGridPlugin<GroupingColumnsConfig> {\n readonly name = 'groupingColumns';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<GroupingColumnsConfig> {\n return {\n enabled: true,\n showGroupBorders: true,\n };\n }\n\n // ===== Internal State =====\n private groups: ColumnGroup[] = [];\n private isActive = false;\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.groups = [];\n this.isActive = false;\n }\n\n // ===== Static Detection =====\n\n /**\n * Auto-detect column groups from column configuration.\n */\n static detect(rows: readonly any[], config: any): boolean {\n const columns = config?.columns;\n if (!Array.isArray(columns)) return false;\n return hasColumnGroups(columns);\n }\n\n // ===== Hooks =====\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.enabled) {\n this.isActive = false;\n this.groups = [];\n return [...columns];\n }\n\n // Compute groups from column definitions\n const groups = computeColumnGroups(columns as ColumnConfig[]);\n\n if (groups.length === 0) {\n this.isActive = false;\n this.groups = [];\n return [...columns];\n }\n\n this.isActive = true;\n this.groups = groups;\n\n // Return columns unchanged - the afterRender hook will add the group header\n return [...columns];\n }\n\n override afterRender(): void {\n if (!this.isActive || this.groups.length === 0) {\n // Remove any existing group header\n const header = this.shadowRoot?.querySelector('.header');\n const existingGroupRow = header?.querySelector('.header-group-row');\n if (existingGroupRow) existingGroupRow.remove();\n return;\n }\n\n const header = this.shadowRoot?.querySelector('.header');\n if (!header) return;\n\n // Remove existing group row if present\n const existingGroupRow = header.querySelector('.header-group-row');\n if (existingGroupRow) existingGroupRow.remove();\n\n // Build and insert group header row\n const groupRow = buildGroupHeaderRow(this.groups, this.columns as ColumnConfig[]);\n if (groupRow) {\n const headerRow = header.querySelector('.header-row');\n if (headerRow) {\n header.insertBefore(groupRow, headerRow);\n } else {\n header.appendChild(groupRow);\n }\n }\n\n // Apply classes to header cells\n const headerRow = header.querySelector('.header-row') as HTMLElement;\n if (headerRow) {\n applyGroupedHeaderCellClasses(headerRow, this.groups, this.columns as ColumnConfig[]);\n }\n }\n\n // ===== Public API =====\n\n /**\n * Check if column groups are active.\n * @returns Whether grouping is active\n */\n isGroupingActive(): boolean {\n return this.isActive;\n }\n\n /**\n * Get the computed column groups.\n * @returns Array of column groups\n */\n getGroups(): ColumnGroup[] {\n return this.groups;\n }\n\n /**\n * Get columns in a specific group.\n * @param groupId - The group ID to find\n * @returns Array of columns in the group\n */\n getGroupColumns(groupId: string): ColumnConfig[] {\n const group = this.groups.find((g) => g.id === groupId);\n return group ? group.columns : [];\n }\n\n /**\n * Refresh column groups (recompute from current columns).\n */\n refresh(): void {\n this.requestRender();\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n .header-group-row {\n display: grid;\n grid-auto-flow: column;\n background: var(--tbw-grouping-columns-header-bg, var(--tbw-color-header-bg));\n border-bottom: 1px solid var(--tbw-grouping-columns-border, var(--tbw-color-border));\n }\n .header-group-cell {\n display: flex;\n align-items: center;\n justify-content: center;\n padding: 4px 8px;\n font-weight: 600;\n font-size: 0.9em;\n text-transform: uppercase;\n letter-spacing: 0.5px;\n border-right: 1px solid var(--tbw-grouping-columns-border, var(--tbw-color-border));\n }\n .header-group-cell:last-child {\n border-right: none;\n }\n .header-row .cell.grouped {\n border-top: none;\n }\n .header-row .cell.group-end {\n border-right: 2px solid var(--tbw-grouping-columns-separator, var(--tbw-color-border-strong));\n }\n `;\n}\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// 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","/**\n * Row Grouping Core Logic\n *\n * Pure functions for building grouped row models and aggregations.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { RenderRow, RowGroupingConfig } from './types';\n\n// Re-export aggregator functions from core for backward compatibility\nexport {\n getAggregator,\n listAggregators,\n registerAggregator,\n runAggregator,\n} from '../../core/internal/aggregators';\n\ninterface GroupNode {\n key: string; // composite key\n value: any;\n depth: number;\n rows: any[];\n children: Map<string, GroupNode>;\n parent?: GroupNode;\n}\n\ninterface BuildGroupingArgs {\n rows: any[];\n config: RowGroupingConfig;\n expanded: Set<string>;\n}\n\n/**\n * Build a flattened grouping projection (collapsed by default).\n * Returns empty array when grouping disabled or all rows ungrouped.\n *\n * @param args - The grouping arguments\n * @returns Flattened array of render rows (groups + data rows)\n */\nexport function buildGroupedRowModel({ rows, config, expanded }: BuildGroupingArgs): RenderRow[] {\n const groupOn = config.groupOn;\n if (!config.enabled || typeof groupOn !== 'function') {\n return [];\n }\n\n const root: GroupNode = { key: '__root__', value: null, depth: -1, rows: [], children: new Map() };\n\n // Build tree structure\n rows.forEach((r) => {\n let path: any = groupOn(r);\n if (path == null || path === false) path = ['__ungrouped__'];\n else if (!Array.isArray(path)) path = [path];\n\n let parent = root;\n path.forEach((rawVal: any, depthIdx: number) => {\n const seg = rawVal == null ? '∅' : String(rawVal);\n const composite = parent.key === '__root__' ? seg : parent.key + '||' + seg;\n let node = parent.children.get(seg);\n if (!node) {\n node = { key: composite, value: rawVal, depth: depthIdx, rows: [], children: new Map(), parent };\n parent.children.set(seg, node);\n }\n parent = node;\n });\n parent.rows.push(r);\n });\n\n // All ungrouped? treat as no grouping\n if (root.children.size === 1 && root.children.has('__ungrouped__')) {\n const only = root.children.get('__ungrouped__')!;\n if (only.rows.length === rows.length) return [];\n }\n\n // Flatten tree to array\n const flat: RenderRow[] = [];\n const visit = (node: GroupNode) => {\n if (node === root) {\n node.children.forEach((c) => visit(c));\n return;\n }\n\n const isExpanded = expanded.has(node.key);\n flat.push({\n kind: 'group',\n key: node.key,\n value: node.value,\n depth: node.depth,\n rows: node.rows,\n expanded: isExpanded,\n });\n\n if (isExpanded) {\n if (node.children.size) {\n node.children.forEach((c) => visit(c));\n } else {\n node.rows.forEach((r) => flat.push({ kind: 'data', row: r, rowIndex: rows.indexOf(r) }));\n }\n }\n };\n visit(root);\n\n return flat;\n}\n\n/**\n * Toggle expansion state for a group key.\n *\n * @param expandedKeys - Current set of expanded keys\n * @param key - The group key to toggle\n * @returns New set with toggled state\n */\nexport function toggleGroupExpansion(expandedKeys: Set<string>, key: string): Set<string> {\n const newSet = new Set(expandedKeys);\n if (newSet.has(key)) {\n newSet.delete(key);\n } else {\n newSet.add(key);\n }\n return newSet;\n}\n\n/**\n * Expand all groups.\n *\n * @param rows - The flattened render rows\n * @returns Set of all group keys\n */\nexport function expandAllGroups(rows: RenderRow[]): Set<string> {\n const keys = new Set<string>();\n for (const row of rows) {\n if (row.kind === 'group') {\n keys.add(row.key);\n }\n }\n return keys;\n}\n\n/**\n * Collapse all groups.\n *\n * @returns Empty set\n */\nexport function collapseAllGroups(): Set<string> {\n return new Set();\n}\n\n/**\n * Get all group keys from a flattened model.\n *\n * @param rows - The flattened render rows\n * @returns Array of group keys\n */\nexport function getGroupKeys(rows: RenderRow[]): string[] {\n return rows.filter((r) => r.kind === 'group').map((r) => (r as any).key);\n}\n\n/**\n * Count total rows in a group (including nested groups).\n *\n * @param groupRow - The group row\n * @returns Total row count\n */\nexport function getGroupRowCount(groupRow: RenderRow): number {\n if (groupRow.kind !== 'group') return 0;\n return groupRow.rows.length;\n}\n","/**\n * Row Grouping Plugin (Class-based)\n *\n * Enables hierarchical row grouping with expand/collapse and aggregations.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin, CellClickEvent } from '../../core/plugin/base-plugin';\nimport {\n buildGroupedRowModel,\n collapseAllGroups,\n expandAllGroups,\n getGroupRowCount,\n runAggregator,\n toggleGroupExpansion,\n} from './grouping-rows';\nimport type { GroupingRowsConfig, GroupRowModelItem, GroupToggleDetail, RenderRow } from './types';\n\n/**\n * Group state information returned by getGroupState()\n */\nexport interface GroupState {\n /** Whether grouping is currently active */\n isActive: boolean;\n /** Number of expanded groups */\n expandedCount: number;\n /** Total number of groups */\n totalGroups: number;\n /** Array of expanded group keys */\n expandedKeys: string[];\n}\n\n/**\n * Row Grouping Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new GroupingRowsPlugin({\n * enabled: true,\n * groupOn: (row) => row.category,\n * defaultExpanded: false,\n * showRowCount: true,\n * })\n * ```\n */\nexport class GroupingRowsPlugin extends BaseGridPlugin<GroupingRowsConfig> {\n readonly name = 'groupingRows';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<GroupingRowsConfig> {\n return {\n enabled: true,\n defaultExpanded: false,\n showRowCount: true,\n indentWidth: 20,\n aggregators: {},\n };\n }\n\n // ===== Internal State =====\n private expandedKeys: Set<string> = new Set();\n private flattenedRows: RenderRow[] = [];\n private isActive = false;\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.expandedKeys.clear();\n this.flattenedRows = [];\n this.isActive = false;\n }\n\n // ===== Hooks =====\n\n /**\n * Auto-detect grouping configuration from grid config.\n * Called by plugin system to determine if plugin should activate.\n */\n static detect(rows: readonly any[], config: any): boolean {\n return typeof config?.groupOn === 'function' || typeof config?.enableRowGrouping === 'boolean';\n }\n\n override processRows(rows: readonly any[]): any[] {\n const config = this.config;\n\n // Check if grouping is configured\n if (!config.enabled || typeof config.groupOn !== 'function') {\n this.isActive = false;\n this.flattenedRows = [];\n return [...rows];\n }\n\n // Build grouped model\n const grouped = buildGroupedRowModel({\n rows: rows as any[],\n config: config,\n expanded: this.expandedKeys,\n });\n\n // If no grouping produced, return original rows\n if (grouped.length === 0) {\n this.isActive = false;\n this.flattenedRows = [];\n return [...rows];\n }\n\n this.isActive = true;\n this.flattenedRows = grouped;\n\n // Return flattened rows for rendering\n // The grid will need to handle group rows specially\n return grouped.map((item) => {\n if (item.kind === 'group') {\n return {\n __isGroupRow: true,\n __groupKey: item.key,\n __groupValue: item.value,\n __groupDepth: item.depth,\n __groupRows: item.rows,\n __groupExpanded: item.expanded,\n __groupRowCount: getGroupRowCount(item),\n };\n }\n return item.row;\n });\n }\n\n override onCellClick(event: CellClickEvent): boolean | void {\n const row = event.row;\n\n // Check if this is a group row toggle\n if (row?.__isGroupRow) {\n const target = event.originalEvent.target as HTMLElement;\n if (target?.closest('.group-toggle')) {\n this.toggle(row.__groupKey);\n return true; // Prevent default\n }\n }\n }\n\n /**\n * Render a row. Returns true if we handled the row (group row), false otherwise.\n */\n override renderRow(row: any, rowEl: HTMLElement, _rowIndex: number): boolean {\n // Only handle group rows\n if (!row?.__isGroupRow) {\n return false;\n }\n\n const config = this.config;\n\n // If a custom renderer is provided, use it\n if (config.groupRowRenderer) {\n const toggleExpand = () => {\n this.toggle(row.__groupKey);\n };\n\n const result = config.groupRowRenderer({\n key: row.__groupKey,\n value: row.__groupValue,\n depth: row.__groupDepth,\n rows: row.__groupRows,\n expanded: row.__groupExpanded,\n toggleExpand,\n });\n\n if (result) {\n rowEl.className = 'group-row';\n (rowEl as any).__isCustomRow = true; // Mark for proper class reset on recycle\n rowEl.setAttribute('data-group-depth', String(row.__groupDepth));\n if (typeof result === 'string') {\n rowEl.innerHTML = result;\n } else {\n rowEl.innerHTML = '';\n rowEl.appendChild(result);\n }\n return true;\n }\n }\n\n // Helper to toggle expansion\n const handleToggle = () => {\n this.toggle(row.__groupKey);\n };\n\n // Default group row rendering\n rowEl.className = 'group-row';\n (rowEl as any).__isCustomRow = true; // Mark for proper class reset on recycle\n rowEl.setAttribute('data-group-depth', String(row.__groupDepth));\n rowEl.setAttribute('role', 'row');\n rowEl.setAttribute('aria-expanded', String(row.__groupExpanded));\n rowEl.style.paddingLeft = `${(row.__groupDepth || 0) * (config.indentWidth ?? 20)}px`;\n rowEl.innerHTML = '';\n\n const isFullWidth = config.fullWidth !== false; // default true\n\n if (isFullWidth) {\n this.renderFullWidthGroupRow(row, rowEl, handleToggle);\n } else {\n this.renderPerColumnGroupRow(row, rowEl, handleToggle);\n }\n\n return true;\n }\n\n override afterRender(): void {\n // No additional DOM manipulation needed for grouping\n // The renderRow hook handles all group row rendering\n }\n\n // ===== Private Rendering Helpers =====\n\n private renderFullWidthGroupRow(row: any, rowEl: HTMLElement, handleToggle: () => void): void {\n const config = this.config;\n\n // Full-width mode: single spanning cell with toggle + label + count\n const cell = document.createElement('div');\n cell.className = 'cell group-full';\n cell.style.gridColumn = '1 / -1';\n cell.setAttribute('role', 'gridcell');\n\n // Toggle button with click handler\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'group-toggle';\n btn.setAttribute('aria-label', row.__groupExpanded ? 'Collapse group' : 'Expand group');\n btn.textContent = row.__groupExpanded ? '▾' : '▸';\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n handleToggle();\n });\n cell.appendChild(btn);\n\n // Group label - use formatLabel if provided\n const label = document.createElement('span');\n label.className = 'group-label';\n const labelText = config.formatLabel\n ? config.formatLabel(row.__groupValue, row.__groupDepth || 0, row.__groupKey)\n : String(row.__groupValue);\n label.textContent = labelText;\n cell.appendChild(label);\n\n // Row count\n if (config.showRowCount !== false) {\n const count = document.createElement('span');\n count.className = 'group-count';\n count.textContent = `(${row.__groupRowCount ?? row.__groupRows?.length ?? 0})`;\n cell.appendChild(count);\n }\n\n rowEl.appendChild(cell);\n }\n\n private renderPerColumnGroupRow(row: any, rowEl: HTMLElement, handleToggle: () => void): void {\n const config = this.config;\n const aggregators = config.aggregators ?? {};\n const columns = this.columns;\n const groupRows = row.__groupRows ?? [];\n\n // Get grid template from the grid element\n const gridEl = this.grid as any;\n const bodyEl = this.shadowRoot?.querySelector('.body') as HTMLElement | null;\n const gridTemplate = bodyEl?.style.gridTemplateColumns || '';\n if (gridTemplate) {\n rowEl.style.display = 'grid';\n rowEl.style.gridTemplateColumns = gridTemplate;\n }\n\n columns.forEach((col, colIdx) => {\n const cell = document.createElement('div');\n cell.className = 'cell group-cell';\n cell.setAttribute('data-col', String(colIdx));\n cell.setAttribute('role', 'gridcell');\n\n if (colIdx === 0) {\n // First column: toggle button + label\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'group-toggle';\n btn.textContent = row.__groupExpanded ? '▾' : '▸';\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n handleToggle();\n });\n cell.appendChild(btn);\n\n const label = document.createElement('span');\n const firstColAgg = aggregators[col.field];\n if (firstColAgg) {\n const aggResult = runAggregator(firstColAgg, groupRows, col.field, col);\n label.textContent = aggResult != null ? String(aggResult) : String(row.__groupValue);\n } else {\n const labelText = config.formatLabel\n ? config.formatLabel(row.__groupValue, row.__groupDepth || 0, row.__groupKey)\n : String(row.__groupValue);\n label.textContent = labelText;\n }\n cell.appendChild(label);\n\n if (config.showRowCount !== false) {\n const count = document.createElement('span');\n count.className = 'group-count';\n count.textContent = ` (${groupRows.length})`;\n cell.appendChild(count);\n }\n } else {\n // Other columns: run aggregator if defined\n const aggRef = aggregators[col.field];\n if (aggRef) {\n const result = runAggregator(aggRef, groupRows, col.field, col);\n cell.textContent = result != null ? String(result) : '';\n } else {\n cell.textContent = '';\n }\n }\n\n rowEl.appendChild(cell);\n });\n }\n\n // ===== Public API =====\n\n /**\n * Expand all groups.\n */\n expandAll(): void {\n this.expandedKeys = expandAllGroups(this.flattenedRows);\n this.requestRender();\n }\n\n /**\n * Collapse all groups.\n */\n collapseAll(): void {\n this.expandedKeys = collapseAllGroups();\n this.requestRender();\n }\n\n /**\n * Toggle expansion of a specific group.\n * @param key - The group key to toggle\n */\n toggle(key: string): void {\n this.expandedKeys = toggleGroupExpansion(this.expandedKeys, key);\n\n // Find the group to emit event details\n const group = this.flattenedRows.find((r) => r.kind === 'group' && r.key === key) as GroupRowModelItem | undefined;\n\n this.emit<GroupToggleDetail>('group-toggle', {\n key,\n expanded: this.expandedKeys.has(key),\n value: group?.value,\n depth: group?.depth ?? 0,\n });\n\n this.requestRender();\n }\n\n /**\n * Check if a specific group is expanded.\n * @param key - The group key to check\n * @returns Whether the group is expanded\n */\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n /**\n * Expand a specific group.\n * @param key - The group key to expand\n */\n expand(key: string): void {\n if (!this.expandedKeys.has(key)) {\n this.expandedKeys = new Set([...this.expandedKeys, key]);\n this.requestRender();\n }\n }\n\n /**\n * Collapse a specific group.\n * @param key - The group key to collapse\n */\n collapse(key: string): void {\n if (this.expandedKeys.has(key)) {\n const newKeys = new Set(this.expandedKeys);\n newKeys.delete(key);\n this.expandedKeys = newKeys;\n this.requestRender();\n }\n }\n\n /**\n * Get the current group state.\n * @returns Group state information\n */\n getGroupState(): GroupState {\n const groupRows = this.flattenedRows.filter((r) => r.kind === 'group');\n return {\n isActive: this.isActive,\n expandedCount: this.expandedKeys.size,\n totalGroups: groupRows.length,\n expandedKeys: [...this.expandedKeys],\n };\n }\n\n /**\n * Get the total count of visible rows (including group headers).\n * @returns Number of visible rows\n */\n getRowCount(): number {\n return this.flattenedRows.length;\n }\n\n /**\n * Refresh the grouped row model.\n * Call this after modifying groupOn or other config options.\n */\n refreshGroups(): void {\n this.requestRender();\n }\n\n /**\n * Get current expanded group keys.\n * @returns Array of expanded group keys\n */\n getExpandedGroups(): string[] {\n return [...this.expandedKeys];\n }\n\n /**\n * Get the flattened row model.\n * @returns Array of render rows (groups + data rows)\n */\n getFlattenedRows(): RenderRow[] {\n return this.flattenedRows;\n }\n\n /**\n * Check if grouping is currently active.\n * @returns Whether grouping is active\n */\n isGroupingActive(): boolean {\n return this.isActive;\n }\n\n /**\n * Set the groupOn function dynamically.\n * @param fn - The groupOn function or undefined to disable\n */\n setGroupOn(fn: ((row: any) => any[] | any | null | false) | undefined): void {\n (this.config as GroupingRowsConfig).groupOn = fn;\n this.requestRender();\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n .group-row {\n background: var(--tbw-grouping-rows-bg, var(--tbw-color-panel-bg));\n font-weight: 500;\n }\n .group-row:hover {\n background: var(--tbw-grouping-rows-bg-hover, var(--tbw-color-row-hover));\n }\n .group-toggle {\n cursor: pointer;\n user-select: none;\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 20px;\n height: 20px;\n margin-right: 4px;\n font-size: 10px;\n }\n .group-toggle:hover {\n background: var(--tbw-grouping-rows-toggle-hover, var(--tbw-color-row-hover));\n border-radius: 2px;\n }\n .group-label {\n display: inline-flex;\n align-items: center;\n gap: 8px;\n }\n .group-count {\n color: var(--tbw-grouping-rows-count-color, var(--tbw-color-fg-muted));\n font-size: 0.85em;\n font-weight: normal;\n }\n [data-group-depth=\"0\"] .group-label { padding-left: 0; }\n [data-group-depth=\"1\"] .group-label { padding-left: 20px; }\n [data-group-depth=\"2\"] .group-label { padding-left: 40px; }\n [data-group-depth=\"3\"] .group-label { padding-left: 60px; }\n [data-group-depth=\"4\"] .group-label { padding-left: 80px; }\n `;\n}\n","/**\n * Master/Detail Core Logic\n *\n * Pure functions for managing detail row expansion state.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n// Uses `any` for maximum flexibility with user-defined row types.\n\n/**\n * Toggle the expansion state of a detail row.\n * Returns a new Set with the updated state.\n */\nexport function toggleDetailRow(expandedRows: Set<object>, row: object): Set<object> {\n const newExpanded = new Set(expandedRows);\n if (newExpanded.has(row)) {\n newExpanded.delete(row);\n } else {\n newExpanded.add(row);\n }\n return newExpanded;\n}\n\n/**\n * Expand a detail row.\n * Returns a new Set with the row added.\n */\nexport function expandDetailRow(expandedRows: Set<object>, row: object): Set<object> {\n const newExpanded = new Set(expandedRows);\n newExpanded.add(row);\n return newExpanded;\n}\n\n/**\n * Collapse a detail row.\n * Returns a new Set with the row removed.\n */\nexport function collapseDetailRow(expandedRows: Set<object>, row: object): Set<object> {\n const newExpanded = new Set(expandedRows);\n newExpanded.delete(row);\n return newExpanded;\n}\n\n/**\n * Check if a detail row is expanded.\n */\nexport function isDetailExpanded(expandedRows: Set<object>, row: object): boolean {\n return expandedRows.has(row);\n}\n\n/**\n * Create a detail element for a given row.\n * The element spans all columns and contains the rendered content.\n */\nexport function createDetailElement(\n row: any,\n rowIndex: number,\n renderer: (row: any, rowIndex: number) => HTMLElement | string,\n columnCount: number\n): HTMLElement {\n const detailRow = document.createElement('div');\n detailRow.className = 'master-detail-row';\n detailRow.setAttribute('data-detail-for', String(rowIndex));\n detailRow.setAttribute('role', 'row');\n\n const detailCell = document.createElement('div');\n detailCell.className = 'master-detail-cell';\n detailCell.setAttribute('role', 'cell');\n detailCell.style.gridColumn = `1 / ${columnCount + 1}`;\n\n const content = renderer(row, rowIndex);\n if (typeof content === 'string') {\n detailCell.innerHTML = content;\n } else if (content instanceof HTMLElement) {\n detailCell.appendChild(content);\n }\n\n detailRow.appendChild(detailCell);\n return detailRow;\n}\n","/**\n * Master/Detail Plugin (Class-based)\n *\n * Enables expandable detail rows showing additional content for each row.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin, RowClickEvent } from '../../core/plugin/base-plugin';\nimport {\n collapseDetailRow,\n createDetailElement,\n expandDetailRow,\n isDetailExpanded,\n toggleDetailRow,\n} from './master-detail';\nimport type { DetailExpandDetail, MasterDetailConfig } from './types';\n\n/**\n * Master/Detail Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new MasterDetailPlugin({\n * enabled: true,\n * detailRenderer: (row) => `<div>Details for ${row.name}</div>`,\n * expandOnRowClick: true,\n * })\n * ```\n */\nexport class MasterDetailPlugin extends BaseGridPlugin<MasterDetailConfig> {\n readonly name = 'masterDetail';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<MasterDetailConfig> {\n return {\n enabled: true,\n detailHeight: 'auto',\n expandOnRowClick: false,\n collapseOnClickOutside: false,\n showExpandColumn: true,\n };\n }\n\n // ===== Internal State =====\n private expandedRows: Set<any> = new Set();\n private detailElements: Map<any, HTMLElement> = new Map();\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.expandedRows.clear();\n this.detailElements.clear();\n }\n\n // ===== Hooks =====\n\n override processColumns(\n columns: readonly import('../../core/types').ColumnConfig[]\n ): import('../../core/types').ColumnConfig[] {\n if (!this.config.detailRenderer) {\n return [...columns];\n }\n\n // Wrap first column's renderer to add expand/collapse toggle\n const cols = [...columns];\n if (cols.length > 0) {\n const firstCol = { ...cols[0] };\n const originalRenderer = firstCol.viewRenderer;\n\n firstCol.viewRenderer = (renderCtx) => {\n const { value, row } = renderCtx;\n const isExpanded = this.expandedRows.has(row);\n\n const container = document.createElement('span');\n container.className = 'master-detail-cell-wrapper';\n\n // Expand/collapse toggle icon\n const toggle = document.createElement('span');\n toggle.className = 'master-detail-toggle';\n toggle.textContent = isExpanded ? '▼' : '▶';\n toggle.setAttribute('aria-expanded', String(isExpanded));\n toggle.setAttribute('aria-label', isExpanded ? 'Collapse details' : 'Expand details');\n toggle.addEventListener('click', (e) => {\n e.stopPropagation();\n const rowIndex = this.rows.indexOf(row);\n this.expandedRows = toggleDetailRow(this.expandedRows, row);\n this.emit<DetailExpandDetail>('detail-expand', {\n rowIndex,\n row,\n expanded: this.expandedRows.has(row),\n });\n this.requestRender();\n });\n container.appendChild(toggle);\n\n // Cell content\n const content = document.createElement('span');\n if (originalRenderer) {\n const rendered = originalRenderer(renderCtx);\n if (rendered instanceof Node) {\n content.appendChild(rendered);\n } else {\n content.textContent = String(rendered ?? value ?? '');\n }\n } else {\n content.textContent = String(value ?? '');\n }\n container.appendChild(content);\n\n return container;\n };\n\n cols[0] = firstCol;\n }\n\n return cols;\n }\n\n override onRowClick(event: RowClickEvent): boolean | void {\n if (!this.config.expandOnRowClick || !this.config.detailRenderer) return;\n\n this.expandedRows = toggleDetailRow(this.expandedRows, event.row);\n\n this.emit<DetailExpandDetail>('detail-expand', {\n rowIndex: event.rowIndex,\n row: event.row,\n expanded: this.expandedRows.has(event.row),\n });\n\n this.requestRender();\n return false;\n }\n\n override afterRender(): void {\n if (!this.config.detailRenderer) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n // Remove old detail rows\n body.querySelectorAll('.master-detail-row').forEach((el) => el.remove());\n this.detailElements.clear();\n\n // Insert detail rows as last child of expanded row elements\n const dataRows = body.querySelectorAll('.data-grid-row');\n const columnCount = this.columns.length;\n\n for (const rowEl of dataRows) {\n const firstCell = rowEl.querySelector('.cell[data-row]');\n const rowIndex = firstCell ? parseInt(firstCell.getAttribute('data-row') ?? '-1', 10) : -1;\n if (rowIndex < 0) continue;\n\n const row = this.rows[rowIndex];\n if (!row || !this.expandedRows.has(row)) continue;\n\n const detailEl = createDetailElement(row, rowIndex, this.config.detailRenderer, columnCount);\n\n if (typeof this.config.detailHeight === 'number') {\n detailEl.style.height = `${this.config.detailHeight}px`;\n }\n\n rowEl.appendChild(detailEl);\n this.detailElements.set(row, detailEl);\n }\n }\n\n // ===== Public API =====\n\n /**\n * Expand the detail row at the given index.\n * @param rowIndex - Index of the row to expand\n */\n expand(rowIndex: number): void {\n const row = this.rows[rowIndex];\n if (row) {\n this.expandedRows = expandDetailRow(this.expandedRows, row);\n this.requestRender();\n }\n }\n\n /**\n * Collapse the detail row at the given index.\n * @param rowIndex - Index of the row to collapse\n */\n collapse(rowIndex: number): void {\n const row = this.rows[rowIndex];\n if (row) {\n this.expandedRows = collapseDetailRow(this.expandedRows, row);\n this.requestRender();\n }\n }\n\n /**\n * Toggle the detail row at the given index.\n * @param rowIndex - Index of the row to toggle\n */\n toggle(rowIndex: number): void {\n const row = this.rows[rowIndex];\n if (row) {\n this.expandedRows = toggleDetailRow(this.expandedRows, row);\n this.requestRender();\n }\n }\n\n /**\n * Check if the detail row at the given index is expanded.\n * @param rowIndex - Index of the row to check\n * @returns Whether the detail row is expanded\n */\n isExpanded(rowIndex: number): boolean {\n const row = this.rows[rowIndex];\n return row ? isDetailExpanded(this.expandedRows, row) : false;\n }\n\n /**\n * Expand all detail rows.\n */\n expandAll(): void {\n for (const row of this.rows) {\n this.expandedRows.add(row);\n }\n this.requestRender();\n }\n\n /**\n * Collapse all detail rows.\n */\n collapseAll(): void {\n this.expandedRows.clear();\n this.requestRender();\n }\n\n /**\n * Get the indices of all expanded rows.\n * @returns Array of row indices that are expanded\n */\n getExpandedRows(): number[] {\n const indices: number[] = [];\n for (const row of this.expandedRows) {\n const idx = this.rows.indexOf(row);\n if (idx >= 0) indices.push(idx);\n }\n return indices;\n }\n\n /**\n * Get the detail element for a specific row.\n * @param rowIndex - Index of the row\n * @returns The detail HTMLElement or undefined\n */\n getDetailElement(rowIndex: number): HTMLElement | undefined {\n const row = this.rows[rowIndex];\n return row ? this.detailElements.get(row) : undefined;\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n .master-detail-cell-wrapper {\n display: flex;\n align-items: center;\n gap: 4px;\n }\n .master-detail-toggle {\n cursor: pointer;\n font-size: 10px;\n opacity: 0.7;\n user-select: none;\n }\n .master-detail-toggle:hover {\n opacity: 1;\n }\n .master-detail-row {\n grid-column: 1 / -1;\n display: grid;\n background: var(--tbw-master-detail-bg, var(--tbw-color-row-alt));\n border-bottom: 1px solid var(--tbw-master-detail-border, var(--tbw-color-border));\n }\n .master-detail-cell {\n padding: 16px;\n overflow: auto;\n }\n `;\n}\n","/**\n * Multi-Sort Core Logic\n *\n * Pure functions for multi-column sorting operations.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { SortModel } from './types';\n\n/**\n * Apply multiple sort columns to a row array.\n * Sorts are applied in order - first sort has highest priority.\n *\n * @param rows - Array of row objects to sort\n * @param sorts - Ordered array of sort configurations\n * @param columns - Column configurations (for custom comparators)\n * @returns New sorted array (does not mutate original)\n */\nexport function applySorts<TRow = unknown>(rows: TRow[], sorts: SortModel[], columns: ColumnConfig<TRow>[]): TRow[] {\n if (!sorts.length) return [...rows];\n\n return [...rows].sort((a, b) => {\n for (const sort of sorts) {\n const col = columns.find((c) => c.field === sort.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n const aVal = (a as Record<string, unknown>)[sort.field];\n const bVal = (b as Record<string, unknown>)[sort.field];\n const result = comparator(aVal, bVal, a, b);\n if (result !== 0) {\n return sort.direction === 'asc' ? result : -result;\n }\n }\n return 0;\n });\n}\n\n/**\n * Default comparator for sorting values.\n * Handles nulls, numbers, dates, and strings.\n *\n * @param a - First value\n * @param b - Second value\n * @returns Comparison result (-1, 0, 1)\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n // Handle nulls/undefined - push to end\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n\n // Type-aware comparison\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n // Boolean comparison\n if (typeof a === 'boolean' && typeof b === 'boolean') {\n return a === b ? 0 : a ? -1 : 1;\n }\n\n // String comparison (fallback)\n return String(a).localeCompare(String(b));\n}\n\n/**\n * Toggle sort state for a field.\n * With shift key: adds/toggles in multi-sort list\n * Without shift key: replaces entire sort with single column\n *\n * @param current - Current sort model\n * @param field - Field to toggle\n * @param shiftKey - Whether shift key is held (multi-sort mode)\n * @param maxColumns - Maximum columns allowed in sort\n * @returns New sort model\n */\nexport function toggleSort(current: SortModel[], field: string, shiftKey: boolean, maxColumns: number): SortModel[] {\n const existing = current.find((s) => s.field === field);\n\n if (shiftKey) {\n // Multi-sort: add/toggle in list\n if (existing) {\n if (existing.direction === 'asc') {\n // Flip to descending\n return current.map((s) => (s.field === field ? { ...s, direction: 'desc' as const } : s));\n } else {\n // Remove from sort\n return current.filter((s) => s.field !== field);\n }\n } else if (current.length < maxColumns) {\n // Add new sort column\n return [...current, { field, direction: 'asc' as const }];\n }\n // Max columns reached, return unchanged\n return current;\n } else {\n // Single sort: replace all\n if (existing?.direction === 'asc') {\n return [{ field, direction: 'desc' }];\n } else if (existing?.direction === 'desc') {\n return [];\n }\n return [{ field, direction: 'asc' }];\n }\n}\n\n/**\n * Get the sort index (1-based) for a field in the sort model.\n * Returns undefined if the field is not in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns 1-based index or undefined\n */\nexport function getSortIndex(sortModel: SortModel[], field: string): number | undefined {\n const index = sortModel.findIndex((s) => s.field === field);\n return index >= 0 ? index + 1 : undefined;\n}\n\n/**\n * Get the sort direction for a field in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\nexport function getSortDirection(sortModel: SortModel[], field: string): 'asc' | 'desc' | undefined {\n return sortModel.find((s) => s.field === field)?.direction;\n}\n","/**\r\n * Multi-Sort Plugin (Class-based)\r\n *\r\n * Provides multi-column sorting capabilities for tbw-grid.\r\n * Supports shift+click for adding secondary sort columns.\r\n */\r\n\r\nimport { BaseGridPlugin, HeaderClickEvent } from '../../core/plugin/base-plugin';\r\nimport type { ColumnState } from '../../core/types';\r\nimport { applySorts, getSortDirection, getSortIndex, toggleSort } from './multi-sort';\r\nimport type { MultiSortConfig, SortModel } from './types';\r\n\r\n/**\r\n * Multi-Sort Plugin for tbw-grid\r\n *\r\n * @example\r\n * ```ts\r\n * new MultiSortPlugin({ maxSortColumns: 3, showSortIndex: true })\r\n * ```\r\n */\r\nexport class MultiSortPlugin extends BaseGridPlugin<MultiSortConfig> {\r\n readonly name = 'multiSort';\r\n override readonly version = '1.0.0';\r\n\r\n protected override get defaultConfig(): Partial<MultiSortConfig> {\r\n return {\r\n enabled: true,\r\n maxSortColumns: 3,\r\n showSortIndex: true,\r\n };\r\n }\r\n\r\n // ===== Internal State =====\r\n private sortModel: SortModel[] = [];\r\n\r\n // ===== Lifecycle =====\r\n\r\n override detach(): void {\r\n this.sortModel = [];\r\n }\r\n\r\n // ===== Hooks =====\r\n\r\n override processRows(rows: readonly unknown[]): unknown[] {\r\n if (this.sortModel.length === 0) {\r\n return [...rows];\r\n }\r\n return applySorts([...rows], this.sortModel, [...this.columns]);\r\n }\r\n\r\n override onHeaderClick(event: HeaderClickEvent): boolean {\r\n const column = this.columns.find((c) => c.field === event.field);\r\n if (!column?.sortable) return false;\r\n\r\n const shiftKey = event.originalEvent.shiftKey;\r\n const maxColumns = this.config.maxSortColumns ?? 3;\r\n\r\n this.sortModel = toggleSort(this.sortModel, event.field, shiftKey, maxColumns);\r\n\r\n this.emit('sort-change', { sortModel: [...this.sortModel] });\r\n this.requestRender();\r\n\r\n return true;\r\n }\r\n\r\n override afterRender(): void {\r\n const shadowRoot = this.shadowRoot;\r\n if (!shadowRoot) return;\r\n\r\n const showIndex = this.config.showSortIndex !== false;\r\n\r\n // Update all sortable header cells with sort indicators\r\n const headerCells = shadowRoot.querySelectorAll('.header-row .cell[data-field]');\r\n headerCells.forEach((cell) => {\r\n const field = cell.getAttribute('data-field');\r\n if (!field) return;\r\n\r\n const sortIndex = getSortIndex(this.sortModel, field);\r\n const sortDir = getSortDirection(this.sortModel, field);\r\n\r\n // Remove existing sort index badge (always clean up)\r\n const existingBadge = cell.querySelector('.sort-index');\r\n existingBadge?.remove();\r\n\r\n if (sortDir) {\r\n // Column is sorted - remove base indicator and add our own\r\n const existingIndicator = cell.querySelector('[part~=\"sort-indicator\"], .sort-indicator');\r\n existingIndicator?.remove();\r\n\r\n cell.setAttribute('data-sort', sortDir);\r\n\r\n // Add sort arrow indicator\r\n const indicator = document.createElement('span');\r\n indicator.className = 'sort-indicator';\r\n indicator.style.marginLeft = '4px';\r\n indicator.style.opacity = '0.8';\r\n indicator.textContent = sortDir === 'asc' ? '▲' : '▼';\r\n cell.appendChild(indicator);\r\n\r\n // Add sort index badge if multiple columns sorted and showSortIndex is enabled\r\n if (showIndex && this.sortModel.length > 1 && sortIndex !== undefined) {\r\n const badge = document.createElement('span');\r\n badge.className = 'sort-index';\r\n badge.textContent = String(sortIndex);\r\n cell.appendChild(badge);\r\n }\r\n } else {\r\n cell.removeAttribute('data-sort');\r\n // For unsorted columns, leave the base indicator (⇅) alone\r\n }\r\n });\r\n }\r\n\r\n // ===== Public API =====\r\n\r\n /**\r\n * Get the current sort model.\r\n * @returns Copy of the current sort model\r\n */\r\n getSortModel(): SortModel[] {\r\n return [...this.sortModel];\r\n }\r\n\r\n /**\r\n * Set the sort model programmatically.\r\n * @param model - New sort model to apply\r\n */\r\n setSortModel(model: SortModel[]): void {\r\n this.sortModel = [...model];\r\n this.emit('sort-change', { sortModel: [...model] });\r\n this.requestRender();\r\n }\r\n\r\n /**\r\n * Clear all sorting.\r\n */\r\n clearSort(): void {\r\n this.sortModel = [];\r\n this.emit('sort-change', { sortModel: [] });\r\n this.requestRender();\r\n }\r\n\r\n /**\r\n * Get the sort index (1-based) for a specific field.\r\n * @param field - Field to check\r\n * @returns 1-based index or undefined if not sorted\r\n */\r\n getSortIndex(field: string): number | undefined {\r\n return getSortIndex(this.sortModel, field);\r\n }\r\n\r\n /**\r\n * Get the sort direction for a specific field.\r\n * @param field - Field to check\r\n * @returns Sort direction or undefined if not sorted\r\n */\r\n getSortDirection(field: string): 'asc' | 'desc' | undefined {\r\n return getSortDirection(this.sortModel, field);\r\n }\r\n\r\n // ===== Column State Hooks =====\r\n\r\n /**\r\n * Return sort state for a column if it's in the sort model.\r\n */\r\n override getColumnState(field: string): Partial<ColumnState> | undefined {\r\n const index = this.sortModel.findIndex((s) => s.field === field);\r\n if (index === -1) return undefined;\r\n\r\n const sortEntry = this.sortModel[index];\r\n return {\r\n sort: {\r\n direction: sortEntry.direction,\r\n priority: index,\r\n },\r\n };\r\n }\r\n\r\n /**\r\n * Apply sort state from column state.\r\n * Rebuilds the sort model from all column states.\r\n */\r\n override applyColumnState(field: string, state: ColumnState): void {\r\n // Only process if the column has sort state\r\n if (!state.sort) {\r\n // Remove this field from sortModel if it exists\r\n this.sortModel = this.sortModel.filter((s) => s.field !== field);\r\n return;\r\n }\r\n\r\n // Find existing entry or add new one\r\n const existingIndex = this.sortModel.findIndex((s) => s.field === field);\r\n const newEntry: SortModel = {\r\n field,\r\n direction: state.sort.direction,\r\n };\r\n\r\n if (existingIndex !== -1) {\r\n // Update existing entry\r\n this.sortModel[existingIndex] = newEntry;\r\n } else {\r\n // Add at the correct priority position\r\n this.sortModel.splice(state.sort.priority, 0, newEntry);\r\n }\r\n\r\n // Re-sort the model by priority to ensure correct order\r\n // This is handled after all columns are processed, but we maintain order here\r\n }\r\n\r\n // ===== Styles =====\r\n\r\n override readonly styles = `\r\n .header-cell[data-sort=\"asc\"]::after {\r\n content: '↑';\r\n margin-left: 4px;\r\n opacity: 0.8;\r\n }\r\n .header-cell[data-sort=\"desc\"]::after {\r\n content: '↓';\r\n margin-left: 4px;\r\n opacity: 0.8;\r\n }\r\n .sort-index {\r\n font-size: 10px;\r\n background: var(--tbw-multi-sort-badge-bg, var(--tbw-color-panel-bg));\r\n color: var(--tbw-multi-sort-badge-color, var(--tbw-color-fg));\r\n border-radius: 50%;\r\n width: 14px;\r\n height: 14px;\r\n display: inline-flex;\r\n align-items: center;\r\n justify-content: center;\r\n margin-left: 2px;\r\n font-weight: 600;\r\n }\r\n `;\r\n}\r\n","/**\n * Sticky Columns Core Logic\n *\n * Pure functions for applying sticky (pinned) column positioning.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport type { StickyPosition } from './types';\n\n/**\n * Get columns that should be sticky on the left.\n *\n * @param columns - Array of column configurations\n * @returns Array of columns with sticky='left'\n */\nexport function getLeftStickyColumns(columns: any[]): any[] {\n return columns.filter((col) => col.sticky === 'left');\n}\n\n/**\n * Get columns that should be sticky on the right.\n *\n * @param columns - Array of column configurations\n * @returns Array of columns with sticky='right'\n */\nexport function getRightStickyColumns(columns: any[]): any[] {\n return columns.filter((col) => col.sticky === 'right');\n}\n\n/**\n * Check if any columns have sticky positioning.\n *\n * @param columns - Array of column configurations\n * @returns True if any column has sticky position\n */\nexport function hasStickyColumns(columns: any[]): boolean {\n return columns.some((col) => col.sticky === 'left' || col.sticky === 'right');\n}\n\n/**\n * Get the sticky position of a column.\n *\n * @param column - Column configuration\n * @returns The sticky position or null if not sticky\n */\nexport function getColumnStickyPosition(column: any): StickyPosition | null {\n if (column.sticky === 'left') return 'left';\n if (column.sticky === 'right') return 'right';\n return null;\n}\n\n/**\n * Calculate left offsets for sticky-left columns.\n * Returns a map of field -> offset in pixels.\n *\n * @param columns - Array of column configurations (in order)\n * @param getColumnWidth - Function to get column width by field\n * @returns Map of field to left offset\n */\nexport function calculateLeftStickyOffsets(\n columns: any[],\n getColumnWidth: (field: string) => number\n): Map<string, number> {\n const offsets = new Map<string, number>();\n let currentOffset = 0;\n\n for (const col of columns) {\n if (col.sticky === 'left') {\n offsets.set(col.field, currentOffset);\n currentOffset += getColumnWidth(col.field);\n }\n }\n\n return offsets;\n}\n\n/**\n * Calculate right offsets for sticky-right columns.\n * Processes columns in reverse order.\n *\n * @param columns - Array of column configurations (in order)\n * @param getColumnWidth - Function to get column width by field\n * @returns Map of field to right offset\n */\nexport function calculateRightStickyOffsets(\n columns: any[],\n getColumnWidth: (field: string) => number\n): Map<string, number> {\n const offsets = new Map<string, number>();\n let currentOffset = 0;\n\n // Process in reverse for right-sticky columns\n const reversed = [...columns].reverse();\n for (const col of reversed) {\n if (col.sticky === 'right') {\n offsets.set(col.field, currentOffset);\n currentOffset += getColumnWidth(col.field);\n }\n }\n\n return offsets;\n}\n\n/**\n * Apply sticky offsets to header and body cells.\n * This modifies the DOM elements in place.\n *\n * @param host - The grid host element\n * @param columns - Array of column configurations\n */\nexport function applyStickyOffsets(host: HTMLElement, columns: any[]): void {\n const shadowRoot = host.shadowRoot;\n if (!shadowRoot) return;\n\n const headerCells = Array.from(shadowRoot.querySelectorAll('.header-row .cell')) as HTMLElement[];\n if (!headerCells.length) return;\n\n // Build column index map for matching body cells (which use data-col, not data-field)\n const fieldToIndex = new Map<string, number>();\n columns.forEach((col, i) => {\n if (col.field) fieldToIndex.set(col.field, i);\n });\n\n // Apply left sticky\n let left = 0;\n for (const col of columns) {\n if (col.sticky === 'left') {\n const colIndex = fieldToIndex.get(col.field);\n const cell = headerCells.find((c) => c.getAttribute('data-field') === col.field);\n if (cell) {\n cell.classList.add('sticky-left');\n cell.style.left = left + 'px';\n // Body cells use data-col (column index), not data-field\n if (colIndex !== undefined) {\n shadowRoot.querySelectorAll(`.data-grid-row .cell[data-col=\"${colIndex}\"]`).forEach((el) => {\n el.classList.add('sticky-left');\n (el as HTMLElement).style.left = left + 'px';\n });\n }\n left += cell.offsetWidth;\n }\n }\n }\n\n // Apply right sticky (process in reverse)\n let right = 0;\n for (const col of [...columns].reverse()) {\n if (col.sticky === 'right') {\n const colIndex = fieldToIndex.get(col.field);\n const cell = headerCells.find((c) => c.getAttribute('data-field') === col.field);\n if (cell) {\n cell.classList.add('sticky-right');\n cell.style.right = right + 'px';\n // Body cells use data-col (column index), not data-field\n if (colIndex !== undefined) {\n shadowRoot.querySelectorAll(`.data-grid-row .cell[data-col=\"${colIndex}\"]`).forEach((el) => {\n el.classList.add('sticky-right');\n (el as HTMLElement).style.right = right + 'px';\n });\n }\n right += cell.offsetWidth;\n }\n }\n }\n}\n\n/**\n * Clear sticky positioning from all cells.\n *\n * @param host - The grid host element\n */\nexport function clearStickyOffsets(host: HTMLElement): void {\n const shadowRoot = host.shadowRoot;\n if (!shadowRoot) return;\n\n const cells = shadowRoot.querySelectorAll('.sticky-left, .sticky-right');\n cells.forEach((cell) => {\n cell.classList.remove('sticky-left', 'sticky-right');\n (cell as HTMLElement).style.left = '';\n (cell as HTMLElement).style.right = '';\n });\n}\n","/**\n * Pinned Columns Plugin (Class-based)\n *\n * Enables column pinning (sticky left/right positioning).\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport {\n applyStickyOffsets,\n clearStickyOffsets,\n getLeftStickyColumns,\n getRightStickyColumns,\n hasStickyColumns,\n} from './pinned-columns';\nimport type { PinnedColumnsConfig } from './types';\n\n/**\n * Pinned Columns Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new PinnedColumnsPlugin({ enabled: true })\n * ```\n */\nexport class PinnedColumnsPlugin extends BaseGridPlugin<PinnedColumnsConfig> {\n readonly name = 'pinnedColumns';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<PinnedColumnsConfig> {\n return {\n enabled: true,\n };\n }\n\n // ===== Internal State =====\n private isApplied = false;\n private leftOffsets = new Map<string, number>();\n private rightOffsets = new Map<string, number>();\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.leftOffsets.clear();\n this.rightOffsets.clear();\n this.isApplied = false;\n }\n\n // ===== Detection =====\n\n /**\n * Auto-detect sticky columns from column configuration.\n */\n static detect(rows: readonly unknown[], config: { columns?: ColumnConfig[] }): boolean {\n const columns = config?.columns;\n if (!Array.isArray(columns)) return false;\n return hasStickyColumns(columns);\n }\n\n // ===== Hooks =====\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.enabled) {\n this.isApplied = false;\n return [...columns];\n }\n\n // Mark that we have sticky columns to apply\n this.isApplied = hasStickyColumns([...columns]);\n return [...columns];\n }\n\n override afterRender(): void {\n if (!this.config.enabled || !this.isApplied) {\n return;\n }\n\n const host = this.grid as unknown as HTMLElement;\n const columns = [...this.columns];\n\n if (!hasStickyColumns(columns)) {\n clearStickyOffsets(host);\n this.isApplied = false;\n return;\n }\n\n // Apply sticky offsets after a microtask to ensure DOM is ready\n queueMicrotask(() => {\n applyStickyOffsets(host, columns);\n });\n }\n\n // ===== Public API =====\n\n /**\n * Re-apply sticky offsets (e.g., after column resize).\n */\n refreshStickyOffsets(): void {\n const columns = [...this.columns];\n applyStickyOffsets(this.grid as unknown as HTMLElement, columns);\n }\n\n /**\n * Get columns pinned to the left.\n */\n getLeftPinnedColumns(): ColumnConfig[] {\n const columns = [...this.columns];\n return getLeftStickyColumns(columns);\n }\n\n /**\n * Get columns pinned to the right.\n */\n getRightPinnedColumns(): ColumnConfig[] {\n const columns = [...this.columns];\n return getRightStickyColumns(columns);\n }\n\n /**\n * Clear all sticky positioning.\n */\n clearStickyPositions(): void {\n clearStickyOffsets(this.grid as unknown as HTMLElement);\n }\n}\n","/**\n * Status Bar Rendering Logic\n *\n * Pure functions for creating and updating the status bar UI.\n * Includes both info bar and aggregation row rendering.\n */\n\nimport type { PinnedRowsPanel, PinnedRowsContext, PinnedRowsConfig, AggregationRowConfig } from './types';\nimport type { ColumnConfig } from '../../core/types';\nimport { getAggregator } from '../../core/internal/aggregators';\n\n/**\n * Creates the info bar DOM element with all configured panels.\n *\n * @param config - The status bar configuration\n * @param context - The current grid context for rendering\n * @returns The complete info bar element\n */\nexport function createInfoBarElement(config: PinnedRowsConfig, context: PinnedRowsContext): HTMLElement {\n const pinnedRows = document.createElement('div');\n pinnedRows.className = 'tbw-pinned-rows';\n pinnedRows.setAttribute('role', 'status');\n pinnedRows.setAttribute('aria-live', 'polite');\n\n const left = document.createElement('div');\n left.className = 'tbw-pinned-rows-left';\n\n const center = document.createElement('div');\n center.className = 'tbw-pinned-rows-center';\n\n const right = document.createElement('div');\n right.className = 'tbw-pinned-rows-right';\n\n // Default panels - row count\n if (config.showRowCount !== false) {\n const rowCount = document.createElement('span');\n rowCount.className = 'tbw-status-panel tbw-status-panel-row-count';\n rowCount.textContent = `Total: ${context.totalRows} rows`;\n left.appendChild(rowCount);\n }\n\n // Filtered count panel (only shows when filter is active)\n if (config.showFilteredCount && context.filteredRows !== context.totalRows) {\n const filteredCount = document.createElement('span');\n filteredCount.className = 'tbw-status-panel tbw-status-panel-filtered-count';\n filteredCount.textContent = `Filtered: ${context.filteredRows}`;\n left.appendChild(filteredCount);\n }\n\n // Selected count panel (only shows when rows are selected)\n if (config.showSelectedCount && context.selectedRows > 0) {\n const selectedCount = document.createElement('span');\n selectedCount.className = 'tbw-status-panel tbw-status-panel-selected-count';\n selectedCount.textContent = `Selected: ${context.selectedRows}`;\n right.appendChild(selectedCount);\n }\n\n // Render custom panels\n if (config.customPanels) {\n for (const panel of config.customPanels) {\n const panelEl = renderCustomPanel(panel, context);\n switch (panel.position) {\n case 'left':\n left.appendChild(panelEl);\n break;\n case 'center':\n center.appendChild(panelEl);\n break;\n case 'right':\n right.appendChild(panelEl);\n break;\n }\n }\n }\n\n pinnedRows.appendChild(left);\n pinnedRows.appendChild(center);\n pinnedRows.appendChild(right);\n\n return pinnedRows;\n}\n\n/**\n * Creates a container for aggregation rows at top or bottom.\n *\n * @param position - 'top' or 'bottom'\n * @returns The container element\n */\nexport function createAggregationContainer(position: 'top' | 'bottom'): HTMLElement {\n const container = document.createElement('div');\n container.className = `tbw-aggregation-rows tbw-aggregation-rows-${position}`;\n container.setAttribute('role', 'rowgroup');\n return container;\n}\n\n/**\n * Renders aggregation rows into a container.\n *\n * @param container - The container to render into\n * @param rows - Aggregation row configurations\n * @param columns - Current column configuration\n * @param dataRows - Current row data for aggregation calculations\n */\nexport function renderAggregationRows(\n container: HTMLElement,\n rows: AggregationRowConfig[],\n columns: ColumnConfig[],\n dataRows: unknown[]\n): void {\n container.innerHTML = '';\n\n for (const rowConfig of rows) {\n const rowEl = document.createElement('div');\n rowEl.className = 'tbw-aggregation-row';\n rowEl.setAttribute('role', 'row');\n if (rowConfig.id) {\n rowEl.setAttribute('data-aggregation-id', rowConfig.id);\n }\n\n if (rowConfig.fullWidth) {\n // Full-width mode: single cell spanning all columns\n const cell = document.createElement('div');\n cell.className = 'tbw-aggregation-cell tbw-aggregation-cell-full';\n cell.style.gridColumn = '1 / -1';\n cell.textContent = rowConfig.label || '';\n rowEl.appendChild(cell);\n } else {\n // Per-column mode: one cell per column with aggregated/static values\n for (const col of columns) {\n const cell = document.createElement('div');\n cell.className = 'tbw-aggregation-cell';\n cell.setAttribute('data-field', col.field);\n\n let value: unknown;\n\n // Check for aggregator first\n const aggRef = rowConfig.aggregators?.[col.field];\n if (aggRef) {\n const aggFn = getAggregator(aggRef);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\n } else if (rowConfig.cells && Object.prototype.hasOwnProperty.call(rowConfig.cells, col.field)) {\n // Static or computed cell value\n const staticVal = rowConfig.cells[col.field];\n if (typeof staticVal === 'function') {\n value = staticVal(dataRows, col.field, col);\n } else {\n value = staticVal;\n }\n }\n\n cell.textContent = value != null ? String(value) : '';\n rowEl.appendChild(cell);\n }\n }\n\n container.appendChild(rowEl);\n }\n}\n\n/**\n * Renders a custom panel element.\n *\n * @param panel - The panel definition\n * @param context - The current grid context\n * @returns The panel DOM element\n */\nfunction renderCustomPanel(panel: PinnedRowsPanel, context: PinnedRowsContext): HTMLElement {\n const panelEl = document.createElement('div');\n panelEl.className = 'tbw-status-panel tbw-status-panel-custom';\n panelEl.id = `status-panel-${panel.id}`;\n\n const content = panel.render(context);\n\n if (typeof content === 'string') {\n panelEl.innerHTML = content;\n } else {\n panelEl.appendChild(content);\n }\n\n return panelEl;\n}\n\n/**\n * Builds the status bar context from grid state and plugin states.\n *\n * @param rows - Current row data\n * @param columns - Current column configuration\n * @param grid - Grid element reference\n * @param selectionState - Optional selection plugin state\n * @param filterState - Optional filtering plugin state\n * @returns The status bar context\n */\nexport function buildContext(\n rows: unknown[],\n columns: unknown[],\n grid: HTMLElement,\n selectionState?: { selected: Set<number> } | null,\n filterState?: { cachedResult: unknown[] | null } | null\n): PinnedRowsContext {\n return {\n totalRows: rows.length,\n filteredRows: filterState?.cachedResult?.length ?? rows.length,\n selectedRows: selectionState?.selected?.size ?? 0,\n columns: columns as PinnedRowsContext['columns'],\n rows,\n grid,\n };\n}\n\n// Keep old name as alias for backwards compatibility\nexport const createPinnedRowsElement = createInfoBarElement;\n","/**\n * Pinned Rows Plugin (Class-based)\n *\n * Adds info bars and aggregation rows to the grid.\n * - Info bar: Shows row counts, selection info, and custom panels\n * - Aggregation rows: Footer/header rows with computed values (sum, avg, etc.)\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildContext, createAggregationContainer, createInfoBarElement, renderAggregationRows } from './pinned-rows';\nimport type { AggregationRowConfig, PinnedRowsConfig, PinnedRowsContext, PinnedRowsPanel } from './types';\n\n/**\n * Pinned Rows Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new PinnedRowsPlugin({\n * enabled: true,\n * position: 'bottom',\n * showRowCount: true,\n * showSelectedCount: true,\n * aggregationRows: [\n * { id: 'totals', position: 'bottom', values: { amount: 'sum' } },\n * ],\n * })\n * ```\n */\nexport class PinnedRowsPlugin extends BaseGridPlugin<PinnedRowsConfig> {\n readonly name = 'pinnedRows';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<PinnedRowsConfig> {\n return {\n enabled: true,\n position: 'bottom',\n showRowCount: true,\n showSelectedCount: true,\n showFilteredCount: true,\n };\n }\n\n // ===== Internal State =====\n private infoBarElement: HTMLElement | null = null;\n private topAggregationContainer: HTMLElement | null = null;\n private bottomAggregationContainer: HTMLElement | null = null;\n private footerWrapper: HTMLElement | null = null;\n\n // ===== Lifecycle =====\n\n override detach(): void {\n if (this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n }\n\n // ===== Hooks =====\n\n override afterRender(): void {\n if (!this.config.enabled) {\n this.cleanup();\n return;\n }\n\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n // Use .tbw-scroll-area so footer is inside the horizontal scroll area,\n // otherwise fall back to .tbw-grid-content or root container\n const container =\n shadowRoot.querySelector('.tbw-scroll-area') ??\n shadowRoot.querySelector('.tbw-grid-content') ??\n shadowRoot.children[0];\n if (!container) return;\n\n // Build context with plugin states\n const selectionState = this.getSelectionState();\n const filterState = this.getFilterState();\n\n const context = buildContext(\n this.rows as unknown[],\n this.columns as unknown[],\n this.grid as unknown as HTMLElement,\n selectionState,\n filterState\n );\n\n // ===== Handle Aggregation Rows =====\n const aggregationRows = this.config.aggregationRows || [];\n const topRows = aggregationRows.filter((r) => r.position === 'top');\n const bottomRows = aggregationRows.filter((r) => r.position !== 'top');\n\n // Top aggregation rows\n if (topRows.length > 0) {\n if (!this.topAggregationContainer) {\n this.topAggregationContainer = createAggregationContainer('top');\n const header = shadowRoot.querySelector('.header');\n if (header && header.nextSibling) {\n container.insertBefore(this.topAggregationContainer, header.nextSibling);\n } else {\n container.appendChild(this.topAggregationContainer);\n }\n }\n renderAggregationRows(\n this.topAggregationContainer,\n topRows,\n this.visibleColumns as ColumnConfig[],\n this.rows as unknown[]\n );\n } else if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n\n // Handle footer\n const hasInfoContent =\n this.config.showRowCount !== false ||\n (this.config.showSelectedCount && context.selectedRows > 0) ||\n (this.config.showFilteredCount && context.filteredRows !== context.totalRows) ||\n (this.config.customPanels && this.config.customPanels.length > 0);\n const hasBottomInfoBar = hasInfoContent && this.config.position !== 'top';\n const needsFooter = bottomRows.length > 0 || hasBottomInfoBar;\n\n // Handle top info bar\n if (hasInfoContent && this.config.position === 'top') {\n if (!this.infoBarElement) {\n this.infoBarElement = createInfoBarElement(this.config, context);\n container.insertBefore(this.infoBarElement, container.firstChild);\n } else {\n const newInfoBar = createInfoBarElement(this.config, context);\n this.infoBarElement.replaceWith(newInfoBar);\n this.infoBarElement = newInfoBar;\n }\n } else if (this.config.position === 'top' && this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n\n // Create/manage footer wrapper\n if (needsFooter) {\n if (!this.footerWrapper) {\n this.footerWrapper = document.createElement('div');\n this.footerWrapper.className = 'tbw-footer';\n container.appendChild(this.footerWrapper);\n }\n\n this.footerWrapper.innerHTML = '';\n\n if (bottomRows.length > 0) {\n if (!this.bottomAggregationContainer) {\n this.bottomAggregationContainer = createAggregationContainer('bottom');\n }\n this.footerWrapper.appendChild(this.bottomAggregationContainer);\n renderAggregationRows(\n this.bottomAggregationContainer,\n bottomRows,\n this.visibleColumns as ColumnConfig[],\n this.rows as unknown[]\n );\n }\n\n if (hasBottomInfoBar) {\n this.infoBarElement = createInfoBarElement(this.config, context);\n this.footerWrapper.appendChild(this.infoBarElement);\n }\n } else {\n this.cleanupFooter();\n }\n }\n\n // ===== Private Methods =====\n\n private cleanup(): void {\n if (this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n }\n\n private cleanupFooter(): void {\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.infoBarElement && this.config.position !== 'top') {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n }\n\n private getSelectionState(): { selected: Set<number> } | null {\n // Try to get selection plugin state\n try {\n const grid = this.grid as any;\n return grid?.getPluginState?.('selection') ?? null;\n } catch {\n return null;\n }\n }\n\n private getFilterState(): { cachedResult: unknown[] | null } | null {\n try {\n const grid = this.grid as any;\n return grid?.getPluginState?.('filtering') ?? null;\n } catch {\n return null;\n }\n }\n\n // ===== Public API =====\n\n /**\n * Refresh the status bar to reflect current grid state.\n */\n refresh(): void {\n this.requestRender();\n }\n\n /**\n * Get the current status bar context.\n * @returns The context with row counts and other info\n */\n getContext(): PinnedRowsContext {\n const selectionState = this.getSelectionState();\n const filterState = this.getFilterState();\n\n return buildContext(\n this.rows as unknown[],\n this.columns as unknown[],\n this.grid as unknown as HTMLElement,\n selectionState,\n filterState\n );\n }\n\n /**\n * Add a custom panel to the info bar.\n * @param panel - The panel configuration to add\n */\n addPanel(panel: PinnedRowsPanel): void {\n if (!this.config.customPanels) {\n this.config.customPanels = [];\n }\n this.config.customPanels.push(panel);\n this.requestRender();\n }\n\n /**\n * Remove a custom panel by ID.\n * @param id - The panel ID to remove\n */\n removePanel(id: string): void {\n if (this.config.customPanels) {\n this.config.customPanels = this.config.customPanels.filter((p) => p.id !== id);\n this.requestRender();\n }\n }\n\n /**\n * Add an aggregation row.\n * @param row - The aggregation row configuration\n */\n addAggregationRow(row: AggregationRowConfig): void {\n if (!this.config.aggregationRows) {\n this.config.aggregationRows = [];\n }\n this.config.aggregationRows.push(row);\n this.requestRender();\n }\n\n /**\n * Remove an aggregation row by ID.\n * @param id - The aggregation row ID to remove\n */\n removeAggregationRow(id: string): void {\n if (this.config.aggregationRows) {\n this.config.aggregationRows = this.config.aggregationRows.filter((r) => r.id !== id);\n this.requestRender();\n }\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n .tbw-footer {\n position: sticky;\n bottom: 0;\n z-index: var(--tbw-z-layer-pinned-rows, 20);\n background: var(--tbw-color-panel-bg);\n }\n\n .tbw-pinned-rows {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 8px 12px;\n background: var(--tbw-pinned-rows-bg, var(--tbw-color-panel-bg));\n border-top: 1px solid var(--tbw-pinned-rows-border, var(--tbw-color-border));\n font-size: 12px;\n color: var(--tbw-pinned-rows-color, var(--tbw-color-fg-muted));\n min-height: 32px;\n box-sizing: border-box;\n min-width: fit-content;\n }\n .tbw-pinned-rows-left,\n .tbw-pinned-rows-center,\n .tbw-pinned-rows-right {\n display: flex;\n align-items: center;\n gap: 16px;\n }\n .tbw-pinned-rows-left {\n justify-content: flex-start;\n }\n .tbw-pinned-rows-center {\n justify-content: center;\n flex: 1;\n }\n .tbw-pinned-rows-right {\n justify-content: flex-end;\n }\n .tbw-status-panel {\n white-space: nowrap;\n }\n\n .tbw-aggregation-rows {\n min-width: fit-content;\n background: var(--tbw-aggregation-bg, var(--tbw-color-header-bg));\n }\n .tbw-aggregation-rows-top {\n border-bottom: 1px solid var(--tbw-aggregation-border, var(--tbw-color-border));\n }\n .tbw-aggregation-rows-bottom {\n border-top: 1px solid var(--tbw-aggregation-border, var(--tbw-color-border));\n }\n .tbw-aggregation-row {\n display: grid;\n grid-template-columns: var(--tbw-column-template);\n font-weight: var(--tbw-aggregation-font-weight, 600);\n }\n .tbw-aggregation-cell {\n padding: var(--tbw-cell-padding, 2px 8px);\n min-height: var(--tbw-row-height, 28px);\n display: flex;\n align-items: center;\n border-right: 1px solid var(--tbw-color-border-cell);\n }\n .tbw-aggregation-cell:last-child {\n border-right: 0;\n }\n .tbw-aggregation-cell-full {\n grid-column: 1 / -1;\n border-right: 0;\n }\n `;\n}\n","import type { PivotConfig } from './types';\n\nexport function validatePivotConfig(config: PivotConfig): string[] {\n const errors: string[] = [];\n\n if (!config.rowGroupFields?.length && !config.columnGroupFields?.length) {\n errors.push('At least one row or column group field is required');\n }\n\n if (!config.valueFields?.length) {\n errors.push('At least one value field is required');\n }\n\n return errors;\n}\n\n/**\n * Get a value-based aggregator function for pivot operations.\n * This operates on pre-extracted numeric values (different from core row-based aggregators).\n */\nexport function getPivotAggregator(aggFunc: string): (values: number[]) => number {\n switch (aggFunc) {\n case 'sum':\n return (vals) => vals.reduce((a, b) => a + b, 0);\n case 'avg':\n return (vals) => (vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0);\n case 'count':\n return (vals) => vals.length;\n case 'min':\n return (vals) => (vals.length ? Math.min(...vals) : 0);\n case 'max':\n return (vals) => (vals.length ? Math.max(...vals) : 0);\n case 'first':\n return (vals) => vals[0] ?? 0;\n case 'last':\n return (vals) => vals[vals.length - 1] ?? 0;\n default:\n return (vals) => vals.reduce((a, b) => a + b, 0);\n }\n}\n\nexport function createValueKey(columnValues: string[], valueField: string): string {\n return [...columnValues, valueField].join('|');\n}\n","import type { PivotConfig, PivotResult, PivotRow, PivotValueField } from './types';\nimport { getPivotAggregator, createValueKey } from './pivot-model';\n\nexport type PivotDataRow = Record<string, unknown>;\n\nexport function buildPivot(rows: PivotDataRow[], config: PivotConfig): PivotResult {\n const rowGroupFields = config.rowGroupFields ?? [];\n const columnGroupFields = config.columnGroupFields ?? [];\n const valueFields = config.valueFields ?? [];\n\n // Get unique column combinations\n const columnKeys = getUniqueColumnKeys(rows, columnGroupFields);\n\n // Group rows by row group fields\n const groupedData = groupByFields(rows, rowGroupFields);\n\n // Build pivot rows\n const pivotRows = buildPivotRows(groupedData, columnGroupFields, columnKeys, valueFields, 0);\n\n // Calculate totals\n const totals = calculateTotals(pivotRows, columnKeys, valueFields);\n const grandTotal = Object.values(totals).reduce((a, b) => a + b, 0);\n\n return {\n rows: pivotRows,\n columnKeys,\n totals,\n grandTotal,\n };\n}\n\nexport function getUniqueColumnKeys(rows: PivotDataRow[], columnFields: string[]): string[] {\n if (columnFields.length === 0) return ['value'];\n\n const keys = new Set<string>();\n for (const row of rows) {\n const key = columnFields.map((f) => String(row[f] ?? '')).join('|');\n keys.add(key);\n }\n return [...keys].sort();\n}\n\nexport function groupByFields(rows: PivotDataRow[], fields: string[]): Map<string, PivotDataRow[]> {\n const groups = new Map<string, PivotDataRow[]>();\n\n for (const row of rows) {\n const key = fields.map((f) => String(row[f] ?? '')).join('|');\n if (!groups.has(key)) {\n groups.set(key, []);\n }\n const group = groups.get(key);\n if (group) group.push(row);\n }\n\n return groups;\n}\n\nexport function buildPivotRows(\n groupedData: Map<string, PivotDataRow[]>,\n columnFields: string[],\n columnKeys: string[],\n valueFields: PivotValueField[],\n depth: number\n): PivotRow[] {\n const result: PivotRow[] = [];\n\n for (const [rowKey, groupRows] of groupedData) {\n const values: Record<string, number | null> = {};\n let total = 0;\n\n for (const colKey of columnKeys) {\n for (const vf of valueFields) {\n const matchingRows =\n columnFields.length > 0\n ? groupRows.filter((r) => columnFields.map((f) => String(r[f] ?? '')).join('|') === colKey)\n : groupRows;\n\n const nums = matchingRows.map((r) => Number(r[vf.field]) || 0);\n const aggregator = getPivotAggregator(vf.aggFunc);\n const aggregatedResult = nums.length > 0 ? aggregator(nums) : null;\n\n const valueKey = createValueKey([colKey], vf.field);\n values[valueKey] = aggregatedResult;\n\n if (aggregatedResult !== null) total += aggregatedResult;\n }\n }\n\n result.push({\n rowKey,\n rowLabel: rowKey || '(blank)',\n depth,\n values,\n total,\n isGroup: false,\n });\n }\n\n return result;\n}\n\nexport function calculateTotals(\n pivotRows: PivotRow[],\n columnKeys: string[],\n valueFields: PivotValueField[]\n): Record<string, number> {\n const totals: Record<string, number> = {};\n\n for (const colKey of columnKeys) {\n for (const vf of valueFields) {\n const valueKey = createValueKey([colKey], vf.field);\n totals[valueKey] = pivotRows.reduce((sum, row) => {\n return sum + (row.values[valueKey] ?? 0);\n }, 0);\n }\n }\n\n return totals;\n}\n\nexport function flattenPivotRows(rows: PivotRow[]): PivotRow[] {\n const result: PivotRow[] = [];\n\n function flatten(row: PivotRow) {\n result.push(row);\n if (row.children) {\n for (const child of row.children) {\n flatten(child);\n }\n }\n }\n\n for (const row of rows) {\n flatten(row);\n }\n\n return result;\n}\n","/**\n * Pivot Plugin (Class-based)\n *\n * Provides pivot table functionality for tbw-grid.\n * Transforms flat data into grouped, aggregated pivot views.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildPivot, flattenPivotRows, type PivotDataRow } from './pivot-engine';\nimport { createValueKey, validatePivotConfig } from './pivot-model';\nimport type { PivotConfig, PivotResult, PivotValueField } from './types';\n\n/**\n * Pivot Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new PivotPlugin({\n * rowGroupFields: ['category'],\n * columnGroupFields: ['region'],\n * valueFields: [{ field: 'sales', aggFunc: 'sum' }]\n * })\n * ```\n */\nexport class PivotPlugin extends BaseGridPlugin<PivotConfig> {\n readonly name = 'pivot';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<PivotConfig> {\n return {\n enabled: true,\n showTotals: true,\n showGrandTotal: true,\n };\n }\n\n // ===== Internal State =====\n private isActive = false;\n private pivotResult: PivotResult | null = null;\n private columnHeaders: string[] = [];\n private rowHeaders: string[] = [];\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.isActive = false;\n this.pivotResult = null;\n this.columnHeaders = [];\n this.rowHeaders = [];\n }\n\n // ===== Hooks =====\n\n override processRows(rows: readonly unknown[]): PivotDataRow[] {\n if (!this.config.enabled || !this.isActive) {\n return [...rows] as PivotDataRow[];\n }\n\n const errors = validatePivotConfig(this.config);\n if (errors.length > 0) {\n this.warn(`Config errors: ${errors.join(', ')}`);\n return [...rows] as PivotDataRow[];\n }\n\n // Build pivot\n this.pivotResult = buildPivot(rows as PivotDataRow[], this.config);\n\n // Return flattened pivot rows for rendering\n return flattenPivotRows(this.pivotResult.rows).map((pr) => ({\n __pivotRowKey: pr.rowKey,\n __pivotLabel: pr.rowLabel,\n __pivotDepth: pr.depth,\n __pivotIsGroup: pr.isGroup,\n __pivotTotal: pr.total,\n ...pr.values,\n }));\n }\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.enabled || !this.isActive || !this.pivotResult) {\n return [...columns];\n }\n\n const pivotColumns: ColumnConfig[] = [];\n\n // Row label column\n pivotColumns.push({\n field: '__pivotLabel',\n header: this.config.rowGroupFields?.join(' / ') ?? 'Group',\n width: 200,\n });\n\n // Value columns for each column key\n for (const colKey of this.pivotResult.columnKeys) {\n for (const vf of this.config.valueFields ?? []) {\n const valueKey = createValueKey([colKey], vf.field);\n pivotColumns.push({\n field: valueKey,\n header: `${colKey} - ${vf.header || vf.field} (${vf.aggFunc})`,\n width: 120,\n type: 'number',\n });\n }\n }\n\n // Totals column\n if (this.config.showTotals) {\n pivotColumns.push({\n field: '__pivotTotal',\n header: 'Total',\n width: 100,\n type: 'number',\n });\n }\n\n return pivotColumns;\n }\n\n // ===== Public API =====\n\n /**\n * Enable pivot mode.\n */\n enablePivot(): void {\n this.isActive = true;\n this.requestRender();\n }\n\n /**\n * Disable pivot mode and return to normal grid view.\n */\n disablePivot(): void {\n this.isActive = false;\n this.pivotResult = null;\n this.requestRender();\n }\n\n /**\n * Check if pivot mode is currently active.\n */\n isPivotActive(): boolean {\n return this.isActive;\n }\n\n /**\n * Get the current pivot result.\n */\n getPivotResult(): PivotResult | null {\n return this.pivotResult;\n }\n\n /**\n * Set the row group fields for pivoting.\n * @param fields - Array of field names to group rows by\n */\n setRowGroupFields(fields: string[]): void {\n this.config.rowGroupFields = fields;\n this.requestRender();\n }\n\n /**\n * Set the column group fields for pivoting.\n * @param fields - Array of field names to create columns from\n */\n setColumnGroupFields(fields: string[]): void {\n this.config.columnGroupFields = fields;\n this.requestRender();\n }\n\n /**\n * Set the value fields with aggregation functions.\n * @param fields - Array of value field configurations\n */\n setValueFields(fields: PivotValueField[]): void {\n this.config.valueFields = fields;\n this.requestRender();\n }\n\n /**\n * Refresh the pivot by clearing cached results.\n */\n refresh(): void {\n this.pivotResult = null;\n this.requestRender();\n }\n\n // ===== Styles =====\n\n override readonly styles = `\n [data-pivot-depth=\"1\"] { padding-left: 20px; }\n [data-pivot-depth=\"2\"] { padding-left: 40px; }\n [data-pivot-depth=\"3\"] { padding-left: 60px; }\n .pivot-group-row { font-weight: bold; background: var(--tbw-pivot-group-bg, var(--tbw-color-panel-bg)); }\n .pivot-total-row { font-weight: bold; border-top: 2px solid var(--tbw-pivot-border, var(--tbw-color-border-strong)); }\n `;\n}\n","/**\n * Column Reordering Core Logic\n *\n * Pure functions for column drag and reordering operations.\n */\n\nimport type { ColumnConfig } from '../../core/types';\n\n/**\n * Check if a column can be moved.\n * Respects lockPosition, suppressMovable, and sticky properties.\n * Sticky (pinned) columns cannot be reordered as they have fixed positions.\n *\n * @param column - The column configuration to check\n * @returns True if the column can be moved\n */\nexport function canMoveColumn<TRow = unknown>(column: ColumnConfig<TRow>): boolean {\n // Sticky columns cannot be reordered - they have fixed left/right positions\n // Check via meta since sticky is added by PinnedColumnsPlugin\n const meta = column.meta ?? {};\n const sticky = (meta as { sticky?: 'left' | 'right' }).sticky;\n if (sticky === 'left' || sticky === 'right') {\n return false;\n }\n // Check for lockPosition or suppressMovable properties in the column config\n return meta.lockPosition !== true && meta.suppressMovable !== true;\n}\n\n/**\n * Move a column from one position to another in the order array.\n *\n * @param columns - Array of field names in current order\n * @param fromIndex - The current index of the column to move\n * @param toIndex - The target index to move the column to\n * @returns New array with updated order\n */\nexport function moveColumn(columns: string[], fromIndex: number, toIndex: number): string[] {\n if (fromIndex === toIndex) return columns;\n if (fromIndex < 0 || fromIndex >= columns.length) return columns;\n if (toIndex < 0 || toIndex > columns.length) return columns;\n\n const result = [...columns];\n const [removed] = result.splice(fromIndex, 1);\n result.splice(toIndex, 0, removed);\n return result;\n}\n\n/**\n * Calculate the drop index based on the current drag position.\n *\n * @param dragX - The current X position of the drag\n * @param headerRect - The bounding rect of the header container\n * @param columnWidths - Array of column widths in order\n * @returns The index where the column should be dropped\n */\nexport function getDropIndex(dragX: number, headerRect: DOMRect, columnWidths: number[]): number {\n let x = headerRect.left;\n\n for (let i = 0; i < columnWidths.length; i++) {\n const mid = x + columnWidths[i] / 2;\n if (dragX < mid) return i;\n x += columnWidths[i];\n }\n\n return columnWidths.length;\n}\n\n/**\n * Reorder columns according to a specified order.\n * Columns not in the order array are appended at the end.\n *\n * @param columns - Array of column configurations\n * @param order - Array of field names specifying the desired order\n * @returns New array of columns in the specified order\n */\nexport function reorderColumns<TRow = unknown>(columns: ColumnConfig<TRow>[], order: string[]): ColumnConfig<TRow>[] {\n const columnMap = new Map<string, ColumnConfig<TRow>>(columns.map((c) => [c.field as string, c]));\n const reordered: ColumnConfig<TRow>[] = [];\n\n // Add columns in specified order\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 any remaining columns not in order\n for (const col of columnMap.values()) {\n reordered.push(col);\n }\n\n return reordered;\n}\n","/**\r\n * Column Reordering Plugin (Class-based)\r\n *\r\n * Provides drag-and-drop column reordering functionality for tbw-grid.\r\n * Supports keyboard and mouse interactions with visual feedback.\r\n */\r\n\r\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\r\nimport { canMoveColumn, moveColumn } from './column-drag';\r\nimport type { ColumnMoveDetail, ReorderConfig } from './types';\r\n\r\n/** Extended grid interface with column order methods */\r\ninterface GridWithColumnOrder {\r\n setColumnOrder(order: string[]): void;\r\n getColumnOrder(): string[];\r\n requestStateChange?: () => void;\r\n}\r\n\r\n/**\r\n * Column Reordering Plugin for tbw-grid\r\n *\r\n * @example\r\n * ```ts\r\n * new ReorderPlugin({\r\n * enabled: true,\r\n * animation: true,\r\n * animationDuration: 200,\r\n * })\r\n * ```\r\n */\r\nexport class ReorderPlugin extends BaseGridPlugin<ReorderConfig> {\r\n readonly name = 'reorder';\r\n override readonly version = '1.0.0';\r\n\r\n protected override get defaultConfig(): Partial<ReorderConfig> {\r\n return {\r\n enabled: true,\r\n animation: true,\r\n animationDuration: 200,\r\n };\r\n }\r\n\r\n // ===== Internal State =====\r\n private isDragging = false;\r\n private draggedField: string | null = null;\r\n private draggedIndex: number | null = null;\r\n private dropIndex: number | null = null;\r\n private boundReorderRequestHandler: ((e: Event) => void) | null = null;\r\n\r\n // ===== Lifecycle =====\r\n\r\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\r\n super.attach(grid);\r\n\r\n // Listen for reorder requests from other plugins (e.g., VisibilityPlugin)\r\n this.boundReorderRequestHandler = (e: Event) => {\r\n const detail = (e as CustomEvent).detail;\r\n if (detail?.field && typeof detail.toIndex === 'number') {\r\n this.moveColumn(detail.field, detail.toIndex);\r\n }\r\n };\r\n (grid as unknown as HTMLElement).addEventListener('column-reorder-request', this.boundReorderRequestHandler);\r\n }\r\n\r\n override detach(): void {\r\n // Remove event listener\r\n if (this.boundReorderRequestHandler && this.grid) {\r\n (this.grid as unknown as HTMLElement).removeEventListener(\r\n 'column-reorder-request',\r\n this.boundReorderRequestHandler\r\n );\r\n this.boundReorderRequestHandler = null;\r\n }\r\n\r\n this.isDragging = false;\r\n this.draggedField = null;\r\n this.draggedIndex = null;\r\n this.dropIndex = null;\r\n }\r\n\r\n // ===== Hooks =====\r\n // Note: No processColumns hook needed - we directly update the grid's column order\r\n\r\n override afterRender(): void {\r\n if (!this.config.enabled) return;\r\n\r\n const shadowRoot = this.shadowRoot;\r\n if (!shadowRoot) return;\r\n\r\n const headers = shadowRoot.querySelectorAll('.header-row > .cell');\r\n\r\n headers.forEach((header) => {\r\n const headerEl = header as HTMLElement;\r\n const field = headerEl.getAttribute('data-field');\r\n if (!field) return;\r\n\r\n const column = this.columns.find((c) => c.field === field);\r\n if (!column || !canMoveColumn(column)) {\r\n headerEl.draggable = false;\r\n return;\r\n }\r\n\r\n headerEl.draggable = true;\r\n\r\n // Remove existing listeners to prevent duplicates\r\n if (headerEl.getAttribute('data-dragstart-bound')) return;\r\n headerEl.setAttribute('data-dragstart-bound', 'true');\r\n\r\n headerEl.addEventListener('dragstart', (e: DragEvent) => {\r\n const currentOrder = this.getColumnOrder();\r\n const orderIndex = currentOrder.indexOf(field);\r\n this.isDragging = true;\r\n this.draggedField = field;\r\n this.draggedIndex = orderIndex;\r\n\r\n if (e.dataTransfer) {\r\n e.dataTransfer.effectAllowed = 'move';\r\n e.dataTransfer.setData('text/plain', field);\r\n }\r\n\r\n headerEl.classList.add('dragging');\r\n });\r\n\r\n headerEl.addEventListener('dragend', () => {\r\n this.isDragging = false;\r\n this.draggedField = null;\r\n this.draggedIndex = null;\r\n this.dropIndex = null;\r\n\r\n shadowRoot.querySelectorAll('.header-row > .cell').forEach((h) => {\r\n h.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\r\n });\r\n });\r\n\r\n headerEl.addEventListener('dragover', (e: DragEvent) => {\r\n e.preventDefault();\r\n if (!this.isDragging || this.draggedField === field) return;\r\n\r\n const rect = headerEl.getBoundingClientRect();\r\n const midX = rect.left + rect.width / 2;\r\n\r\n const currentOrder = this.getColumnOrder();\r\n const orderIndex = currentOrder.indexOf(field);\r\n this.dropIndex = e.clientX < midX ? orderIndex : orderIndex + 1;\r\n\r\n headerEl.classList.add('drop-target');\r\n headerEl.classList.toggle('drop-before', e.clientX < midX);\r\n headerEl.classList.toggle('drop-after', e.clientX >= midX);\r\n });\r\n\r\n headerEl.addEventListener('dragleave', () => {\r\n headerEl.classList.remove('drop-target', 'drop-before', 'drop-after');\r\n });\r\n\r\n headerEl.addEventListener('drop', (e: DragEvent) => {\r\n e.preventDefault();\r\n const draggedField = this.draggedField;\r\n const draggedIndex = this.draggedIndex;\r\n const dropIndex = this.dropIndex;\r\n\r\n if (!this.isDragging || draggedField === null || draggedIndex === null || dropIndex === null) {\r\n return;\r\n }\r\n\r\n const effectiveToIndex = dropIndex > draggedIndex ? dropIndex - 1 : dropIndex;\r\n const currentOrder = this.getColumnOrder();\r\n const newOrder = moveColumn(currentOrder, draggedIndex, effectiveToIndex);\r\n\r\n const detail: ColumnMoveDetail = {\r\n field: draggedField,\r\n fromIndex: draggedIndex,\r\n toIndex: effectiveToIndex,\r\n columnOrder: newOrder,\r\n };\r\n\r\n // Directly update the grid's column order\r\n (this.grid as unknown as GridWithColumnOrder).setColumnOrder(newOrder);\r\n\r\n this.emit('column-move', detail);\r\n // Trigger state change after reorder\r\n (this.grid as unknown as GridWithColumnOrder).requestStateChange?.();\r\n });\r\n });\r\n }\r\n\r\n // ===== Public API =====\r\n\r\n /**\r\n * Get the current column order from the grid.\r\n * @returns Array of field names in display order\r\n */\r\n getColumnOrder(): string[] {\r\n return (this.grid as unknown as GridWithColumnOrder).getColumnOrder();\r\n }\r\n\r\n /**\r\n * Move a column to a new position.\r\n * @param field - The field name of the column to move\r\n * @param toIndex - The target index\r\n */\r\n moveColumn(field: string, toIndex: number): void {\r\n const currentOrder = this.getColumnOrder();\r\n const fromIndex = currentOrder.indexOf(field);\r\n if (fromIndex === -1) return;\r\n\r\n const newOrder = moveColumn(currentOrder, fromIndex, toIndex);\r\n\r\n // Directly update the grid's column order\r\n (this.grid as unknown as GridWithColumnOrder).setColumnOrder(newOrder);\r\n\r\n this.emit<ColumnMoveDetail>('column-move', {\r\n field,\r\n fromIndex,\r\n toIndex,\r\n columnOrder: newOrder,\r\n });\r\n\r\n // Trigger state change after reorder\r\n (this.grid as unknown as GridWithColumnOrder).requestStateChange?.();\r\n }\r\n\r\n /**\r\n * Set a specific column order.\r\n * @param order - Array of field names in desired order\r\n */\r\n setColumnOrder(order: string[]): void {\r\n (this.grid as unknown as GridWithColumnOrder).setColumnOrder(order);\r\n // Trigger state change after reorder\r\n (this.grid as unknown as GridWithColumnOrder).requestStateChange?.();\r\n }\r\n\r\n /**\r\n * Reset column order to the original configuration order.\r\n */\r\n resetColumnOrder(): void {\r\n const originalOrder = this.columns.map((c) => c.field);\r\n (this.grid as unknown as GridWithColumnOrder).setColumnOrder(originalOrder);\r\n // Trigger state change after reset\r\n (this.grid as unknown as GridWithColumnOrder).requestStateChange?.();\r\n }\r\n\r\n // ===== Styles =====\r\n\r\n override readonly styles = `\r\n .header-row > .cell[draggable=\"true\"] {\r\n cursor: grab;\r\n position: relative;\r\n }\r\n .header-row > .cell.dragging {\r\n opacity: 0.5;\r\n cursor: grabbing;\r\n }\r\n .header-row > .cell.drop-before::before {\r\n content: '';\r\n position: absolute;\r\n left: 0;\r\n top: 0;\r\n bottom: 0;\r\n width: 2px;\r\n background: var(--tbw-reorder-indicator, var(--tbw-color-accent));\r\n z-index: 1;\r\n }\r\n .header-row > .cell.drop-after::after {\r\n content: '';\r\n position: absolute;\r\n right: 0;\r\n top: 0;\r\n bottom: 0;\r\n width: 2px;\r\n background: var(--tbw-reorder-indicator, var(--tbw-color-accent));\r\n z-index: 1;\r\n }\r\n `;\r\n}\r\n","import type { ServerSideDataSource, GetRowsParams, GetRowsResult } from './types';\n\nexport function getBlockNumber(rowIndex: number, blockSize: number): number {\n return Math.floor(rowIndex / blockSize);\n}\n\nexport function getBlockRange(blockNumber: number, blockSize: number): { start: number; end: number } {\n return {\n start: blockNumber * blockSize,\n end: (blockNumber + 1) * blockSize,\n };\n}\n\nexport function getRequiredBlocks(startRow: number, endRow: number, blockSize: number): number[] {\n const startBlock = getBlockNumber(startRow, blockSize);\n const endBlock = getBlockNumber(endRow - 1, blockSize);\n\n const blocks: number[] = [];\n for (let i = startBlock; i <= endBlock; i++) {\n blocks.push(i);\n }\n return blocks;\n}\n\nexport async function loadBlock(\n dataSource: ServerSideDataSource,\n blockNumber: number,\n blockSize: number,\n params: Partial<GetRowsParams>\n): Promise<GetRowsResult> {\n const range = getBlockRange(blockNumber, blockSize);\n\n return dataSource.getRows({\n startRow: range.start,\n endRow: range.end,\n sortModel: params.sortModel,\n filterModel: params.filterModel,\n });\n}\n\nexport function getRowFromCache(\n rowIndex: number,\n blockSize: number,\n loadedBlocks: Map<number, any[]>\n): any | undefined {\n const blockNumber = getBlockNumber(rowIndex, blockSize);\n const block = loadedBlocks.get(blockNumber);\n if (!block) return undefined;\n\n const indexInBlock = rowIndex % blockSize;\n return block[indexInBlock];\n}\n\nexport function isBlockLoaded(blockNumber: number, loadedBlocks: Map<number, any[]>): boolean {\n return loadedBlocks.has(blockNumber);\n}\n\nexport function isBlockLoading(blockNumber: number, loadingBlocks: Set<number>): boolean {\n return loadingBlocks.has(blockNumber);\n}\n","/**\n * Server-Side Data Plugin (Class-based)\n *\n * Provides server-side data loading with caching and lazy loading.\n */\n\nimport { BaseGridPlugin, ScrollEvent } from '../../core/plugin/base-plugin';\nimport { getBlockNumber, getRequiredBlocks, getRowFromCache, loadBlock } from './datasource';\nimport type { ServerSideConfig, ServerSideDataSource } from './types';\n\n/** Scroll debounce delay in ms */\nconst SCROLL_DEBOUNCE_MS = 100;\n\n/**\n * Server-Side Plugin for tbw-grid\n *\n * @example\n * ```ts\n * const plugin = new ServerSidePlugin({ cacheBlockSize: 100 });\n * plugin.setDataSource(myDataSource);\n * ```\n */\nexport class ServerSidePlugin extends BaseGridPlugin<ServerSideConfig> {\n readonly name = 'serverSide';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ServerSideConfig> {\n return {\n enabled: true,\n pageSize: 100,\n cacheBlockSize: 100,\n maxConcurrentRequests: 2,\n };\n }\n\n // ===== Internal State =====\n private dataSource: ServerSideDataSource | null = null;\n private totalRowCount = 0;\n private loadedBlocks = new Map<number, unknown[]>();\n private loadingBlocks = new Set<number>();\n private lastRequestId = 0;\n private scrollDebounceTimer?: ReturnType<typeof setTimeout>;\n\n // ===== Lifecycle =====\n\n override detach(): void {\n this.dataSource = null;\n this.totalRowCount = 0;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n this.lastRequestId = 0;\n if (this.scrollDebounceTimer) {\n clearTimeout(this.scrollDebounceTimer);\n this.scrollDebounceTimer = undefined;\n }\n }\n\n // ===== Private Methods =====\n\n /**\n * Check current viewport and load any missing blocks.\n */\n private loadRequiredBlocks(): void {\n if (!this.dataSource) return;\n\n // Get fresh viewport from grid's virtualization state\n const gridRef = this.grid as unknown as { virtualization: { start: number; end: number } };\n const blockSize = this.config.cacheBlockSize ?? 100;\n const viewport = { startRow: gridRef.virtualization.start, endRow: gridRef.virtualization.end };\n\n // Determine which blocks are needed for current viewport\n const requiredBlocks = getRequiredBlocks(viewport.startRow, viewport.endRow, blockSize);\n\n // Load missing blocks\n for (const blockNum of requiredBlocks) {\n if (this.loadedBlocks.has(blockNum) || this.loadingBlocks.has(blockNum)) {\n continue;\n }\n\n // Check concurrent request limit\n if (this.loadingBlocks.size >= (this.config.maxConcurrentRequests ?? 2)) {\n break;\n }\n\n this.loadingBlocks.add(blockNum);\n\n loadBlock(this.dataSource, blockNum, blockSize, {})\n .then((result) => {\n this.loadedBlocks.set(blockNum, result.rows);\n this.totalRowCount = result.totalRowCount;\n this.loadingBlocks.delete(blockNum);\n this.requestRender();\n // Re-check with fresh viewport: user may have scrolled further\n this.loadRequiredBlocks();\n })\n .catch(() => {\n this.loadingBlocks.delete(blockNum);\n });\n }\n }\n\n // ===== Hooks =====\n\n override processRows(rows: readonly unknown[]): unknown[] {\n if (!this.dataSource) return [...rows];\n\n // Create placeholder rows for total count\n const result: unknown[] = [];\n for (let i = 0; i < this.totalRowCount; i++) {\n const cached = getRowFromCache(i, this.config.cacheBlockSize ?? 100, this.loadedBlocks);\n result.push(cached ?? { __loading: true, __index: i });\n }\n\n return result;\n }\n\n override onScroll(event: ScrollEvent): void {\n if (!this.dataSource) return;\n\n // Immediate check for blocks\n this.loadRequiredBlocks();\n\n // Debounce: when scrolling stops, do a final check with fresh viewport\n if (this.scrollDebounceTimer) {\n clearTimeout(this.scrollDebounceTimer);\n }\n this.scrollDebounceTimer = setTimeout(() => {\n this.loadRequiredBlocks();\n }, SCROLL_DEBOUNCE_MS);\n }\n\n // ===== Public API =====\n\n /**\n * Set the data source for server-side loading.\n * @param dataSource - Data source implementing the getRows method\n */\n setDataSource(dataSource: ServerSideDataSource): void {\n this.dataSource = dataSource;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n\n // Load first block\n const blockSize = this.config.cacheBlockSize ?? 100;\n loadBlock(dataSource, 0, blockSize, {}).then((result) => {\n this.loadedBlocks.set(0, result.rows);\n this.totalRowCount = result.totalRowCount;\n this.requestRender();\n });\n }\n\n /**\n * Refresh all data from the server.\n */\n refresh(): void {\n if (!this.dataSource) return;\n this.loadedBlocks.clear();\n this.loadingBlocks.clear();\n this.requestRender();\n }\n\n /**\n * Clear all cached data without refreshing.\n */\n purgeCache(): void {\n this.loadedBlocks.clear();\n }\n\n /**\n * Get the total row count from the server.\n */\n getTotalRowCount(): number {\n return this.totalRowCount;\n }\n\n /**\n * Check if a specific row is loaded in the cache.\n * @param rowIndex - Row index to check\n */\n isRowLoaded(rowIndex: number): boolean {\n const blockSize = this.config.cacheBlockSize ?? 100;\n const blockNum = getBlockNumber(rowIndex, blockSize);\n return this.loadedBlocks.has(blockNum);\n }\n\n /**\n * Get the number of loaded cache blocks.\n */\n getLoadedBlockCount(): number {\n return this.loadedBlocks.size;\n }\n}\n","/**\n * Undo/Redo History Management\n *\n * Pure functions for managing the undo/redo stacks.\n * These functions are stateless and return new state objects.\n */\n\nimport type { EditAction, UndoRedoState } from './types';\n\n/**\n * Push a new action onto the undo stack.\n * Clears the redo stack since new actions invalidate redo history.\n *\n * @param state - Current undo/redo state\n * @param action - The action to add\n * @param maxSize - Maximum history size\n * @returns New state with the action added\n */\nexport function pushAction(state: UndoRedoState, action: EditAction, maxSize: number): UndoRedoState {\n const undoStack = [...state.undoStack, action];\n\n // Trim oldest actions if over max size\n while (undoStack.length > maxSize) {\n undoStack.shift();\n }\n\n return {\n undoStack,\n redoStack: [], // Clear redo on new action\n };\n}\n\n/**\n * Undo the most recent action.\n * Moves the action from undo stack to redo stack.\n *\n * @param state - Current undo/redo state\n * @returns New state and the action that was undone (or null if nothing to undo)\n */\nexport function undo(state: UndoRedoState): {\n newState: UndoRedoState;\n action: EditAction | null;\n} {\n if (state.undoStack.length === 0) {\n return { newState: state, action: null };\n }\n\n const undoStack = [...state.undoStack];\n const action = undoStack.pop();\n\n // This should never happen due to the length check above,\n // but TypeScript needs the explicit check\n if (!action) {\n return { newState: state, action: null };\n }\n\n return {\n newState: {\n undoStack,\n redoStack: [...state.redoStack, action],\n },\n action,\n };\n}\n\n/**\n * Redo the most recently undone action.\n * Moves the action from redo stack back to undo stack.\n *\n * @param state - Current undo/redo state\n * @returns New state and the action that was redone (or null if nothing to redo)\n */\nexport function redo(state: UndoRedoState): {\n newState: UndoRedoState;\n action: EditAction | null;\n} {\n if (state.redoStack.length === 0) {\n return { newState: state, action: null };\n }\n\n const redoStack = [...state.redoStack];\n const action = redoStack.pop();\n\n // This should never happen due to the length check above,\n // but TypeScript needs the explicit check\n if (!action) {\n return { newState: state, action: null };\n }\n\n return {\n newState: {\n undoStack: [...state.undoStack, action],\n redoStack,\n },\n action,\n };\n}\n\n/**\n * Check if there are any actions that can be undone.\n *\n * @param state - Current undo/redo state\n * @returns True if undo is available\n */\nexport function canUndo(state: UndoRedoState): boolean {\n return state.undoStack.length > 0;\n}\n\n/**\n * Check if there are any actions that can be redone.\n *\n * @param state - Current undo/redo state\n * @returns True if redo is available\n */\nexport function canRedo(state: UndoRedoState): boolean {\n return state.redoStack.length > 0;\n}\n\n/**\n * Clear all history, returning an empty state.\n *\n * @returns Fresh empty state\n */\nexport function clearHistory(): UndoRedoState {\n return { undoStack: [], redoStack: [] };\n}\n\n/**\n * Create a new edit action with the current timestamp.\n *\n * @param rowIndex - The row index where the edit occurred\n * @param field - The field (column key) that was edited\n * @param oldValue - The value before the edit\n * @param newValue - The value after the edit\n * @returns A new EditAction object\n */\nexport function createEditAction(rowIndex: number, field: string, oldValue: unknown, newValue: unknown): EditAction {\n return {\n type: 'cell-edit',\n rowIndex,\n field,\n oldValue,\n newValue,\n timestamp: Date.now(),\n };\n}\n","/**\n * Undo/Redo Plugin (Class-based)\n *\n * Provides undo/redo functionality for cell edits in tbw-grid.\n * Supports Ctrl+Z/Cmd+Z for undo and Ctrl+Y/Cmd+Y (or Ctrl+Shift+Z) for redo.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport { canRedo, canUndo, clearHistory, createEditAction, pushAction, redo, undo } from './history';\nimport type { EditAction, UndoRedoConfig, UndoRedoDetail } from './types';\n\n/**\n * Class-based Undo/Redo plugin for tbw-grid.\n *\n * Tracks cell edits and provides undo/redo functionality via keyboard shortcuts\n * or programmatic API.\n */\nexport class UndoRedoPlugin extends BaseGridPlugin<UndoRedoConfig> {\n readonly name = 'undoRedo';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<UndoRedoConfig> {\n return {\n enabled: true,\n maxHistorySize: 100,\n };\n }\n\n // State as class properties\n private undoStack: EditAction[] = [];\n private redoStack: EditAction[] = [];\n\n /**\n * Clean up state when plugin is detached.\n */\n override detach(): void {\n this.undoStack = [];\n this.redoStack = [];\n }\n\n /**\n * Handle keyboard shortcuts for undo/redo.\n * - Ctrl+Z / Cmd+Z: Undo\n * - Ctrl+Y / Cmd+Y / Ctrl+Shift+Z / Cmd+Shift+Z: Redo\n */\n override onKeyDown(event: KeyboardEvent): boolean {\n if (!this.config.enabled) return false;\n\n const isUndo = (event.ctrlKey || event.metaKey) && event.key === 'z' && !event.shiftKey;\n const isRedo = (event.ctrlKey || event.metaKey) && (event.key === 'y' || (event.key === 'z' && event.shiftKey));\n\n if (isUndo) {\n const result = undo({ undoStack: this.undoStack, redoStack: this.redoStack });\n if (result.action) {\n // Apply undo - restore old value\n const rows = this.rows as Record<string, unknown>[];\n if (rows[result.action.rowIndex]) {\n rows[result.action.rowIndex][result.action.field] = result.action.oldValue;\n }\n\n // Update state from result\n this.undoStack = result.newState.undoStack;\n this.redoStack = result.newState.redoStack;\n\n this.emit<UndoRedoDetail>('undo', {\n action: result.action,\n type: 'undo',\n });\n\n this.requestRender();\n }\n return true;\n }\n\n if (isRedo) {\n const result = redo({ undoStack: this.undoStack, redoStack: this.redoStack });\n if (result.action) {\n // Apply redo - restore new value\n const rows = this.rows as Record<string, unknown>[];\n if (rows[result.action.rowIndex]) {\n rows[result.action.rowIndex][result.action.field] = result.action.newValue;\n }\n\n // Update state from result\n this.undoStack = result.newState.undoStack;\n this.redoStack = result.newState.redoStack;\n\n this.emit<UndoRedoDetail>('redo', {\n action: result.action,\n type: 'redo',\n });\n\n this.requestRender();\n }\n return true;\n }\n\n return false;\n }\n\n // ===== Public API Methods =====\n\n /**\n * Record a cell edit for undo/redo tracking.\n * Call this when a cell value changes.\n *\n * @param rowIndex - The row index where the edit occurred\n * @param field - The field (column key) that was edited\n * @param oldValue - The value before the edit\n * @param newValue - The value after the edit\n */\n recordEdit(rowIndex: number, field: string, oldValue: unknown, newValue: unknown): void {\n const action = createEditAction(rowIndex, field, oldValue, newValue);\n const newState = pushAction(\n { undoStack: this.undoStack, redoStack: this.redoStack },\n action,\n this.config.maxHistorySize ?? 100\n );\n this.undoStack = newState.undoStack;\n this.redoStack = newState.redoStack;\n }\n\n /**\n * Programmatically undo the last action.\n *\n * @returns The undone action, or null if nothing to undo\n */\n undo(): EditAction | null {\n const result = undo({ undoStack: this.undoStack, redoStack: this.redoStack });\n if (result.action) {\n const rows = this.rows as Record<string, unknown>[];\n if (rows[result.action.rowIndex]) {\n rows[result.action.rowIndex][result.action.field] = result.action.oldValue;\n }\n this.undoStack = result.newState.undoStack;\n this.redoStack = result.newState.redoStack;\n this.requestRender();\n }\n return result.action;\n }\n\n /**\n * Programmatically redo the last undone action.\n *\n * @returns The redone action, or null if nothing to redo\n */\n redo(): EditAction | null {\n const result = redo({ undoStack: this.undoStack, redoStack: this.redoStack });\n if (result.action) {\n const rows = this.rows as Record<string, unknown>[];\n if (rows[result.action.rowIndex]) {\n rows[result.action.rowIndex][result.action.field] = result.action.newValue;\n }\n this.undoStack = result.newState.undoStack;\n this.redoStack = result.newState.redoStack;\n this.requestRender();\n }\n return result.action;\n }\n\n /**\n * Check if there are any actions that can be undone.\n */\n canUndo(): boolean {\n return canUndo({ undoStack: this.undoStack, redoStack: this.redoStack });\n }\n\n /**\n * Check if there are any actions that can be redone.\n */\n canRedo(): boolean {\n return canRedo({ undoStack: this.undoStack, redoStack: this.redoStack });\n }\n\n /**\n * Clear all undo/redo history.\n */\n clearHistory(): void {\n const newState = clearHistory();\n this.undoStack = newState.undoStack;\n this.redoStack = newState.redoStack;\n }\n\n /**\n * Get a copy of the current undo stack.\n */\n getUndoStack(): EditAction[] {\n return [...this.undoStack];\n }\n\n /**\n * Get a copy of the current redo stack.\n */\n getRedoStack(): EditAction[] {\n return [...this.redoStack];\n }\n}\n","/**\r\n * Column Visibility Plugin (Class-based)\r\n *\r\n * Provides a UI for column visibility control via the shell's tool panel system.\r\n * Column visibility is a core grid feature - this plugin provides:\r\n * - A tool panel for column visibility management (registered with the shell)\r\n * - Backward-compatible API methods that delegate to grid.setColumnVisible(), etc.\r\n *\r\n * The grid emits 'column-visibility' events when columns are shown/hidden,\r\n * allowing consumers to save user preferences.\r\n *\r\n * When a reorder plugin is present, column rows become draggable for reordering.\r\n * Drag-drop emits 'column-reorder-request' events that the ReorderPlugin can listen for.\r\n */\r\n\r\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\r\nimport type { ColumnConfig, ToolPanelDefinition } from '../../core/types';\r\nimport type { VisibilityConfig } from './types';\r\n\r\n/**\r\n * Detail for column-reorder-request events emitted when users drag-drop in the visibility panel.\r\n */\r\nexport interface ColumnReorderRequestDetail {\r\n /** The field name of the column to move */\r\n field: string;\r\n /** The source index (before move) */\r\n fromIndex: number;\r\n /** The target index (after move) */\r\n toIndex: number;\r\n}\r\n\r\n/**\r\n * Check if a column can be moved (respects lockPosition/suppressMovable).\r\n * Inlined to avoid importing from reorder plugin.\r\n */\r\nfunction canMoveColumn(column: ColumnConfig): boolean {\r\n const meta = column.meta ?? {};\r\n return meta.lockPosition !== true && meta.suppressMovable !== true;\r\n}\r\n\r\n/** Extended grid interface with visibility methods */\r\ninterface GridWithVisibility {\r\n shadowRoot: ShadowRoot | null;\r\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }>;\r\n setColumnVisible(field: string, visible: boolean): void;\r\n toggleColumnVisibility(field: string): void;\r\n showAllColumns(): void;\r\n isColumnVisible(field: string): boolean;\r\n requestRender(): void;\r\n openToolPanel(id: string): void;\r\n closeToolPanel(): void;\r\n toggleToolPanel(id: string): void;\r\n activeToolPanel: string | undefined;\r\n}\r\n\r\n/**\r\n * Column Visibility Plugin for tbw-grid\r\n *\r\n * @example\r\n * ```ts\r\n * new VisibilityPlugin({ enabled: true, allowHideAll: false })\r\n * ```\r\n */\r\nexport class VisibilityPlugin extends BaseGridPlugin<VisibilityConfig> {\r\n readonly name = 'visibility';\r\n override readonly version = '1.0.0';\r\n\r\n /** Tool panel ID for shell integration */\r\n static readonly PANEL_ID = 'columns';\r\n\r\n protected override get defaultConfig(): Partial<VisibilityConfig> {\r\n return {\r\n enabled: true,\r\n allowHideAll: false,\r\n };\r\n }\r\n\r\n // ===== Internal State =====\r\n private columnListElement: HTMLElement | null = null;\r\n\r\n // Drag state for reorder integration\r\n private isDragging = false;\r\n private draggedField: string | null = null;\r\n private draggedIndex: number | null = null;\r\n private dropIndex: number | null = null;\r\n\r\n // ===== Lifecycle =====\r\n\r\n override detach(): void {\r\n this.columnListElement = null;\r\n this.isDragging = false;\r\n this.draggedField = null;\r\n this.draggedIndex = null;\r\n this.dropIndex = null;\r\n }\r\n\r\n // ===== Shell Integration =====\r\n\r\n /**\r\n * Register the column visibility tool panel with the shell.\r\n */\r\n override getToolPanel(): ToolPanelDefinition | undefined {\r\n if (!this.config.enabled) return undefined;\r\n\r\n return {\r\n id: VisibilityPlugin.PANEL_ID,\r\n title: 'Columns',\r\n icon: '☰',\r\n tooltip: 'Column visibility',\r\n order: 100, // High order so it appears last\r\n render: (container) => this.renderPanelContent(container),\r\n };\r\n }\r\n\r\n // ===== Public API =====\r\n\r\n /**\r\n * Show the visibility sidebar panel.\r\n */\r\n show(): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.openToolPanel(VisibilityPlugin.PANEL_ID);\r\n }\r\n\r\n /**\r\n * Hide the visibility sidebar panel.\r\n */\r\n hide(): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.closeToolPanel();\r\n }\r\n\r\n /**\r\n * Toggle the visibility sidebar panel.\r\n */\r\n toggle(): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.toggleToolPanel(VisibilityPlugin.PANEL_ID);\r\n }\r\n\r\n /**\r\n * Check if a specific column is visible.\r\n * Delegates to grid.isColumnVisible().\r\n * @param field - The field name to check\r\n * @returns True if the column is visible\r\n */\r\n isColumnVisible(field: string): boolean {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n return grid.isColumnVisible(field);\r\n }\r\n\r\n /**\r\n * Set visibility for a specific column.\r\n * Delegates to grid.setColumnVisible().\r\n * @param field - The field name of the column\r\n * @param visible - Whether the column should be visible\r\n */\r\n setColumnVisible(field: string, visible: boolean): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.setColumnVisible(field, visible);\r\n }\r\n\r\n /**\r\n * Get list of all visible column fields.\r\n * @returns Array of visible field names\r\n */\r\n getVisibleColumns(): string[] {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n return grid\r\n .getAllColumns()\r\n .filter((c) => c.visible)\r\n .map((c) => c.field);\r\n }\r\n\r\n /**\r\n * Get list of all hidden column fields.\r\n * @returns Array of hidden field names\r\n */\r\n getHiddenColumns(): string[] {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n return grid\r\n .getAllColumns()\r\n .filter((c) => !c.visible)\r\n .map((c) => c.field);\r\n }\r\n\r\n /**\r\n * Show all columns.\r\n * Delegates to grid.showAllColumns().\r\n */\r\n showAll(): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.showAllColumns();\r\n }\r\n\r\n /**\r\n * Toggle visibility for a specific column.\r\n * Delegates to grid.toggleColumnVisibility().\r\n * @param field - The field name of the column\r\n */\r\n toggleColumn(field: string): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.toggleColumnVisibility(field);\r\n }\r\n\r\n /**\r\n * Show a specific column.\r\n * Delegates to grid.setColumnVisible().\r\n * @param field - The field name of the column to show\r\n */\r\n showColumn(field: string): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.setColumnVisible(field, true);\r\n }\r\n\r\n /**\r\n * Hide a specific column.\r\n * Delegates to grid.setColumnVisible().\r\n * @param field - The field name of the column to hide\r\n */\r\n hideColumn(field: string): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n grid.setColumnVisible(field, false);\r\n }\r\n\r\n /**\r\n * Get all columns with their visibility status.\r\n * Useful for building visibility UI.\r\n * @returns Array of column info with visibility status\r\n */\r\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n return grid.getAllColumns();\r\n }\r\n\r\n /**\r\n * Check if the sidebar panel is currently open.\r\n * @returns True if the panel is open\r\n */\r\n isPanelVisible(): boolean {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n return grid.activeToolPanel === VisibilityPlugin.PANEL_ID;\r\n }\r\n\r\n // ===== Private Methods =====\r\n\r\n /**\r\n * Render the panel content into the shell's tool panel container.\r\n * Returns a cleanup function.\r\n */\r\n private renderPanelContent(container: HTMLElement): (() => void) | void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n\r\n // Create content wrapper\r\n const wrapper = document.createElement('div');\r\n wrapper.className = 'tbw-visibility-content';\r\n\r\n // Column list container\r\n const columnList = document.createElement('div');\r\n columnList.className = 'tbw-visibility-list';\r\n wrapper.appendChild(columnList);\r\n\r\n // Show all button\r\n const showAllBtn = document.createElement('button');\r\n showAllBtn.className = 'tbw-visibility-show-all';\r\n showAllBtn.textContent = 'Show All';\r\n showAllBtn.addEventListener('click', () => {\r\n grid.showAllColumns();\r\n this.rebuildToggles(columnList);\r\n });\r\n wrapper.appendChild(showAllBtn);\r\n\r\n // Store reference\r\n this.columnListElement = columnList;\r\n\r\n // Build initial toggles\r\n this.rebuildToggles(columnList);\r\n\r\n // Append to container\r\n container.appendChild(wrapper);\r\n\r\n // Return cleanup function\r\n return () => {\r\n this.columnListElement = null;\r\n wrapper.remove();\r\n };\r\n }\r\n\r\n /**\r\n * Check if a reorder plugin is present (by name to avoid static import).\r\n */\r\n private hasReorderPlugin(): boolean {\r\n const plugin = this.grid?.getPluginByName?.('reorder');\r\n // Duck-type check - just verify the plugin exists and has a moveColumn method\r\n return !!(plugin && typeof (plugin as { moveColumn?: unknown }).moveColumn === 'function');\r\n }\r\n\r\n /**\r\n * Build the column toggle checkboxes.\r\n * When a reorder plugin is present, adds drag handles for reordering.\r\n */\r\n private rebuildToggles(columnList: HTMLElement): void {\r\n const grid = this.grid as unknown as GridWithVisibility;\r\n const reorderEnabled = this.hasReorderPlugin();\r\n\r\n columnList.innerHTML = '';\r\n\r\n // getAllColumns() now returns columns in their effective display order\r\n const allColumns = grid.getAllColumns();\r\n\r\n for (let i = 0; i < allColumns.length; i++) {\r\n const col = allColumns[i];\r\n const label = col.header || col.field;\r\n\r\n const row = document.createElement('div');\r\n row.className = col.lockVisible ? 'tbw-visibility-row locked' : 'tbw-visibility-row';\r\n row.setAttribute('data-field', col.field);\r\n row.setAttribute('data-index', String(i));\r\n\r\n // Add drag handle if reorder is enabled\r\n if (reorderEnabled && canMoveColumn(col as unknown as ColumnConfig)) {\r\n row.draggable = true;\r\n row.classList.add('reorderable');\r\n\r\n this.setupDragListeners(row, col.field, i, columnList);\r\n }\r\n\r\n const labelWrapper = document.createElement('label');\r\n labelWrapper.className = 'tbw-visibility-label';\r\n\r\n const checkbox = document.createElement('input');\r\n checkbox.type = 'checkbox';\r\n checkbox.checked = col.visible;\r\n checkbox.disabled = col.lockVisible ?? false;\r\n checkbox.addEventListener('change', () => {\r\n grid.toggleColumnVisibility(col.field);\r\n // Refresh after toggle (grid may re-render)\r\n setTimeout(() => this.rebuildToggles(columnList), 0);\r\n });\r\n\r\n const text = document.createElement('span');\r\n text.textContent = label;\r\n\r\n labelWrapper.appendChild(checkbox);\r\n labelWrapper.appendChild(text);\r\n\r\n // Add drag handle icon if reorderable\r\n if (reorderEnabled && canMoveColumn(col as unknown as ColumnConfig)) {\r\n const handle = document.createElement('span');\r\n handle.className = 'tbw-visibility-handle';\r\n handle.textContent = '⋮⋮';\r\n handle.title = 'Drag to reorder';\r\n row.appendChild(handle);\r\n }\r\n\r\n row.appendChild(labelWrapper);\r\n columnList.appendChild(row);\r\n }\r\n }\r\n\r\n /**\r\n * Set up drag-and-drop event listeners for a row.\r\n * On drop, emits a 'column-reorder-request' event for other plugins to handle.\r\n */\r\n private setupDragListeners(row: HTMLElement, field: string, index: number, columnList: HTMLElement): void {\r\n row.addEventListener('dragstart', (e: DragEvent) => {\r\n this.isDragging = true;\r\n this.draggedField = field;\r\n this.draggedIndex = index;\r\n\r\n if (e.dataTransfer) {\r\n e.dataTransfer.effectAllowed = 'move';\r\n e.dataTransfer.setData('text/plain', field);\r\n }\r\n\r\n row.classList.add('dragging');\r\n });\r\n\r\n row.addEventListener('dragend', () => {\r\n this.isDragging = false;\r\n this.draggedField = null;\r\n this.draggedIndex = null;\r\n this.dropIndex = null;\r\n\r\n columnList.querySelectorAll('.tbw-visibility-row').forEach((r) => {\r\n r.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\r\n });\r\n });\r\n\r\n row.addEventListener('dragover', (e: DragEvent) => {\r\n e.preventDefault();\r\n if (!this.isDragging || this.draggedField === field) return;\r\n\r\n const rect = row.getBoundingClientRect();\r\n const midY = rect.top + rect.height / 2;\r\n\r\n this.dropIndex = e.clientY < midY ? index : index + 1;\r\n\r\n // Clear other row highlights\r\n columnList.querySelectorAll('.tbw-visibility-row').forEach((r) => {\r\n if (r !== row) r.classList.remove('drop-target', 'drop-before', 'drop-after');\r\n });\r\n\r\n row.classList.add('drop-target');\r\n row.classList.toggle('drop-before', e.clientY < midY);\r\n row.classList.toggle('drop-after', e.clientY >= midY);\r\n });\r\n\r\n row.addEventListener('dragleave', () => {\r\n row.classList.remove('drop-target', 'drop-before', 'drop-after');\r\n });\r\n\r\n row.addEventListener('drop', (e: DragEvent) => {\r\n e.preventDefault();\r\n const draggedField = this.draggedField;\r\n const draggedIndex = this.draggedIndex;\r\n const dropIndex = this.dropIndex;\r\n\r\n if (!this.isDragging || draggedField === null || draggedIndex === null || dropIndex === null) {\r\n return;\r\n }\r\n\r\n // Calculate the effective target index\r\n const effectiveToIndex = dropIndex > draggedIndex ? dropIndex - 1 : dropIndex;\r\n\r\n if (effectiveToIndex !== draggedIndex) {\r\n // Emit a request event - other plugins (like ReorderPlugin) can listen and handle\r\n const detail: ColumnReorderRequestDetail = {\r\n field: draggedField,\r\n fromIndex: draggedIndex,\r\n toIndex: effectiveToIndex,\r\n };\r\n this.emit<ColumnReorderRequestDetail>('column-reorder-request', detail);\r\n\r\n // Rebuild the panel after reorder (deferred to allow re-render)\r\n setTimeout(() => {\r\n this.rebuildToggles(columnList);\r\n }, 0);\r\n }\r\n });\r\n }\r\n\r\n // ===== Styles =====\r\n\r\n override readonly styles = `\r\n .tbw-visibility-content {\r\n display: flex;\r\n flex-direction: column;\r\n height: 100%;\r\n }\r\n\r\n .tbw-visibility-list {\r\n flex: 1;\r\n overflow-y: auto;\r\n padding: 8px;\r\n }\r\n\r\n .tbw-visibility-row {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n padding: 6px 4px;\r\n cursor: pointer;\r\n font-size: 13px;\r\n border-radius: var(--tbw-border-radius, 4px);\r\n position: relative;\r\n }\r\n .tbw-visibility-row:hover {\r\n background: var(--tbw-visibility-hover, var(--tbw-color-row-hover, #f3f4f6));\r\n }\r\n .tbw-visibility-row input[type=\"checkbox\"] {\r\n cursor: pointer;\r\n }\r\n .tbw-visibility-row.locked span {\r\n color: var(--tbw-color-fg-muted, #888);\r\n }\r\n\r\n /* Drag handle */\r\n .tbw-visibility-handle {\r\n cursor: grab;\r\n color: var(--tbw-color-fg-muted, #888);\r\n font-size: 10px;\r\n letter-spacing: -2px;\r\n user-select: none;\r\n flex-shrink: 0;\r\n }\r\n .tbw-visibility-row.reorderable:hover .tbw-visibility-handle {\r\n color: var(--tbw-color-fg, #1f2937);\r\n }\r\n\r\n /* Label wrapper for checkbox and text */\r\n .tbw-visibility-label {\r\n display: flex;\r\n align-items: center;\r\n gap: 8px;\r\n flex: 1;\r\n cursor: pointer;\r\n }\r\n\r\n /* Drag states */\r\n .tbw-visibility-row.dragging {\r\n opacity: 0.5;\r\n cursor: grabbing;\r\n }\r\n .tbw-visibility-row.drop-before::before {\r\n content: '';\r\n position: absolute;\r\n left: 0;\r\n right: 0;\r\n top: 0;\r\n height: 2px;\r\n background: var(--tbw-reorder-indicator, var(--tbw-color-accent, #3b82f6));\r\n }\r\n .tbw-visibility-row.drop-after::after {\r\n content: '';\r\n position: absolute;\r\n left: 0;\r\n right: 0;\r\n bottom: 0;\r\n height: 2px;\r\n background: var(--tbw-reorder-indicator, var(--tbw-color-accent, #3b82f6));\r\n }\r\n\r\n .tbw-visibility-show-all {\r\n margin: 8px;\r\n padding: 8px 12px;\r\n border: 1px solid var(--tbw-visibility-border, var(--tbw-color-border, #e5e7eb));\r\n border-radius: var(--tbw-border-radius, 4px);\r\n background: var(--tbw-visibility-btn-bg, var(--tbw-color-header-bg, #f9fafb));\r\n color: var(--tbw-color-fg, #1f2937);\r\n cursor: pointer;\r\n font-size: 13px;\r\n }\r\n .tbw-visibility-show-all:hover {\r\n background: var(--tbw-visibility-hover, var(--tbw-color-row-hover, #f3f4f6));\r\n }\r\n `;\r\n}\r\n"],"names":["getSortState","grid","sortMap","collectColumnState","plugins","columns","sortStates","col","index","state","internalCol","sortState","plugin","pluginState","applyColumnState","allColumns","stateMap","s","updatedColumns","updated","a","b","orderA","orderB","sortedByPriority","primarySort","colState","createStateChangeHandler","getPlugins","emit","timeoutId","FitModeEnum","inferType","value","inferColumns","rows","provided","typeMap","sample","k","v","type","c","EXPR_RE","EMPTY_SENTINEL","SAFE_EXPR","FORBIDDEN","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","field","rawType","header","sortable","editable","config","optionsAttr","item","label","viewTpl","editorTpl","headerTpl","mergeColumns","programmatic","dom","domMap","merged","d","m","addPart","token","existing","getColumnConfiguration","lightDomColumns","autoSizeColumns","mode","headerCells","changed","i","headerCell","max","rowEl","w","updateTemplate","min","defaultEditorFor","column","input","e","select","opt","o","commitValue","values","handleGridKeyDown","maxRow","maxCol","editing","colType","path","target","isFormField","tag","ensureCellVisible","rowHeight","viewport","y","rowIndex","vStart","vEnd","focusTarget","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","renderInlineRow","fastPatchRow","isChanged","hasChangedClass","children","colsLen","childLen","minLen","hasSpecialCols","rowIndexStr","displayStr","formatted","focusRow","focusCol","editMode","gridEl","fragment","colIndex","sticky","format","compiled","tplHolder","viewRenderer","externalView","needsSanitization","produced","spec","placeholder","context","output","blocked","rawTpl","tentative","isTrue","textContent","inlineEnterEdit","startRowEdit","rowElCurrent","col2","selectEl","newVal","commitCellValue","isDbl","firstCell","cellEl","active","targetCell","editor","exitRowEdit","revert","snapshot","current","rowIdx","colIdx","rowEl2","newValue","firstTime","originalValue","commit","cancel","inputLike","editorHost","editorSpec","clone","compiledEditor","node","g","hasHTMLInput","val","toggleSort","applySort","r","renderHeader","h","dir","comparator","rA","rB","headerRow","maybeTpl","span","icon","handle","hostRoot","gc","createResizeController","resizeState","pendingRaf","prevCursor","prevUserSelect","onMove","delta","width","justFinishedResize","onUp","hadResize","rect","createShellState","shouldRenderShellHeader","renderShellHeader","title","hasTitle","configButtons","hasConfigButtons","hasApiButtons","hasLightDomButtons","hasPanelToggles","showSeparator","sortedConfigButtons","sortedApiButtons","sortedPanels","toolbarHtml","btn","panel","isActive","renderShellBody","gridContentHtml","position","hasPanel","isOpen","activePanel","panelHtml","parseLightDomShell","headerEl","headerContents","toolButtons","setupShellEventListeners","shadowRoot","callbacks","toolbar","panelId","customBtn","btnId","closeBtn","renderCustomToolbarButtons","allButtons","slot","existingCleanup","cleanup","renderHeaderContent","contentArea","sortedContents","content","container","renderPanelContent","updateToolbarActiveStates","updatePanelState","panelDef","titleEl","getToolbarButtonsInfo","result","cleanupShellState","PluginManager","renderer","PluginClass","row","event","params","items","pluginItems","panels","contents","DataGridElement","#shadow","#initialized","#readyPromise","#readyResolve","#rows","#columns","#gridConfig","#fitMode","#editOn","#effectiveConfig","#connected","#scrollRaf","#pendingScrollTop","#hasScrollPlugins","#renderRowHook","#boundEsc","#boundOutside","#isDragging","#boundMouseMove","#boundMouseUp","#pluginManager","#stateChangeHandler","#initialColumnState","#shellState","#shellInitialized","oldValue","#onRowsChanged","#onColsChanged","#onGridConfigChanged","#onFitChanged","#onEditModeChanged","#injectStyles","sheet","styles","#rebuildRowModel","#processColumns","#renderHeader","#executeAfterRender","#initializePlugins","pluginsConfig","#injectAllPluginStyles","allStyles","styleEl","#updatePluginConfigs","#destroyPlugins","#collectPluginShellContributions","pluginPanels","pluginContents","#mergeEffectiveConfig","#render","#afterConnect","gridRoot","defaultOpen","#setup","#exitRowEdit","fauxScrollbar","rowsEl","currentScrollTop","subPixelOffset","#onScrollBatched","rowsBody","#handleMouseDown","#handleMouseMove","#handleMouseUp","firstRow","#updateAriaSelection","#emit","eventName","detail","isActiveRow","#autoSizeColumns","#getColumnConfiguration","visibleCols","processedColumns","processedMap","processed","originalRows","processedRows","base","domCols","map","exist","existingCols","#renderVisibleRows","#startRowEdit","configCols","existingHiddenMap","seeded","#applyColumnStateInternal","allCols","#shouldBypassVirtualization","scrollTop","scrollEvent","cellClickEvent","headerClickEvent","#buildCellMouseEvent","elAtPoint","silent","visible","wasHidden","willBeHidden","isCurrentlyHidden","order","columnMap","reordered","#applyColumnState","#refreshShellHeader","contentId","button","buttonId","disabled","apiBtn","force","totalRows","viewportHeight","visibleCount","shellConfig","hasShell","shellHeaderHtml","shellBodyHtml","#setupShellListeners","#handleToolbarButtonClick","configBtn","BaseGridPlugin","message","normalizeRange","range","toPublicRange","normalized","toPublicRanges","ranges","isCellInRange","isCellInAnyRange","getCellsInRange","cells","getAllCellsInRanges","cellMap","createRangeFromAnchor","anchor","buildSelectionEvent","colCount","SelectionPlugin","originalEvent","#buildEvent","shiftKey","ctrlKey","newRange","rowCount","allRange","_event","allRows","generateRowKey","parentKey","flattenTree","expandedKeys","depth","childrenField","hasChildren","isExpanded","childRows","toggleExpand","newExpanded","expandAll","keys","childKeys","collapseAll","getPathToKey","targetKey","childPath","expandToKey","existingExpanded","detectTreeStructure","inferChildrenField","commonArrayFields","TreePlugin","flatRow","fr","indentWidth","showExpandIcons","cols","firstCol","originalRenderer","renderCtx","colConfig","spacer","rendered","GridClasses","GridDataAttrs","GridSelectors","GridCSSVars","DGEvents","PluginEvents","formatCellValue","delimiter","newline","buildClipboardText","selectedIndices","visibleColumns","lines","sortedIndices","idx","copyToClipboard","text","textarea","success","parseClipboardText","normalizedText","currentRow","currentCell","inQuotes","char","readFromClipboard","ClipboardPlugin","isCopy","isPaste","#handleCopy","#handlePaste","selectionPlugin","#getSelectionPlugin","selectedRows","hasRowSelection","hasRangeSelection","hasCellSelection","columnCount","#buildRangeText","#buildCellText","#buildSingleCellText","parsed","formattedValue","startRow","startCol","endRow","endCol","minRow","minCol","rangeColumns","indices","DEFAULT_COLUMN_WIDTH","parseColumnWidth","numeric","getColumnWidths","computeColumnOffsets","offsets","offset","computeTotalWidth","sum","getVisibleColumnRange","scrollLeft","viewportWidth","columnOffsets","columnWidths","overscan","binarySearchFirstVisible","rightEdge","low","high","mid","shouldVirtualize","threshold","autoEnable","ColumnVirtualizationPlugin","isVirtualized","leftPadding","bodyRows","rowsContainer","columnIndex","buildMenuItems","isItemDisabled","createMenuElement","onAction","menu","separator","menuItem","shortcut","arrow","subMenuItems","subMenu","positionMenu","x","menuRect","left","top","globalClickHandler","globalKeydownHandler","globalStyleSheet","contextMenuStyles","defaultItems","ContextMenuPlugin","fullParams","formatCsvValue","quote","buildCsv","options","bom","downloadBlob","blob","fileName","url","link","downloadCsv","escapeXml","buildExcelXml","xml","displayValue","downloadExcel","finalName","ExportPlugin","colSet","selectionState","jsonData","obj","computeVirtualWindow","visibleRows","shouldBypassVirtualization","matchesFilter","filter","caseSensitive","rawValue","stringValue","compareValue","filterValue","filterRows","filters","f","computeFilterCacheKey","getUniqueValues","filterPanelStyles","FilteringPlugin","filterList","newCacheKey","filterBtn","style","buttonEl","uniqueValues","excludedSet","currentSearchText","excluded","operator","valueTo","usedCustomRenderer","handler","panelRect","excludedValues","searchContainer","searchInput","actionsRow","selectAllLabel","selectAllCheckbox","selectAllText","updateSelectAllState","checkState","allChecked","noneChecked","newState","renderVisibleItems","valuesContainer","contentContainer","filteredValues","createItem","strValue","checkbox","totalItems","window","renderValues","filterText","lowerFilter","noMatch","debounceTimer","buttonRow","applyBtn","isChecked","original","clearBtn","filterModel","computeColumnGroups","explicitMap","groupsOrdered","pushImplicit","startIdx","prev","run","runStart","id","group","applyGroupedHeaderCellClasses","headerRowEl","groups","fieldToGroup","gid","last","buildGroupHeaderRow","groupRow","startIndex","isImplicit","hasColumnGroups","GroupingColumnsPlugin","existingGroupRow","groupId","builtInAggregators","acc","customAggregators","aggregatorRegistry","ref","getAggregator","runAggregator","buildGroupedRowModel","expanded","groupOn","parent","rawVal","depthIdx","seg","composite","flat","visit","toggleGroupExpansion","newSet","expandAllGroups","collapseAllGroups","getGroupRowCount","GroupingRowsPlugin","grouped","_rowIndex","handleToggle","labelText","count","aggregators","groupRows","gridTemplate","firstColAgg","aggResult","aggRef","newKeys","toggleDetailRow","expandedRows","expandDetailRow","collapseDetailRow","isDetailExpanded","createDetailElement","detailRow","detailCell","MasterDetailPlugin","toggle","body","dataRows","detailEl","applySorts","sorts","sort","defaultComparator","aVal","bVal","maxColumns","getSortIndex","sortModel","getSortDirection","MultiSortPlugin","showIndex","sortIndex","sortDir","indicator","badge","model","existingIndex","newEntry","getLeftStickyColumns","getRightStickyColumns","hasStickyColumns","applyStickyOffsets","fieldToIndex","right","clearStickyOffsets","PinnedColumnsPlugin","createInfoBarElement","pinnedRows","center","filteredCount","selectedCount","panelEl","renderCustomPanel","createAggregationContainer","renderAggregationRows","rowConfig","aggFn","staticVal","buildContext","filterState","PinnedRowsPlugin","aggregationRows","topRows","bottomRows","hasInfoContent","hasBottomInfoBar","needsFooter","newInfoBar","validatePivotConfig","errors","getPivotAggregator","aggFunc","vals","createValueKey","columnValues","valueField","buildPivot","rowGroupFields","columnGroupFields","valueFields","columnKeys","getUniqueColumnKeys","groupedData","groupByFields","pivotRows","buildPivotRows","totals","calculateTotals","grandTotal","columnFields","fields","rowKey","total","colKey","vf","nums","aggregator","aggregatedResult","valueKey","flattenPivotRows","flatten","child","PivotPlugin","pr","pivotColumns","canMoveColumn","meta","moveColumn","fromIndex","toIndex","removed","ReorderPlugin","orderIndex","midX","draggedField","draggedIndex","dropIndex","effectiveToIndex","currentOrder","newOrder","originalOrder","getBlockNumber","blockSize","getBlockRange","blockNumber","getRequiredBlocks","startBlock","endBlock","blocks","loadBlock","dataSource","getRowFromCache","loadedBlocks","block","indexInBlock","SCROLL_DEBOUNCE_MS","ServerSidePlugin","gridRef","requiredBlocks","blockNum","cached","pushAction","action","maxSize","undoStack","undo","redo","redoStack","canUndo","canRedo","clearHistory","createEditAction","UndoRedoPlugin","isUndo","isRedo","VisibilityPlugin","wrapper","columnList","showAllBtn","reorderEnabled","labelWrapper","midY"],"mappings":"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"}