@toolbox-web/grid 0.2.8 → 0.3.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 (273) hide show
  1. package/README.md +13 -13
  2. package/all.d.ts +25 -5659
  3. package/all.d.ts.map +1 -0
  4. package/all.js +524 -450
  5. package/all.js.map +1 -1
  6. package/index.d.ts +8 -2678
  7. package/index.d.ts.map +1 -0
  8. package/index.js +3929 -25
  9. package/index.js.map +1 -1
  10. package/lib/core/constants.d.ts +114 -0
  11. package/lib/core/constants.d.ts.map +1 -0
  12. package/lib/core/grid.d.ts +337 -0
  13. package/lib/core/grid.d.ts.map +1 -0
  14. package/lib/core/internal/aggregators.d.ts +67 -0
  15. package/lib/core/internal/aggregators.d.ts.map +1 -0
  16. package/lib/core/internal/column-state.d.ts +124 -0
  17. package/lib/core/internal/column-state.d.ts.map +1 -0
  18. package/lib/core/internal/columns.d.ts +107 -0
  19. package/lib/core/internal/columns.d.ts.map +1 -0
  20. package/lib/core/internal/dom-builder.d.ts +115 -0
  21. package/lib/core/internal/dom-builder.d.ts.map +1 -0
  22. package/lib/core/internal/editing.d.ts +76 -0
  23. package/lib/core/internal/editing.d.ts.map +1 -0
  24. package/lib/core/internal/editors.d.ts +8 -0
  25. package/lib/core/internal/editors.d.ts.map +1 -0
  26. package/lib/core/internal/event-delegation.d.ts +11 -0
  27. package/lib/core/internal/event-delegation.d.ts.map +1 -0
  28. package/lib/core/internal/grid-internals.d.ts +83 -0
  29. package/lib/core/internal/grid-internals.d.ts.map +1 -0
  30. package/lib/core/internal/header.d.ts +7 -0
  31. package/lib/core/internal/header.d.ts.map +1 -0
  32. package/lib/core/internal/idle-scheduler.d.ts +65 -0
  33. package/lib/core/internal/idle-scheduler.d.ts.map +1 -0
  34. package/lib/core/internal/inference.d.ts +12 -0
  35. package/lib/core/internal/inference.d.ts.map +1 -0
  36. package/lib/core/internal/keyboard.d.ts +18 -0
  37. package/lib/core/internal/keyboard.d.ts.map +1 -0
  38. package/lib/core/internal/resize.d.ts +3 -0
  39. package/lib/core/internal/resize.d.ts.map +1 -0
  40. package/lib/core/internal/rows.d.ts +35 -0
  41. package/lib/core/internal/rows.d.ts.map +1 -0
  42. package/lib/core/internal/sanitize.d.ts +13 -0
  43. package/lib/core/internal/sanitize.d.ts.map +1 -0
  44. package/lib/core/internal/shell.d.ts +228 -0
  45. package/lib/core/internal/shell.d.ts.map +1 -0
  46. package/lib/core/internal/sorting.d.ts +24 -0
  47. package/lib/core/internal/sorting.d.ts.map +1 -0
  48. package/lib/core/internal/touch-scroll.d.ts +54 -0
  49. package/lib/core/internal/touch-scroll.d.ts.map +1 -0
  50. package/lib/core/internal/utils.d.ts +38 -0
  51. package/lib/core/internal/utils.d.ts.map +1 -0
  52. package/lib/core/internal/virtualization.d.ts +66 -0
  53. package/lib/core/internal/virtualization.d.ts.map +1 -0
  54. package/lib/core/plugin/base-plugin.d.ts +616 -0
  55. package/lib/core/plugin/base-plugin.d.ts.map +1 -0
  56. package/lib/core/plugin/index.d.ts +11 -0
  57. package/lib/core/plugin/index.d.ts.map +1 -0
  58. package/lib/core/plugin/plugin-manager.d.ts +183 -0
  59. package/lib/core/plugin/plugin-manager.d.ts.map +1 -0
  60. package/lib/core/plugin/types.d.ts +196 -0
  61. package/lib/core/plugin/types.d.ts.map +1 -0
  62. package/lib/core/types.d.ts +841 -0
  63. package/lib/core/types.d.ts.map +1 -0
  64. package/lib/plugins/clipboard/ClipboardPlugin.d.ts +46 -0
  65. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -0
  66. package/lib/plugins/clipboard/copy.d.ts +47 -0
  67. package/lib/plugins/clipboard/copy.d.ts.map +1 -0
  68. package/lib/plugins/clipboard/index.d.ts +7 -0
  69. package/lib/plugins/clipboard/index.d.ts.map +1 -0
  70. package/lib/plugins/clipboard/index.js.map +1 -1
  71. package/lib/plugins/clipboard/paste.d.ts +25 -0
  72. package/lib/plugins/clipboard/paste.d.ts.map +1 -0
  73. package/lib/plugins/clipboard/types.d.ts +40 -0
  74. package/lib/plugins/clipboard/types.d.ts.map +1 -0
  75. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts +54 -0
  76. package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts.map +1 -0
  77. package/lib/plugins/column-virtualization/column-virtualization.d.ts +53 -0
  78. package/lib/plugins/column-virtualization/column-virtualization.d.ts.map +1 -0
  79. package/lib/plugins/column-virtualization/index.d.ts +7 -0
  80. package/lib/plugins/column-virtualization/index.d.ts.map +1 -0
  81. package/lib/plugins/column-virtualization/index.js.map +1 -1
  82. package/lib/plugins/column-virtualization/types.d.ts +41 -0
  83. package/lib/plugins/column-virtualization/types.d.ts.map +1 -0
  84. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +52 -0
  85. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -0
  86. package/lib/plugins/context-menu/index.d.ts +7 -0
  87. package/lib/plugins/context-menu/index.d.ts.map +1 -0
  88. package/lib/plugins/context-menu/index.js +24 -24
  89. package/lib/plugins/context-menu/index.js.map +1 -1
  90. package/lib/plugins/context-menu/menu.d.ts +38 -0
  91. package/lib/plugins/context-menu/menu.d.ts.map +1 -0
  92. package/lib/plugins/context-menu/types.d.ts +77 -0
  93. package/lib/plugins/context-menu/types.d.ts.map +1 -0
  94. package/lib/plugins/export/ExportPlugin.d.ts +53 -0
  95. package/lib/plugins/export/ExportPlugin.d.ts.map +1 -0
  96. package/lib/plugins/export/csv.d.ts +31 -0
  97. package/lib/plugins/export/csv.d.ts.map +1 -0
  98. package/lib/plugins/export/excel.d.ts +12 -0
  99. package/lib/plugins/export/excel.d.ts.map +1 -0
  100. package/lib/plugins/export/index.d.ts +7 -0
  101. package/lib/plugins/export/index.d.ts.map +1 -0
  102. package/lib/plugins/export/index.js.map +1 -1
  103. package/lib/plugins/export/types.d.ts +57 -0
  104. package/lib/plugins/export/types.d.ts.map +1 -0
  105. package/lib/plugins/filtering/FilteringPlugin.d.ts +128 -0
  106. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -0
  107. package/lib/plugins/filtering/filter-model.d.ts +38 -0
  108. package/lib/plugins/filtering/filter-model.d.ts.map +1 -0
  109. package/lib/plugins/filtering/index.d.ts +7 -0
  110. package/lib/plugins/filtering/index.d.ts.map +1 -0
  111. package/lib/plugins/filtering/index.js +5 -5
  112. package/lib/plugins/filtering/index.js.map +1 -1
  113. package/lib/plugins/filtering/types.d.ts +157 -0
  114. package/lib/plugins/filtering/types.d.ts.map +1 -0
  115. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts +51 -0
  116. package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts.map +1 -0
  117. package/lib/plugins/grouping-columns/grouping-columns.d.ts +41 -0
  118. package/lib/plugins/grouping-columns/grouping-columns.d.ts.map +1 -0
  119. package/lib/plugins/grouping-columns/index.d.ts +7 -0
  120. package/lib/plugins/grouping-columns/index.d.ts.map +1 -0
  121. package/lib/plugins/grouping-columns/index.js +58 -42
  122. package/lib/plugins/grouping-columns/index.js.map +1 -1
  123. package/lib/plugins/grouping-columns/types.d.ts +91 -0
  124. package/lib/plugins/grouping-columns/types.d.ts.map +1 -0
  125. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts +120 -0
  126. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -0
  127. package/lib/plugins/grouping-rows/grouping-rows.d.ts +51 -0
  128. package/lib/plugins/grouping-rows/grouping-rows.d.ts.map +1 -0
  129. package/lib/plugins/grouping-rows/index.d.ts +7 -0
  130. package/lib/plugins/grouping-rows/index.d.ts.map +1 -0
  131. package/lib/plugins/grouping-rows/index.js +51 -51
  132. package/lib/plugins/grouping-rows/index.js.map +1 -1
  133. package/lib/plugins/grouping-rows/types.d.ts +95 -0
  134. package/lib/plugins/grouping-rows/types.d.ts.map +1 -0
  135. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +147 -0
  136. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -0
  137. package/lib/plugins/master-detail/index.d.ts +7 -0
  138. package/lib/plugins/master-detail/index.d.ts.map +1 -0
  139. package/lib/plugins/master-detail/index.js +235 -78
  140. package/lib/plugins/master-detail/index.js.map +1 -1
  141. package/lib/plugins/master-detail/master-detail.d.ts +30 -0
  142. package/lib/plugins/master-detail/master-detail.d.ts.map +1 -0
  143. package/lib/plugins/master-detail/types.d.ts +40 -0
  144. package/lib/plugins/master-detail/types.d.ts.map +1 -0
  145. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts +58 -0
  146. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts.map +1 -0
  147. package/lib/plugins/multi-sort/index.d.ts +7 -0
  148. package/lib/plugins/multi-sort/index.d.ts.map +1 -0
  149. package/lib/plugins/multi-sort/index.js.map +1 -1
  150. package/lib/plugins/multi-sort/multi-sort.d.ts +51 -0
  151. package/lib/plugins/multi-sort/multi-sort.d.ts.map +1 -0
  152. package/lib/plugins/multi-sort/types.d.ts +25 -0
  153. package/lib/plugins/multi-sort/types.d.ts.map +1 -0
  154. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts +58 -0
  155. package/lib/plugins/pinned-columns/PinnedColumnsPlugin.d.ts.map +1 -0
  156. package/lib/plugins/pinned-columns/index.d.ts +7 -0
  157. package/lib/plugins/pinned-columns/index.d.ts.map +1 -0
  158. package/lib/plugins/pinned-columns/index.js.map +1 -1
  159. package/lib/plugins/pinned-columns/pinned-columns.d.ts +62 -0
  160. package/lib/plugins/pinned-columns/pinned-columns.d.ts.map +1 -0
  161. package/lib/plugins/pinned-columns/types.d.ts +20 -0
  162. package/lib/plugins/pinned-columns/types.d.ts.map +1 -0
  163. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts +64 -0
  164. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -0
  165. package/lib/plugins/pinned-rows/index.d.ts +7 -0
  166. package/lib/plugins/pinned-rows/index.d.ts.map +1 -0
  167. package/lib/plugins/pinned-rows/index.js +1 -1
  168. package/lib/plugins/pinned-rows/index.js.map +1 -1
  169. package/lib/plugins/pinned-rows/pinned-rows.d.ts +43 -0
  170. package/lib/plugins/pinned-rows/pinned-rows.d.ts.map +1 -0
  171. package/lib/plugins/pinned-rows/types.d.ts +95 -0
  172. package/lib/plugins/pinned-rows/types.d.ts.map +1 -0
  173. package/lib/plugins/pivot/PivotPlugin.d.ts +94 -0
  174. package/lib/plugins/pivot/PivotPlugin.d.ts.map +1 -0
  175. package/lib/plugins/pivot/index.d.ts +7 -0
  176. package/lib/plugins/pivot/index.d.ts.map +1 -0
  177. package/lib/plugins/pivot/index.js.map +1 -1
  178. package/lib/plugins/pivot/pivot-engine.d.ts +50 -0
  179. package/lib/plugins/pivot/pivot-engine.d.ts.map +1 -0
  180. package/lib/plugins/pivot/pivot-model.d.ts +6 -0
  181. package/lib/plugins/pivot/pivot-model.d.ts.map +1 -0
  182. package/lib/plugins/pivot/pivot-panel.d.ts +25 -0
  183. package/lib/plugins/pivot/pivot-panel.d.ts.map +1 -0
  184. package/lib/plugins/pivot/pivot-rows.d.ts +33 -0
  185. package/lib/plugins/pivot/pivot-rows.d.ts.map +1 -0
  186. package/lib/plugins/pivot/types.d.ts +62 -0
  187. package/lib/plugins/pivot/types.d.ts.map +1 -0
  188. package/lib/plugins/reorder/ReorderPlugin.d.ts +81 -0
  189. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -0
  190. package/lib/plugins/reorder/column-drag.d.ts +41 -0
  191. package/lib/plugins/reorder/column-drag.d.ts.map +1 -0
  192. package/lib/plugins/reorder/index.d.ts +7 -0
  193. package/lib/plugins/reorder/index.d.ts.map +1 -0
  194. package/lib/plugins/reorder/index.js +51 -48
  195. package/lib/plugins/reorder/index.js.map +1 -1
  196. package/lib/plugins/reorder/types.d.ts +54 -0
  197. package/lib/plugins/reorder/types.d.ts.map +1 -0
  198. package/lib/plugins/selection/SelectionPlugin.d.ts +77 -0
  199. package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -0
  200. package/lib/plugins/selection/index.d.ts +8 -0
  201. package/lib/plugins/selection/index.d.ts.map +1 -0
  202. package/lib/plugins/selection/index.js +86 -75
  203. package/lib/plugins/selection/index.js.map +1 -1
  204. package/lib/plugins/selection/range-selection.d.ts +109 -0
  205. package/lib/plugins/selection/range-selection.d.ts.map +1 -0
  206. package/lib/plugins/selection/row-selection.d.ts +48 -0
  207. package/lib/plugins/selection/row-selection.d.ts.map +1 -0
  208. package/lib/plugins/selection/types.d.ts +80 -0
  209. package/lib/plugins/selection/types.d.ts.map +1 -0
  210. package/lib/plugins/server-side/ServerSidePlugin.d.ts +56 -0
  211. package/lib/plugins/server-side/ServerSidePlugin.d.ts.map +1 -0
  212. package/lib/plugins/server-side/cache.d.ts +14 -0
  213. package/lib/plugins/server-side/cache.d.ts.map +1 -0
  214. package/lib/plugins/server-side/datasource.d.ts +12 -0
  215. package/lib/plugins/server-side/datasource.d.ts.map +1 -0
  216. package/lib/plugins/server-side/index.d.ts +7 -0
  217. package/lib/plugins/server-side/index.d.ts.map +1 -0
  218. package/lib/plugins/server-side/index.js.map +1 -1
  219. package/lib/plugins/server-side/types.d.ts +43 -0
  220. package/lib/plugins/server-side/types.d.ts.map +1 -0
  221. package/lib/plugins/tree/TreePlugin.d.ts +49 -0
  222. package/lib/plugins/tree/TreePlugin.d.ts.map +1 -0
  223. package/lib/plugins/tree/index.d.ts +8 -0
  224. package/lib/plugins/tree/index.d.ts.map +1 -0
  225. package/lib/plugins/tree/index.js.map +1 -1
  226. package/lib/plugins/tree/tree-data.d.ts +42 -0
  227. package/lib/plugins/tree/tree-data.d.ts.map +1 -0
  228. package/lib/plugins/tree/tree-detect.d.ts +24 -0
  229. package/lib/plugins/tree/tree-detect.d.ts.map +1 -0
  230. package/lib/plugins/tree/types.d.ts +61 -0
  231. package/lib/plugins/tree/types.d.ts.map +1 -0
  232. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +68 -0
  233. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts.map +1 -0
  234. package/lib/plugins/undo-redo/history.d.ts +64 -0
  235. package/lib/plugins/undo-redo/history.d.ts.map +1 -0
  236. package/lib/plugins/undo-redo/index.d.ts +7 -0
  237. package/lib/plugins/undo-redo/index.d.ts.map +1 -0
  238. package/lib/plugins/undo-redo/index.js.map +1 -1
  239. package/lib/plugins/undo-redo/types.d.ts +41 -0
  240. package/lib/plugins/undo-redo/types.d.ts.map +1 -0
  241. package/lib/plugins/visibility/VisibilityPlugin.d.ts +135 -0
  242. package/lib/plugins/visibility/VisibilityPlugin.d.ts.map +1 -0
  243. package/lib/plugins/visibility/index.d.ts +8 -0
  244. package/lib/plugins/visibility/index.d.ts.map +1 -0
  245. package/lib/plugins/visibility/index.js.map +1 -1
  246. package/lib/plugins/visibility/types.d.ts +33 -0
  247. package/lib/plugins/visibility/types.d.ts.map +1 -0
  248. package/lib/plugins/visibility/visibility.d.ts +30 -0
  249. package/lib/plugins/visibility/visibility.d.ts.map +1 -0
  250. package/package.json +6 -2
  251. package/public.d.ts +52 -0
  252. package/public.d.ts.map +1 -0
  253. package/umd/grid.all.umd.js +32 -74
  254. package/umd/grid.all.umd.js.map +1 -1
  255. package/umd/grid.umd.js +22 -64
  256. package/umd/grid.umd.js.map +1 -1
  257. package/umd/plugins/context-menu.umd.js +1 -1
  258. package/umd/plugins/context-menu.umd.js.map +1 -1
  259. package/umd/plugins/filtering.umd.js +1 -1
  260. package/umd/plugins/filtering.umd.js.map +1 -1
  261. package/umd/plugins/grouping-columns.umd.js +1 -1
  262. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  263. package/umd/plugins/grouping-rows.umd.js +1 -1
  264. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  265. package/umd/plugins/master-detail.umd.js +1 -1
  266. package/umd/plugins/master-detail.umd.js.map +1 -1
  267. package/umd/plugins/pinned-rows.umd.js +1 -1
  268. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  269. package/umd/plugins/pivot.umd.js.map +1 -1
  270. package/umd/plugins/selection.umd.js +1 -1
  271. package/umd/plugins/selection.umd.js.map +1 -1
  272. package/index-YjW60MHD.js +0 -3235
  273. package/index-YjW60MHD.js.map +0 -1
@@ -1 +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/core/constants.ts","../../../../libs/grid/src/public.ts","../../../../libs/grid/src/lib/core/internal/aggregators.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/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/pivot-panel.ts","../../../../libs/grid/src/lib/plugins/pivot/pivot-rows.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/selection/range-selection.ts","../../../../libs/grid/src/lib/plugins/selection/SelectionPlugin.ts","../../../../libs/grid/src/lib/plugins/server-side/datasource.ts","../../../../libs/grid/src/lib/plugins/server-side/ServerSidePlugin.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/plugins/undo-redo/history.ts","../../../../libs/grid/src/lib/plugins/undo-redo/UndoRedoPlugin.ts","../../../../libs/grid/src/lib/plugins/visibility/VisibilityPlugin.ts"],"sourcesContent":["/**\n * Column State Module\n *\n * Handles collection and application of column state for persistence.\n * State includes user-driven changes: order, width, visibility, and sort.\n * Plugins can contribute additional state via getColumnState/applyColumnState hooks.\n */\n\nimport type { BaseGridPlugin } from '../plugin';\nimport type {\n ColumnConfig,\n ColumnInternal,\n ColumnSortState,\n ColumnState,\n GridColumnState,\n InternalGrid,\n} from '../types';\n\n/** Debounce timeout for state change events */\nconst STATE_CHANGE_DEBOUNCE_MS = 100;\n\n/**\n * Get sort state for a column from the grid's sortState.\n */\nfunction getSortState(grid: InternalGrid): Map<string, ColumnSortState> {\n const sortMap = new Map<string, ColumnSortState>();\n\n // Core sort state (single column)\n if (grid._sortState) {\n sortMap.set(grid._sortState.field, {\n direction: grid._sortState.direction === 1 ? 'asc' : 'desc',\n priority: 0,\n });\n }\n\n return sortMap;\n}\n\n/**\n * Collect column state from the grid and all plugins.\n * Returns a complete GridColumnState object ready for serialization.\n */\nexport function collectColumnState<T>(grid: InternalGrid<T>, plugins: BaseGridPlugin[]): GridColumnState {\n const columns = grid._columns;\n const sortStates = getSortState(grid);\n\n return {\n columns: columns.map((col, index) => {\n // 1. Core state\n const state: ColumnState = {\n field: col.field,\n order: index,\n visible: true, // If it's in _columns, it's visible (hidden columns are filtered out)\n };\n\n // Include width if set (either from config or resize)\n const internalCol = col as ColumnInternal<T>;\n if (internalCol.__renderedWidth !== undefined) {\n state.width = internalCol.__renderedWidth;\n } else if (col.width !== undefined) {\n state.width = typeof col.width === 'string' ? parseFloat(col.width) : col.width;\n }\n\n // Include sort state if present\n const sortState = sortStates.get(col.field);\n if (sortState) {\n state.sort = sortState;\n }\n\n // 2. Collect from each plugin\n for (const plugin of plugins) {\n if (plugin.getColumnState) {\n const pluginState = plugin.getColumnState(col.field);\n if (pluginState) {\n Object.assign(state, pluginState);\n }\n }\n }\n\n return state;\n }),\n };\n}\n\n/**\n * Apply column state to the grid and all plugins.\n * Modifies the grid's internal state and triggers plugin state restoration.\n *\n * @param grid - The grid instance\n * @param state - The state to apply\n * @param allColumns - All available columns (including hidden ones)\n * @param plugins - Plugins that may have applyColumnState hooks\n */\nexport function applyColumnState<T>(\n grid: InternalGrid<T>,\n state: GridColumnState,\n allColumns: ColumnConfig<T>[],\n plugins: BaseGridPlugin[],\n): void {\n if (!state.columns || state.columns.length === 0) return;\n\n const stateMap = new Map(state.columns.map((s) => [s.field, s]));\n\n // 1. Apply width and visibility to columns\n const updatedColumns = allColumns.map((col) => {\n const s = stateMap.get(col.field);\n if (!s) return col;\n\n const updated: ColumnInternal<T> = { ...col };\n\n // Apply width\n if (s.width !== undefined) {\n updated.width = s.width;\n updated.__renderedWidth = s.width;\n }\n\n // Apply visibility (hidden is inverse of visible)\n if (s.visible !== undefined) {\n updated.hidden = !s.visible;\n }\n\n return updated;\n });\n\n // 2. Reorder columns based on state\n updatedColumns.sort((a, b) => {\n const orderA = stateMap.get(a.field)?.order ?? Infinity;\n const orderB = stateMap.get(b.field)?.order ?? Infinity;\n return orderA - orderB;\n });\n\n // 3. Update grid's internal columns\n grid._columns = updatedColumns as ColumnInternal<T>[];\n\n // 4. Apply sort state (core single-column sort)\n // Find the column with highest sort priority\n const sortedByPriority = state.columns\n .filter((s) => s.sort !== undefined)\n .sort((a, b) => (a.sort?.priority ?? 0) - (b.sort?.priority ?? 0));\n\n if (sortedByPriority.length > 0) {\n const primarySort = sortedByPriority[0];\n if (primarySort.sort) {\n grid._sortState = {\n field: primarySort.field,\n direction: primarySort.sort.direction === 'asc' ? 1 : -1,\n };\n }\n } else {\n grid._sortState = null;\n }\n\n // 5. Let each plugin apply its state\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n for (const colState of state.columns) {\n plugin.applyColumnState(colState.field, colState);\n }\n }\n }\n}\n\n/**\n * Create a state change handler with debouncing.\n * Returns a function that, when called, will eventually emit the state change event.\n */\nexport function createStateChangeHandler<T>(\n grid: InternalGrid<T>,\n getPlugins: () => BaseGridPlugin[],\n emit: (detail: GridColumnState) => void,\n): () => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return () => {\n // Clear any pending timeout\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n // Schedule the emit\n timeoutId = setTimeout(() => {\n timeoutId = null;\n const state = collectColumnState(grid, getPlugins());\n emit(state);\n }, STATE_CHANGE_DEBOUNCE_MS);\n };\n}\n\n/**\n * Compare two column states to check if they are equal.\n * Useful for preventing duplicate state change events.\n */\nexport function areColumnStatesEqual(a: GridColumnState, b: GridColumnState): boolean {\n if (a.columns.length !== b.columns.length) return false;\n\n for (let i = 0; i < a.columns.length; i++) {\n const colA = a.columns[i];\n const colB = b.columns[i];\n\n if (colA.field !== colB.field) return false;\n if (colA.order !== colB.order) return false;\n if (colA.visible !== colB.visible) return false;\n if (colA.width !== colB.width) return false;\n\n // Compare sort state\n const sortA = colA.sort;\n const sortB = colB.sort;\n if ((sortA === undefined) !== (sortB === undefined)) return false;\n if (sortA && sortB) {\n if (sortA.direction !== sortB.direction) return false;\n if (sortA.priority !== sortB.priority) return false;\n }\n }\n\n return true;\n}\n","import type { PluginQuery } from './plugin/base-plugin';\n\n/**\n * The compiled webcomponent interface for DataGrid\n */\nexport interface DataGridElement extends PublicGrid, HTMLElement {}\n\n/**\n * Public API interface for DataGrid component.\n *\n * **Property Getters vs Setters:**\n *\n * Property getters return the EFFECTIVE (resolved) value after merging all config sources.\n * This is the \"current situation\" - what consumers and plugins need to know.\n *\n * Property setters accept input values which are merged into the effective config.\n * Multiple sources can contribute (gridConfig, columns prop, light DOM, individual props).\n *\n * For example:\n * - `grid.fitMode` returns the resolved fitMode (e.g., 'stretch' even if you set undefined)\n * - `grid.columns` returns the effective columns after merging\n * - `grid.gridConfig` returns the full effective config\n */\nexport interface PublicGrid<T = any> {\n /**\n * Full config object. Setter merges with other inputs per precedence rules.\n * Getter returns the effective (resolved) config.\n */\n gridConfig?: GridConfig<T>;\n /**\n * Column definitions.\n * Getter returns effective columns (after merging config, light DOM, inference).\n */\n columns?: ColumnConfig<T>[];\n /** Current row data (after plugin processing like grouping, filtering). */\n rows?: T[];\n /** Resolves once the component has finished initial work (layout, inference). */\n ready?: () => Promise<void>;\n /** Force a layout / measurement pass (e.g. after container resize). */\n forceLayout?: () => Promise<void>;\n /** Return effective resolved config (after inference & precedence). */\n getConfig?: () => Promise<Readonly<GridConfig<T>>>;\n /** Toggle expansion state of a group row by its generated key. */\n toggleGroup?: (key: string) => Promise<void>;\n}\n\n/**\n * Internal-only augmented interface for DataGrid component\n */\nexport interface InternalGrid<T = any> extends PublicGrid<T>, GridConfig<T> {\n shadowRoot: ShadowRoot | null;\n _rows: T[];\n _columns: ColumnInternal<T>[];\n /** Visible columns only (excludes hidden). Use for rendering. */\n _visibleColumns: ColumnInternal<T>[];\n _headerRowEl: HTMLElement;\n _bodyEl: HTMLElement;\n _rowPool: HTMLElement[];\n _resizeController: ResizeController;\n _sortState: { field: string; direction: 1 | -1 } | null;\n __originalOrder: T[];\n __rowRenderEpoch: number;\n __didInitialAutoSize?: boolean;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n _gridTemplate: string;\n _virtualization: VirtualState;\n _focusRow: number;\n _focusCol: number;\n _activeEditRows: number;\n _rowEditSnapshots: Map<number, T>;\n _changedRowIndices: Set<number>;\n changedRows?: T[];\n changedRowIndices?: number[];\n effectiveConfig?: GridConfig<T>;\n findHeaderRow?: () => HTMLElement;\n refreshVirtualWindow: (full: boolean) => void;\n updateTemplate?: () => void;\n findRenderedRowElement?: (rowIndex: number) => HTMLElement | null;\n beginBulkEdit?: (rowIndex: number) => void;\n commitActiveRowEdit?: () => void;\n /** Dispatch cell click to plugin system, returns true if handled */\n _dispatchCellClick?: (event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement) => boolean;\n /** Dispatch row click to plugin system, returns true if handled */\n _dispatchRowClick?: (event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement) => boolean;\n /** Dispatch header click to plugin system, returns true if handled */\n _dispatchHeaderClick?: (event: MouseEvent, colIndex: number, headerEl: HTMLElement) => boolean;\n /** Dispatch keydown to plugin system, returns true if handled */\n _dispatchKeyDown?: (event: KeyboardEvent) => boolean;\n /** Get horizontal scroll boundary offsets from plugins (e.g., pinned columns) */\n _getHorizontalScrollOffsets?: (\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ) => { left: number; right: number; skipScroll?: boolean };\n /** Query all plugins with a generic query and collect responses */\n queryPlugins?: <T>(query: PluginQuery) => T[];\n /** Request emission of column-state-change event (debounced) */\n requestStateChange?: () => void;\n}\n\nexport type PrimitiveColumnType = 'number' | 'string' | 'date' | 'boolean' | 'select' | 'typeahead';\n\n/**\n * Base contract for a column. Public; kept intentionally lean so host apps can extend via intersection types.\n * Prefer adding optional properties here only when broadly useful to most grids.\n */\nexport interface BaseColumnConfig<TRow = any, TValue = any> {\n /** Unique field key referencing property in row objects */\n field: keyof TRow & string;\n /** Visible header label; defaults to capitalized field */\n header?: string;\n /** Column data type; inferred if omitted */\n type?: PrimitiveColumnType;\n /** Column width in pixels; fixed size (no flexibility) */\n width?: string | number;\n /** Minimum column width in pixels (stretch mode only); when set, column uses minmax(minWidth, 1fr) */\n minWidth?: number;\n /** Whether column can be sorted */\n sortable?: boolean;\n /** Whether column can be resized by user */\n resizable?: boolean;\n /** Optional custom comparator for sorting (a,b) -> number */\n sortComparator?: (a: TValue, b: TValue, rowA: TRow, rowB: TRow) => number;\n /** Whether the field is editable (enables editors) */\n editable?: boolean;\n /** Optional custom editor factory or element tag name */\n editor?: ColumnEditorSpec<TRow, TValue>;\n /** For select/typeahead types - available options */\n options?: Array<{ label: string; value: unknown }> | (() => Array<{ label: string; value: unknown }>);\n /** For select/typeahead - allow multi select */\n multi?: boolean;\n /** Optional formatter */\n format?: (value: TValue, row: TRow) => string;\n /** Arbitrary extra metadata */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Full column configuration including optional custom view/renderer & grouping metadata.\n */\nexport interface ColumnConfig<TRow = any> extends BaseColumnConfig<TRow, any> {\n /** Optional custom view renderer used instead of default text rendering */\n viewRenderer?: ColumnViewRenderer<TRow, any>;\n /** External view spec (lets host app mount any framework component) */\n externalView?: {\n component: unknown;\n props?: Record<string, unknown>;\n mount?: (options: {\n placeholder: HTMLElement;\n context: CellRenderContext<TRow, unknown>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n /** Whether the column is initially hidden */\n hidden?: boolean;\n /** Prevent this column from being hidden 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 = unknown, TValue = unknown> =\n | string // custom element tag name\n | ((context: ColumnEditorContext<TRow, TValue>) => HTMLElement | string)\n | {\n /** Arbitrary component reference (class, function, token) */\n component: unknown;\n /** Optional static props passed to mount */\n props?: Record<string, unknown>;\n /** Optional custom mount function; if provided we call it directly instead of emitting an event */\n mount?: (options: {\n placeholder: HTMLElement;\n context: ColumnEditorContext<TRow, TValue>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n\n/**\n * Context object provided to editor factories allowing mutation (commit/cancel) of a cell value.\n */\nexport interface ColumnEditorContext<TRow = any, TValue = any> {\n /** Underlying full row object for the active edit. */\n row: TRow;\n /** Current cell value (mutable only via commit). */\n value: TValue;\n /** Field name being edited. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n /** Accept the edit; triggers change tracking + rerender. */\n commit: (newValue: TValue) => void;\n /** Abort edit without persisting changes. */\n cancel: () => void;\n}\n\n/**\n * Context passed to custom view renderers (pure display – no commit helpers).\n */\nexport interface CellRenderContext<TRow = any, TValue = any> {\n /** Row object for the cell being rendered. */\n row: TRow;\n /** Value at field. */\n value: TValue;\n /** Field key. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n}\n\nexport type ColumnViewRenderer<TRow = unknown, TValue = unknown> = (\n ctx: CellRenderContext<TRow, TValue>,\n) => Node | string | void;\n\n// #region Internal-only augmented types (not re-exported publicly)\nexport interface ColumnInternal<T = any> extends ColumnConfig<T> {\n __autoSized?: boolean;\n __userResized?: boolean;\n __renderedWidth?: number;\n /** Original configured width (for reset on double-click) */\n __originalWidth?: number;\n __viewTemplate?: HTMLElement;\n __editorTemplate?: HTMLElement;\n __headerTemplate?: HTMLElement;\n __compiledView?: (ctx: CellContext<T>) => string;\n __compiledEditor?: (ctx: EditorExecContext<T>) => string;\n}\n\n/**\n * Runtime cell context used internally for compiled template execution.\n */\nexport interface CellContext<T = any> {\n row: T;\n value: unknown;\n field: string;\n column: ColumnInternal<T>;\n}\n\n/**\n * Internal editor execution context extending the generic cell context with commit helpers.\n */\nexport interface EditorExecContext<T = any> extends CellContext<T> {\n commit: (newValue: unknown) => void;\n cancel: () => void;\n}\n\n/** Controller managing drag-based column resize lifecycle. */\nexport interface ResizeController {\n start: (e: MouseEvent, colIndex: number, cell: HTMLElement) => void;\n /** Reset a column to its configured width (or auto-size if none configured). */\n resetColumn: (colIndex: number) => void;\n dispose: () => void;\n /** True while a resize drag is in progress (used to suppress header click/sort). */\n isResizing: boolean;\n}\n\n/** Virtual window bookkeeping; modified in-place as scroll position changes. */\nexport interface VirtualState {\n enabled: boolean;\n rowHeight: number;\n /** Threshold for bypassing virtualization (renders all rows if totalRows <= bypassThreshold) */\n bypassThreshold: number;\n start: number;\n end: number;\n /** Faux scrollbar element that provides scroll events (AG Grid pattern) */\n container: HTMLElement | null;\n /** Rows viewport element for measuring visible area height */\n viewportEl: HTMLElement | null;\n /** Spacer element inside faux scrollbar for setting virtual height */\n totalHeightEl: HTMLElement | null;\n}\n// #endregion\n\n// #region Grouping & Footer Public Types\n/**\n * Group row rendering customization options.\n * Used within grouping-rows plugin config for presentation of group rows.\n */\nexport interface RowGroupRenderConfig {\n /** If true, group rows span all columns (single full-width cell). Default false. */\n fullWidth?: boolean;\n /** Optional label formatter override. Receives raw group value + depth. */\n formatLabel?: (value: unknown, depth: number, key: string) => string;\n /** Optional aggregate overrides per field for group summary cells (only when not fullWidth). */\n aggregators?: Record<string, AggregatorRef>;\n /** Additional CSS class applied to each group row root element. */\n class?: string;\n}\n\nexport type AggregatorRef = string | ((rows: unknown[], field: string, column?: unknown) => unknown);\n\n/** Result of automatic column inference from sample rows. */\nexport interface InferredColumnResult<TRow = unknown> {\n columns: ColumnConfigMap<TRow>;\n typeMap: Record<string, PrimitiveColumnType>;\n}\n\nexport const FitModeEnum = {\n STRETCH: 'stretch',\n FIXED: 'fixed',\n} as const;\nexport type FitMode = (typeof FitModeEnum)[keyof typeof FitModeEnum]; // evaluates to 'stretch' | 'fixed'\n// #endregion\n\n// #region Plugin Interface\n/**\n * Minimal plugin interface for type-checking.\n * This interface is defined here to avoid circular imports with BaseGridPlugin.\n * All plugins must satisfy this shape (BaseGridPlugin implements it).\n */\nexport interface GridPlugin {\n /** Unique plugin identifier */\n readonly name: string;\n /** Plugin version */\n readonly version: string;\n /** CSS styles to inject into grid's shadow DOM */\n readonly styles?: string;\n}\n// #endregion\n\n// #region Grid Config\n/**\n * Grid configuration object - the **single source of truth** for grid behavior.\n *\n * Users can configure the grid via multiple input methods, all of which converge\n * into an effective `GridConfig` internally:\n *\n * **Configuration Input Methods:**\n * - `gridConfig` property - direct assignment of this object\n * - `columns` property - shorthand for `gridConfig.columns`\n * - `fitMode` property - shorthand for `gridConfig.fitMode`\n * - `editOn` property - shorthand for `gridConfig.editOn`\n * - Light DOM `<tbw-grid-column>` - declarative columns (merged into `columns`)\n * - Light DOM `<tbw-grid-header>` - declarative shell header (merged into `shell.header`)\n *\n * **Precedence (when same property set multiple ways):**\n * Individual props (`fitMode`, `editOn`) > `columns` prop > Light DOM > `gridConfig`\n *\n * @example\n * ```ts\n * // Via gridConfig (recommended for complex setups)\n * grid.gridConfig = {\n * columns: [{ field: 'name' }, { field: 'age' }],\n * fitMode: 'stretch',\n * plugins: [new SelectionPlugin()],\n * shell: { header: { title: 'My Grid' } }\n * };\n *\n * // Via individual props (convenience for simple cases)\n * grid.columns = [{ field: 'name' }, { field: 'age' }];\n * grid.fitMode = 'stretch';\n * ```\n */\nexport interface GridConfig<TRow = any> {\n /** Column definitions. Can also be set via `columns` prop or `<tbw-grid-column>` light DOM. */\n columns?: ColumnConfigMap<TRow>;\n /** Sizing mode for columns. Can also be set via `fitMode` prop. */\n fitMode?: FitMode;\n /** Edit activation mode ('click' | 'dblclick'). Can also be set via `editOn` prop. */\n editOn?: string;\n /**\n * Row height in pixels for virtualization calculations.\n * The virtualization system assumes uniform row heights for performance.\n *\n * If not specified, the grid measures the first rendered row's height,\n * which respects the CSS variable `--tbw-row-height` set by themes.\n *\n * Set this explicitly when:\n * - Row content may wrap to multiple lines (also set `--tbw-cell-white-space: normal`)\n * - Using custom row templates with variable content\n * - You want to override theme-defined row height\n *\n * @default Auto-measured from first row (respects --tbw-row-height CSS variable)\n *\n * @example\n * ```ts\n * // Fixed height for rows that may wrap to 2 lines\n * gridConfig = { rowHeight: 56 };\n * ```\n */\n rowHeight?: number;\n /**\n * Array of plugin instances.\n * Each plugin is instantiated with its configuration and attached to this grid.\n *\n * @example\n * ```ts\n * plugins: [\n * new SelectionPlugin({ mode: 'range' }),\n * new MultiSortPlugin(),\n * new FilteringPlugin({ debounceMs: 150 }),\n * ]\n * ```\n */\n plugins?: GridPlugin[];\n\n /**\n * Saved column state to restore on initialization.\n * Includes order, width, visibility, sort, and plugin-contributed state.\n */\n columnState?: GridColumnState;\n\n /**\n * Shell configuration for header bar and tool panels.\n * When configured, adds an optional wrapper with title, toolbar, and collapsible side panels.\n */\n shell?: ShellConfig;\n\n /**\n * Grid-wide icon configuration.\n * Provides consistent icons across all plugins (tree, grouping, sorting, etc.).\n * Plugins will use these by default but can override with their own config.\n */\n icons?: GridIcons;\n\n /**\n * Grid-wide animation configuration.\n * Controls animations for expand/collapse, reordering, and other visual transitions.\n * Individual plugins can override these defaults in their own config.\n */\n animation?: AnimationConfig;\n\n /**\n * Custom sort handler for full control over sorting behavior.\n *\n * When provided, this handler is called instead of the built-in sorting logic.\n * Enables custom sorting algorithms, server-side sorting, or plugin-specific sorting.\n *\n * The handler receives:\n * - `rows`: Current row array to sort\n * - `sortState`: Sort field and direction (1 = asc, -1 = desc)\n * - `columns`: Column configurations (for accessing sortComparator)\n *\n * Return the sorted array (sync) or a Promise that resolves to the sorted array (async).\n * For server-side sorting, return a Promise that resolves when data is fetched.\n *\n * @example\n * ```ts\n * // Custom stable sort\n * sortHandler: (rows, state, cols) => {\n * return stableSort(rows, (a, b) => compare(a[state.field], b[state.field]) * state.direction);\n * }\n *\n * // Server-side sorting\n * sortHandler: async (rows, state) => {\n * const response = await fetch(`/api/data?sort=${state.field}&dir=${state.direction}`);\n * return response.json();\n * }\n * ```\n */\n sortHandler?: SortHandler<TRow>;\n}\n// #endregion\n\n// #region Animation\n\n/**\n * Sort state passed to custom sort handlers.\n */\nexport interface SortState {\n /** Field to sort by */\n field: string;\n /** Sort direction: 1 = ascending, -1 = descending */\n direction: 1 | -1;\n}\n\n/**\n * Custom sort handler function signature.\n *\n * @param rows - Current row array to sort\n * @param sortState - Sort field and direction\n * @param columns - Column configurations (for accessing sortComparator)\n * @returns Sorted array (sync) or Promise resolving to sorted array (async)\n */\nexport type SortHandler<TRow = any> = (\n rows: TRow[],\n sortState: SortState,\n columns: ColumnConfig<TRow>[],\n) => TRow[] | Promise<TRow[]>;\n\n/**\n * Animation behavior mode.\n * - `true` or `'on'`: Animations always enabled\n * - `false` or `'off'`: Animations always disabled\n * - `'reduced-motion'`: Respects `prefers-reduced-motion` media query (default)\n */\nexport type AnimationMode = boolean | 'on' | 'off' | 'reduced-motion';\n\n/**\n * Animation style for visual transitions.\n * - `'slide'`: Slide/transform animation (e.g., expand down, slide left/right)\n * - `'fade'`: Opacity fade animation\n * - `'flip'`: FLIP technique for position changes (First, Last, Invert, Play)\n * - `false`: No animation for this specific feature\n */\nexport type AnimationStyle = 'slide' | 'fade' | 'flip' | false;\n\n/**\n * Grid-wide animation configuration.\n * Controls global animation behavior - individual plugins define their own animation styles.\n * Duration and easing values set corresponding CSS variables on the grid element.\n */\nexport interface AnimationConfig {\n /**\n * Global animation mode.\n * @default 'reduced-motion'\n */\n mode?: AnimationMode;\n\n /**\n * Default animation duration in milliseconds.\n * Sets `--tbw-animation-duration` CSS variable.\n * @default 200\n */\n duration?: number;\n\n /**\n * Default easing function.\n * Sets `--tbw-animation-easing` CSS variable.\n * @default 'ease-out'\n */\n easing?: string;\n}\n\n/** Default animation configuration */\nexport const DEFAULT_ANIMATION_CONFIG: Required<Omit<AnimationConfig, 'sort'>> = {\n mode: 'reduced-motion',\n duration: 200,\n easing: 'ease-out',\n};\n\n// #endregion\n\n// #region Grid Icons\n\n/** Icon value - can be a string (text/HTML) or HTMLElement */\nexport type IconValue = string | HTMLElement;\n\n/**\n * Grid-wide icon configuration.\n * All icons are optional - sensible defaults are used when not specified.\n */\nexport interface GridIcons {\n /** Expand icon for collapsed items (trees, groups, details). Default: '▶' */\n expand?: IconValue;\n /** Collapse icon for expanded items (trees, groups, details). Default: '▼' */\n collapse?: IconValue;\n /** Sort ascending indicator. Default: '▲' */\n sortAsc?: IconValue;\n /** Sort descending indicator. Default: '▼' */\n sortDesc?: IconValue;\n /** Sort neutral/unsorted indicator. Default: '⇅' */\n sortNone?: IconValue;\n /** Submenu arrow for context menus. Default: '▶' */\n submenuArrow?: IconValue;\n /** Drag handle icon for reordering. Default: '⋮⋮' */\n dragHandle?: IconValue;\n /** Tool panel toggle icon in toolbar. Default: '☰' */\n toolPanel?: IconValue;\n}\n\n/** Default icons used when not overridden */\nexport const DEFAULT_GRID_ICONS: Required<GridIcons> = {\n expand: '▶',\n collapse: '▼',\n sortAsc: '▲',\n sortDesc: '▼',\n sortNone: '⇅',\n submenuArrow: '▶',\n dragHandle: '⋮⋮',\n toolPanel: '☰',\n};\n// #endregion\n\n// #region Shell Configuration\n\n/**\n * Shell configuration for the grid's optional header bar and tool panels.\n */\nexport interface ShellConfig {\n /** Shell header bar configuration */\n header?: ShellHeaderConfig;\n /** Tool panel configuration */\n toolPanel?: ToolPanelConfig;\n}\n\n/**\n * Shell header bar configuration\n */\nexport interface ShellHeaderConfig {\n /** Grid title displayed on the left (optional) */\n title?: string;\n /** Custom toolbar buttons (rendered before tool panel toggles) */\n toolbarButtons?: ToolbarButtonConfig[];\n}\n\n/**\n * Tool panel configuration\n */\nexport interface ToolPanelConfig {\n /** Panel position: 'left' | 'right' (default: 'right') */\n position?: 'left' | 'right';\n /** Default panel width in pixels (default: 280) */\n width?: number;\n /** Panel ID to open by default on load */\n defaultOpen?: string;\n /** Whether to persist open/closed state (requires Column State Events) */\n persistState?: boolean;\n}\n\n/**\n * Toolbar button defined via config (programmatic approach).\n * 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 accordion header */\n title: string;\n /** Icon for accordion section header (optional, emoji or SVG) */\n icon?: string;\n /** Tooltip for accordion section header */\n tooltip?: string;\n /** Panel content factory - called when panel section opens */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when panel closes (for cleanup) */\n onClose?: () => void;\n /** Panel order priority (lower = first, default: 100) */\n order?: number;\n}\n\n/**\n * Header content definition for plugins contributing to shell header center section.\n */\nexport interface HeaderContentDefinition {\n /** Unique content ID */\n id: string;\n /** Content factory - called once when shell header renders */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when content is removed (for cleanup) */\n onDestroy?: () => void;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n}\n// #endregion\n\n// #region Column State (Persistence)\n\n/**\n * State for a single column. Captures user-driven changes at runtime.\n * Plugins can extend this interface via module augmentation to add their own state.\n *\n * @example\n * ```ts\n * // In filtering plugin\n * declare module '@toolbox-web/grid' {\n * interface ColumnState {\n * filter?: FilterValue;\n * }\n * }\n * ```\n */\nexport interface ColumnState {\n /** Column field identifier */\n field: string;\n /** Position index after reordering (0-based) */\n order: number;\n /** Width in pixels (undefined = use default) */\n width?: number;\n /** Visibility state */\n visible: boolean;\n /** Sort state (undefined = not sorted) */\n sort?: ColumnSortState;\n}\n\n/**\n * Sort state for a column\n */\nexport interface ColumnSortState {\n /** Sort direction */\n direction: 'asc' | 'desc';\n /** Priority for multi-sort (0 = primary, 1 = secondary, etc.) */\n priority: number;\n}\n\n/**\n * Complete grid column state for persistence.\n * Contains state for all columns, including plugin-contributed properties.\n */\nexport interface GridColumnState {\n columns: ColumnState[];\n}\n// #endregion\n\n// #region Public Event Detail Interfaces\nexport interface CellCommitDetail<TRow = unknown> {\n /** The mutated row after commit. */\n row: TRow;\n /** Field name whose value changed. */\n field: string;\n /** New value stored. */\n value: unknown;\n /** Index of the row in current data set. */\n rowIndex: number;\n /** All rows that have at least one committed change (snapshot list). */\n changedRows: TRow[];\n /** Indices parallel to changedRows. */\n changedRowIndices: number[];\n /** True if this row just entered the changed set. */\n firstTimeForRow: boolean;\n}\n\n/** Detail payload for a committed row edit (may or may not include changes). */\nexport interface RowCommitDetail<TRow = unknown> {\n /** Row index that lost edit focus. */\n rowIndex: number;\n /** Row object reference. */\n row: TRow;\n /** Whether any cell changes were actually committed in this row during the session. */\n changed: boolean;\n /** Current changed row collection. */\n changedRows: TRow[];\n /** Indices of changed rows. */\n changedRowIndices: number[];\n}\n\n/** Emitted when the changed rows tracking set is cleared programmatically. */\nexport interface ChangedRowsResetDetail<TRow = unknown> {\n /** New (empty) changed rows array after reset. */\n rows: TRow[];\n /** Parallel indices (likely empty). */\n indices: number[];\n}\n\n/** Detail for a sort change (direction 0 indicates cleared sort). */\nexport interface SortChangeDetail {\n /** Sorted field key. */\n field: string;\n /** Direction: 1 ascending, -1 descending, 0 cleared. */\n direction: 1 | -1 | 0;\n}\n\n/** Column resize event detail containing final pixel width. */\nexport interface ColumnResizeDetail {\n /** Resized column field key. */\n field: string;\n /** New width in pixels. */\n width: number;\n}\n\n/** Fired when keyboard navigation or programmatic focus changes active cell. */\nexport interface ActivateCellDetail {\n /** Zero-based row index now focused. */\n row: number;\n /** Zero-based column index now focused. */\n col: number;\n}\n\nexport interface ExternalMountViewDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: { row: TRow; value: unknown; field: string; column: unknown };\n}\n\nexport interface ExternalMountEditorDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: {\n row: TRow;\n value: unknown;\n field: string;\n column: unknown;\n commit: (v: unknown) => void;\n cancel: () => void;\n };\n}\n\nexport interface DataGridEventMap<TRow = unknown> {\n 'cell-commit': CellCommitDetail<TRow>;\n 'row-commit': RowCommitDetail<TRow>;\n 'changed-rows-reset': ChangedRowsResetDetail<TRow>;\n 'mount-external-view': ExternalMountViewDetail<TRow>;\n 'mount-external-editor': ExternalMountEditorDetail<TRow>;\n 'sort-change': SortChangeDetail;\n 'column-resize': ColumnResizeDetail;\n 'activate-cell': ActivateCellDetail;\n 'column-state-change': GridColumnState;\n}\n\nexport type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = DataGridEventMap<TRow>[K];\nexport type DataGridCustomEvent<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = CustomEvent<\n DataGridEventMap<TRow>[K]\n>;\n\n// Internal code now reuses the public ColumnEditorContext; provide alias for backward compatibility\nexport type EditorContext<T = unknown> = ColumnEditorContext<T, unknown>;\n\nexport interface EvalContext {\n value: unknown;\n row: Record<string, unknown> | null;\n}\n// #endregion\n","import type { ColumnConfigMap, InferredColumnResult, PrimitiveColumnType } from '../types';\n/**\n * Best-effort primitive type inference for a cell value used during automatic column generation.\n */\nfunction inferType(value: any): PrimitiveColumnType {\n if (value == null) return 'string';\n if (typeof value === 'number') return 'number';\n if (typeof value === 'boolean') return 'boolean';\n if (value instanceof Date) return 'date';\n if (typeof value === 'string' && /\\d{4}-\\d{2}-\\d{2}/.test(value) && !isNaN(Date.parse(value))) return 'date';\n return 'string';\n}\n/**\n * Derive column definitions from provided configuration or by inspecting the first row of data.\n * Returns both the resolved column array and a field->type map.\n */\nexport function inferColumns<TRow extends Record<string, unknown>>(\n rows: TRow[],\n provided?: ColumnConfigMap<TRow>\n): InferredColumnResult<TRow> {\n if (provided && provided.length) {\n const typeMap: Record<string, PrimitiveColumnType> = {};\n provided.forEach((col) => {\n if (col.type) typeMap[col.field] = col.type;\n });\n return { columns: provided, typeMap };\n }\n const sample = rows[0] || ({} as TRow);\n const columns: ColumnConfigMap<TRow> = Object.keys(sample).map((k) => {\n const v = (sample as any)[k];\n const type = inferType(v);\n return { field: k as keyof TRow & string, header: k.charAt(0).toUpperCase() + k.slice(1), type };\n });\n const typeMap: Record<string, PrimitiveColumnType> = {};\n columns.forEach((c) => {\n typeMap[c.field] = c.type || 'string';\n });\n return { columns, typeMap };\n}\nexport { inferType };\n","// Centralized template expression evaluation & sanitization utilities.\n// Responsible for safely interpolating {{ }} expressions while blocking\n// access to dangerous globals / reflective capabilities.\nimport type { EvalContext } from '../types';\n\nconst EXPR_RE = /{{\\s*([^}]+)\\s*}}/g;\nconst EMPTY_SENTINEL = '__DG_EMPTY__';\nconst SAFE_EXPR = /^[\\w$. '?+\\-*/%:()!<>=,&|]+$/;\nconst FORBIDDEN =\n /__(proto|defineGetter|defineSetter)|constructor|window|globalThis|global|process|Function|import|eval|Reflect|Proxy|Error|arguments|document|location|cookie|localStorage|sessionStorage|indexedDB|fetch|XMLHttpRequest|WebSocket|Worker|SharedWorker|ServiceWorker|opener|parent|top|frames|self|this\\b/;\n\n// #region HTML Sanitization\n\n/**\n * Tags that are considered dangerous and will be completely removed.\n * These can execute scripts, load external resources, or manipulate the page.\n */\nconst DANGEROUS_TAGS = new Set([\n 'script',\n 'iframe',\n 'object',\n 'embed',\n 'form',\n 'input',\n 'button',\n 'textarea',\n 'select',\n 'link',\n 'meta',\n 'base',\n 'style',\n 'template',\n 'slot',\n 'portal',\n 'frame',\n 'frameset',\n 'applet',\n 'noscript',\n 'noembed',\n 'plaintext',\n 'xmp',\n 'listing',\n]);\n\n/**\n * Attributes that are considered dangerous - event handlers and data loading.\n */\nconst DANGEROUS_ATTR_PATTERN = /^on\\w+$/i;\n\n/**\n * Attributes that can contain URLs which might be javascript: or data: URIs.\n */\nconst URL_ATTRS = new Set(['href', 'src', 'action', 'formaction', 'data', 'srcdoc', 'xlink:href', 'poster', 'srcset']);\n\n/**\n * Protocol patterns that are dangerous in URLs.\n */\nconst DANGEROUS_URL_PROTOCOL = /^\\s*(javascript|vbscript|data|blob):/i;\n\n/**\n * Sanitize an HTML string by removing dangerous tags and attributes.\n * This is a defense-in-depth measure for content rendered via innerHTML.\n *\n * @param html - Raw HTML string to sanitize\n * @returns Sanitized HTML string safe for innerHTML\n */\nexport function sanitizeHTML(html: string): string {\n if (!html || typeof html !== 'string') return '';\n\n // Fast path: if no HTML tags at all, return as-is (already safe)\n if (html.indexOf('<') === -1) return html;\n\n const template = document.createElement('template');\n template.innerHTML = html;\n\n sanitizeNode(template.content);\n\n return template.innerHTML;\n}\n\n/**\n * Recursively sanitize a DOM node tree.\n */\nfunction sanitizeNode(root: DocumentFragment | Element): void {\n const toRemove: Element[] = [];\n\n // Use querySelectorAll to find all elements, then filter\n const elements = root.querySelectorAll('*');\n\n for (const el of elements) {\n const tagName = el.tagName.toLowerCase();\n\n // Check if tag is dangerous\n if (DANGEROUS_TAGS.has(tagName)) {\n toRemove.push(el);\n continue;\n }\n\n // SVG elements need special handling - they can contain script-like behavior\n if (tagName === 'svg' || el.namespaceURI === 'http://www.w3.org/2000/svg') {\n // Remove entire SVG if it has any suspicious attributes\n const hasDangerousContent = Array.from(el.attributes).some(\n (attr) => DANGEROUS_ATTR_PATTERN.test(attr.name) || attr.name === 'href' || attr.name === 'xlink:href',\n );\n if (hasDangerousContent) {\n toRemove.push(el);\n continue;\n }\n }\n\n // Check and remove dangerous attributes\n const attrsToRemove: string[] = [];\n for (const attr of el.attributes) {\n const attrName = attr.name.toLowerCase();\n\n // Event handlers (onclick, onerror, onload, etc.)\n if (DANGEROUS_ATTR_PATTERN.test(attrName)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // URL attributes with dangerous protocols\n if (URL_ATTRS.has(attrName) && DANGEROUS_URL_PROTOCOL.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // style attribute can contain expressions (IE) or url() with javascript:\n if (attrName === 'style' && /expression\\s*\\(|javascript:|behavior\\s*:/i.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n }\n\n attrsToRemove.forEach((name) => el.removeAttribute(name));\n }\n\n // Remove dangerous elements (do this after iteration to avoid modifying during traversal)\n toRemove.forEach((el) => el.remove());\n}\n\n// #endregion\n\nexport function evalTemplateString(raw: string, ctx: EvalContext): string {\n if (!raw || raw.indexOf('{{') === -1) return raw; // fast path (no expressions)\n const parts: { expr: string; result: string }[] = [];\n const evaluated = raw.replace(EXPR_RE, (_m, expr) => {\n const res = evalSingle(expr, ctx);\n parts.push({ expr: expr.trim(), result: res });\n return res;\n });\n const finalStr = postProcess(evaluated);\n // If every part evaluated to EMPTY_SENTINEL we treat this as intentionally blank.\n // If any expression was blocked due to forbidden token (EMPTY_SENTINEL) we *still* only output ''\n // but do not escalate to BLOCKED_SENTINEL unless the original contained explicit forbidden tokens.\n const allEmpty = parts.length && parts.every((p) => p.result === '' || p.result === EMPTY_SENTINEL);\n const hadForbidden = /Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(raw);\n if (hadForbidden || allEmpty) return '';\n return finalStr;\n}\n\nfunction evalSingle(expr: string, ctx: EvalContext): string {\n expr = (expr || '').trim();\n if (!expr) return EMPTY_SENTINEL;\n if (/\\b(Reflect|Proxy|ownKeys)\\b/.test(expr)) return EMPTY_SENTINEL;\n if (expr === 'value') return ctx.value == null ? EMPTY_SENTINEL : String(ctx.value);\n if (expr.startsWith('row.') && !/[()?]/.test(expr) && !expr.includes(':')) {\n const key = expr.slice(4);\n const v = ctx.row ? ctx.row[key] : undefined;\n return v == null ? EMPTY_SENTINEL : String(v);\n }\n if (expr.length > 80) return EMPTY_SENTINEL;\n if (!SAFE_EXPR.test(expr) || FORBIDDEN.test(expr)) return EMPTY_SENTINEL;\n const dotChain = expr.match(/\\./g);\n if (dotChain && dotChain.length > 1) return EMPTY_SENTINEL;\n try {\n \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 != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value === '' ? null : Number(input.value)));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value === '' ? null : Number(input.value));\n if (e.key === 'Escape') ctx.cancel();\n });\n 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 != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value);\n if (e.key === 'Escape') ctx.cancel();\n });\n 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 // Let arrow left/right navigate within text inputs instead of moving cells\n if (isFormField(target) && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) return;\n // Let Enter/Escape be handled by the input's own handlers first\n if (isFormField(target) && (e.key === 'Enter' || e.key === 'Escape')) return;\n if (editing && (colType === 'select' || colType === 'typeahead') && (e.key === 'ArrowDown' || e.key === 'ArrowUp'))\n return;\n switch (e.key) {\n case 'Tab': {\n e.preventDefault();\n const forward = !e.shiftKey;\n if (forward) {\n if (grid._focusCol < maxCol) grid._focusCol += 1;\n else {\n if (typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n if (grid._focusRow < maxRow) {\n grid._focusRow += 1;\n grid._focusCol = 0;\n }\n }\n } else {\n if (grid._focusCol > 0) grid._focusCol -= 1;\n else if (grid._focusRow > 0) {\n if (typeof grid.commitActiveRowEdit === 'function' && grid._activeEditRows === grid._focusRow)\n grid.commitActiveRowEdit();\n grid._focusRow -= 1;\n grid._focusCol = maxCol;\n }\n }\n ensureCellVisible(grid);\n return;\n }\n case 'ArrowDown':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.min(maxRow, grid._focusRow + 1);\n e.preventDefault();\n break;\n case 'ArrowUp':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.max(0, grid._focusRow - 1);\n e.preventDefault();\n break;\n case 'ArrowRight':\n grid._focusCol = Math.min(maxCol, grid._focusCol + 1);\n e.preventDefault();\n break;\n case 'ArrowLeft':\n grid._focusCol = Math.max(0, grid._focusCol - 1);\n e.preventDefault();\n break;\n case 'Home':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+Home: navigate to first row, first cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = 0;\n grid._focusCol = 0;\n } else {\n // Home: navigate to first cell in current row\n grid._focusCol = 0;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollLeft: true });\n return;\n case 'End':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+End: navigate to last row, last cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = maxRow;\n grid._focusCol = maxCol;\n } else {\n // End: navigate to last cell in current row\n grid._focusCol = maxCol;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollRight: true });\n return;\n case 'PageDown':\n grid._focusRow = Math.min(maxRow, grid._focusRow + 20);\n e.preventDefault();\n break;\n case 'PageUp':\n grid._focusRow = Math.max(0, grid._focusRow - 20);\n e.preventDefault();\n break;\n case 'Enter':\n if (typeof grid.beginBulkEdit === 'function') 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 * Options for ensureCellVisible to control scroll behavior.\n */\ninterface EnsureCellVisibleOptions {\n /** Force scroll to the leftmost position (for Home key) */\n forceScrollLeft?: boolean;\n /** Force scroll to the rightmost position (for End key) */\n forceScrollRight?: boolean;\n}\n\n/**\n * Scroll the viewport (virtualized or static) so the focused cell's row is visible\n * and apply visual focus styling / tabindex management.\n */\nexport function ensureCellVisible(grid: InternalGrid, options?: EnsureCellVisibleOptions): void {\n if (grid._virtualization?.enabled) {\n const { rowHeight, container, viewportEl } = grid._virtualization;\n // container is the faux scrollbar element that handles actual scrolling\n // viewportEl is the visible area element that has the correct height\n const scrollEl = container as HTMLElement | undefined;\n const visibleHeight = viewportEl?.clientHeight ?? scrollEl?.clientHeight ?? 0;\n if (scrollEl && visibleHeight > 0) {\n const y = grid._focusRow * rowHeight;\n if (y < scrollEl.scrollTop) {\n scrollEl.scrollTop = y;\n } else if (y + rowHeight > scrollEl.scrollTop + visibleHeight) {\n scrollEl.scrollTop = y - visibleHeight + rowHeight;\n }\n }\n }\n // Skip refreshVirtualWindow when in edit mode to avoid wiping editors\n const isEditing = grid._activeEditRows !== undefined && grid._activeEditRows !== -1;\n if (!isEditing) {\n grid.refreshVirtualWindow(false);\n }\n 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\n // Horizontal scroll: ensure focused cell is visible in the horizontal scroll area\n // The .tbw-scroll-area element handles horizontal scrolling\n const scrollArea = grid.shadowRoot?.querySelector('.tbw-scroll-area') as HTMLElement | null;\n if (scrollArea && cell) {\n // Handle forced scroll for Home/End keys - always scroll to edge\n if (options?.forceScrollLeft) {\n scrollArea.scrollLeft = 0;\n } else if (options?.forceScrollRight) {\n scrollArea.scrollLeft = scrollArea.scrollWidth - scrollArea.clientWidth;\n } else {\n // Get scroll boundary offsets from plugins (e.g., pinned columns)\n // This allows plugins to report how much of the scroll area they obscure\n // and whether the focused cell should skip scrolling (e.g., pinned cells are always visible)\n const offsets = grid._getHorizontalScrollOffsets?.(rowEl ?? undefined, cell) ?? { left: 0, right: 0 };\n\n if (!offsets.skipScroll) {\n // Get cell position relative to the scroll area\n const cellRect = cell.getBoundingClientRect();\n const scrollAreaRect = scrollArea.getBoundingClientRect();\n // Calculate the cell's position relative to scroll area's visible region\n const cellLeft = cellRect.left - scrollAreaRect.left + scrollArea.scrollLeft;\n const cellRight = cellLeft + cellRect.width;\n // Adjust visible boundaries to account for plugin-reported offsets\n const visibleLeft = scrollArea.scrollLeft + offsets.left;\n const visibleRight = scrollArea.scrollLeft + scrollArea.clientWidth - offsets.right;\n // Scroll horizontally if needed\n if (cellLeft < visibleLeft) {\n scrollArea.scrollLeft = cellLeft - offsets.left;\n } else if (cellRight > visibleRight) {\n scrollArea.scrollLeft = cellRight - scrollArea.clientWidth + offsets.right;\n }\n }\n }\n }\n\n if (grid._activeEditRows !== undefined && grid._activeEditRows !== -1 && cell.classList.contains('editing')) {\n const focusTarget = cell.querySelector(\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 const hasEditingCell = rowEl.querySelector('.cell.editing');\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n // (This happens when virtualization recycles the DOM element for a different row)\n if (hasEditingCell && !isActivelyEditedRow) {\n // Force full rebuild to clear stale editors\n if ((rowEl as any).__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n (rowEl as any).__isCustomRow = false;\n }\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else if (hasEditingCell && isActivelyEditedRow) {\n // Row is in editing mode AND this is the correct row - preserve editors\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__rowDataRef = rowData;\n } else {\n if ((rowEl as any).__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n (rowEl as any).__isCustomRow = false;\n }\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement);\n }\n }\n }\n }\n } else if (dataRefChanged) {\n // Same structure, different row data - fast update\n const hasEditingCell = rowEl.querySelector('.cell.editing');\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditingCell && !isActivelyEditedRow) {\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__rowDataRef = rowData;\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow && !hasEditingCell) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement);\n }\n }\n }\n }\n } else {\n // Same row data reference - just patch if any values changed\n const hasEditingCell = rowEl.querySelector('.cell.editing');\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditingCell && !isActivelyEditedRow) {\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow && !hasEditingCell) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement);\n }\n }\n }\n }\n }\n\n // Changed class toggle\n const isChanged = grid._changedRowIndices.has(rowIndex);\n const hasChangedClass = rowEl.classList.contains('changed');\n if (isChanged !== hasChangedClass) {\n rowEl.classList.toggle('changed', isChanged);\n }\n\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n }\n}\n\n/**\n * Fast patch path for an already-rendered row: updates plain text cells whose data changed\n * while skipping cells with external views, templates, or active editors.\n *\n * Optimized for scroll performance - avoids querySelectorAll in favor of children access.\n */\nfunction fastPatchRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n const children = rowEl.children;\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const childLen = children.length;\n const minLen = colsLen < childLen ? colsLen : childLen;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n\n // Ultra-fast path: if no special columns (templates, formatters, etc.), use direct assignment\n // Check is cached on grid to avoid repeated iteration\n let hasSpecialCols = (grid as any).__hasSpecialColumns;\n if (hasSpecialCols === undefined) {\n hasSpecialCols = false;\n for (let i = 0; i < colsLen; i++) {\n const col = columns[i] as any;\n if (\n col.__viewTemplate ||\n col.__compiledView ||\n col.viewRenderer ||\n col.externalView ||\n col.format ||\n col.type === 'date' ||\n col.type === 'boolean'\n ) {\n hasSpecialCols = true;\n break;\n }\n }\n (grid as any).__hasSpecialColumns = hasSpecialCols;\n }\n\n const rowIndexStr = String(rowIndex);\n\n // Ultra-fast path for plain text grids - just set textContent directly\n if (!hasSpecialCols) {\n for (let i = 0; i < minLen; i++) {\n const cell = children[i] as HTMLElement;\n const value = rowData[columns[i].field];\n cell.textContent = value == null ? '' : String(value);\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n // aria-selected only valid for gridcell, not checkbox (but ultra-fast path has no special cols)\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n }\n return;\n }\n\n // Check if any external view placeholder is missing - if so, do full rebuild\n for (let i = 0; i < minLen; i++) {\n const col = columns[i] as any;\n if (col.externalView) {\n const cell = children[i] as HTMLElement;\n if (!cell.querySelector('[data-external-view]')) {\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n return;\n }\n }\n }\n\n // Standard path for grids with special columns\n for (let i = 0; i < minLen; i++) {\n const col = columns[i] as any;\n const cell = children[i] as HTMLElement;\n\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n\n // Skip cells in edit mode\n if (cell.classList.contains('editing')) continue;\n\n // Handle viewRenderer - must re-invoke to get updated content\n if (col.viewRenderer) {\n const value = rowData[col.field];\n const produced = col.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 // Skip templated / external cells (these need full rebuild to remount)\n if (col.__viewTemplate || col.__compiledView || col.externalView) {\n continue;\n }\n\n // Compute and set display value\n const value = rowData[col.field];\n let displayStr: string;\n\n if (col.format) {\n try {\n const formatted = col.format(value, rowData);\n displayStr = formatted == null ? '' : String(formatted);\n } catch {\n displayStr = value == null ? '' : String(value);\n }\n } else if (col.type === 'date') {\n 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 cell.textContent = displayStr;\n } else if (col.type === 'boolean') {\n const isTrue = !!value;\n // Boolean cells have inner span with checkbox role for ARIA compliance\n cell.innerHTML = `<span role=\"checkbox\" aria-checked=\"${isTrue}\" aria-label=\"${isTrue}\">${isTrue ? '&#x1F5F9;' : '&#9744;'}</span>`;\n } else {\n displayStr = value == null ? '' : String(value);\n cell.textContent = displayStr;\n }\n }\n}\n\n/**\n * Full reconstruction of a row's set of cells including templated, external view, and formatted content.\n * Attaches event handlers for editing and accessibility per cell.\n */\nexport function renderInlineRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n rowEl.innerHTML = '';\n\n // Pre-cache values used in the loop\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n const editMode = (grid as any).effectiveConfig?.editOn || grid.editOn;\n const gridEl = grid as unknown as HTMLElement;\n\n // Use DocumentFragment for batch DOM insertion\n const fragment = document.createDocumentFragment();\n\n for (let colIndex = 0; colIndex < colsLen; colIndex++) {\n const col: ColumnConfig<any> = columns[colIndex];\n const cell = document.createElement('div');\n cell.className = 'cell';\n addPart(cell, 'cell');\n\n // All cells get role=gridcell (required by role=row)\n cell.setAttribute('role', 'gridcell');\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(colIndex + 1));\n cell.setAttribute('data-col', String(colIndex));\n cell.setAttribute('data-row', String(rowIndex));\n cell.setAttribute('data-field', col.field); // Field name for column identification\n const isCheckbox = col.type === 'boolean';\n if (col.type) cell.setAttribute('data-type', col.type as any);\n\n let value = (rowData as any)[col.field];\n const format = (col as any).format;\n if (format) {\n try {\n value = format(value, rowData);\n } catch {\n /* empty */\n }\n }\n\n const compiled = (col as any).__compiledView as ((ctx: any) => string) | undefined;\n const tplHolder = (col as any).__viewTemplate as HTMLElement | undefined;\n 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 // Wrap checkbox in span to satisfy ARIA: gridcell can contain checkbox\n cell.innerHTML = `<span role=\"checkbox\" aria-checked=\"${isTrue}\" aria-label=\"${isTrue}\">${isTrue ? '&#x1F5F9;' : '&#9744;'}</span>`;\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 // Skip if cell is already in editing mode - avoid refreshVirtualWindow wiping editors\n if (cell.classList.contains('editing')) return;\n // Read row/col index from data attributes to handle virtualization row reuse\n const currentRowIndex = Number(cell.getAttribute('data-row'));\n const currentColIndex = Number(cell.getAttribute('data-col'));\n if (isNaN(currentRowIndex) || isNaN(currentColIndex)) return;\n grid._focusRow = currentRowIndex;\n grid._focusCol = currentColIndex;\n ensureCellVisible(grid);\n });\n if (editMode === 'click') {\n cell.addEventListener('click', (e) => {\n if (cell.classList.contains('editing')) return;\n e.stopPropagation();\n // Read row/col index from data attributes to handle virtualization row reuse\n const currentRowIndex = Number(cell.getAttribute('data-row'));\n const currentColIndex = Number(cell.getAttribute('data-col'));\n if (isNaN(currentRowIndex) || isNaN(currentColIndex)) return;\n const currentRowData = grid._rows[currentRowIndex];\n const currentCol = grid._visibleColumns[currentColIndex];\n if (!currentRowData || !currentCol) return;\n grid._focusRow = currentRowIndex;\n grid._focusCol = currentColIndex;\n inlineEnterEdit(grid, currentRowData, currentRowIndex, currentCol, cell);\n });\n } else {\n cell.addEventListener('dblclick', (e) => {\n e.stopPropagation();\n // Read row index from data attribute to handle virtualization row reuse\n const currentRowIndex = Number(cell.getAttribute('data-row'));\n if (isNaN(currentRowIndex)) return;\n const currentRowData = grid._rows[currentRowIndex];\n if (!currentRowData) return;\n startRowEdit(grid, currentRowIndex, currentRowData);\n const rowElCurrent = grid.findRenderedRowElement?.(currentRowIndex);\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, currentRowData, currentRowIndex, col2, children[i] as HTMLElement);\n }\n }\n });\n }\n cell.addEventListener('keydown', (e) => {\n // Read row/col index from data attributes to handle virtualization row reuse\n const currentRowIndex = Number(cell.getAttribute('data-row'));\n const currentColIndex = Number(cell.getAttribute('data-col'));\n if (isNaN(currentRowIndex) || isNaN(currentColIndex)) return;\n const currentRowData = grid._rows[currentRowIndex];\n const currentCol = grid._visibleColumns[currentColIndex];\n if (!currentRowData || !currentCol) return;\n if (\n (currentCol.type === 'select' || currentCol.type === 'typeahead') &&\n !cell.classList.contains('editing') &&\n e.key === 'Enter'\n ) {\n e.preventDefault();\n if (grid._activeEditRows !== currentRowIndex) startRowEdit(grid, currentRowIndex, currentRowData);\n inlineEnterEdit(grid, currentRowData, currentRowIndex, currentCol, 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 (currentCol.type === 'boolean' && e.key === ' ' && !cell.classList.contains('editing')) {\n e.preventDefault();\n if (grid._activeEditRows !== currentRowIndex) startRowEdit(grid, currentRowIndex, currentRowData);\n const newVal = !currentRowData[currentCol.field];\n commitCellValue(grid, currentRowIndex, currentCol, newVal, currentRowData);\n cell.innerHTML = `<span role=\"checkbox\" aria-checked=\"${newVal}\" aria-label=\"${newVal}\">${newVal ? '&#x1F5F9;' : '&#9744;'}</span>`;\n return;\n }\n if (e.key === 'Enter' && !cell.classList.contains('editing')) {\n e.preventDefault();\n e.stopPropagation(); // Prevent grid-level handler from also processing Enter\n grid._focusRow = currentRowIndex;\n grid._focusCol = currentColIndex;\n if (typeof grid.beginBulkEdit === 'function') grid.beginBulkEdit(currentRowIndex);\n else inlineEnterEdit(grid, currentRowData, currentRowIndex, currentCol, cell);\n return;\n }\n if (e.key === 'F2' && !cell.classList.contains('editing')) {\n e.preventDefault();\n inlineEnterEdit(grid, currentRowData, currentRowIndex, currentCol, cell);\n return;\n }\n });\n } else if (col.type === 'boolean') {\n // Non-editable boolean cells should NOT toggle on space key\n // They are read-only, only set tabindex for focus navigation\n if (!cell.hasAttribute('tabindex')) cell.tabIndex = 0;\n }\n\n // Initialize focus state (must match fastPatchRow for consistent behavior)\n if (focusRow === rowIndex && focusCol === colIndex) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n } else {\n cell.setAttribute('aria-selected', 'false');\n }\n\n fragment.appendChild(cell);\n }\n\n // Single DOM operation to append all cells\n rowEl.appendChild(fragment);\n}\n\n/**\n * Handle click / double click interaction to focus cells and optionally start row editing\n * according to the grid's configured edit activation mode.\n */\nexport function handleRowClick(grid: InternalGrid, e: MouseEvent, rowEl: HTMLElement, isDbl: boolean): void {\n if ((e.target as HTMLElement)?.closest('.resize-handle')) return;\n const firstCell = rowEl.querySelector('.cell[data-row]') as HTMLElement | null;\n 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\n // Dispatch row click to plugin system first (e.g., for master-detail expansion)\n if (grid._dispatchRowClick?.(e, rowIndex, rowData, rowEl)) {\n return;\n }\n\n const cellEl = (e.target as HTMLElement)?.closest('.cell[data-col]') as HTMLElement | null;\n if (cellEl) {\n // Skip focus/ensureCellVisible if cell is already editing - avoid wiping editors\n if (cellEl.classList.contains('editing')) return;\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 { invalidateCellCache, renderInlineRow } from './rows';\n\n/**\n * Returns true if the given property key is safe to use on a plain object without risking\n * prototype pollution via special names like \"__proto__\", \"constructor\", or \"prototype\".\n */\nfunction isSafePropertyKey(key: any): boolean {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') return false;\n return true;\n}\n\n/**\n * Snapshot original row data and mark the row as actively being edited.\n */\nexport function startRowEdit(grid: InternalGrid, rowIndex: number, rowData: any): void {\n if (grid._activeEditRows !== rowIndex) {\n grid._rowEditSnapshots.set(rowIndex, { ...rowData });\n grid._activeEditRows = rowIndex;\n }\n}\n\n/**\n * Finish editing for a row. If `revert` is true restore original snapshot and clear change marks.\n * Otherwise emit a row-commit event describing change status.\n */\nexport function exitRowEdit(grid: InternalGrid, rowIndex: number, revert: boolean): void {\n if (grid._activeEditRows !== rowIndex) return;\n const snapshot = grid._rowEditSnapshots.get(rowIndex);\n const current = grid._rows[rowIndex];\n\n // Before re-rendering, collect and commit values from any active editors\n // This ensures values are persisted even if blur hasn't fired yet\n const rowEl = grid.findRenderedRowElement?.(rowIndex);\n if (!revert && rowEl && current) {\n const editingCells = rowEl.querySelectorAll('.cell.editing');\n editingCells.forEach((cell) => {\n const colIndex = Number((cell as HTMLElement).getAttribute('data-col'));\n if (isNaN(colIndex)) return;\n const col = grid._visibleColumns[colIndex];\n if (!col) return;\n const input = cell.querySelector('input,textarea,select') as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement\n | null;\n if (input) {\n let val: unknown;\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n val = input.checked;\n } else {\n val = input.value;\n // Convert to number for number columns\n if (col.type === 'number' && val !== '') {\n val = Number(val);\n }\n }\n // Only commit if value actually changed\n if (current[col.field] !== val) {\n commitCellValue(grid, rowIndex, col, val, current);\n }\n }\n });\n }\n\n if (revert && snapshot && current) {\n Object.keys(snapshot).forEach((k) => (current[k] = snapshot[k]));\n grid._changedRowIndices.delete(rowIndex);\n // Invalidate cell cache so reverted values display correctly\n invalidateCellCache(grid);\n } else if (!revert) {\n const changed = grid._changedRowIndices.has(rowIndex);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('row-commit', {\n detail: {\n rowIndex,\n row: current,\n changed,\n changedRows: grid.changedRows,\n changedRowIndices: grid.changedRowIndices,\n },\n }),\n );\n }\n grid._rowEditSnapshots.delete(rowIndex);\n grid._activeEditRows = -1;\n if (rowEl) {\n renderInlineRow(grid, rowEl, grid._rows[rowIndex], rowIndex);\n if (grid._changedRowIndices.has(rowIndex)) rowEl.classList.add('changed');\n else rowEl.classList.remove('changed');\n }\n // Restore focus to the cell after exiting edit mode (for both commit and revert)\n queueMicrotask(() => {\n try {\n const rowIdx = grid._focusRow;\n const colIdx = grid._focusCol;\n const rowEl2 = grid.findRenderedRowElement?.(rowIdx);\n if (rowEl2) {\n // Clear all cell-focus markers\n Array.from(grid._bodyEl.querySelectorAll('.cell-focus')).forEach((el: any) =>\n el.classList.remove('cell-focus'),\n );\n // Find and focus the cell\n const cell = rowEl2.querySelector(`.cell[data-row=\"${rowIdx}\"][data-col=\"${colIdx}\"]`) as HTMLElement | null;\n if (cell) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '-1');\n cell.focus({ preventScroll: true });\n }\n }\n } catch {\n /* empty */\n }\n });\n}\n\n/**\n * Commit a single cell value change, updating the row object, marking the row as changed (first-time flag),\n * and emitting a `cell-commit` event with row + field metadata.\n */\nexport function commitCellValue(\n grid: InternalGrid,\n rowIndex: number,\n column: ColumnConfig<any>,\n newValue: any,\n rowData: any,\n): void {\n const field = column.field;\n if (!isSafePropertyKey(field)) return;\n const oldValue = rowData[field];\n if (oldValue === newValue) return;\n rowData[field] = newValue;\n const firstTime = !grid._changedRowIndices.has(rowIndex);\n grid._changedRowIndices.add(rowIndex);\n const rowEl = grid.findRenderedRowElement?.(rowIndex);\n if (rowEl) rowEl.classList.add('changed');\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('cell-commit', {\n detail: {\n row: rowData,\n field,\n value: newValue,\n rowIndex,\n changedRows: grid.changedRows,\n changedRowIndices: grid.changedRowIndices,\n firstTimeForRow: firstTime,\n },\n }),\n );\n}\n\n/**\n * Replace a cell's content with an editor resolved from column configuration (custom editor, template, external\n * mount spec or default editor by type). Manages commit / cancel lifecycle and value restoration.\n */\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 = isSafePropertyKey(column.field) ? rowData[column.field] : undefined;\n cell.classList.add('editing');\n let editFinalized = false; // Flag to prevent blur from committing after explicit Enter/Escape\n const commit = (newValue: any) => {\n // Skip if edit was already finalized by Enter/Escape, or if we've exited edit mode\n // (handles bulk edit case where one cell's exit removes all editors)\n if (editFinalized || grid._activeEditRows === -1) return;\n commitCellValue(grid, rowIndex, column, newValue, rowData);\n };\n const cancel = () => {\n editFinalized = true; // Mark as finalized to prevent blur from re-committing\n rowData[column.field] = isSafePropertyKey(column.field) ? originalValue : undefined;\n const inputLike = cell.querySelector('input,textarea,select') as any;\n if (inputLike) {\n const hasHTMLInput = typeof HTMLInputElement !== 'undefined';\n if (hasHTMLInput && inputLike instanceof HTMLInputElement && inputLike.type === 'checkbox')\n inputLike.checked = !!originalValue;\n else if ('value' in inputLike) inputLike.value = originalValue ?? '';\n }\n };\n const editorHost = document.createElement('div');\n editorHost.style.display = 'contents';\n cell.innerHTML = '';\n cell.appendChild(editorHost);\n\n // Common keydown handler for all editor types to handle Enter/Escape with proper exit\n // This catches events that bubble up from child elements (default editors, custom editors)\n editorHost.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true; // Prevent blur from committing again\n // Value should already be committed by the editor's own handler\n // Just need to exit edit mode\n exitRowEdit(grid, rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel(); // cancel() sets editFinalized = true\n exitRowEdit(grid, rowIndex, true);\n }\n });\n\n const tplHolder = (column as any).__editorTemplate as HTMLElement | undefined;\n const editorSpec = (column as any).editor || (tplHolder ? 'template' : defaultEditorFor(column));\n const value = originalValue;\n if (editorSpec === 'template' && tplHolder) {\n const clone = tplHolder.cloneNode(true) as HTMLElement;\n const compiledEditor = (column as any).__compiledEditor as ((ctx: any) => string) | undefined;\n if (compiledEditor)\n clone.innerHTML = compiledEditor({ row: rowData, value: originalValue, field: column.field, column });\n else\n clone.querySelectorAll<HTMLElement>('*').forEach((node) => {\n if (node.childNodes.length === 1 && node.firstChild?.nodeType === Node.TEXT_NODE) {\n node.textContent =\n node.textContent\n ?.replace(/{{\\s*value\\s*}}/g, originalValue == null ? '' : String(originalValue))\n .replace(/{{\\s*row\\.([a-zA-Z0-9_]+)\\s*}}/g, (_m, g) => {\n const v = (rowData as any)[g];\n return v == null ? '' : String(v);\n }) || '';\n }\n });\n const input = clone.querySelector('input,textarea,select') as HTMLInputElement | HTMLSelectElement | null;\n if (input) {\n const hasHTMLInput = typeof HTMLInputElement !== 'undefined';\n if (hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox')\n input.checked = !!originalValue;\n else if ('value' in input) (input as any).value = originalValue ?? '';\n input.addEventListener('blur', () => {\n // commit() will check editFinalized flag and skip if already handled\n const val =\n hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox'\n ? input.checked\n : (input as any).value;\n commit(val);\n });\n input.addEventListener('keydown', (e: any) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true; // Prevent blur from committing again\n const val =\n hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox'\n ? input.checked\n : (input as any).value;\n commit(val);\n exitRowEdit(grid, rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel(); // cancel() sets editFinalized = true\n exitRowEdit(grid, rowIndex, true);\n }\n });\n if (hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox') {\n input.addEventListener('change', () => {\n const val = input.checked;\n commit(val);\n });\n }\n 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","/**\n * Sorting Module\n *\n * Handles column sorting state transitions and row ordering.\n */\n\nimport type { ColumnConfig, InternalGrid, SortHandler, SortState } from '../types';\nimport { renderHeader } from './header';\n\n/**\n * Default comparator for sorting values.\n * Handles nulls (pushed to end), numbers, and string fallback.\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n return a > b ? 1 : a < b ? -1 : 0;\n}\n\n/**\n * Built-in sort implementation using column comparator or default.\n * This is the default sortHandler when none is configured.\n */\nexport function builtInSort<T>(rows: T[], sortState: SortState, columns: ColumnConfig<T>[]): T[] {\n const col = columns.find((c) => c.field === sortState.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n const { field, direction } = sortState;\n\n return [...rows].sort((rA: any, rB: any) => {\n return comparator(rA[field], rB[field], rA, rB) * direction;\n });\n}\n\n/**\n * Apply sort result to grid and update UI.\n * Called after sync or async sort completes.\n */\nfunction finalizeSortResult(grid: InternalGrid, sortedRows: unknown[], col: ColumnConfig<any>, dir: 1 | -1): void {\n grid._rows = sortedRows as any[];\n // Bump epoch so renderVisibleRows triggers full inline rebuild\n grid.__rowRenderEpoch++;\n // Invalidate pooled rows to guarantee rebuild\n grid._rowPool.forEach((r) => ((r as any).__epoch = -1));\n renderHeader(grid);\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: dir } }),\n );\n // Trigger state change after sort applied\n grid.requestStateChange?.();\n}\n\n/**\n * Cycle sort state for a column: none -> ascending -> descending -> none.\n * Restores original row order when clearing sort.\n */\nexport function toggleSort(grid: InternalGrid, col: ColumnConfig<any>): void {\n if (!grid._sortState || grid._sortState.field !== col.field) {\n if (!grid._sortState) grid.__originalOrder = grid._rows.slice();\n applySort(grid, col, 1);\n } else if (grid._sortState.direction === 1) {\n applySort(grid, col, -1);\n } else {\n grid._sortState = null;\n // Force full row rebuild after clearing sort so templated cells reflect original order\n grid.__rowRenderEpoch++;\n // Invalidate existing pooled row epochs so virtualization triggers a full inline rebuild\n grid._rowPool.forEach((r) => ((r as any).__epoch = -1));\n grid._rows = grid.__originalOrder.slice();\n renderHeader(grid);\n // After re-render ensure cleared column shows aria-sort=\"none\" baseline.\n const headers = grid._headerRowEl?.querySelectorAll('[role=\"columnheader\"].sortable');\n headers?.forEach((h: any) => {\n if (!h.getAttribute('aria-sort')) h.setAttribute('aria-sort', 'none');\n else if (h.getAttribute('aria-sort') === 'ascending' || h.getAttribute('aria-sort') === 'descending') {\n // The active column was re-rendered already, but normalize any missing ones.\n if (!grid._sortState) h.setAttribute('aria-sort', 'none');\n }\n });\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: 0 } }),\n );\n // Trigger state change after sort is cleared\n grid.requestStateChange?.();\n }\n}\n\n/**\n * Apply a concrete sort direction to rows.\n *\n * Uses custom sortHandler from gridConfig if provided, otherwise uses built-in sorting.\n * Supports both sync and async handlers (for server-side sorting).\n */\nexport function applySort(grid: InternalGrid, col: ColumnConfig<any>, dir: 1 | -1): void {\n grid._sortState = { field: col.field, direction: dir };\n\n const sortState: SortState = { field: col.field, direction: dir };\n const columns = grid._columns as ColumnConfig<any>[];\n\n // Get custom handler from effectiveConfig, or use built-in\n const handler: SortHandler<any> = (grid as any).effectiveConfig?.sortHandler ?? builtInSort;\n\n const result = handler(grid._rows, sortState, columns);\n\n // Handle async (Promise) or sync result\n if (result && typeof (result as Promise<any>).then === 'function') {\n // Async handler - wait for result\n (result as Promise<any[]>).then((sortedRows) => {\n finalizeSortResult(grid, sortedRows, col, dir);\n });\n } else {\n // Sync handler - apply immediately\n finalizeSortResult(grid, result as any[], col, dir);\n }\n}\n","/**\n * Header Rendering Module\n *\n * Handles rendering of the grid header row with sorting and resize affordances.\n */\n\nimport type { ColumnConfig, IconValue, InternalGrid } from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\nimport { addPart } from './columns';\nimport { toggleSort } from './sorting';\n\n/**\n * Set an icon value on an element. Handles both string and HTMLElement icons.\n */\nfunction setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.textContent = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n}\n\n/**\n * Rebuild the header row DOM based on current column configuration, attaching\n * sorting and resize affordances where enabled.\n */\nexport function renderHeader(grid: InternalGrid): void {\n grid._headerRowEl = (grid.findHeaderRow! as any)();\n const headerRow = grid._headerRowEl as HTMLElement;\n headerRow.innerHTML = '';\n\n grid._visibleColumns.forEach((col: ColumnConfig<any>, i: number) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n addPart(cell, 'header-cell');\n cell.setAttribute('role', 'columnheader');\n\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(i + 1));\n cell.setAttribute('data-field', col.field);\n cell.setAttribute('data-col', String(i)); // Add data-col for consistency with body cells\n\n // Column grouping styling is handled by the grouping-columns plugin via afterRender\n const maybeTpl = (col as any).__headerTemplate as HTMLElement | undefined;\n if (maybeTpl) Array.from(maybeTpl.childNodes).forEach((n) => cell.appendChild(n.cloneNode(true)));\n else {\n const label = (col as any).header || col.field;\n const span = document.createElement('span');\n span.textContent = label;\n cell.appendChild(span);\n }\n if (col.sortable) {\n cell.classList.add('sortable');\n cell.tabIndex = 0;\n const icon = document.createElement('span');\n addPart(icon as any, 'sort-indicator');\n const active = grid._sortState?.field === col.field ? grid._sortState.direction : 0;\n // Use grid-level icons (fall back to defaults)\n const icons = { ...DEFAULT_GRID_ICONS, ...grid.icons };\n const iconValue = active === 1 ? icons.sortAsc : active === -1 ? icons.sortDesc : icons.sortNone;\n setIcon(icon, iconValue);\n cell.appendChild(icon);\n // Always set a baseline aria-sort for sortable headers for assistive tech clarity.\n cell.setAttribute('aria-sort', active === 0 ? 'none' : active === 1 ? 'ascending' : 'descending');\n cell.addEventListener('click', (e) => {\n // Ignore clicks that are the result of a resize drag ending\n if (grid._resizeController?.isResizing) return;\n // Let plugins handle the click first (e.g., multi-sort)\n if (grid._dispatchHeaderClick?.(e, i, cell)) return;\n toggleSort(grid, col);\n });\n cell.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n // Let plugins handle the keydown first\n if (grid._dispatchHeaderClick?.(e as unknown as MouseEvent, i, cell)) return;\n toggleSort(grid, col);\n }\n });\n }\n if (col.resizable) {\n // Set position: relative for the resize handle positioning context\n // Note: If a plugin applies position: sticky (e.g., PinnedColumnsPlugin), it will override this\n cell.style.position = 'relative';\n const handle = document.createElement('div');\n handle.className = 'resize-handle';\n handle.setAttribute('aria-hidden', 'true');\n handle.addEventListener('mousedown', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.start(e, i, cell);\n });\n // Double-click to reset column width to default\n handle.addEventListener('dblclick', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.resetColumn(i);\n });\n cell.appendChild(handle);\n }\n headerRow.appendChild(cell);\n });\n\n // Ensure every sortable header has a baseline aria-sort if not already set during construction.\n headerRow.querySelectorAll('.cell.sortable').forEach((el) => {\n if (!el.getAttribute('aria-sort')) el.setAttribute('aria-sort', 'none');\n });\n\n // Set ARIA role only if header has children (role=\"row\" requires columnheader children)\n // When grid is cleared with 0 columns, the header row should not have role=\"row\"\n if (headerRow.children.length > 0) {\n headerRow.setAttribute('role', 'row');\n headerRow.setAttribute('aria-rowindex', '1');\n } else {\n headerRow.removeAttribute('role');\n headerRow.removeAttribute('aria-rowindex');\n }\n}\n","import type { InternalGrid, ResizeController } from '../types';\n\nexport function createResizeController(grid: InternalGrid): ResizeController {\n let resizeState: { startX: number; colIndex: number; startWidth: number } | null = null;\n let pendingRaf: number | null = null;\n let prevCursor: string | null = null;\n let prevUserSelect: string | null = null;\n const onMove = (e: MouseEvent) => {\n if (!resizeState) return;\n const delta = e.clientX - resizeState.startX;\n const width = Math.max(40, resizeState.startWidth + delta);\n const col = grid._visibleColumns[resizeState.colIndex];\n col.width = width;\n col.__userResized = true;\n col.__renderedWidth = width;\n if (pendingRaf == null) {\n pendingRaf = requestAnimationFrame(() => {\n pendingRaf = null;\n grid.updateTemplate?.();\n });\n }\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize', { detail: { field: col.field, width } }),\n );\n };\n let justFinishedResize = false;\n const onUp = () => {\n const hadResize = resizeState !== null;\n // Set flag to suppress click events that fire immediately after mouseup\n if (hadResize) {\n justFinishedResize = true;\n requestAnimationFrame(() => {\n justFinishedResize = false;\n });\n }\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n if (prevCursor !== null) {\n document.documentElement.style.cursor = prevCursor;\n prevCursor = null;\n }\n if (prevUserSelect !== null) {\n document.body.style.userSelect = prevUserSelect;\n prevUserSelect = null;\n }\n resizeState = null;\n // Trigger state change after resize completes\n if (hadResize && grid.requestStateChange) {\n grid.requestStateChange();\n }\n };\n return {\n get isResizing() {\n return resizeState !== null || justFinishedResize;\n },\n start(e, colIndex, cell) {\n e.preventDefault();\n const rect = cell.getBoundingClientRect();\n resizeState = { startX: e.clientX, colIndex, startWidth: rect.width };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n if (prevCursor === null) prevCursor = document.documentElement.style.cursor;\n document.documentElement.style.cursor = 'e-resize';\n if (prevUserSelect === null) prevUserSelect = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n },\n resetColumn(colIndex) {\n const col = grid._visibleColumns[colIndex];\n if (!col) return;\n\n // Reset to original configured width (or undefined for auto-sizing)\n col.__userResized = false;\n col.__renderedWidth = undefined;\n col.width = col.__originalWidth;\n\n grid.updateTemplate?.();\n grid.requestStateChange?.();\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize-reset', { detail: { field: col.field, width: col.width } }),\n );\n },\n dispose() {\n onUp();\n },\n };\n}\n","/**\n * Shell infrastructure for grid header bar and tool panels.\n *\n * The shell is an optional wrapper layer that provides:\n * - Header bar with title, plugin content, and toolbar buttons\n * - Tool panels that plugins can register content into\n * - Light DOM parsing for framework-friendly configuration\n */\n\nimport type {\n HeaderContentDefinition,\n IconValue,\n ShellConfig,\n ToolbarButtonConfig,\n ToolbarButtonInfo,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\n\n/**\n * Convert an IconValue to a string for rendering in HTML.\n */\nfunction iconToString(icon: IconValue | undefined): string {\n if (!icon) return '';\n if (typeof icon === 'string') return icon;\n // For HTMLElement, get the outerHTML\n return icon.outerHTML;\n}\n\n/**\n * State for managing shell UI.\n */\nexport interface ShellState {\n /** Registered tool panels (from plugins + consumer API) */\n toolPanels: Map<string, ToolPanelDefinition>;\n /** Registered header content (from plugins + consumer API) */\n headerContents: Map<string, HeaderContentDefinition>;\n /** Custom toolbar buttons registered via API */\n toolbarButtons: Map<string, ToolbarButtonConfig>;\n /** Light DOM toolbar buttons */\n lightDomButtons: HTMLElement[];\n /** Light DOM header content elements */\n lightDomHeaderContent: HTMLElement[];\n /** Whether the tool panel sidebar is open */\n isPanelOpen: boolean;\n /** Which accordion sections are expanded (by panel ID) */\n expandedSections: Set<string>;\n /** Cleanup functions for header content render returns */\n headerContentCleanups: Map<string, () => void>;\n /** Cleanup functions for each panel section's render return */\n panelCleanups: Map<string, () => void>;\n /** Cleanup functions for toolbar button render returns */\n toolbarButtonCleanups: Map<string, () => void>;\n /** @deprecated Use isPanelOpen instead. Kept for backward compatibility. */\n activePanel: string | null;\n /** @deprecated Use panelCleanups instead. Kept for backward compatibility. */\n activePanelCleanup: (() => void) | null;\n}\n\n/**\n * Create initial shell state.\n */\nexport function createShellState(): ShellState {\n return {\n toolPanels: new Map(),\n headerContents: new Map(),\n toolbarButtons: new Map(),\n lightDomButtons: [],\n lightDomHeaderContent: [],\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarButtonCleanups: new Map(),\n // Deprecated - kept for backward compatibility\n activePanel: null,\n activePanelCleanup: null,\n };\n}\n\n/**\n * Determine if shell header should be rendered.\n */\nexport function shouldRenderShellHeader(config: ShellConfig | undefined, state: ShellState): boolean {\n // Check if title is configured\n if (config?.header?.title) return true;\n\n // Check if config has toolbar buttons\n if (config?.header?.toolbarButtons?.length) return true;\n\n // Check if any tool panels are registered (need toolbar buttons for them)\n if (state.toolPanels.size > 0) return true;\n\n // Check if any header content is registered\n if (state.headerContents.size > 0) return true;\n\n // Check if any API toolbar buttons registered\n if (state.toolbarButtons.size > 0) return true;\n\n // Check if light DOM has header elements\n if (state.lightDomButtons.length > 0 || state.lightDomHeaderContent.length > 0) return true;\n\n return false;\n}\n\n/**\n * Render the shell header HTML.\n * @param toolPanelIcon - Icon for the tool panel toggle (from grid icon config)\n */\nexport function renderShellHeader(\n config: ShellConfig | undefined,\n state: ShellState,\n toolPanelIcon: IconValue = '☰',\n): string {\n const title = config?.header?.title ?? '';\n const hasTitle = !!title;\n const iconStr = iconToString(toolPanelIcon);\n\n // Collect all toolbar buttons in order\n // 1. Config buttons (sorted by order)\n // 2. API-registered buttons (sorted by order)\n // 3. Light DOM buttons via slot\n // 4. Single panel toggle button (if any panels registered)\n\n const configButtons = config?.header?.toolbarButtons ?? [];\n const hasConfigButtons = configButtons.length > 0;\n const hasApiButtons = state.toolbarButtons.size > 0;\n const hasLightDomButtons = state.lightDomButtons.length > 0;\n const hasPanels = state.toolPanels.size > 0;\n const hasCustomButtons = hasConfigButtons || hasApiButtons || hasLightDomButtons;\n const showSeparator = hasCustomButtons && hasPanels;\n\n // Sort config buttons by order\n const sortedConfigButtons = [...configButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Sort API buttons by order\n const sortedApiButtons = [...state.toolbarButtons.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build toolbar HTML\n let toolbarHtml = '';\n\n // Config buttons with icon/action (grid renders these)\n for (const btn of sortedConfigButtons) {\n if (btn.icon && btn.action) {\n toolbarHtml += `<button class=\"tbw-toolbar-btn\" data-btn=\"${btn.id}\" title=\"${btn.label}\" aria-label=\"${\n btn.label\n }\"${btn.disabled ? ' disabled' : ''}>${btn.icon}</button>`;\n }\n }\n\n // API buttons with icon/action\n for (const btn of sortedApiButtons) {\n if (btn.icon && btn.action) {\n toolbarHtml += `<button class=\"tbw-toolbar-btn\" data-btn=\"${btn.id}\" title=\"${btn.label}\" aria-label=\"${\n btn.label\n }\"${btn.disabled ? ' disabled' : ''}>${btn.icon}</button>`;\n }\n }\n\n // Placeholders for config/API buttons with element or render function\n for (const btn of sortedConfigButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n for (const btn of sortedApiButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n\n // Light DOM slot\n if (hasLightDomButtons) {\n toolbarHtml += '<slot name=\"toolbar\"></slot>';\n }\n\n // Separator\n if (showSeparator) {\n toolbarHtml += '<div class=\"tbw-toolbar-separator\"></div>';\n }\n\n // Single panel toggle button (opens accordion-style sidebar with all panels)\n if (hasPanels) {\n const isOpen = state.isPanelOpen;\n toolbarHtml += `<button class=\"tbw-toolbar-btn${isOpen ? ' active' : ''}\" data-panel-toggle title=\"Settings\" aria-label=\"Toggle settings panel\" aria-pressed=\"${isOpen}\" aria-controls=\"tbw-tool-panel\">${iconStr}</button>`;\n }\n\n return `\n <div class=\"tbw-shell-header\" part=\"shell-header\" role=\"presentation\">\n ${hasTitle ? `<div class=\"tbw-shell-title\">${title}</div>` : ''}\n <div class=\"tbw-shell-content\" part=\"shell-content\" role=\"presentation\">\n <slot name=\"header-content\"></slot>\n </div>\n <div class=\"tbw-shell-toolbar\" part=\"shell-toolbar\" role=\"presentation\">\n ${toolbarHtml}\n </div>\n </div>\n `;\n}\n\n/**\n * Render the shell body wrapper HTML (contains grid content + accordion-style tool panel).\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderShellBody(\n config: ShellConfig | undefined,\n state: ShellState,\n gridContentHtml: string,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): string {\n const position = config?.toolPanel?.position ?? 'right';\n const hasPanel = state.toolPanels.size > 0;\n const isOpen = state.isPanelOpen;\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n // Sort panels by order for accordion sections\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const isSinglePanel = sortedPanels.length === 1;\n\n // Build accordion sections HTML\n let accordionHtml = '';\n for (const panel of sortedPanels) {\n const isExpanded = state.expandedSections.has(panel.id);\n const iconHtml = panel.icon ? `<span class=\"tbw-accordion-icon\">${panel.icon}</span>` : '';\n // Hide chevron for single panel (no toggling needed)\n const chevronHtml = isSinglePanel\n ? ''\n : `<span class=\"tbw-accordion-chevron\">${isExpanded ? collapseIcon : expandIcon}</span>`;\n // Disable accordion toggle for single panel\n const sectionClasses = `tbw-accordion-section${isExpanded ? ' expanded' : ''}${isSinglePanel ? ' single' : ''}`;\n accordionHtml += `\n <div class=\"${sectionClasses}\" data-section=\"${panel.id}\">\n <button class=\"tbw-accordion-header\" aria-expanded=\"${isExpanded}\" aria-controls=\"tbw-section-${panel.id}\"${isSinglePanel ? ' aria-disabled=\"true\"' : ''}>\n ${iconHtml}\n <span class=\"tbw-accordion-title\">${panel.title}</span>\n ${chevronHtml}\n </button>\n <div class=\"tbw-accordion-content\" id=\"tbw-section-${panel.id}\" role=\"presentation\"></div>\n </div>\n `;\n }\n\n // Resize handle position depends on panel position\n const resizeHandlePosition = position === 'left' ? 'right' : 'left';\n\n const panelHtml = hasPanel\n ? `\n <aside class=\"tbw-tool-panel${isOpen ? ' open' : ''}\" part=\"tool-panel\" data-position=\"${position}\" role=\"presentation\" id=\"tbw-tool-panel\">\n <div class=\"tbw-tool-panel-resize\" data-resize-handle data-handle-position=\"${resizeHandlePosition}\" aria-hidden=\"true\"></div>\n <div class=\"tbw-tool-panel-content\" role=\"presentation\">\n <div class=\"tbw-accordion\">\n ${accordionHtml}\n </div>\n </div>\n </aside>\n `\n : '';\n\n // For left position, panel comes before content in DOM order\n if (position === 'left') {\n return `\n <div class=\"tbw-shell-body\">\n ${panelHtml}\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n </div>\n `;\n }\n\n return `\n <div class=\"tbw-shell-body\">\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n ${panelHtml}\n </div>\n `;\n}\n\n/**\n * Parse light DOM shell elements (tbw-grid-header, etc.).\n */\nexport function parseLightDomShell(host: HTMLElement, state: ShellState): void {\n const headerEl = host.querySelector('tbw-grid-header');\n if (!headerEl) return;\n\n // Hide the light DOM container\n (headerEl as HTMLElement).style.display = 'none';\n\n // Parse header content elements\n const headerContents = headerEl.querySelectorAll('tbw-grid-header-content');\n state.lightDomHeaderContent = Array.from(headerContents) as HTMLElement[];\n\n // Assign slot names for slotting into shadow DOM\n state.lightDomHeaderContent.forEach((el) => {\n el.setAttribute('slot', 'header-content');\n });\n\n // Parse toolbar button elements\n const toolButtons = headerEl.querySelectorAll('tbw-grid-tool-button');\n state.lightDomButtons = Array.from(toolButtons) as HTMLElement[];\n\n // Sort by order attribute and assign slots\n state.lightDomButtons.sort((a, b) => {\n const orderA = parseInt(a.getAttribute('order') ?? '100', 10);\n const orderB = parseInt(b.getAttribute('order') ?? '100', 10);\n return orderA - orderB;\n });\n\n state.lightDomButtons.forEach((el) => {\n el.setAttribute('slot', 'toolbar');\n });\n}\n\n/**\n * Set up event listeners for shell toolbar buttons and accordion.\n */\nexport function setupShellEventListeners(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n callbacks: {\n onPanelToggle: () => void;\n onSectionToggle: (sectionId: string) => void;\n onToolbarButtonClick: (buttonId: string) => void;\n },\n): void {\n const toolbar = shadowRoot.querySelector('.tbw-shell-toolbar');\n if (toolbar) {\n toolbar.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n\n // Handle single panel toggle button\n const panelToggle = target.closest('[data-panel-toggle]') as HTMLElement | null;\n if (panelToggle) {\n callbacks.onPanelToggle();\n return;\n }\n\n // Handle custom toolbar buttons\n const customBtn = target.closest('[data-btn]') as HTMLElement | null;\n if (customBtn) {\n const btnId = customBtn.getAttribute('data-btn');\n if (btnId) {\n callbacks.onToolbarButtonClick(btnId);\n }\n }\n });\n }\n\n // Accordion header clicks\n const accordion = shadowRoot.querySelector('.tbw-accordion');\n if (accordion) {\n accordion.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n const header = target.closest('.tbw-accordion-header') as HTMLElement | null;\n if (header) {\n const section = header.closest('[data-section]') as HTMLElement | null;\n const sectionId = section?.getAttribute('data-section');\n if (sectionId) {\n callbacks.onSectionToggle(sectionId);\n }\n }\n });\n }\n}\n\n/**\n * Set up resize handle for tool panel.\n * Returns a cleanup function to remove event listeners.\n */\nexport function setupToolPanelResize(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n onResize: (width: number) => void,\n): () => void {\n const panel = shadowRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n const handle = shadowRoot.querySelector('[data-resize-handle]') as HTMLElement | null;\n const shellBody = shadowRoot.querySelector('.tbw-shell-body') as HTMLElement | null;\n if (!panel || !handle || !shellBody) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const position = config?.toolPanel?.position ?? 'right';\n const minWidth = 200;\n\n let startX = 0;\n let startWidth = 0;\n let maxWidth = 0;\n let isResizing = false;\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n e.preventDefault();\n\n // For right-positioned panel: dragging left (negative clientX change) should expand\n // For left-positioned panel: dragging right (positive clientX change) should expand\n const delta = position === 'left' ? e.clientX - startX : startX - e.clientX;\n const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidth + delta));\n\n panel.style.width = `${newWidth}px`;\n };\n\n const onMouseUp = () => {\n if (!isResizing) return;\n isResizing = false;\n handle.classList.remove('resizing');\n panel.style.transition = ''; // Re-enable transition\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n\n // Get final width and notify\n const finalWidth = panel.getBoundingClientRect().width;\n onResize(finalWidth);\n\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n isResizing = true;\n startX = e.clientX;\n startWidth = panel.getBoundingClientRect().width;\n // Calculate max width dynamically based on grid container width\n maxWidth = shellBody.getBoundingClientRect().width - 20; // Leave 20px margin\n handle.classList.add('resizing');\n panel.style.transition = 'none'; // Disable transition for smooth resize\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n\n // Return cleanup function\n return () => {\n handle.removeEventListener('mousedown', onMouseDown);\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n}\n\n/**\n * Render custom button elements/render functions into toolbar slots.\n */\nexport function renderCustomToolbarButtons(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n): void {\n const allButtons = [...(config?.header?.toolbarButtons ?? []), ...state.toolbarButtons.values()];\n\n for (const btn of allButtons) {\n const slot = shadowRoot.querySelector(`[data-btn-slot=\"${btn.id}\"]`);\n if (!slot) continue;\n\n // Clean up previous render if any\n const existingCleanup = state.toolbarButtonCleanups.get(btn.id);\n if (existingCleanup) {\n existingCleanup();\n state.toolbarButtonCleanups.delete(btn.id);\n }\n\n if (btn.element) {\n slot.appendChild(btn.element);\n } else if (btn.render) {\n const cleanup = btn.render(slot as HTMLElement);\n if (cleanup) {\n state.toolbarButtonCleanups.set(btn.id, cleanup);\n }\n }\n }\n}\n\n/**\n * Render header content from plugins into the shell content area.\n */\nexport function renderHeaderContent(shadowRoot: ShadowRoot, state: ShellState): void {\n const contentArea = shadowRoot.querySelector('.tbw-shell-content');\n if (!contentArea) return;\n\n // Sort by order\n const sortedContents = [...state.headerContents.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Create containers for each content piece (before the slot)\n const slot = contentArea.querySelector('slot[name=\"header-content\"]');\n\n for (const content of sortedContents) {\n // Clean up previous render if any\n const existingCleanup = state.headerContentCleanups.get(content.id);\n if (existingCleanup) {\n existingCleanup();\n state.headerContentCleanups.delete(content.id);\n }\n\n // Check if container already exists\n let container = contentArea.querySelector(`[data-header-content=\"${content.id}\"]`) as HTMLElement | null;\n if (!container) {\n container = document.createElement('div');\n container.setAttribute('data-header-content', content.id);\n // Insert before the slot\n if (slot) {\n contentArea.insertBefore(container, slot);\n } else {\n contentArea.appendChild(container);\n }\n }\n\n const cleanup = content.render(container);\n if (cleanup) {\n state.headerContentCleanups.set(content.id, cleanup);\n }\n }\n}\n\n/**\n * Render content for expanded accordion sections.\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderPanelContent(\n shadowRoot: ShadowRoot,\n state: ShellState,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): void {\n if (!state.isPanelOpen) return;\n\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n for (const [panelId, panel] of state.toolPanels) {\n const isExpanded = state.expandedSections.has(panelId);\n const section = shadowRoot.querySelector(`[data-section=\"${panelId}\"]`);\n const contentArea = section?.querySelector('.tbw-accordion-content') as HTMLElement | null;\n\n if (!section || !contentArea) continue;\n\n // Update expanded state\n section.classList.toggle('expanded', isExpanded);\n const header = section.querySelector('.tbw-accordion-header');\n if (header) {\n header.setAttribute('aria-expanded', String(isExpanded));\n }\n const chevron = section.querySelector('.tbw-accordion-chevron');\n if (chevron) {\n chevron.innerHTML = isExpanded ? collapseIcon : expandIcon;\n }\n\n if (isExpanded) {\n // Check if content is already rendered\n if (contentArea.children.length === 0) {\n // Render panel content\n const cleanup = panel.render(contentArea);\n if (cleanup) {\n state.panelCleanups.set(panelId, cleanup);\n }\n }\n } else {\n // Clean up and clear content when collapsed\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n contentArea.innerHTML = '';\n }\n }\n}\n\n/**\n * Update toolbar button active states.\n */\nexport function updateToolbarActiveStates(shadowRoot: ShadowRoot, state: ShellState): void {\n // Update single panel toggle button\n const panelToggle = shadowRoot.querySelector('[data-panel-toggle]');\n if (panelToggle) {\n panelToggle.classList.toggle('active', state.isPanelOpen);\n panelToggle.setAttribute('aria-pressed', String(state.isPanelOpen));\n }\n}\n\n/**\n * Update tool panel open/close state.\n */\nexport function updatePanelState(shadowRoot: ShadowRoot, state: ShellState): void {\n const panel = shadowRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n if (!panel) return;\n\n panel.classList.toggle('open', state.isPanelOpen);\n\n // Clear inline width when closing (resize sets inline style that overrides CSS)\n if (!state.isPanelOpen) {\n panel.style.width = '';\n }\n}\n\n/**\n * Get all toolbar button info.\n */\nexport function getToolbarButtonsInfo(config: ShellConfig | undefined, state: ShellState): ToolbarButtonInfo[] {\n const result: ToolbarButtonInfo[] = [];\n\n // Config buttons\n for (const btn of config?.header?.toolbarButtons ?? []) {\n result.push({\n id: btn.id,\n label: btn.label,\n disabled: btn.disabled ?? false,\n source: 'config',\n });\n }\n\n // API buttons\n for (const btn of state.toolbarButtons.values()) {\n result.push({\n id: btn.id,\n label: btn.label,\n disabled: btn.disabled ?? false,\n source: 'config',\n });\n }\n\n // Light DOM buttons (limited info since user provides DOM)\n for (let i = 0; i < state.lightDomButtons.length; i++) {\n const el = state.lightDomButtons[i];\n const btn = el.querySelector('button');\n result.push({\n id: `light-dom-${i}`,\n label: btn?.getAttribute('title') ?? btn?.getAttribute('aria-label') ?? '',\n disabled: btn?.disabled ?? false,\n source: 'light-dom',\n });\n }\n\n // Panel toggles\n for (const panel of state.toolPanels.values()) {\n result.push({\n id: `panel-toggle-${panel.id}`,\n label: panel.tooltip ?? panel.title,\n disabled: false,\n source: 'panel-toggle',\n panelId: panel.id,\n });\n }\n\n return result;\n}\n\n/**\n * Cleanup all shell state when grid disconnects.\n */\nexport function cleanupShellState(state: ShellState): void {\n // Clean up header content\n for (const cleanup of state.headerContentCleanups.values()) {\n cleanup();\n }\n state.headerContentCleanups.clear();\n\n // Clean up active panel\n if (state.activePanelCleanup) {\n state.activePanelCleanup();\n state.activePanelCleanup = null;\n }\n\n // Clean up toolbar buttons\n for (const cleanup of state.toolbarButtonCleanups.values()) {\n cleanup();\n }\n state.toolbarButtonCleanups.clear();\n\n // Invoke panel onClose if open\n if (state.activePanel) {\n const panel = state.toolPanels.get(state.activePanel);\n panel?.onClose?.();\n }\n\n // Clear registrations\n state.toolPanels.clear();\n state.headerContents.clear();\n state.toolbarButtons.clear();\n state.lightDomButtons = [];\n state.lightDomHeaderContent = [];\n state.activePanel = null;\n}\n\n// ============================================================================\n// ShellController - Encapsulates tool panel orchestration logic\n// ============================================================================\n\n/**\n * Callbacks for ShellController to communicate with the grid.\n * This interface decouples the controller from grid internals.\n */\nexport interface ShellControllerCallbacks {\n /** Get the shadow root for DOM queries */\n getShadow: () => ShadowRoot;\n /** Get the current shell config */\n getShellConfig: () => ShellConfig | undefined;\n /** Get accordion expand/collapse icons */\n getAccordionIcons: () => { expand: IconValue; collapse: IconValue };\n /** Emit an event from the grid */\n emit: (eventName: string, detail: unknown) => void;\n /** Refresh the shell header (re-parse light DOM and re-render) */\n refreshShellHeader: () => void;\n}\n\n/**\n * Controller interface for managing shell/tool panel behavior.\n */\nexport interface ShellController {\n /** Whether the shell has been initialized */\n readonly isInitialized: boolean;\n /** Set the initialized state */\n setInitialized(value: boolean): void;\n /** Whether the tool panel is currently open */\n readonly isPanelOpen: boolean;\n /** Get the currently active panel ID (deprecated) */\n readonly activePanel: string | null;\n /** Get IDs of expanded accordion sections */\n readonly expandedSections: string[];\n /** Open the tool panel */\n openToolPanel(): void;\n /** Close the tool panel */\n closeToolPanel(): void;\n /** Toggle the tool panel */\n toggleToolPanel(): void;\n /** Toggle an accordion section */\n toggleToolPanelSection(sectionId: string): void;\n /** Get registered tool panels */\n getToolPanels(): ToolPanelDefinition[];\n /** Register a tool panel */\n registerToolPanel(panel: ToolPanelDefinition): void;\n /** Unregister a tool panel */\n unregisterToolPanel(panelId: string): void;\n /** Get registered header contents */\n getHeaderContents(): HeaderContentDefinition[];\n /** Register header content */\n registerHeaderContent(content: HeaderContentDefinition): void;\n /** Unregister header content */\n unregisterHeaderContent(contentId: string): void;\n /** Get all toolbar buttons info */\n getToolbarButtons(): ToolbarButtonInfo[];\n /** Register a toolbar button */\n registerToolbarButton(button: ToolbarButtonConfig): void;\n /** Unregister a toolbar button */\n unregisterToolbarButton(buttonId: string): void;\n /** Enable/disable a toolbar button */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): void;\n}\n\n/**\n * Create a ShellController instance.\n * The controller encapsulates all tool panel orchestration logic.\n */\nexport function createShellController(state: ShellState, callbacks: ShellControllerCallbacks): ShellController {\n let initialized = false;\n\n const controller: ShellController = {\n get isInitialized() {\n return initialized;\n },\n setInitialized(value: boolean) {\n initialized = value;\n },\n\n get isPanelOpen() {\n return state.isPanelOpen;\n },\n\n get activePanel() {\n // For backward compatibility, return first expanded section if panel is open\n if (state.isPanelOpen && state.expandedSections.size > 0) {\n return [...state.expandedSections][0];\n }\n return null;\n },\n\n get expandedSections() {\n return [...state.expandedSections];\n },\n\n openToolPanel() {\n if (state.isPanelOpen) return;\n if (state.toolPanels.size === 0) {\n console.warn('[tbw-grid] No tool panels registered');\n return;\n }\n\n state.isPanelOpen = true;\n\n // Auto-expand first section if none expanded\n if (state.expandedSections.size === 0 && state.toolPanels.size > 0) {\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const firstPanel = sortedPanels[0];\n if (firstPanel) {\n state.expandedSections.add(firstPanel.id);\n }\n }\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Render accordion sections\n renderPanelContent(shadow, state, callbacks.getAccordionIcons());\n\n // Emit event\n callbacks.emit('tool-panel-open', { sections: controller.expandedSections });\n },\n\n closeToolPanel() {\n if (!state.isPanelOpen) return;\n\n // Clean up all panel content\n for (const cleanup of state.panelCleanups.values()) {\n cleanup();\n }\n state.panelCleanups.clear();\n\n // Legacy cleanup\n if (state.activePanelCleanup) {\n state.activePanelCleanup();\n state.activePanelCleanup = null;\n }\n\n // Call onClose for all panels\n for (const panel of state.toolPanels.values()) {\n panel.onClose?.();\n }\n\n state.isPanelOpen = false;\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Emit event\n callbacks.emit('tool-panel-close', {});\n },\n\n toggleToolPanel() {\n if (state.isPanelOpen) {\n controller.closeToolPanel();\n } else {\n controller.openToolPanel();\n }\n },\n\n toggleToolPanelSection(sectionId: string) {\n const panel = state.toolPanels.get(sectionId);\n if (!panel) {\n console.warn(`[tbw-grid] Tool panel section \"${sectionId}\" not found`);\n return;\n }\n\n // Don't allow toggling when there's only one panel (it should stay expanded)\n if (state.toolPanels.size === 1) {\n return;\n }\n\n const shadow = callbacks.getShadow();\n const isExpanded = state.expandedSections.has(sectionId);\n\n if (isExpanded) {\n // Collapsing current section\n const cleanup = state.panelCleanups.get(sectionId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(sectionId);\n }\n panel.onClose?.();\n state.expandedSections.delete(sectionId);\n updateAccordionSectionState(shadow, sectionId, false);\n } else {\n // Expanding - first collapse all others (exclusive accordion)\n for (const [otherId, otherPanel] of state.toolPanels) {\n if (otherId !== sectionId && state.expandedSections.has(otherId)) {\n const cleanup = state.panelCleanups.get(otherId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(otherId);\n }\n otherPanel.onClose?.();\n state.expandedSections.delete(otherId);\n updateAccordionSectionState(shadow, otherId, false);\n // Clear content of collapsed section\n const contentEl = shadow.querySelector(`[data-section=\"${otherId}\"] .tbw-accordion-content`);\n if (contentEl) contentEl.innerHTML = '';\n }\n }\n // Now expand the target section\n state.expandedSections.add(sectionId);\n updateAccordionSectionState(shadow, sectionId, true);\n renderAccordionSectionContent(shadow, state, sectionId);\n }\n\n // Emit event\n callbacks.emit('tool-panel-section-toggle', { id: sectionId, expanded: !isExpanded });\n },\n\n getToolPanels() {\n return [...state.toolPanels.values()];\n },\n\n registerToolPanel(panel: ToolPanelDefinition) {\n if (state.toolPanels.has(panel.id)) {\n console.warn(`[tbw-grid] Tool panel \"${panel.id}\" already registered`);\n return;\n }\n state.toolPanels.set(panel.id, panel);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolPanel(panelId: string) {\n // Close panel if open and this section is expanded\n if (state.expandedSections.has(panelId)) {\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n state.expandedSections.delete(panelId);\n }\n\n state.toolPanels.delete(panelId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n getHeaderContents() {\n return [...state.headerContents.values()];\n },\n\n registerHeaderContent(content: HeaderContentDefinition) {\n if (state.headerContents.has(content.id)) {\n console.warn(`[tbw-grid] Header content \"${content.id}\" already registered`);\n return;\n }\n state.headerContents.set(content.id, content);\n\n if (initialized) {\n renderHeaderContent(callbacks.getShadow(), state);\n }\n },\n\n unregisterHeaderContent(contentId: string) {\n // Clean up\n const cleanup = state.headerContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n state.headerContentCleanups.delete(contentId);\n }\n\n // Call onDestroy\n const content = state.headerContents.get(contentId);\n content?.onDestroy?.();\n\n state.headerContents.delete(contentId);\n\n // Remove DOM element\n const el = callbacks.getShadow().querySelector(`[data-header-content=\"${contentId}\"]`);\n el?.remove();\n },\n\n getToolbarButtons() {\n return getToolbarButtonsInfo(callbacks.getShellConfig(), state);\n },\n\n registerToolbarButton(button: ToolbarButtonConfig) {\n if (state.toolbarButtons.has(button.id)) {\n console.warn(`[tbw-grid] Toolbar button \"${button.id}\" already registered`);\n return;\n }\n state.toolbarButtons.set(button.id, button);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolbarButton(buttonId: string) {\n // Clean up\n const cleanup = state.toolbarButtonCleanups.get(buttonId);\n if (cleanup) {\n cleanup();\n state.toolbarButtonCleanups.delete(buttonId);\n }\n\n state.toolbarButtons.delete(buttonId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n setToolbarButtonDisabled(buttonId: string, disabled: boolean) {\n // Check API-registered buttons\n const apiBtn = state.toolbarButtons.get(buttonId);\n if (apiBtn) {\n apiBtn.disabled = disabled;\n }\n\n // Update DOM\n const btn = callbacks.getShadow().querySelector(`[data-btn=\"${buttonId}\"]`) as HTMLButtonElement | null;\n if (btn) {\n btn.disabled = disabled;\n }\n },\n };\n\n return controller;\n}\n\n/**\n * Update accordion section visual state.\n */\nfunction updateAccordionSectionState(shadow: ShadowRoot, sectionId: string, expanded: boolean): void {\n const section = shadow.querySelector(`[data-section=\"${sectionId}\"]`);\n if (section) {\n section.classList.toggle('expanded', expanded);\n }\n}\n\n/**\n * Render content for a single accordion section.\n */\nfunction renderAccordionSectionContent(shadow: ShadowRoot, state: ShellState, sectionId: string): void {\n const panel = state.toolPanels.get(sectionId);\n if (!panel?.render) return;\n\n const contentEl = shadow.querySelector(`[data-section=\"${sectionId}\"] .tbw-accordion-content`);\n if (!contentEl) return;\n\n const cleanup = panel.render(contentEl as HTMLElement);\n if (cleanup) {\n state.panelCleanups.set(sectionId, cleanup);\n }\n}\n","/**\n * Plugin Manager\n *\n * Manages plugin instances for a single grid.\n * Each grid has its own PluginManager with its own set of plugin instances.\n */\n\nimport type { ColumnConfig } from '../types';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n HeaderClickEvent,\n HeaderRenderer,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './base-plugin';\n\n/**\n * Manages plugins for a single grid instance.\n */\nexport class PluginManager {\n /** Plugin instances in order of attachment */\n private plugins: BaseGridPlugin[] = [];\n\n /** Map from plugin class to instance for fast lookup */\n private pluginMap: Map<new (...args: unknown[]) => BaseGridPlugin, BaseGridPlugin> = new Map();\n\n /** Cell renderers registered by plugins */\n private cellRenderers: Map<string, CellRenderer> = new Map();\n\n /** Header renderers registered by plugins */\n private headerRenderers: Map<string, HeaderRenderer> = new Map();\n\n /** Cell editors registered by plugins */\n private cellEditors: Map<string, CellEditor> = new Map();\n\n constructor(private grid: any) {}\n\n /**\n * Attach all plugins from the config.\n */\n attachAll(plugins: BaseGridPlugin[]): void {\n for (const plugin of plugins) {\n this.attach(plugin);\n }\n }\n\n /**\n * Attach a plugin to this grid.\n */\n attach(plugin: BaseGridPlugin): void {\n // Store by constructor for type-safe lookup\n this.pluginMap.set(plugin.constructor as new (...args: unknown[]) => BaseGridPlugin, plugin);\n this.plugins.push(plugin);\n\n // Register renderers/editors\n if (plugin.cellRenderers) {\n for (const [type, renderer] of Object.entries(plugin.cellRenderers)) {\n this.cellRenderers.set(type, renderer);\n }\n }\n if (plugin.headerRenderers) {\n for (const [type, renderer] of Object.entries(plugin.headerRenderers)) {\n this.headerRenderers.set(type, renderer);\n }\n }\n if (plugin.cellEditors) {\n for (const [type, editor] of Object.entries(plugin.cellEditors)) {\n this.cellEditors.set(type, editor);\n }\n }\n\n // Call attach lifecycle method\n plugin.attach(this.grid);\n }\n\n /**\n * Detach all plugins and clean up.\n */\n detachAll(): void {\n // Detach in reverse order\n for (let i = this.plugins.length - 1; i >= 0; i--) {\n this.plugins[i].detach();\n }\n this.plugins = [];\n this.pluginMap.clear();\n this.cellRenderers.clear();\n this.headerRenderers.clear();\n this.cellEditors.clear();\n }\n\n /**\n * Get a plugin instance by its class.\n */\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.pluginMap.get(PluginClass) as T | undefined;\n }\n\n /**\n * Get a plugin instance by its name.\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.plugins.find((p) => p.name === name);\n }\n\n /**\n * Check if a plugin is attached.\n */\n hasPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): boolean {\n return this.pluginMap.has(PluginClass);\n }\n\n /**\n * Get all attached plugins.\n */\n getAll(): readonly BaseGridPlugin[] {\n return this.plugins;\n }\n\n /**\n * Get a cell renderer by type name.\n */\n getCellRenderer(type: string): CellRenderer | undefined {\n return this.cellRenderers.get(type);\n }\n\n /**\n * Get a header renderer by type name.\n */\n getHeaderRenderer(type: string): HeaderRenderer | undefined {\n return this.headerRenderers.get(type);\n }\n\n /**\n * Get a cell editor by type name.\n */\n getCellEditor(type: string): CellEditor | undefined {\n return this.cellEditors.get(type);\n }\n\n /**\n * Get all CSS styles from all plugins.\n */\n getAllStyles(): string {\n return this.plugins\n .filter((p) => p.styles)\n .map((p) => p.styles)\n .join('\\n');\n }\n\n // #region Hook execution methods\n\n /**\n * Execute processRows hook on all plugins.\n */\n processRows(rows: readonly any[]): any[] {\n let result = [...rows];\n for (const plugin of this.plugins) {\n if (plugin.processRows) {\n result = plugin.processRows(result);\n }\n }\n return result;\n }\n\n /**\n * Execute processColumns hook on all plugins.\n */\n processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n let result = [...columns];\n for (const plugin of this.plugins) {\n if (plugin.processColumns) {\n result = plugin.processColumns(result);\n }\n }\n return result;\n }\n\n /**\n * Execute beforeRender hook on all plugins.\n */\n beforeRender(): void {\n for (const plugin of this.plugins) {\n plugin.beforeRender?.();\n }\n }\n\n /**\n * Execute afterRender hook on all plugins.\n */\n afterRender(): void {\n for (const plugin of this.plugins) {\n plugin.afterRender?.();\n }\n }\n\n /**\n * Execute onScrollRender hook on all plugins.\n * Called after scroll-triggered row rendering for lightweight visual state updates.\n */\n onScrollRender(): void {\n for (const plugin of this.plugins) {\n plugin.onScrollRender?.();\n }\n }\n\n /**\n * Get total extra height contributed by plugins (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations.\n */\n getExtraHeight(): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeight === 'function') {\n total += plugin.getExtraHeight();\n }\n }\n return total;\n }\n\n /**\n * Get extra height from plugins that appears before a given row index.\n * Used by virtualization to correctly position the scroll window.\n */\n getExtraHeightBefore(beforeRowIndex: number): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeightBefore === 'function') {\n total += plugin.getExtraHeightBefore(beforeRowIndex);\n }\n }\n return total;\n }\n\n /**\n * Adjust the virtualization start index based on plugin needs.\n * Returns the minimum start index from all plugins.\n */\n adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n let adjustedStart = start;\n for (const plugin of this.plugins) {\n if (typeof plugin.adjustVirtualStart === 'function') {\n const pluginStart = plugin.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginStart < adjustedStart) {\n adjustedStart = pluginStart;\n }\n }\n }\n return adjustedStart;\n }\n\n /**\n * Execute renderRow hook on all plugins.\n * Returns true if any plugin handled the row.\n */\n renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean {\n for (const plugin of this.plugins) {\n if (plugin.renderRow?.(row, rowEl, rowIndex)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n *\n * Common query types are defined in PLUGIN_QUERIES, but plugins can define their own.\n *\n * @param query - The query object containing type and context\n * @returns Array of non-undefined responses from plugins\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n const responses: T[] = [];\n for (const plugin of this.plugins) {\n const response = plugin.onPluginQuery?.(query);\n if (response !== undefined) {\n responses.push(response as T);\n }\n }\n return responses;\n }\n\n /**\n * Execute onKeyDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onKeyDown(event: KeyboardEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onKeyDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellClick(event: CellClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onRowClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onRowClick(event: RowClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onRowClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onHeaderClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onHeaderClick(event: HeaderClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onHeaderClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onScroll hook on all plugins.\n */\n onScroll(event: ScrollEvent): void {\n for (const plugin of this.plugins) {\n plugin.onScroll?.(event);\n }\n }\n\n /**\n * Execute onCellMouseDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseDown(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseMove hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseMove(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseMove?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseUp hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseUp(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseUp?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Collect horizontal scroll boundary offsets from all plugins.\n * Combines offsets from all plugins that report them.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Combined left and right pixel offsets, plus skipScroll if any plugin requests it\n */\n getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n let left = 0;\n let right = 0;\n let skipScroll = false;\n for (const plugin of this.plugins) {\n const offsets = plugin.getHorizontalScrollOffsets?.(rowEl, focusedCell);\n if (offsets) {\n left += offsets.left;\n right += offsets.right;\n if (offsets.skipScroll) {\n skipScroll = true;\n }\n }\n }\n return { left, right, skipScroll };\n }\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Collect tool panels from all plugins.\n * Returns panels sorted by order (ascending).\n */\n getToolPanels(): {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] {\n const panels: {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const panel = plugin.getToolPanel?.();\n if (panel) {\n panels.push({ plugin, panel });\n }\n }\n // Sort by order (ascending), default to 0\n return panels.sort((a, b) => (a.panel.order ?? 0) - (b.panel.order ?? 0));\n }\n\n /**\n * Collect header contents from all plugins.\n * Returns contents sorted by order (ascending).\n */\n getHeaderContents(): {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] {\n const contents: {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const content = plugin.getHeaderContent?.();\n if (content) {\n contents.push({ plugin, content });\n }\n }\n // Sort by order (ascending), default to 0\n return contents.sort((a, b) => (a.content.order ?? 0) - (b.content.order ?? 0));\n }\n // #endregion\n}\n","import styles from './grid.css?inline';\nimport { applyColumnState, collectColumnState, createStateChangeHandler } from './internal/column-state';\nimport { autoSizeColumns, getColumnConfiguration, updateTemplate } from './internal/columns';\nimport { exitRowEdit, inlineEnterEdit, startRowEdit } from './internal/editing';\nimport { renderHeader } from './internal/header';\nimport { inferColumns } from './internal/inference';\nimport { ensureCellVisible, handleGridKeyDown } from './internal/keyboard';\nimport { createResizeController } from './internal/resize';\nimport { invalidateCellCache, renderVisibleRows } from './internal/rows';\nimport {\n cleanupShellState,\n createShellController,\n createShellState,\n parseLightDomShell,\n renderCustomToolbarButtons,\n renderHeaderContent,\n renderShellBody,\n renderShellHeader,\n setupShellEventListeners,\n setupToolPanelResize,\n shouldRenderShellHeader,\n type ShellController,\n type ShellState,\n} from './internal/shell';\nimport type { CellMouseEvent, ScrollEvent } from './plugin';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n HeaderClickEvent,\n PluginQuery,\n RowClickEvent,\n} from './plugin/base-plugin';\nimport { PluginManager } from './plugin/plugin-manager';\nimport type {\n ActivateCellDetail,\n AnimationConfig,\n CellCommitDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n ColumnResizeDetail,\n FitMode,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n InternalGrid,\n ResizeController,\n RowCommitDetail,\n SortChangeDetail,\n ToolbarButtonConfig,\n ToolbarButtonInfo,\n ToolPanelDefinition,\n VirtualState,\n} from './types';\nimport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS } from './types';\n\n/**\n * High-performance data grid web component.\n *\n * ## Configuration Architecture\n *\n * The grid follows a **single source of truth** pattern where all configuration\n * converges into `#effectiveConfig`. Users can set configuration via multiple inputs:\n *\n * **Input Sources (precedence low → high):**\n * 1. `gridConfig` property - base configuration object\n * 2. Light DOM elements:\n * - `<tbw-grid-column>` → `effectiveConfig.columns`\n * - `<tbw-grid-header title=\"...\">` → `effectiveConfig.shell.header.title`\n * - `<tbw-grid-header-content>` → `effectiveConfig.shell.header.content`\n * 3. `columns` property → merged into `effectiveConfig.columns`\n * 4. `fitMode` property → merged into `effectiveConfig.fitMode`\n * 5. `editOn` property → merged into `effectiveConfig.editOn`\n * 6. Column inference from first row (if no columns defined)\n *\n * **Derived State:**\n * - `_columns` - processed columns from `effectiveConfig.columns` after plugin hooks\n * - `_rows` - processed rows after plugin hooks (grouping, filtering, etc.)\n *\n * The `#mergeEffectiveConfig()` method is the single place where all inputs converge.\n * All rendering and logic should read from `effectiveConfig` or derived state.\n *\n * @element tbw-grid\n *\n * @csspart container - The main grid container\n * @csspart header - The header row container\n * @csspart body - The body/rows container\n *\n * @fires cell-commit - Fired when a cell value is committed\n * @fires row-commit - Fired when a bulk row edit session commits\n * @fires changed-rows-reset - Fired after resetChangedRows() unless silent\n * @fires mount-external-view - Fired to request mounting of an external view renderer\n * @fires mount-external-editor - Fired to request mounting of an external editor renderer\n * @fires sort-change - Fired when sort state changes for a column\n * @fires column-resize - Fired after a column resize drag completes\n * @fires activate-cell - Fired when a cell activation intent occurs\n * @fires group-toggle - Fired when a group row is toggled\n *\n * @cssprop --tbw-color-bg - Background color\n * @cssprop --tbw-color-fg - Foreground/text color\n */\n// Injected by Vite at build time from package.json\ndeclare const __GRID_VERSION__: string;\n\nexport class DataGridElement<T = any> extends HTMLElement implements InternalGrid<T> {\n // TODO: Rename to 'data-grid' when migration is complete\n static readonly tagName = 'tbw-grid';\n static readonly version = typeof __GRID_VERSION__ !== 'undefined' ? __GRID_VERSION__ : 'dev';\n\n // ---------------- Observed Attributes ----------------\n static get observedAttributes(): string[] {\n return ['rows', 'columns', 'grid-config', 'fit-mode', 'edit-on'];\n }\n\n readonly #shadow: ShadowRoot;\n #initialized = false;\n\n // ---------------- Ready Promise ----------------\n #readyPromise: Promise<void>;\n #readyResolve?: () => void;\n\n // #region Input Properties\n // These backing fields store raw user input. They are merged into\n // #effectiveConfig by #mergeEffectiveConfig(). Never read directly\n // for rendering logic - always use effectiveConfig or derived state.\n #rows: T[] = [];\n #columns?: ColumnConfig<T>[] | ColumnConfigMap<T>;\n #gridConfig?: GridConfig<T>;\n #fitMode?: FitMode;\n #editOn?: string;\n // #endregion\n\n // #region Private properties\n // All input sources converge here. This is the canonical config\n // that all rendering and logic should read from.\n #effectiveConfig: GridConfig<T> = {};\n #connected = false;\n #scrollRaf = 0;\n #pendingScrollTop: number | null = null;\n #hasScrollPlugins = false; // Cached flag for plugin scroll handlers\n #renderRowHook?: (row: any, rowEl: HTMLElement, rowIndex: number) => boolean; // Cached hook to avoid closures\n #isDragging = false;\n #touchStartY: number | null = null;\n #touchStartX: number | null = null;\n #touchScrollTop: number | null = null;\n #touchScrollLeft: number | null = null;\n #touchLastY: number | null = null;\n #touchLastX: number | null = null;\n #touchLastTime: number | null = null;\n #touchVelocityY = 0;\n #touchVelocityX = 0;\n #momentumRaf = 0;\n #eventAbortController?: AbortController;\n #resizeObserver?: ResizeObserver;\n\n // ---------------- Plugin System ----------------\n #pluginManager!: PluginManager;\n\n // ---------------- Column State ----------------\n #stateChangeHandler?: () => void;\n #initialColumnState?: GridColumnState;\n\n // ---------------- Shell State ----------------\n #shellState: ShellState = createShellState();\n #shellController!: ShellController;\n #resizeCleanup?: () => void;\n // #endregion\n\n // #region Derived State\n // _rows: result of applying plugin processRows hooks\n _rows: T[] = [];\n\n // _baseColumns: columns before plugin transformation (analogous to #rows for row processing)\n // This is the source of truth for processColumns - plugins transform these\n #baseColumns: ColumnInternal<T>[] = [];\n\n // _columns is a getter/setter that operates on effectiveConfig.columns\n // This ensures effectiveConfig.columns is the single source of truth for columns\n // _columns always contains ALL columns (including hidden)\n get _columns(): ColumnInternal<T>[] {\n return (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n }\n set _columns(value: ColumnInternal<T>[]) {\n this.#effectiveConfig.columns = value as ColumnConfig<T>[];\n }\n\n // visibleColumns returns only visible columns for rendering\n // This is what header/row rendering should use\n get _visibleColumns(): ColumnInternal<T>[] {\n return this._columns.filter((c) => !c.hidden);\n }\n // #endregion\n\n // #region Runtime State (Plugin-accessible)\n // DOM references\n _headerRowEl!: HTMLElement;\n _bodyEl!: HTMLElement;\n _rowPool: HTMLElement[] = [];\n _resizeController!: ResizeController;\n\n // Virtualization & scroll state\n _virtualization: VirtualState = {\n enabled: true,\n rowHeight: 28,\n bypassThreshold: 24,\n start: 0,\n end: 0,\n container: null,\n viewportEl: null,\n totalHeightEl: null,\n };\n\n // Focus & navigation\n _focusRow = 0;\n _focusCol = 0;\n\n // Sort state\n _sortState: { field: string; direction: 1 | -1 } | null = null;\n\n // Edit state\n _activeEditRows = -1;\n _rowEditSnapshots = new Map<number, T>();\n _changedRowIndices = new Set<number>();\n\n // Layout\n _gridTemplate = '';\n // #endregion\n\n // #region Implementation Details (Internal only)\n __rowRenderEpoch = 0;\n __didInitialAutoSize = false;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n __originalOrder: T[] = [];\n // #endregion\n\n // #region Public API Props (getters/setters)\n // Getters return the EFFECTIVE value (after merging), not the raw input.\n // This is what consumers and plugins need - the current resolved state.\n // Setters update input properties which trigger re-merge into effectiveConfig.\n\n get rows(): T[] {\n return this._rows;\n }\n set rows(value: T[]) {\n const oldValue = this.#rows;\n this.#rows = value;\n if (oldValue !== value) {\n this.#onRowsChanged();\n }\n }\n\n /**\n * Get the original unfiltered/unprocessed rows.\n * Use this when you need access to all source data regardless of active filters.\n */\n get sourceRows(): T[] {\n return this.#rows;\n }\n\n get columns(): ColumnConfig<T>[] {\n return [...this._columns] as ColumnConfig<T>[];\n }\n set columns(value: ColumnConfig<T>[] | ColumnConfigMap<T> | undefined) {\n const oldValue = this.#columns;\n this.#columns = value;\n if (oldValue !== value) {\n this.#onColsChanged();\n }\n }\n\n get gridConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n set gridConfig(value: GridConfig<T> | undefined) {\n const oldValue = this.#gridConfig;\n this.#gridConfig = value;\n if (oldValue !== value) {\n this.#onGridConfigChanged();\n }\n }\n\n get fitMode(): FitMode {\n return this.#effectiveConfig.fitMode ?? 'stretch';\n }\n set fitMode(value: FitMode | undefined) {\n const oldValue = this.#fitMode;\n this.#fitMode = value;\n if (oldValue !== value) {\n this.#onFitChanged();\n }\n }\n\n get editOn(): string | undefined {\n return this.#effectiveConfig.editOn;\n }\n set editOn(value: string | undefined) {\n const oldValue = this.#editOn;\n this.#editOn = value;\n if (oldValue !== value) {\n this.#onEditModeChanged();\n }\n }\n\n /**\n * Effective config accessor for internal modules and plugins.\n * Returns the merged config (single source of truth) before plugin processing.\n * Use this when you need the raw merged config (e.g., for column definitions including hidden).\n * @internal Plugin API\n */\n get effectiveConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Plugins and internal code can use this for automatic listener cleanup.\n * @internal Plugin API\n * @example\n * element.addEventListener('click', handler, { signal: this.grid.disconnectSignal });\n */\n get disconnectSignal(): AbortSignal {\n // Ensure AbortController exists (created in connectedCallback before plugins attach)\n if (!this.#eventAbortController) {\n this.#eventAbortController = new AbortController();\n }\n return this.#eventAbortController.signal;\n }\n // #endregion\n\n constructor() {\n super();\n this.#shadow = this.attachShadow({ mode: 'open' });\n this.#injectStyles();\n this.#readyPromise = new Promise((res) => (this.#readyResolve = res));\n\n // Initialize shell controller with callbacks\n this.#shellController = createShellController(this.#shellState, {\n getShadow: () => this.#shadow,\n getShellConfig: () => this.#effectiveConfig?.shell,\n getAccordionIcons: () => ({\n expand: this.#effectiveConfig?.icons?.expand ?? DEFAULT_GRID_ICONS.expand,\n collapse: this.#effectiveConfig?.icons?.collapse ?? DEFAULT_GRID_ICONS.collapse,\n }),\n emit: (eventName, detail) => this.#emit(eventName, detail),\n refreshShellHeader: () => this.refreshShellHeader(),\n });\n }\n\n #injectStyles(): void {\n const sheet = new CSSStyleSheet();\n sheet.replaceSync(styles);\n this.#shadow.adoptedStyleSheets = [sheet];\n }\n\n // ---------------- Plugin System ----------------\n\n /**\n * Get a plugin instance by its class.\n * Used by plugins for inter-plugin communication.\n * @internal Plugin API\n */\n getPlugin<P extends BaseGridPlugin>(PluginClass: new (...args: any[]) => P): P | undefined {\n return this.#pluginManager?.getPlugin(PluginClass);\n }\n\n /**\n * Get a plugin instance by its name.\n * Used for loose coupling between plugins (avoids static imports).\n * @internal Plugin API\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.#pluginManager?.getPluginByName(name);\n }\n\n /**\n * Request a full re-render of the grid.\n * Called by plugins when they need the grid to update.\n * Note: This does NOT reset plugin state - just re-processes rows/columns and renders.\n * @internal Plugin API\n */\n requestRender(): void {\n this.#rebuildRowModel();\n this.#processColumns();\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n }\n\n /**\n * Update the grid's column template CSS.\n * Called by resize controller during column resize operations.\n * @internal\n */\n updateTemplate(): void {\n updateTemplate(this);\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Called by plugins when they only need to update CSS classes/styles.\n * This runs all plugin afterRender hooks without rebuilding row/column DOM.\n * @internal Plugin API\n */\n requestAfterRender(): void {\n this.#pluginManager?.afterRender();\n }\n\n /**\n * Initialize plugin system with instances from config.\n * Plugins are class instances passed in gridConfig.plugins[].\n */\n #initializePlugins(): void {\n // Create plugin manager for this grid\n this.#pluginManager = new PluginManager(this);\n\n // Get plugin instances from config - ensure it's an array\n const pluginsConfig = this.#effectiveConfig?.plugins;\n const plugins = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Attach all plugins\n this.#pluginManager.attachAll(plugins);\n }\n\n /**\n * Inject all plugin styles into the shadow DOM.\n * Must be called after #render() since innerHTML wipes existing content.\n */\n #injectAllPluginStyles(): void {\n const allStyles = this.#pluginManager?.getAllStyles() ?? '';\n if (allStyles) {\n const styleEl = document.createElement('style');\n styleEl.setAttribute('data-plugin', 'all');\n styleEl.textContent = allStyles;\n this.#shadow.appendChild(styleEl);\n }\n }\n\n /**\n * Update plugins when grid config changes.\n * With class-based plugins, we need to detach old and attach new.\n */\n #updatePluginConfigs(): void {\n // With class-based plugins, config changes require re-initialization\n // The new plugins are in the new config - detach old, attach new\n if (this.#pluginManager) {\n this.#pluginManager.detachAll();\n }\n this.#initializePlugins();\n this.#injectAllPluginStyles();\n // Update cached scroll plugin flag\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n }\n\n /**\n * Clean up plugin states when grid disconnects.\n */\n #destroyPlugins(): void {\n this.#pluginManager?.detachAll();\n }\n\n /**\n * Collect tool panels and header content from all plugins.\n * Called after plugins are attached but before render.\n */\n #collectPluginShellContributions(): void {\n if (!this.#pluginManager) return;\n\n // Collect tool panels from plugins\n const pluginPanels = this.#pluginManager.getToolPanels();\n for (const { panel } of pluginPanels) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.toolPanels.has(panel.id)) {\n this.#shellState.toolPanels.set(panel.id, panel);\n }\n }\n\n // Collect header contents from plugins\n const pluginContents = this.#pluginManager.getHeaderContents();\n for (const { content } of pluginContents) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.headerContents.has(content.id)) {\n this.#shellState.headerContents.set(content.id, content);\n }\n }\n }\n\n // ---------------- Lifecycle ----------------\n connectedCallback(): void {\n if (!this.hasAttribute('tabindex')) (this as any).tabIndex = 0;\n if (!this.hasAttribute('version')) this.setAttribute('version', DataGridElement.version);\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Create AbortController for all event listeners (grid internal + plugins)\n // This must happen BEFORE plugins attach so they can use disconnectSignal\n // Abort any previous controller first (in case of re-connect)\n this.#eventAbortController?.abort();\n this.#eventAbortController = new AbortController();\n\n // Merge all config sources into effectiveConfig (including columns)\n this.#mergeEffectiveConfig();\n\n // Initialize plugin system (now plugins can access disconnectSignal)\n this.#initializePlugins();\n\n // Collect tool panels and header content from plugins (must be before render)\n this.#collectPluginShellContributions();\n\n if (!this.#initialized) {\n this.#render();\n this.#injectAllPluginStyles(); // Inject plugin styles after render\n this.#initialized = true;\n }\n this.#afterConnect();\n }\n\n disconnectedCallback(): void {\n // Clean up plugin states\n this.#destroyPlugins();\n\n // Clean up shell state\n cleanupShellState(this.#shellState);\n this.#shellController.setInitialized(false);\n\n // Clean up tool panel resize handler\n this.#resizeCleanup?.();\n this.#resizeCleanup = undefined;\n\n // Abort all event listeners (internal + document-level)\n // This cleans up all listeners added with { signal } option\n if (this.#eventAbortController) {\n this.#eventAbortController.abort();\n this.#eventAbortController = undefined;\n }\n\n if (this._resizeController) {\n this._resizeController.dispose();\n }\n if (this.#resizeObserver) {\n this.#resizeObserver.disconnect();\n this.#resizeObserver = undefined;\n }\n this.#connected = false;\n }\n\n /**\n * Handle HTML attribute changes.\n * Only processes attribute values when SET (non-null).\n * Removing an attribute does NOT clear JS-set properties.\n */\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue || !newValue || newValue === 'null' || newValue === 'undefined') return;\n\n // Map kebab-case attributes to camelCase properties\n const propMap: Record<string, keyof this> = {\n rows: 'rows',\n columns: 'columns',\n 'grid-config': 'gridConfig',\n 'fit-mode': 'fitMode',\n 'edit-on': 'editOn',\n };\n\n const prop = propMap[name];\n if (!prop) return;\n\n // JSON attributes need parsing\n if (name === 'rows' || name === 'columns' || name === 'grid-config') {\n try {\n (this as any)[prop] = JSON.parse(newValue);\n } catch {\n console.warn(`[tbw-grid] Invalid JSON for '${name}' attribute:`, newValue);\n }\n } else {\n // String attributes (fit-mode, edit-on)\n (this as any)[prop] = newValue;\n }\n }\n\n #afterConnect(): void {\n // Shell changes the DOM structure - need to find elements appropriately\n const gridContent = this.#shadow.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#shadow.querySelector('.tbw-grid-root');\n\n this._headerRowEl = gridRoot?.querySelector('.header-row') as HTMLElement;\n // Faux scrollbar pattern:\n // - .faux-vscroll-spacer sets virtual height\n // - .rows-viewport provides visible height for virtualization calculations\n this._virtualization.totalHeightEl = gridRoot?.querySelector('.faux-vscroll-spacer') as HTMLElement;\n this._virtualization.viewportEl = gridRoot?.querySelector('.rows-viewport') as HTMLElement;\n this._bodyEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Initialize shell header content and custom buttons if shell is active\n if (this.#shellController.isInitialized) {\n // Render plugin header content\n renderHeaderContent(this.#shadow, this.#shellState);\n // Render custom toolbar buttons (element/render modes)\n renderCustomToolbarButtons(this.#shadow, this.#effectiveConfig?.shell, this.#shellState);\n // Open default section if configured\n const defaultOpen = this.#effectiveConfig?.shell?.toolPanel?.defaultOpen;\n if (defaultOpen && this.#shellState.toolPanels.has(defaultOpen)) {\n this.openToolPanel();\n this.#shellState.expandedSections.add(defaultOpen);\n }\n }\n\n // Mark for tests that afterConnect ran\n this.setAttribute('data-upgraded', '');\n this.#connected = true;\n\n // Get the signal for event listener cleanup (AbortController created in connectedCallback)\n const signal = this.disconnectSignal;\n\n // Create resize controller BEFORE setup - renderHeader() needs it for resize handle mousedown events\n this._resizeController = createResizeController(this as any);\n\n // Run setup\n this.#setup();\n\n // Element-level keydown handler (uses signal for automatic cleanup)\n this.addEventListener('keydown', (e) => handleGridKeyDown(this as any, e), { signal });\n\n // Document-level listeners (also use signal for automatic cleanup)\n // Escape key to cancel row editing\n document.addEventListener(\n 'keydown',\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this._activeEditRows !== -1) {\n exitRowEdit(this, this._activeEditRows, true);\n }\n },\n { capture: true, signal },\n );\n\n // Click outside to commit row editing\n document.addEventListener(\n 'mousedown',\n (e: MouseEvent) => {\n if (this._activeEditRows === -1) return;\n const rowEl = this.findRenderedRowElement(this._activeEditRows);\n if (!rowEl) return;\n const path = (e.composedPath && e.composedPath()) || [];\n if (path.includes(rowEl)) return;\n exitRowEdit(this, this._activeEditRows, false);\n },\n { signal },\n );\n\n // Faux scrollbar pattern: scroll events come from the fake scrollbar element\n // Content area doesn't scroll - rows are positioned via transforms\n // This prevents blank viewport: old content stays until transforms are updated\n // Reuse gridRoot from earlier in this function\n const fauxScrollbar = gridRoot?.querySelector('.faux-vscroll') as HTMLElement;\n const rowsEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Store reference for scroll position reading in refreshVirtualWindow\n this._virtualization.container = fauxScrollbar ?? this;\n\n // Cache whether any plugin has scroll handlers (checked once during setup)\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n\n if (fauxScrollbar && rowsEl) {\n fauxScrollbar.addEventListener(\n 'scroll',\n () => {\n // Fast exit if no scroll processing needed\n if (!this._virtualization.enabled && !this.#hasScrollPlugins) return;\n\n const currentScrollTop = fauxScrollbar.scrollTop;\n const rowHeight = this._virtualization.rowHeight;\n\n // Bypass mode: all rows are rendered, just translate by scroll position\n // No need for virtual window calculations\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n rowsEl.style.transform = `translateY(${-currentScrollTop}px)`;\n } else {\n // Virtualized mode: calculate sub-pixel offset for smooth scrolling\n // Even-aligned start preserves zebra stripe parity\n // DOM nth-child(even) will always match data row parity\n const rawStart = Math.floor(currentScrollTop / rowHeight);\n const evenAlignedStart = rawStart - (rawStart % 2);\n const subPixelOffset = -(currentScrollTop - evenAlignedStart * rowHeight);\n rowsEl.style.transform = `translateY(${subPixelOffset}px)`;\n }\n\n // Batch content update with requestAnimationFrame\n // Old content stays visible with smooth offset until new content renders\n this.#pendingScrollTop = currentScrollTop;\n if (!this.#scrollRaf) {\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n if (this.#pendingScrollTop !== null) {\n this.#onScrollBatched(this.#pendingScrollTop);\n this.#pendingScrollTop = null;\n }\n });\n }\n },\n { passive: true, signal },\n );\n\n // Forward wheel events from content area to faux scrollbar\n // Without this, mouse wheel over content wouldn't scroll\n // Listen on .tbw-grid-content to capture wheel events from entire grid area\n // Note: gridRoot may already BE .tbw-grid-content when shell is active, so search from shadow root\n const gridContentEl = this.#shadow.querySelector('.tbw-grid-content') as HTMLElement;\n const scrollArea = this.#shadow.querySelector('.tbw-scroll-area') as HTMLElement;\n if (gridContentEl) {\n gridContentEl.addEventListener(\n 'wheel',\n (e: WheelEvent) => {\n // SHIFT+wheel or trackpad deltaX = horizontal scroll\n const isHorizontal = e.shiftKey || Math.abs(e.deltaX) > Math.abs(e.deltaY);\n\n if (isHorizontal && scrollArea) {\n const delta = e.shiftKey ? e.deltaY : e.deltaX;\n const { scrollLeft, scrollWidth, clientWidth } = scrollArea;\n const canScroll = (delta > 0 && scrollLeft < scrollWidth - clientWidth) || (delta < 0 && scrollLeft > 0);\n if (canScroll) {\n e.preventDefault();\n scrollArea.scrollLeft += delta;\n }\n } else if (!isHorizontal) {\n const { scrollTop, scrollHeight, clientHeight } = fauxScrollbar;\n const canScroll =\n (e.deltaY > 0 && scrollTop < scrollHeight - clientHeight) || (e.deltaY < 0 && scrollTop > 0);\n if (canScroll) {\n e.preventDefault();\n fauxScrollbar.scrollTop += e.deltaY;\n }\n }\n // If can't scroll, event bubbles to scroll the page\n },\n { passive: false, signal },\n );\n\n // Touch scrolling support for mobile devices\n // Supports both vertical (via faux scrollbar) and horizontal (via scroll area) scrolling\n // Includes momentum scrolling for natural \"flick\" behavior\n gridContentEl.addEventListener(\n 'touchstart',\n (e: TouchEvent) => {\n if (e.touches.length === 1) {\n // Cancel any ongoing momentum animation\n if (this.#momentumRaf) {\n cancelAnimationFrame(this.#momentumRaf);\n this.#momentumRaf = 0;\n }\n\n this.#touchStartY = e.touches[0].clientY;\n this.#touchStartX = e.touches[0].clientX;\n this.#touchLastY = e.touches[0].clientY;\n this.#touchLastX = e.touches[0].clientX;\n this.#touchLastTime = performance.now();\n this.#touchScrollTop = fauxScrollbar.scrollTop;\n this.#touchScrollLeft = scrollArea?.scrollLeft ?? 0;\n this.#touchVelocityY = 0;\n this.#touchVelocityX = 0;\n }\n },\n { passive: true, signal },\n );\n\n gridContentEl.addEventListener(\n 'touchmove',\n (e: TouchEvent) => {\n if (\n e.touches.length === 1 &&\n this.#touchStartY !== null &&\n this.#touchStartX !== null &&\n this.#touchScrollTop !== null &&\n this.#touchScrollLeft !== null\n ) {\n const currentY = e.touches[0].clientY;\n const currentX = e.touches[0].clientX;\n const now = performance.now();\n\n const deltaY = this.#touchStartY - currentY;\n const deltaX = this.#touchStartX - currentX;\n\n // Calculate velocity for momentum scrolling\n if (this.#touchLastTime !== null && this.#touchLastY !== null && this.#touchLastX !== null) {\n const dt = now - this.#touchLastTime;\n if (dt > 0) {\n // Velocity in pixels per millisecond\n this.#touchVelocityY = (this.#touchLastY - currentY) / dt;\n this.#touchVelocityX = (this.#touchLastX - currentX) / dt;\n }\n }\n this.#touchLastY = currentY;\n this.#touchLastX = currentX;\n this.#touchLastTime = now;\n\n // Check if grid can scroll in the requested directions\n const { scrollTop, scrollHeight, clientHeight } = fauxScrollbar;\n const maxScrollY = scrollHeight - clientHeight;\n const canScrollVertically = (deltaY > 0 && scrollTop < maxScrollY) || (deltaY < 0 && scrollTop > 0);\n\n let canScrollHorizontally = false;\n if (scrollArea) {\n const { scrollLeft, scrollWidth, clientWidth } = scrollArea;\n const maxScrollX = scrollWidth - clientWidth;\n canScrollHorizontally = (deltaX > 0 && scrollLeft < maxScrollX) || (deltaX < 0 && scrollLeft > 0);\n }\n\n // Apply scroll if grid can scroll in that direction\n if (canScrollVertically) {\n fauxScrollbar.scrollTop = this.#touchScrollTop + deltaY;\n }\n if (canScrollHorizontally && scrollArea) {\n scrollArea.scrollLeft = this.#touchScrollLeft + deltaX;\n }\n\n // Only prevent page scroll when we actually scrolled the grid\n if (canScrollVertically || canScrollHorizontally) {\n e.preventDefault();\n }\n }\n },\n { passive: false, signal },\n );\n\n gridContentEl.addEventListener(\n 'touchend',\n () => {\n // Start momentum scrolling if there's significant velocity\n const minVelocity = 0.1; // pixels per ms threshold\n if (Math.abs(this.#touchVelocityY) > minVelocity || Math.abs(this.#touchVelocityX) > minVelocity) {\n this.#startMomentumScroll(fauxScrollbar, scrollArea);\n }\n\n this.#touchStartY = null;\n this.#touchStartX = null;\n this.#touchScrollTop = null;\n this.#touchScrollLeft = null;\n this.#touchLastY = null;\n this.#touchLastX = null;\n this.#touchLastTime = null;\n },\n { passive: true, signal },\n );\n }\n }\n\n // Central mouse event handling for plugins (uses signal for automatic cleanup)\n this.#shadow.addEventListener('mousedown', (e) => this.#handleMouseDown(e as MouseEvent), { signal });\n\n // Track global mousemove/mouseup for drag operations (uses signal for automatic cleanup)\n document.addEventListener('mousemove', (e: MouseEvent) => this.#handleMouseMove(e), { signal });\n document.addEventListener('mouseup', (e: MouseEvent) => this.#handleMouseUp(e), { signal });\n\n if (this._virtualization.enabled) {\n requestAnimationFrame(() => this.refreshVirtualWindow(true));\n }\n\n // Determine row height for virtualization:\n // 1. User-configured rowHeight in gridConfig takes precedence\n // 2. Otherwise, measure actual row height from DOM (respects CSS variable --tbw-row-height)\n const userRowHeight = this.#effectiveConfig.rowHeight;\n if (userRowHeight && userRowHeight > 0) {\n this._virtualization.rowHeight = userRowHeight;\n } else {\n // Measure after first render to pick up CSS-defined row height\n requestAnimationFrame(() => {\n const firstRow = this._bodyEl?.querySelector('.data-grid-row');\n if (firstRow) {\n const measuredHeight = (firstRow as HTMLElement).getBoundingClientRect().height;\n if (measuredHeight > 0) {\n this._virtualization.rowHeight = measuredHeight;\n this.refreshVirtualWindow(true);\n }\n }\n });\n }\n\n // Resize observer to refresh virtualization and maintain focus when viewport size changes\n if (this._virtualization.viewportEl) {\n this.#resizeObserver = new ResizeObserver(() => {\n // Debounce with RAF to avoid excessive recalculations\n if (!this.#scrollRaf) {\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n this.refreshVirtualWindow(true);\n\n // Ensure focused cell remains visible after resize\n // (viewport size may have changed, pushing the focused cell out of view)\n ensureCellVisible(this as any);\n });\n }\n });\n this.#resizeObserver.observe(this._virtualization.viewportEl);\n }\n\n // Initialize ARIA selection state\n queueMicrotask(() => this.#updateAriaSelection());\n\n requestAnimationFrame(() => requestAnimationFrame(() => this.#readyResolve?.()));\n }\n\n // ---------------- Event Emitters ----------------\n #emit<D>(eventName: string, detail: D): void {\n this.dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true, composed: true }));\n }\n\n _emitCellCommit(detail: CellCommitDetail<T>): void {\n this.#emit('cell-commit', detail);\n }\n\n _emitRowCommit(detail: RowCommitDetail<T>): void {\n this.#emit('row-commit', detail);\n }\n\n _emitSortChange(detail: SortChangeDetail): void {\n this.#emit('sort-change', detail);\n }\n\n _emitColumnResize(detail: ColumnResizeDetail): void {\n this.#emit('column-resize', detail);\n }\n\n _emitActivateCell(detail: ActivateCellDetail): void {\n this.#emit('activate-cell', detail);\n }\n\n /** Update ARIA selection attributes on rendered rows/cells */\n #updateAriaSelection(): void {\n // Mark active row and cell with aria-selected\n const rows = this._bodyEl?.querySelectorAll('.data-grid-row');\n rows?.forEach((row, rowIdx) => {\n const isActiveRow = rowIdx === this._focusRow;\n row.setAttribute('aria-selected', String(isActiveRow));\n row.querySelectorAll('.cell').forEach((cell, colIdx) => {\n (cell as HTMLElement).setAttribute('aria-selected', String(isActiveRow && colIdx === this._focusCol));\n });\n });\n }\n\n // ---------------- Watch Handlers ----------------\n #onFitChanged(): void {\n if (!this.#connected) return;\n this.#mergeEffectiveConfig();\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed') {\n this.__didInitialAutoSize = false;\n autoSizeColumns(this);\n } else {\n this._columns.forEach((c: any) => {\n if (!c.__userResized && c.__autoSized) delete c.width;\n });\n updateTemplate(this);\n }\n }\n\n #onEditModeChanged(): void {\n if (!this.#connected) return;\n this.#mergeEffectiveConfig();\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n this.refreshVirtualWindow(true);\n }\n\n #onRowsChanged(): void {\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n this.#rebuildRowModel();\n // If no columns configured from any source, trigger full setup so inference runs\n // Check _columns (processed columns) or effectiveConfig.columns to avoid re-init when columns exist\n const hasColumns =\n this._columns.length > 0 ||\n (Array.isArray(this.#effectiveConfig?.columns) && this.#effectiveConfig.columns.length > 0) ||\n (Array.isArray(this.#columns) && this.#columns.length > 0);\n if (!hasColumns) {\n this.#setup();\n } else {\n // Process columns hooks (tree plugin needs this after row data changes)\n this.#processColumns();\n this.refreshVirtualWindow(true);\n }\n }\n\n #onColsChanged(): void {\n // Invalidate caches that depend on column configuration\n invalidateCellCache(this);\n\n // Re-merge config and setup - _columns will be set through effectiveConfig\n if (this.#connected) {\n this.#mergeEffectiveConfig();\n this.#setup();\n }\n }\n\n #onGridConfigChanged(): void {\n if (!this.#connected) return;\n this.#mergeEffectiveConfig();\n this.#updatePluginConfigs(); // Sync plugin configs with new grid config\n this.#rebuildRowModel();\n this.#processColumns(); // Process columns after rows for tree plugin\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n }\n\n #processColumns(): void {\n // Let plugins process visible columns (column grouping, etc.)\n // Start from base columns (before any plugin transformation) - like #rebuildRowModel uses #rows\n if (this.#pluginManager) {\n // Use base columns as source of truth, falling back to current _columns if not set\n const sourceColumns = this.#baseColumns.length > 0 ? this.#baseColumns : this._columns;\n const visibleCols = sourceColumns.filter((c) => !c.hidden);\n const hiddenCols = sourceColumns.filter((c) => c.hidden);\n const processedColumns = this.#pluginManager.processColumns([...visibleCols] as any[]);\n\n // If plugins modified visible columns, update them\n if (processedColumns !== visibleCols) {\n // Build a map of processed columns by field for quick lookup\n const processedMap = new Map(processedColumns.map((c: any, i: number) => [c.field, { col: c, order: i }]));\n\n // Check if this is a complete column replacement (e.g., pivot mode)\n // If no processed columns match original columns, use processed columns directly\n const hasMatchingFields = visibleCols.some((c) => processedMap.has(c.field));\n\n if (!hasMatchingFields && processedColumns.length > 0) {\n // Complete replacement: use processed columns directly (pivot mode)\n // Preserve hidden columns at the end\n this._columns = [...processedColumns, ...hiddenCols] as ColumnInternal<T>[];\n } else {\n // Plugins returned original fields (possibly modified) - merge back\n // Use source columns as base, not current _columns\n const updatedColumns = sourceColumns.map((c) => {\n if (c.hidden) return c; // Keep hidden columns unchanged\n const processed = processedMap.get(c.field);\n return processed ? processed.col : c;\n });\n\n this._columns = updatedColumns as ColumnInternal<T>[];\n }\n } else {\n // Plugins returned columns unchanged, but we may need to restore from base\n this._columns = [...sourceColumns] as ColumnInternal<T>[];\n }\n }\n }\n\n /** Recompute row model via plugin hooks (grouping, tree, filtering, etc.). */\n #rebuildRowModel(): void {\n // Invalidate cell display value cache - rows are changing\n invalidateCellCache(this);\n\n // Start fresh from original rows (plugins will transform them)\n const originalRows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Let plugins process rows (row grouping, tree, filtering, etc.)\n // Plugins can transform the rows array, adding markers like __isGroupRow\n // The renderRow hook will handle rendering specialized row types\n const processedRows = this.#pluginManager?.processRows(originalRows) ?? originalRows;\n\n // Store processed rows for rendering\n // Note: processedRows may contain group markers that plugins handle via renderRow hook\n this._rows = processedRows as T[];\n }\n\n /**\n * Build the canonical effective configuration by merging all input sources.\n *\n * This is the **single source of truth** for the grid's configuration.\n * All inputs (gridConfig, light DOM, individual props) converge here.\n *\n * **Precedence (lowest → highest):**\n * 1. `gridConfig` property - base config object\n * 2. Light DOM `<tbw-grid-column>` elements - declarative columns\n * 3. `columns` property - programmatic columns override\n * 4. Inferred columns - auto-detected from row data\n * 5. Individual props (`fitMode`, `editOn`) - convenience overrides\n *\n * After this method runs:\n * - `#effectiveConfig` contains the merged result\n * - `_columns` is NOT set here (done by #getColumnConfiguration + #processColumns)\n * - Plugins receive config via their attach() method\n */\n #mergeEffectiveConfig(): void {\n const base: GridConfig<T> = this.#gridConfig ? { ...this.#gridConfig } : {};\n let columns: ColumnConfig<T>[] = Array.isArray(base.columns) ? [...base.columns] : [];\n\n // Light DOM cached parse (if already parsed by columns pipeline); non-invasive merge (fill gaps only)\n const domCols: ColumnConfig<T>[] = ((this as any).__lightDomColumnsCache || []).map((c: ColumnConfig<T>) => ({\n ...c,\n }));\n if (domCols.length) {\n const map: Record<string, ColumnConfig<T>> = {};\n columns.forEach((c) => (map[(c as any).field] = c));\n domCols.forEach((c: any) => {\n const exist = map[c.field];\n if (!exist) {\n columns.push(c);\n map[c.field] = c;\n } else {\n if (c.header && !exist.header) exist.header = c.header;\n if (c.type && !exist.type) exist.type = c.type;\n exist.sortable = exist.sortable || c.sortable;\n if (c.resizable) exist.resizable = true;\n if (c.editable) exist.editable = true;\n }\n });\n }\n\n // Columns prop highest structural precedence\n if (this.#columns && (this.#columns as ColumnConfig<T>[]).length) {\n columns = [...(this.#columns as ColumnConfig<T>[])];\n }\n\n // Inference if still empty\n if ((!columns || columns.length === 0) && this._rows.length) {\n const result = inferColumns(this._rows as Record<string, unknown>[]);\n columns = result.columns as ColumnConfig<T>[];\n }\n\n if (columns.length) {\n // Apply per-column defaults (sortable/resizable default true unless explicitly false)\n columns.forEach((c) => {\n if (c.sortable === undefined) c.sortable = true;\n if (c.resizable === undefined) c.resizable = true;\n // Store original configured width for reset on double-click (only numeric widths)\n const internal = c as ColumnInternal<T>;\n if (internal.__originalWidth === undefined && typeof c.width === 'number') {\n internal.__originalWidth = c.width;\n }\n });\n // Preserve processed columns (with __compiledView etc.) if already set by #getColumnConfiguration\n // Only set base.columns if effectiveConfig.columns is empty or doesn't have compiled templates\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n const alreadyProcessed = existingCols?.some((c) => c.__compiledView || c.__compiledEditor);\n if (alreadyProcessed) {\n // Keep existing processed columns\n base.columns = existingCols as ColumnConfig<T>[];\n } else {\n base.columns = columns;\n }\n } else {\n // No new columns computed, but preserve existing if processed\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n if (existingCols?.some((c) => c.__compiledView || c.__compiledEditor)) {\n base.columns = existingCols as ColumnConfig<T>[];\n }\n }\n\n // Individual prop overrides (behavioral)\n if (this.#fitMode) base.fitMode = this.#fitMode;\n if (!base.fitMode) base.fitMode = 'stretch';\n if (this.#editOn) base.editOn = this.#editOn;\n\n // Apply rowHeight from config if specified\n if (base.rowHeight && base.rowHeight > 0) {\n this._virtualization.rowHeight = base.rowHeight;\n }\n\n // Store columnState from gridConfig if not already set\n if (base.columnState && !this.#initialColumnState) {\n this.#initialColumnState = base.columnState;\n }\n\n this.#effectiveConfig = base;\n // Note: _columns is a getter/setter for effectiveConfig.columns\n // #getColumnConfiguration() populates it, and we preserve those processed columns above\n // Plugins (like ReorderPlugin) modify effectiveConfig.columns via the _columns setter\n\n // If fixed mode and width not specified: assign default 80px\n if (base.fitMode === 'fixed') {\n this._columns.forEach((c) => {\n if (c.width == null) (c as ColumnConfig<T>).width = 80;\n });\n }\n\n // Apply animation configuration to CSS variables\n this.#applyAnimationConfig();\n }\n\n /**\n * Apply animation configuration to CSS custom properties on the host element.\n * This makes the grid's animation settings available to plugins via CSS variables.\n */\n #applyAnimationConfig(): void {\n const config: AnimationConfig = {\n ...DEFAULT_ANIMATION_CONFIG,\n ...this.#effectiveConfig.animation,\n };\n\n // Resolve animation mode\n const mode = config.mode ?? 'reduced-motion';\n let enabled: 0 | 1 = 1;\n\n if (mode === false || mode === 'off') {\n enabled = 0;\n } else if (mode === true || mode === 'on') {\n enabled = 1;\n }\n // For 'reduced-motion', we leave enabled=1 and let CSS @media query handle it\n\n // Set CSS custom properties\n this.style.setProperty('--tbw-animation-duration', `${config.duration}ms`);\n this.style.setProperty('--tbw-animation-easing', config.easing ?? 'ease-out');\n this.style.setProperty('--tbw-animation-enabled', String(enabled));\n\n // Set data attribute for mode-based CSS selectors\n this.dataset.animationMode = typeof mode === 'boolean' ? (mode ? 'on' : 'off') : mode;\n }\n\n // ---------------- Delegate Wrappers ----------------\n #renderVisibleRows(start: number, end: number, epoch = this.__rowRenderEpoch): void {\n // Use cached hook to avoid creating closures on every render (hot path optimization)\n if (!this.#renderRowHook) {\n this.#renderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number): boolean => {\n return this.#pluginManager?.renderRow(row, rowEl, rowIndex) ?? false;\n };\n }\n renderVisibleRows(this as any, start, end, epoch, this.#renderRowHook);\n }\n\n // ---------------- Core Helpers ----------------\n #setup(): void {\n if (!this.isConnected) return;\n if (!this._headerRowEl || !this._bodyEl) {\n return;\n }\n\n // Seed effectiveConfig.columns from config sources before getColumnConfiguration\n // This ensures columns from gridConfig/columns prop are available for merging with light DOM\n // Preserve hidden state from existing columns (visibility is runtime state)\n const configCols = (this.#gridConfig?.columns || this.#columns || []) as ColumnConfig<T>[];\n if (configCols.length) {\n // Preserve hidden state from existing effectiveConfig.columns\n const existingHiddenMap = new Map(this._columns.filter((c) => c.hidden).map((c) => [c.field, true]));\n const seeded = configCols.map((c) => ({\n ...c,\n hidden: existingHiddenMap.get(c.field) ?? c.hidden,\n }));\n this._columns = seeded as ColumnInternal<T>[];\n }\n\n getColumnConfiguration(this);\n this.#mergeEffectiveConfig();\n this.#updatePluginConfigs(); // Sync plugin configs (including auto-detection) before processing\n\n // Store base columns before plugin transformation (like #rows for row processing)\n this.#baseColumns = [...this._columns];\n\n this.#rebuildRowModel(); // Runs processRows hooks (must run before processColumns for tree plugin)\n this.#processColumns(); // Runs processColumns hooks\n\n // Apply initial column state (from gridConfig.columnState or columnState setter)\n if (this.#initialColumnState) {\n const state = this.#initialColumnState;\n this.#initialColumnState = undefined; // Clear to avoid re-applying\n this.#applyColumnStateInternal(state);\n }\n\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed' && !this.__didInitialAutoSize) {\n requestAnimationFrame(() => autoSizeColumns(this));\n }\n\n // Ensure legacy inline grid styles are cleared from container\n if (this._bodyEl) {\n this._bodyEl.style.display = '';\n this._bodyEl.style.gridTemplateColumns = '';\n }\n\n // Run plugin afterRender hooks (column groups, sticky, etc.)\n queueMicrotask(() => this.#pluginManager?.afterRender());\n }\n\n /** Internal method to apply column state without triggering setup loop */\n #applyColumnStateInternal(state: GridColumnState): void {\n // Get all columns from effectiveConfig (single source of truth)\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n applyColumnState(this, state, allCols, plugins);\n\n // Update hidden property on columns based on state\n for (const colState of state.columns) {\n const col = allCols.find((c) => c.field === colState.field);\n if (col) {\n col.hidden = !colState.visible;\n }\n }\n }\n\n #onScrollBatched(scrollTop: number): void {\n // Faux scrollbar pattern: content never scrolls, just update transforms\n // Old content stays visible until new transforms are applied\n this.refreshVirtualWindow(false);\n\n // Let plugins reapply visual state to recycled DOM elements\n this.#pluginManager?.onScrollRender();\n\n // Dispatch to plugins (using cached flag)\n if (this.#hasScrollPlugins) {\n const fauxScrollbar = this._virtualization.container;\n const scrollEvent: ScrollEvent = {\n scrollTop,\n scrollLeft: fauxScrollbar?.scrollLeft ?? 0,\n scrollHeight: fauxScrollbar?.scrollHeight ?? 0,\n scrollWidth: fauxScrollbar?.scrollWidth ?? 0,\n clientHeight: fauxScrollbar?.clientHeight ?? 0,\n clientWidth: fauxScrollbar?.clientWidth ?? 0,\n originalEvent: new Event('scroll'),\n };\n this.#pluginManager?.onScroll(scrollEvent);\n }\n }\n\n /**\n * Find the header row element in the shadow DOM.\n * Used by plugins that need to access header cells for styling or measurement.\n * @internal Plugin API\n */\n findHeaderRow(): HTMLElement {\n return this.#shadow.querySelector('.header-row') as HTMLElement;\n }\n\n /**\n * Find a rendered row element by its data row index.\n * Returns null if the row is not currently rendered (virtualized out of view).\n * Used by plugins that need to access specific row elements for styling or measurement.\n * @internal Plugin API\n * @param rowIndex - The data row index (not the DOM position)\n */\n findRenderedRowElement(rowIndex: number): HTMLElement | null {\n return (\n (Array.from(this._bodyEl.querySelectorAll('.data-grid-row')) as HTMLElement[]).find((r) => {\n const cell = r.querySelector('.cell[data-row]');\n return cell && Number(cell.getAttribute('data-row')) === rowIndex;\n }) || null\n );\n }\n\n /**\n * Dispatch a cell click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchCellClick(event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement): boolean {\n const row = this._rows[rowIndex];\n const col = this._columns[colIndex];\n if (!row || !col) return false;\n\n const cellClickEvent: CellClickEvent = {\n row,\n rowIndex,\n colIndex,\n field: col.field,\n value: (row as any)[col.field],\n cellEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onCellClick(cellClickEvent) ?? false;\n }\n\n /**\n * Dispatch a row click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchRowClick(event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement): boolean {\n if (!row) return false;\n\n const rowClickEvent: RowClickEvent = {\n rowIndex,\n row,\n rowEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onRowClick(rowClickEvent) ?? false;\n }\n\n /**\n * Dispatch a header click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchHeaderClick(event: MouseEvent, colIndex: number, headerEl: HTMLElement): boolean {\n const col = this._columns[colIndex];\n if (!col) return false;\n\n const headerClickEvent: HeaderClickEvent = {\n colIndex,\n field: col.field,\n column: col,\n headerEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onHeaderClick(headerClickEvent) ?? false;\n }\n\n /**\n * Dispatch a keyboard event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchKeyDown(event: KeyboardEvent): boolean {\n return this.#pluginManager?.onKeyDown(event) ?? false;\n }\n\n /**\n * Get horizontal scroll boundary offsets from plugins.\n * Used by keyboard navigation to ensure focused cells are fully visible\n * when plugins like pinned columns obscure part of the scroll area.\n */\n _getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n return this.#pluginManager?.getHorizontalScrollOffsets(rowEl, focusedCell) ?? { left: 0, right: 0 };\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n * @internal Plugin API\n *\n * @example\n * // Check if any plugin vetoes moving a column\n * const responses = grid.queryPlugins<boolean>({ type: PLUGIN_QUERIES.CAN_MOVE_COLUMN, context: column });\n * const canMove = !responses.includes(false);\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n return this.#pluginManager?.queryPlugins<T>(query) ?? [];\n }\n\n /**\n * Build a CellMouseEvent from a native MouseEvent.\n * Extracts cell/row information from the event target.\n */\n #buildCellMouseEvent(e: MouseEvent, type: 'mousedown' | 'mousemove' | 'mouseup'): CellMouseEvent {\n // For document-level events (mousemove/mouseup during drag), e.target won't be inside shadow DOM.\n // Use composedPath to find elements inside shadow roots, or fall back to elementFromPoint.\n let target: Element | null = null;\n\n // composedPath gives us the full path including shadow DOM elements\n const path = e.composedPath?.() as Element[] | undefined;\n if (path && path.length > 0) {\n target = path[0];\n } else {\n target = e.target as Element;\n }\n\n // If target is still not inside our shadow root (e.g., for document-level events),\n // use elementFromPoint to find the actual element under the mouse\n if (target && !this.#shadow.contains(target)) {\n const elAtPoint = this.#shadow.elementFromPoint(e.clientX, e.clientY);\n if (elAtPoint) {\n target = elAtPoint;\n }\n }\n\n // Cells have data-col and data-row attributes\n const cellEl = target?.closest?.('[data-col]') as HTMLElement | null;\n const rowEl = target?.closest?.('.data-grid-row') as HTMLElement | null;\n const headerEl = target?.closest?.('.header-row') as HTMLElement | null;\n\n let rowIndex: number | undefined;\n let colIndex: number | undefined;\n let row: T | undefined;\n let field: string | undefined;\n let value: unknown;\n let column: any;\n\n if (cellEl) {\n // Get indices from cell attributes\n rowIndex = parseInt(cellEl.getAttribute('data-row') ?? '-1', 10);\n colIndex = parseInt(cellEl.getAttribute('data-col') ?? '-1', 10);\n if (rowIndex >= 0 && colIndex >= 0) {\n row = this._rows[rowIndex];\n column = this._columns[colIndex];\n field = column?.field;\n value = row && field ? (row as any)[field] : undefined;\n }\n }\n\n return {\n type,\n row,\n rowIndex: rowIndex !== undefined && rowIndex >= 0 ? rowIndex : undefined,\n colIndex: colIndex !== undefined && colIndex >= 0 ? colIndex : undefined,\n field,\n value,\n column,\n originalEvent: e,\n cellElement: cellEl ?? undefined,\n rowElement: rowEl ?? undefined,\n isHeader: !!headerEl,\n cell:\n rowIndex !== undefined && colIndex !== undefined && rowIndex >= 0 && colIndex >= 0\n ? { row: rowIndex, col: colIndex }\n : undefined,\n };\n }\n\n /**\n * Apply momentum scrolling animation after touch release.\n * Decelerates smoothly until velocity drops below threshold.\n */\n #startMomentumScroll(fauxScrollbar: HTMLElement, scrollArea: HTMLElement | null): void {\n const friction = 0.95; // Deceleration factor per frame\n const minVelocity = 0.01; // Stop threshold in px/ms\n\n const animate = () => {\n // Apply friction\n this.#touchVelocityY *= friction;\n this.#touchVelocityX *= friction;\n\n // Convert velocity (px/ms) to per-frame scroll amount (~16ms per frame)\n const scrollY = this.#touchVelocityY * 16;\n const scrollX = this.#touchVelocityX * 16;\n\n // Apply scroll if above threshold\n if (Math.abs(this.#touchVelocityY) > minVelocity) {\n fauxScrollbar.scrollTop += scrollY;\n }\n if (Math.abs(this.#touchVelocityX) > minVelocity && scrollArea) {\n scrollArea.scrollLeft += scrollX;\n }\n\n // Continue animation if still moving\n if (Math.abs(this.#touchVelocityY) > minVelocity || Math.abs(this.#touchVelocityX) > minVelocity) {\n this.#momentumRaf = requestAnimationFrame(animate);\n } else {\n this.#momentumRaf = 0;\n }\n };\n\n this.#momentumRaf = requestAnimationFrame(animate);\n }\n\n /**\n * Handle mousedown events and dispatch to plugin system.\n */\n #handleMouseDown(e: MouseEvent): void {\n const event = this.#buildCellMouseEvent(e, 'mousedown');\n const handled = this.#pluginManager?.onCellMouseDown(event) ?? false;\n\n // If any plugin handled mousedown, start tracking for drag\n if (handled) {\n this.#isDragging = true;\n }\n }\n\n /**\n * Handle mousemove events (only when dragging).\n */\n #handleMouseMove(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mousemove');\n this.#pluginManager?.onCellMouseMove(event);\n }\n\n /**\n * Handle mouseup events.\n */\n #handleMouseUp(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mouseup');\n this.#pluginManager?.onCellMouseUp(event);\n this.#isDragging = false;\n }\n\n // API consumed by internal utils (rows.ts)\n get changedRows(): T[] {\n return Array.from(this._changedRowIndices).map((i) => this._rows[i]);\n }\n\n get changedRowIndices(): number[] {\n return Array.from(this._changedRowIndices);\n }\n\n async resetChangedRows(silent?: boolean): Promise<void> {\n this._changedRowIndices.clear();\n if (!silent) {\n this.#emit('changed-rows-reset', { rows: this.changedRows, indices: this.changedRowIndices });\n }\n this._rowPool.forEach((r) => r.classList.remove('changed'));\n }\n\n async beginBulkEdit(rowIndex: number): Promise<void> {\n // Check if any columns are editable - if not, skip edit mode entirely\n const hasEditableColumn = this._columns.some((col) => (col as ColumnInternal<T>).editable);\n if (!hasEditableColumn) return;\n\n const rowData = this._rows[rowIndex];\n startRowEdit(this, rowIndex, rowData);\n\n // Enter edit mode on all editable cells in the row (same as click/dblclick)\n const rowEl = this.findRenderedRowElement?.(rowIndex);\n if (rowEl) {\n Array.from(rowEl.children).forEach((cell, i) => {\n // Use visibleColumns to match the cell index - _columns may include hidden columns\n const col = this._visibleColumns[i] as ColumnInternal<T> | undefined;\n if (col?.editable) {\n const cellEl = cell as HTMLElement;\n if (!cellEl.classList.contains('editing')) {\n inlineEnterEdit(this as unknown as InternalGrid, rowData, rowIndex, col, cellEl);\n }\n }\n });\n\n // Focus the editor in the focused cell\n queueMicrotask(() => {\n const targetCell = rowEl.querySelector(`.cell[data-col=\"${this._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 async commitActiveRowEdit(): Promise<void> {\n if (this._activeEditRows !== -1) {\n exitRowEdit(this, this._activeEditRows, false);\n }\n }\n\n async ready(): Promise<void> {\n return this.#readyPromise;\n }\n\n async forceLayout(): Promise<void> {\n this.#setup();\n await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));\n }\n\n /** Public method: returns a frozen snapshot of the merged effective configuration */\n async getConfig(): Promise<Readonly<GridConfig<T>>> {\n return Object.freeze({ ...(this.#effectiveConfig || {}) });\n }\n\n // ---------------- Column Visibility API ----------------\n\n /**\n * Set the visibility of a column.\n * @param field - The field name of the column\n * @param visible - Whether the column should be visible\n * @returns True if visibility was changed, false if column not found or locked\n */\n setColumnVisible(field: string, visible: boolean): boolean {\n // Find the column in effectiveConfig.columns (includes hidden columns)\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n const col = allCols?.find((c) => c.field === field);\n\n // If column not found, cannot change visibility\n if (!col) return false;\n\n // Check lockVisible - cannot hide locked columns\n if (!visible && col.lockVisible) return false;\n\n // Check if at least one column would remain visible\n if (!visible) {\n const currentVisible = (allCols ?? []).filter((c) => !c.hidden && c.field !== field).length;\n if (currentVisible === 0) return false;\n }\n\n const wasHidden = !!col.hidden;\n const willBeHidden = !visible;\n\n // Only refresh if visibility actually changed\n if (wasHidden !== willBeHidden) {\n // Update the hidden property on the column in effectiveConfig\n col.hidden = willBeHidden;\n\n // Emit event for consumer preference saving\n this.#emit('column-visibility', {\n field,\n visible,\n visibleColumns: (allCols ?? []).filter((c) => !c.hidden).map((c) => c.field),\n });\n\n // Clear row pool to force complete rebuild with new column count\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n\n // Re-setup to rebuild columns with updated visibility\n this.#setup();\n\n // Trigger state change after visibility change\n this.requestStateChange();\n return true;\n }\n return false;\n }\n\n /**\n * Toggle the visibility of a column.\n * @param field - The field name of the column\n * @returns True if visibility was toggled, false if column not found or locked\n */\n toggleColumnVisibility(field: string): boolean {\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n const col = allCols?.find((c) => c.field === field);\n const isCurrentlyHidden = !!col?.hidden;\n return this.setColumnVisible(field, isCurrentlyHidden);\n }\n\n /**\n * Check if a column is currently visible.\n * @param field - The field name of the column\n * @returns True if visible, false if hidden or not found\n */\n isColumnVisible(field: string): boolean {\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n const col = allCols?.find((c) => c.field === field);\n return col ? !col.hidden : false;\n }\n\n /**\n * Show all columns.\n */\n showAllColumns(): void {\n const allCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n const hasHidden = allCols?.some((c) => c.hidden);\n if (!hasHidden) return;\n\n // Clear hidden flag on all columns\n allCols?.forEach((c) => {\n c.hidden = false;\n });\n\n this.#emit('column-visibility', {\n visibleColumns: (allCols ?? []).map((c) => c.field),\n });\n\n // Clear row pool to force complete rebuild with new column count\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n\n this.#setup();\n\n // Trigger state change after visibility change\n this.requestStateChange();\n }\n\n /**\n * Get list of all column fields (including hidden).\n * Returns columns reflecting current display order (after reordering).\n * Hidden columns are interleaved at their original relative positions.\n * @returns Array of all field names with their visibility status\n */\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n // effectiveConfig.columns is the single source of truth\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n\n // Return all columns with their current visibility state\n return allCols.map((c) => ({\n field: c.field,\n header: c.header || c.field,\n visible: !c.hidden,\n lockVisible: c.lockVisible,\n }));\n }\n\n /**\n * Reorder columns according to the specified field order.\n * This directly updates _columns in place without going through processColumns.\n * @param order - Array of field names in the desired order\n */\n setColumnOrder(order: string[]): void {\n if (!order.length) return;\n\n const columnMap = new Map<string, ColumnInternal<T>>(this._columns.map((c) => [c.field as string, c]));\n const reordered: ColumnInternal<T>[] = [];\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 this._columns = reordered;\n\n // Re-render with new order\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n }\n\n /**\n * Get the current column order as an array of field names.\n * @returns Array of field names in display order\n */\n getColumnOrder(): string[] {\n return this._columns.map((c) => c.field);\n }\n\n // ---------------- Column State API ----------------\n\n /**\n * Get the current column state, including order, width, visibility, sort, and plugin state.\n * Returns a serializable object suitable for localStorage or database storage.\n */\n getColumnState(): GridColumnState {\n const plugins = this.#pluginManager?.getAll() ?? [];\n return collectColumnState(this, plugins as BaseGridPlugin[]);\n }\n\n /**\n * Set the column state, restoring order, width, visibility, sort, and plugin state.\n * Use this to restore previously saved column state.\n */\n set columnState(state: GridColumnState | undefined) {\n if (!state) return;\n\n // Store for use after initialization if called before ready\n this.#initialColumnState = state;\n\n // If already initialized, apply immediately\n if (this.#initialized) {\n this.#applyColumnState(state);\n }\n }\n\n /**\n * Get the current column state.\n */\n get columnState(): GridColumnState | undefined {\n return this.getColumnState();\n }\n\n /**\n * Apply column state internally.\n */\n #applyColumnState(state: GridColumnState): void {\n // Clear hidden flags before applying state\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n this.#applyColumnStateInternal(state);\n\n // Re-setup to apply changes\n this.#setup();\n }\n\n /**\n * Request a state change event to be emitted.\n * Called internally after resize, reorder, visibility, or sort changes.\n * Plugins should call this after changing their state.\n * The event is debounced to avoid excessive events during drag operations.\n * @internal Plugin API\n */\n requestStateChange(): void {\n if (!this.#stateChangeHandler) {\n this.#stateChangeHandler = createStateChangeHandler(\n this,\n () => (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[],\n (state) => this.#emit('column-state-change', state),\n );\n }\n this.#stateChangeHandler();\n }\n\n /**\n * Reset column state to initial configuration.\n * Clears all user modifications (order, width, visibility, sort).\n */\n resetColumnState(): void {\n // Clear initial state\n this.#initialColumnState = undefined;\n\n // Clear hidden flag on all columns\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n // Reset sort state\n this._sortState = null;\n this.__originalOrder = [];\n\n // Re-initialize columns from config\n this.#mergeEffectiveConfig();\n this.#setup();\n\n // Notify plugins to reset their state\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n // Pass empty state to indicate reset\n for (const col of this._columns) {\n plugin.applyColumnState(col.field, {\n field: col.field,\n order: 0,\n visible: true,\n });\n }\n }\n }\n\n // Emit state change\n this.requestStateChange();\n }\n\n // ---------------- Shell / Tool Panel API ----------------\n // These methods delegate to ShellController for implementation.\n // The controller encapsulates all tool panel logic while grid.ts\n // exposes the public API surface.\n\n /** Check if the tool panel is currently open. */\n get isToolPanelOpen(): boolean {\n return this.#shellController.isPanelOpen;\n }\n\n /**\n * Get the currently active tool panel ID, or null if none is open.\n * @deprecated Use isToolPanelOpen and expandedToolPanelSections instead.\n */\n get activeToolPanel(): string | null {\n return this.#shellController.activePanel;\n }\n\n /** Get the IDs of expanded accordion sections. */\n get expandedToolPanelSections(): string[] {\n return this.#shellController.expandedSections;\n }\n\n /** Open the tool panel (accordion view with all registered panels). */\n openToolPanel(): void {\n this.#shellController.openToolPanel();\n }\n\n /** Close the tool panel. */\n closeToolPanel(): void {\n this.#shellController.closeToolPanel();\n }\n\n /** Toggle the tool panel open/closed. */\n toggleToolPanel(): void {\n this.#shellController.toggleToolPanel();\n }\n\n /** Toggle an accordion section expanded/collapsed. */\n toggleToolPanelSection(sectionId: string): void {\n this.#shellController.toggleToolPanelSection(sectionId);\n }\n\n /** Get registered tool panel definitions. */\n getToolPanels(): ToolPanelDefinition[] {\n return this.#shellController.getToolPanels();\n }\n\n /** Register a custom tool panel (without creating a plugin). */\n registerToolPanel(panel: ToolPanelDefinition): void {\n this.#shellController.registerToolPanel(panel);\n }\n\n /** Unregister a custom tool panel. */\n unregisterToolPanel(panelId: string): void {\n this.#shellController.unregisterToolPanel(panelId);\n }\n\n /** Get registered header content definitions. */\n getHeaderContents(): HeaderContentDefinition[] {\n return this.#shellController.getHeaderContents();\n }\n\n /** Register custom header content (without creating a plugin). */\n registerHeaderContent(content: HeaderContentDefinition): void {\n this.#shellController.registerHeaderContent(content);\n }\n\n /** Unregister custom header content. */\n unregisterHeaderContent(contentId: string): void {\n this.#shellController.unregisterHeaderContent(contentId);\n }\n\n /** Get all registered toolbar buttons. */\n getToolbarButtons(): ToolbarButtonInfo[] {\n return this.#shellController.getToolbarButtons();\n }\n\n /** Register a custom toolbar button programmatically. */\n registerToolbarButton(button: ToolbarButtonConfig): void {\n this.#shellController.registerToolbarButton(button);\n }\n\n /** Unregister a custom toolbar button. */\n unregisterToolbarButton(buttonId: string): void {\n this.#shellController.unregisterToolbarButton(buttonId);\n }\n\n /** Enable/disable a toolbar button by ID. */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): void {\n this.#shellController.setToolbarButtonDisabled(buttonId, disabled);\n }\n\n /**\n * Re-parse light DOM shell elements and refresh shell header.\n * Call this after dynamically modifying <tbw-grid-header> children.\n */\n refreshShellHeader(): void {\n // Re-parse light DOM\n parseLightDomShell(this, this.#shellState);\n\n // Re-render the entire grid (shell structure may change)\n this.#render();\n this.#afterConnect();\n }\n\n // ---------------- Virtual Window ----------------\n /**\n * Core virtualization routine. Chooses between bypass (small datasets), grouped window rendering,\n * or standard row window rendering.\n * @internal Plugin API\n */\n refreshVirtualWindow(force = false): void {\n if (!this._bodyEl) return;\n\n const totalRows = this._rows.length;\n\n if (!this._virtualization.enabled) {\n this.#renderVisibleRows(0, totalRows);\n this.#pluginManager?.afterRender();\n return;\n }\n\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n this._virtualization.start = 0;\n this._virtualization.end = totalRows;\n // Only reset transform on force refresh (initial render, data change)\n // Don't reset on scroll-triggered updates - the scroll handler manages transforms\n if (force) {\n this._bodyEl.style.transform = 'translateY(0px)';\n }\n this.#renderVisibleRows(0, totalRows, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n if (this._virtualization.totalHeightEl) {\n // The faux-vscroll height includes the header, but rows-viewport doesn't.\n // viewportEl.clientHeight already reflects any reduction from horizontal scrollbar,\n // so heightDiff captures all vertical space differences.\n const fauxScrollHeight = this._virtualization.container?.clientHeight ?? 0;\n const viewportHeight = this._virtualization.viewportEl?.clientHeight ?? fauxScrollHeight;\n const heightDiff = fauxScrollHeight - viewportHeight;\n this._virtualization.totalHeightEl.style.height = `${totalRows * this._virtualization.rowHeight + heightDiff}px`;\n }\n // Set ARIA counts on inner grid element (not host, which may contain shell chrome)\n const innerGrid = this.#shadow.querySelector('.rows-body');\n innerGrid?.setAttribute('aria-rowcount', String(totalRows));\n innerGrid?.setAttribute('aria-colcount', String(this._visibleColumns.length));\n this.#pluginManager?.afterRender();\n return;\n }\n\n // --- Normal virtualization path with faux scrollbar pattern ---\n // Faux scrollbar provides scrollTop, viewport provides visible height\n const fauxScrollbar = this._virtualization.container ?? this;\n const viewportEl = this._virtualization.viewportEl ?? fauxScrollbar;\n const viewportHeight = viewportEl.clientHeight;\n const rowHeight = this._virtualization.rowHeight;\n const scrollTop = fauxScrollbar.scrollTop;\n\n // When plugins add extra height (e.g., expanded details), the scroll position\n // includes that extra height. We need to find the actual row at scrollTop\n // by iteratively accounting for cumulative extra heights.\n // This prevents jumping when scrolling past expanded content.\n let start = Math.floor(scrollTop / rowHeight);\n\n // Iteratively refine: the initial guess may be too high because scrollTop\n // includes extra heights from expanded rows before it. Adjust downward.\n let iterations = 0;\n const maxIterations = 10; // Prevent infinite loop\n while (iterations < maxIterations) {\n const extraHeightBefore = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const adjustedStart = Math.floor((scrollTop - extraHeightBefore) / rowHeight);\n if (adjustedStart >= start || adjustedStart < 0) break;\n start = adjustedStart;\n iterations++;\n }\n\n // Faux scrollbar pattern: calculate effective position for this start\n // With translateY(0), the first rendered row appears at viewport top\n // Round down to even number so DOM nth-child(even) always matches data row parity\n // This prevents zebra stripe flickering during scroll since rows shift in pairs\n start = start - (start % 2);\n if (start < 0) start = 0;\n\n // Allow plugins to extend the start index backwards\n // (e.g., to keep expanded detail rows visible as they scroll out)\n const pluginAdjustedStart = this.#pluginManager?.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginAdjustedStart !== undefined && pluginAdjustedStart < start) {\n start = pluginAdjustedStart;\n // Re-apply even alignment after plugin adjustment\n start = start - (start % 2);\n if (start < 0) start = 0;\n }\n\n // Faux pattern buffer: render 2 extra rows below for smooth edge transition\n // This is smaller than traditional overscan since sub-pixel offset handles smoothness\n // +1 extra to account for the even-alignment above potentially showing 1 more row at top\n const visibleCount = Math.ceil(viewportHeight / rowHeight) + 3;\n let end = start + visibleCount;\n if (end > totalRows) end = totalRows;\n\n this._virtualization.start = start;\n this._virtualization.end = end;\n\n // Height spacer for scrollbar\n // Add 1 extra row height to account for even-alignment: when we round down\n // from odd to even start, we need extra scroll range to reveal the last row\n // Also add footer height: faux-vscroll is outside .tbw-scroll-area so it doesn't\n // shrink when footer is present - we need extra spacer to scroll past the footer\n const footerEl = this.#shadow.querySelector('.tbw-footer') as HTMLElement;\n const footerHeight = footerEl?.offsetHeight ?? 0;\n // Add extra height from plugins (e.g., expanded master-detail rows)\n // This ensures the scrollbar range accounts for all content including expanded details\n const pluginExtraHeight = this.#pluginManager?.getExtraHeight() ?? 0;\n // Add horizontal scrollbar height: when horizontal scrollbar is visible in .tbw-scroll-area,\n // it takes space at the bottom that the faux vertical scrollbar doesn't account for.\n // Detect by comparing offsetHeight (includes scrollbar) vs clientHeight (excludes scrollbar).\n const scrollAreaEl = this.#shadow.querySelector('.tbw-scroll-area') as HTMLElement;\n const hScrollbarHeight = scrollAreaEl ? scrollAreaEl.offsetHeight - scrollAreaEl.clientHeight : 0;\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${\n totalRows * rowHeight + rowHeight + footerHeight + pluginExtraHeight + hScrollbarHeight\n }px`;\n }\n\n // Smooth scroll: apply offset for fluid motion\n // Since start is even-aligned, offset is distance from that aligned position\n // This creates smooth sliding while preserving zebra stripe parity\n // Account for extra heights (expanded details) before the start row\n const extraHeightBeforeStart = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const subPixelOffset = -(scrollTop - start * rowHeight - extraHeightBeforeStart);\n this._bodyEl.style.transform = `translateY(${subPixelOffset}px)`;\n\n this.#renderVisibleRows(start, end, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n\n // Set ARIA counts on inner grid element (not host, which may contain shell chrome)\n const innerGrid = this.#shadow.querySelector('.rows-body');\n innerGrid?.setAttribute('aria-rowcount', String(totalRows));\n innerGrid?.setAttribute('aria-colcount', String(this._visibleColumns.length));\n\n // Only run plugin afterRender hooks on force refresh (structural changes)\n // Skip on scroll-triggered renders for maximum performance\n if (force) {\n this.#pluginManager?.afterRender();\n }\n }\n\n // ---------------- Render ----------------\n #render(): void {\n // Parse light DOM shell elements before rendering\n parseLightDomShell(this, this.#shellState);\n\n // Get shell config\n const shellConfig = this.#effectiveConfig?.shell;\n\n // Determine if shell should be rendered\n const hasShell = shouldRenderShellHeader(shellConfig, this.#shellState);\n\n // Core grid content HTML\n // Uses faux scrollbar pattern (like AG Grid) for smooth virtualized scrolling:\n // - .tbw-grid-content: outer container (row layout: scroll-area + faux-vscroll)\n // - .tbw-scroll-area: horizontal scroll container (overflow-x: auto) - footer appends here\n // - .rows-body-wrapper: header + rows in column layout\n // - .faux-vscroll: vertical scrollbar at inline-end, sticky during horizontal scroll\n // - .rows-viewport: visible rows area (no scroll, overflow hidden)\n // - Scroll events come from faux scrollbar, content positioned via transforms\n // This prevents blank viewport during fast scroll - old content stays until new renders\n const gridContentHtml = `\n <div class=\"tbw-scroll-area\">\n <div class=\"rows-body-wrapper\">\n <div class=\"rows-body\" role=\"grid\">\n <div class=\"header\">\n <div class=\"header-row\" part=\"header-row\"></div>\n </div>\n <div class=\"rows-container\">\n <div class=\"rows-viewport\">\n <div class=\"rows\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"faux-vscroll\">\n <div class=\"faux-vscroll-spacer\"></div>\n </div>\n `;\n\n if (hasShell) {\n // Build shell DOM structure\n const toolPanelIcon = this.#effectiveConfig?.icons?.toolPanel ?? DEFAULT_GRID_ICONS.toolPanel;\n const accordionIcons = {\n expand: this.#effectiveConfig?.icons?.expand ?? DEFAULT_GRID_ICONS.expand,\n collapse: this.#effectiveConfig?.icons?.collapse ?? DEFAULT_GRID_ICONS.collapse,\n };\n const shellHeaderHtml = renderShellHeader(shellConfig, this.#shellState, toolPanelIcon);\n const shellBodyHtml = renderShellBody(shellConfig, this.#shellState, gridContentHtml, accordionIcons);\n\n this.#shadow.innerHTML = `\n <div class=\"tbw-grid-root has-shell\">\n ${shellHeaderHtml}\n ${shellBodyHtml}\n </div>\n `;\n\n // Set up shell event listeners\n this.#setupShellListeners();\n\n // Mark shell as initialized\n this.#shellController.setInitialized(true);\n } else {\n // Build minimal DOM structure (no shell)\n // Wrap in .tbw-grid-content for consistent horizontal scroll behavior\n this.#shadow.innerHTML = `\n <div class=\"tbw-grid-root\">\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n </div>\n `;\n }\n }\n\n /**\n * Set up shell event listeners after render.\n */\n #setupShellListeners(): void {\n setupShellEventListeners(this.#shadow, this.#effectiveConfig?.shell, this.#shellState, {\n onPanelToggle: () => this.toggleToolPanel(),\n onSectionToggle: (sectionId: string) => this.toggleToolPanelSection(sectionId),\n onToolbarButtonClick: (buttonId) => this.#handleToolbarButtonClick(buttonId),\n });\n\n // Set up tool panel resize\n this.#resizeCleanup?.();\n this.#resizeCleanup = setupToolPanelResize(this.#shadow, this.#effectiveConfig?.shell, (width: number) => {\n // Update the CSS variable to persist the new width\n this.style.setProperty('--tbw-tool-panel-width', `${width}px`);\n });\n }\n\n /**\n * Handle toolbar button click (for config buttons with action).\n */\n #handleToolbarButtonClick(buttonId: string): void {\n // Check config buttons\n const configButtons = this.#effectiveConfig?.shell?.header?.toolbarButtons ?? [];\n const configBtn = configButtons.find((b) => b.id === buttonId);\n if (configBtn?.action) {\n configBtn.action();\n return;\n }\n\n // Check API-registered buttons\n const apiBtn = this.#shellState.toolbarButtons.get(buttonId);\n if (apiBtn?.action) {\n apiBtn.action();\n }\n }\n}\n\n// Self-registering custom element\nif (!customElements.get(DataGridElement.tagName)) {\n customElements.define(DataGridElement.tagName, DataGridElement);\n}\n\n// Type augmentation for querySelector/createElement\ndeclare global {\n interface HTMLElementTagNameMap {\n 'tbw-grid': DataGridElement;\n }\n}\n","/**\n * Base Grid Plugin Class\n *\n * All plugins extend this abstract class.\n * Plugins are instantiated per-grid and manage their own state.\n */\n\nimport type {\n ColumnConfig,\n ColumnState,\n GridPlugin,\n HeaderContentDefinition,\n IconValue,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\n\n// Forward declare to avoid circular imports\nexport interface GridElement {\n shadowRoot: ShadowRoot | null;\n rows: any[];\n columns: ColumnConfig[];\n gridConfig: any;\n /**\n * Current focused row index\n * @internal Plugin API\n */\n _focusRow: number;\n /**\n * Current focused column index\n * @internal Plugin API\n */\n _focusCol: number;\n /** AbortSignal that is aborted when the grid disconnects from the DOM */\n disconnectSignal: AbortSignal;\n requestRender(): void;\n requestAfterRender(): void;\n forceLayout(): Promise<void>;\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined;\n getPluginByName(name: string): BaseGridPlugin | undefined;\n dispatchEvent(event: Event): boolean;\n}\n\n/**\n * Keyboard modifier flags\n */\nexport interface KeyboardModifiers {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n}\n\n/**\n * Cell coordinates\n */\nexport interface CellCoords {\n row: number;\n col: number;\n}\n\n/**\n * Cell click event\n */\nexport interface CellClickEvent {\n rowIndex: number;\n colIndex: number;\n field: string;\n value: any;\n row: any;\n cellEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Row click event\n */\nexport interface RowClickEvent {\n rowIndex: number;\n row: any;\n rowEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Header click event\n */\nexport interface HeaderClickEvent {\n colIndex: number;\n field: string;\n column: ColumnConfig;\n headerEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Scroll event\n */\nexport interface ScrollEvent {\n scrollTop: number;\n scrollLeft: number;\n scrollHeight: number;\n scrollWidth: number;\n clientHeight: number;\n clientWidth: number;\n originalEvent?: Event;\n}\n\n/**\n * Cell mouse event (for drag operations, selection, etc.)\n */\nexport interface CellMouseEvent {\n /** Event type: mousedown, mousemove, or mouseup */\n type: 'mousedown' | 'mousemove' | 'mouseup';\n /** Row index, undefined if not over a data cell */\n rowIndex?: number;\n /** Column index, undefined if not over a cell */\n colIndex?: number;\n /** Field name, undefined if not over a cell */\n field?: string;\n /** Cell value, undefined if not over a data cell */\n value?: unknown;\n /** Row data object, undefined if not over a data row */\n row?: unknown;\n /** Column configuration, undefined if not over a column */\n column?: ColumnConfig;\n /** The cell element, undefined if not over a cell */\n cellElement?: HTMLElement;\n /** The row element, undefined if not over a row */\n rowElement?: HTMLElement;\n /** Whether the event is over a header cell */\n isHeader: boolean;\n /** Cell coordinates if over a valid data cell */\n cell?: CellCoords;\n /** The original mouse event */\n originalEvent: MouseEvent;\n}\n\n/**\n * Context menu parameters\n */\nexport interface ContextMenuParams {\n x: number;\n y: number;\n rowIndex?: number;\n colIndex?: number;\n field?: string;\n value?: any;\n row?: any;\n column?: ColumnConfig;\n isHeader?: boolean;\n}\n\n/**\n * Context menu item (used by context-menu plugin query)\n */\nexport interface ContextMenuItem {\n id: string;\n label: string;\n icon?: string;\n disabled?: boolean;\n separator?: boolean;\n children?: ContextMenuItem[];\n action?: (params: ContextMenuParams) => void;\n}\n\n/**\n * Generic plugin query for inter-plugin communication.\n * Plugins can define their own query types as string constants\n * and respond to queries from other plugins.\n */\nexport interface PluginQuery<T = unknown> {\n /** Query type identifier (e.g., 'canMoveColumn', 'getContextMenuItems') */\n type: string;\n /** Query-specific context/parameters */\n context: T;\n}\n\n/**\n * Well-known plugin query types.\n * Plugins can define additional query types beyond these.\n */\nexport const PLUGIN_QUERIES = {\n /** Ask if a column can be moved. Context: ColumnConfig. Response: boolean | undefined */\n CAN_MOVE_COLUMN: 'canMoveColumn',\n /** Get context menu items. Context: ContextMenuParams. Response: ContextMenuItem[] */\n GET_CONTEXT_MENU_ITEMS: 'getContextMenuItems',\n} as const;\n\n/**\n * Cell render context for plugin cell renderers.\n * Provides full context including position and editing state.\n *\n * Note: This differs from the core `CellRenderContext` in types.ts which is\n * simpler and used for column view renderers. This version provides additional\n * context needed by plugins that register custom cell renderers.\n */\nexport interface PluginCellRenderContext {\n /** The cell value */\n value: any;\n /** The field/column key */\n field: string;\n /** The row data object */\n row: any;\n /** Row index in the data array */\n rowIndex: number;\n /** Column index */\n colIndex: number;\n /** Column configuration */\n column: ColumnConfig;\n /** Whether the cell is currently in edit mode */\n isEditing: boolean;\n}\n\n/**\n * Header render context for plugin header renderers.\n */\nexport interface PluginHeaderRenderContext {\n /** Column configuration */\n column: ColumnConfig;\n /** Column index */\n colIndex: number;\n}\n\n/**\n * Cell renderer function type for plugins.\n */\nexport type CellRenderer = (ctx: PluginCellRenderContext) => string | HTMLElement;\n\n/**\n * Header renderer function type for plugins.\n */\nexport type HeaderRenderer = (ctx: PluginHeaderRenderContext) => string | HTMLElement;\n\n/**\n * Cell editor interface for plugins.\n */\nexport interface CellEditor {\n create(ctx: PluginCellRenderContext, commitFn: (value: any) => void, cancelFn: () => void): HTMLElement;\n getValue?(element: HTMLElement): any;\n focus?(element: HTMLElement): void;\n}\n\n/**\n * Abstract base class for all grid plugins.\n *\n * @template TConfig - Configuration type for the plugin\n */\nexport abstract class BaseGridPlugin<TConfig = unknown> implements GridPlugin {\n /** Unique plugin identifier (derived from class name by default) */\n abstract readonly name: string;\n\n /** Plugin version - override in subclass if needed */\n readonly version: string = '1.0.0';\n\n /** CSS styles to inject into the grid's shadow DOM */\n readonly styles?: string;\n\n /** Custom cell renderers keyed by type name */\n readonly cellRenderers?: Record<string, CellRenderer>;\n\n /** Custom header renderers keyed by type name */\n readonly headerRenderers?: Record<string, HeaderRenderer>;\n\n /** Custom cell editors keyed by type name */\n readonly cellEditors?: Record<string, CellEditor>;\n\n /** The grid instance this plugin is attached to */\n protected grid!: GridElement;\n\n /** Plugin configuration - merged with defaults in attach() */\n protected config!: TConfig;\n\n /** User-provided configuration from constructor */\n protected readonly userConfig: Partial<TConfig>;\n\n /**\n * Default configuration - subclasses should override this getter.\n * Note: This must be a getter (not property initializer) for proper inheritance\n * since property initializers run after parent constructor.\n */\n protected get defaultConfig(): Partial<TConfig> {\n return {};\n }\n\n constructor(config: Partial<TConfig> = {}) {\n this.userConfig = config;\n }\n\n /**\n * Called when the plugin is attached to a grid.\n * Override to set up event listeners, initialize state, etc.\n */\n attach(grid: GridElement): void {\n this.grid = grid;\n // Merge config here (after subclass construction is complete)\n this.config = { ...this.defaultConfig, ...this.userConfig } as TConfig;\n }\n\n /**\n * Called when the plugin is detached from a grid.\n * Override to clean up event listeners, timers, etc.\n */\n detach(): void {\n // Override in subclass\n }\n\n /**\n * Get another plugin instance from the same grid.\n * Use for inter-plugin communication.\n */\n protected getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.grid?.getPlugin(PluginClass);\n }\n\n /**\n * Emit a custom event from the grid.\n */\n protected emit<T>(eventName: string, detail: T): void {\n this.grid?.dispatchEvent?.(new CustomEvent(eventName, { detail, bubbles: true }));\n }\n\n /**\n * Request a re-render of the grid.\n */\n protected requestRender(): void {\n this.grid?.requestRender?.();\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Use this instead of requestRender() when only CSS classes need updating.\n */\n protected requestAfterRender(): void {\n this.grid?.requestAfterRender?.();\n }\n\n /**\n * Get the current rows from the grid.\n */\n protected get rows(): any[] {\n return this.grid?.rows ?? [];\n }\n\n /**\n * Get the original unfiltered/unprocessed rows from the grid.\n * Use this when you need all source data regardless of active filters.\n */\n protected get sourceRows(): any[] {\n return (this.grid as any)?.sourceRows ?? [];\n }\n\n /**\n * Get the current columns from the grid.\n */\n protected get columns(): ColumnConfig[] {\n return this.grid?.columns ?? [];\n }\n\n /**\n * Get only visible columns from the grid (excludes hidden).\n * Use this for rendering that needs to match the grid template.\n */\n protected get visibleColumns(): ColumnConfig[] {\n return (this.grid as any)?._visibleColumns ?? [];\n }\n\n /**\n * Get the shadow root of the grid.\n */\n protected get shadowRoot(): ShadowRoot | null {\n return this.grid?.shadowRoot ?? null;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Use this when adding event listeners that should be cleaned up automatically.\n *\n * Best for:\n * - Document/window-level listeners added in attach()\n * - Listeners on the grid element itself\n * - Any listener that should persist across renders\n *\n * Not needed for:\n * - Listeners on elements created in afterRender() (removed with element)\n *\n * @example\n * element.addEventListener('click', handler, { signal: this.disconnectSignal });\n * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });\n */\n protected get disconnectSignal(): AbortSignal {\n return this.grid?.disconnectSignal;\n }\n\n /**\n * Get the grid-level icons configuration.\n * Returns merged icons (user config + defaults).\n */\n protected get gridIcons(): typeof DEFAULT_GRID_ICONS {\n const userIcons = this.grid?.gridConfig?.icons ?? {};\n return { ...DEFAULT_GRID_ICONS, ...userIcons };\n }\n\n /**\n * Resolve an icon value to string or HTMLElement.\n * Checks plugin config first, then grid-level icons, then defaults.\n *\n * @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')\n * @param pluginOverride - Optional plugin-level override\n * @returns The resolved icon value\n */\n protected resolveIcon(iconKey: keyof typeof DEFAULT_GRID_ICONS, pluginOverride?: IconValue): IconValue {\n // Plugin override takes precedence\n if (pluginOverride !== undefined) {\n return pluginOverride;\n }\n // Then grid-level config\n return this.gridIcons[iconKey];\n }\n\n /**\n * Set an icon value on an element.\n * Handles both string (text/HTML) and HTMLElement values.\n *\n * @param element - The element to set the icon on\n * @param icon - The icon value (string or HTMLElement)\n */\n protected setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.innerHTML = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n }\n\n /**\n * Log a warning message.\n */\n protected warn(message: string): void {\n console.warn(`[tbw-grid:${this.name}] ${message}`);\n }\n\n // #region Lifecycle Hooks\n\n /**\n * Transform rows before rendering.\n * Called during each render cycle before rows are rendered to the DOM.\n * Use this to filter, sort, or add computed properties to rows.\n *\n * @param rows - The current rows array (readonly to encourage returning a new array)\n * @returns The modified rows array to render\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Filter out hidden rows\n * return rows.filter(row => !row._hidden);\n * }\n * ```\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Add computed properties\n * return rows.map(row => ({\n * ...row,\n * _fullName: `${row.firstName} ${row.lastName}`\n * }));\n * }\n * ```\n */\n processRows?(rows: readonly any[]): any[];\n\n /**\n * Transform columns before rendering.\n * Called during each render cycle before column headers and cells are rendered.\n * Use this to add, remove, or modify column definitions.\n *\n * @param columns - The current columns array (readonly to encourage returning a new array)\n * @returns The modified columns array to render\n *\n * @example\n * ```ts\n * processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n * // Add a selection checkbox column\n * return [\n * { field: '_select', header: '', width: 40 },\n * ...columns\n * ];\n * }\n * ```\n */\n processColumns?(columns: readonly ColumnConfig[]): ColumnConfig[];\n\n /**\n * Called before each render cycle begins.\n * Use this to prepare state or cache values needed during rendering.\n *\n * @example\n * ```ts\n * beforeRender(): void {\n * this.visibleRowCount = this.calculateVisibleRows();\n * }\n * ```\n */\n beforeRender?(): void;\n\n /**\n * Called after each render cycle completes.\n * Use this for DOM manipulation, adding event listeners to rendered elements,\n * or applying visual effects like selection highlights.\n *\n * @example\n * ```ts\n * afterRender(): void {\n * // Apply selection styling to rendered rows\n * const rows = this.shadowRoot?.querySelectorAll('.data-row');\n * rows?.forEach((row, i) => {\n * row.classList.toggle('selected', this.selectedRows.has(i));\n * });\n * }\n * ```\n */\n afterRender?(): void;\n\n /**\n * Called after scroll-triggered row rendering completes.\n * This is a lightweight hook for applying visual state to recycled DOM elements.\n * Use this instead of afterRender when you need to reapply styling during scroll.\n *\n * Performance note: This is called frequently during scroll. Keep implementation fast.\n *\n * @example\n * ```ts\n * onScrollRender(): void {\n * // Reapply selection state to visible cells\n * this.applySelectionToVisibleCells();\n * }\n * ```\n */\n onScrollRender?(): void;\n\n /**\n * Return extra height contributed by this plugin (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations for virtualization.\n *\n * @returns Total extra height in pixels\n *\n * @example\n * ```ts\n * getExtraHeight(): number {\n * return this.expandedRows.size * this.detailHeight;\n * }\n * ```\n */\n getExtraHeight?(): number;\n\n /**\n * Return extra height that appears before a given row index.\n * Used by virtualization to correctly calculate scroll positions when\n * there's variable height content (like expanded detail rows) above the viewport.\n *\n * @param beforeRowIndex - The row index to calculate extra height before\n * @returns Extra height in pixels that appears before this row\n *\n * @example\n * ```ts\n * getExtraHeightBefore(beforeRowIndex: number): number {\n * let height = 0;\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * if (expandedRowIndex < beforeRowIndex) {\n * height += this.getDetailHeight(expandedRowIndex);\n * }\n * }\n * return height;\n * }\n * ```\n */\n getExtraHeightBefore?(beforeRowIndex: number): number;\n\n /**\n * Adjust the virtualization start index to render additional rows before the visible range.\n * Use this when expanded content (like detail rows) needs its parent row to remain rendered\n * even when the parent row itself has scrolled above the viewport.\n *\n * @param start - The calculated start row index\n * @param scrollTop - The current scroll position\n * @param rowHeight - The height of a single row\n * @returns The adjusted start index (lower than or equal to original start)\n *\n * @example\n * ```ts\n * adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n * // If row 5 is expanded and scrolled partially, keep it rendered\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * const expandedRowTop = expandedRowIndex * rowHeight;\n * const expandedRowBottom = expandedRowTop + rowHeight + this.detailHeight;\n * if (expandedRowBottom > scrollTop && expandedRowIndex < start) {\n * return expandedRowIndex;\n * }\n * }\n * return start;\n * }\n * ```\n */\n adjustVirtualStart?(start: number, scrollTop: number, rowHeight: number): number;\n\n /**\n * Render a custom row, bypassing the default row rendering.\n * Use this for special row types like group headers, detail rows, or footers.\n *\n * @param row - The row data object\n * @param rowEl - The row DOM element to render into\n * @param rowIndex - The index of the row in the data array\n * @returns `true` if the plugin handled rendering (prevents default), `false`/`void` for default rendering\n *\n * @example\n * ```ts\n * renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void {\n * if (row._isGroupHeader) {\n * rowEl.innerHTML = `<div class=\"group-header\">${row._groupLabel}</div>`;\n * return true; // Handled - skip default rendering\n * }\n * // Return void to let default rendering proceed\n * }\n * ```\n */\n renderRow?(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void;\n\n // #endregion\n\n // #region Inter-Plugin Communication\n\n /**\n * Handle queries from other plugins.\n * This is the generic mechanism for inter-plugin communication.\n * Plugins can respond to well-known query types or define their own.\n *\n * @param query - The query object with type and context\n * @returns Query-specific response, or undefined if not handling this query\n *\n * @example\n * ```ts\n * onPluginQuery(query: PluginQuery): unknown {\n * switch (query.type) {\n * case PLUGIN_QUERIES.CAN_MOVE_COLUMN:\n * // Prevent moving pinned columns\n * const column = query.context as ColumnConfig;\n * if (column.sticky === 'left' || column.sticky === 'right') {\n * return false;\n * }\n * break;\n * case PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS:\n * const params = query.context as ContextMenuParams;\n * return [{ id: 'my-action', label: 'My Action', action: () => {} }];\n * }\n * }\n * ```\n */\n onPluginQuery?(query: PluginQuery): unknown;\n\n // #endregion\n\n // #region Interaction Hooks\n\n /**\n * Handle keyboard events on the grid.\n * Called when a key is pressed while the grid or a cell has focus.\n *\n * @param event - The native KeyboardEvent\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onKeyDown(event: KeyboardEvent): boolean | void {\n * // Handle Ctrl+A for select all\n * if (event.ctrlKey && event.key === 'a') {\n * this.selectAllRows();\n * return true; // Prevent default browser select-all\n * }\n * }\n * ```\n */\n onKeyDown?(event: KeyboardEvent): boolean | void;\n\n /**\n * Handle cell click events.\n * Called when a data cell is clicked (not headers).\n *\n * @param event - Cell click event with row/column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onCellClick(event: CellClickEvent): boolean | void {\n * if (event.field === '_select') {\n * this.toggleRowSelection(event.rowIndex);\n * return true; // Handled\n * }\n * }\n * ```\n */\n onCellClick?(event: CellClickEvent): boolean | void;\n\n /**\n * Handle row click events.\n * Called when any part of a data row is clicked.\n * Note: This is called in addition to onCellClick, not instead of.\n *\n * @param event - Row click event with row context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onRowClick(event: RowClickEvent): boolean | void {\n * if (this.config.mode === 'row') {\n * this.selectRow(event.rowIndex, event.originalEvent);\n * return true;\n * }\n * }\n * ```\n */\n onRowClick?(event: RowClickEvent): boolean | void;\n\n /**\n * Handle header click events.\n * Called when a column header is clicked. Commonly used for sorting.\n *\n * @param event - Header click event with column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onHeaderClick(event: HeaderClickEvent): boolean | void {\n * if (event.column.sortable !== false) {\n * this.toggleSort(event.field);\n * return true;\n * }\n * }\n * ```\n */\n onHeaderClick?(event: HeaderClickEvent): boolean | void;\n\n /**\n * Handle scroll events on the grid viewport.\n * Called during scrolling. Note: This may be called frequently; debounce if needed.\n *\n * @param event - Scroll event with scroll position and viewport dimensions\n *\n * @example\n * ```ts\n * onScroll(event: ScrollEvent): void {\n * // Update sticky column positions\n * this.updateStickyPositions(event.scrollLeft);\n * }\n * ```\n */\n onScroll?(event: ScrollEvent): void;\n\n /**\n * Handle cell mousedown events.\n * Used for initiating drag operations like range selection or column resize.\n *\n * @param event - Mouse event with cell context\n * @returns `true` to indicate drag started (prevents text selection), `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseDown(event: CellMouseEvent): boolean | void {\n * if (event.rowIndex !== undefined && this.config.mode === 'range') {\n * this.startDragSelection(event.rowIndex, event.colIndex);\n * return true; // Prevent text selection\n * }\n * }\n * ```\n */\n onCellMouseDown?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mousemove events during drag operations.\n * Only called when a drag is in progress (after mousedown returned true).\n *\n * @param event - Mouse event with current cell context\n * @returns `true` to continue handling the drag, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseMove(event: CellMouseEvent): boolean | void {\n * if (this.isDragging && event.rowIndex !== undefined) {\n * this.extendSelection(event.rowIndex, event.colIndex);\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseMove?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mouseup events to end drag operations.\n *\n * @param event - Mouse event with final cell context\n * @returns `true` if drag was finalized, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseUp(event: CellMouseEvent): boolean | void {\n * if (this.isDragging) {\n * this.finalizeDragSelection();\n * this.isDragging = false;\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseUp?(event: CellMouseEvent): boolean | void;\n\n // Note: Context menu items are now provided via onPluginQuery with PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS\n // This keeps the core decoupled from the context-menu plugin specifics.\n\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Contribute plugin-specific state for a column.\n * Called by the grid when collecting column state for serialization.\n * Plugins can add their own properties to the column state.\n *\n * @param field - The field name of the column\n * @returns Partial column state with plugin-specific properties, or undefined if no state to contribute\n *\n * @example\n * ```ts\n * getColumnState(field: string): Partial<ColumnState> | undefined {\n * const filterModel = this.filterModels.get(field);\n * if (filterModel) {\n * // Uses module augmentation to add filter property to ColumnState\n * return { filter: filterModel } as Partial<ColumnState>;\n * }\n * return undefined;\n * }\n * ```\n */\n getColumnState?(field: string): Partial<ColumnState> | undefined;\n\n /**\n * Apply plugin-specific state to a column.\n * Called by the grid when restoring column state from serialized data.\n * Plugins should restore their internal state based on the provided state.\n *\n * @param field - The field name of the column\n * @param state - The column state to apply (may contain plugin-specific properties)\n *\n * @example\n * ```ts\n * applyColumnState(field: string, state: ColumnState): void {\n * // Check for filter property added via module augmentation\n * const filter = (state as any).filter;\n * if (filter) {\n * this.filterModels.set(field, filter);\n * this.applyFilter();\n * }\n * }\n * ```\n */\n applyColumnState?(field: string, state: ColumnState): void;\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Report horizontal scroll boundary offsets for this plugin.\n * Plugins that obscure part of the scroll area (e.g., pinned/sticky columns)\n * should return how much space they occupy on each side.\n * The keyboard navigation uses this to ensure focused cells are fully visible.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Object with left/right pixel offsets and optional skipScroll flag, or undefined if plugin has no offsets\n *\n * @example\n * ```ts\n * getHorizontalScrollOffsets(rowEl?: HTMLElement, focusedCell?: HTMLElement): { left: number; right: number; skipScroll?: boolean } | undefined {\n * // Calculate total width of left-pinned columns\n * const leftCells = rowEl?.querySelectorAll('.sticky-left') ?? [];\n * let left = 0;\n * leftCells.forEach(el => { left += (el as HTMLElement).offsetWidth; });\n * // Skip scroll if focused cell is pinned (always visible)\n * const skipScroll = focusedCell?.classList.contains('sticky-left');\n * return { left, right: 0, skipScroll };\n * }\n * ```\n */\n getHorizontalScrollOffsets?(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } | undefined;\n\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Register a tool panel for this plugin.\n * Return undefined if plugin has no tool panel.\n * The shell will create a toolbar toggle button and render the panel content\n * when the user opens the panel.\n *\n * @returns Tool panel definition, or undefined if plugin has no panel\n *\n * @example\n * ```ts\n * getToolPanel(): ToolPanelDefinition | undefined {\n * return {\n * id: 'columns',\n * title: 'Columns',\n * icon: '☰',\n * tooltip: 'Show/hide columns',\n * order: 10,\n * render: (container) => {\n * this.renderColumnList(container);\n * return () => this.cleanup();\n * },\n * };\n * }\n * ```\n */\n getToolPanel?(): ToolPanelDefinition | undefined;\n\n /**\n * Register content for the shell header center section.\n * Return undefined if plugin has no header content.\n * Examples: search input, selection summary, status indicators.\n *\n * @returns Header content definition, or undefined if plugin has no header content\n *\n * @example\n * ```ts\n * getHeaderContent(): HeaderContentDefinition | undefined {\n * return {\n * id: 'quick-filter',\n * order: 10,\n * render: (container) => {\n * const input = document.createElement('input');\n * input.type = 'text';\n * input.placeholder = 'Search...';\n * input.addEventListener('input', this.handleInput);\n * container.appendChild(input);\n * return () => input.removeEventListener('input', this.handleInput);\n * },\n * };\n * }\n * ```\n */\n getHeaderContent?(): HeaderContentDefinition | undefined;\n\n // #endregion\n}\n","/**\n * Grid DOM Constants\n *\n * Centralized constants for CSS classes, data attributes, and selectors\n * used throughout the grid. Use these instead of magic strings.\n *\n * Note: Some constants here are used by plugins but defined in core because:\n * 1. The core CSS needs to style these elements (e.g., sticky positioning)\n * 2. Multiple plugins may share the same class names\n *\n * Plugins can define their own additional constants and export them.\n * See each plugin's index.ts for plugin-specific exports.\n */\n\n// #region CSS Classes\n\n/**\n * CSS class names used in the grid's shadow DOM.\n * Use these when adding/removing classes or querying elements.\n *\n * Classes are organized by:\n * - Core: Used by the grid core for structure and basic functionality\n * - Shared: Used by multiple features/plugins, styled by core CSS\n */\nexport const GridClasses = {\n // ─── Core Structure ───────────────────────────────────────────────\n ROOT: 'tbw-grid-root',\n HEADER: 'header',\n HEADER_ROW: 'header-row',\n HEADER_CELL: 'header-cell',\n\n // Body structure\n ROWS_VIEWPORT: 'rows-viewport',\n ROWS_SPACER: 'rows-spacer',\n ROWS_CONTAINER: 'rows',\n\n // Row elements\n DATA_ROW: 'data-row',\n GROUP_ROW: 'group-row',\n\n // Cell elements\n DATA_CELL: 'data-cell',\n\n // ─── Core States ──────────────────────────────────────────────────\n SELECTED: 'selected',\n FOCUSED: 'focused',\n EDITING: 'editing',\n EXPANDED: 'expanded',\n COLLAPSED: 'collapsed',\n DRAGGING: 'dragging',\n RESIZING: 'resizing',\n\n // Sorting (core feature)\n SORTABLE: 'sortable',\n SORTED_ASC: 'sorted-asc',\n SORTED_DESC: 'sorted-desc',\n\n // Visibility\n HIDDEN: 'hidden',\n\n // ─── Shared Classes (used by plugins, styled by core CSS) ────────\n // These are defined here because core CSS provides the base styling.\n // Plugins apply these classes; core CSS defines how they look.\n\n // Sticky positioning (PinnedColumnsPlugin applies, core CSS styles)\n STICKY_LEFT: 'sticky-left',\n STICKY_RIGHT: 'sticky-right',\n\n // Pinned rows (PinnedRowsPlugin applies, core CSS styles)\n PINNED_TOP: 'pinned-top',\n PINNED_BOTTOM: 'pinned-bottom',\n\n // Tree structure (TreePlugin applies, core CSS styles)\n TREE_TOGGLE: 'tree-toggle',\n TREE_INDENT: 'tree-indent',\n\n // Grouping (GroupingRowsPlugin applies, core CSS styles)\n GROUP_TOGGLE: 'group-toggle',\n GROUP_LABEL: 'group-label',\n GROUP_COUNT: 'group-count',\n\n // Selection (SelectionPlugin applies, core CSS styles)\n RANGE_SELECTION: 'range-selection',\n SELECTION_OVERLAY: 'selection-overlay',\n} as const;\n\n// #endregion\n\n// #region Data Attributes\n\n/**\n * Data attribute names used on grid elements.\n * Use these when getting/setting data attributes.\n */\nexport const GridDataAttrs = {\n // ─── Core Attributes ──────────────────────────────────────────────\n ROW_INDEX: 'data-row-index',\n COL_INDEX: 'data-col-index',\n FIELD: 'data-field',\n\n // ─── Shared Attributes (used by plugins) ──────────────────────────\n GROUP_KEY: 'data-group-key', // GroupingRowsPlugin\n TREE_LEVEL: 'data-tree-level', // TreePlugin\n STICKY: 'data-sticky', // PinnedColumnsPlugin\n} as const;\n\n// #endregion\n\n// #region Selectors\n\n/**\n * Common CSS selectors for querying grid elements.\n * Built from the class constants for consistency.\n */\nexport const GridSelectors = {\n ROOT: `.${GridClasses.ROOT}`,\n HEADER: `.${GridClasses.HEADER}`,\n HEADER_ROW: `.${GridClasses.HEADER_ROW}`,\n HEADER_CELL: `.${GridClasses.HEADER_CELL}`,\n ROWS_VIEWPORT: `.${GridClasses.ROWS_VIEWPORT}`,\n ROWS_CONTAINER: `.${GridClasses.ROWS_CONTAINER}`,\n DATA_ROW: `.${GridClasses.DATA_ROW}`,\n DATA_CELL: `.${GridClasses.DATA_CELL}`,\n GROUP_ROW: `.${GridClasses.GROUP_ROW}`,\n\n // By data attribute\n ROW_BY_INDEX: (index: number) => `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${index}\"]`,\n CELL_BY_FIELD: (field: string) => `.${GridClasses.DATA_CELL}[${GridDataAttrs.FIELD}=\"${field}\"]`,\n CELL_AT: (row: number, col: number) =>\n `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${row}\"] .${GridClasses.DATA_CELL}[${GridDataAttrs.COL_INDEX}=\"${col}\"]`,\n\n // State selectors\n SELECTED_ROWS: `.${GridClasses.DATA_ROW}.${GridClasses.SELECTED}`,\n EDITING_CELL: `.${GridClasses.DATA_CELL}.${GridClasses.EDITING}`,\n} as const;\n\n// #endregion\n\n// #region CSS Custom Properties\n\n/**\n * CSS custom property names for theming.\n * Use these when programmatically setting styles.\n */\nexport const GridCSSVars = {\n // Colors\n COLOR_BG: '--tbw-color-bg',\n COLOR_FG: '--tbw-color-fg',\n COLOR_FG_MUTED: '--tbw-color-fg-muted',\n COLOR_BORDER: '--tbw-color-border',\n COLOR_ACCENT: '--tbw-color-accent',\n COLOR_HEADER_BG: '--tbw-color-header-bg',\n COLOR_HEADER_FG: '--tbw-color-header-fg',\n COLOR_SELECTION: '--tbw-color-selection',\n COLOR_ROW_HOVER: '--tbw-color-row-hover',\n COLOR_ROW_ALT: '--tbw-color-row-alt',\n\n // Sizing\n ROW_HEIGHT: '--tbw-row-height',\n HEADER_HEIGHT: '--tbw-header-height',\n CELL_PADDING: '--tbw-cell-padding',\n\n // Typography\n FONT_FAMILY: '--tbw-font-family',\n FONT_SIZE: '--tbw-font-size',\n\n // Borders\n BORDER_RADIUS: '--tbw-border-radius',\n FOCUS_OUTLINE: '--tbw-focus-outline',\n} as const;\n\n// #endregion\n\n// Type helpers\nexport type GridClassName = (typeof GridClasses)[keyof typeof GridClasses];\nexport type GridDataAttr = (typeof GridDataAttrs)[keyof typeof GridDataAttrs];\nexport type GridCSSVar = (typeof GridCSSVars)[keyof typeof GridCSSVars];\n","// #region Public API surface - only export what consumers need\nexport { DataGridElement, DataGridElement as GridElement } from './lib/core/grid';\n\n// Event name constants for DataGrid (public API)\nexport const DGEvents = {\n CELL_COMMIT: 'cell-commit',\n ROW_COMMIT: 'row-commit',\n CHANGED_ROWS_RESET: 'changed-rows-reset',\n MOUNT_EXTERNAL_VIEW: 'mount-external-view',\n MOUNT_EXTERNAL_EDITOR: 'mount-external-editor',\n SORT_CHANGE: 'sort-change',\n COLUMN_RESIZE: 'column-resize',\n ACTIVATE_CELL: 'activate-cell',\n GROUP_TOGGLE: 'group-toggle',\n COLUMN_STATE_CHANGE: 'column-state-change',\n} as const;\n\nexport type DGEventName = (typeof DGEvents)[keyof typeof DGEvents];\n\n// Plugin event constants (mirrors DGEvents pattern)\nexport const PluginEvents = {\n // Selection plugin\n SELECTION_CHANGE: 'selection-change',\n // Tree plugin\n TREE_EXPAND: 'tree-expand',\n // Filtering plugin\n FILTER_CHANGE: 'filter-change',\n // Sorting plugin\n SORT_MODEL_CHANGE: 'sort-model-change',\n // Export plugin\n EXPORT_START: 'export-start',\n EXPORT_COMPLETE: 'export-complete',\n // Clipboard plugin\n CLIPBOARD_COPY: 'clipboard-copy',\n CLIPBOARD_PASTE: 'clipboard-paste',\n // Context menu plugin\n CONTEXT_MENU_OPEN: 'context-menu-open',\n CONTEXT_MENU_CLOSE: 'context-menu-close',\n // Undo/Redo plugin\n HISTORY_CHANGE: 'history-change',\n // Server-side plugin\n SERVER_LOADING: 'server-loading',\n SERVER_ERROR: 'server-error',\n // Visibility plugin\n COLUMN_VISIBILITY_CHANGE: 'column-visibility-change',\n // Reorder plugin\n COLUMN_REORDER: 'column-reorder',\n // Master-detail plugin\n DETAIL_EXPAND: 'detail-expand',\n // Grouping rows plugin\n GROUP_EXPAND: 'group-expand',\n} as const;\n\nexport type PluginEventName = (typeof PluginEvents)[keyof typeof PluginEvents];\n\n// Public type exports\nexport type {\n ActivateCellDetail,\n AggregatorRef,\n // Animation types\n AnimationConfig,\n AnimationMode,\n AnimationStyle,\n BaseColumnConfig,\n // Event detail types\n CellCommitDetail,\n CellRenderContext,\n ChangedRowsResetDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnEditorContext,\n // Column features\n ColumnEditorSpec,\n ColumnResizeDetail,\n // Column state types\n ColumnSortState,\n ColumnState,\n ColumnViewRenderer,\n DataGridCustomEvent,\n DataGridElement as DataGridElementInterface,\n DataGridEventMap,\n ExternalMountEditorDetail,\n ExternalMountViewDetail,\n FitMode,\n GridColumnState,\n // Core configuration types\n GridConfig,\n // Icons\n GridIcons,\n // Plugin interface (minimal shape for type-checking)\n GridPlugin,\n // Shell types\n HeaderContentDefinition,\n IconValue,\n // Inference types\n InferredColumnResult,\n PrimitiveColumnType,\n // Public interface\n PublicGrid,\n RowCommitDetail,\n // Grouping & Footer types\n RowGroupRenderConfig,\n ShellConfig,\n ShellHeaderConfig,\n SortChangeDetail,\n // Sorting types\n SortHandler,\n SortState,\n ToolbarButtonConfig,\n ToolPanelConfig,\n ToolPanelDefinition,\n} from './lib/core/types';\n\n// Re-export FitModeEnum for runtime usage\nexport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS, FitModeEnum } from './lib/core/types';\n\n// Re-export sorting utilities for custom sort handlers\nexport { builtInSort, defaultComparator } from './lib/core/internal/sorting';\n// #endregion\n\n// #region Plugin Types\n// Only export types that consumers need to use plugins\n// Plugin classes are available via @toolbox-web/grid/plugins/<name> or from 'all.ts'\n\n// Selection plugin\nexport type { CellRange, SelectionChangeDetail, SelectionConfig, SelectionMode } from './lib/plugins/selection/types';\n\n// Tree plugin\nexport type { TreeConfig, TreeExpandDetail } from './lib/plugins/tree/types';\n\n// Filtering plugin\nexport type {\n FilterConfig,\n FilterHandler,\n FilterModel,\n FilterOperator,\n FilterType,\n FilterValuesHandler,\n} from './lib/plugins/filtering/types';\n\n// Multi-sort plugin\nexport type { MultiSortConfig, SortModel } from './lib/plugins/multi-sort/types';\n\n// Export plugin\nexport type { ExportFormat, ExportParams } from './lib/plugins/export/types';\n\n// Pinned rows plugin\nexport type { PinnedRowsContext, PinnedRowsPanel } from './lib/plugins/pinned-rows/types';\n\n// Pivot plugin\nexport type { PivotConfig, PivotResult, PivotValueField } from './lib/plugins/pivot/types';\n\n// Server-side plugin\nexport type { GetRowsParams, GetRowsResult, ServerSideDataSource } from './lib/plugins/server-side/types';\n\n// Undo/Redo plugin\nexport type { EditAction } from './lib/plugins/undo-redo/types';\n\n// Grouping rows plugin\nexport type { GroupingRowsConfig } from './lib/plugins/grouping-rows/types';\n\n// Plugin base class - for creating custom plugins\nexport { BaseGridPlugin, PLUGIN_QUERIES } from './lib/core/plugin';\nexport type { PluginQuery } from './lib/core/plugin';\n\n// DOM constants - for querying grid elements and styling\nexport { GridClasses, GridCSSVars, GridDataAttrs, GridSelectors } from './lib/core/constants';\nexport type { GridClassName, GridCSSVar, GridDataAttr } from './lib/core/constants';\n// #endregion\n","/**\n * Aggregators Core Registry\n *\n * Provides a central registry for aggregator functions.\n * Built-in aggregators are provided by default.\n * Plugins can register additional aggregators.\n *\n * The registry is exposed as a singleton object that can be accessed:\n * - By ES module imports: import { aggregatorRegistry } from '@toolbox-web/grid'\n * - By UMD/CDN: TbwGrid.aggregatorRegistry\n * - By plugins via context: ctx.aggregatorRegistry\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nexport type AggregatorFn = (rows: any[], field: string, column?: any) => any;\nexport type AggregatorRef = string | AggregatorFn;\n\n/** Built-in aggregator functions */\nconst builtInAggregators: Record<string, AggregatorFn> = {\n sum: (rows, field) => rows.reduce((acc, row) => acc + (Number(row[field]) || 0), 0),\n avg: (rows, field) => {\n const sum = rows.reduce((acc, row) => acc + (Number(row[field]) || 0), 0);\n return rows.length ? sum / rows.length : 0;\n },\n count: (rows) => rows.length,\n min: (rows, field) => Math.min(...rows.map((r) => Number(r[field]) || Infinity)),\n max: (rows, field) => Math.max(...rows.map((r) => Number(r[field]) || -Infinity)),\n first: (rows, field) => rows[0]?.[field],\n last: (rows, field) => rows[rows.length - 1]?.[field],\n};\n\n/** Custom aggregator registry (for plugins to add to) */\nconst customAggregators: Map<string, AggregatorFn> = new Map();\n\n/**\n * The aggregator registry singleton.\n * Plugins should access this through context or the global namespace.\n */\nexport const aggregatorRegistry = {\n /**\n * Register a custom aggregator function.\n */\n register(name: string, fn: AggregatorFn): void {\n customAggregators.set(name, fn);\n },\n\n /**\n * Unregister a custom aggregator function.\n */\n unregister(name: string): void {\n customAggregators.delete(name);\n },\n\n /**\n * Get an aggregator function by reference.\n */\n get(ref: AggregatorRef | undefined): AggregatorFn | undefined {\n if (ref === undefined) return undefined;\n if (typeof ref === 'function') return ref;\n // Check custom first, then built-in\n return customAggregators.get(ref) ?? builtInAggregators[ref];\n },\n\n /**\n * Run an aggregator on a set of rows.\n */\n run(ref: AggregatorRef | undefined, rows: any[], field: string, column?: any): any {\n const fn = this.get(ref);\n return fn ? fn(rows, field, column) : undefined;\n },\n\n /**\n * Check if an aggregator exists.\n */\n has(name: string): boolean {\n return customAggregators.has(name) || name in builtInAggregators;\n },\n\n /**\n * List all available aggregator names.\n */\n list(): string[] {\n return [...Object.keys(builtInAggregators), ...customAggregators.keys()];\n },\n};\n\n// #region Value-based Aggregators\n// Used by plugins like Pivot that work with pre-extracted numeric values\n\nexport type ValueAggregatorFn = (values: number[]) => number;\n\n/**\n * Built-in value-based aggregators.\n * These operate on arrays of numbers (unlike row-based aggregators).\n */\nconst builtInValueAggregators: Record<string, ValueAggregatorFn> = {\n sum: (vals) => vals.reduce((a, b) => a + b, 0),\n avg: (vals) => (vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0),\n count: (vals) => vals.length,\n min: (vals) => (vals.length ? Math.min(...vals) : 0),\n max: (vals) => (vals.length ? Math.max(...vals) : 0),\n first: (vals) => vals[0] ?? 0,\n last: (vals) => vals[vals.length - 1] ?? 0,\n};\n\n/**\n * Get a value-based aggregator function.\n * Used by Pivot plugin and other features that aggregate pre-extracted values.\n *\n * @param aggFunc - Aggregation function name ('sum', 'avg', 'count', 'min', 'max', 'first', 'last')\n * @returns Aggregator function that takes number[] and returns number\n */\nexport function getValueAggregator(aggFunc: string): ValueAggregatorFn {\n return builtInValueAggregators[aggFunc] ?? builtInValueAggregators.sum;\n}\n\n/**\n * Run a value-based aggregator on a set of values.\n *\n * @param aggFunc - Aggregation function name\n * @param values - Array of numbers to aggregate\n * @returns Aggregated result\n */\nexport function runValueAggregator(aggFunc: string, values: number[]): number {\n return getValueAggregator(aggFunc)(values);\n}\n// #endregion\n\n// Legacy function exports for backward compatibility\nexport const registerAggregator = aggregatorRegistry.register.bind(aggregatorRegistry);\nexport const unregisterAggregator = aggregatorRegistry.unregister.bind(aggregatorRegistry);\nexport const getAggregator = aggregatorRegistry.get.bind(aggregatorRegistry);\nexport const runAggregator = aggregatorRegistry.run.bind(aggregatorRegistry);\nexport const listAggregators = aggregatorRegistry.list.bind(aggregatorRegistry);\n","/**\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 includeHeaders: false,\n delimiter: '\\t',\n newline: '\\n',\n quoteStrings: false,\n };\n }\n\n // #region Internal State\n /** The last copied text (for reference/debugging) */\n private lastCopied: { text: string; timestamp: number } | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.lastCopied = null;\n }\n // #endregion\n\n // #region Event Handlers\n\n override onKeyDown(event: KeyboardEvent): boolean {\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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n}\n\n// #region 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// #endregion\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 autoEnable: true,\n threshold: 30,\n overscan: 3,\n };\n }\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region Hooks\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n const isVirtualized = 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 // #endregion\n\n // #region 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 // #endregion\n}\n","/**\n * Context Menu Rendering Logic\n *\n * Pure functions for building and positioning context menus.\n */\n\nimport type { IconValue } from '../../core/types';\nimport { DEFAULT_GRID_ICONS } from '../../core/types';\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 * @param submenuArrow - Optional custom submenu arrow icon\n * @returns The created menu element\n */\nexport function createMenuElement(\n items: ContextMenuItem[],\n params: ContextMenuParams,\n onAction: (item: ContextMenuItem) => void,\n submenuArrow: IconValue = DEFAULT_GRID_ICONS.submenuArrow\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 // Use provided submenu arrow icon (string or HTMLElement)\n if (typeof submenuArrow === 'string') {\n arrow.innerHTML = submenuArrow;\n } else if (submenuArrow instanceof HTMLElement) {\n arrow.appendChild(submenuArrow.cloneNode(true));\n }\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, submenuArrow);\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 contextMenuStyles from './context-menu.css?inline';\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/** Reference count for instances using global handlers */\nlet globalHandlerRefCount = 0;\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 items: defaultItems,\n };\n }\n\n // #region 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 // #endregion\n\n // #region Lifecycle\n\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\n super.attach(grid);\n this.installGlobalHandlers();\n globalHandlerRefCount++;\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 this.uninstallGlobalHandlers();\n }\n // #endregion\n\n // #region 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 /**\n * Clean up global handlers when the last instance detaches.\n * Uses reference counting to ensure handlers persist while any grid uses the plugin.\n */\n private uninstallGlobalHandlers(): void {\n globalHandlerRefCount--;\n if (globalHandlerRefCount > 0) return;\n\n // Last instance - clean up all global resources\n if (globalClickHandler) {\n document.removeEventListener('click', globalClickHandler);\n globalClickHandler = null;\n }\n if (globalKeydownHandler) {\n document.removeEventListener('keydown', globalKeydownHandler);\n globalKeydownHandler = null;\n }\n if (globalStyleSheet) {\n globalStyleSheet.remove();\n globalStyleSheet = null;\n }\n }\n // #endregion\n\n // #region Hooks\n\n override afterRender(): void {\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 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(\n items,\n params,\n (item) => {\n if (item.action) {\n item.action(params);\n }\n this.menuElement?.remove();\n this.menuElement = null;\n this.isOpen = false;\n },\n this.gridIcons.submenuArrow,\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 // #endregion\n\n // #region 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(\n items,\n fullParams,\n (item) => {\n if (item.action) item.action(fullParams);\n this.menuElement?.remove();\n this.menuElement = null;\n this.isOpen = false;\n },\n this.gridIcons.submenuArrow,\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 // #endregion\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 fileName: 'export',\n includeHeaders: true,\n onlyVisible: true,\n onlySelected: false,\n };\n }\n\n // #region Internal State\n private isExportingFlag = false;\n private lastExportInfo: { format: ExportFormat; timestamp: Date } | null = null;\n // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\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 * 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 { computeVirtualWindow, shouldBypassVirtualization } from '../../core/internal/virtualization';\nimport { BaseGridPlugin, type GridElement } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, ColumnState } from '../../core/types';\nimport { computeFilterCacheKey, filterRows, getUniqueValues } from './filter-model';\nimport styles from './filtering.css?inline';\nimport filterPanelStyles from './FilteringPlugin.css?inline';\nimport type { FilterChangeDetail, FilterConfig, FilterModel, FilterPanelParams } from './types';\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 debounceMs: 300,\n caseSensitive: false,\n trimInput: true,\n useWorker: true,\n };\n }\n\n // #region 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 panelAbortController: AbortController | null = null; // For panel-scoped listeners\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 // #endregion\n\n // #region 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 // Abort panel-scoped listeners (document click handler, etc.)\n this.panelAbortController?.abort();\n this.panelAbortController = null;\n }\n // #endregion\n\n // #region Hooks\n\n override processRows(rows: readonly unknown[]): unknown[] {\n const filterList = [...this.filters.values()];\n if (!filterList.length) return [...rows];\n\n // If using async filterHandler, processRows becomes a passthrough\n // Actual filtering happens in applyFiltersAsync and rows are set directly on grid\n if (this.config.filterHandler) {\n // Return cached result if available (set by async handler)\n if (this.cachedResult) return this.cachedResult;\n // Otherwise return rows as-is (filtering happens async)\n return [...rows];\n }\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 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 // Use visibleColumns since data-col is the index within _visibleColumns\n const col = this.visibleColumns[parseInt(colIndex, 10)] as ColumnConfig;\n if (!col || col.filterable === false) return;\n\n const field = col.field;\n if (!field) return;\n\n const hasFilter = this.filters.has(field);\n\n // Check if button already exists\n let filterBtn = cell.querySelector('.tbw-filter-btn') as HTMLElement | null;\n\n if (filterBtn) {\n // Update active state of existing button\n filterBtn.classList.toggle('active', hasFilter);\n (cell as HTMLElement).classList.toggle('filtered', hasFilter);\n return;\n }\n\n // Create filter button\n 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 (hasFilter) {\n filterBtn.classList.add('active');\n (cell as HTMLElement).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 // #endregion\n\n // #region 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 this.excludedValues.delete(field);\n } else {\n this.filters.set(field, { ...filter, field });\n // Sync excludedValues for set filters so the panel reflects correct state\n if (filter.type === 'set' && filter.operator === 'notIn' && Array.isArray(filter.value)) {\n this.excludedValues.set(field, new Set(filter.value));\n } else if (filter.type === 'set') {\n // Other set operators may have different semantics; clear for safety\n this.excludedValues.delete(field);\n }\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 this.excludedValues.clear();\n for (const filter of filters) {\n this.filters.set(filter.field, filter);\n // Sync excludedValues for set filters so the panel reflects correct state\n if (filter.type === 'set' && filter.operator === 'notIn' && Array.isArray(filter.value)) {\n this.excludedValues.set(filter.field, new Set(filter.value));\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 * Clear all filters.\n */\n clearAllFilters(): void {\n this.filters.clear();\n this.excludedValues.clear();\n this.searchText.clear();\n\n this.applyFiltersInternal();\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.applyFiltersInternal();\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 // #endregion\n\n // #region 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 // If using async valuesHandler, show loading state and fetch values\n if (this.config.valuesHandler) {\n panel.innerHTML = '<div class=\"tbw-filter-loading\">Loading...</div>';\n document.body.appendChild(panel);\n this.positionPanel(panel, buttonEl);\n this.setupPanelCloseHandler(panel, buttonEl);\n\n this.config.valuesHandler(field, column).then((values) => {\n // Check if panel is still open for this field\n if (this.openPanelField !== field || !this.panelElement) return;\n panel.innerHTML = '';\n this.renderPanelContent(field, column, panel, values);\n });\n return;\n }\n\n // Sync path: get unique values from local rows\n const uniqueValues = getUniqueValues(this.sourceRows as Record<string, unknown>[], field);\n this.renderPanelContent(field, column, panel, uniqueValues);\n\n // Position and append to body\n document.body.appendChild(panel);\n this.positionPanel(panel, buttonEl);\n this.setupPanelCloseHandler(panel, buttonEl);\n }\n\n /**\n * Render filter panel content with given values\n */\n private renderPanelContent(field: string, column: ColumnConfig, panel: HTMLElement, uniqueValues: unknown[]): void {\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 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\n /**\n * Setup click-outside handler to close the panel\n */\n private setupPanelCloseHandler(panel: HTMLElement, buttonEl: HTMLElement): void {\n // Create abort controller for panel-scoped listeners\n // This allows cleanup when panel closes OR when grid disconnects\n this.panelAbortController = new AbortController();\n\n // Add global click handler to close on outside click\n // Defer to next tick to avoid immediate close from the click that opened the panel\n setTimeout(() => {\n document.addEventListener(\n 'click',\n (e: MouseEvent) => {\n if (!panel.contains(e.target as Node) && e.target !== buttonEl) {\n this.closeFilterPanel();\n }\n },\n { signal: this.panelAbortController?.signal },\n );\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 // Abort panel-scoped listeners (document click handler)\n this.panelAbortController?.abort();\n this.panelAbortController = null;\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.applyFiltersInternal();\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.applyFiltersInternal();\n }\n\n /**\n * Internal method to apply filters (sync or async based on config)\n */\n private applyFiltersInternal(): void {\n this.cachedResult = null;\n this.cacheKey = null;\n\n const filterList = [...this.filters.values()];\n\n // If using async filterHandler, delegate to server\n if (this.config.filterHandler) {\n const gridEl = this.grid as HTMLElement;\n gridEl.setAttribute('aria-busy', 'true');\n\n const result = this.config.filterHandler(filterList, this.sourceRows as unknown[]);\n\n // Handle async or sync result\n const handleResult = (rows: unknown[]) => {\n gridEl.removeAttribute('aria-busy');\n this.cachedResult = rows;\n\n // Update grid rows directly for async filtering\n (this.grid as unknown as { rows: unknown[] }).rows = rows;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: filterList,\n filteredRowCount: rows.length,\n });\n\n // Trigger afterRender to update filter button active state\n this.requestRender();\n };\n\n if (result && typeof (result as Promise<unknown[]>).then === 'function') {\n (result as Promise<unknown[]>).then(handleResult);\n } else {\n handleResult(result as unknown[]);\n }\n return;\n }\n\n // Sync path: emit event and re-render (processRows will handle filtering)\n this.emit<FilterChangeDetail>('filter-change', {\n filters: filterList,\n filteredRowCount: 0,\n });\n this.requestRender();\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\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 styles from './grouping-columns.css?inline';\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 showGroupBorders: true,\n };\n }\n\n // #region Internal State\n private groups: ColumnGroup[] = [];\n private isActive = false;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.groups = [];\n this.isActive = false;\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Hooks\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\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 // Toggle border visibility class\n groupRow.classList.toggle('no-borders', !this.config.showGroupBorders);\n\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 // Toggle border visibility on header cells\n headerRow.classList.toggle('no-group-borders', !this.config.showGroupBorders);\n applyGroupedHeaderCellClasses(headerRow, this.groups, this.columns as ColumnConfig[]);\n }\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\n}\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 { getAggregator, listAggregators, registerAggregator, runAggregator } 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 groupOn not configured 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 (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 type { GridConfig } from '../../core/types';\nimport {\n buildGroupedRowModel,\n collapseAllGroups,\n expandAllGroups,\n getGroupRowCount,\n runAggregator,\n toggleGroupExpansion,\n} from './grouping-rows';\nimport styles from './grouping-rows.css?inline';\nimport type {\n ExpandCollapseAnimation,\n GroupingRowsConfig,\n GroupRowModelItem,\n GroupToggleDetail,\n RenderRow,\n} from './types';\n\ninterface GridWithConfig {\n effectiveConfig?: GridConfig;\n}\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 defaultExpanded: false,\n showRowCount: true,\n indentWidth: 20,\n aggregators: {},\n animation: 'slide',\n };\n }\n\n // #region Internal State\n private expandedKeys: Set<string> = new Set();\n private flattenedRows: RenderRow[] = [];\n private isActive = false;\n private previousVisibleKeys = new Set<string>();\n private keysToAnimate = new Set<string>();\n // #endregion\n\n // #region Animation\n\n private get animationStyle(): ExpandCollapseAnimation {\n const gridEl = this.grid as unknown as GridWithConfig;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode !== true && mode !== 'on') {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host && getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim() === '0') {\n return false;\n }\n }\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.expandedKeys.clear();\n this.flattenedRows = [];\n this.isActive = false;\n this.previousVisibleKeys.clear();\n this.keysToAnimate.clear();\n }\n // #endregion\n\n // #region 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 (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 // Track which data rows are newly visible (for animation)\n this.keysToAnimate.clear();\n const currentVisibleKeys = new Set<string>();\n grouped.forEach((item, idx) => {\n if (item.kind === 'data') {\n const key = `data-${idx}`;\n currentVisibleKeys.add(key);\n if (!this.previousVisibleKeys.has(key)) {\n this.keysToAnimate.add(key);\n }\n }\n });\n this.previousVisibleKeys = currentVisibleKeys;\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 const style = this.animationStyle;\n if (style === false || this.keysToAnimate.size === 0) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n const animClass = style === 'fade' ? 'tbw-group-fade-in' : 'tbw-group-slide-in';\n for (const rowEl of body.querySelectorAll('.data-grid-row:not(.group-row)')) {\n const cell = rowEl.querySelector('.cell[data-row]');\n const idx = cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n const item = this.flattenedRows[idx];\n const key = item?.kind === 'data' ? `data-${idx}` : undefined;\n\n if (key && this.keysToAnimate.has(key)) {\n rowEl.classList.add(animClass);\n rowEl.addEventListener('animationend', () => rowEl.classList.remove(animClass), { once: true });\n }\n }\n this.keysToAnimate.clear();\n }\n // #endregion\n\n // #region 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${row.__groupExpanded ? ' expanded' : ''}`;\n btn.setAttribute('aria-label', row.__groupExpanded ? 'Collapse group' : 'Expand group');\n this.setIcon(btn, this.resolveIcon(row.__groupExpanded ? 'collapse' : 'expand'));\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 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${row.__groupExpanded ? ' expanded' : ''}`;\n btn.setAttribute('aria-label', row.__groupExpanded ? 'Collapse group' : 'Expand group');\n this.setIcon(btn, this.resolveIcon(row.__groupExpanded ? 'collapse' : 'expand'));\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 // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\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 * Animation style is plugin-configured; respects grid-level animation.mode.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin, RowClickEvent } from '../../core/plugin/base-plugin';\nimport type { GridConfig } from '../../core/types';\nimport {\n collapseDetailRow,\n createDetailElement,\n expandDetailRow,\n isDetailExpanded,\n toggleDetailRow,\n} from './master-detail';\nimport styles from './master-detail.css?inline';\nimport type { DetailExpandDetail, ExpandCollapseAnimation, MasterDetailConfig } from './types';\n\n/** Extended grid interface for accessing effective config */\ninterface GridWithConfig {\n effectiveConfig?: GridConfig;\n}\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 detailHeight: 'auto',\n expandOnRowClick: false,\n collapseOnClickOutside: false,\n showExpandColumn: true,\n animation: 'slide', // Plugin's own default\n };\n }\n\n // #region Animation Helpers\n\n /**\n * Check if animations are enabled at the grid level.\n * Respects gridConfig.animation.mode and CSS variable.\n */\n private get isAnimationEnabled(): boolean {\n const gridEl = this.grid as unknown as GridWithConfig;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode === true || mode === 'on') return true;\n\n // reduced-motion: check CSS variable\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const enabled = getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim();\n return enabled !== '0';\n }\n return true;\n }\n\n /**\n * Get expand/collapse animation style from plugin config.\n */\n private get animationStyle(): ExpandCollapseAnimation {\n if (!this.isAnimationEnabled) return false;\n return this.config.animation ?? 'slide'; // Plugin default\n }\n\n /**\n * Get animation duration from CSS variable (set by grid).\n */\n private get animationDuration(): number {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const durationStr = getComputedStyle(host).getPropertyValue('--tbw-animation-duration').trim();\n const parsed = parseInt(durationStr, 10);\n if (!isNaN(parsed)) return parsed;\n }\n return 200; // Default\n }\n\n /**\n * Apply expand animation to a detail element.\n */\n private animateExpand(detailEl: HTMLElement): void {\n if (!this.isAnimationEnabled || this.animationStyle === false) return;\n\n detailEl.classList.add('tbw-expanding');\n detailEl.addEventListener(\n 'animationend',\n () => {\n detailEl.classList.remove('tbw-expanding');\n },\n { once: true },\n );\n }\n\n /**\n * Apply collapse animation to a detail element and remove after animation.\n */\n private animateCollapse(detailEl: HTMLElement, onComplete: () => void): void {\n if (!this.isAnimationEnabled || this.animationStyle === false) {\n onComplete();\n return;\n }\n\n detailEl.classList.add('tbw-collapsing');\n const cleanup = () => {\n detailEl.classList.remove('tbw-collapsing');\n onComplete();\n };\n detailEl.addEventListener('animationend', cleanup, { once: true });\n // Fallback timeout in case animation doesn't fire\n setTimeout(cleanup, this.animationDuration + 50);\n }\n\n // #endregion\n\n // #region Internal State\n private expandedRows: Set<any> = new Set();\n private detailElements: Map<any, HTMLElement> = new Map();\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.expandedRows.clear();\n this.detailElements.clear();\n }\n // #endregion\n\n // #region 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 // Skip if already wrapped by this plugin (prevents double-wrapping on re-render)\n if ((originalRenderer as any)?.__masterDetailWrapped) {\n return cols;\n }\n\n const wrappedRenderer = (renderCtx: Parameters<NonNullable<typeof originalRenderer>>[0]) => {\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${isExpanded ? ' expanded' : ''}`;\n // Use grid-level icons (fall back to defaults)\n this.setIcon(toggle, this.resolveIcon(isExpanded ? 'collapse' : 'expand'));\n // role=\"button\" is required for aria-expanded to be valid\n toggle.setAttribute('role', 'button');\n toggle.setAttribute('tabindex', '0');\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 // Mark renderer as wrapped to prevent double-wrapping\n (wrappedRenderer as any).__masterDetailWrapped = true;\n firstCol.viewRenderer = wrappedRenderer;\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 onCellClick(): boolean | void {\n // Sync detail rows after cell click triggers refreshVirtualWindow\n // This runs in microtask to ensure DOM updates are complete\n if (this.expandedRows.size > 0) {\n queueMicrotask(() => this.#syncDetailRows());\n }\n return; // Don't prevent default\n }\n\n override afterRender(): void {\n this.#syncDetailRows();\n }\n\n /**\n * Called on scroll to sync detail elements with visible rows.\n * Removes details for rows that scrolled out of view and reattaches for visible rows.\n */\n override onScrollRender(): void {\n if (!this.config.detailRenderer || this.expandedRows.size === 0) return;\n // Full sync needed on scroll to clean up orphaned details\n this.#syncDetailRows();\n }\n\n /**\n * Full sync of detail rows - cleans up stale elements and creates new ones.\n * Detail rows are inserted as siblings AFTER their master row to survive row rebuilds.\n */\n #syncDetailRows(): void {\n if (!this.config.detailRenderer) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n // Build a map of row index -> row element for visible rows\n const visibleRowMap = new Map<number, Element>();\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) {\n visibleRowMap.set(rowIndex, rowEl);\n }\n }\n\n // Remove detail rows whose parent row is no longer visible or no longer expanded\n const existingDetails = body.querySelectorAll('.master-detail-row');\n for (const detailEl of existingDetails) {\n const forIndex = parseInt(detailEl.getAttribute('data-detail-for') ?? '-1', 10);\n const row = forIndex >= 0 ? this.rows[forIndex] : undefined;\n const isStillExpanded = row && this.expandedRows.has(row);\n const isRowVisible = visibleRowMap.has(forIndex);\n\n // Remove detail if not expanded or if parent row scrolled out\n if (!isStillExpanded || !isRowVisible) {\n detailEl.remove();\n if (row) this.detailElements.delete(row);\n }\n }\n\n // Insert detail rows for expanded rows that are visible\n for (const [rowIndex, rowEl] of visibleRowMap) {\n const row = this.rows[rowIndex];\n if (!row || !this.expandedRows.has(row)) continue;\n\n // Check if detail already exists for this row\n const existingDetail = this.detailElements.get(row);\n if (existingDetail) {\n // Ensure it's positioned correctly (as next sibling of row element)\n if (existingDetail.previousElementSibling !== rowEl) {\n rowEl.after(existingDetail);\n }\n continue;\n }\n\n // Create new detail element\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 // Insert as sibling after the row element (not as child)\n rowEl.after(detailEl);\n this.detailElements.set(row, detailEl);\n\n // Apply expand animation\n this.animateExpand(detailEl);\n }\n }\n\n /**\n * Return total extra height from all expanded detail rows.\n * Used by grid virtualization to adjust scrollbar height.\n */\n override getExtraHeight(): number {\n let totalHeight = 0;\n for (const row of this.expandedRows) {\n const detailEl = this.detailElements.get(row);\n if (detailEl) {\n totalHeight += detailEl.offsetHeight;\n } else {\n // Detail not yet rendered - estimate based on config or default\n const configHeight = this.config?.detailHeight;\n totalHeight += typeof configHeight === 'number' ? configHeight : 150;\n }\n }\n return totalHeight;\n }\n\n /**\n * Return extra height that appears before a given row index.\n * This is the sum of heights of all expanded details whose parent row is before the given index.\n */\n override getExtraHeightBefore(beforeRowIndex: number): number {\n let totalHeight = 0;\n for (const row of this.expandedRows) {\n const rowIndex = this.rows.indexOf(row);\n // Include detail if it's for a row before the given index\n if (rowIndex >= 0 && rowIndex < beforeRowIndex) {\n const detailEl = this.detailElements.get(row);\n if (detailEl) {\n totalHeight += detailEl.offsetHeight;\n } else {\n const configHeight = this.config?.detailHeight;\n totalHeight += typeof configHeight === 'number' ? configHeight : 150;\n }\n }\n }\n return totalHeight;\n }\n\n /**\n * Adjust the virtualization start index to keep expanded row visible while its detail is visible.\n * This ensures the detail scrolls smoothly out of view instead of disappearing abruptly.\n */\n override adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n if (this.expandedRows.size === 0) return start;\n\n // Build sorted list of expanded row indices for cumulative height calculation\n const expandedIndices: Array<{ index: number; row: any }> = [];\n for (const row of this.expandedRows) {\n const index = this.rows.indexOf(row);\n if (index >= 0) {\n expandedIndices.push({ index, row });\n }\n }\n expandedIndices.sort((a, b) => a.index - b.index);\n\n let minStart = start;\n\n // Calculate actual scroll position for each expanded row,\n // accounting for cumulative detail heights before it\n let cumulativeExtraHeight = 0;\n\n for (const { index: rowIndex, row } of expandedIndices) {\n // Actual position includes all detail heights before this row\n const actualRowTop = rowIndex * rowHeight + cumulativeExtraHeight;\n const detailEl = this.detailElements.get(row);\n const detailHeight =\n detailEl?.offsetHeight ?? (typeof this.config?.detailHeight === 'number' ? this.config.detailHeight : 150);\n const actualDetailBottom = actualRowTop + rowHeight + detailHeight;\n\n // Update cumulative height for next iteration\n cumulativeExtraHeight += detailHeight;\n\n // Skip rows that are at or after the calculated start\n if (rowIndex >= start) continue;\n\n // If any part of the detail is still visible (below the scroll position),\n // we need to keep the parent row in the render range\n if (actualDetailBottom > scrollTop) {\n if (rowIndex < minStart) {\n minStart = rowIndex;\n }\n }\n }\n\n return minStart;\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n override readonly styles = styles;\n // #endregion\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","/**\n * Multi-Sort Plugin (Class-based)\n *\n * Provides multi-column sorting capabilities for tbw-grid.\n * Supports shift+click for adding secondary sort columns.\n */\n\nimport { BaseGridPlugin, HeaderClickEvent } from '../../core/plugin/base-plugin';\nimport type { ColumnState } from '../../core/types';\nimport { applySorts, getSortDirection, getSortIndex, toggleSort } from './multi-sort';\nimport styles from './multi-sort.css?inline';\nimport type { MultiSortConfig, SortModel } from './types';\n\n/**\n * Multi-Sort Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new MultiSortPlugin({ maxSortColumns: 3, showSortIndex: true })\n * ```\n */\nexport class MultiSortPlugin extends BaseGridPlugin<MultiSortConfig> {\n readonly name = 'multiSort';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<MultiSortConfig> {\n return {\n maxSortColumns: 3,\n showSortIndex: true,\n };\n }\n\n // #region Internal State\n private sortModel: SortModel[] = [];\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.sortModel = [];\n }\n // #endregion\n\n // #region Hooks\n\n override processRows(rows: readonly unknown[]): unknown[] {\n if (this.sortModel.length === 0) {\n return [...rows];\n }\n return applySorts([...rows], this.sortModel, [...this.columns]);\n }\n\n override onHeaderClick(event: HeaderClickEvent): boolean {\n const column = this.columns.find((c) => c.field === event.field);\n if (!column?.sortable) return false;\n\n const shiftKey = event.originalEvent.shiftKey;\n const maxColumns = this.config.maxSortColumns ?? 3;\n\n this.sortModel = toggleSort(this.sortModel, event.field, shiftKey, maxColumns);\n\n this.emit('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n\n return true;\n }\n\n override afterRender(): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const showIndex = this.config.showSortIndex !== false;\n\n // Update all sortable header cells with sort indicators\n const headerCells = shadowRoot.querySelectorAll('.header-row .cell[data-field]');\n headerCells.forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n\n const sortIndex = getSortIndex(this.sortModel, field);\n const sortDir = getSortDirection(this.sortModel, field);\n\n // Remove existing sort index badge (always clean up)\n const existingBadge = cell.querySelector('.sort-index');\n existingBadge?.remove();\n\n if (sortDir) {\n // Column is sorted - remove base indicator and add our own\n const existingIndicator = cell.querySelector('[part~=\"sort-indicator\"], .sort-indicator');\n existingIndicator?.remove();\n\n cell.setAttribute('data-sort', sortDir);\n\n // Add sort arrow indicator\n const indicator = document.createElement('span');\n indicator.className = 'sort-indicator';\n // Use grid-level icons (fall back to defaults)\n this.setIcon(indicator, this.resolveIcon(sortDir === 'asc' ? 'sortAsc' : 'sortDesc'));\n cell.appendChild(indicator);\n\n // Add sort index badge if multiple columns sorted and showSortIndex is enabled\n if (showIndex && this.sortModel.length > 1 && sortIndex !== undefined) {\n const badge = document.createElement('span');\n badge.className = 'sort-index';\n badge.textContent = String(sortIndex);\n cell.appendChild(badge);\n }\n } else {\n cell.removeAttribute('data-sort');\n // For unsorted columns, leave the base indicator (⇅) alone\n }\n });\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Get the current sort model.\n * @returns Copy of the current sort model\n */\n getSortModel(): SortModel[] {\n return [...this.sortModel];\n }\n\n /**\n * Set the sort model programmatically.\n * @param model - New sort model to apply\n */\n setSortModel(model: SortModel[]): void {\n this.sortModel = [...model];\n this.emit('sort-change', { sortModel: [...model] });\n this.requestRender();\n }\n\n /**\n * Clear all sorting.\n */\n clearSort(): void {\n this.sortModel = [];\n this.emit('sort-change', { sortModel: [] });\n this.requestRender();\n }\n\n /**\n * Get the sort index (1-based) for a specific field.\n * @param field - Field to check\n * @returns 1-based index or undefined if not sorted\n */\n getSortIndex(field: string): number | undefined {\n return getSortIndex(this.sortModel, field);\n }\n\n /**\n * Get the sort direction for a specific field.\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\n getSortDirection(field: string): 'asc' | 'desc' | undefined {\n return getSortDirection(this.sortModel, field);\n }\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Return sort state for a column if it's in the sort model.\n */\n override getColumnState(field: string): Partial<ColumnState> | undefined {\n const index = this.sortModel.findIndex((s) => s.field === field);\n if (index === -1) return undefined;\n\n const sortEntry = this.sortModel[index];\n return {\n sort: {\n direction: sortEntry.direction,\n priority: index,\n },\n };\n }\n\n /**\n * Apply sort state from column state.\n * Rebuilds the sort model from all column states.\n */\n override applyColumnState(field: string, state: ColumnState): void {\n // Only process if the column has sort state\n if (!state.sort) {\n // Remove this field from sortModel if it exists\n this.sortModel = this.sortModel.filter((s) => s.field !== field);\n return;\n }\n\n // Find existing entry or add new one\n const existingIndex = this.sortModel.findIndex((s) => s.field === field);\n const newEntry: SortModel = {\n field,\n direction: state.sort.direction,\n };\n\n if (existingIndex !== -1) {\n // Update existing entry\n this.sortModel[existingIndex] = newEntry;\n } else {\n // Add at the correct priority position\n this.sortModel.splice(state.sort.priority, 0, newEntry);\n }\n\n // Re-sort the model by priority to ensure correct order\n // This is handled after all columns are processed, but we maintain order here\n }\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\n}\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.position = 'sticky';\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.position = 'sticky';\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.position = 'sticky';\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.position = 'sticky';\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.position = '';\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, PLUGIN_QUERIES, type PluginQuery } 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 }\n\n // #region Internal State\n private isApplied = false;\n private leftOffsets = new Map<string, number>();\n private rightOffsets = new Map<string, number>();\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.leftOffsets.clear();\n this.rightOffsets.clear();\n this.isApplied = false;\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Hooks\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\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.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 /**\n * Handle inter-plugin queries.\n */\n override onPluginQuery(query: PluginQuery): unknown {\n switch (query.type) {\n case PLUGIN_QUERIES.CAN_MOVE_COLUMN: {\n // Prevent pinned columns from being moved/reordered.\n // Pinned columns have fixed positions and should not be draggable.\n const column = query.context as ColumnConfig;\n const sticky = (column as ColumnConfig & { sticky?: 'left' | 'right' }).sticky;\n if (sticky === 'left' || sticky === 'right') {\n return false;\n }\n // Also check meta.sticky for backwards compatibility\n const metaSticky = (column.meta as { sticky?: 'left' | 'right' } | undefined)?.sticky;\n if (metaSticky === 'left' || metaSticky === 'right') {\n return false;\n }\n return undefined; // Let other plugins or default behavior decide\n }\n default:\n return undefined;\n }\n }\n // #endregion\n\n // #region 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 * Report horizontal scroll boundary offsets for pinned columns.\n * Used by keyboard navigation to ensure focused cells aren't hidden behind sticky columns.\n */\n override getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } | undefined {\n if (!this.isApplied) {\n return undefined;\n }\n\n let left = 0;\n let right = 0;\n\n if (rowEl) {\n // Calculate from rendered cells in the row\n const stickyLeftCells = rowEl.querySelectorAll('.sticky-left');\n const stickyRightCells = rowEl.querySelectorAll('.sticky-right');\n stickyLeftCells.forEach((el) => {\n left += (el as HTMLElement).offsetWidth;\n });\n stickyRightCells.forEach((el) => {\n right += (el as HTMLElement).offsetWidth;\n });\n } else {\n // Fall back to header row if no row element provided\n const host = this.grid as unknown as HTMLElement;\n const shadowRoot = host.shadowRoot;\n if (shadowRoot) {\n const headerCells = shadowRoot.querySelectorAll('.header-row .cell');\n headerCells.forEach((cell) => {\n if (cell.classList.contains('sticky-left')) {\n left += (cell as HTMLElement).offsetWidth;\n } else if (cell.classList.contains('sticky-right')) {\n right += (cell as HTMLElement).offsetWidth;\n }\n });\n }\n }\n\n // Skip horizontal scrolling if focused cell is pinned (it's always visible)\n const skipScroll =\n focusedCell?.classList.contains('sticky-left') || focusedCell?.classList.contains('sticky-right');\n\n return { left, right, skipScroll };\n }\n // #endregion\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 { getAggregator } from '../../core/internal/aggregators';\nimport type { ColumnConfig } from '../../core/types';\nimport type {\n AggregationRowConfig,\n AggregatorConfig,\n AggregatorDefinition,\n PinnedRowsConfig,\n PinnedRowsContext,\n PinnedRowsPanel,\n} from './types';\n\n/**\n * Check if an aggregator definition is a full config object (with aggFunc and optional formatter).\n */\nfunction isAggregatorConfig(def: AggregatorDefinition): def is AggregatorConfig {\n return typeof def === 'object' && def !== null && 'aggFunc' in def;\n}\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', 'presentation');\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 // Use presentation role since aggregation rows are outside the role=\"grid\" element for layout reasons\n container.setAttribute('role', 'presentation');\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 // Use presentation role since aggregation rows are outside the role=\"grid\" element\n rowEl.setAttribute('role', 'presentation');\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 let formatter: ((value: unknown, field: string, column?: ColumnConfig) => string) | undefined;\n\n // Check for aggregator first\n const aggDef = rowConfig.aggregators?.[col.field];\n if (aggDef) {\n // Handle both simple ref and full config object\n if (isAggregatorConfig(aggDef)) {\n const aggFn = getAggregator(aggDef.aggFunc);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\n formatter = aggDef.formatter;\n } else {\n const aggFn = getAggregator(aggDef);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\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 // Apply formatter if provided, otherwise convert to string\n if (value != null) {\n cell.textContent = formatter ? formatter(value, col.field, col) : String(value);\n } else {\n cell.textContent = '';\n }\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 styles from './pinned-rows.css?inline';\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 position: 'bottom',\n showRowCount: true,\n showSelectedCount: true,\n showFilteredCount: true,\n };\n }\n\n // #region 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 // #endregion\n\n // #region Lifecycle\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 // #endregion\n\n // #region Hooks\n override afterRender(): void {\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 // #region 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 // #endregion\n }\n // #endregion\n\n // #region Private Methods\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 // #endregion\n\n // #region Public API\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 // #endregion\n\n // #region Styles\n override readonly styles = styles;\n // #endregion\n}\n","import { getValueAggregator } from '../../core/internal/aggregators';\nimport type { PivotConfig } from './types';\n\n// Re-export for backward compatibility within pivot plugin\nexport const getPivotAggregator = getValueAggregator;\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\nexport function createValueKey(columnValues: string[], valueField: string): string {\n return [...columnValues, valueField].join('|');\n}\n","import { createValueKey, getPivotAggregator } from './pivot-model';\nimport type { PivotConfig, PivotResult, PivotRow, PivotValueField } from './types';\n\nexport type PivotDataRow = Record<string, unknown>;\n\n/**\n * Build a hierarchical pivot result from flat data.\n * Supports multiple row group fields for nested hierarchy.\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 // Build hierarchical pivot rows\n const pivotRows = buildHierarchicalPivotRows(\n rows,\n rowGroupFields,\n columnGroupFields,\n columnKeys,\n valueFields,\n 0, // starting depth\n '', // parent key prefix\n );\n\n // Calculate grand 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\n/**\n * Get unique column key combinations from the data.\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\n/**\n * Group rows by a single field.\n */\nexport function groupByField(rows: PivotDataRow[], field: string): Map<string, PivotDataRow[]> {\n const groups = new Map<string, PivotDataRow[]>();\n\n for (const row of rows) {\n const key = String(row[field] ?? '');\n const existing = groups.get(key);\n if (existing) {\n existing.push(row);\n } else {\n groups.set(key, [row]);\n }\n }\n\n return groups;\n}\n\n/**\n * Group rows by multiple fields (legacy flat grouping).\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 const existing = groups.get(key);\n if (existing) {\n existing.push(row);\n } else {\n groups.set(key, [row]);\n }\n }\n\n return groups;\n}\n\n/**\n * Build hierarchical pivot rows recursively.\n * Each level of rowGroupFields creates a new depth level.\n */\nexport function buildHierarchicalPivotRows(\n rows: PivotDataRow[],\n rowGroupFields: string[],\n columnFields: string[],\n columnKeys: string[],\n valueFields: PivotValueField[],\n depth: number,\n parentKey: string,\n): PivotRow[] {\n const result: PivotRow[] = [];\n\n // If no more row group fields, we're at the leaf level - aggregate the data\n if (rowGroupFields.length === 0) {\n // This shouldn't normally happen as we need at least one grouping field\n // But handle it by creating a single aggregated row\n const values = aggregateValues(rows, columnFields, columnKeys, valueFields);\n const total = calculateRowTotal(values);\n result.push({\n rowKey: parentKey || 'all',\n rowLabel: parentKey || 'All',\n depth,\n values,\n total,\n isGroup: false,\n rowCount: rows.length,\n });\n return result;\n }\n\n // Get the current grouping field\n const currentField = rowGroupFields[0];\n const remainingFields = rowGroupFields.slice(1);\n const hasChildren = remainingFields.length > 0;\n\n // Group rows by current field\n const grouped = groupByField(rows, currentField);\n\n for (const [groupValue, groupRows] of grouped) {\n const rowKey = parentKey ? `${parentKey}|${groupValue}` : groupValue;\n\n // Aggregate values for this group (sum of all child rows)\n const values = aggregateValues(groupRows, columnFields, columnKeys, valueFields);\n const total = calculateRowTotal(values);\n\n // Build children if there are more grouping levels\n let children: PivotRow[] | undefined;\n if (hasChildren) {\n children = buildHierarchicalPivotRows(\n groupRows,\n remainingFields,\n columnFields,\n columnKeys,\n valueFields,\n depth + 1,\n rowKey,\n );\n }\n\n result.push({\n rowKey,\n rowLabel: groupValue || '(blank)',\n depth,\n values,\n total,\n isGroup: hasChildren,\n children,\n rowCount: groupRows.length,\n });\n }\n\n return result;\n}\n\n/**\n * Aggregate values for a set of rows across all column keys.\n */\nexport function aggregateValues(\n rows: PivotDataRow[],\n columnFields: string[],\n columnKeys: string[],\n valueFields: PivotValueField[],\n): Record<string, number | null> {\n const values: Record<string, number | null> = {};\n\n for (const colKey of columnKeys) {\n for (const vf of valueFields) {\n // Filter rows that match this column key\n const matchingRows =\n columnFields.length > 0\n ? rows.filter((r) => columnFields.map((f) => String(r[f] ?? '')).join('|') === colKey)\n : rows;\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 }\n\n return values;\n}\n\n/**\n * Calculate the total for a row's values.\n */\nexport function calculateRowTotal(values: Record<string, number | null>): number {\n let sum = 0;\n for (const val of Object.values(values)) {\n sum += val ?? 0;\n }\n return sum;\n}\n\n/**\n * Legacy flat pivot row building (for backwards compatibility).\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 = aggregateValues(groupRows, columnFields, columnKeys, valueFields);\n const total = calculateRowTotal(values);\n\n result.push({\n rowKey,\n rowLabel: rowKey || '(blank)',\n depth,\n values,\n total,\n isGroup: false,\n rowCount: groupRows.length,\n });\n }\n\n return result;\n}\n\n/**\n * Calculate grand totals across all pivot rows.\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 // Recursively sum all rows (including nested children)\n function sumRows(rows: PivotRow[]) {\n for (const row of rows) {\n // Only count leaf rows to avoid double-counting\n if (!row.isGroup || !row.children?.length) {\n for (const colKey of columnKeys) {\n for (const vf of valueFields) {\n const valueKey = createValueKey([colKey], vf.field);\n totals[valueKey] = (totals[valueKey] ?? 0) + (row.values[valueKey] ?? 0);\n }\n }\n } else if (row.children) {\n sumRows(row.children);\n }\n }\n }\n\n sumRows(pivotRows);\n return totals;\n}\n\n/**\n * Flatten hierarchical pivot rows for rendering.\n * Respects expanded state - only includes children of expanded groups.\n */\nexport function flattenPivotRows(rows: PivotRow[], expandedKeys?: Set<string>, defaultExpanded = true): PivotRow[] {\n const result: PivotRow[] = [];\n\n function flatten(row: PivotRow) {\n result.push(row);\n\n // Check if this group is expanded\n const isExpanded = expandedKeys ? expandedKeys.has(row.rowKey) : defaultExpanded;\n\n // Only include children if expanded\n if (row.children && isExpanded) {\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/**\n * Get all group keys from pivot rows (for expand all / collapse all).\n */\nexport function getAllGroupKeys(rows: PivotRow[]): string[] {\n const keys: string[] = [];\n\n function collectKeys(row: PivotRow) {\n if (row.isGroup) {\n keys.push(row.rowKey);\n }\n if (row.children) {\n for (const child of row.children) {\n collectKeys(child);\n }\n }\n }\n\n for (const row of rows) {\n collectKeys(row);\n }\n\n return keys;\n}\n","/**\n * Pivot Tool Panel Rendering\n *\n * Pure functions for rendering the pivot configuration panel.\n * Separated from PivotPlugin for better code organization.\n */\n\nimport type { AggFunc, PivotConfig, PivotValueField } from './types';\n\n/** All available aggregation functions */\nexport const AGG_FUNCS: AggFunc[] = ['sum', 'avg', 'count', 'min', 'max', 'first', 'last'];\n\n/** Field info for available fields */\nexport interface FieldInfo {\n field: string;\n header: string;\n}\n\n/** Callbacks for panel interactions */\nexport interface PanelCallbacks {\n onTogglePivot: (enabled: boolean) => void;\n onAddFieldToZone: (field: string, zone: 'rowGroups' | 'columnGroups') => void;\n onRemoveFieldFromZone: (field: string, zone: 'rowGroups' | 'columnGroups') => void;\n onAddValueField: (field: string, aggFunc: AggFunc) => void;\n onRemoveValueField: (field: string) => void;\n onUpdateValueAggFunc: (field: string, aggFunc: AggFunc) => void;\n onOptionChange: (option: 'showTotals' | 'showGrandTotal', value: boolean) => void;\n getAvailableFields: () => FieldInfo[];\n}\n\n/** Internal context passed to rendering functions */\ninterface RenderContext {\n config: PivotConfig;\n callbacks: PanelCallbacks;\n signal: AbortSignal;\n}\n\n/**\n * Render the complete pivot panel content.\n * Returns a cleanup function that removes all event listeners and DOM elements.\n */\nexport function renderPivotPanel(\n container: HTMLElement,\n config: PivotConfig,\n isActive: boolean,\n callbacks: PanelCallbacks,\n): () => void {\n // Create AbortController for automatic listener cleanup\n const controller = new AbortController();\n const ctx: RenderContext = { config, callbacks, signal: controller.signal };\n\n const wrapper = document.createElement('div');\n wrapper.className = 'tbw-pivot-panel';\n\n // Options section (at top, includes pivot toggle)\n wrapper.appendChild(createSection('Options', () => createOptionsPanel(isActive, ctx)));\n\n // Row Groups section\n wrapper.appendChild(createSection('Row Groups', () => createFieldZone('rowGroups', ctx)));\n\n // Column Groups section\n wrapper.appendChild(createSection('Column Groups', () => createFieldZone('columnGroups', ctx)));\n\n // Values section\n wrapper.appendChild(createSection('Values', () => createValuesZone(ctx)));\n\n // Available fields section\n wrapper.appendChild(createSection('Available Fields', () => createAvailableFieldsZone(ctx)));\n\n container.appendChild(wrapper);\n\n // Cleanup: abort all listeners, then remove DOM\n return () => {\n controller.abort();\n wrapper.remove();\n };\n}\n\n/**\n * Create a collapsible section wrapper.\n */\nfunction createSection(title: string, contentFactory: () => HTMLElement): HTMLElement {\n const section = document.createElement('div');\n section.className = 'tbw-pivot-section';\n\n const header = document.createElement('div');\n header.className = 'tbw-pivot-section-header';\n header.textContent = title;\n\n const content = document.createElement('div');\n content.className = 'tbw-pivot-section-content';\n content.appendChild(contentFactory());\n\n section.appendChild(header);\n section.appendChild(content);\n\n return section;\n}\n\n/**\n * Create a drop zone for row/column group fields.\n */\nfunction createFieldZone(zoneType: 'rowGroups' | 'columnGroups', ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const zone = document.createElement('div');\n zone.className = 'tbw-pivot-drop-zone';\n zone.setAttribute('data-zone', zoneType);\n\n const currentFields = zoneType === 'rowGroups' ? (config.rowGroupFields ?? []) : (config.columnGroupFields ?? []);\n\n if (currentFields.length === 0) {\n const placeholder = document.createElement('div');\n placeholder.className = 'tbw-pivot-placeholder';\n placeholder.textContent = 'Drag fields here or click to add';\n zone.appendChild(placeholder);\n } else {\n for (const field of currentFields) {\n zone.appendChild(createFieldChip(field, zoneType, ctx));\n }\n }\n\n // Drop handling\n zone.addEventListener(\n 'dragover',\n (e) => {\n e.preventDefault();\n zone.classList.add('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'dragleave',\n () => {\n zone.classList.remove('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'drop',\n (e) => {\n e.preventDefault();\n zone.classList.remove('drag-over');\n\n const field = e.dataTransfer?.getData('text/plain');\n if (field) {\n callbacks.onAddFieldToZone(field, zoneType);\n }\n },\n { signal },\n );\n\n return zone;\n}\n\n/**\n * Create a field chip for row/column zones.\n */\nfunction createFieldChip(field: string, zoneType: 'rowGroups' | 'columnGroups', ctx: RenderContext): HTMLElement {\n const { callbacks, signal } = ctx;\n const chip = document.createElement('div');\n chip.className = 'tbw-pivot-field-chip';\n chip.draggable = true;\n\n const fieldInfo = callbacks.getAvailableFields().find((f) => f.field === field);\n const label = document.createElement('span');\n label.className = 'tbw-pivot-chip-label';\n label.textContent = fieldInfo?.header ?? field;\n\n const removeBtn = document.createElement('button');\n removeBtn.className = 'tbw-pivot-chip-remove';\n removeBtn.innerHTML = '×';\n removeBtn.title = 'Remove field';\n removeBtn.addEventListener(\n 'click',\n (e) => {\n e.stopPropagation();\n callbacks.onRemoveFieldFromZone(field, zoneType);\n },\n { signal },\n );\n\n chip.appendChild(label);\n chip.appendChild(removeBtn);\n\n // Drag handling for reordering\n chip.addEventListener(\n 'dragstart',\n (e) => {\n e.dataTransfer?.setData('text/plain', field);\n e.dataTransfer?.setData('source-zone', zoneType);\n chip.classList.add('dragging');\n },\n { signal },\n );\n\n chip.addEventListener(\n 'dragend',\n () => {\n chip.classList.remove('dragging');\n },\n { signal },\n );\n\n return chip;\n}\n\n/**\n * Create the values zone with aggregation controls.\n */\nfunction createValuesZone(ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const zone = document.createElement('div');\n zone.className = 'tbw-pivot-drop-zone tbw-pivot-values-zone';\n zone.setAttribute('data-zone', 'values');\n\n const currentValues = config.valueFields ?? [];\n\n if (currentValues.length === 0) {\n const placeholder = document.createElement('div');\n placeholder.className = 'tbw-pivot-placeholder';\n placeholder.textContent = 'Drag numeric fields here for aggregation';\n zone.appendChild(placeholder);\n } else {\n for (const valueField of currentValues) {\n zone.appendChild(createValueChip(valueField, ctx));\n }\n }\n\n // Drop handling with signal for cleanup\n zone.addEventListener(\n 'dragover',\n (e) => {\n e.preventDefault();\n zone.classList.add('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'dragleave',\n () => {\n zone.classList.remove('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'drop',\n (e) => {\n e.preventDefault();\n zone.classList.remove('drag-over');\n const field = e.dataTransfer?.getData('text/plain');\n if (field) {\n callbacks.onAddValueField(field, 'sum');\n }\n },\n { signal },\n );\n\n return zone;\n}\n\n/**\n * Create a value chip with aggregation selector.\n */\nfunction createValueChip(valueField: PivotValueField, ctx: RenderContext): HTMLElement {\n const { callbacks, signal } = ctx;\n const chip = document.createElement('div');\n chip.className = 'tbw-pivot-field-chip tbw-pivot-value-chip';\n\n const fieldInfo = callbacks.getAvailableFields().find((f) => f.field === valueField.field);\n\n const labelWrapper = document.createElement('div');\n labelWrapper.className = 'tbw-pivot-value-label-wrapper';\n\n const label = document.createElement('span');\n label.className = 'tbw-pivot-chip-label';\n label.textContent = fieldInfo?.header ?? valueField.field;\n\n const aggSelect = document.createElement('select');\n aggSelect.className = 'tbw-pivot-agg-select';\n aggSelect.title = 'Aggregation function';\n\n for (const aggFunc of AGG_FUNCS) {\n const option = document.createElement('option');\n option.value = aggFunc;\n option.textContent = aggFunc.toUpperCase();\n option.selected = aggFunc === valueField.aggFunc;\n aggSelect.appendChild(option);\n }\n\n aggSelect.addEventListener(\n 'change',\n () => {\n callbacks.onUpdateValueAggFunc(valueField.field, aggSelect.value as AggFunc);\n },\n { signal },\n );\n\n const removeBtn = document.createElement('button');\n removeBtn.className = 'tbw-pivot-chip-remove';\n removeBtn.innerHTML = '×';\n removeBtn.title = 'Remove value field';\n removeBtn.addEventListener(\n 'click',\n (e) => {\n e.stopPropagation();\n callbacks.onRemoveValueField(valueField.field);\n },\n { signal },\n );\n\n labelWrapper.appendChild(label);\n labelWrapper.appendChild(aggSelect);\n\n chip.appendChild(labelWrapper);\n chip.appendChild(removeBtn);\n\n return chip;\n}\n\n/**\n * Create the available fields zone.\n */\nfunction createAvailableFieldsZone(ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const zone = document.createElement('div');\n zone.className = 'tbw-pivot-available-fields';\n\n const allFields = callbacks.getAvailableFields();\n const usedFields = new Set([\n ...(config.rowGroupFields ?? []),\n ...(config.columnGroupFields ?? []),\n ...(config.valueFields?.map((v) => v.field) ?? []),\n ]);\n\n // Filter to show only unused fields\n const availableFields = allFields.filter((f) => !usedFields.has(f.field));\n\n if (availableFields.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'tbw-pivot-placeholder';\n empty.textContent = 'All fields are in use';\n zone.appendChild(empty);\n } else {\n for (const field of availableFields) {\n const chip = document.createElement('div');\n chip.className = 'tbw-pivot-field-chip available';\n chip.textContent = field.header;\n chip.draggable = true;\n chip.title = `Drag to add \"${field.field}\" to a zone`;\n\n chip.addEventListener(\n 'dragstart',\n (e) => {\n e.dataTransfer?.setData('text/plain', field.field);\n chip.classList.add('dragging');\n },\n { signal },\n );\n\n chip.addEventListener(\n 'dragend',\n () => {\n chip.classList.remove('dragging');\n },\n { signal },\n );\n\n zone.appendChild(chip);\n }\n }\n\n return zone;\n}\n\n/**\n * Create the options panel with pivot toggle and checkboxes for totals.\n */\nfunction createOptionsPanel(isActive: boolean, ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const panel = document.createElement('div');\n panel.className = 'tbw-pivot-options';\n\n // Pivot Mode toggle\n panel.appendChild(\n createCheckbox(\n 'Enable Pivot View',\n isActive,\n (checked) => {\n callbacks.onTogglePivot(checked);\n },\n signal,\n ),\n );\n\n // Show Totals checkbox\n panel.appendChild(\n createCheckbox(\n 'Show Row Totals',\n config.showTotals ?? true,\n (checked) => {\n callbacks.onOptionChange('showTotals', checked);\n },\n signal,\n ),\n );\n\n // Show Grand Total checkbox\n panel.appendChild(\n createCheckbox(\n 'Show Grand Total',\n config.showGrandTotal ?? true,\n (checked) => {\n callbacks.onOptionChange('showGrandTotal', checked);\n },\n signal,\n ),\n );\n\n return panel;\n}\n\n/**\n * Create a checkbox with label.\n */\nfunction createCheckbox(\n label: string,\n checked: boolean,\n onChange: (checked: boolean) => void,\n signal: AbortSignal,\n): HTMLElement {\n const wrapper = document.createElement('label');\n wrapper.className = 'tbw-pivot-checkbox';\n\n const input = document.createElement('input');\n input.type = 'checkbox';\n input.checked = checked;\n input.addEventListener('change', () => onChange(input.checked), { signal });\n\n const span = document.createElement('span');\n span.textContent = label;\n\n wrapper.appendChild(input);\n wrapper.appendChild(span);\n\n return wrapper;\n}\n","/**\n * Pivot Row Rendering\n *\n * Pure functions for rendering pivot rows (group rows, leaf rows, grand total).\n * Separated from PivotPlugin for better code organization.\n */\n\nimport type { ColumnConfig, IconValue } from '../../core/types';\n\n/** Row data with pivot metadata */\nexport interface PivotRowData {\n __pivotRowKey?: string;\n __pivotLabel?: string;\n __pivotDepth?: number;\n __pivotIndent?: number;\n __pivotExpanded?: boolean;\n __pivotHasChildren?: boolean;\n __pivotRowCount?: number;\n __pivotIsGrandTotal?: boolean;\n [key: string]: unknown;\n}\n\n/** Context for row rendering */\nexport interface RowRenderContext {\n columns: ColumnConfig[];\n onToggle: (key: string) => void;\n resolveIcon: (iconKey: 'expand' | 'collapse') => IconValue;\n setIcon: (element: HTMLElement, icon: IconValue) => void;\n}\n\n/**\n * Render a pivot group row (has children, can expand/collapse).\n */\nexport function renderPivotGroupRow(row: PivotRowData, rowEl: HTMLElement, ctx: RowRenderContext): boolean {\n rowEl.className = 'pivot-group-row';\n rowEl.setAttribute('data-pivot-depth', String(row.__pivotDepth ?? 0));\n rowEl.setAttribute('data-pivot-key', String(row.__pivotRowKey ?? ''));\n rowEl.setAttribute('role', 'row');\n // Note: aria-expanded is not set here because it's only valid in treegrid, not grid\n // The expand/collapse state is conveyed via the toggle button's aria-label\n rowEl.innerHTML = '';\n\n ctx.columns.forEach((col, colIdx) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n cell.setAttribute('data-col', String(colIdx));\n cell.setAttribute('role', 'gridcell');\n\n if (colIdx === 0) {\n // First column: indent + toggle + label + count\n const indent = Number(row.__pivotIndent) || 0;\n cell.style.paddingLeft = `${indent}px`;\n\n // Toggle button\n const rowKey = String(row.__pivotRowKey);\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'pivot-toggle';\n btn.setAttribute('aria-label', row.__pivotExpanded ? 'Collapse group' : 'Expand group');\n ctx.setIcon(btn, ctx.resolveIcon(row.__pivotExpanded ? 'collapse' : 'expand'));\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.onToggle(rowKey);\n });\n cell.appendChild(btn);\n\n // Group label\n const label = document.createElement('span');\n label.className = 'pivot-label';\n label.textContent = String(row.__pivotLabel ?? '');\n cell.appendChild(label);\n\n // Row count\n const count = document.createElement('span');\n count.className = 'pivot-count';\n count.textContent = ` (${Number(row.__pivotRowCount) || 0})`;\n cell.appendChild(count);\n } else {\n // Other columns: render value\n const value = row[col.field];\n cell.textContent = value != null ? String(value) : '';\n }\n\n rowEl.appendChild(cell);\n });\n\n return true;\n}\n\n/**\n * Render a pivot leaf row (no children, just indentation).\n */\nexport function renderPivotLeafRow(row: PivotRowData, rowEl: HTMLElement, columns: ColumnConfig[]): boolean {\n rowEl.className = 'pivot-leaf-row';\n rowEl.setAttribute('data-pivot-depth', String(row.__pivotDepth ?? 0));\n rowEl.setAttribute('data-pivot-key', String(row.__pivotRowKey ?? ''));\n rowEl.innerHTML = '';\n\n columns.forEach((col, colIdx) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n cell.setAttribute('data-col', String(colIdx));\n cell.setAttribute('role', 'gridcell');\n\n if (colIdx === 0) {\n // First column: indent + label (no toggle for leaves)\n const indent = Number(row.__pivotIndent) || 0;\n // Add extra indent for alignment with toggle button\n cell.style.paddingLeft = `${indent + 20}px`;\n\n const label = document.createElement('span');\n label.className = 'pivot-label';\n label.textContent = String(row.__pivotLabel ?? '');\n cell.appendChild(label);\n } else {\n // Other columns: render value\n const value = row[col.field];\n cell.textContent = value != null ? String(value) : '';\n }\n\n rowEl.appendChild(cell);\n });\n\n return true;\n}\n\n/**\n * Render the grand total row.\n */\nexport function renderPivotGrandTotalRow(row: PivotRowData, rowEl: HTMLElement, columns: ColumnConfig[]): boolean {\n rowEl.className = 'pivot-grand-total-row';\n // Use role=presentation since grand total is rendered outside the role=grid element\n rowEl.setAttribute('role', 'presentation');\n rowEl.innerHTML = '';\n\n columns.forEach((col, colIdx) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n cell.setAttribute('data-col', String(colIdx));\n // No role attribute - parent row has role=presentation so children don't need grid semantics\n\n if (colIdx === 0) {\n // First column: Grand Total label\n const label = document.createElement('span');\n label.className = 'pivot-label';\n label.textContent = 'Grand Total';\n cell.appendChild(label);\n } else {\n // Other columns: render totals\n const value = row[col.field];\n cell.textContent = value != null ? String(value) : '';\n }\n\n rowEl.appendChild(cell);\n });\n\n return true;\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 * Includes a tool panel for interactive pivot configuration.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridConfig, ToolPanelDefinition } from '../../core/types';\nimport { buildPivot, flattenPivotRows, getAllGroupKeys, type PivotDataRow } from './pivot-engine';\nimport { createValueKey, validatePivotConfig } from './pivot-model';\nimport { renderPivotPanel, type FieldInfo, type PanelCallbacks } from './pivot-panel';\nimport { renderPivotGrandTotalRow, renderPivotGroupRow, renderPivotLeafRow, type PivotRowData } from './pivot-rows';\nimport type { AggFunc, ExpandCollapseAnimation, PivotConfig, PivotResult, PivotValueField } from './types';\n\n// Import CSS as inline string (Vite handles this)\nimport styles from './pivot.css?inline';\n\n/** Extended grid interface with column access */\ninterface GridWithColumns {\n shadowRoot: ShadowRoot | null;\n effectiveConfig?: GridConfig;\n getAllColumns(): Array<{ field: string; header: string; visible: boolean }>;\n columns: ColumnConfig[];\n rows: unknown[];\n requestRender(): void;\n openToolPanel(id: string): void;\n closeToolPanel(): void;\n toggleToolPanel(id: string): void;\n activeToolPanel: string | undefined;\n}\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 /** Tool panel ID for shell integration */\n static readonly PANEL_ID = 'pivot';\n\n protected override get defaultConfig(): Partial<PivotConfig> {\n return {\n active: true,\n showTotals: true,\n showGrandTotal: true,\n showToolPanel: true,\n animation: 'slide',\n };\n }\n\n // #region Internal State\n private isActive = false;\n private hasInitialized = false;\n private pivotResult: PivotResult | null = null;\n private fieldHeaderMap: Map<string, string> = new Map();\n private expandedKeys: Set<string> = new Set();\n private defaultExpanded = true;\n private originalColumns: Array<{ field: string; header: string }> = [];\n private panelContainer: HTMLElement | null = null;\n private grandTotalFooter: HTMLElement | null = null;\n private previousVisibleKeys = new Set<string>();\n private keysToAnimate = new Set<string>();\n\n /**\n * Check if the plugin has valid pivot configuration (at least value fields).\n */\n private hasValidPivotConfig(): boolean {\n return (this.config.valueFields?.length ?? 0) > 0;\n }\n\n /**\n * Get animation style respecting grid-level animation mode.\n */\n private get animationStyle(): ExpandCollapseAnimation {\n const gridEl = this.grid as unknown as GridWithColumns;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode !== true && mode !== 'on') {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host && getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim() === '0') {\n return false;\n }\n }\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.isActive = false;\n this.hasInitialized = false;\n this.pivotResult = null;\n this.fieldHeaderMap.clear();\n this.originalColumns = [];\n this.panelContainer = null;\n this.cleanupGrandTotalFooter();\n this.previousVisibleKeys.clear();\n this.keysToAnimate.clear();\n }\n\n // #endregion\n\n // #region Shell Integration\n\n override getToolPanel(): ToolPanelDefinition | undefined {\n // Allow users to disable the tool panel for programmatic-only pivot\n // Check userConfig first (works before attach), then merged config\n const showToolPanel = this.config?.showToolPanel ?? this.userConfig?.showToolPanel ?? true;\n if (showToolPanel === false) {\n return undefined;\n }\n\n return {\n id: PivotPlugin.PANEL_ID,\n title: 'Pivot',\n icon: '⊞',\n tooltip: 'Configure pivot table',\n order: 90,\n render: (container) => this.renderPanel(container),\n };\n }\n\n // #endregion\n\n // #region Hooks\n\n override processRows(rows: readonly unknown[]): PivotDataRow[] {\n // Auto-enable pivot if config.active is true and we have valid pivot fields\n if (!this.hasInitialized && this.config.active !== false && this.hasValidPivotConfig()) {\n this.hasInitialized = true;\n this.isActive = true;\n }\n\n if (!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 this.buildFieldHeaderMap();\n this.defaultExpanded = this.config.defaultExpanded ?? true;\n\n // Initialize expanded state with defaults if first build\n if (this.expandedKeys.size === 0 && this.defaultExpanded && this.pivotResult) {\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n }\n\n // Build pivot\n this.pivotResult = buildPivot(rows as PivotDataRow[], this.config);\n\n // If default expanded and we just built the pivot, add all group keys\n if (this.expandedKeys.size === 0 && this.defaultExpanded) {\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n }\n\n // Return flattened pivot rows respecting expanded state\n const indentWidth = this.config.indentWidth ?? 20;\n const flatRows: PivotDataRow[] = flattenPivotRows(\n this.pivotResult.rows,\n this.expandedKeys,\n this.defaultExpanded,\n ).map((pr) => ({\n __pivotRowKey: pr.rowKey,\n __pivotLabel: pr.rowLabel,\n __pivotDepth: pr.depth,\n __pivotIsGroup: pr.isGroup,\n __pivotHasChildren: Boolean(pr.children?.length),\n __pivotExpanded: this.expandedKeys.has(pr.rowKey),\n __pivotRowCount: pr.rowCount ?? 0,\n __pivotIndent: pr.depth * indentWidth,\n __pivotTotal: pr.total,\n ...pr.values,\n }));\n\n // Track which rows are newly visible (for animation)\n this.keysToAnimate.clear();\n const currentVisibleKeys = new Set<string>();\n for (const row of flatRows) {\n const key = row.__pivotRowKey;\n currentVisibleKeys.add(key);\n // Animate non-root rows that weren't previously visible\n if (!this.previousVisibleKeys.has(key) && row.__pivotDepth > 0) {\n this.keysToAnimate.add(key);\n }\n }\n this.previousVisibleKeys = currentVisibleKeys;\n\n // Grand total is rendered as a pinned footer row in afterRender,\n // not as part of the scrolling row data\n\n return flatRows;\n }\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.isActive || !this.pivotResult) {\n return [...columns];\n }\n\n const pivotColumns: ColumnConfig[] = [];\n\n // Row label column\n const rowGroupHeaders = (this.config.rowGroupFields ?? []).map((f) => this.fieldHeaderMap.get(f) ?? f).join(' / ');\n pivotColumns.push({\n field: '__pivotLabel',\n header: rowGroupHeaders || '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 const valueHeader = vf.header || this.fieldHeaderMap.get(vf.field) || vf.field;\n pivotColumns.push({\n field: valueKey,\n header: `${colKey} - ${valueHeader} (${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 override renderRow(row: Record<string, unknown>, rowEl: HTMLElement): boolean {\n const pivotRow = row as PivotRowData;\n\n // Handle pivot group row (has children)\n if (pivotRow.__pivotRowKey && pivotRow.__pivotHasChildren) {\n return renderPivotGroupRow(pivotRow, rowEl, {\n columns: this.gridColumns,\n onToggle: (key) => this.toggle(key),\n resolveIcon: (iconKey) => this.resolveIcon(iconKey),\n setIcon: (el, icon) => this.setIcon(el, icon),\n });\n }\n\n // Handle pivot leaf row (no children but in pivot mode)\n if (pivotRow.__pivotRowKey !== undefined && this.isActive) {\n return renderPivotLeafRow(pivotRow, rowEl, this.gridColumns);\n }\n\n // Clean up any leftover pivot styling from pooled row elements\n this.cleanupPivotStyling(rowEl);\n\n return false;\n }\n\n /**\n * Remove pivot-specific classes, attributes, and inline styles from a row element.\n * Called when pivot mode is disabled to clean up reused DOM elements.\n * Clears innerHTML so the grid's default renderer can rebuild the row.\n */\n private cleanupPivotStyling(rowEl: HTMLElement): void {\n // Check if this row was previously rendered by pivot (has pivot classes)\n const wasPivotRow =\n rowEl.classList.contains('pivot-group-row') ||\n rowEl.classList.contains('pivot-leaf-row') ||\n rowEl.classList.contains('pivot-grand-total-row');\n\n if (wasPivotRow) {\n // Remove pivot row classes and restore the default grid row class\n rowEl.classList.remove('pivot-group-row', 'pivot-leaf-row', 'pivot-grand-total-row');\n rowEl.classList.add('data-grid-row');\n\n // Remove pivot-specific attributes\n rowEl.removeAttribute('data-pivot-depth');\n\n // Clear the row content so the default renderer can rebuild it\n rowEl.innerHTML = '';\n }\n }\n\n override afterRender(): void {\n // Render grand total as a sticky pinned footer when pivot is active\n if (this.isActive && this.config.showGrandTotal && this.pivotResult) {\n this.renderGrandTotalFooter();\n } else {\n this.cleanupGrandTotalFooter();\n }\n\n // Apply animations to newly visible rows\n const style = this.animationStyle;\n if (style === false || this.keysToAnimate.size === 0) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n const animClass = style === 'fade' ? 'tbw-pivot-fade-in' : 'tbw-pivot-slide-in';\n for (const rowEl of body.querySelectorAll('.pivot-group-row, .pivot-leaf-row')) {\n const key = (rowEl as HTMLElement).dataset.pivotKey;\n if (key && this.keysToAnimate.has(key)) {\n rowEl.classList.add(animClass);\n rowEl.addEventListener('animationend', () => rowEl.classList.remove(animClass), { once: true });\n }\n }\n this.keysToAnimate.clear();\n }\n\n /**\n * Render the grand total row as a sticky footer pinned to the bottom.\n */\n private renderGrandTotalFooter(): void {\n if (!this.pivotResult) return;\n\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n // Find the scroll container to append the footer\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 // Create footer if it doesn't exist\n if (!this.grandTotalFooter) {\n this.grandTotalFooter = document.createElement('div');\n this.grandTotalFooter.className = 'pivot-grand-total-footer';\n container.appendChild(this.grandTotalFooter);\n }\n\n // Build the row data for grand total\n const grandTotalRow: PivotRowData = {\n __pivotRowKey: '__grandTotal',\n __pivotLabel: 'Grand Total',\n __pivotIsGrandTotal: true,\n __pivotTotal: this.pivotResult.grandTotal,\n ...this.pivotResult.totals,\n };\n\n // Render the grand total row into the footer\n renderPivotGrandTotalRow(grandTotalRow, this.grandTotalFooter, this.gridColumns);\n }\n\n /**\n * Remove the grand total footer element.\n */\n private cleanupGrandTotalFooter(): void {\n if (this.grandTotalFooter) {\n this.grandTotalFooter.remove();\n this.grandTotalFooter = null;\n }\n }\n\n // #endregion\n\n // #region Expand/Collapse API\n\n toggle(key: string): void {\n if (this.expandedKeys.has(key)) {\n this.expandedKeys.delete(key);\n } else {\n this.expandedKeys.add(key);\n }\n this.requestRender();\n }\n\n expand(key: string): void {\n this.expandedKeys.add(key);\n this.requestRender();\n }\n\n collapse(key: string): void {\n this.expandedKeys.delete(key);\n this.requestRender();\n }\n\n expandAll(): void {\n if (this.pivotResult) {\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n this.requestRender();\n }\n }\n\n collapseAll(): void {\n this.expandedKeys.clear();\n this.requestRender();\n }\n\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n // #endregion\n\n // #region Public API\n\n enablePivot(): void {\n if (this.originalColumns.length === 0) {\n this.captureOriginalColumns();\n }\n this.isActive = true;\n this.requestRender();\n }\n\n disablePivot(): void {\n this.isActive = false;\n this.pivotResult = null;\n this.requestRender();\n }\n\n isPivotActive(): boolean {\n return this.isActive;\n }\n\n getPivotResult(): PivotResult | null {\n return this.pivotResult;\n }\n\n setRowGroupFields(fields: string[]): void {\n this.config.rowGroupFields = fields;\n this.requestRender();\n }\n\n setColumnGroupFields(fields: string[]): void {\n this.config.columnGroupFields = fields;\n this.requestRender();\n }\n\n setValueFields(fields: PivotValueField[]): void {\n this.config.valueFields = fields;\n this.requestRender();\n }\n\n refresh(): void {\n this.pivotResult = null;\n this.requestRender();\n }\n\n // #endregion\n\n // #region Tool Panel API\n\n showPanel(): void {\n const grid = this.grid as unknown as GridWithColumns;\n grid.openToolPanel(PivotPlugin.PANEL_ID);\n }\n\n hidePanel(): void {\n const grid = this.grid as unknown as GridWithColumns;\n grid.closeToolPanel();\n }\n\n togglePanel(): void {\n const grid = this.grid as unknown as GridWithColumns;\n grid.toggleToolPanel(PivotPlugin.PANEL_ID);\n }\n\n isPanelVisible(): boolean {\n const grid = this.grid as unknown as GridWithColumns;\n return grid.activeToolPanel === PivotPlugin.PANEL_ID;\n }\n\n // #endregion\n\n // #region Private Helpers\n\n private get gridColumns(): ColumnConfig[] {\n const grid = this.grid as unknown as GridWithColumns;\n return grid.columns ?? [];\n }\n\n private buildFieldHeaderMap(): void {\n const availableFields = this.getAvailableFields();\n this.fieldHeaderMap.clear();\n for (const field of availableFields) {\n this.fieldHeaderMap.set(field.field, field.header);\n }\n }\n\n private getAvailableFields(): FieldInfo[] {\n if (this.originalColumns.length > 0) {\n return this.originalColumns;\n }\n return this.captureOriginalColumns();\n }\n\n private captureOriginalColumns(): FieldInfo[] {\n const grid = this.grid as unknown as GridWithColumns;\n try {\n const columns = grid.getAllColumns?.() ?? grid.columns ?? [];\n this.originalColumns = columns\n .filter((col: { field: string }) => !col.field.startsWith('__pivot'))\n .map((col: { field: string; header?: string }) => ({\n field: col.field,\n header: col.header ?? col.field,\n }));\n return this.originalColumns;\n } catch {\n return [];\n }\n }\n\n private renderPanel(container: HTMLElement): (() => void) | void {\n this.panelContainer = container;\n\n if (this.originalColumns.length === 0) {\n this.captureOriginalColumns();\n }\n\n const callbacks: PanelCallbacks = {\n onTogglePivot: (enabled) => {\n if (enabled) {\n this.enablePivot();\n } else {\n this.disablePivot();\n }\n this.refreshPanel();\n },\n onAddFieldToZone: (field, zone) => this.addFieldToZone(field, zone),\n onRemoveFieldFromZone: (field, zone) => this.removeFieldFromZone(field, zone),\n onAddValueField: (field, aggFunc) => this.addValueField(field, aggFunc),\n onRemoveValueField: (field) => this.removeValueField(field),\n onUpdateValueAggFunc: (field, aggFunc) => this.updateValueAggFunc(field, aggFunc),\n onOptionChange: (option, value) => {\n this.config[option] = value;\n if (this.isActive) this.refresh();\n },\n getAvailableFields: () => this.getAvailableFields(),\n };\n\n return renderPivotPanel(container, this.config, this.isActive, callbacks);\n }\n\n private refreshPanel(): void {\n if (!this.panelContainer) return;\n this.panelContainer.innerHTML = '';\n this.renderPanel(this.panelContainer);\n }\n\n private addFieldToZone(field: string, zoneType: 'rowGroups' | 'columnGroups'): void {\n if (zoneType === 'rowGroups') {\n const current = this.config.rowGroupFields ?? [];\n if (!current.includes(field)) {\n this.config.rowGroupFields = [...current, field];\n }\n } else {\n const current = this.config.columnGroupFields ?? [];\n if (!current.includes(field)) {\n this.config.columnGroupFields = [...current, field];\n }\n }\n\n this.removeFromOtherZones(field, zoneType);\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private removeFieldFromZone(field: string, zoneType: 'rowGroups' | 'columnGroups'): void {\n if (zoneType === 'rowGroups') {\n this.config.rowGroupFields = (this.config.rowGroupFields ?? []).filter((f) => f !== field);\n } else {\n this.config.columnGroupFields = (this.config.columnGroupFields ?? []).filter((f) => f !== field);\n }\n\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private removeFromOtherZones(field: string, targetZone: 'rowGroups' | 'columnGroups' | 'values'): void {\n if (targetZone !== 'rowGroups') {\n this.config.rowGroupFields = (this.config.rowGroupFields ?? []).filter((f) => f !== field);\n }\n if (targetZone !== 'columnGroups') {\n this.config.columnGroupFields = (this.config.columnGroupFields ?? []).filter((f) => f !== field);\n }\n if (targetZone !== 'values') {\n this.config.valueFields = (this.config.valueFields ?? []).filter((v) => v.field !== field);\n }\n }\n\n private addValueField(field: string, aggFunc: AggFunc): void {\n const current = this.config.valueFields ?? [];\n if (!current.some((v) => v.field === field)) {\n this.config.valueFields = [...current, { field, aggFunc }];\n }\n\n this.removeFromOtherZones(field, 'values');\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private removeValueField(field: string): void {\n this.config.valueFields = (this.config.valueFields ?? []).filter((v) => v.field !== field);\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private updateValueAggFunc(field: string, aggFunc: AggFunc): void {\n const valueFields = this.config.valueFields ?? [];\n const fieldIndex = valueFields.findIndex((v) => v.field === field);\n if (fieldIndex >= 0) {\n valueFields[fieldIndex] = { ...valueFields[fieldIndex], aggFunc };\n this.config.valueFields = [...valueFields];\n }\n if (this.isActive) this.refresh();\n }\n\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n\n // #endregion\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 based on its own metadata.\n * This checks column-level properties like lockPosition and suppressMovable.\n *\n * Note: For full movability checks including plugin constraints (e.g., pinned columns),\n * use `grid.queryPlugins({ type: PLUGIN_QUERIES.CAN_MOVE_COLUMN, context: column })`\n * which queries all plugins via the generic plugin query system.\n *\n * @param column - The column configuration to check\n * @returns True if the column can be moved based on its metadata\n */\nexport function canMoveColumn<TRow = unknown>(column: ColumnConfig<TRow>): boolean {\n // Check for lockPosition or suppressMovable properties in the column config\n const meta = column.meta ?? {};\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","/**\n * Column Reordering Plugin (Class-based)\n *\n * Provides drag-and-drop column reordering functionality for tbw-grid.\n * Supports keyboard and mouse interactions with visual feedback.\n * Uses FLIP animation technique for smooth column transitions.\n *\n * Animation respects grid-level animation.mode setting but style is plugin-configured.\n */\n\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { BaseGridPlugin, PLUGIN_QUERIES } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridConfig } from '../../core/types';\nimport { canMoveColumn, moveColumn } from './column-drag';\nimport styles from './reorder.css?inline';\nimport type { ColumnMoveDetail, ReorderConfig } from './types';\n\n/** Extended grid interface with column order methods */\ninterface GridWithColumnOrder {\n setColumnOrder(order: string[]): void;\n getColumnOrder(): string[];\n requestStateChange?: () => void;\n /** Query plugins for inter-plugin communication */\n queryPlugins<T>(query: { type: string; context: unknown }): T[];\n /** Effective grid config */\n effectiveConfig?: GridConfig;\n}\n\n/**\n * Column Reordering Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ReorderPlugin()\n * ```\n */\nexport class ReorderPlugin extends BaseGridPlugin<ReorderConfig> {\n readonly name = 'reorder';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ReorderConfig> {\n return {\n animation: 'flip', // Plugin's own default\n };\n }\n\n /**\n * Resolve animation type from plugin config.\n * Respects grid-level animation.mode (disabled = no animation).\n */\n private get animationType(): false | 'flip' | 'fade' {\n // Check if animations are globally disabled\n if (!this.isAnimationEnabled) return false;\n\n // Plugin config (with default from defaultConfig)\n if (this.config.animation !== undefined) return this.config.animation;\n\n // Legacy viewTransition fallback\n if (this.config.viewTransition === false) return false;\n if (this.config.viewTransition === true) return 'flip';\n\n return 'flip'; // Plugin default\n }\n\n /**\n * Check if animations are enabled at the grid level.\n * Respects gridConfig.animation.mode and CSS variable.\n */\n private get isAnimationEnabled(): boolean {\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n // Explicit off = disabled\n if (mode === false || mode === 'off') return false;\n\n // Explicit on = always enabled\n if (mode === true || mode === 'on') return true;\n\n // reduced-motion: check CSS variable (set by grid based on media query)\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const enabled = getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim();\n return enabled !== '0';\n }\n\n return true; // Default to enabled\n }\n\n /**\n * Get animation duration from CSS variable (set by grid config).\n */\n private get animationDuration(): number {\n // Plugin config override\n if (this.config.animationDuration !== undefined) {\n return this.config.animationDuration;\n }\n\n // Read from CSS variable (already set by grid from gridConfig.animation.duration)\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const durationStr = getComputedStyle(host).getPropertyValue('--tbw-animation-duration').trim();\n const parsed = parseInt(durationStr, 10);\n if (!isNaN(parsed)) return parsed;\n }\n\n return 200; // Default\n }\n\n // #region Internal State\n private isDragging = false;\n private draggedField: string | null = null;\n private draggedIndex: number | null = null;\n private dropIndex: number | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\n super.attach(grid);\n\n // Listen for reorder requests from other plugins (e.g., VisibilityPlugin)\n // Uses disconnectSignal for automatic cleanup - no need for manual removeEventListener\n (grid as unknown as HTMLElement).addEventListener(\n 'column-reorder-request',\n (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (detail?.field && typeof detail.toIndex === 'number') {\n this.moveColumn(detail.field, detail.toIndex);\n }\n },\n { signal: this.disconnectSignal },\n );\n }\n\n override detach(): void {\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n }\n // #endregion\n\n // #region Hooks\n\n override afterRender(): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const headers = shadowRoot.querySelectorAll('.header-row > .cell');\n\n headers.forEach((header) => {\n const headerEl = header as HTMLElement;\n const field = headerEl.getAttribute('data-field');\n if (!field) return;\n\n const column = this.columns.find((c) => c.field === field);\n // Check both local metadata and plugin queries (e.g., PinnedColumnsPlugin)\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const pluginResponses = gridEl.queryPlugins<boolean>({\n type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n context: column as ColumnConfig,\n });\n const pluginAllows = !pluginResponses.includes(false);\n if (!column || !canMoveColumn(column) || !pluginAllows) {\n headerEl.draggable = false;\n return;\n }\n\n headerEl.draggable = true;\n\n // Remove existing listeners to prevent duplicates\n if (headerEl.getAttribute('data-dragstart-bound')) return;\n headerEl.setAttribute('data-dragstart-bound', 'true');\n\n headerEl.addEventListener('dragstart', (e: DragEvent) => {\n const currentOrder = this.getColumnOrder();\n const orderIndex = currentOrder.indexOf(field);\n this.isDragging = true;\n this.draggedField = field;\n this.draggedIndex = orderIndex;\n\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', field);\n }\n\n headerEl.classList.add('dragging');\n });\n\n headerEl.addEventListener('dragend', () => {\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n\n shadowRoot.querySelectorAll('.header-row > .cell').forEach((h) => {\n h.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\n });\n });\n\n headerEl.addEventListener('dragover', (e: DragEvent) => {\n e.preventDefault();\n if (!this.isDragging || this.draggedField === field) return;\n\n const rect = headerEl.getBoundingClientRect();\n const midX = rect.left + rect.width / 2;\n\n const currentOrder = this.getColumnOrder();\n const orderIndex = currentOrder.indexOf(field);\n this.dropIndex = e.clientX < midX ? orderIndex : orderIndex + 1;\n\n headerEl.classList.add('drop-target');\n headerEl.classList.toggle('drop-before', e.clientX < midX);\n headerEl.classList.toggle('drop-after', e.clientX >= midX);\n });\n\n headerEl.addEventListener('dragleave', () => {\n headerEl.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n headerEl.addEventListener('drop', (e: DragEvent) => {\n e.preventDefault();\n const draggedField = this.draggedField;\n const draggedIndex = this.draggedIndex;\n const dropIndex = this.dropIndex;\n\n if (!this.isDragging || draggedField === null || draggedIndex === null || dropIndex === null) {\n return;\n }\n\n const effectiveToIndex = dropIndex > draggedIndex ? dropIndex - 1 : dropIndex;\n const currentOrder = this.getColumnOrder();\n const newOrder = moveColumn(currentOrder, draggedIndex, effectiveToIndex);\n\n const detail: ColumnMoveDetail = {\n field: draggedField,\n fromIndex: draggedIndex,\n toIndex: effectiveToIndex,\n columnOrder: newOrder,\n };\n\n // Update the grid's column order (with optional view transition)\n this.updateColumnOrder(newOrder);\n\n this.emit('column-move', detail);\n });\n });\n }\n\n /**\n * Handle Alt+Arrow keyboard shortcuts for column reordering.\n */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n if (!event.altKey || (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight')) {\n return;\n }\n\n const grid = this.grid as unknown as { _focusCol: number; _visibleColumns: ColumnConfig[] };\n const focusCol = grid._focusCol;\n const columns = grid._visibleColumns;\n\n if (focusCol < 0 || focusCol >= columns.length) return;\n\n const column = columns[focusCol];\n if (!column || !canMoveColumn(column)) return;\n\n // Check plugin queries (e.g., PinnedColumnsPlugin)\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const pluginResponses = gridEl.queryPlugins<boolean>({\n type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n context: column,\n });\n if (pluginResponses.includes(false)) return;\n\n const currentOrder = this.getColumnOrder();\n const fromIndex = currentOrder.indexOf(column.field);\n if (fromIndex === -1) return;\n\n const toIndex = event.key === 'ArrowLeft' ? fromIndex - 1 : fromIndex + 1;\n\n // Check bounds\n if (toIndex < 0 || toIndex >= currentOrder.length) return;\n\n // Check if target position is allowed (e.g., not into pinned area)\n const targetColumn = columns.find((c) => c.field === currentOrder[toIndex]);\n if (targetColumn) {\n const targetResponses = gridEl.queryPlugins<boolean>({\n type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n context: targetColumn,\n });\n if (targetResponses.includes(false)) return;\n }\n\n this.moveColumn(column.field, toIndex);\n\n // Update focus to follow the moved column and refresh visual focus state\n grid._focusCol = toIndex;\n ensureCellVisible(this.grid as any);\n\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Get the current column order from the grid.\n * @returns Array of field names in display order\n */\n getColumnOrder(): string[] {\n return (this.grid as unknown as GridWithColumnOrder).getColumnOrder();\n }\n\n /**\n * Move a column to a new position.\n * @param field - The field name of the column to move\n * @param toIndex - The target index\n */\n moveColumn(field: string, toIndex: number): void {\n const currentOrder = this.getColumnOrder();\n const fromIndex = currentOrder.indexOf(field);\n if (fromIndex === -1) return;\n\n const newOrder = moveColumn(currentOrder, fromIndex, toIndex);\n\n // Update with view transition\n this.updateColumnOrder(newOrder);\n\n this.emit<ColumnMoveDetail>('column-move', {\n field,\n fromIndex,\n toIndex,\n columnOrder: newOrder,\n });\n }\n\n /**\n * Set a specific column order.\n * @param order - Array of field names in desired order\n */\n setColumnOrder(order: string[]): void {\n this.updateColumnOrder(order);\n }\n\n /**\n * Reset column order to the original configuration order.\n */\n resetColumnOrder(): void {\n const originalOrder = this.columns.map((c) => c.field);\n this.updateColumnOrder(originalOrder);\n }\n // #endregion\n\n // #region View Transition\n\n /**\n * Capture header cell positions before reorder.\n */\n private captureHeaderPositions(): Map<string, number> {\n const positions = new Map<string, number>();\n this.shadowRoot?.querySelectorAll('.header-row > .cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (field) positions.set(field, cell.getBoundingClientRect().left);\n });\n return positions;\n }\n\n /**\n * Apply FLIP animation for column reorder.\n * Uses CSS transitions - JS sets initial transform and toggles class.\n * @param oldPositions - Header positions captured before DOM change\n */\n private animateFLIP(oldPositions: Map<string, number>): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot || oldPositions.size === 0) return;\n\n // Compute deltas from header cells (stable reference points)\n const deltas = new Map<string, number>();\n shadowRoot.querySelectorAll('.header-row > .cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n const oldLeft = oldPositions.get(field);\n if (oldLeft === undefined) return;\n const deltaX = oldLeft - cell.getBoundingClientRect().left;\n if (Math.abs(deltaX) > 1) deltas.set(field, deltaX);\n });\n\n if (deltas.size === 0) return;\n\n // Set initial transform (First → Last position offset)\n const cells: HTMLElement[] = [];\n shadowRoot.querySelectorAll('.cell[data-field]').forEach((cell) => {\n const deltaX = deltas.get(cell.getAttribute('data-field') ?? '');\n if (deltaX !== undefined) {\n const el = cell as HTMLElement;\n el.style.transform = `translateX(${deltaX}px)`;\n cells.push(el);\n }\n });\n\n if (cells.length === 0) return;\n\n // Force reflow then animate to final position via CSS transition\n void (shadowRoot.host as HTMLElement).offsetHeight;\n\n const duration = this.animationDuration;\n\n requestAnimationFrame(() => {\n cells.forEach((el) => {\n el.classList.add('flip-animating');\n el.style.transform = '';\n });\n\n // Cleanup after animation\n setTimeout(() => {\n cells.forEach((el) => {\n el.style.transform = '';\n el.classList.remove('flip-animating');\n });\n }, duration + 50);\n });\n }\n\n /**\n * Apply crossfade animation for moved columns.\n * Uses CSS keyframes - JS just toggles classes.\n */\n private animateFade(applyChange: () => void): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) {\n applyChange();\n return;\n }\n\n // Capture old positions to detect which columns moved\n const oldPositions = this.captureHeaderPositions();\n\n // Apply the change first\n applyChange();\n\n // Find which columns changed position\n const movedFields = new Set<string>();\n shadowRoot.querySelectorAll('.header-row > .cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n const oldLeft = oldPositions.get(field);\n if (oldLeft === undefined) return;\n const newLeft = cell.getBoundingClientRect().left;\n if (Math.abs(oldLeft - newLeft) > 1) {\n movedFields.add(field);\n }\n });\n\n if (movedFields.size === 0) return;\n\n // Add animation class to moved columns (headers + body cells)\n const cells: HTMLElement[] = [];\n shadowRoot.querySelectorAll('.cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (field && movedFields.has(field)) {\n const el = cell as HTMLElement;\n el.classList.add('fade-animating');\n cells.push(el);\n }\n });\n\n if (cells.length === 0) return;\n\n // Remove class after animation completes\n const duration = this.animationDuration;\n setTimeout(() => {\n cells.forEach((el) => el.classList.remove('fade-animating'));\n }, duration + 50);\n }\n\n /**\n * Update column order with configured animation.\n */\n private updateColumnOrder(newOrder: string[]): void {\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const animation = this.animationType;\n\n if (animation === 'flip' && this.shadowRoot) {\n const oldPositions = this.captureHeaderPositions();\n gridEl.setColumnOrder(newOrder);\n void (this.shadowRoot.host as HTMLElement).offsetHeight;\n this.animateFLIP(oldPositions);\n } else if (animation === 'fade') {\n this.animateFade(() => gridEl.setColumnOrder(newOrder));\n } else {\n gridEl.setColumnOrder(newOrder);\n }\n\n gridEl.requestStateChange?.();\n }\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\n}\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 styles from './selection.css?inline';\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 // #region 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 /** Pending keyboard navigation update (processed in afterRender) */\n private pendingKeyboardUpdate: { shiftKey: boolean } | null = null;\n\n /** Cell selection state (cell mode) */\n private selectedCell: { row: number; col: number } | null = null;\n\n // #endregion\n\n // #region 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 this.pendingKeyboardUpdate = null;\n }\n\n // #endregion\n\n // #region 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 const navKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Tab', 'Home', 'End', 'PageUp', 'PageDown'];\n const isNavKey = navKeys.includes(event.key);\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 // CELL MODE: Selection follows focus\n if (mode === 'cell' && isNavKey) {\n // Use queueMicrotask so grid's handler runs first and updates focusRow/focusCol\n queueMicrotask(() => {\n this.selectedCell = { row: this.grid._focusRow, col: this.grid._focusCol };\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n });\n return false; // Let grid handle navigation\n }\n\n // ROW MODE: Only Up/Down arrows move row selection\n if (mode === 'row' && (event.key === 'ArrowUp' || event.key === 'ArrowDown')) {\n // Let grid move focus first, then sync row selection\n queueMicrotask(() => {\n this.selected.clear();\n this.selected.add(this.grid._focusRow);\n this.lastSelected = this.grid._focusRow;\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n });\n return false; // Let grid handle navigation\n }\n\n // RANGE MODE: Shift+Arrow extends, plain Arrow resets\n if (mode === 'range' && isNavKey) {\n // Capture anchor BEFORE grid moves focus (synchronous)\n // This ensures the anchor is the starting point, not the destination\n if (event.shiftKey && !this.cellAnchor) {\n this.cellAnchor = { row: this.grid._focusRow, col: this.grid._focusCol };\n }\n\n // Mark pending update - will be processed in afterRender when grid updates focus\n this.pendingKeyboardUpdate = { shiftKey: event.shiftKey };\n\n // Schedule afterRender to run after grid's keyboard handler completes\n // Grid's refreshVirtualWindow(false) skips afterRender for performance,\n // so we explicitly request it to process pendingKeyboardUpdate\n queueMicrotask(() => this.requestAfterRender());\n\n return false; // Let grid handle navigation\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 /**\n * Apply selection classes to visible cells/rows.\n * Shared by afterRender and onScrollRender.\n */\n #applySelectionClasses(): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const { mode } = this.config;\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', 'row-focus');\n });\n\n // ROW MODE: Add row-focus class to selected rows, disable cell-focus\n if (mode === 'row') {\n // In row mode, disable ALL cell-focus styling - row selection takes precedence\n shadowRoot.querySelectorAll('.cell-focus').forEach((cell) => cell.classList.remove('cell-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 // Remove cell-focus from selected cells - selection highlight takes precedence\n cell.classList.remove('cell-focus');\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 // CELL MODE: Remove cell-focus when selection plugin handles focus\n if (mode === 'cell' && this.selectedCell) {\n // Remove all cell-focus - the selection plugin manages focus styling\n shadowRoot.querySelectorAll('.cell-focus').forEach((cell) => cell.classList.remove('cell-focus'));\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 // Process pending keyboard navigation update (range mode)\n // This runs AFTER the grid has updated focusRow/focusCol\n if (this.pendingKeyboardUpdate && mode === 'range') {\n const { shiftKey } = this.pendingKeyboardUpdate;\n this.pendingKeyboardUpdate = null;\n\n const currentRow = this.grid._focusRow;\n const currentCol = this.grid._focusCol;\n\n if (shiftKey && this.cellAnchor) {\n // Extend selection from anchor to current focus\n const newRange = createRangeFromAnchor(this.cellAnchor, { row: currentRow, col: currentCol });\n this.ranges = [newRange];\n this.activeRange = newRange;\n } else if (!shiftKey) {\n // Without shift, clear selection (cell-focus will show instead)\n this.ranges = [];\n this.activeRange = null;\n this.cellAnchor = { row: currentRow, col: currentCol }; // Reset anchor to current position\n }\n\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n }\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 this.#applySelectionClasses();\n }\n\n /**\n * Called after scroll-triggered row rendering.\n * Reapplies selection classes to recycled DOM elements.\n */\n override onScrollRender(): void {\n this.#applySelectionClasses();\n }\n\n // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n\n // #endregion\n}\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 pageSize: 100,\n cacheBlockSize: 100,\n maxConcurrentRequests: 2,\n };\n }\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\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\n *\n * Enables hierarchical tree data with expand/collapse, sorting, and auto-detection.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin, CellClickEvent, HeaderClickEvent } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridConfig } from '../../core/types';\nimport { collapseAll, expandAll, expandToKey, toggleExpand } from './tree-data';\nimport { detectTreeStructure, inferChildrenField } from './tree-detect';\nimport styles from './tree.css?inline';\nimport type { ExpandCollapseAnimation, FlattenedTreeRow, TreeConfig, TreeExpandDetail } from './types';\n\ninterface GridWithConfig {\n effectiveConfig?: GridConfig;\n _sortState?: { field: string; direction: 1 | -1 } | null;\n}\n\n/**\n * Tree Data Plugin for tbw-grid\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 override readonly styles = styles;\n\n protected override get defaultConfig(): Partial<TreeConfig> {\n return {\n childrenField: 'children',\n autoDetect: true,\n defaultExpanded: false,\n indentWidth: 20,\n showExpandIcons: true,\n animation: 'slide',\n };\n }\n\n // #region State\n\n private expandedKeys = new Set<string>();\n private initialExpansionDone = false;\n private flattenedRows: FlattenedTreeRow[] = [];\n private rowKeyMap = new Map<string, FlattenedTreeRow>();\n private previousVisibleKeys = new Set<string>();\n private keysToAnimate = new Set<string>();\n private sortState: { field: string; direction: 1 | -1 } | null = null;\n\n override detach(): void {\n this.expandedKeys.clear();\n this.initialExpansionDone = false;\n this.flattenedRows = [];\n this.rowKeyMap.clear();\n this.previousVisibleKeys.clear();\n this.keysToAnimate.clear();\n this.sortState = null;\n }\n\n // #endregion\n\n // #region Animation\n\n private get animationStyle(): ExpandCollapseAnimation {\n const gridEl = this.grid as unknown as GridWithConfig;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode !== true && mode !== 'on') {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host && getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim() === '0') {\n return false;\n }\n }\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Auto-Detection\n\n detect(rows: readonly unknown[]): boolean {\n if (!this.config.autoDetect) return false;\n const field = this.config.childrenField ?? inferChildrenField(rows as any[]) ?? 'children';\n return detectTreeStructure(rows as any[], field);\n }\n\n // #endregion\n\n // #region Data Processing\n\n override processRows(rows: readonly unknown[]): any[] {\n const childrenField = this.config.childrenField ?? 'children';\n\n if (!detectTreeStructure(rows as any[], childrenField)) {\n this.flattenedRows = [];\n this.rowKeyMap.clear();\n this.previousVisibleKeys.clear();\n return [...rows];\n }\n\n // Assign stable keys, then optionally sort\n let data = this.withStableKeys(rows as any[]);\n if (this.sortState) {\n data = this.sortTree(data, this.sortState.field, this.sortState.direction);\n }\n\n // Initialize expansion if needed\n if (this.config.defaultExpanded && !this.initialExpansionDone) {\n this.expandedKeys = expandAll(data, this.config);\n this.initialExpansionDone = true;\n }\n\n // Flatten and track animations\n this.flattenedRows = this.flattenTree(data, this.expandedKeys);\n this.rowKeyMap.clear();\n this.keysToAnimate.clear();\n const currentKeys = new Set<string>();\n\n for (const row of this.flattenedRows) {\n this.rowKeyMap.set(row.key, row);\n currentKeys.add(row.key);\n if (!this.previousVisibleKeys.has(row.key) && row.depth > 0) {\n this.keysToAnimate.add(row.key);\n }\n }\n this.previousVisibleKeys = currentKeys;\n\n return this.flattenedRows.map((r) => ({\n ...r.data,\n __treeKey: r.key,\n __treeDepth: r.depth,\n __treeHasChildren: r.hasChildren,\n __treeExpanded: r.isExpanded,\n }));\n }\n\n /** Assign stable keys to rows (preserves key across sort operations) */\n private withStableKeys(rows: any[], parentKey: string | null = null): any[] {\n const childrenField = this.config.childrenField ?? 'children';\n return rows.map((row, i) => {\n const key =\n row.id !== undefined ? String(row.id) : (row.__stableKey ?? (parentKey ? `${parentKey}-${i}` : String(i)));\n const children = row[childrenField];\n const hasChildren = Array.isArray(children) && children.length > 0;\n return {\n ...row,\n __stableKey: key,\n ...(hasChildren ? { [childrenField]: this.withStableKeys(children, key) } : {}),\n };\n });\n }\n\n /** Flatten tree using stable keys */\n private flattenTree(rows: any[], expanded: Set<string>, depth = 0): FlattenedTreeRow[] {\n const childrenField = this.config.childrenField ?? 'children';\n const result: FlattenedTreeRow[] = [];\n\n for (const row of rows) {\n const key = row.__stableKey ?? row.id ?? '?';\n const children = row[childrenField];\n const hasChildren = Array.isArray(children) && children.length > 0;\n const isExpanded = expanded.has(key);\n\n result.push({\n key,\n data: row,\n depth,\n hasChildren,\n isExpanded,\n parentKey: depth > 0 ? key.substring(0, key.lastIndexOf('-')) || null : null,\n });\n\n if (hasChildren && isExpanded) {\n result.push(...this.flattenTree(children, expanded, depth + 1));\n }\n }\n return result;\n }\n\n /** Sort tree recursively, keeping children with parents */\n private sortTree(rows: any[], field: string, dir: 1 | -1): any[] {\n const childrenField = this.config.childrenField ?? 'children';\n const sorted = [...rows].sort((a, b) => {\n const aVal = a[field],\n bVal = b[field];\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return -1;\n if (bVal == null) return 1;\n return aVal > bVal ? dir : aVal < bVal ? -dir : 0;\n });\n return sorted.map((row) => {\n const children = row[childrenField];\n return Array.isArray(children) && children.length > 0\n ? { ...row, [childrenField]: this.sortTree(children, field, dir) }\n : row;\n });\n }\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (this.flattenedRows.length === 0) return [...columns];\n\n const cols = [...columns] as ColumnConfig[];\n if (cols.length === 0) return cols;\n\n const firstCol = { ...cols[0] };\n const original = firstCol.viewRenderer;\n if ((original as any)?.__treeWrapped) return cols;\n\n const getConfig = () => this.config;\n const setIcon = this.setIcon.bind(this);\n const resolveIcon = this.resolveIcon.bind(this);\n\n const wrapped = (ctx: Parameters<NonNullable<typeof original>>[0]) => {\n const { value, row } = ctx;\n const { indentWidth = 20, showExpandIcons = true } = getConfig();\n\n const container = document.createElement('span');\n container.className = 'tree-cell';\n container.style.setProperty('--tree-depth', String(row.__treeDepth ?? 0));\n container.style.setProperty('--tbw-tree-indent', `${indentWidth}px`);\n\n if (row.__treeHasChildren && showExpandIcons) {\n const icon = document.createElement('span');\n icon.className = `tree-toggle${row.__treeExpanded ? ' expanded' : ''}`;\n setIcon(icon, resolveIcon(row.__treeExpanded ? 'collapse' : 'expand'));\n icon.setAttribute('data-tree-key', row.__treeKey);\n container.appendChild(icon);\n } else if (showExpandIcons) {\n const spacer = document.createElement('span');\n spacer.className = 'tree-spacer';\n container.appendChild(spacer);\n }\n\n const content = document.createElement('span');\n if (original) {\n const rendered = original(ctx);\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 return container;\n };\n\n (wrapped as any).__treeWrapped = true;\n firstCol.viewRenderer = wrapped;\n cols[0] = firstCol;\n return cols;\n }\n\n // #endregion\n\n // #region 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 const flatRow = key ? this.rowKeyMap.get(key) : null;\n if (!flatRow) return false;\n\n this.expandedKeys = toggleExpand(this.expandedKeys, key!);\n this.emit<TreeExpandDetail>('tree-expand', {\n key: key!,\n row: flatRow.data,\n expanded: this.expandedKeys.has(key!),\n depth: flatRow.depth,\n });\n this.requestRender();\n return true;\n }\n\n override onHeaderClick(event: HeaderClickEvent): boolean {\n if (this.flattenedRows.length === 0 || !event.column.sortable) return false;\n\n const { field } = event.column;\n if (!this.sortState || this.sortState.field !== field) {\n this.sortState = { field, direction: 1 };\n } else if (this.sortState.direction === 1) {\n this.sortState = { field, direction: -1 };\n } else {\n this.sortState = null;\n }\n\n // Sync grid sort indicator\n const gridEl = this.grid as unknown as GridWithConfig;\n if (gridEl._sortState !== undefined) {\n gridEl._sortState = this.sortState ? { ...this.sortState } : null;\n }\n\n this.emit('sort-change', { field, direction: this.sortState?.direction ?? 0 });\n this.requestRender();\n return true;\n }\n\n override afterRender(): void {\n const style = this.animationStyle;\n if (style === false || this.keysToAnimate.size === 0) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n const animClass = style === 'fade' ? 'tbw-tree-fade-in' : 'tbw-tree-slide-in';\n for (const rowEl of body.querySelectorAll('.data-grid-row')) {\n const cell = rowEl.querySelector('.cell[data-row]');\n const idx = cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n const key = this.flattenedRows[idx]?.key;\n\n if (key && this.keysToAnimate.has(key)) {\n rowEl.classList.add(animClass);\n rowEl.addEventListener('animationend', () => rowEl.classList.remove(animClass), { once: true });\n }\n }\n this.keysToAnimate.clear();\n }\n\n // #endregion\n\n // #region Public API\n\n expand(key: string): void {\n this.expandedKeys.add(key);\n this.requestRender();\n }\n\n collapse(key: string): void {\n this.expandedKeys.delete(key);\n this.requestRender();\n }\n\n toggle(key: string): void {\n this.expandedKeys = toggleExpand(this.expandedKeys, key);\n this.requestRender();\n }\n\n expandAll(): void {\n this.expandedKeys = expandAll(this.rows as any[], this.config);\n this.requestRender();\n }\n\n collapseAll(): void {\n this.expandedKeys = collapseAll();\n this.requestRender();\n }\n\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n getExpandedKeys(): string[] {\n return [...this.expandedKeys];\n }\n\n getFlattenedRows(): FlattenedTreeRow[] {\n return [...this.flattenedRows];\n }\n\n getRowByKey(key: string): any | undefined {\n return this.rowKeyMap.get(key)?.data;\n }\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 // #endregion\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 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 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 // #region 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 // #endregion\n}\n","/**\n * Column Visibility Plugin (Class-based)\n *\n * Provides a UI for column visibility control via the shell's tool panel system.\n * Column visibility is a core grid feature - this plugin provides:\n * - A tool panel for column visibility management (registered with the shell)\n * - Backward-compatible API methods that delegate to grid.setColumnVisible(), etc.\n *\n * The grid emits 'column-visibility' events when columns are shown/hidden,\n * allowing consumers to save user preferences.\n *\n * When a reorder plugin is present, column rows become draggable for reordering.\n * Drag-drop emits 'column-reorder-request' events that the ReorderPlugin can listen for.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, ToolPanelDefinition } from '../../core/types';\nimport type { VisibilityConfig } from './types';\nimport styles from './visibility.css?inline';\n\n/**\n * Detail for column-reorder-request events emitted when users drag-drop in the visibility panel.\n */\nexport interface ColumnReorderRequestDetail {\n /** The field name of the column to move */\n field: string;\n /** The source index (before move) */\n fromIndex: number;\n /** The target index (after move) */\n toIndex: number;\n}\n\n/**\n * Check if a column can be moved (respects lockPosition/suppressMovable).\n * Inlined to avoid importing from reorder plugin.\n */\nfunction canMoveColumn(column: ColumnConfig): boolean {\n const meta = column.meta ?? {};\n return meta.lockPosition !== true && meta.suppressMovable !== true;\n}\n\n/** Extended grid interface with visibility methods */\ninterface GridWithVisibility {\n shadowRoot: ShadowRoot | null;\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }>;\n setColumnVisible(field: string, visible: boolean): void;\n toggleColumnVisibility(field: string): void;\n showAllColumns(): void;\n isColumnVisible(field: string): boolean;\n requestRender(): void;\n openToolPanel(id: string): void;\n closeToolPanel(): void;\n toggleToolPanel(id: string): void;\n activeToolPanel: string | undefined;\n}\n\n/**\n * Column Visibility Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new VisibilityPlugin({ enabled: true, allowHideAll: false })\n * ```\n */\nexport class VisibilityPlugin extends BaseGridPlugin<VisibilityConfig> {\n readonly name = 'visibility';\n override readonly version = '1.0.0';\n\n /** Tool panel ID for shell integration */\n static readonly PANEL_ID = 'columns';\n\n protected override get defaultConfig(): Partial<VisibilityConfig> {\n return {\n allowHideAll: false,\n };\n }\n\n // #region Internal State\n private columnListElement: HTMLElement | null = null;\n\n // Drag state for reorder integration\n private isDragging = false;\n private draggedField: string | null = null;\n private draggedIndex: number | null = null;\n private dropIndex: number | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.columnListElement = null;\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n }\n // #endregion\n\n // #region Shell Integration\n\n /**\n * Register the column visibility tool panel with the shell.\n */\n override getToolPanel(): ToolPanelDefinition | undefined {\n return {\n id: VisibilityPlugin.PANEL_ID,\n title: 'Columns',\n icon: '☰',\n tooltip: 'Column visibility',\n order: 100, // High order so it appears last\n render: (container) => this.renderPanelContent(container),\n };\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Show the visibility sidebar panel.\n */\n show(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.openToolPanel(VisibilityPlugin.PANEL_ID);\n }\n\n /**\n * Hide the visibility sidebar panel.\n */\n hide(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.closeToolPanel();\n }\n\n /**\n * Toggle the visibility sidebar panel.\n */\n toggle(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.toggleToolPanel(VisibilityPlugin.PANEL_ID);\n }\n\n /**\n * Check if a specific column is visible.\n * Delegates to grid.isColumnVisible().\n * @param field - The field name to check\n * @returns True if the column is visible\n */\n isColumnVisible(field: string): boolean {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid.isColumnVisible(field);\n }\n\n /**\n * Set visibility for a specific column.\n * Delegates to grid.setColumnVisible().\n * @param field - The field name of the column\n * @param visible - Whether the column should be visible\n */\n setColumnVisible(field: string, visible: boolean): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.setColumnVisible(field, visible);\n }\n\n /**\n * Get list of all visible column fields.\n * @returns Array of visible field names\n */\n getVisibleColumns(): string[] {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid\n .getAllColumns()\n .filter((c) => c.visible)\n .map((c) => c.field);\n }\n\n /**\n * Get list of all hidden column fields.\n * @returns Array of hidden field names\n */\n getHiddenColumns(): string[] {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid\n .getAllColumns()\n .filter((c) => !c.visible)\n .map((c) => c.field);\n }\n\n /**\n * Show all columns.\n * Delegates to grid.showAllColumns().\n */\n showAll(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.showAllColumns();\n }\n\n /**\n * Toggle visibility for a specific column.\n * Delegates to grid.toggleColumnVisibility().\n * @param field - The field name of the column\n */\n toggleColumn(field: string): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.toggleColumnVisibility(field);\n }\n\n /**\n * Show a specific column.\n * Delegates to grid.setColumnVisible().\n * @param field - The field name of the column to show\n */\n showColumn(field: string): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.setColumnVisible(field, true);\n }\n\n /**\n * Hide a specific column.\n * Delegates to grid.setColumnVisible().\n * @param field - The field name of the column to hide\n */\n hideColumn(field: string): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.setColumnVisible(field, false);\n }\n\n /**\n * Get all columns with their visibility status.\n * Useful for building visibility UI.\n * @returns Array of column info with visibility status\n */\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid.getAllColumns();\n }\n\n /**\n * Check if the sidebar panel is currently open.\n * @returns True if the panel is open\n */\n isPanelVisible(): boolean {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid.activeToolPanel === VisibilityPlugin.PANEL_ID;\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Render the panel content into the shell's tool panel container.\n * Returns a cleanup function.\n */\n private renderPanelContent(container: HTMLElement): (() => void) | void {\n const grid = this.grid as unknown as GridWithVisibility;\n\n // Create content wrapper\n const wrapper = document.createElement('div');\n wrapper.className = 'tbw-visibility-content';\n\n // Column list container\n const columnList = document.createElement('div');\n columnList.className = 'tbw-visibility-list';\n wrapper.appendChild(columnList);\n\n // Show all button\n const showAllBtn = document.createElement('button');\n showAllBtn.className = 'tbw-visibility-show-all';\n showAllBtn.textContent = 'Show All';\n showAllBtn.addEventListener('click', () => {\n grid.showAllColumns();\n this.rebuildToggles(columnList);\n });\n wrapper.appendChild(showAllBtn);\n\n // Store reference\n this.columnListElement = columnList;\n\n // Build initial toggles\n this.rebuildToggles(columnList);\n\n // Append to container\n container.appendChild(wrapper);\n\n // Return cleanup function\n return () => {\n this.columnListElement = null;\n wrapper.remove();\n };\n }\n\n /**\n * Check if a reorder plugin is present (by name to avoid static import).\n */\n private hasReorderPlugin(): boolean {\n const plugin = this.grid?.getPluginByName?.('reorder');\n // Duck-type check - just verify the plugin exists and has a moveColumn method\n return !!(plugin && typeof (plugin as { moveColumn?: unknown }).moveColumn === 'function');\n }\n\n /**\n * Build the column toggle checkboxes.\n * When a reorder plugin is present, adds drag handles for reordering.\n */\n private rebuildToggles(columnList: HTMLElement): void {\n const grid = this.grid as unknown as GridWithVisibility;\n const reorderEnabled = this.hasReorderPlugin();\n\n columnList.innerHTML = '';\n\n // getAllColumns() now returns columns in their effective display order\n const allColumns = grid.getAllColumns();\n\n for (let i = 0; i < allColumns.length; i++) {\n const col = allColumns[i];\n const label = col.header || col.field;\n\n const row = document.createElement('div');\n row.className = col.lockVisible ? 'tbw-visibility-row locked' : 'tbw-visibility-row';\n row.setAttribute('data-field', col.field);\n row.setAttribute('data-index', String(i));\n\n // Add drag handle if reorder is enabled\n if (reorderEnabled && canMoveColumn(col as unknown as ColumnConfig)) {\n row.draggable = true;\n row.classList.add('reorderable');\n\n this.setupDragListeners(row, col.field, i, columnList);\n }\n\n const labelWrapper = document.createElement('label');\n labelWrapper.className = 'tbw-visibility-label';\n\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.checked = col.visible;\n checkbox.disabled = col.lockVisible ?? false;\n checkbox.addEventListener('change', () => {\n grid.toggleColumnVisibility(col.field);\n // Refresh after toggle (grid may re-render)\n setTimeout(() => this.rebuildToggles(columnList), 0);\n });\n\n const text = document.createElement('span');\n text.textContent = label;\n\n labelWrapper.appendChild(checkbox);\n labelWrapper.appendChild(text);\n\n // Add drag handle icon if reorderable\n if (reorderEnabled && canMoveColumn(col as unknown as ColumnConfig)) {\n const handle = document.createElement('span');\n handle.className = 'tbw-visibility-handle';\n // Use grid-level icons (fall back to defaults)\n this.setIcon(handle, this.resolveIcon('dragHandle'));\n handle.title = 'Drag to reorder';\n row.appendChild(handle);\n }\n\n row.appendChild(labelWrapper);\n columnList.appendChild(row);\n }\n }\n\n /**\n * Set up drag-and-drop event listeners for a row.\n * On drop, emits a 'column-reorder-request' event for other plugins to handle.\n */\n private setupDragListeners(row: HTMLElement, field: string, index: number, columnList: HTMLElement): void {\n row.addEventListener('dragstart', (e: DragEvent) => {\n this.isDragging = true;\n this.draggedField = field;\n this.draggedIndex = index;\n\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', field);\n }\n\n row.classList.add('dragging');\n });\n\n row.addEventListener('dragend', () => {\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n\n columnList.querySelectorAll('.tbw-visibility-row').forEach((r) => {\n r.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\n });\n });\n\n row.addEventListener('dragover', (e: DragEvent) => {\n e.preventDefault();\n if (!this.isDragging || this.draggedField === field) return;\n\n const rect = row.getBoundingClientRect();\n const midY = rect.top + rect.height / 2;\n\n this.dropIndex = e.clientY < midY ? index : index + 1;\n\n // Clear other row highlights\n columnList.querySelectorAll('.tbw-visibility-row').forEach((r) => {\n if (r !== row) r.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n row.classList.add('drop-target');\n row.classList.toggle('drop-before', e.clientY < midY);\n row.classList.toggle('drop-after', e.clientY >= midY);\n });\n\n row.addEventListener('dragleave', () => {\n row.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n row.addEventListener('drop', (e: DragEvent) => {\n e.preventDefault();\n const draggedField = this.draggedField;\n const draggedIndex = this.draggedIndex;\n const dropIndex = this.dropIndex;\n\n if (!this.isDragging || draggedField === null || draggedIndex === null || dropIndex === null) {\n return;\n }\n\n // Calculate the effective target index\n const effectiveToIndex = dropIndex > draggedIndex ? dropIndex - 1 : dropIndex;\n\n if (effectiveToIndex !== draggedIndex) {\n // Emit a request event - other plugins (like ReorderPlugin) can listen and handle\n const detail: ColumnReorderRequestDetail = {\n field: draggedField,\n fromIndex: draggedIndex,\n toIndex: effectiveToIndex,\n };\n this.emit<ColumnReorderRequestDetail>('column-reorder-request', detail);\n\n // Rebuild the panel after reorder (deferred to allow re-render)\n setTimeout(() => {\n this.rebuildToggles(columnList);\n }, 0);\n }\n });\n }\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\n}\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","DEFAULT_ANIMATION_CONFIG","DEFAULT_GRID_ICONS","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","options","rowHeight","container","viewportEl","scrollEl","visibleHeight","y","rowIndex","vStart","vEnd","scrollArea","offsets","cellRect","scrollAreaRect","cellLeft","cellRight","visibleLeft","visibleRight","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","hasEditingCell","isActivelyEditedRow","renderInlineRow","fastPatchRow","children","inlineEnterEdit","isChanged","hasChangedClass","colsLen","childLen","minLen","focusRow","focusCol","hasSpecialCols","rowIndexStr","shouldHaveFocus","hasFocus","produced","displayStr","formatted","isTrue","editMode","gridEl","fragment","colIndex","format","compiled","tplHolder","viewRenderer","externalView","needsSanitization","spec","placeholder","context","output","blocked","rawTpl","tentative","textContent","currentRowIndex","currentColIndex","currentRowData","currentCol","startRowEdit","rowElCurrent","col2","selectEl","newVal","commitCellValue","isDbl","firstCell","cellEl","active","targetCell","editor","isSafePropertyKey","exitRowEdit","revert","snapshot","current","val","rowIdx","colIdx","rowEl2","newValue","firstTime","originalValue","editFinalized","commit","cancel","inputLike","editorHost","editorSpec","clone","compiledEditor","node","g","hasHTMLInput","defaultComparator","builtInSort","comparator","direction","rA","rB","finalizeSortResult","sortedRows","dir","r","renderHeader","toggleSort","applySort","h","result","setIcon","element","icon","headerRow","maybeTpl","span","icons","iconValue","handle","createResizeController","resizeState","pendingRaf","prevCursor","prevUserSelect","onMove","delta","width","justFinishedResize","onUp","hadResize","rect","iconToString","createShellState","shouldRenderShellHeader","renderShellHeader","toolPanelIcon","title","hasTitle","iconStr","configButtons","hasConfigButtons","hasApiButtons","hasLightDomButtons","hasPanels","showSeparator","sortedConfigButtons","sortedApiButtons","toolbarHtml","btn","isOpen","renderShellBody","gridContentHtml","position","hasPanel","expandIcon","collapseIcon","sortedPanels","isSinglePanel","accordionHtml","panel","isExpanded","iconHtml","chevronHtml","panelHtml","parseLightDomShell","headerEl","headerContents","toolButtons","setupShellEventListeners","shadowRoot","callbacks","toolbar","customBtn","btnId","accordion","sectionId","setupToolPanelResize","onResize","shellBody","minWidth","startX","startWidth","maxWidth","isResizing","onMouseMove","newWidth","onMouseUp","finalWidth","onMouseDown","renderCustomToolbarButtons","allButtons","slot","existingCleanup","cleanup","renderHeaderContent","contentArea","sortedContents","content","renderPanelContent","panelId","section","chevron","updateToolbarActiveStates","panelToggle","updatePanelState","getToolbarButtonsInfo","cleanupShellState","createShellController","initialized","controller","firstPanel","shadow","updateAccordionSectionState","otherId","otherPanel","contentEl","renderAccordionSectionContent","contentId","button","buttonId","disabled","apiBtn","expanded","PluginManager","renderer","PluginClass","total","beforeRowIndex","scrollTop","adjustedStart","pluginStart","row","query","responses","response","event","focusedCell","left","right","skipScroll","panels","contents","DataGridElement","#shadow","#initialized","#readyPromise","#readyResolve","#rows","#columns","#gridConfig","#fitMode","#editOn","#effectiveConfig","#connected","#scrollRaf","#pendingScrollTop","#hasScrollPlugins","#renderRowHook","#isDragging","#touchStartY","#touchStartX","#touchScrollTop","#touchScrollLeft","#touchLastY","#touchLastX","#touchLastTime","#touchVelocityY","#touchVelocityX","#momentumRaf","#eventAbortController","#resizeObserver","#pluginManager","#stateChangeHandler","#initialColumnState","#shellState","#shellController","#resizeCleanup","#baseColumns","oldValue","#onRowsChanged","#onColsChanged","#onGridConfigChanged","#onFitChanged","#onEditModeChanged","#injectStyles","eventName","detail","#emit","sheet","styles","#rebuildRowModel","#processColumns","#initializePlugins","pluginsConfig","#injectAllPluginStyles","allStyles","styleEl","#updatePluginConfigs","#destroyPlugins","#collectPluginShellContributions","pluginPanels","pluginContents","#mergeEffectiveConfig","#render","#afterConnect","prop","gridRoot","defaultOpen","signal","#setup","fauxScrollbar","rowsEl","currentScrollTop","rawStart","evenAlignedStart","subPixelOffset","#onScrollBatched","gridContentEl","isHorizontal","scrollLeft","scrollWidth","clientWidth","scrollHeight","clientHeight","currentY","currentX","now","deltaY","deltaX","dt","maxScrollY","canScrollVertically","canScrollHorizontally","maxScrollX","#startMomentumScroll","#handleMouseDown","#handleMouseMove","#handleMouseUp","userRowHeight","firstRow","measuredHeight","#updateAriaSelection","isActiveRow","sourceColumns","visibleCols","hiddenCols","processedColumns","processedMap","processed","originalRows","processedRows","base","domCols","map","exist","internal","existingCols","#applyAnimationConfig","enabled","#renderVisibleRows","configCols","existingHiddenMap","seeded","#applyColumnStateInternal","allCols","scrollEvent","cellClickEvent","rowClickEvent","headerClickEvent","#buildCellMouseEvent","elAtPoint","animate","scrollY","scrollX","silent","visible","wasHidden","willBeHidden","isCurrentlyHidden","order","columnMap","reordered","#applyColumnState","force","totalRows","fauxScrollHeight","viewportHeight","heightDiff","innerGrid","iterations","maxIterations","extraHeightBefore","pluginAdjustedStart","visibleCount","footerHeight","pluginExtraHeight","scrollAreaEl","hScrollbarHeight","extraHeightBeforeStart","shellConfig","hasShell","accordionIcons","shellHeaderHtml","shellBodyHtml","#setupShellListeners","#handleToolbarButtonClick","configBtn","PLUGIN_QUERIES","BaseGridPlugin","userIcons","iconKey","pluginOverride","message","GridClasses","GridDataAttrs","GridSelectors","GridCSSVars","DGEvents","PluginEvents","builtInAggregators","acc","sum","customAggregators","aggregatorRegistry","ref","builtInValueAggregators","vals","getValueAggregator","aggFunc","runValueAggregator","registerAggregator","unregisterAggregator","getAggregator","runAggregator","listAggregators","formatCellValue","delimiter","newline","buildClipboardText","params","selectedIndices","visibleColumns","lines","sortedIndices","idx","cells","copyToClipboard","text","textarea","success","parseClipboardText","normalizedText","currentRow","currentCell","inQuotes","char","readFromClipboard","ClipboardPlugin","isCopy","isPaste","#handleCopy","#handlePaste","selectionPlugin","#getSelectionPlugin","selectedRows","hasRowSelection","ranges","hasRangeSelection","hasCellSelection","rowCount","columnCount","range","#buildRangeText","#buildCellText","#buildSingleCellText","parsed","formattedValue","startRow","startCol","endRow","endCol","minRow","minCol","rangeColumns","indices","DEFAULT_COLUMN_WIDTH","parseColumnWidth","numeric","getColumnWidths","computeColumnOffsets","offset","computeTotalWidth","getVisibleColumnRange","viewportWidth","columnOffsets","columnWidths","overscan","binarySearchFirstVisible","rightEdge","low","high","mid","shouldVirtualize","threshold","autoEnable","ColumnVirtualizationPlugin","isVirtualized","viewport","leftPadding","bodyRows","rowsContainer","columnIndex","buildMenuItems","items","isItemDisabled","createMenuElement","onAction","submenuArrow","menu","separator","menuItem","shortcut","arrow","subMenuItems","subMenu","positionMenu","x","menuRect","top","globalClickHandler","globalKeydownHandler","globalStyleSheet","globalHandlerRefCount","defaultItems","ContextMenuPlugin","contextMenuStyles","fullParams","formatCsvValue","quote","buildCsv","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","FilteringPlugin","filterList","newCacheKey","hasFilter","filterBtn","style","filterPanelStyles","buttonEl","uniqueValues","excludedSet","currentSearchText","excluded","operator","valueTo","usedCustomRenderer","panelRect","excludedValues","searchContainer","searchInput","actionsRow","selectAllLabel","selectAllCheckbox","selectAllText","updateSelectAllState","checkState","allChecked","noneChecked","newState","renderVisibleItems","valuesContainer","spacer","contentContainer","filteredValues","createItem","strValue","checkbox","totalItems","window","renderValues","filterText","lowerFilter","noMatch","debounceTimer","buttonRow","applyBtn","isChecked","original","clearBtn","handleResult","filterModel","computeColumnGroups","explicitMap","groupsOrdered","pushImplicit","startIdx","cols","prev","run","runStart","id","group","applyGroupedHeaderCellClasses","headerRowEl","groups","fieldToGroup","gid","last","buildGroupHeaderRow","groupRow","startIndex","isImplicit","hasColumnGroups","GroupingColumnsPlugin","existingGroupRow","groupId","buildGroupedRowModel","groupOn","parent","rawVal","depthIdx","seg","composite","flat","visit","toggleGroupExpansion","expandedKeys","newSet","expandAllGroups","keys","collapseAllGroups","getGroupRowCount","GroupingRowsPlugin","grouped","currentVisibleKeys","_rowIndex","toggleExpand","handleToggle","body","animClass","labelText","count","aggregators","groupRows","gridTemplate","firstColAgg","aggResult","aggRef","newKeys","toggleDetailRow","expandedRows","newExpanded","expandDetailRow","collapseDetailRow","isDetailExpanded","createDetailElement","detailRow","detailCell","MasterDetailPlugin","durationStr","detailEl","onComplete","firstCol","originalRenderer","wrappedRenderer","renderCtx","toggle","rendered","#syncDetailRows","visibleRowMap","dataRows","existingDetails","forIndex","isStillExpanded","isRowVisible","existingDetail","totalHeight","configHeight","expandedIndices","minStart","cumulativeExtraHeight","actualRowTop","detailHeight","actualDetailBottom","applySorts","sorts","sort","aVal","bVal","shiftKey","maxColumns","getSortIndex","sortModel","getSortDirection","MultiSortPlugin","showIndex","sortIndex","sortDir","indicator","badge","model","existingIndex","newEntry","getLeftStickyColumns","getRightStickyColumns","hasStickyColumns","applyStickyOffsets","fieldToIndex","clearStickyOffsets","PinnedColumnsPlugin","sticky","metaSticky","stickyLeftCells","stickyRightCells","isAggregatorConfig","def","createInfoBarElement","pinnedRows","center","filteredCount","selectedCount","panelEl","renderCustomPanel","createAggregationContainer","renderAggregationRows","rowConfig","formatter","aggDef","aggFn","staticVal","buildContext","filterState","PinnedRowsPlugin","aggregationRows","topRows","bottomRows","hasInfoContent","hasBottomInfoBar","needsFooter","newInfoBar","getPivotAggregator","validatePivotConfig","errors","createValueKey","columnValues","valueField","buildPivot","rowGroupFields","columnGroupFields","valueFields","columnKeys","getUniqueColumnKeys","pivotRows","buildHierarchicalPivotRows","totals","calculateTotals","grandTotal","columnFields","groupByField","depth","parentKey","aggregateValues","calculateRowTotal","currentField","remainingFields","hasChildren","groupValue","rowKey","colKey","vf","nums","aggregator","aggregatedResult","valueKey","sumRows","flattenPivotRows","defaultExpanded","flatten","child","getAllGroupKeys","collectKeys","AGG_FUNCS","renderPivotPanel","isActive","wrapper","createSection","createOptionsPanel","createFieldZone","createValuesZone","createAvailableFieldsZone","contentFactory","zoneType","zone","currentFields","createFieldChip","chip","fieldInfo","removeBtn","currentValues","createValueChip","labelWrapper","aggSelect","option","allFields","usedFields","availableFields","empty","createCheckbox","checked","onChange","renderPivotGroupRow","indent","renderPivotLeafRow","renderPivotGrandTotalRow","PivotPlugin","allKeys","indentWidth","flatRows","pr","pivotColumns","rowGroupHeaders","valueHeader","pivotRow","grandTotalRow","fields","targetZone","fieldIndex","canMoveColumn","meta","moveColumn","fromIndex","toIndex","removed","ReorderPlugin","pluginAllows","orderIndex","midX","draggedField","draggedIndex","dropIndex","effectiveToIndex","currentOrder","newOrder","targetColumn","originalOrder","positions","oldPositions","deltas","oldLeft","duration","applyChange","movedFields","newLeft","animation","normalizeRange","toPublicRange","normalized","toPublicRanges","isCellInRange","isCellInAnyRange","getCellsInRange","getAllCellsInRanges","cellMap","createRangeFromAnchor","anchor","buildSelectionEvent","colCount","SelectionPlugin","originalEvent","#buildEvent","ctrlKey","newRange","isNavKey","allRange","_event","#applySelectionClasses","allRows","getBlockNumber","blockSize","getBlockRange","blockNumber","getRequiredBlocks","startBlock","endBlock","blocks","loadBlock","dataSource","getRowFromCache","loadedBlocks","block","indexInBlock","SCROLL_DEBOUNCE_MS","ServerSidePlugin","gridRef","requiredBlocks","blockNum","cached","generateRowKey","expandAll","childrenField","childKeys","collapseAll","getPathToKey","targetKey","childPath","expandToKey","existingExpanded","detectTreeStructure","inferChildrenField","commonArrayFields","TreePlugin","data","currentKeys","getConfig","resolveIcon","wrapped","showExpandIcons","flatRow","pushAction","action","maxSize","undoStack","undo","redo","redoStack","canUndo","canRedo","clearHistory","createEditAction","UndoRedoPlugin","isUndo","isRedo","VisibilityPlugin","columnList","showAllBtn","reorderEnabled","midY"],"mappings":"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"}
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/utils.ts","../../../../libs/grid/src/lib/core/internal/keyboard.ts","../../../../libs/grid/src/lib/core/internal/rows.ts","../../../../libs/grid/src/lib/core/internal/editing.ts","../../../../libs/grid/src/lib/core/internal/event-delegation.ts","../../../../libs/grid/src/lib/core/internal/sorting.ts","../../../../libs/grid/src/lib/core/internal/header.ts","../../../../libs/grid/src/lib/core/internal/idle-scheduler.ts","../../../../libs/grid/src/lib/core/internal/resize.ts","../../../../libs/grid/src/lib/core/internal/dom-builder.ts","../../../../libs/grid/src/lib/core/internal/shell.ts","../../../../libs/grid/src/lib/core/internal/touch-scroll.ts","../../../../libs/grid/src/lib/core/plugin/plugin-manager.ts","../../../../libs/grid/src/lib/core/grid.ts","../../../../libs/grid/src/lib/core/plugin/types.ts","../../../../libs/grid/src/lib/core/plugin/base-plugin.ts","../../../../libs/grid/src/lib/core/constants.ts","../../../../libs/grid/src/public.ts","../../../../libs/grid/src/lib/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/pivot-panel.ts","../../../../libs/grid/src/lib/plugins/pivot/pivot-rows.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/selection/range-selection.ts","../../../../libs/grid/src/lib/plugins/selection/SelectionPlugin.ts","../../../../libs/grid/src/lib/plugins/server-side/datasource.ts","../../../../libs/grid/src/lib/plugins/server-side/ServerSidePlugin.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/plugins/undo-redo/history.ts","../../../../libs/grid/src/lib/plugins/undo-redo/UndoRedoPlugin.ts","../../../../libs/grid/src/lib/plugins/visibility/VisibilityPlugin.ts"],"sourcesContent":["/**\n * Column State Module\n *\n * Handles collection and application of column state for persistence.\n * State includes user-driven changes: order, width, visibility, and sort.\n * Plugins can contribute additional state via getColumnState/applyColumnState hooks.\n */\n\nimport type { BaseGridPlugin } from '../plugin';\nimport type {\n ColumnConfig,\n ColumnInternal,\n ColumnSortState,\n ColumnState,\n GridColumnState,\n InternalGrid,\n} from '../types';\n\n/** Debounce timeout for state change events */\nconst STATE_CHANGE_DEBOUNCE_MS = 100;\n\n/**\n * Get sort state for a column from the grid's sortState.\n */\nfunction getSortState(grid: InternalGrid): Map<string, ColumnSortState> {\n const sortMap = new Map<string, ColumnSortState>();\n\n // Core sort state (single column)\n if (grid._sortState) {\n sortMap.set(grid._sortState.field, {\n direction: grid._sortState.direction === 1 ? 'asc' : 'desc',\n priority: 0,\n });\n }\n\n return sortMap;\n}\n\n/**\n * Collect column state from the grid and all plugins.\n * Returns a complete GridColumnState object ready for serialization.\n */\nexport function collectColumnState<T>(grid: InternalGrid<T>, plugins: BaseGridPlugin[]): GridColumnState {\n const columns = grid._columns;\n const sortStates = getSortState(grid);\n\n return {\n columns: columns.map((col, index) => {\n // 1. Core state\n const state: ColumnState = {\n field: col.field,\n order: index,\n visible: true, // If it's in _columns, it's visible (hidden columns are filtered out)\n };\n\n // Include width if set (either from config or resize)\n const internalCol = col as ColumnInternal<T>;\n if (internalCol.__renderedWidth !== undefined) {\n state.width = internalCol.__renderedWidth;\n } else if (col.width !== undefined) {\n state.width = typeof col.width === 'string' ? parseFloat(col.width) : col.width;\n }\n\n // Include sort state if present\n const sortState = sortStates.get(col.field);\n if (sortState) {\n state.sort = sortState;\n }\n\n // 2. Collect from each plugin\n for (const plugin of plugins) {\n if (plugin.getColumnState) {\n const pluginState = plugin.getColumnState(col.field);\n if (pluginState) {\n Object.assign(state, pluginState);\n }\n }\n }\n\n return state;\n }),\n };\n}\n\n/**\n * Apply column state to the grid and all plugins.\n * Modifies the grid's internal state and triggers plugin state restoration.\n *\n * @param grid - The grid instance\n * @param state - The state to apply\n * @param allColumns - All available columns (including hidden ones)\n * @param plugins - Plugins that may have applyColumnState hooks\n */\nexport function applyColumnState<T>(\n grid: InternalGrid<T>,\n state: GridColumnState,\n allColumns: ColumnConfig<T>[],\n plugins: BaseGridPlugin[],\n): void {\n if (!state.columns || state.columns.length === 0) return;\n\n const stateMap = new Map(state.columns.map((s) => [s.field, s]));\n\n // 1. Apply width and visibility to columns\n const updatedColumns = allColumns.map((col) => {\n const s = stateMap.get(col.field);\n if (!s) return col;\n\n const updated: ColumnInternal<T> = { ...col };\n\n // Apply width\n if (s.width !== undefined) {\n updated.width = s.width;\n updated.__renderedWidth = s.width;\n }\n\n // Apply visibility (hidden is inverse of visible)\n if (s.visible !== undefined) {\n updated.hidden = !s.visible;\n }\n\n return updated;\n });\n\n // 2. Reorder columns based on state\n updatedColumns.sort((a, b) => {\n const orderA = stateMap.get(a.field)?.order ?? Infinity;\n const orderB = stateMap.get(b.field)?.order ?? Infinity;\n return orderA - orderB;\n });\n\n // 3. Update grid's internal columns\n grid._columns = updatedColumns as ColumnInternal<T>[];\n\n // 4. Apply sort state (core single-column sort)\n // Find the column with highest sort priority\n const sortedByPriority = state.columns\n .filter((s) => s.sort !== undefined)\n .sort((a, b) => (a.sort?.priority ?? 0) - (b.sort?.priority ?? 0));\n\n if (sortedByPriority.length > 0) {\n const primarySort = sortedByPriority[0];\n if (primarySort.sort) {\n grid._sortState = {\n field: primarySort.field,\n direction: primarySort.sort.direction === 'asc' ? 1 : -1,\n };\n }\n } else {\n grid._sortState = null;\n }\n\n // 5. Let each plugin apply its state\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n for (const colState of state.columns) {\n plugin.applyColumnState(colState.field, colState);\n }\n }\n }\n}\n\n/**\n * Create a state change handler with debouncing.\n * Returns a function that, when called, will eventually emit the state change event.\n */\nexport function createStateChangeHandler<T>(\n grid: InternalGrid<T>,\n getPlugins: () => BaseGridPlugin[],\n emit: (detail: GridColumnState) => void,\n): () => void {\n let timeoutId: ReturnType<typeof setTimeout> | null = null;\n\n return () => {\n // Clear any pending timeout\n if (timeoutId !== null) {\n clearTimeout(timeoutId);\n }\n\n // Schedule the emit\n timeoutId = setTimeout(() => {\n timeoutId = null;\n const state = collectColumnState(grid, getPlugins());\n emit(state);\n }, STATE_CHANGE_DEBOUNCE_MS);\n };\n}\n\n/**\n * Compare two column states to check if they are equal.\n * Useful for preventing duplicate state change events.\n */\nexport function areColumnStatesEqual(a: GridColumnState, b: GridColumnState): boolean {\n if (a.columns.length !== b.columns.length) return false;\n\n for (let i = 0; i < a.columns.length; i++) {\n const colA = a.columns[i];\n const colB = b.columns[i];\n\n if (colA.field !== colB.field) return false;\n if (colA.order !== colB.order) return false;\n if (colA.visible !== colB.visible) return false;\n if (colA.width !== colB.width) return false;\n\n // Compare sort state\n const sortA = colA.sort;\n const sortB = colB.sort;\n if ((sortA === undefined) !== (sortB === undefined)) return false;\n if (sortA && sortB) {\n if (sortA.direction !== sortB.direction) return false;\n if (sortA.priority !== sortB.priority) return false;\n }\n }\n\n return true;\n}\n\n// ============================================================================\n// Column State API (High-level functions)\n// ============================================================================\n// These functions are extracted from grid.ts to reduce the god object size.\n// Grid.ts delegates to these functions for all state-related operations.\n\n/**\n * State manager for a grid instance.\n * Encapsulates the state change handler and initial state storage.\n */\nexport interface ColumnStateManager {\n /** The initial state to apply after initialization */\n initialState: GridColumnState | undefined;\n /** Debounced state change handler */\n stateChangeHandler: (() => void) | undefined;\n}\n\n/**\n * Create a column state manager for a grid.\n */\nexport function createColumnStateManager(): ColumnStateManager {\n return {\n initialState: undefined,\n stateChangeHandler: undefined,\n };\n}\n\n/**\n * Get the current column state from the grid.\n * @param grid - The grid instance\n * @param plugins - Array of attached plugins\n * @returns Serializable column state object\n */\nexport function getGridColumnState<T>(grid: InternalGrid<T>, plugins: BaseGridPlugin[]): GridColumnState {\n return collectColumnState(grid, plugins);\n}\n\n/**\n * Set column state on a grid, storing for later if not yet initialized.\n * @param grid - The grid instance\n * @param state - The state to apply\n * @param manager - The column state manager\n * @param isInitialized - Whether the grid is initialized\n * @param applyNow - Function to apply the state immediately\n */\nexport function setGridColumnState<T>(\n state: GridColumnState | undefined,\n manager: ColumnStateManager,\n isInitialized: boolean,\n applyNow: (state: GridColumnState) => void,\n): void {\n if (!state) return;\n\n // Store for use after initialization if called before ready\n manager.initialState = state;\n\n // If already initialized, apply immediately\n if (isInitialized) {\n applyNow(state);\n }\n}\n\n/**\n * Request a state change event emission (debounced).\n * @param grid - The grid instance\n * @param manager - The column state manager\n * @param getPlugins - Function to get attached plugins\n * @param emit - Function to emit the event\n */\nexport function requestGridStateChange<T>(\n grid: InternalGrid<T>,\n manager: ColumnStateManager,\n getPlugins: () => BaseGridPlugin[],\n emit: (state: GridColumnState) => void,\n): void {\n if (!manager.stateChangeHandler) {\n manager.stateChangeHandler = createStateChangeHandler(grid, getPlugins, emit);\n }\n manager.stateChangeHandler();\n}\n\n/**\n * Reset column state to initial configuration.\n * @param grid - The grid instance\n * @param manager - The column state manager\n * @param plugins - Array of attached plugins\n * @param callbacks - Grid callbacks for triggering updates\n */\nexport function resetGridColumnState<T>(\n grid: InternalGrid<T>,\n manager: ColumnStateManager,\n plugins: BaseGridPlugin[],\n callbacks: {\n mergeEffectiveConfig: () => void;\n setup: () => void;\n requestStateChange: () => void;\n },\n): void {\n // Clear initial state\n manager.initialState = undefined;\n\n // Clear hidden flag on all columns\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n // Reset sort state\n grid._sortState = null;\n grid.__originalOrder = [];\n\n // Re-initialize columns from config\n callbacks.mergeEffectiveConfig();\n callbacks.setup();\n\n // Notify plugins to reset their state\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n // Pass empty state to indicate reset\n for (const col of grid._columns) {\n plugin.applyColumnState(col.field, {\n field: col.field,\n order: 0,\n visible: true,\n });\n }\n }\n }\n\n // Emit state change\n callbacks.requestStateChange();\n}\n\n// ============================================================================\n// Column Visibility API\n// ============================================================================\n// Pure functions for column visibility operations.\n\n/** Callbacks for visibility changes that need grid integration */\nexport interface VisibilityCallbacks {\n emit: (eventName: string, detail: unknown) => void;\n clearRowPool: () => void;\n setup: () => void;\n requestStateChange: () => void;\n}\n\n/**\n * Set the visibility of a column.\n * @returns true if visibility changed, false otherwise\n */\nexport function setColumnVisible<T>(\n grid: InternalGrid<T>,\n field: string,\n visible: boolean,\n callbacks: VisibilityCallbacks,\n): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n\n if (!col) return false;\n if (!visible && col.lockVisible) return false;\n\n // Ensure at least one column remains visible\n if (!visible) {\n const remainingVisible = allCols.filter((c) => !c.hidden && c.field !== field).length;\n if (remainingVisible === 0) return false;\n }\n\n const wasHidden = !!col.hidden;\n if (wasHidden === !visible) return false; // No change\n\n col.hidden = !visible;\n\n callbacks.emit('column-visibility', {\n field,\n visible,\n visibleColumns: allCols.filter((c) => !c.hidden).map((c) => c.field),\n });\n\n callbacks.clearRowPool();\n callbacks.setup();\n callbacks.requestStateChange();\n return true;\n}\n\n/**\n * Toggle column visibility.\n * @returns true if toggled, false if column not found or locked\n */\nexport function toggleColumnVisibility<T>(\n grid: InternalGrid<T>,\n field: string,\n callbacks: VisibilityCallbacks,\n): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n return col ? setColumnVisible(grid, field, !!col.hidden, callbacks) : false;\n}\n\n/**\n * Check if a column is visible.\n */\nexport function isColumnVisible<T>(grid: InternalGrid<T>, field: string): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n return col ? !col.hidden : false;\n}\n\n/**\n * Show all columns.\n */\nexport function showAllColumns<T>(grid: InternalGrid<T>, callbacks: VisibilityCallbacks): void {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n if (!allCols.some((c) => c.hidden)) return;\n\n allCols.forEach((c) => (c.hidden = false));\n\n callbacks.emit('column-visibility', {\n visibleColumns: allCols.map((c) => c.field),\n });\n\n callbacks.clearRowPool();\n callbacks.setup();\n callbacks.requestStateChange();\n}\n\n/**\n * Get all columns with visibility info.\n */\nexport function getAllColumns<T>(\n grid: InternalGrid<T>,\n): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n return allCols.map((c) => ({\n field: c.field,\n header: c.header || c.field,\n visible: !c.hidden,\n lockVisible: c.lockVisible,\n }));\n}\n\n/**\n * Get current column order.\n */\nexport function getColumnOrder<T>(grid: InternalGrid<T>): string[] {\n return grid._columns.map((c) => c.field);\n}\n\n/**\n * Set column order.\n */\nexport function setColumnOrder<T>(\n grid: InternalGrid<T>,\n order: string[],\n callbacks: { renderHeader: () => void; updateTemplate: () => void; refreshVirtualWindow: () => void },\n): void {\n if (!order.length) return;\n\n const columnMap = new Map(grid._columns.map((c) => [c.field as string, c]));\n const reordered: ColumnInternal<T>[] = [];\n\n for (const field of order) {\n const col = columnMap.get(field);\n if (col) {\n reordered.push(col);\n columnMap.delete(field);\n }\n }\n\n // Add remaining columns not in order\n for (const col of columnMap.values()) {\n reordered.push(col);\n }\n\n grid._columns = reordered;\n\n callbacks.renderHeader();\n callbacks.updateTemplate();\n callbacks.refreshVirtualWindow();\n}\n","import type { PluginQuery } from './plugin/base-plugin';\n\n/**\n * The compiled webcomponent interface for DataGrid\n */\nexport interface DataGridElement extends PublicGrid, HTMLElement {}\n\n/**\n * Public API interface for DataGrid component.\n *\n * **Property Getters vs Setters:**\n *\n * Property getters return the EFFECTIVE (resolved) value after merging all config sources.\n * This is the \"current situation\" - what consumers and plugins need to know.\n *\n * Property setters accept input values which are merged into the effective config.\n * Multiple sources can contribute (gridConfig, columns prop, light DOM, individual props).\n *\n * For example:\n * - `grid.fitMode` returns the resolved fitMode (e.g., 'stretch' even if you set undefined)\n * - `grid.columns` returns the effective columns after merging\n * - `grid.gridConfig` returns the full effective config\n */\nexport interface PublicGrid<T = any> {\n /**\n * Full config object. Setter merges with other inputs per precedence rules.\n * Getter returns the effective (resolved) config.\n */\n gridConfig?: GridConfig<T>;\n /**\n * Column definitions.\n * Getter returns effective columns (after merging config, light DOM, inference).\n */\n columns?: ColumnConfig<T>[];\n /** Current row data (after plugin processing like grouping, filtering). */\n rows?: T[];\n /** Resolves once the component has finished initial work (layout, inference). */\n ready?: () => Promise<void>;\n /** Force a layout / measurement pass (e.g. after container resize). */\n forceLayout?: () => Promise<void>;\n /** Return effective resolved config (after inference & precedence). */\n getConfig?: () => Promise<Readonly<GridConfig<T>>>;\n /** Toggle expansion state of a group row by its generated key. */\n toggleGroup?: (key: string) => Promise<void>;\n\n // Custom Styles API\n /**\n * Register custom CSS styles to be injected into the grid's shadow DOM.\n * Use this to style custom cell renderers, editors, or detail panels.\n * @param id - Unique identifier for the style block (for removal/updates)\n * @param css - CSS string to inject\n */\n registerStyles?: (id: string, css: string) => void;\n /**\n * Remove previously registered custom styles.\n * @param id - The ID used when registering the styles\n */\n unregisterStyles?: (id: string) => void;\n /**\n * Get list of registered custom style IDs.\n */\n getRegisteredStyles?: () => string[];\n}\n\n/**\n * Internal-only augmented interface for DataGrid component\n */\nexport interface InternalGrid<T = any> extends PublicGrid<T>, GridConfig<T> {\n shadowRoot: ShadowRoot | null;\n _rows: T[];\n _columns: ColumnInternal<T>[];\n /** Visible columns only (excludes hidden). Use for rendering. */\n _visibleColumns: ColumnInternal<T>[];\n _headerRowEl: HTMLElement;\n _bodyEl: HTMLElement;\n _rowPool: HTMLElement[];\n _resizeController: ResizeController;\n _sortState: { field: string; direction: 1 | -1 } | null;\n __originalOrder: T[];\n __rowRenderEpoch: number;\n __didInitialAutoSize?: boolean;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n _gridTemplate: string;\n _virtualization: VirtualState;\n _focusRow: number;\n _focusCol: number;\n _activeEditRows: number;\n _rowEditSnapshots: Map<number, T>;\n _changedRowIndices: Set<number>;\n changedRows?: T[];\n changedRowIndices?: number[];\n effectiveConfig?: GridConfig<T>;\n findHeaderRow?: () => HTMLElement;\n refreshVirtualWindow: (full: boolean) => void;\n updateTemplate?: () => void;\n findRenderedRowElement?: (rowIndex: number) => HTMLElement | null;\n beginBulkEdit?: (rowIndex: number) => void;\n commitActiveRowEdit?: () => void;\n /** Dispatch cell click to plugin system, returns true if handled */\n _dispatchCellClick?: (event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement) => boolean;\n /** Dispatch row click to plugin system, returns true if handled */\n _dispatchRowClick?: (event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement) => boolean;\n /** Dispatch header click to plugin system, returns true if handled */\n _dispatchHeaderClick?: (event: MouseEvent, colIndex: number, headerEl: HTMLElement) => boolean;\n /** Dispatch keydown to plugin system, returns true if handled */\n _dispatchKeyDown?: (event: KeyboardEvent) => boolean;\n /** Get horizontal scroll boundary offsets from plugins */\n _getHorizontalScrollOffsets?: (\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ) => { left: number; right: number; skipScroll?: boolean };\n /** Query all plugins with a generic query and collect responses */\n queryPlugins?: <T>(query: PluginQuery) => T[];\n /** Request emission of column-state-change event (debounced) */\n requestStateChange?: () => void;\n}\n\nexport type PrimitiveColumnType = 'number' | 'string' | 'date' | 'boolean' | 'select' | 'typeahead';\n\n/**\n * Base contract for a column. Public; kept intentionally lean so host apps can extend via intersection types.\n * Prefer adding optional properties here only when broadly useful to most grids.\n */\nexport interface BaseColumnConfig<TRow = any, TValue = any> {\n /** Unique field key referencing property in row objects */\n field: keyof TRow & string;\n /** Visible header label; defaults to capitalized field */\n header?: string;\n /** Column data type; inferred if omitted */\n type?: PrimitiveColumnType;\n /** Column width in pixels; fixed size (no flexibility) */\n width?: string | number;\n /** Minimum column width in pixels (stretch mode only); when set, column uses minmax(minWidth, 1fr) */\n minWidth?: number;\n /** Whether column can be sorted */\n sortable?: boolean;\n /** Whether column can be resized by user */\n resizable?: boolean;\n /** Optional custom comparator for sorting (a,b) -> number */\n sortComparator?: (a: TValue, b: TValue, rowA: TRow, rowB: TRow) => number;\n /** Whether the field is editable (enables editors) */\n editable?: boolean;\n /** Optional custom editor factory or element tag name */\n editor?: ColumnEditorSpec<TRow, TValue>;\n /** For select/typeahead types - available options */\n options?: Array<{ label: string; value: unknown }> | (() => Array<{ label: string; value: unknown }>);\n /** For select/typeahead - allow multi select */\n multi?: boolean;\n /** Optional formatter */\n format?: (value: TValue, row: TRow) => string;\n /** Arbitrary extra metadata */\n meta?: Record<string, unknown>;\n}\n\n/**\n * Full column configuration including optional custom view/renderer & grouping metadata.\n */\nexport interface ColumnConfig<TRow = any> extends BaseColumnConfig<TRow, any> {\n /** Optional custom view renderer used instead of default text rendering */\n viewRenderer?: ColumnViewRenderer<TRow, any>;\n /** External view spec (lets host app mount any framework component) */\n externalView?: {\n component: unknown;\n props?: Record<string, unknown>;\n mount?: (options: {\n placeholder: HTMLElement;\n context: CellRenderContext<TRow, unknown>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n /** Whether the column is initially hidden */\n hidden?: boolean;\n /** Prevent this column from being hidden programmatically */\n lockVisible?: boolean;\n}\n\nexport type ColumnConfigMap<TRow = any> = ColumnConfig<TRow>[];\n\n/** External editor spec: tag name, factory function, or external mount spec */\nexport type ColumnEditorSpec<TRow = unknown, TValue = unknown> =\n | string // custom element tag name\n | ((context: ColumnEditorContext<TRow, TValue>) => HTMLElement | string)\n | {\n /** Arbitrary component reference (class, function, token) */\n component: unknown;\n /** Optional static props passed to mount */\n props?: Record<string, unknown>;\n /** Optional custom mount function; if provided we call it directly instead of emitting an event */\n mount?: (options: {\n placeholder: HTMLElement;\n context: ColumnEditorContext<TRow, TValue>;\n spec: unknown;\n }) => void | { dispose?: () => void };\n };\n\n/**\n * Context object provided to editor factories allowing mutation (commit/cancel) of a cell value.\n */\nexport interface ColumnEditorContext<TRow = any, TValue = any> {\n /** Underlying full row object for the active edit. */\n row: TRow;\n /** Current cell value (mutable only via commit). */\n value: TValue;\n /** Field name being edited. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n /** Accept the edit; triggers change tracking + rerender. */\n commit: (newValue: TValue) => void;\n /** Abort edit without persisting changes. */\n cancel: () => void;\n}\n\n/**\n * Context passed to custom view renderers (pure display – no commit helpers).\n */\nexport interface CellRenderContext<TRow = any, TValue = any> {\n /** Row object for the cell being rendered. */\n row: TRow;\n /** Value at field. */\n value: TValue;\n /** Field key. */\n field: keyof TRow & string;\n /** Column configuration reference. */\n column: ColumnConfig<TRow>;\n}\n\nexport type ColumnViewRenderer<TRow = unknown, TValue = unknown> = (\n ctx: CellRenderContext<TRow, TValue>,\n) => Node | string | void;\n\n/**\n * Framework adapter interface for handling framework-specific component instantiation.\n * Allows framework libraries (Angular, React, Vue) to register handlers that convert\n * declarative light DOM elements into functional renderers/editors.\n *\n * @example\n * ```typescript\n * // In @toolbox-web/grid-angular\n * class AngularGridAdapter implements FrameworkAdapter {\n * canHandle(element: HTMLElement): boolean {\n * return element.tagName.startsWith('APP-');\n * }\n * createRenderer(element: HTMLElement): ColumnViewRenderer {\n * return (ctx) => {\n * // Angular-specific instantiation logic\n * const componentRef = createComponent(...);\n * componentRef.setInput('value', ctx.value);\n * return componentRef.location.nativeElement;\n * };\n * }\n * createEditor(element: HTMLElement): ColumnEditorSpec {\n * return (ctx) => {\n * // Angular-specific editor with commit/cancel\n * const componentRef = createComponent(...);\n * componentRef.setInput('value', ctx.value);\n * // Subscribe to commit/cancel outputs\n * return componentRef.location.nativeElement;\n * };\n * }\n * }\n *\n * // User registers adapter once in their app\n * GridElement.registerAdapter(new AngularGridAdapter(injector, appRef));\n * ```\n */\nexport interface FrameworkAdapter {\n /**\n * Determines if this adapter can handle the given element.\n * Typically checks tag name, attributes, or other conventions.\n */\n canHandle(element: HTMLElement): boolean;\n\n /**\n * Creates a view renderer function from a light DOM element.\n * The renderer receives cell context and returns DOM or string.\n */\n createRenderer<TRow = unknown, TValue = unknown>(element: HTMLElement): ColumnViewRenderer<TRow, TValue>;\n\n /**\n * Creates an editor spec from a light DOM element.\n * The editor receives context with commit/cancel and returns DOM.\n */\n createEditor<TRow = unknown, TValue = unknown>(element: HTMLElement): ColumnEditorSpec<TRow, TValue>;\n\n /**\n * Creates a tool panel renderer from a light DOM element.\n * The renderer receives a container element and optionally returns a cleanup function.\n */\n createToolPanelRenderer?(element: HTMLElement): ((container: HTMLElement) => void | (() => void)) | undefined;\n}\n\n// #region Internal-only augmented types (not re-exported publicly)\nexport interface ColumnInternal<T = any> extends ColumnConfig<T> {\n __autoSized?: boolean;\n __userResized?: boolean;\n __renderedWidth?: number;\n /** Original configured width (for reset on double-click) */\n __originalWidth?: number;\n __viewTemplate?: HTMLElement;\n __editorTemplate?: HTMLElement;\n __headerTemplate?: HTMLElement;\n __compiledView?: (ctx: CellContext<T>) => string;\n __compiledEditor?: (ctx: EditorExecContext<T>) => string;\n}\n\n/**\n * Runtime cell context used internally for compiled template execution.\n */\nexport interface CellContext<T = any> {\n row: T;\n value: unknown;\n field: string;\n column: ColumnInternal<T>;\n}\n\n/**\n * Internal editor execution context extending the generic cell context with commit helpers.\n */\nexport interface EditorExecContext<T = any> extends CellContext<T> {\n commit: (newValue: unknown) => void;\n cancel: () => void;\n}\n\n/** Controller managing drag-based column resize lifecycle. */\nexport interface ResizeController {\n start: (e: MouseEvent, colIndex: number, cell: HTMLElement) => void;\n /** Reset a column to its configured width (or auto-size if none configured). */\n resetColumn: (colIndex: number) => void;\n dispose: () => void;\n /** True while a resize drag is in progress (used to suppress header click/sort). */\n isResizing: boolean;\n}\n\n/** Virtual window bookkeeping; modified in-place as scroll position changes. */\nexport interface VirtualState {\n enabled: boolean;\n rowHeight: number;\n /** Threshold for bypassing virtualization (renders all rows if totalRows <= bypassThreshold) */\n bypassThreshold: number;\n start: number;\n end: number;\n /** Faux scrollbar element that provides scroll events (AG Grid pattern) */\n container: HTMLElement | null;\n /** Rows viewport element for measuring visible area height */\n viewportEl: HTMLElement | null;\n /** Spacer element inside faux scrollbar for setting virtual height */\n totalHeightEl: HTMLElement | null;\n}\n// #endregion\n\n// #region Grouping & Footer Public Types\n/**\n * Group row rendering customization options.\n * Used within grouping-rows plugin config for presentation of group rows.\n */\nexport interface RowGroupRenderConfig {\n /** If true, group rows span all columns (single full-width cell). Default false. */\n fullWidth?: boolean;\n /** Optional label formatter override. Receives raw group value + depth. */\n formatLabel?: (value: unknown, depth: number, key: string) => string;\n /** Optional aggregate overrides per field for group summary cells (only when not fullWidth). */\n aggregators?: Record<string, AggregatorRef>;\n /** Additional CSS class applied to each group row root element. */\n class?: string;\n}\n\nexport type AggregatorRef = string | ((rows: unknown[], field: string, column?: unknown) => unknown);\n\n/** Result of automatic column inference from sample rows. */\nexport interface InferredColumnResult<TRow = unknown> {\n columns: ColumnConfigMap<TRow>;\n typeMap: Record<string, PrimitiveColumnType>;\n}\n\nexport const FitModeEnum = {\n STRETCH: 'stretch',\n FIXED: 'fixed',\n} as const;\nexport type FitMode = (typeof FitModeEnum)[keyof typeof FitModeEnum]; // evaluates to 'stretch' | 'fixed'\n// #endregion\n\n// #region Plugin Interface\n/**\n * Minimal plugin interface for type-checking.\n * This interface is defined here to avoid circular imports with BaseGridPlugin.\n * All plugins must satisfy this shape (BaseGridPlugin implements it).\n */\nexport interface GridPlugin {\n /** Unique plugin identifier */\n readonly name: string;\n /** Plugin version */\n readonly version: string;\n /** CSS styles to inject into grid's shadow DOM */\n readonly styles?: string;\n}\n// #endregion\n\n// #region Grid Config\n/**\n * Grid configuration object - the **single source of truth** for grid behavior.\n *\n * Users can configure the grid via multiple input methods, all of which converge\n * into an effective `GridConfig` internally:\n *\n * **Configuration Input Methods:**\n * - `gridConfig` property - direct assignment of this object\n * - `columns` property - shorthand for `gridConfig.columns`\n * - `fitMode` property - shorthand for `gridConfig.fitMode`\n * - `editOn` property - shorthand for `gridConfig.editOn`\n * - Light DOM `<tbw-grid-column>` - declarative columns (merged into `columns`)\n * - Light DOM `<tbw-grid-header>` - declarative shell header (merged into `shell.header`)\n *\n * **Precedence (when same property set multiple ways):**\n * Individual props (`fitMode`, `editOn`) > `columns` prop > Light DOM > `gridConfig`\n *\n * @example\n * ```ts\n * // Via gridConfig (recommended for complex setups)\n * grid.gridConfig = {\n * columns: [{ field: 'name' }, { field: 'age' }],\n * fitMode: 'stretch',\n * plugins: [new SelectionPlugin()],\n * shell: { header: { title: 'My Grid' } }\n * };\n *\n * // Via individual props (convenience for simple cases)\n * grid.columns = [{ field: 'name' }, { field: 'age' }];\n * grid.fitMode = 'stretch';\n * ```\n */\nexport interface GridConfig<TRow = any> {\n /** Column definitions. Can also be set via `columns` prop or `<tbw-grid-column>` light DOM. */\n columns?: ColumnConfigMap<TRow>;\n /** Sizing mode for columns. Can also be set via `fitMode` prop. */\n fitMode?: FitMode;\n /** Edit activation mode ('click' | 'dblClick' | false). Set to false to disable editing. Can also be set via `editOn` prop. */\n editOn?: string | boolean;\n /**\n * Row height in pixels for virtualization calculations.\n * The virtualization system assumes uniform row heights for performance.\n *\n * If not specified, the grid measures the first rendered row's height,\n * which respects the CSS variable `--tbw-row-height` set by themes.\n *\n * Set this explicitly when:\n * - Row content may wrap to multiple lines (also set `--tbw-cell-white-space: normal`)\n * - Using custom row templates with variable content\n * - You want to override theme-defined row height\n *\n * @default Auto-measured from first row (respects --tbw-row-height CSS variable)\n *\n * @example\n * ```ts\n * // Fixed height for rows that may wrap to 2 lines\n * gridConfig = { rowHeight: 56 };\n * ```\n */\n rowHeight?: number;\n /**\n * Array of plugin instances.\n * Each plugin is instantiated with its configuration and attached to this grid.\n *\n * @example\n * ```ts\n * plugins: [\n * new SelectionPlugin({ mode: 'range' }),\n * new MultiSortPlugin(),\n * new FilteringPlugin({ debounceMs: 150 }),\n * ]\n * ```\n */\n plugins?: GridPlugin[];\n\n /**\n * Saved column state to restore on initialization.\n * Includes order, width, visibility, sort, and plugin-contributed state.\n */\n columnState?: GridColumnState;\n\n /**\n * Shell configuration for header bar and tool panels.\n * When configured, adds an optional wrapper with title, toolbar, and collapsible side panels.\n */\n shell?: ShellConfig;\n\n /**\n * Grid-wide icon configuration.\n * Provides consistent icons across all plugins (tree, grouping, sorting, etc.).\n * Plugins will use these by default but can override with their own config.\n */\n icons?: GridIcons;\n\n /**\n * Grid-wide animation configuration.\n * Controls animations for expand/collapse, reordering, and other visual transitions.\n * Individual plugins can override these defaults in their own config.\n */\n animation?: AnimationConfig;\n\n /**\n * Custom sort handler for full control over sorting behavior.\n *\n * When provided, this handler is called instead of the built-in sorting logic.\n * Enables custom sorting algorithms, server-side sorting, or plugin-specific sorting.\n *\n * The handler receives:\n * - `rows`: Current row array to sort\n * - `sortState`: Sort field and direction (1 = asc, -1 = desc)\n * - `columns`: Column configurations (for accessing sortComparator)\n *\n * Return the sorted array (sync) or a Promise that resolves to the sorted array (async).\n * For server-side sorting, return a Promise that resolves when data is fetched.\n *\n * @example\n * ```ts\n * // Custom stable sort\n * sortHandler: (rows, state, cols) => {\n * return stableSort(rows, (a, b) => compare(a[state.field], b[state.field]) * state.direction);\n * }\n *\n * // Server-side sorting\n * sortHandler: async (rows, state) => {\n * const response = await fetch(`/api/data?sort=${state.field}&dir=${state.direction}`);\n * return response.json();\n * }\n * ```\n */\n sortHandler?: SortHandler<TRow>;\n}\n// #endregion\n\n// #region Animation\n\n/**\n * Sort state passed to custom sort handlers.\n */\nexport interface SortState {\n /** Field to sort by */\n field: string;\n /** Sort direction: 1 = ascending, -1 = descending */\n direction: 1 | -1;\n}\n\n/**\n * Custom sort handler function signature.\n *\n * @param rows - Current row array to sort\n * @param sortState - Sort field and direction\n * @param columns - Column configurations (for accessing sortComparator)\n * @returns Sorted array (sync) or Promise resolving to sorted array (async)\n */\nexport type SortHandler<TRow = any> = (\n rows: TRow[],\n sortState: SortState,\n columns: ColumnConfig<TRow>[],\n) => TRow[] | Promise<TRow[]>;\n\n/**\n * Animation behavior mode.\n * - `true` or `'on'`: Animations always enabled\n * - `false` or `'off'`: Animations always disabled\n * - `'reduced-motion'`: Respects `prefers-reduced-motion` media query (default)\n */\nexport type AnimationMode = boolean | 'on' | 'off' | 'reduced-motion';\n\n/**\n * Animation style for visual transitions.\n * - `'slide'`: Slide/transform animation (e.g., expand down, slide left/right)\n * - `'fade'`: Opacity fade animation\n * - `'flip'`: FLIP technique for position changes (First, Last, Invert, Play)\n * - `false`: No animation for this specific feature\n */\nexport type AnimationStyle = 'slide' | 'fade' | 'flip' | false;\n\n/**\n * Animation style for expand/collapse operations.\n * Subset of AnimationStyle - excludes 'flip' which is for position changes.\n * - `'slide'`: Slide down/up animation for expanding/collapsing content\n * - `'fade'`: Fade in/out animation\n * - `false`: No animation\n */\nexport type ExpandCollapseAnimation = 'slide' | 'fade' | false;\n\n/**\n * Grid-wide animation configuration.\n * Controls global animation behavior - individual plugins define their own animation styles.\n * Duration and easing values set corresponding CSS variables on the grid element.\n */\nexport interface AnimationConfig {\n /**\n * Global animation mode.\n * @default 'reduced-motion'\n */\n mode?: AnimationMode;\n\n /**\n * Default animation duration in milliseconds.\n * Sets `--tbw-animation-duration` CSS variable.\n * @default 200\n */\n duration?: number;\n\n /**\n * Default easing function.\n * Sets `--tbw-animation-easing` CSS variable.\n * @default 'ease-out'\n */\n easing?: string;\n}\n\n/** Default animation configuration */\nexport const DEFAULT_ANIMATION_CONFIG: Required<Omit<AnimationConfig, 'sort'>> = {\n mode: 'reduced-motion',\n duration: 200,\n easing: 'ease-out',\n};\n\n// #endregion\n\n// #region Grid Icons\n\n/** Icon value - can be a string (text/HTML) or HTMLElement */\nexport type IconValue = string | HTMLElement;\n\n/**\n * Grid-wide icon configuration.\n * All icons are optional - sensible defaults are used when not specified.\n */\nexport interface GridIcons {\n /** Expand icon for collapsed items (trees, groups, details). Default: '▶' */\n expand?: IconValue;\n /** Collapse icon for expanded items (trees, groups, details). Default: '▼' */\n collapse?: IconValue;\n /** Sort ascending indicator. Default: '▲' */\n sortAsc?: IconValue;\n /** Sort descending indicator. Default: '▼' */\n sortDesc?: IconValue;\n /** Sort neutral/unsorted indicator. Default: '⇅' */\n sortNone?: IconValue;\n /** Submenu arrow for context menus. Default: '▶' */\n submenuArrow?: IconValue;\n /** Drag handle icon for reordering. Default: '⋮⋮' */\n dragHandle?: IconValue;\n /** Tool panel toggle icon in toolbar. Default: '☰' */\n toolPanel?: IconValue;\n}\n\n/** Default icons used when not overridden */\nexport const DEFAULT_GRID_ICONS: Required<GridIcons> = {\n expand: '▶',\n collapse: '▼',\n sortAsc: '▲',\n sortDesc: '▼',\n sortNone: '⇅',\n submenuArrow: '▶',\n dragHandle: '⋮⋮',\n toolPanel: '☰',\n};\n// #endregion\n\n// #region Shell Configuration\n\n/**\n * Shell configuration for the grid's optional header bar and tool panels.\n */\nexport interface ShellConfig {\n /** Shell header bar configuration */\n header?: ShellHeaderConfig;\n /** Tool panel configuration */\n toolPanel?: ToolPanelConfig;\n}\n\n/**\n * Shell header bar configuration\n */\nexport interface ShellHeaderConfig {\n /** Grid title displayed on the left (optional) */\n title?: string;\n /** Custom toolbar buttons (rendered before tool panel toggles) */\n toolbarButtons?: ToolbarButtonConfig[];\n}\n\n/**\n * Tool panel configuration\n */\nexport interface ToolPanelConfig {\n /** Panel position: 'left' | 'right' (default: 'right') */\n position?: 'left' | 'right';\n /** Default panel width in pixels (default: 280) */\n width?: number;\n /** Panel ID to open by default on load */\n defaultOpen?: string;\n /** Whether to persist open/closed state (requires Column State Events) */\n persistState?: boolean;\n}\n\n/**\n * Toolbar button defined via config (programmatic approach).\n * 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 accordion header */\n title: string;\n /** Icon for accordion section header (optional, emoji or SVG) */\n icon?: string;\n /** Tooltip for accordion section header */\n tooltip?: string;\n /** Panel content factory - called when panel section opens */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when panel closes (for cleanup) */\n onClose?: () => void;\n /** Panel order priority (lower = first, default: 100) */\n order?: number;\n}\n\n/**\n * Header content definition for plugins contributing to shell header center section.\n */\nexport interface HeaderContentDefinition {\n /** Unique content ID */\n id: string;\n /** Content factory - called once when shell header renders */\n render: (container: HTMLElement) => void | (() => void);\n /** Called when content is removed (for cleanup) */\n onDestroy?: () => void;\n /** Order priority (lower = first, default: 100) */\n order?: number;\n}\n// #endregion\n\n// #region Column State (Persistence)\n\n/**\n * State for a single column. Captures user-driven changes at runtime.\n * Plugins can extend this interface via module augmentation to add their own state.\n *\n * @example\n * ```ts\n * // In filtering plugin\n * declare module '@toolbox-web/grid' {\n * interface ColumnState {\n * filter?: FilterValue;\n * }\n * }\n * ```\n */\nexport interface ColumnState {\n /** Column field identifier */\n field: string;\n /** Position index after reordering (0-based) */\n order: number;\n /** Width in pixels (undefined = use default) */\n width?: number;\n /** Visibility state */\n visible: boolean;\n /** Sort state (undefined = not sorted) */\n sort?: ColumnSortState;\n}\n\n/**\n * Sort state for a column\n */\nexport interface ColumnSortState {\n /** Sort direction */\n direction: 'asc' | 'desc';\n /** Priority for multi-sort (0 = primary, 1 = secondary, etc.) */\n priority: number;\n}\n\n/**\n * Complete grid column state for persistence.\n * Contains state for all columns, including plugin-contributed properties.\n */\nexport interface GridColumnState {\n columns: ColumnState[];\n}\n// #endregion\n\n// #region Public Event Detail Interfaces\nexport interface CellCommitDetail<TRow = unknown> {\n /** The mutated row after commit. */\n row: TRow;\n /** Field name whose value changed. */\n field: string;\n /** New value stored. */\n value: unknown;\n /** Index of the row in current data set. */\n rowIndex: number;\n /** All rows that have at least one committed change (snapshot list). */\n changedRows: TRow[];\n /** Indices parallel to changedRows. */\n changedRowIndices: number[];\n /** True if this row just entered the changed set. */\n firstTimeForRow: boolean;\n}\n\n/** Detail payload for a committed row edit (may or may not include changes). */\nexport interface RowCommitDetail<TRow = unknown> {\n /** Row index that lost edit focus. */\n rowIndex: number;\n /** Row object reference. */\n row: TRow;\n /** Whether any cell changes were actually committed in this row during the session. */\n changed: boolean;\n /** Current changed row collection. */\n changedRows: TRow[];\n /** Indices of changed rows. */\n changedRowIndices: number[];\n}\n\n/** Emitted when the changed rows tracking set is cleared programmatically. */\nexport interface ChangedRowsResetDetail<TRow = unknown> {\n /** New (empty) changed rows array after reset. */\n rows: TRow[];\n /** Parallel indices (likely empty). */\n indices: number[];\n}\n\n/** Detail for a sort change (direction 0 indicates cleared sort). */\nexport interface SortChangeDetail {\n /** Sorted field key. */\n field: string;\n /** Direction: 1 ascending, -1 descending, 0 cleared. */\n direction: 1 | -1 | 0;\n}\n\n/** Column resize event detail containing final pixel width. */\nexport interface ColumnResizeDetail {\n /** Resized column field key. */\n field: string;\n /** New width in pixels. */\n width: number;\n}\n\n/** Fired when keyboard navigation or programmatic focus changes active cell. */\nexport interface ActivateCellDetail {\n /** Zero-based row index now focused. */\n row: number;\n /** Zero-based column index now focused. */\n col: number;\n}\n\nexport interface ExternalMountViewDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: { row: TRow; value: unknown; field: string; column: unknown };\n}\n\nexport interface ExternalMountEditorDetail<TRow = unknown> {\n placeholder: HTMLElement;\n spec: unknown;\n context: {\n row: TRow;\n value: unknown;\n field: string;\n column: unknown;\n commit: (v: unknown) => void;\n cancel: () => void;\n };\n}\n\nexport interface DataGridEventMap<TRow = unknown> {\n 'cell-commit': CellCommitDetail<TRow>;\n 'row-commit': RowCommitDetail<TRow>;\n 'changed-rows-reset': ChangedRowsResetDetail<TRow>;\n 'mount-external-view': ExternalMountViewDetail<TRow>;\n 'mount-external-editor': ExternalMountEditorDetail<TRow>;\n 'sort-change': SortChangeDetail;\n 'column-resize': ColumnResizeDetail;\n 'activate-cell': ActivateCellDetail;\n 'column-state-change': GridColumnState;\n}\n\nexport type DataGridEventDetail<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = DataGridEventMap<TRow>[K];\nexport type DataGridCustomEvent<K extends keyof DataGridEventMap<unknown>, TRow = unknown> = CustomEvent<\n DataGridEventMap<TRow>[K]\n>;\n\n// Internal code now reuses the public ColumnEditorContext; provide alias for backward compatibility\nexport type EditorContext<T = unknown> = ColumnEditorContext<T, unknown>;\n\nexport interface EvalContext {\n value: unknown;\n row: Record<string, unknown> | null;\n}\n// #endregion\n","import type { ColumnConfigMap, InferredColumnResult, PrimitiveColumnType } from '../types';\n/**\n * Best-effort primitive type inference for a cell value used during automatic column generation.\n */\nfunction inferType(value: any): PrimitiveColumnType {\n if (value == null) return 'string';\n if (typeof value === 'number') return 'number';\n if (typeof value === 'boolean') return 'boolean';\n if (value instanceof Date) return 'date';\n if (typeof value === 'string' && /\\d{4}-\\d{2}-\\d{2}/.test(value) && !isNaN(Date.parse(value))) return 'date';\n return 'string';\n}\n/**\n * Derive column definitions from provided configuration or by inspecting the first row of data.\n * Returns both the resolved column array and a field->type map.\n */\nexport function inferColumns<TRow extends Record<string, unknown>>(\n rows: TRow[],\n provided?: ColumnConfigMap<TRow>\n): InferredColumnResult<TRow> {\n if (provided && provided.length) {\n const typeMap: Record<string, PrimitiveColumnType> = {};\n provided.forEach((col) => {\n if (col.type) typeMap[col.field] = col.type;\n });\n return { columns: provided, typeMap };\n }\n const sample = rows[0] || ({} as TRow);\n const columns: ColumnConfigMap<TRow> = Object.keys(sample).map((k) => {\n const v = (sample as any)[k];\n const type = inferType(v);\n return { field: k as keyof TRow & string, header: k.charAt(0).toUpperCase() + k.slice(1), type };\n });\n const typeMap: Record<string, PrimitiveColumnType> = {};\n columns.forEach((c) => {\n typeMap[c.field] = c.type || 'string';\n });\n return { columns, typeMap };\n}\nexport { inferType };\n","// Centralized template expression evaluation & sanitization utilities.\n// Responsible for safely interpolating {{ }} expressions while blocking\n// access to dangerous globals / reflective capabilities.\nimport type { EvalContext } from '../types';\n\nconst EXPR_RE = /{{\\s*([^}]+)\\s*}}/g;\nconst EMPTY_SENTINEL = '__DG_EMPTY__';\nconst SAFE_EXPR = /^[\\w$. '?+\\-*/%:()!<>=,&|]+$/;\nconst FORBIDDEN =\n /__(proto|defineGetter|defineSetter)|constructor|window|globalThis|global|process|Function|import|eval|Reflect|Proxy|Error|arguments|document|location|cookie|localStorage|sessionStorage|indexedDB|fetch|XMLHttpRequest|WebSocket|Worker|SharedWorker|ServiceWorker|opener|parent|top|frames|self|this\\b/;\n\n// #region HTML Sanitization\n\n/**\n * Tags that are considered dangerous and will be completely removed.\n * These can execute scripts, load external resources, or manipulate the page.\n */\nconst DANGEROUS_TAGS = new Set([\n 'script',\n 'iframe',\n 'object',\n 'embed',\n 'form',\n 'input',\n 'button',\n 'textarea',\n 'select',\n 'link',\n 'meta',\n 'base',\n 'style',\n 'template',\n 'slot',\n 'portal',\n 'frame',\n 'frameset',\n 'applet',\n 'noscript',\n 'noembed',\n 'plaintext',\n 'xmp',\n 'listing',\n]);\n\n/**\n * Attributes that are considered dangerous - event handlers and data loading.\n */\nconst DANGEROUS_ATTR_PATTERN = /^on\\w+$/i;\n\n/**\n * Attributes that can contain URLs which might be javascript: or data: URIs.\n */\nconst URL_ATTRS = new Set(['href', 'src', 'action', 'formaction', 'data', 'srcdoc', 'xlink:href', 'poster', 'srcset']);\n\n/**\n * Protocol patterns that are dangerous in URLs.\n */\nconst DANGEROUS_URL_PROTOCOL = /^\\s*(javascript|vbscript|data|blob):/i;\n\n/**\n * Sanitize an HTML string by removing dangerous tags and attributes.\n * This is a defense-in-depth measure for content rendered via innerHTML.\n *\n * @param html - Raw HTML string to sanitize\n * @returns Sanitized HTML string safe for innerHTML\n */\nexport function sanitizeHTML(html: string): string {\n if (!html || typeof html !== 'string') return '';\n\n // Fast path: if no HTML tags at all, return as-is (already safe)\n if (html.indexOf('<') === -1) return html;\n\n const template = document.createElement('template');\n template.innerHTML = html;\n\n sanitizeNode(template.content);\n\n return template.innerHTML;\n}\n\n/**\n * Recursively sanitize a DOM node tree.\n */\nfunction sanitizeNode(root: DocumentFragment | Element): void {\n const toRemove: Element[] = [];\n\n // Use querySelectorAll to find all elements, then filter\n const elements = root.querySelectorAll('*');\n\n for (const el of elements) {\n const tagName = el.tagName.toLowerCase();\n\n // Check if tag is dangerous\n if (DANGEROUS_TAGS.has(tagName)) {\n toRemove.push(el);\n continue;\n }\n\n // SVG elements need special handling - they can contain script-like behavior\n if (tagName === 'svg' || el.namespaceURI === 'http://www.w3.org/2000/svg') {\n // Remove entire SVG if it has any suspicious attributes\n const hasDangerousContent = Array.from(el.attributes).some(\n (attr) => DANGEROUS_ATTR_PATTERN.test(attr.name) || attr.name === 'href' || attr.name === 'xlink:href',\n );\n if (hasDangerousContent) {\n toRemove.push(el);\n continue;\n }\n }\n\n // Check and remove dangerous attributes\n const attrsToRemove: string[] = [];\n for (const attr of el.attributes) {\n const attrName = attr.name.toLowerCase();\n\n // Event handlers (onclick, onerror, onload, etc.)\n if (DANGEROUS_ATTR_PATTERN.test(attrName)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // URL attributes with dangerous protocols\n if (URL_ATTRS.has(attrName) && DANGEROUS_URL_PROTOCOL.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n\n // style attribute can contain expressions (IE) or url() with javascript:\n if (attrName === 'style' && /expression\\s*\\(|javascript:|behavior\\s*:/i.test(attr.value)) {\n attrsToRemove.push(attr.name);\n continue;\n }\n }\n\n attrsToRemove.forEach((name) => el.removeAttribute(name));\n }\n\n // Remove dangerous elements (do this after iteration to avoid modifying during traversal)\n toRemove.forEach((el) => el.remove());\n}\n\n// #endregion\n\nexport function evalTemplateString(raw: string, ctx: EvalContext): string {\n if (!raw || raw.indexOf('{{') === -1) return raw; // fast path (no expressions)\n const parts: { expr: string; result: string }[] = [];\n const evaluated = raw.replace(EXPR_RE, (_m, expr) => {\n const res = evalSingle(expr, ctx);\n parts.push({ expr: expr.trim(), result: res });\n return res;\n });\n const finalStr = postProcess(evaluated);\n // If every part evaluated to EMPTY_SENTINEL we treat this as intentionally blank.\n // If any expression was blocked due to forbidden token (EMPTY_SENTINEL) we *still* only output ''\n // but do not escalate to BLOCKED_SENTINEL unless the original contained explicit forbidden tokens.\n const allEmpty = parts.length && parts.every((p) => p.result === '' || p.result === EMPTY_SENTINEL);\n const hadForbidden = /Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(raw);\n if (hadForbidden || allEmpty) return '';\n return finalStr;\n}\n\nfunction evalSingle(expr: string, ctx: EvalContext): string {\n expr = (expr || '').trim();\n if (!expr) return EMPTY_SENTINEL;\n if (/\\b(Reflect|Proxy|ownKeys)\\b/.test(expr)) return EMPTY_SENTINEL;\n if (expr === 'value') return ctx.value == null ? EMPTY_SENTINEL : String(ctx.value);\n if (expr.startsWith('row.') && !/[()?]/.test(expr) && !expr.includes(':')) {\n const key = expr.slice(4);\n const v = ctx.row ? ctx.row[key] : undefined;\n return v == null ? EMPTY_SENTINEL : String(v);\n }\n if (expr.length > 80) return EMPTY_SENTINEL;\n if (!SAFE_EXPR.test(expr) || FORBIDDEN.test(expr)) return EMPTY_SENTINEL;\n const dotChain = expr.match(/\\./g);\n if (dotChain && dotChain.length > 1) return EMPTY_SENTINEL;\n try {\n \n const fn = new Function('value', 'row', `return (${expr});`);\n const out = fn(ctx.value, ctx.row);\n const str = out == null ? '' : String(out);\n if (/Reflect|Proxy|ownKeys/.test(str)) return EMPTY_SENTINEL;\n return str || EMPTY_SENTINEL;\n } catch {\n return EMPTY_SENTINEL;\n }\n}\n\nfunction postProcess(s: string): string {\n if (!s) return s;\n return s\n .replace(new RegExp(EMPTY_SENTINEL, 'g'), '')\n .replace(/Reflect\\.[^<>{}\\s]+/g, '')\n .replace(/\\bProxy\\b/g, '')\n .replace(/ownKeys\\([^)]*\\)/g, '');\n}\n\nexport function finalCellScrub(cell: HTMLElement): void {\n if (/Reflect|Proxy|ownKeys/.test(cell.textContent || '')) {\n Array.from(cell.childNodes).forEach((n) => {\n if (n.nodeType === Node.TEXT_NODE && /Reflect|Proxy|ownKeys/.test(n.textContent || '')) n.textContent = '';\n });\n if (/Reflect|Proxy|ownKeys/.test(cell.textContent || '')) {\n // If remaining content still includes forbidden tokens inside element nodes, clear children entirely.\n const still = /Reflect|Proxy|ownKeys/.test(cell.textContent || '');\n if (still) {\n while (cell.firstChild) cell.removeChild(cell.firstChild);\n }\n cell.textContent = (cell.textContent || '').replace(/Reflect|Proxy|ownKeys/g, '');\n }\n if ((cell.textContent || '').trim().length === 0) cell.textContent = '';\n }\n}\n\nexport function compileTemplate(raw: string) {\n const forceBlank = /Reflect\\.|\\bProxy\\b|ownKeys\\(/.test(raw);\n const fn = (ctx: EvalContext) => {\n if (forceBlank) return '';\n const out = evalTemplateString(raw, ctx);\n return out;\n };\n (fn as any).__blocked = forceBlank;\n return fn;\n}\n","import type { ColumnConfig, ColumnInternal, InternalGrid } from '../types';\nimport { FitModeEnum } from '../types';\nimport { inferColumns } from './inference';\nimport { compileTemplate } from './sanitize';\n\n/**\n * Parse `<tbw-grid-column>` elements from the host light DOM into column config objects,\n * capturing template elements for later cloning / compilation.\n */\nexport function parseLightDomColumns(host: HTMLElement): ColumnInternal[] {\n const domColumns = Array.from(host.querySelectorAll('tbw-grid-column')) as HTMLElement[];\n return domColumns\n .map((el) => {\n const field = el.getAttribute('field') || '';\n if (!field) return null;\n const rawType = el.getAttribute('type') || undefined;\n const allowedTypes = new Set(['number', 'string', 'date', 'boolean', 'select', 'typeahead']);\n const type = rawType && allowedTypes.has(rawType) ? (rawType as any) : undefined;\n const header = el.getAttribute('header') || undefined;\n const sortable = el.hasAttribute('sortable');\n const editable = el.hasAttribute('editable');\n const config: ColumnInternal = { field, type, header, sortable, editable };\n\n // Parse width attribute (supports px values, percentages, or plain numbers)\n const widthAttr = el.getAttribute('width');\n if (widthAttr) {\n const numericWidth = parseFloat(widthAttr);\n if (!isNaN(numericWidth) && /^\\d+(\\.\\d+)?$/.test(widthAttr.trim())) {\n config.width = numericWidth;\n } else {\n config.width = widthAttr; // e.g. \"100px\", \"20%\", \"1fr\"\n }\n }\n\n // Parse minWidth attribute (numeric only)\n const minWidthAttr = el.getAttribute('minWidth') || el.getAttribute('min-width');\n if (minWidthAttr) {\n const numericMinWidth = parseFloat(minWidthAttr);\n if (!isNaN(numericMinWidth)) {\n config.minWidth = numericMinWidth;\n }\n }\n\n if (el.hasAttribute('resizable')) (config as any).resizable = true;\n if (el.hasAttribute('sizable')) (config as any).resizable = true; // legacy attribute support\n\n // Parse editor and renderer attribute names for programmatic lookup\n const editorName = el.getAttribute('editor');\n const rendererName = el.getAttribute('renderer');\n if (editorName) (config as any).__editorName = editorName;\n if (rendererName) (config as any).__rendererName = rendererName;\n\n // Parse options attribute for select/typeahead: \"value1:Label1,value2:Label2\" or \"value1,value2\"\n const optionsAttr = el.getAttribute('options');\n if (optionsAttr) {\n (config as any).options = optionsAttr.split(',').map((item) => {\n const [value, label] = item.includes(':') ? item.split(':') : [item.trim(), item.trim()];\n return { value: value.trim(), label: label?.trim() || value.trim() };\n });\n }\n const viewTpl = el.querySelector('tbw-grid-column-view');\n const editorTpl = el.querySelector('tbw-grid-column-editor');\n const headerTpl = el.querySelector('tbw-grid-column-header');\n if (viewTpl) config.__viewTemplate = viewTpl as HTMLElement;\n if (editorTpl) config.__editorTemplate = editorTpl as HTMLElement;\n if (headerTpl) config.__headerTemplate = headerTpl as HTMLElement;\n\n // Check if framework adapters can handle the template wrapper elements\n if (viewTpl) {\n const adapters = (globalThis as any).DataGridElement?.getAdapters?.() ?? [];\n const adapter = adapters.find((a: any) => a.canHandle(viewTpl));\n if (adapter) {\n config.viewRenderer = adapter.createRenderer(viewTpl);\n }\n }\n\n if (editorTpl) {\n const adapters = (globalThis as any).DataGridElement?.getAdapters?.() ?? [];\n const adapter = adapters.find((a: any) => a.canHandle(editorTpl));\n if (adapter) {\n config.editor = adapter.createEditor(editorTpl);\n }\n }\n\n return config;\n })\n .filter((c): c is ColumnInternal => !!c);\n}\n\n/**\n * Merge programmatic columns with light DOM columns by field name, allowing DOM-provided\n * attributes / templates to supplement (not overwrite) programmatic definitions.\n * Any DOM columns without a programmatic counterpart are appended.\n */\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 // Merge width/minWidth from DOM if not set programmatically\n if (d.width != null && m.width == null) m.width = d.width;\n if (d.minWidth != null && m.minWidth == null) m.minWidth = d.minWidth;\n if ((d as any).__viewTemplate) (m as any).__viewTemplate = (d as any).__viewTemplate;\n if ((d as any).__editorTemplate) (m as any).__editorTemplate = (d as any).__editorTemplate;\n if ((d as any).__headerTemplate) (m as any).__headerTemplate = (d as any).__headerTemplate;\n // Merge framework adapter renderers/editors from DOM\n if (d.viewRenderer && !m.viewRenderer) m.viewRenderer = d.viewRenderer;\n if (d.editor && !m.editor) m.editor = d.editor;\n delete domMap[c.field];\n return m;\n });\n Object.keys(domMap).forEach((field) => merged.push(domMap[field]));\n return merged;\n}\n\n/**\n * Safely add a token to an element's `part` attribute (supporting the CSS ::part API)\n * without duplicating values. Falls back to string manipulation if `el.part` API isn't present.\n */\nexport function addPart(el: HTMLElement, token: string): void {\n try {\n (el as any).part?.add?.(token);\n } catch {\n /* empty */\n }\n const existing = el.getAttribute('part');\n if (!existing) el.setAttribute('part', token);\n else if (!existing.split(/\\s+/).includes(token)) el.setAttribute('part', existing + ' ' + token);\n}\n\n/**\n * Resolve the effective column list for the grid by combining:\n * 1. Programmatic columns (`grid._columns`)\n * 2. Light DOM `<tbw-grid-column>` definitions (cached)\n * 3. Inferred columns (if none provided)\n * Also compiles inline template expressions into fast functions.\n * Columns with `hidden: true` in config are added to hidden tracking.\n */\nexport function getColumnConfiguration(grid: InternalGrid): void {\n if (!grid.__lightDomColumnsCache) {\n grid.__originalColumnNodes = Array.from(\n (grid as unknown as HTMLElement).querySelectorAll('tbw-grid-column'),\n ) as HTMLElement[];\n grid.__lightDomColumnsCache = grid.__originalColumnNodes.length\n ? parseLightDomColumns(grid as unknown as HTMLElement)\n : [];\n }\n const lightDomColumns = grid.__lightDomColumnsCache;\n const merged = mergeColumns(grid._columns, lightDomColumns);\n merged.forEach((c: ColumnInternal) => {\n if (c.__viewTemplate && !c.__compiledView) {\n c.__compiledView = compileTemplate((c.__viewTemplate as HTMLElement).innerHTML);\n }\n if (c.__editorTemplate && !c.__compiledEditor) {\n c.__compiledEditor = compileTemplate((c.__editorTemplate as HTMLElement).innerHTML);\n }\n });\n const { columns } = inferColumns(grid._rows, merged as any);\n grid._columns = columns as ColumnInternal[];\n}\n\n/**\n * Measure rendered header + visible cell content to assign initial pixel widths\n * to columns when in `content` fit mode. Runs only once unless fit mode changes.\n */\nexport function autoSizeColumns(grid: InternalGrid): void {\n const mode = (grid as any).effectiveConfig?.fitMode || grid.fitMode || FitModeEnum.STRETCH;\n // Run for both stretch (to derive baseline pixel widths before fr distribution) and fixed.\n if (mode !== FitModeEnum.STRETCH && mode !== FitModeEnum.FIXED) return;\n if (grid.__didInitialAutoSize) return;\n if (!(grid as unknown as HTMLElement).isConnected) return;\n const headerCells = (grid._headerRowEl?.children || []) as any;\n if (!headerCells.length) return;\n let changed = false;\n grid._visibleColumns.forEach((col: ColumnInternal, i: number) => {\n if (col.width) return;\n const headerCell = headerCells[i] as HTMLElement | undefined;\n let max = headerCell ? headerCell.scrollWidth : 0;\n for (const rowEl of grid._rowPool) {\n const cell = rowEl.children[i] as HTMLElement | undefined;\n if (cell) {\n const w = cell.scrollWidth;\n if (w > max) max = w;\n }\n }\n if (max > 0) {\n col.width = max + 2;\n (col as ColumnInternal).__autoSized = true;\n changed = true;\n }\n });\n if (changed) updateTemplate(grid);\n grid.__didInitialAutoSize = true;\n}\n\n/**\n * Compute and apply the CSS grid template string that drives column layout.\n * Uses `fr` units for flexible (non user-resized) columns in stretch mode, otherwise\n * explicit pixel widths or auto sizing.\n */\nexport function updateTemplate(grid: InternalGrid): void {\n // Modes:\n // - 'stretch': columns with explicit width use that width; columns without width are flexible\n // Uses minmax(minWidth, maxWidth) when both min/max specified (bounded flex)\n // Uses minmax(minWidth, 1fr) when only min specified (grows unbounded)\n // Uses minmax(defaultMin, maxWidth) when only max specified (capped growth)\n // - 'fixed': columns with explicit width use that width; columns without width use max-content\n const mode = (grid as any).effectiveConfig?.fitMode || grid.fitMode || FitModeEnum.STRETCH;\n\n if (mode === FitModeEnum.STRETCH) {\n grid._gridTemplate = grid._visibleColumns\n .map((c: ColumnInternal) => {\n if (c.width) return `${c.width}px`;\n // Flexible column: pure 1fr unless minWidth specified\n const min = (c as any).minWidth;\n return min != null ? `minmax(${min}px, 1fr)` : '1fr';\n })\n .join(' ')\n .trim();\n } else {\n // fixed mode: explicit pixel widths or max-content for content-based sizing\n grid._gridTemplate = grid._visibleColumns\n .map((c: ColumnInternal) => (c.width ? `${c.width}px` : 'max-content'))\n .join(' ');\n }\n ((grid as unknown as HTMLElement).style as any).setProperty('--tbw-column-template', grid._gridTemplate);\n}\n\n// ============================================================================\n// Column Visibility API\n// ============================================================================\n// These functions are extracted from grid.ts to reduce the god object size.\n// Grid.ts delegates to these functions for all visibility-related operations.\n\n/**\n * Emit a custom event from the grid element.\n */\nfunction emitEvent(grid: InternalGrid, eventName: string, detail: any): void {\n (grid as unknown as HTMLElement).dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true }));\n}\n\n/**\n * Set the visibility of a column.\n * @param grid - The grid instance\n * @param field - The field name of the column\n * @param visible - Whether the column should be visible\n * @param callbacks - Grid callbacks for triggering setup/state changes\n * @returns True if visibility was changed, false if column not found or locked\n */\nexport function setColumnVisible<T>(\n grid: InternalGrid<T>,\n field: string,\n visible: boolean,\n callbacks: { setup: () => void; requestStateChange: () => void },\n): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n\n // If column not found, cannot change visibility\n if (!col) return false;\n\n // Check lockVisible - cannot hide locked columns\n if (!visible && col.lockVisible) return false;\n\n // Check if at least one column would remain visible\n if (!visible) {\n const currentVisible = allCols.filter((c) => !c.hidden && c.field !== field).length;\n if (currentVisible === 0) return false;\n }\n\n const wasHidden = !!col.hidden;\n const willBeHidden = !visible;\n\n // Only refresh if visibility actually changed\n if (wasHidden !== willBeHidden) {\n // Update the hidden property on the column in effectiveConfig\n col.hidden = willBeHidden;\n\n // Emit event for consumer preference saving\n emitEvent(grid, 'column-visibility', {\n field,\n visible,\n visibleColumns: allCols.filter((c) => !c.hidden).map((c) => c.field),\n });\n\n // Clear row pool to force complete rebuild with new column count\n grid._rowPool.length = 0;\n if (grid._bodyEl) grid._bodyEl.innerHTML = '';\n grid.__rowRenderEpoch++;\n\n // Re-setup to rebuild columns with updated visibility\n callbacks.setup();\n\n // Trigger state change after visibility change\n callbacks.requestStateChange();\n return true;\n }\n return false;\n}\n\n/**\n * Toggle the visibility of a column.\n * @param grid - The grid instance\n * @param field - The field name of the column\n * @param callbacks - Grid callbacks for triggering setup/state changes\n * @returns True if visibility was toggled, false if column not found or locked\n */\nexport function toggleColumnVisibility<T>(\n grid: InternalGrid<T>,\n field: string,\n callbacks: { setup: () => void; requestStateChange: () => void },\n): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n const isCurrentlyHidden = !!col?.hidden;\n return setColumnVisible(grid, field, isCurrentlyHidden, callbacks);\n}\n\n/**\n * Check if a column is currently visible.\n * @param grid - The grid instance\n * @param field - The field name of the column\n * @returns True if visible, false if hidden or not found\n */\nexport function isColumnVisible<T>(grid: InternalGrid<T>, field: string): boolean {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const col = allCols.find((c) => c.field === field);\n return col ? !col.hidden : false;\n}\n\n/**\n * Show all columns.\n * @param grid - The grid instance\n * @param callbacks - Grid callbacks for triggering setup/state changes\n */\nexport function showAllColumns<T>(\n grid: InternalGrid<T>,\n callbacks: { setup: () => void; requestStateChange: () => void },\n): void {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n const hasHidden = allCols.some((c) => c.hidden);\n if (!hasHidden) return;\n\n // Clear hidden flag on all columns\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n emitEvent(grid, 'column-visibility', {\n visibleColumns: allCols.map((c) => c.field),\n });\n\n // Clear row pool to force complete rebuild with new column count\n grid._rowPool.length = 0;\n if (grid._bodyEl) grid._bodyEl.innerHTML = '';\n grid.__rowRenderEpoch++;\n\n callbacks.setup();\n\n // Trigger state change after visibility change\n callbacks.requestStateChange();\n}\n\n/**\n * Get list of all column fields (including hidden).\n * Returns columns reflecting current display order (after reordering).\n * Hidden columns are interleaved at their original relative positions.\n * @param grid - The grid instance\n * @returns Array of all field names with their visibility status\n */\nexport function getAllColumns<T>(\n grid: InternalGrid<T>,\n): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n const allCols = (grid.effectiveConfig?.columns ?? []) as ColumnInternal<T>[];\n\n return allCols.map((c) => ({\n field: c.field,\n header: c.header || c.field,\n visible: !c.hidden,\n lockVisible: c.lockVisible,\n }));\n}\n\n/**\n * Reorder columns according to the specified field order.\n * This directly updates _columns in place without going through processColumns.\n * @param grid - The grid instance\n * @param order - Array of field names in the desired order\n * @param callbacks - Grid callbacks for triggering re-render\n */\nexport function setColumnOrder<T>(\n grid: InternalGrid<T>,\n order: string[],\n callbacks: { renderHeader: () => void; refreshVirtualWindow: (full: boolean) => void },\n): void {\n if (!order.length) return;\n\n const columnMap = new Map<string, ColumnInternal<T>>(grid._columns.map((c) => [c.field as string, c]));\n const reordered: ColumnInternal<T>[] = [];\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 grid._columns = reordered;\n\n // Re-render with new order\n callbacks.renderHeader();\n updateTemplate(grid);\n callbacks.refreshVirtualWindow(true);\n}\n\n/**\n * Get the current column order as an array of field names.\n * @param grid - The grid instance\n * @returns Array of field names in display order\n */\nexport function getColumnOrder<T>(grid: InternalGrid<T>): string[] {\n return grid._columns.map((c) => c.field);\n}\n","/**\n * Default Editors Module\n *\n * Provides built-in editor factories for different column types.\n *\n * IMPORTANT: Editor factories should NOT call focus() on elements - they are called\n * before the element is appended to the DOM. The calling code (beginBulkEdit,\n * inlineEnterEdit) is responsible for focusing the correct editor after insertion.\n */\n\nimport type { ColumnConfig, EditorContext } from '../types';\n\n/**\n * Returns a default editor factory function for the given column type.\n * Each editor handles commit on blur/Enter, and cancel on Escape.\n * Note: Focus is NOT called here - the calling code handles focusing after DOM insertion.\n */\nexport function defaultEditorFor(column: ColumnConfig<any>): (ctx: EditorContext) => HTMLElement | string {\n switch (column.type) {\n case 'number':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'number';\n input.value = ctx.value != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value === '' ? null : Number(input.value)));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value === '' ? null : Number(input.value));\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n case 'boolean':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'checkbox';\n input.checked = !!ctx.value;\n input.addEventListener('change', () => ctx.commit(input.checked));\n return input;\n };\n case 'date':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'date';\n if (ctx.value instanceof Date) input.valueAsDate = ctx.value;\n input.addEventListener('change', () => ctx.commit(input.valueAsDate));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n case 'select':\n case 'typeahead':\n return (ctx: EditorContext) => {\n const select = document.createElement('select');\n if ((ctx.column as any).multi) select.multiple = true;\n const options =\n typeof (ctx.column as any).options === 'function'\n ? (ctx.column as any).options()\n : (ctx.column as any).options || [];\n options.forEach((opt: any) => {\n const o = document.createElement('option');\n o.value = String(opt.value);\n o.textContent = opt.label;\n if ((ctx.column as any).multi && Array.isArray(ctx.value) && ctx.value.includes(opt.value)) o.selected = true;\n else if (!(ctx.column as any).multi && ctx.value === opt.value) o.selected = true;\n select.appendChild(o);\n });\n const commitValue = () => {\n if ((ctx.column as any).multi) {\n const values: any[] = [];\n Array.from(select.selectedOptions).forEach((o) => {\n values.push(o.value);\n });\n ctx.commit(values);\n } else {\n ctx.commit(select.value);\n }\n };\n select.addEventListener('change', commitValue);\n select.addEventListener('blur', commitValue);\n select.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') ctx.cancel();\n });\n return select;\n };\n default:\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'text';\n input.value = ctx.value != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value);\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n }\n}\n","/**\n * Create a requestAnimationFrame-based debounce wrapper. Consecutive calls in the same frame\n * cancel the previous scheduled callback ensuring it runs at most once per frame.\n */\nexport function rafDebounce<T extends (...args: any[]) => void>(fn: T) {\n let handle: number | null = null;\n const wrapped = (...args: Parameters<T>) => {\n if (handle != null) cancelAnimationFrame(handle);\n handle = requestAnimationFrame(() => {\n handle = null;\n fn(...args);\n });\n };\n (wrapped as any).cancel = () => {\n if (handle != null) cancelAnimationFrame(handle);\n handle = null;\n };\n return wrapped as T & { cancel: () => void };\n}\n\n// ────────────────────────────────────────────────────────────────────────────\n// Cell Rendering Helpers (reduces duplicate code in rows.ts)\n// ────────────────────────────────────────────────────────────────────────────\n\n/** Unicode checkmark for true boolean values */\nconst BOOL_TRUE = '\\u{1F5F9}';\n/** Unicode empty checkbox for false boolean values */\nconst BOOL_FALSE = '\\u2610';\n\n/**\n * Generate accessible HTML for a boolean cell.\n * Uses role=\"checkbox\" with proper aria attributes.\n */\nexport function booleanCellHTML(value: boolean): string {\n return `<span role=\"checkbox\" aria-checked=\"${value}\" aria-label=\"${value}\">${value ? '&#x1F5F9;' : '&#9744;'}</span>`;\n}\n\n/**\n * Format a date value for display.\n * Handles Date objects, timestamps, and date strings.\n * Returns empty string for invalid dates.\n */\nexport function formatDateValue(value: unknown): string {\n if (value == null || value === '') return '';\n if (value instanceof Date) {\n return isNaN(value.getTime()) ? '' : value.toLocaleDateString();\n }\n if (typeof value === 'number' || typeof value === 'string') {\n const d = new Date(value);\n return isNaN(d.getTime()) ? '' : d.toLocaleDateString();\n }\n return '';\n}\n\n/**\n * Format a boolean value for text display (not HTML).\n */\nexport function formatBooleanValue(value: unknown): string {\n return value ? BOOL_TRUE : BOOL_FALSE;\n}\n\n/**\n * Get the row index from a cell element's data-row attribute.\n * Returns -1 if no valid row index is found.\n */\nexport function getRowIndexFromCell(cell: Element | null): number {\n if (!cell) return -1;\n const attr = cell.getAttribute('data-row');\n return attr ? parseInt(attr, 10) : -1;\n}\n\n/**\n * Get the column index from a cell element's data-col attribute.\n * Returns -1 if no valid column index is found.\n */\nexport function getColIndexFromCell(cell: Element | null): number {\n if (!cell) return -1;\n const attr = cell.getAttribute('data-col');\n return attr ? parseInt(attr, 10) : -1;\n}\n\n/**\n * Clear all cell-focus styling from a root element (shadowRoot or bodyEl).\n * Used when changing focus or when selection plugin takes over focus management.\n */\nexport function clearCellFocus(root: Element | ShadowRoot | null): void {\n if (!root) return;\n root.querySelectorAll('.cell-focus').forEach((el) => el.classList.remove('cell-focus'));\n}\n","/**\n * Central keyboard handler attached to the host element. Manages navigation, paging,\n * and edit lifecycle triggers while respecting active form field interactions.\n */\nimport type { InternalGrid } from '../types';\nimport { FOCUSABLE_EDITOR_SELECTOR } from './editing';\nimport { clearCellFocus } from './utils';\n\nexport function handleGridKeyDown(grid: InternalGrid, e: KeyboardEvent): void {\n // Dispatch to plugin system first - if any plugin handles it, stop here\n if (grid._dispatchKeyDown?.(e)) {\n return;\n }\n\n const maxRow = grid._rows.length - 1;\n const maxCol = grid._visibleColumns.length - 1;\n const editing = grid._activeEditRows !== undefined && grid._activeEditRows !== -1;\n const col = grid._visibleColumns[grid._focusCol];\n const colType = col?.type;\n const path = (e as any).composedPath ? (e as any).composedPath() : [];\n const target = (path && path.length ? path[0] : (e.target as any)) as HTMLElement | null;\n const isFormField = (el: HTMLElement | null) => {\n if (!el) return false;\n const tag = el.tagName;\n if (tag === 'INPUT' || tag === 'SELECT' || tag === 'TEXTAREA') return true;\n if (el.isContentEditable) return true;\n return false;\n };\n if (isFormField(target) && (e.key === 'Home' || e.key === 'End')) return;\n if (isFormField(target) && (e.key === 'ArrowUp' || e.key === 'ArrowDown')) {\n if ((target as HTMLInputElement).tagName === 'INPUT' && (target as HTMLInputElement).type === 'number') return;\n }\n // Let arrow left/right navigate within text inputs instead of moving cells\n if (isFormField(target) && (e.key === 'ArrowLeft' || e.key === 'ArrowRight')) return;\n // Let Enter/Escape be handled by the input's own handlers first\n if (isFormField(target) && (e.key === 'Enter' || e.key === 'Escape')) return;\n if (editing && (colType === 'select' || colType === 'typeahead') && (e.key === 'ArrowDown' || e.key === 'ArrowUp'))\n return;\n switch (e.key) {\n case 'Tab': {\n e.preventDefault();\n const forward = !e.shiftKey;\n if (forward) {\n if (grid._focusCol < maxCol) grid._focusCol += 1;\n else {\n if (typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n if (grid._focusRow < maxRow) {\n grid._focusRow += 1;\n grid._focusCol = 0;\n }\n }\n } else {\n if (grid._focusCol > 0) grid._focusCol -= 1;\n else if (grid._focusRow > 0) {\n if (typeof grid.commitActiveRowEdit === 'function' && grid._activeEditRows === grid._focusRow)\n grid.commitActiveRowEdit();\n grid._focusRow -= 1;\n grid._focusCol = maxCol;\n }\n }\n ensureCellVisible(grid);\n return;\n }\n case 'ArrowDown':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.min(maxRow, grid._focusRow + 1);\n e.preventDefault();\n break;\n case 'ArrowUp':\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = Math.max(0, grid._focusRow - 1);\n e.preventDefault();\n break;\n case 'ArrowRight':\n grid._focusCol = Math.min(maxCol, grid._focusCol + 1);\n e.preventDefault();\n break;\n case 'ArrowLeft':\n grid._focusCol = Math.max(0, grid._focusCol - 1);\n e.preventDefault();\n break;\n case 'Home':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+Home: navigate to first row, first cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = 0;\n grid._focusCol = 0;\n } else {\n // Home: navigate to first cell in current row\n grid._focusCol = 0;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollLeft: true });\n return;\n case 'End':\n if (e.ctrlKey || e.metaKey) {\n // CTRL+End: navigate to last row, last cell\n if (editing && typeof grid.commitActiveRowEdit === 'function') grid.commitActiveRowEdit();\n grid._focusRow = maxRow;\n grid._focusCol = maxCol;\n } else {\n // End: navigate to last cell in current row\n grid._focusCol = maxCol;\n }\n e.preventDefault();\n ensureCellVisible(grid, { forceScrollRight: true });\n return;\n case 'PageDown':\n grid._focusRow = Math.min(maxRow, grid._focusRow + 20);\n e.preventDefault();\n break;\n case 'PageUp':\n grid._focusRow = Math.max(0, grid._focusRow - 20);\n e.preventDefault();\n break;\n case 'Enter':\n if (typeof grid.beginBulkEdit === 'function') {\n grid.beginBulkEdit(grid._focusRow);\n // Don't call ensureCellVisible - beginBulkEdit handles focus\n return;\n } else {\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('activate-cell', { detail: { row: grid._focusRow, col: grid._focusCol } }),\n );\n }\n return ensureCellVisible(grid);\n default:\n return;\n }\n ensureCellVisible(grid);\n}\n\n/**\n * Options for ensureCellVisible to control scroll behavior.\n */\ninterface EnsureCellVisibleOptions {\n /** Force scroll to the leftmost position (for Home key) */\n forceScrollLeft?: boolean;\n /** Force scroll to the rightmost position (for End key) */\n forceScrollRight?: boolean;\n}\n\n/**\n * Scroll the viewport (virtualized or static) so the focused cell's row is visible\n * and apply visual focus styling / tabindex management.\n */\nexport function ensureCellVisible(grid: InternalGrid, options?: EnsureCellVisibleOptions): void {\n if (grid._virtualization?.enabled) {\n const { rowHeight, container, viewportEl } = grid._virtualization;\n // container is the faux scrollbar element that handles actual scrolling\n // viewportEl is the visible area element that has the correct height\n const scrollEl = container as HTMLElement | undefined;\n const visibleHeight = viewportEl?.clientHeight ?? scrollEl?.clientHeight ?? 0;\n if (scrollEl && visibleHeight > 0) {\n const y = grid._focusRow * rowHeight;\n if (y < scrollEl.scrollTop) {\n scrollEl.scrollTop = y;\n } else if (y + rowHeight > scrollEl.scrollTop + visibleHeight) {\n scrollEl.scrollTop = y - visibleHeight + rowHeight;\n }\n }\n }\n // Skip refreshVirtualWindow when in edit mode to avoid wiping editors\n const isEditing = grid._activeEditRows !== undefined && grid._activeEditRows !== -1;\n if (!isEditing) {\n grid.refreshVirtualWindow(false);\n }\n clearCellFocus(grid._bodyEl);\n // Clear previous aria-selected markers\n Array.from(grid._bodyEl.querySelectorAll('[aria-selected=\"true\"]')).forEach((el: any) => {\n el.setAttribute('aria-selected', 'false');\n });\n const rowIndex = grid._focusRow;\n const vStart = (grid._virtualization as any).start ?? 0;\n const vEnd = (grid._virtualization as any).end ?? grid._rows.length;\n if (rowIndex >= vStart && rowIndex < vEnd) {\n const rowEl = grid._bodyEl.querySelectorAll('.data-grid-row')[rowIndex - vStart] as HTMLElement | null;\n const cell = rowEl?.children[grid._focusCol] as HTMLElement | undefined;\n if (cell) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n\n // Horizontal scroll: ensure focused cell is visible in the horizontal scroll area\n // The .tbw-scroll-area element handles horizontal scrolling\n // Skip horizontal scrolling when in edit mode to prevent scroll jumps when editors are created\n const scrollArea = grid.shadowRoot?.querySelector('.tbw-scroll-area') as HTMLElement | null;\n if (scrollArea && cell && !isEditing) {\n // Handle forced scroll for Home/End keys - always scroll to edge\n if (options?.forceScrollLeft) {\n scrollArea.scrollLeft = 0;\n } else if (options?.forceScrollRight) {\n scrollArea.scrollLeft = scrollArea.scrollWidth - scrollArea.clientWidth;\n } else {\n // Get scroll boundary offsets from plugins (e.g., pinned columns)\n // This allows plugins to report how much of the scroll area they obscure\n // and whether the focused cell should skip scrolling (e.g., pinned cells are always visible)\n const offsets = grid._getHorizontalScrollOffsets?.(rowEl ?? undefined, cell) ?? { left: 0, right: 0 };\n\n if (!offsets.skipScroll) {\n // Get cell position relative to the scroll area\n const cellRect = cell.getBoundingClientRect();\n const scrollAreaRect = scrollArea.getBoundingClientRect();\n // Calculate the cell's position relative to scroll area's visible region\n const cellLeft = cellRect.left - scrollAreaRect.left + scrollArea.scrollLeft;\n const cellRight = cellLeft + cellRect.width;\n // Adjust visible boundaries to account for plugin-reported offsets\n const visibleLeft = scrollArea.scrollLeft + offsets.left;\n const visibleRight = scrollArea.scrollLeft + scrollArea.clientWidth - offsets.right;\n // Scroll horizontally if needed\n if (cellLeft < visibleLeft) {\n scrollArea.scrollLeft = cellLeft - offsets.left;\n } else if (cellRight > visibleRight) {\n scrollArea.scrollLeft = cellRight - scrollArea.clientWidth + offsets.right;\n }\n }\n }\n }\n\n if (grid._activeEditRows !== undefined && grid._activeEditRows !== -1 && cell.classList.contains('editing')) {\n const focusTarget = cell.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n if (focusTarget && document.activeElement !== focusTarget) {\n try {\n focusTarget.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n } else if (!cell.contains(document.activeElement)) {\n if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '-1');\n try {\n (cell as HTMLElement).focus({ preventScroll: true } as any);\n } catch {\n /* empty */\n }\n }\n }\n }\n}\n","import type { ColumnConfig, InternalGrid } from '../types';\nimport {\n clearEditingState,\n FOCUSABLE_EDITOR_SELECTOR,\n hasEditingCells,\n inlineEnterEdit,\n startRowEdit,\n} from './editing';\nimport { ensureCellVisible } from './keyboard';\nimport { evalTemplateString, finalCellScrub, sanitizeHTML } from './sanitize';\nimport { booleanCellHTML, clearCellFocus, formatBooleanValue, formatDateValue, getRowIndexFromCell } from './utils';\n\n/** Callback type for plugin row rendering hook */\nexport type RenderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number) => boolean;\n\n// ============== Template Cloning System ==============\n// Using template cloning is 3-4x faster than document.createElement + setAttribute\n// for repetitive element creation because the browser can skip parsing.\n\n/**\n * Cell template for cloning. Pre-configured with static attributes.\n * Dynamic attributes (data-col, data-row, etc.) are set after cloning.\n */\nconst cellTemplate = document.createElement('template');\ncellTemplate.innerHTML = '<div class=\"cell\" role=\"gridcell\" part=\"cell\"></div>';\n\n/**\n * Row template for cloning. Pre-configured with static attributes.\n * Dynamic attributes (data-row) and children (cells) are set after cloning.\n */\nconst rowTemplate = document.createElement('template');\nrowTemplate.innerHTML = '<div class=\"data-grid-row\" role=\"row\" part=\"row\"></div>';\n\n/**\n * Create a cell element from template. Significantly faster than createElement + setAttribute.\n */\nfunction createCellFromTemplate(): HTMLDivElement {\n return cellTemplate.content.firstElementChild!.cloneNode(true) as HTMLDivElement;\n}\n\n/**\n * Create a row element from template. Significantly faster than createElement + setAttribute.\n */\nexport function createRowFromTemplate(): HTMLDivElement {\n return rowTemplate.content.firstElementChild!.cloneNode(true) as HTMLDivElement;\n}\n\n// ============== End Template Cloning System ==============\n\n/**\n * Cell display value cache key on grid instance.\n * Structure: Map<rowIndex, Map<colIndex, displayString>>\n * This cache is invalidated when rows or columns change (epoch bump).\n */\nconst CELL_CACHE_KEY = '__cellDisplayCache';\nconst CELL_CACHE_EPOCH_KEY = '__cellCacheEpoch';\n\n/**\n * Get the cached display value for a cell, computing it if not cached.\n * This is the hot path during scroll - must be as fast as possible.\n */\nfunction getCellDisplayValue(\n grid: InternalGrid,\n rowIndex: number,\n colIndex: number,\n rowData: any,\n col: ColumnConfig<any>,\n epoch: number | undefined,\n): string {\n // Fast path: check cache first\n let cache = (grid as any)[CELL_CACHE_KEY] as Map<number, string[]> | undefined;\n const cacheEpoch = (grid as any)[CELL_CACHE_EPOCH_KEY];\n\n // Invalidate cache if epoch changed\n if (cache && cacheEpoch !== epoch) {\n cache = undefined;\n (grid as any)[CELL_CACHE_KEY] = undefined;\n }\n\n if (!cache) {\n cache = new Map();\n (grid as any)[CELL_CACHE_KEY] = cache;\n (grid as any)[CELL_CACHE_EPOCH_KEY] = epoch;\n }\n\n let rowCache = cache.get(rowIndex);\n if (rowCache && rowCache[colIndex] !== undefined) {\n return rowCache[colIndex];\n }\n\n // Compute the display value\n const displayValue = computeCellDisplayValue(rowData, col);\n\n // Cache it\n if (!rowCache) {\n rowCache = [];\n cache.set(rowIndex, rowCache);\n }\n rowCache[colIndex] = displayValue;\n\n return displayValue;\n}\n\n/**\n * Compute the display string for a cell value.\n * Handles formatting, date conversion, boolean display, etc.\n */\nfunction computeCellDisplayValue(rowData: any, col: ColumnConfig<any>): string {\n let value = rowData[col.field];\n\n // Apply format function if present\n const format = (col as any).format;\n if (format) {\n try {\n value = format(value, rowData);\n } catch {\n // Keep original value on format error\n }\n }\n\n // Type-specific conversion\n if (col.type === 'date') {\n return formatDateValue(value);\n }\n\n if (col.type === 'boolean') {\n return formatBooleanValue(value);\n }\n\n return value == null ? '' : String(value);\n}\n\n/**\n * 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 // Use template cloning - 3-4x faster than createElement + setAttribute\n const rowEl = createRowFromTemplate();\n rowEl.addEventListener('click', (e) => handleRowClick(grid, e, rowEl, false));\n rowEl.addEventListener('dblclick', (e) => handleRowClick(grid, e, rowEl, true));\n grid._rowPool.push(rowEl);\n }\n\n // Remove excess pool elements from DOM and shrink pool\n if (grid._rowPool.length > needed) {\n for (let i = needed; i < grid._rowPool.length; i++) {\n const el = grid._rowPool[i];\n if (el.parentNode === bodyEl) el.remove();\n }\n grid._rowPool.length = needed;\n }\n\n // Check if any plugin has a renderRow hook (cache this)\n const hasRenderRowPlugins = renderRowHook && (grid as any).__hasRenderRowPlugins !== false;\n\n for (let i = 0; i < needed; i++) {\n const rowIndex = start + i;\n const rowData = grid._rows[rowIndex];\n const rowEl = grid._rowPool[i];\n\n // Always set aria-rowindex (1-based, accounting for header rows)\n rowEl.setAttribute('aria-rowindex', String(rowIndex + headerRowCount + 1));\n\n // Let plugins handle custom row rendering (e.g., group rows)\n if (hasRenderRowPlugins && renderRowHook!(rowData, rowEl, rowIndex)) {\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n continue;\n }\n\n const rowEpoch = (rowEl as any).__epoch;\n const prevRef = (rowEl as any).__rowDataRef;\n const cellCount = rowEl.children.length;\n\n // Check if we need a full rebuild vs fast update\n const epochMatch = rowEpoch === epoch;\n const structureValid = epochMatch && cellCount === colLen;\n const dataRefChanged = prevRef !== rowData;\n\n // Need external view rebuild check when structure is valid but data changed\n let needsExternalRebuild = false;\n if (structureValid && dataRefChanged) {\n for (let c = 0; c < colLen; c++) {\n const col = columns[c];\n if ((col as any).externalView) {\n const cellCheck = rowEl.querySelector(`.cell[data-col=\"${c}\"] [data-external-view]`);\n if (!cellCheck) {\n needsExternalRebuild = true;\n break;\n }\n }\n }\n }\n\n if (!structureValid || needsExternalRebuild) {\n // Full rebuild needed - epoch changed, cell count mismatch, or external view missing\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n // (This happens when virtualization recycles the DOM element for a different row)\n if (hasEditing && !isActivelyEditedRow) {\n // Force full rebuild to clear stale editors\n if ((rowEl as any).__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n (rowEl as any).__isCustomRow = false;\n }\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else if (hasEditing && isActivelyEditedRow) {\n // Row is in editing mode AND this is the correct row - preserve editors\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__rowDataRef = rowData;\n } else {\n if ((rowEl as any).__isCustomRow) {\n rowEl.className = 'data-grid-row';\n rowEl.setAttribute('role', 'row');\n (rowEl as any).__isCustomRow = false;\n }\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n // Skip focus - editors are being re-created during virtualization scroll\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement, true);\n }\n }\n }\n }\n } else if (dataRefChanged) {\n // Same structure, different row data - fast update\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditing && !isActivelyEditedRow) {\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__rowDataRef = rowData;\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow && !hasEditing) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n // Skip focus - editors are being re-created during virtualization scroll\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement, true);\n }\n }\n }\n }\n } else {\n // Same row data reference - just patch if any values changed\n // Use cached editing state for O(1) check instead of querySelector\n const hasEditing = hasEditingCells(rowEl);\n const isActivelyEditedRow = grid._activeEditRows === rowIndex;\n\n // If DOM element has editors but this is NOT the actively edited row, clear them\n if (hasEditing && !isActivelyEditedRow) {\n clearEditingState(rowEl); // Clear editing state before rebuild\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n (rowEl as any).__epoch = epoch;\n (rowEl as any).__rowDataRef = rowData;\n } else {\n fastPatchRow(grid, rowEl, rowData, rowIndex);\n\n // If this is the actively edited row but DOM doesn't have editors, create them\n if (isActivelyEditedRow && !hasEditing) {\n const children = rowEl.children;\n for (let c = 0; c < children.length; c++) {\n const col = grid._visibleColumns[c];\n if (col && (col as any).editable) {\n // Skip focus - editors are being re-created during virtualization scroll\n inlineEnterEdit(grid, rowData, rowIndex, col, children[c] as HTMLElement, true);\n }\n }\n }\n }\n }\n\n // Changed class toggle\n const isChanged = grid._changedRowIndices.has(rowIndex);\n const hasChangedClass = rowEl.classList.contains('changed');\n if (isChanged !== hasChangedClass) {\n rowEl.classList.toggle('changed', isChanged);\n }\n\n if (rowEl.parentNode !== bodyEl) bodyEl.appendChild(rowEl);\n }\n}\n\n/**\n * Fast patch path for an already-rendered row: updates plain text cells whose data changed\n * while skipping cells with external views, templates, or active editors.\n *\n * Optimized for scroll performance - avoids querySelectorAll in favor of children access.\n */\nfunction fastPatchRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n const children = rowEl.children;\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const childLen = children.length;\n const minLen = colsLen < childLen ? colsLen : childLen;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n\n // Ultra-fast path: if no special columns (templates, formatters, etc.), use direct assignment\n // Check is cached on grid to avoid repeated iteration\n let hasSpecialCols = (grid as any).__hasSpecialColumns;\n if (hasSpecialCols === undefined) {\n hasSpecialCols = false;\n for (let i = 0; i < colsLen; i++) {\n const col = columns[i] as any;\n if (\n col.__viewTemplate ||\n col.__compiledView ||\n col.viewRenderer ||\n col.externalView ||\n col.format ||\n col.type === 'date' ||\n col.type === 'boolean'\n ) {\n hasSpecialCols = true;\n break;\n }\n }\n (grid as any).__hasSpecialColumns = hasSpecialCols;\n }\n\n const rowIndexStr = String(rowIndex);\n\n // Ultra-fast path for plain text grids - just set textContent directly\n if (!hasSpecialCols) {\n for (let i = 0; i < minLen; i++) {\n const cell = children[i] as HTMLElement;\n const value = rowData[columns[i].field];\n cell.textContent = value == null ? '' : String(value);\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n // aria-selected only valid for gridcell, not checkbox (but ultra-fast path has no special cols)\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n }\n return;\n }\n\n // Check if any external view placeholder is missing - if so, do full rebuild\n for (let i = 0; i < minLen; i++) {\n const col = columns[i] as any;\n if (col.externalView) {\n const cell = children[i] as HTMLElement;\n if (!cell.querySelector('[data-external-view]')) {\n renderInlineRow(grid, rowEl, rowData, rowIndex);\n return;\n }\n }\n }\n\n // Standard path for grids with special columns\n for (let i = 0; i < minLen; i++) {\n const col = columns[i] as any;\n const cell = children[i] as HTMLElement;\n\n // Update data-row for click handling\n if (cell.getAttribute('data-row') !== rowIndexStr) {\n cell.setAttribute('data-row', rowIndexStr);\n }\n\n // Update focus state - must be data-driven, not DOM-element-driven\n const shouldHaveFocus = focusRow === rowIndex && focusCol === i;\n const hasFocus = cell.classList.contains('cell-focus');\n if (shouldHaveFocus !== hasFocus) {\n cell.classList.toggle('cell-focus', shouldHaveFocus);\n cell.setAttribute('aria-selected', String(shouldHaveFocus));\n }\n\n // Skip cells in edit mode\n if (cell.classList.contains('editing')) continue;\n\n // Handle viewRenderer - must re-invoke to get updated content\n if (col.viewRenderer) {\n const value = rowData[col.field];\n const produced = col.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 // Skip templated / external cells (these need full rebuild to remount)\n if (col.__viewTemplate || col.__compiledView || col.externalView) {\n continue;\n }\n\n // Compute and set display value\n const value = rowData[col.field];\n let displayStr: string;\n\n if (col.format) {\n try {\n const formatted = col.format(value, rowData);\n displayStr = formatted == null ? '' : String(formatted);\n } catch {\n displayStr = value == null ? '' : String(value);\n }\n } else if (col.type === 'date') {\n displayStr = formatDateValue(value);\n cell.textContent = displayStr;\n } else if (col.type === 'boolean') {\n // Boolean cells have inner span with checkbox role for ARIA compliance\n cell.innerHTML = booleanCellHTML(!!value);\n } else {\n displayStr = value == null ? '' : String(value);\n cell.textContent = displayStr;\n }\n }\n}\n\n/**\n * Full reconstruction of a row's set of cells including templated, external view, and formatted content.\n * Attaches event handlers for editing and accessibility per cell.\n */\nexport function renderInlineRow(grid: InternalGrid, rowEl: HTMLElement, rowData: any, rowIndex: number): void {\n rowEl.innerHTML = '';\n\n // Pre-cache values used in the loop\n const columns = grid._visibleColumns;\n const colsLen = columns.length;\n const focusRow = grid._focusRow;\n const focusCol = grid._focusCol;\n const editMode = (grid as any).effectiveConfig?.editOn || grid.editOn;\n const gridEl = grid as unknown as HTMLElement;\n\n // Use DocumentFragment for batch DOM insertion\n const fragment = document.createDocumentFragment();\n\n for (let colIndex = 0; colIndex < colsLen; colIndex++) {\n const col: ColumnConfig<any> = columns[colIndex];\n // Use template cloning - 3-4x faster than createElement + setAttribute\n const cell = createCellFromTemplate();\n\n // Only set dynamic attributes (role, class, part are already set in template)\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(colIndex + 1));\n cell.setAttribute('data-col', String(colIndex));\n cell.setAttribute('data-row', String(rowIndex));\n cell.setAttribute('data-field', col.field); // Field name for column identification\n const isCheckbox = col.type === 'boolean';\n if (col.type) cell.setAttribute('data-type', col.type as any);\n\n let value = (rowData as any)[col.field];\n const format = (col as any).format;\n if (format) {\n try {\n value = format(value, rowData);\n } catch {\n /* empty */\n }\n }\n\n const compiled = (col as any).__compiledView as ((ctx: any) => string) | undefined;\n const tplHolder = (col as any).__viewTemplate as HTMLElement | undefined;\n 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 cell.textContent = formatDateValue(value);\n } else if (col.type === 'boolean') {\n // Wrap checkbox in span to satisfy ARIA: gridcell can contain checkbox\n cell.innerHTML = booleanCellHTML(!!value);\n } else {\n cell.textContent = value == null ? '' : String(value);\n }\n }\n\n // Only run expensive sanitization when we used innerHTML with user content\n if (needsSanitization) {\n finalCellScrub(cell);\n // Defensive: if forbidden tokens leaked via async or framework hydration, scrub again.\n const textContent = cell.textContent || '';\n if (/Proxy|Reflect\\.ownKeys/.test(textContent)) {\n cell.textContent = textContent.replace(/Proxy|Reflect\\.ownKeys/g, '').trim();\n if (/Proxy|Reflect\\.ownKeys/.test(cell.textContent || '')) cell.textContent = '';\n }\n }\n\n if (cell.hasAttribute('data-blocked-template')) {\n // If anything at all remains (e.g., 'function () { [native code] }'), blank it completely.\n if ((cell.textContent || '').trim().length) cell.textContent = '';\n }\n // Mark editable cells with tabindex for keyboard navigation\n // Event handlers are set up via delegation in setupCellEventDelegation()\n if ((col as any).editable) {\n cell.tabIndex = 0;\n } else if (col.type === 'boolean') {\n // Non-editable boolean cells should NOT toggle on space key\n // They are read-only, only set tabindex for focus navigation\n if (!cell.hasAttribute('tabindex')) cell.tabIndex = 0;\n }\n\n // Initialize focus state (must match fastPatchRow for consistent behavior)\n if (focusRow === rowIndex && focusCol === colIndex) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n } else {\n cell.setAttribute('aria-selected', 'false');\n }\n\n fragment.appendChild(cell);\n }\n\n // Single DOM operation to append all cells\n rowEl.appendChild(fragment);\n}\n\n/**\n * Handle click / double click interaction to focus cells and optionally start row editing\n * according to the grid's configured edit activation mode.\n */\nexport function handleRowClick(grid: InternalGrid, e: MouseEvent, rowEl: HTMLElement, isDbl: boolean): void {\n if ((e.target as HTMLElement)?.closest('.resize-handle')) return;\n const firstCell = rowEl.querySelector('.cell[data-row]') as HTMLElement | null;\n const rowIndex = getRowIndexFromCell(firstCell);\n if (rowIndex < 0) return;\n const rowData = grid._rows[rowIndex];\n if (!rowData) return;\n\n // Dispatch row click to plugin system first (e.g., for master-detail expansion)\n if (grid._dispatchRowClick?.(e, rowIndex, rowData, rowEl)) {\n return;\n }\n\n const cellEl = (e.target as HTMLElement)?.closest('.cell[data-col]') as HTMLElement | null;\n if (cellEl) {\n const colIndex = Number(cellEl.getAttribute('data-col'));\n if (!isNaN(colIndex)) {\n // Dispatch to plugin system first - if handled, stop propagation\n if (grid._dispatchCellClick?.(e, rowIndex, colIndex, cellEl)) {\n return;\n }\n\n // Always update focus to the clicked cell\n const focusChanged = grid._focusRow !== rowIndex || grid._focusCol !== colIndex;\n grid._focusRow = rowIndex;\n grid._focusCol = colIndex;\n\n // If clicking an already-editing cell, just update focus styling and return\n if (cellEl.classList.contains('editing')) {\n if (focusChanged) {\n // Update .cell-focus class to reflect new focus (clear from entire shadow root)\n clearCellFocus(grid.shadowRoot ?? grid._bodyEl);\n cellEl.classList.add('cell-focus');\n }\n return;\n }\n\n ensureCellVisible(grid);\n }\n }\n\n // If this row is already in edit mode, don't re-render editors\n // Just update focus if clicking an editable cell\n const isRowAlreadyEditing = grid._activeEditRows === rowIndex;\n if (isRowAlreadyEditing) {\n // For single-click on already-editing row, just update focus to the clicked cell\n if (cellEl) {\n // Clear all cell-focus markers and set focus on the clicked cell\n clearCellFocus(grid.shadowRoot ?? grid._bodyEl);\n cellEl.classList.add('cell-focus');\n\n queueMicrotask(() => {\n const colIndex = Number(cellEl.getAttribute('data-col'));\n const col = grid._visibleColumns[colIndex];\n // If clicking an editable cell, focus its editor\n if (col && (col as any).editable && cellEl.classList.contains('editing')) {\n const editor = cellEl.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n });\n }\n return;\n }\n\n // Use cached editing state check (O(1) vs querySelector)\n if (hasEditingCells(rowEl)) {\n // If clicking (not double-clicking) on an already-editing row, do nothing\n if (!isDbl) return;\n // Double-click on editing row - clear editing classes\n const children = rowEl.children;\n for (let i = 0; i < children.length; i++) {\n (children[i] as HTMLElement).classList.remove('editing');\n }\n clearEditingState(rowEl);\n }\n const rawMode = (grid as any).effectiveConfig?.editOn ?? grid.editOn ?? 'dblClick';\n // editOn: false disables all editing\n if (rawMode === false) return;\n // Normalize: accept both 'dblClick' and 'dblclick' (DOM event name)\n const mode = rawMode === 'dblclick' ? 'dblClick' : rawMode;\n if (mode === 'click' || (mode === 'dblClick' && isDbl)) {\n // Use beginBulkEdit if available for consistent behavior with Enter key\n if (typeof grid.beginBulkEdit === 'function') {\n grid.beginBulkEdit(rowIndex);\n return;\n }\n startRowEdit(grid, rowIndex, rowData);\n } else return;\n Array.from(rowEl.children).forEach((c: any, i: number) => {\n const col = grid._visibleColumns[i];\n // Skip focus - we'll focus the correct editor in the queueMicrotask below\n if (col && (col as any).editable) inlineEnterEdit(grid, rowData, rowIndex, col, c as HTMLElement, true);\n });\n if (cellEl) {\n queueMicrotask(() => {\n const targetCell = rowEl.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`);\n if (targetCell?.classList.contains('editing')) {\n const editor = (targetCell as HTMLElement).querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n });\n }\n}\n","/**\n * Editing Lifecycle Module\n *\n * Handles row/cell editing state, commit/cancel operations, and value persistence.\n */\n\nimport type { ColumnConfig, InternalGrid } from '../types';\nimport { defaultEditorFor } from './editors';\nimport { invalidateCellCache, renderInlineRow } from './rows';\n\n/**\n * CSS selector for focusable editor elements within a cell.\n * Used by multiple modules to find and focus the active editor.\n */\nexport const FOCUSABLE_EDITOR_SELECTOR =\n 'input,select,textarea,[contenteditable=\"true\"],[contenteditable=\"\"],[tabindex]:not([tabindex=\"-1\"])';\n\n/**\n * Returns true if the given property key is safe to use on a plain object without risking\n * prototype pollution via special names like \"__proto__\", \"constructor\", or \"prototype\".\n */\nfunction isSafePropertyKey(key: any): boolean {\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') return false;\n return true;\n}\n\n/**\n * Check if a row element has any cells in editing mode.\n * Uses a cached count on the row element for O(1) lookup instead of querySelector.\n */\nexport function hasEditingCells(rowEl: HTMLElement): boolean {\n return ((rowEl as any).__editingCellCount ?? 0) > 0;\n}\n\n/**\n * Increment the editing cell count on a row element.\n * Called when a cell enters edit mode.\n */\nfunction incrementEditingCount(rowEl: HTMLElement): void {\n const count = ((rowEl as any).__editingCellCount ?? 0) + 1;\n (rowEl as any).__editingCellCount = count;\n rowEl.setAttribute('data-has-editing', '');\n}\n\n/**\n * Decrement the editing cell count on a row element.\n * Called when a cell exits edit mode.\n */\nfunction decrementEditingCount(rowEl: HTMLElement): void {\n const count = Math.max(0, ((rowEl as any).__editingCellCount ?? 0) - 1);\n (rowEl as any).__editingCellCount = count;\n if (count === 0) {\n rowEl.removeAttribute('data-has-editing');\n }\n}\n\n/**\n * Clear all editing state from a row element.\n * Called when the row is recycled or fully re-rendered.\n */\nexport function clearEditingState(rowEl: HTMLElement): void {\n (rowEl as any).__editingCellCount = 0;\n rowEl.removeAttribute('data-has-editing');\n}\n\n/**\n * Snapshot original row data and mark the row as actively being edited.\n */\nexport function startRowEdit(grid: InternalGrid, rowIndex: number, rowData: any): void {\n if (grid._activeEditRows !== rowIndex) {\n grid._rowEditSnapshots.set(rowIndex, { ...rowData });\n grid._activeEditRows = rowIndex;\n }\n}\n\n/**\n * Finish editing for a row. If `revert` is true restore original snapshot and clear change marks.\n * Otherwise emit a row-commit event describing change status.\n */\nexport function exitRowEdit(grid: InternalGrid, rowIndex: number, revert: boolean): void {\n if (grid._activeEditRows !== rowIndex) return;\n const snapshot = grid._rowEditSnapshots.get(rowIndex);\n const current = grid._rows[rowIndex];\n\n // Before re-rendering, collect and commit values from any active editors\n // This ensures values are persisted even if blur hasn't fired yet\n const rowEl = grid.findRenderedRowElement?.(rowIndex);\n if (!revert && rowEl && current) {\n const editingCells = rowEl.querySelectorAll('.cell.editing');\n editingCells.forEach((cell) => {\n const colIndex = Number((cell as HTMLElement).getAttribute('data-col'));\n if (isNaN(colIndex)) return;\n const col = grid._visibleColumns[colIndex];\n if (!col) return;\n const input = cell.querySelector('input,textarea,select') as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement\n | null;\n if (input) {\n let val: unknown;\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n val = input.checked;\n } else {\n val = input.value;\n // Convert to number for number columns\n if (col.type === 'number' && val !== '') {\n val = Number(val);\n }\n }\n // Only commit if value actually changed\n if (current[col.field] !== val) {\n commitCellValue(grid, rowIndex, col, val, current);\n }\n }\n });\n }\n\n if (revert && snapshot && current) {\n Object.keys(snapshot).forEach((k) => (current[k] = snapshot[k]));\n grid._changedRowIndices.delete(rowIndex);\n // Invalidate cell cache so reverted values display correctly\n invalidateCellCache(grid);\n } else if (!revert) {\n const changed = grid._changedRowIndices.has(rowIndex);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('row-commit', {\n detail: {\n rowIndex,\n row: current,\n changed,\n changedRows: grid.changedRows,\n changedRowIndices: grid.changedRowIndices,\n },\n }),\n );\n }\n grid._rowEditSnapshots.delete(rowIndex);\n grid._activeEditRows = -1;\n if (rowEl) {\n renderInlineRow(grid, rowEl, grid._rows[rowIndex], rowIndex);\n if (grid._changedRowIndices.has(rowIndex)) rowEl.classList.add('changed');\n else rowEl.classList.remove('changed');\n }\n // Restore focus to the cell after exiting edit mode (for both commit and revert)\n queueMicrotask(() => {\n try {\n const rowIdx = grid._focusRow;\n const colIdx = grid._focusCol;\n const rowEl2 = grid.findRenderedRowElement?.(rowIdx);\n if (rowEl2) {\n // Clear all cell-focus markers\n Array.from(grid._bodyEl.querySelectorAll('.cell-focus')).forEach((el: any) =>\n el.classList.remove('cell-focus'),\n );\n // Find and focus the cell\n const cell = rowEl2.querySelector(`.cell[data-row=\"${rowIdx}\"][data-col=\"${colIdx}\"]`) as HTMLElement | null;\n if (cell) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '-1');\n cell.focus({ preventScroll: true });\n }\n }\n } catch {\n /* empty */\n }\n });\n}\n\n/**\n * Commit a single cell value change, updating the row object, marking the row as changed (first-time flag),\n * and emitting a `cell-commit` event with row + field metadata.\n */\nexport function commitCellValue(\n grid: InternalGrid,\n rowIndex: number,\n column: ColumnConfig<any>,\n newValue: any,\n rowData: any,\n): void {\n const field = column.field;\n if (!isSafePropertyKey(field)) return;\n const oldValue = rowData[field];\n if (oldValue === newValue) return;\n rowData[field] = newValue;\n const firstTime = !grid._changedRowIndices.has(rowIndex);\n grid._changedRowIndices.add(rowIndex);\n const rowEl = grid.findRenderedRowElement?.(rowIndex);\n if (rowEl) rowEl.classList.add('changed');\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('cell-commit', {\n detail: {\n row: rowData,\n field,\n value: newValue,\n rowIndex,\n changedRows: grid.changedRows,\n changedRowIndices: grid.changedRowIndices,\n firstTimeForRow: firstTime,\n },\n }),\n );\n}\n\n/**\n * Replace a cell's content with an editor resolved from column configuration (custom editor, template, external\n * mount spec or default editor by type). Manages commit / cancel lifecycle and value restoration.\n *\n * @param skipFocus - When true, don't auto-focus the editor. Used when creating multiple editors\n * at once (e.g., beginBulkEdit) so the caller can control focus.\n */\nexport function inlineEnterEdit(\n grid: InternalGrid,\n rowData: any,\n rowIndex: number,\n column: ColumnConfig<any>,\n cell: HTMLElement,\n skipFocus = false,\n): void {\n if (!column.editable) return;\n if (grid._activeEditRows !== rowIndex) startRowEdit(grid, rowIndex, rowData);\n if (cell.classList.contains('editing')) return;\n const originalValue = isSafePropertyKey(column.field) ? rowData[column.field] : undefined;\n cell.classList.add('editing');\n\n // Track editing state on the row element for fast O(1) lookup\n const rowEl = cell.parentElement;\n if (rowEl) incrementEditingCount(rowEl);\n\n let editFinalized = false; // Flag to prevent blur from committing after explicit Enter/Escape\n const commit = (newValue: any) => {\n // Skip if edit was already finalized by Enter/Escape, or if we've exited edit mode\n // (handles bulk edit case where one cell's exit removes all editors)\n if (editFinalized || grid._activeEditRows === -1) return;\n commitCellValue(grid, rowIndex, column, newValue, rowData);\n };\n const cancel = () => {\n editFinalized = true; // Mark as finalized to prevent blur from re-committing\n rowData[column.field] = isSafePropertyKey(column.field) ? originalValue : undefined;\n const inputLike = cell.querySelector('input,textarea,select') as any;\n if (inputLike) {\n const hasHTMLInput = typeof HTMLInputElement !== 'undefined';\n if (hasHTMLInput && inputLike instanceof HTMLInputElement && inputLike.type === 'checkbox')\n inputLike.checked = !!originalValue;\n else if ('value' in inputLike) inputLike.value = originalValue ?? '';\n }\n };\n const editorHost = document.createElement('div');\n editorHost.style.display = 'contents';\n cell.innerHTML = '';\n cell.appendChild(editorHost);\n\n // Common keydown handler for all editor types to handle Enter/Escape with proper exit\n // This catches events that bubble up from child elements (default editors, custom editors)\n editorHost.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true; // Prevent blur from committing again\n // Value should already be committed by the editor's own handler\n // Just need to exit edit mode\n exitRowEdit(grid, rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel(); // cancel() sets editFinalized = true\n exitRowEdit(grid, rowIndex, true);\n }\n });\n\n const tplHolder = (column as any).__editorTemplate as HTMLElement | undefined;\n const editorSpec = (column as any).editor || (tplHolder ? 'template' : defaultEditorFor(column));\n const value = originalValue;\n if (editorSpec === 'template' && tplHolder) {\n const clone = tplHolder.cloneNode(true) as HTMLElement;\n const compiledEditor = (column as any).__compiledEditor as ((ctx: any) => string) | undefined;\n if (compiledEditor)\n clone.innerHTML = compiledEditor({ row: rowData, value: originalValue, field: column.field, column });\n else\n clone.querySelectorAll<HTMLElement>('*').forEach((node) => {\n if (node.childNodes.length === 1 && node.firstChild?.nodeType === Node.TEXT_NODE) {\n node.textContent =\n node.textContent\n ?.replace(/{{\\s*value\\s*}}/g, originalValue == null ? '' : String(originalValue))\n .replace(/{{\\s*row\\.([a-zA-Z0-9_]+)\\s*}}/g, (_m, g) => {\n const v = (rowData as any)[g];\n return v == null ? '' : String(v);\n }) || '';\n }\n });\n const input = clone.querySelector('input,textarea,select') as HTMLInputElement | HTMLSelectElement | null;\n if (input) {\n const hasHTMLInput = typeof HTMLInputElement !== 'undefined';\n if (hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox')\n input.checked = !!originalValue;\n else if ('value' in input) (input as any).value = originalValue ?? '';\n input.addEventListener('blur', () => {\n // commit() will check editFinalized flag and skip if already handled\n const val =\n hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox'\n ? input.checked\n : (input as any).value;\n commit(val);\n });\n input.addEventListener('keydown', (e: any) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true; // Prevent blur from committing again\n const val =\n hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox'\n ? input.checked\n : (input as any).value;\n commit(val);\n exitRowEdit(grid, rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel(); // cancel() sets editFinalized = true\n exitRowEdit(grid, rowIndex, true);\n }\n });\n if (hasHTMLInput && input instanceof HTMLInputElement && input.type === 'checkbox') {\n input.addEventListener('change', () => {\n const val = input.checked;\n commit(val);\n });\n }\n if (!skipFocus) {\n setTimeout(() => input.focus({ preventScroll: true }), 0);\n }\n }\n editorHost.appendChild(clone);\n } else if (typeof editorSpec === 'string') {\n const el = document.createElement(editorSpec);\n (el as any).value = value;\n el.addEventListener('change', () => commit((el as any).value));\n editorHost.appendChild(el);\n // Focus the custom element editor after DOM insertion\n if (!skipFocus) {\n queueMicrotask(() => {\n const focusable = editorHost.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n focusable?.focus({ preventScroll: true });\n });\n }\n } else if (typeof editorSpec === 'function') {\n const produced = editorSpec({ row: rowData, value, field: column.field, column, commit, cancel });\n if (typeof produced === 'string') editorHost.innerHTML = produced;\n else editorHost.appendChild(produced);\n // Focus the editor after DOM insertion (editors no longer auto-focus)\n if (!skipFocus) {\n queueMicrotask(() => {\n const focusable = editorHost.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n focusable?.focus({ preventScroll: true });\n });\n }\n } else if (editorSpec && typeof editorSpec === 'object') {\n const placeholder = document.createElement('div');\n placeholder.setAttribute('data-external-editor', '');\n placeholder.setAttribute('data-field', column.field);\n editorHost.appendChild(placeholder);\n const context = { row: rowData, value, field: column.field, column, commit, cancel };\n if (editorSpec.mount) {\n try {\n editorSpec.mount({ placeholder, context, spec: editorSpec });\n } catch {\n /* empty */\n }\n } else {\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('mount-external-editor', { detail: { placeholder, spec: editorSpec, context } }),\n );\n }\n }\n}\n\n// ============================================================================\n// Bulk Editing API\n// ============================================================================\n// These functions are extracted from grid.ts to reduce the god object size.\n// Grid.ts delegates to these functions for all bulk editing operations.\n\n/**\n * Emit a custom event from the grid element.\n */\nfunction emitEvent(grid: InternalGrid, eventName: string, detail: any): void {\n (grid as unknown as HTMLElement).dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true }));\n}\n\n/**\n * Get all changed rows from the grid.\n * @param grid - The grid instance\n * @returns Array of changed row data objects\n */\nexport function getChangedRows<T>(grid: InternalGrid<T>): T[] {\n return Array.from(grid._changedRowIndices).map((i) => grid._rows[i]);\n}\n\n/**\n * Get indices of all changed rows.\n * @param grid - The grid instance\n * @returns Array of row indices that have been modified\n */\nexport function getChangedRowIndices(grid: InternalGrid): number[] {\n return Array.from(grid._changedRowIndices);\n}\n\n/**\n * Reset all changed row markers.\n * @param grid - The grid instance\n * @param silent - If true, don't emit the reset event\n */\nexport function resetChangedRows<T>(grid: InternalGrid<T>, silent?: boolean): void {\n grid._changedRowIndices.clear();\n if (!silent) {\n emitEvent(grid, 'changed-rows-reset', {\n rows: getChangedRows(grid),\n indices: getChangedRowIndices(grid),\n });\n }\n grid._rowPool.forEach((r) => r.classList.remove('changed'));\n}\n\n/**\n * Begin bulk editing for a row. Enters edit mode on all editable cells.\n * @param grid - The grid instance\n * @param rowIndex - The row index to start editing\n * @param callbacks - Grid callbacks for finding row elements\n */\nexport function beginBulkEdit<T>(\n grid: InternalGrid<T>,\n rowIndex: number,\n callbacks: { findRenderedRowElement: (rowIndex: number) => HTMLElement | null },\n): void {\n // editOn: false disables all editing\n if ((grid as any).effectiveConfig?.editOn === false) return;\n\n // Check if any columns are editable - if not, skip edit mode entirely\n const hasEditableColumn = grid._columns.some((col) => col.editable);\n if (!hasEditableColumn) return;\n\n const rowData = grid._rows[rowIndex];\n startRowEdit(grid, rowIndex, rowData);\n\n // Enter edit mode on all editable cells in the row\n const rowEl = callbacks.findRenderedRowElement(rowIndex);\n if (rowEl) {\n Array.from(rowEl.children).forEach((cell, i) => {\n // Use visibleColumns to match the cell index - _columns may include hidden columns\n const col = grid._visibleColumns[i];\n if (col?.editable) {\n const cellEl = cell as HTMLElement;\n if (!cellEl.classList.contains('editing')) {\n // Skip auto-focus - we'll focus the correct editor below\n inlineEnterEdit(grid, rowData, rowIndex, col, cellEl, true);\n }\n }\n });\n\n // Focus the editor in the focused cell, or the first editable cell if focused cell is not editable\n // Use setTimeout to ensure custom editors have time to render their focusable elements\n setTimeout(() => {\n // First try the focused cell\n let targetCell = rowEl.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`);\n if (!targetCell?.classList.contains('editing')) {\n // Focused cell is not editable, find the first editable cell\n targetCell = rowEl.querySelector('.cell.editing');\n }\n if (targetCell?.classList.contains('editing')) {\n const editor = (targetCell as HTMLElement).querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n }, 0);\n }\n}\n\n/**\n * Commit the currently active row edit.\n * @param grid - The grid instance\n */\nexport function commitActiveRowEdit(grid: InternalGrid): void {\n if (grid._activeEditRows !== -1) {\n exitRowEdit(grid, grid._activeEditRows, false);\n }\n}\n\n/**\n * Cancel the currently active row edit, reverting to original values.\n * @param grid - The grid instance\n */\nexport function cancelActiveRowEdit(grid: InternalGrid): void {\n if (grid._activeEditRows !== -1) {\n exitRowEdit(grid, grid._activeEditRows, true);\n }\n}\n","/**\n * Event Delegation Module\n *\n * Provides centralized event handling for grid cells using event delegation.\n * Instead of attaching 3-6 listeners per cell (30,000+ for large grids),\n * we attach a single listener per event type on the container.\n *\n * This dramatically reduces memory usage and improves initialization time.\n */\n\nimport type { ColumnConfig, InternalGrid } from '../types';\nimport { commitCellValue, FOCUSABLE_EDITOR_SELECTOR, inlineEnterEdit, startRowEdit } from './editing';\nimport { ensureCellVisible } from './keyboard';\nimport { booleanCellHTML, getColIndexFromCell, getRowIndexFromCell } from './utils';\n\n/**\n * Extract cell context from a cell element.\n * Returns null if the cell is invalid or indices are missing.\n */\nfunction getCellContext(\n grid: InternalGrid,\n cell: HTMLElement,\n): { rowIndex: number; colIndex: number; rowData: any; col: ColumnConfig<any> } | null {\n const rowIndex = getRowIndexFromCell(cell);\n const colIndex = getColIndexFromCell(cell);\n if (rowIndex < 0 || colIndex < 0) return null;\n\n const rowData = grid._rows[rowIndex];\n const col = grid._visibleColumns[colIndex];\n if (!rowData || !col) return null;\n\n return { rowIndex, colIndex, rowData, col };\n}\n\n/**\n * Handle delegated mousedown on editable cells.\n * Updates focus position without starting edit.\n */\nfunction handleCellMousedown(grid: InternalGrid, cell: HTMLElement): void {\n if (cell.classList.contains('editing')) return;\n\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n grid._focusRow = ctx.rowIndex;\n grid._focusCol = ctx.colIndex;\n ensureCellVisible(grid);\n}\n\n/**\n * Handle delegated click on editable cells (editMode === 'click').\n */\nfunction handleCellClick(grid: InternalGrid, cell: HTMLElement, e: MouseEvent): void {\n if (cell.classList.contains('editing')) return;\n\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n e.stopPropagation();\n grid._focusRow = ctx.rowIndex;\n grid._focusCol = ctx.colIndex;\n inlineEnterEdit(grid, ctx.rowData, ctx.rowIndex, ctx.col, cell);\n}\n\n/**\n * Handle delegated dblclick on editable cells (editMode === 'dblClick').\n */\nfunction handleCellDblclick(grid: InternalGrid, cell: HTMLElement, e: MouseEvent): void {\n e.stopPropagation();\n\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n // Use beginBulkEdit if available for consistent behavior with Enter key\n if (typeof grid.beginBulkEdit === 'function') {\n grid._focusRow = ctx.rowIndex;\n grid._focusCol = ctx.colIndex;\n grid.beginBulkEdit(ctx.rowIndex);\n return;\n }\n\n // Fallback: manual edit initiation\n startRowEdit(grid, ctx.rowIndex, ctx.rowData);\n const rowEl = grid.findRenderedRowElement?.(ctx.rowIndex);\n if (rowEl) {\n const children = rowEl.children;\n for (let i = 0; i < children.length; i++) {\n const col2 = grid._visibleColumns[i];\n if (col2 && (col2 as any).editable) {\n inlineEnterEdit(grid, ctx.rowData, ctx.rowIndex, col2, children[i] as HTMLElement, true);\n }\n }\n // Focus the editor in the clicked cell\n queueMicrotask(() => {\n const targetCell = rowEl.querySelector(`.cell[data-col=\"${grid._focusCol}\"]`);\n if (targetCell?.classList.contains('editing')) {\n const editor = (targetCell as HTMLElement).querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n });\n }\n}\n\n/**\n * Handle delegated keydown on editable cells.\n * Handles Enter, F2, Space (for boolean), and select/typeahead special cases.\n */\nfunction handleCellKeydown(grid: InternalGrid, cell: HTMLElement, e: KeyboardEvent): void {\n const ctx = getCellContext(grid, cell);\n if (!ctx) return;\n\n const { rowIndex, colIndex, rowData, col } = ctx;\n const isEditing = cell.classList.contains('editing');\n\n // Select/typeahead: Enter opens picker\n if ((col.type === 'select' || col.type === 'typeahead') && !isEditing && e.key === 'Enter') {\n e.preventDefault();\n if (grid._activeEditRows !== rowIndex) startRowEdit(grid, rowIndex, rowData);\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n setTimeout(() => {\n const selectEl = cell.querySelector('select') as HTMLSelectElement | null;\n try {\n (selectEl as any)?.showPicker?.();\n } catch {\n /* empty */\n }\n selectEl?.focus({ preventScroll: true });\n }, 0);\n return;\n }\n\n // Boolean: Space toggles value\n if (col.type === 'boolean' && e.key === ' ' && !isEditing) {\n e.preventDefault();\n if (grid._activeEditRows !== rowIndex) startRowEdit(grid, rowIndex, rowData);\n const newVal = !rowData[col.field];\n commitCellValue(grid, rowIndex, col, newVal, rowData);\n cell.innerHTML = booleanCellHTML(!!newVal);\n return;\n }\n\n // Enter: Start editing\n if (e.key === 'Enter' && !isEditing) {\n e.preventDefault();\n e.stopPropagation();\n grid._focusRow = rowIndex;\n grid._focusCol = colIndex;\n if (typeof grid.beginBulkEdit === 'function') {\n grid.beginBulkEdit(rowIndex);\n } else {\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n }\n return;\n }\n\n // F2: Start editing (alternative)\n if (e.key === 'F2' && !isEditing) {\n e.preventDefault();\n inlineEnterEdit(grid, rowData, rowIndex, col, cell);\n return;\n }\n}\n\n/**\n * Set up delegated event listeners on the grid body.\n * Call once during grid initialization.\n *\n * @param grid - The grid instance\n * @param bodyEl - The .rows element containing all data rows\n * @param signal - AbortSignal for cleanup\n */\nexport function setupCellEventDelegation(grid: InternalGrid, bodyEl: HTMLElement, signal: AbortSignal): void {\n const getEditMode = () => (grid as any).effectiveConfig?.editOn || (grid as any).editOn;\n\n // Mousedown - update focus on editable cells\n bodyEl.addEventListener(\n 'mousedown',\n (e) => {\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n // Check if this cell's column is editable\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellMousedown(grid, cell);\n }\n },\n { signal },\n );\n\n // Click - for editMode === 'click'\n bodyEl.addEventListener(\n 'click',\n (e) => {\n const editMode = getEditMode();\n if (editMode !== 'click') return;\n\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellClick(grid, cell, e);\n }\n },\n { signal },\n );\n\n // Dblclick - for editMode === 'dblClick' (default)\n bodyEl.addEventListener(\n 'dblclick',\n (e) => {\n const editMode = getEditMode();\n // Normalize: accept both 'dblClick' and 'dblclick'\n const normalized = editMode === 'dblclick' ? 'dblClick' : editMode;\n if (normalized === 'click' || editMode === false) return;\n\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellDblclick(grid, cell, e);\n }\n },\n { signal },\n );\n\n // Keydown - for Enter, F2, Space on editable cells\n bodyEl.addEventListener(\n 'keydown',\n (e) => {\n const cell = (e.target as HTMLElement).closest('.cell[data-col]') as HTMLElement | null;\n if (!cell) return;\n\n const colIndex = getColIndexFromCell(cell);\n if (colIndex < 0) return;\n\n const col = grid._visibleColumns[colIndex];\n if (col && (col as any).editable) {\n handleCellKeydown(grid, cell, e as KeyboardEvent);\n }\n },\n { signal },\n );\n}\n","/**\n * Sorting Module\n *\n * Handles column sorting state transitions and row ordering.\n */\n\nimport type { ColumnConfig, InternalGrid, SortHandler, SortState } from '../types';\nimport { renderHeader } from './header';\n\n/**\n * Default comparator for sorting values.\n * Handles nulls (pushed to end), numbers, and string fallback.\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n if (a == null && b == null) return 0;\n if (a == null) return -1;\n if (b == null) return 1;\n return a > b ? 1 : a < b ? -1 : 0;\n}\n\n/**\n * Built-in sort implementation using column comparator or default.\n * This is the default sortHandler when none is configured.\n */\nexport function builtInSort<T>(rows: T[], sortState: SortState, columns: ColumnConfig<T>[]): T[] {\n const col = columns.find((c) => c.field === sortState.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n const { field, direction } = sortState;\n\n return [...rows].sort((rA: any, rB: any) => {\n return comparator(rA[field], rB[field], rA, rB) * direction;\n });\n}\n\n/**\n * Apply sort result to grid and update UI.\n * Called after sync or async sort completes.\n */\nfunction finalizeSortResult(grid: InternalGrid, sortedRows: unknown[], col: ColumnConfig<any>, dir: 1 | -1): void {\n grid._rows = sortedRows as any[];\n // Bump epoch so renderVisibleRows triggers full inline rebuild\n grid.__rowRenderEpoch++;\n // Invalidate pooled rows to guarantee rebuild\n grid._rowPool.forEach((r) => ((r as any).__epoch = -1));\n renderHeader(grid);\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: dir } }),\n );\n // Trigger state change after sort applied\n grid.requestStateChange?.();\n}\n\n/**\n * Cycle sort state for a column: none -> ascending -> descending -> none.\n * Restores original row order when clearing sort.\n */\nexport function toggleSort(grid: InternalGrid, col: ColumnConfig<any>): void {\n if (!grid._sortState || grid._sortState.field !== col.field) {\n if (!grid._sortState) grid.__originalOrder = grid._rows.slice();\n applySort(grid, col, 1);\n } else if (grid._sortState.direction === 1) {\n applySort(grid, col, -1);\n } else {\n grid._sortState = null;\n // Force full row rebuild after clearing sort so templated cells reflect original order\n grid.__rowRenderEpoch++;\n // Invalidate existing pooled row epochs so virtualization triggers a full inline rebuild\n grid._rowPool.forEach((r) => ((r as any).__epoch = -1));\n grid._rows = grid.__originalOrder.slice();\n renderHeader(grid);\n // After re-render ensure cleared column shows aria-sort=\"none\" baseline.\n const headers = grid._headerRowEl?.querySelectorAll('[role=\"columnheader\"].sortable');\n headers?.forEach((h: any) => {\n if (!h.getAttribute('aria-sort')) h.setAttribute('aria-sort', 'none');\n else if (h.getAttribute('aria-sort') === 'ascending' || h.getAttribute('aria-sort') === 'descending') {\n // The active column was re-rendered already, but normalize any missing ones.\n if (!grid._sortState) h.setAttribute('aria-sort', 'none');\n }\n });\n grid.refreshVirtualWindow(true);\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('sort-change', { detail: { field: col.field, direction: 0 } }),\n );\n // Trigger state change after sort is cleared\n grid.requestStateChange?.();\n }\n}\n\n/**\n * Apply a concrete sort direction to rows.\n *\n * Uses custom sortHandler from gridConfig if provided, otherwise uses built-in sorting.\n * Supports both sync and async handlers (for server-side sorting).\n */\nexport function applySort(grid: InternalGrid, col: ColumnConfig<any>, dir: 1 | -1): void {\n grid._sortState = { field: col.field, direction: dir };\n\n const sortState: SortState = { field: col.field, direction: dir };\n const columns = grid._columns as ColumnConfig<any>[];\n\n // Get custom handler from effectiveConfig, or use built-in\n const handler: SortHandler<any> = (grid as any).effectiveConfig?.sortHandler ?? builtInSort;\n\n const result = handler(grid._rows, sortState, columns);\n\n // Handle async (Promise) or sync result\n if (result && typeof (result as Promise<any>).then === 'function') {\n // Async handler - wait for result\n (result as Promise<any[]>).then((sortedRows) => {\n finalizeSortResult(grid, sortedRows, col, dir);\n });\n } else {\n // Sync handler - apply immediately\n finalizeSortResult(grid, result as any[], col, dir);\n }\n}\n","/**\n * Header Rendering Module\n *\n * Handles rendering of the grid header row with sorting and resize affordances.\n */\n\nimport type { ColumnConfig, IconValue, InternalGrid } from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\nimport { addPart } from './columns';\nimport { toggleSort } from './sorting';\n\n/**\n * Set an icon value on an element. Handles both string and HTMLElement icons.\n */\nfunction setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.textContent = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n}\n\n/**\n * Rebuild the header row DOM based on current column configuration, attaching\n * sorting and resize affordances where enabled.\n */\nexport function renderHeader(grid: InternalGrid): void {\n grid._headerRowEl = (grid.findHeaderRow! as any)();\n const headerRow = grid._headerRowEl as HTMLElement;\n headerRow.innerHTML = '';\n\n grid._visibleColumns.forEach((col: ColumnConfig<any>, i: number) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n addPart(cell, 'header-cell');\n cell.setAttribute('role', 'columnheader');\n\n // aria-colindex is 1-based\n cell.setAttribute('aria-colindex', String(i + 1));\n cell.setAttribute('data-field', col.field);\n cell.setAttribute('data-col', String(i)); // Add data-col for consistency with body cells\n\n // Column grouping styling is handled by the grouping-columns plugin via afterRender\n const maybeTpl = (col as any).__headerTemplate as HTMLElement | undefined;\n if (maybeTpl) Array.from(maybeTpl.childNodes).forEach((n) => cell.appendChild(n.cloneNode(true)));\n else {\n const label = (col as any).header || col.field;\n const span = document.createElement('span');\n span.textContent = label;\n cell.appendChild(span);\n }\n if (col.sortable) {\n cell.classList.add('sortable');\n cell.tabIndex = 0;\n const icon = document.createElement('span');\n addPart(icon as any, 'sort-indicator');\n const active = grid._sortState?.field === col.field ? grid._sortState.direction : 0;\n // Use grid-level icons (fall back to defaults)\n const icons = { ...DEFAULT_GRID_ICONS, ...grid.icons };\n const iconValue = active === 1 ? icons.sortAsc : active === -1 ? icons.sortDesc : icons.sortNone;\n setIcon(icon, iconValue);\n cell.appendChild(icon);\n // Always set a baseline aria-sort for sortable headers for assistive tech clarity.\n cell.setAttribute('aria-sort', active === 0 ? 'none' : active === 1 ? 'ascending' : 'descending');\n cell.addEventListener('click', (e) => {\n // Ignore clicks that are the result of a resize drag ending\n if (grid._resizeController?.isResizing) return;\n // Let plugins handle the click first (e.g., multi-sort)\n if (grid._dispatchHeaderClick?.(e, i, cell)) return;\n toggleSort(grid, col);\n });\n cell.addEventListener('keydown', (e) => {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n // Let plugins handle the keydown first\n if (grid._dispatchHeaderClick?.(e as unknown as MouseEvent, i, cell)) return;\n toggleSort(grid, col);\n }\n });\n }\n if (col.resizable) {\n // Set position: relative for the resize handle positioning context\n // Note: If a plugin applies position: sticky (e.g., PinnedColumnsPlugin), it will override this\n cell.style.position = 'relative';\n const handle = document.createElement('div');\n handle.className = 'resize-handle';\n handle.setAttribute('aria-hidden', 'true');\n handle.addEventListener('mousedown', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.start(e, i, cell);\n });\n // Double-click to reset column width to default\n handle.addEventListener('dblclick', (e: MouseEvent) => {\n e.stopPropagation();\n e.preventDefault();\n grid._resizeController.resetColumn(i);\n });\n cell.appendChild(handle);\n }\n headerRow.appendChild(cell);\n });\n\n // Ensure every sortable header has a baseline aria-sort if not already set during construction.\n headerRow.querySelectorAll('.cell.sortable').forEach((el) => {\n if (!el.getAttribute('aria-sort')) el.setAttribute('aria-sort', 'none');\n });\n\n // Set ARIA role only if header has children (role=\"row\" requires columnheader children)\n // When grid is cleared with 0 columns, the header row should not have role=\"row\"\n if (headerRow.children.length > 0) {\n headerRow.setAttribute('role', 'row');\n headerRow.setAttribute('aria-rowindex', '1');\n } else {\n headerRow.removeAttribute('role');\n headerRow.removeAttribute('aria-rowindex');\n }\n}\n","/**\n * Idle Scheduler - Defer non-critical work to browser idle periods.\n *\n * Uses requestIdleCallback where available, with fallback to setTimeout.\n * This allows the main thread to remain responsive during startup.\n */\n\n/**\n * Check if requestIdleCallback is available (not in Safari < 17.4).\n */\nconst hasIdleCallback = typeof requestIdleCallback === 'function';\n\n/**\n * IdleDeadline-compatible interface for fallback.\n */\ninterface IdleDeadlineLike {\n didTimeout: boolean;\n timeRemaining(): number;\n}\n\n/**\n * Schedule work to run during browser idle time.\n * Falls back to setTimeout(0) if requestIdleCallback is not available.\n *\n * @param callback - Work to run when idle\n * @param options - Optional timeout configuration\n * @returns Handle for cancellation\n */\nexport function scheduleIdle(callback: (deadline: IdleDeadlineLike) => void, options?: { timeout?: number }): number {\n if (hasIdleCallback) {\n return requestIdleCallback(callback, options);\n }\n\n // Fallback for Safari (before 17.4) and older browsers\n return window.setTimeout(() => {\n const start = Date.now();\n callback({\n didTimeout: false,\n timeRemaining: () => Math.max(0, 50 - (Date.now() - start)),\n });\n }, 1) as unknown as number;\n}\n\n/**\n * Cancel a scheduled idle callback.\n */\nexport function cancelIdle(handle: number): void {\n if (hasIdleCallback) {\n cancelIdleCallback(handle);\n } else {\n clearTimeout(handle);\n }\n}\n\n/**\n * Queue of deferred tasks to run during idle periods.\n */\ninterface DeferredTask {\n fn: () => void;\n priority: number; // Lower = higher priority\n}\n\n/**\n * Deferred work queue that runs tasks during idle periods.\n * Groups related work and processes in priority order.\n */\nexport class IdleQueue {\n private tasks: DeferredTask[] = [];\n private scheduled = false;\n private handle: number | null = null;\n\n /**\n * Add a task to the queue.\n * @param fn - Function to execute\n * @param priority - Priority (lower = run sooner). Default 10.\n */\n add(fn: () => void, priority = 10): void {\n this.tasks.push({ fn, priority });\n\n if (!this.scheduled) {\n this.scheduled = true;\n this.handle = scheduleIdle((deadline) => this.process(deadline), { timeout: 100 });\n }\n }\n\n /**\n * Process tasks until we run out of time or tasks.\n */\n private process(deadline: IdleDeadlineLike): void {\n // Sort by priority (stable sort for same priority)\n this.tasks.sort((a, b) => a.priority - b.priority);\n\n // Process tasks while we have time\n while (this.tasks.length > 0 && (deadline.timeRemaining() > 0 || deadline.didTimeout)) {\n const task = this.tasks.shift();\n if (task) {\n try {\n task.fn();\n } catch (error) {\n console.error('[IdleQueue] Task error:', error);\n }\n }\n }\n\n // If tasks remain, schedule another idle callback\n if (this.tasks.length > 0) {\n this.handle = scheduleIdle((d) => this.process(d), { timeout: 100 });\n } else {\n this.scheduled = false;\n this.handle = null;\n }\n }\n\n /**\n * Cancel all pending tasks.\n */\n cancel(): void {\n this.tasks = [];\n if (this.handle !== null) {\n cancelIdle(this.handle);\n this.handle = null;\n }\n this.scheduled = false;\n }\n\n /**\n * Check if the queue is empty.\n */\n get isEmpty(): boolean {\n return this.tasks.length === 0;\n }\n}\n\n/**\n * Singleton idle queue for grid-wide deferred work.\n */\nlet globalIdleQueue: IdleQueue | null = null;\n\n/**\n * Get the global idle queue (creates if needed).\n */\nexport function getIdleQueue(): IdleQueue {\n if (!globalIdleQueue) {\n globalIdleQueue = new IdleQueue();\n }\n return globalIdleQueue;\n}\n\n/**\n * Schedule work to run during idle time using the global queue.\n */\nexport function deferToIdle(fn: () => void, priority = 10): void {\n getIdleQueue().add(fn, priority);\n}\n","import type { InternalGrid, ResizeController } from '../types';\n\nexport function createResizeController(grid: InternalGrid): ResizeController {\n let resizeState: { startX: number; colIndex: number; startWidth: number } | null = null;\n let pendingRaf: number | null = null;\n let prevCursor: string | null = null;\n let prevUserSelect: string | null = null;\n const onMove = (e: MouseEvent) => {\n if (!resizeState) return;\n const delta = e.clientX - resizeState.startX;\n const width = Math.max(40, resizeState.startWidth + delta);\n const col = grid._visibleColumns[resizeState.colIndex];\n col.width = width;\n col.__userResized = true;\n col.__renderedWidth = width;\n if (pendingRaf == null) {\n pendingRaf = requestAnimationFrame(() => {\n pendingRaf = null;\n grid.updateTemplate?.();\n });\n }\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize', { detail: { field: col.field, width } }),\n );\n };\n let justFinishedResize = false;\n const onUp = () => {\n const hadResize = resizeState !== null;\n // Set flag to suppress click events that fire immediately after mouseup\n if (hadResize) {\n justFinishedResize = true;\n requestAnimationFrame(() => {\n justFinishedResize = false;\n });\n }\n window.removeEventListener('mousemove', onMove);\n window.removeEventListener('mouseup', onUp);\n if (prevCursor !== null) {\n document.documentElement.style.cursor = prevCursor;\n prevCursor = null;\n }\n if (prevUserSelect !== null) {\n document.body.style.userSelect = prevUserSelect;\n prevUserSelect = null;\n }\n resizeState = null;\n // Trigger state change after resize completes\n if (hadResize && grid.requestStateChange) {\n grid.requestStateChange();\n }\n };\n return {\n get isResizing() {\n return resizeState !== null || justFinishedResize;\n },\n start(e, colIndex, cell) {\n e.preventDefault();\n const rect = cell.getBoundingClientRect();\n resizeState = { startX: e.clientX, colIndex, startWidth: rect.width };\n window.addEventListener('mousemove', onMove);\n window.addEventListener('mouseup', onUp);\n if (prevCursor === null) prevCursor = document.documentElement.style.cursor;\n document.documentElement.style.cursor = 'e-resize';\n if (prevUserSelect === null) prevUserSelect = document.body.style.userSelect;\n document.body.style.userSelect = 'none';\n },\n resetColumn(colIndex) {\n const col = grid._visibleColumns[colIndex];\n if (!col) return;\n\n // Reset to original configured width (or undefined for auto-sizing)\n col.__userResized = false;\n col.__renderedWidth = undefined;\n col.width = col.__originalWidth;\n\n grid.updateTemplate?.();\n grid.requestStateChange?.();\n (grid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('column-resize-reset', { detail: { field: col.field, width: col.width } }),\n );\n },\n dispose() {\n onUp();\n },\n };\n}\n","/**\n * DOM Builder - Direct DOM construction for performance.\n *\n * Using direct DOM APIs instead of innerHTML is significantly faster:\n * - No HTML parsing by the browser\n * - No template string concatenation\n * - Immediate element creation without serialization/deserialization\n *\n * Benchmark: DOM construction is ~2-3x faster than innerHTML for complex structures.\n */\n\n/**\n * Create an element with attributes and optional children.\n * Optimized helper that avoids repeated function calls.\n */\nexport function createElement<K extends keyof HTMLElementTagNameMap>(\n tag: K,\n attrs?: Record<string, string>,\n children?: (Node | string | null | undefined)[],\n): HTMLElementTagNameMap[K] {\n const el = document.createElement(tag);\n\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n\n if (children) {\n for (const child of children) {\n if (child == null) continue;\n if (typeof child === 'string') {\n el.appendChild(document.createTextNode(child));\n } else {\n el.appendChild(child);\n }\n }\n }\n\n return el;\n}\n\n/**\n * Create a text node (shorthand).\n */\nexport function text(content: string): Text {\n return document.createTextNode(content);\n}\n\n/**\n * Create an element with class (common pattern).\n */\nexport function div(className?: string, attrs?: Record<string, string>): HTMLDivElement {\n const el = document.createElement('div');\n if (className) el.className = className;\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n return el;\n}\n\n/**\n * Create a button element.\n */\nexport function button(className?: string, attrs?: Record<string, string>, content?: string | Node): HTMLButtonElement {\n const el = document.createElement('button');\n if (className) el.className = className;\n if (attrs) {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n }\n if (content) {\n if (typeof content === 'string') {\n el.textContent = content;\n } else {\n el.appendChild(content);\n }\n }\n return el;\n}\n\n/**\n * Create a slot element for light DOM projection.\n */\nexport function slot(name?: string): HTMLSlotElement {\n const el = document.createElement('slot');\n if (name) el.name = name;\n return el;\n}\n\n/**\n * Append multiple children to a parent element.\n */\nexport function appendChildren(parent: Element, children: (Node | null | undefined)[]): void {\n for (const child of children) {\n if (child) parent.appendChild(child);\n }\n}\n\n/**\n * Set multiple attributes on an element.\n */\nexport function setAttrs(el: Element, attrs: Record<string, string | undefined>): void {\n for (const key in attrs) {\n const value = attrs[key];\n if (value !== undefined && value !== null) {\n el.setAttribute(key, value);\n }\n }\n}\n\n// ============================================================================\n// Grid Structure Templates (Pre-built for cloning)\n// ============================================================================\n\n/**\n * Template for grid content (the core scrollable grid area).\n * Pre-built once, then cloned for each grid instance.\n */\nconst gridContentTemplate = document.createElement('template');\ngridContentTemplate.innerHTML = `\n <div class=\"tbw-scroll-area\">\n <div class=\"rows-body-wrapper\">\n <div class=\"rows-body\" role=\"grid\">\n <div class=\"header\">\n <div class=\"header-row\" part=\"header-row\"></div>\n </div>\n <div class=\"rows-container\">\n <div class=\"rows-viewport\">\n <div class=\"rows\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"faux-vscroll\">\n <div class=\"faux-vscroll-spacer\"></div>\n </div>\n`;\n\n/**\n * Clone the grid content structure.\n * Using template cloning is faster than createElement for nested structures.\n */\nexport function cloneGridContent(): DocumentFragment {\n return gridContentTemplate.content.cloneNode(true) as DocumentFragment;\n}\n\n/**\n * Build the grid root structure using direct DOM construction.\n * This is called once per grid instance during initial render.\n */\nexport interface GridDOMOptions {\n hasShell: boolean;\n /** Shell header element (pre-built) */\n shellHeader?: Element;\n /** Shell body element with tool panel (pre-built) */\n shellBody?: Element;\n}\n\n/**\n * Build the complete grid DOM structure.\n * Returns a DocumentFragment ready to be appended to shadow root.\n */\nexport function buildGridDOM(options: GridDOMOptions): DocumentFragment {\n const fragment = document.createDocumentFragment();\n\n const root = div(options.hasShell ? 'tbw-grid-root has-shell' : 'tbw-grid-root');\n\n if (options.hasShell && options.shellHeader && options.shellBody) {\n // Shell mode: header + body (with grid content inside)\n root.appendChild(options.shellHeader);\n root.appendChild(options.shellBody);\n } else {\n // No shell: just grid content in wrapper\n const contentWrapper = div('tbw-grid-content');\n contentWrapper.appendChild(cloneGridContent());\n root.appendChild(contentWrapper);\n }\n\n fragment.appendChild(root);\n return fragment;\n}\n\n/**\n * Build shell header using direct DOM construction.\n */\nexport interface ShellHeaderOptions {\n title?: string;\n hasLightDomButtons: boolean;\n hasPanels: boolean;\n isPanelOpen: boolean;\n toolPanelIcon: string;\n /** Config toolbar buttons (pre-sorted by order) */\n configButtons: Array<{\n id: string;\n label: string;\n icon?: string;\n disabled?: boolean;\n hasElement?: boolean;\n hasRender?: boolean;\n action?: () => void;\n }>;\n /** API toolbar buttons (pre-sorted by order) */\n apiButtons: Array<{\n id: string;\n label: string;\n icon?: string;\n disabled?: boolean;\n hasElement?: boolean;\n hasRender?: boolean;\n action?: () => void;\n }>;\n}\n\n/**\n * Build shell header element directly without innerHTML.\n */\nexport function buildShellHeader(options: ShellHeaderOptions): HTMLDivElement {\n const header = div('tbw-shell-header', { part: 'shell-header', role: 'presentation' });\n\n // Title\n if (options.title) {\n const titleEl = div('tbw-shell-title');\n titleEl.textContent = options.title;\n header.appendChild(titleEl);\n }\n\n // Shell content with slot for header content\n const content = div('tbw-shell-content', { part: 'shell-content', role: 'presentation' });\n content.appendChild(slot('header-content'));\n header.appendChild(content);\n\n // Toolbar\n const toolbar = div('tbw-shell-toolbar', { part: 'shell-toolbar', role: 'presentation' });\n\n // Config buttons with icon/action\n for (const btn of options.configButtons) {\n if (btn.icon && btn.action) {\n const btnEl = button('tbw-toolbar-btn', {\n 'data-btn': btn.id,\n title: btn.label,\n 'aria-label': btn.label,\n });\n if (btn.disabled) btnEl.disabled = true;\n btnEl.innerHTML = btn.icon; // Icons can be HTML (e.g., emojis or SVG)\n toolbar.appendChild(btnEl);\n }\n }\n\n // API buttons with icon/action\n for (const btn of options.apiButtons) {\n if (btn.icon && btn.action) {\n const btnEl = button('tbw-toolbar-btn', {\n 'data-btn': btn.id,\n title: btn.label,\n 'aria-label': btn.label,\n });\n if (btn.disabled) btnEl.disabled = true;\n btnEl.innerHTML = btn.icon;\n toolbar.appendChild(btnEl);\n }\n }\n\n // Placeholders for config/API buttons with element or render function\n for (const btn of options.configButtons) {\n if (btn.hasElement || btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-btn-slot', { 'data-btn-slot': btn.id }));\n }\n }\n for (const btn of options.apiButtons) {\n if (btn.hasElement || btn.hasRender) {\n toolbar.appendChild(div('tbw-toolbar-btn-slot', { 'data-btn-slot': btn.id }));\n }\n }\n\n // Light DOM slot for toolbar\n if (options.hasLightDomButtons) {\n toolbar.appendChild(slot('toolbar'));\n }\n\n // Separator\n const hasCustomButtons =\n options.configButtons.length > 0 || options.apiButtons.length > 0 || options.hasLightDomButtons;\n if (hasCustomButtons && options.hasPanels) {\n toolbar.appendChild(div('tbw-toolbar-separator'));\n }\n\n // Panel toggle button\n if (options.hasPanels) {\n const toggleBtn = button(options.isPanelOpen ? 'tbw-toolbar-btn active' : 'tbw-toolbar-btn', {\n 'data-panel-toggle': '',\n title: 'Settings',\n 'aria-label': 'Toggle settings panel',\n 'aria-pressed': String(options.isPanelOpen),\n 'aria-controls': 'tbw-tool-panel',\n });\n toggleBtn.innerHTML = options.toolPanelIcon;\n toolbar.appendChild(toggleBtn);\n }\n\n header.appendChild(toolbar);\n return header;\n}\n\n/**\n * Build shell body (grid content + optional tool panel).\n */\nexport interface ShellBodyOptions {\n position: 'left' | 'right';\n isPanelOpen: boolean;\n expandIcon: string;\n collapseIcon: string;\n /** Sorted panels for accordion */\n panels: Array<{\n id: string;\n title: string;\n icon?: string;\n isExpanded: boolean;\n }>;\n}\n\n/**\n * Build shell body element directly without innerHTML.\n */\nexport function buildShellBody(options: ShellBodyOptions): HTMLDivElement {\n const body = div('tbw-shell-body');\n const hasPanel = options.panels.length > 0;\n const isSinglePanel = options.panels.length === 1;\n\n // Grid content wrapper with cloned grid structure\n const gridContent = div('tbw-grid-content');\n gridContent.appendChild(cloneGridContent());\n\n // Tool panel\n let panelEl: HTMLElement | null = null;\n if (hasPanel) {\n panelEl = createElement('aside', {\n class: options.isPanelOpen ? 'tbw-tool-panel open' : 'tbw-tool-panel',\n part: 'tool-panel',\n 'data-position': options.position,\n role: 'presentation',\n id: 'tbw-tool-panel',\n });\n\n // Resize handle\n const resizeHandlePosition = options.position === 'left' ? 'right' : 'left';\n panelEl.appendChild(\n div('tbw-tool-panel-resize', {\n 'data-resize-handle': '',\n 'data-handle-position': resizeHandlePosition,\n 'aria-hidden': 'true',\n }),\n );\n\n // Panel content with accordion\n const panelContent = div('tbw-tool-panel-content', { role: 'presentation' });\n const accordion = div('tbw-accordion');\n\n for (const panel of options.panels) {\n const sectionClasses = `tbw-accordion-section${panel.isExpanded ? ' expanded' : ''}${isSinglePanel ? ' single' : ''}`;\n const section = div(sectionClasses, { 'data-section': panel.id });\n\n // Accordion header button\n const headerBtn = button('tbw-accordion-header', {\n 'aria-expanded': String(panel.isExpanded),\n 'aria-controls': `tbw-section-${panel.id}`,\n });\n if (isSinglePanel) headerBtn.setAttribute('aria-disabled', 'true');\n\n // Icon\n if (panel.icon) {\n const iconSpan = createElement('span', { class: 'tbw-accordion-icon' });\n iconSpan.innerHTML = panel.icon;\n headerBtn.appendChild(iconSpan);\n }\n\n // Title\n const titleSpan = createElement('span', { class: 'tbw-accordion-title' });\n titleSpan.textContent = panel.title;\n headerBtn.appendChild(titleSpan);\n\n // Chevron (hidden for single panel)\n if (!isSinglePanel) {\n const chevronSpan = createElement('span', { class: 'tbw-accordion-chevron' });\n chevronSpan.innerHTML = panel.isExpanded ? options.collapseIcon : options.expandIcon;\n headerBtn.appendChild(chevronSpan);\n }\n\n section.appendChild(headerBtn);\n\n // Accordion content (empty, will be filled by panel render functions)\n section.appendChild(\n div('tbw-accordion-content', {\n id: `tbw-section-${panel.id}`,\n role: 'presentation',\n }),\n );\n\n accordion.appendChild(section);\n }\n\n panelContent.appendChild(accordion);\n panelEl.appendChild(panelContent);\n }\n\n // Append in correct order based on position\n if (options.position === 'left' && panelEl) {\n body.appendChild(panelEl);\n body.appendChild(gridContent);\n } else {\n body.appendChild(gridContent);\n if (panelEl) body.appendChild(panelEl);\n }\n\n return body;\n}\n","/**\n * Shell infrastructure for grid header bar and tool panels.\n *\n * The shell is an optional wrapper layer that provides:\n * - Header bar with title, plugin content, and toolbar buttons\n * - Tool panels that plugins can register content into\n * - Light DOM parsing for framework-friendly configuration\n */\n\nimport type {\n HeaderContentDefinition,\n IconValue,\n ShellConfig,\n ToolbarButtonConfig,\n ToolbarButtonInfo,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\n\n/**\n * Convert an IconValue to a string for rendering in HTML.\n */\nfunction iconToString(icon: IconValue | undefined): string {\n if (!icon) return '';\n if (typeof icon === 'string') return icon;\n // For HTMLElement, get the outerHTML\n return icon.outerHTML;\n}\n\n/**\n * State for managing shell UI.\n */\nexport interface ShellState {\n /** Registered tool panels (from plugins + consumer API) */\n toolPanels: Map<string, ToolPanelDefinition>;\n /** Registered header content (from plugins + consumer API) */\n headerContents: Map<string, HeaderContentDefinition>;\n /** Custom toolbar buttons registered via API */\n toolbarButtons: Map<string, ToolbarButtonConfig>;\n /** Light DOM toolbar buttons */\n lightDomButtons: HTMLElement[];\n /** Light DOM header content elements */\n lightDomHeaderContent: HTMLElement[];\n /** Light DOM header title from <tbw-grid-header title=\"...\"> */\n lightDomTitle: string | null;\n /** IDs of tool panels registered from light DOM (to avoid re-parsing) */\n lightDomToolPanelIds: Set<string>;\n /** Whether the tool panel sidebar is open */\n isPanelOpen: boolean;\n /** Which accordion sections are expanded (by panel ID) */\n expandedSections: Set<string>;\n /** Cleanup functions for header content render returns */\n headerContentCleanups: Map<string, () => void>;\n /** Cleanup functions for each panel section's render return */\n panelCleanups: Map<string, () => void>;\n /** Cleanup functions for toolbar button render returns */\n toolbarButtonCleanups: Map<string, () => void>;\n /** @deprecated Use isPanelOpen instead. Kept for backward compatibility. */\n activePanel: string | null;\n /** @deprecated Use panelCleanups instead. Kept for backward compatibility. */\n activePanelCleanup: (() => void) | null;\n}\n\n/**\n * Create initial shell state.\n */\nexport function createShellState(): ShellState {\n return {\n toolPanels: new Map(),\n headerContents: new Map(),\n toolbarButtons: new Map(),\n lightDomButtons: [],\n lightDomHeaderContent: [],\n lightDomTitle: null,\n lightDomToolPanelIds: new Set(),\n isPanelOpen: false,\n expandedSections: new Set(),\n headerContentCleanups: new Map(),\n panelCleanups: new Map(),\n toolbarButtonCleanups: new Map(),\n // Deprecated - kept for backward compatibility\n activePanel: null,\n activePanelCleanup: null,\n };\n}\n\n/**\n * Determine if shell header should be rendered.\n */\nexport function shouldRenderShellHeader(config: ShellConfig | undefined, state: ShellState): boolean {\n // Check if title is configured (from config or light DOM)\n if (config?.header?.title) return true;\n if (state.lightDomTitle) return true;\n\n // Check if config has toolbar buttons\n if (config?.header?.toolbarButtons?.length) return true;\n\n // Check if any tool panels are registered (need toolbar buttons for them)\n if (state.toolPanels.size > 0) return true;\n\n // Check if any header content is registered\n if (state.headerContents.size > 0) return true;\n\n // Check if any API toolbar buttons registered\n if (state.toolbarButtons.size > 0) return true;\n\n // Check if light DOM has header elements\n if (state.lightDomButtons.length > 0 || state.lightDomHeaderContent.length > 0) return true;\n\n return false;\n}\n\n/**\n * Render the shell header HTML.\n * @param toolPanelIcon - Icon for the tool panel toggle (from grid icon config)\n */\nexport function renderShellHeader(\n config: ShellConfig | undefined,\n state: ShellState,\n toolPanelIcon: IconValue = '☰',\n): string {\n const title = config?.header?.title ?? '';\n const hasTitle = !!title;\n const iconStr = iconToString(toolPanelIcon);\n\n // Collect all toolbar buttons in order\n // 1. Config buttons (sorted by order)\n // 2. API-registered buttons (sorted by order)\n // 3. Light DOM buttons via slot\n // 4. Single panel toggle button (if any panels registered)\n\n const configButtons = config?.header?.toolbarButtons ?? [];\n const hasConfigButtons = configButtons.length > 0;\n const hasApiButtons = state.toolbarButtons.size > 0;\n const hasLightDomButtons = state.lightDomButtons.length > 0;\n const hasPanels = state.toolPanels.size > 0;\n const hasCustomButtons = hasConfigButtons || hasApiButtons || hasLightDomButtons;\n const showSeparator = hasCustomButtons && hasPanels;\n\n // Sort config buttons by order\n const sortedConfigButtons = [...configButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Sort API buttons by order\n const sortedApiButtons = [...state.toolbarButtons.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build toolbar HTML\n let toolbarHtml = '';\n\n // Config buttons with icon/action (grid renders these)\n for (const btn of sortedConfigButtons) {\n if (btn.icon && btn.action) {\n toolbarHtml += `<button class=\"tbw-toolbar-btn\" data-btn=\"${btn.id}\" title=\"${btn.label}\" aria-label=\"${\n btn.label\n }\"${btn.disabled ? ' disabled' : ''}>${btn.icon}</button>`;\n }\n }\n\n // API buttons with icon/action\n for (const btn of sortedApiButtons) {\n if (btn.icon && btn.action) {\n toolbarHtml += `<button class=\"tbw-toolbar-btn\" data-btn=\"${btn.id}\" title=\"${btn.label}\" aria-label=\"${\n btn.label\n }\"${btn.disabled ? ' disabled' : ''}>${btn.icon}</button>`;\n }\n }\n\n // Placeholders for config/API buttons with element or render function\n for (const btn of sortedConfigButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n for (const btn of sortedApiButtons) {\n if (btn.element || btn.render) {\n toolbarHtml += `<div class=\"tbw-toolbar-btn-slot\" data-btn-slot=\"${btn.id}\"></div>`;\n }\n }\n\n // Light DOM slot\n if (hasLightDomButtons) {\n toolbarHtml += '<slot name=\"toolbar\"></slot>';\n }\n\n // Separator\n if (showSeparator) {\n toolbarHtml += '<div class=\"tbw-toolbar-separator\"></div>';\n }\n\n // Single panel toggle button (opens accordion-style sidebar with all panels)\n if (hasPanels) {\n const isOpen = state.isPanelOpen;\n toolbarHtml += `<button class=\"tbw-toolbar-btn${isOpen ? ' active' : ''}\" data-panel-toggle title=\"Settings\" aria-label=\"Toggle settings panel\" aria-pressed=\"${isOpen}\" aria-controls=\"tbw-tool-panel\">${iconStr}</button>`;\n }\n\n return `\n <div class=\"tbw-shell-header\" part=\"shell-header\" role=\"presentation\">\n ${hasTitle ? `<div class=\"tbw-shell-title\">${title}</div>` : ''}\n <div class=\"tbw-shell-content\" part=\"shell-content\" role=\"presentation\">\n <slot name=\"header-content\"></slot>\n </div>\n <div class=\"tbw-shell-toolbar\" part=\"shell-toolbar\" role=\"presentation\">\n ${toolbarHtml}\n </div>\n </div>\n `;\n}\n\n/**\n * Render the shell body wrapper HTML (contains grid content + accordion-style tool panel).\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderShellBody(\n config: ShellConfig | undefined,\n state: ShellState,\n gridContentHtml: string,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): string {\n const position = config?.toolPanel?.position ?? 'right';\n const hasPanel = state.toolPanels.size > 0;\n const isOpen = state.isPanelOpen;\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n // Sort panels by order for accordion sections\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const isSinglePanel = sortedPanels.length === 1;\n\n // Build accordion sections HTML\n let accordionHtml = '';\n for (const panel of sortedPanels) {\n const isExpanded = state.expandedSections.has(panel.id);\n const iconHtml = panel.icon ? `<span class=\"tbw-accordion-icon\">${panel.icon}</span>` : '';\n // Hide chevron for single panel (no toggling needed)\n const chevronHtml = isSinglePanel\n ? ''\n : `<span class=\"tbw-accordion-chevron\">${isExpanded ? collapseIcon : expandIcon}</span>`;\n // Disable accordion toggle for single panel\n const sectionClasses = `tbw-accordion-section${isExpanded ? ' expanded' : ''}${isSinglePanel ? ' single' : ''}`;\n accordionHtml += `\n <div class=\"${sectionClasses}\" data-section=\"${panel.id}\">\n <button class=\"tbw-accordion-header\" aria-expanded=\"${isExpanded}\" aria-controls=\"tbw-section-${panel.id}\"${isSinglePanel ? ' aria-disabled=\"true\"' : ''}>\n ${iconHtml}\n <span class=\"tbw-accordion-title\">${panel.title}</span>\n ${chevronHtml}\n </button>\n <div class=\"tbw-accordion-content\" id=\"tbw-section-${panel.id}\" role=\"presentation\"></div>\n </div>\n `;\n }\n\n // Resize handle position depends on panel position\n const resizeHandlePosition = position === 'left' ? 'right' : 'left';\n\n const panelHtml = hasPanel\n ? `\n <aside class=\"tbw-tool-panel${isOpen ? ' open' : ''}\" part=\"tool-panel\" data-position=\"${position}\" role=\"presentation\" id=\"tbw-tool-panel\">\n <div class=\"tbw-tool-panel-resize\" data-resize-handle data-handle-position=\"${resizeHandlePosition}\" aria-hidden=\"true\"></div>\n <div class=\"tbw-tool-panel-content\" role=\"presentation\">\n <div class=\"tbw-accordion\">\n ${accordionHtml}\n </div>\n </div>\n </aside>\n `\n : '';\n\n // For left position, panel comes before content in DOM order\n if (position === 'left') {\n return `\n <div class=\"tbw-shell-body\">\n ${panelHtml}\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n </div>\n `;\n }\n\n return `\n <div class=\"tbw-shell-body\">\n <div class=\"tbw-grid-content\">\n ${gridContentHtml}\n </div>\n ${panelHtml}\n </div>\n `;\n}\n\n/**\n * Parse light DOM shell elements (tbw-grid-header, etc.).\n * Safe to call multiple times - will only parse once when elements are available.\n */\nexport function parseLightDomShell(host: HTMLElement, state: ShellState): void {\n const headerEl = host.querySelector('tbw-grid-header');\n if (!headerEl) return;\n\n // Parse title attribute (only if not already parsed)\n if (!state.lightDomTitle) {\n const title = headerEl.getAttribute('title');\n if (title) {\n state.lightDomTitle = title;\n }\n }\n\n // Parse header content elements\n const headerContents = headerEl.querySelectorAll('tbw-grid-header-content');\n if (headerContents.length > 0 && state.lightDomHeaderContent.length === 0) {\n state.lightDomHeaderContent = Array.from(headerContents) as HTMLElement[];\n\n // Assign slot names for slotting into shadow DOM\n state.lightDomHeaderContent.forEach((el) => {\n el.setAttribute('slot', 'header-content');\n });\n }\n\n // Parse toolbar button elements (only process if we haven't already)\n // Check for placeholder elements OR already-transformed button elements\n const toolButtonPlaceholders = headerEl.querySelectorAll('tbw-grid-tool-button');\n const existingButtons = headerEl.querySelectorAll('button.tbw-toolbar-btn[slot=\"toolbar\"]');\n\n if (toolButtonPlaceholders.length > 0 && state.lightDomButtons.length === 0) {\n // First time parsing - convert placeholders to buttons\n state.lightDomButtons = Array.from(toolButtonPlaceholders).map((placeholder) => {\n // Create actual button element from placeholder attributes\n const button = document.createElement('button');\n button.className = 'tbw-toolbar-btn';\n const id = placeholder.getAttribute('id') ?? '';\n const label = placeholder.getAttribute('label') ?? '';\n const icon = placeholder.getAttribute('icon') ?? '';\n const order = placeholder.getAttribute('order') ?? '100';\n\n button.setAttribute('data-btn', id);\n button.setAttribute('title', label);\n button.setAttribute('aria-label', label);\n button.setAttribute('data-order', order);\n button.textContent = icon;\n button.setAttribute('slot', 'toolbar');\n\n // Replace placeholder with actual button in the DOM\n placeholder.replaceWith(button);\n\n return button;\n });\n\n // Sort by order attribute\n state.lightDomButtons.sort((a, b) => {\n const orderA = parseInt(a.getAttribute('data-order') ?? '100', 10);\n const orderB = parseInt(b.getAttribute('data-order') ?? '100', 10);\n return orderA - orderB;\n });\n } else if (existingButtons.length > 0 && state.lightDomButtons.length === 0) {\n // Buttons were already transformed (perhaps by a previous call), just reference them\n state.lightDomButtons = Array.from(existingButtons) as HTMLElement[];\n state.lightDomButtons.sort((a, b) => {\n const orderA = parseInt(a.getAttribute('data-order') ?? '100', 10);\n const orderB = parseInt(b.getAttribute('data-order') ?? '100', 10);\n return orderA - orderB;\n });\n }\n\n // Move buttons outside of header element before hiding it (so they remain visible for slotting)\n state.lightDomButtons.forEach((button) => {\n if (button.parentElement === headerEl) {\n host.appendChild(button);\n }\n });\n\n // Hide the light DOM header container (after moving buttons out)\n (headerEl as HTMLElement).style.display = 'none';\n}\n\n/**\n * Callback type for creating a tool panel renderer from a light DOM element.\n * This is used by framework adapters (Angular, React, etc.) to create renderers\n * from their template syntax.\n */\nexport type ToolPanelRendererFactory = (\n element: HTMLElement,\n) => ((container: HTMLElement) => void | (() => void)) | undefined;\n\n/**\n * Parse light DOM tool panel elements (<tbw-grid-tool-panel>).\n * These can appear as direct children of <tbw-grid> for declarative tool panel configuration.\n *\n * Attributes:\n * - `id` (required): Unique panel identifier\n * - `title` (required): Panel title shown in accordion header\n * - `icon`: Icon for accordion section header (emoji or text)\n * - `tooltip`: Tooltip for accordion section header\n * - `order`: Panel order priority (lower = first, default: 100)\n *\n * For vanilla JS, the element's innerHTML is used as the panel content.\n * For framework adapters, the adapter can provide a custom renderer factory.\n *\n * @param host - The grid host element\n * @param state - Shell state to update\n * @param rendererFactory - Optional factory for creating renderers (used by framework adapters)\n */\nexport function parseLightDomToolPanels(\n host: HTMLElement,\n state: ShellState,\n rendererFactory?: ToolPanelRendererFactory,\n): void {\n const toolPanelElements = host.querySelectorAll(':scope > tbw-grid-tool-panel');\n\n toolPanelElements.forEach((element) => {\n const panelEl = element as HTMLElement;\n const id = panelEl.getAttribute('id');\n const title = panelEl.getAttribute('title');\n\n // Skip if required attributes are missing\n if (!id || !title) {\n console.warn(\n `[parseLightDomToolPanels] Tool panel missing required id or title attribute: id=\"${id ?? ''}\", title=\"${title ?? ''}\"`,\n );\n return;\n }\n\n const icon = panelEl.getAttribute('icon') ?? undefined;\n const tooltip = panelEl.getAttribute('tooltip') ?? undefined;\n const order = parseInt(panelEl.getAttribute('order') ?? '100', 10);\n\n // Try framework adapter first, then fall back to innerHTML\n let render: (container: HTMLElement) => void | (() => void);\n\n const adapterRenderer = rendererFactory?.(panelEl);\n if (adapterRenderer) {\n render = adapterRenderer;\n } else {\n // Vanilla fallback: use innerHTML as static content\n const content = panelEl.innerHTML.trim();\n render = (container: HTMLElement) => {\n const wrapper = document.createElement('div');\n wrapper.innerHTML = content;\n container.appendChild(wrapper);\n return () => wrapper.remove();\n };\n }\n\n // Check if panel was already parsed\n const existingPanel = state.toolPanels.get(id);\n\n // If already parsed and we have an adapter renderer, update the render function\n // and re-read attributes from DOM (Angular may have updated them after initial parse)\n // This handles the case where Angular templates register after initial parsing\n if (existingPanel) {\n if (adapterRenderer) {\n // Update render function with framework adapter renderer\n existingPanel.render = render;\n\n // Re-read attributes from DOM - framework may have set them after initial parse\n // (e.g., Angular directive sets attributes in an effect after template is available)\n existingPanel.order = order;\n existingPanel.icon = icon;\n existingPanel.tooltip = tooltip;\n // Note: title and id are required and shouldn't change\n\n // Clear existing cleanup to force re-render with new renderer\n const cleanup = state.panelCleanups.get(id);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(id);\n }\n }\n return;\n }\n\n // Register the tool panel\n const panel: ToolPanelDefinition = {\n id,\n title,\n icon,\n tooltip,\n order,\n render,\n };\n\n state.toolPanels.set(id, panel);\n state.lightDomToolPanelIds.add(id);\n\n // Hide the light DOM element\n panelEl.style.display = 'none';\n });\n}\n\n/**\n * Set up event listeners for shell toolbar buttons and accordion.\n */\nexport function setupShellEventListeners(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n callbacks: {\n onPanelToggle: () => void;\n onSectionToggle: (sectionId: string) => void;\n onToolbarButtonClick: (buttonId: string) => void;\n },\n): void {\n const toolbar = shadowRoot.querySelector('.tbw-shell-toolbar');\n if (toolbar) {\n toolbar.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n\n // Handle single panel toggle button\n const panelToggle = target.closest('[data-panel-toggle]') as HTMLElement | null;\n if (panelToggle) {\n callbacks.onPanelToggle();\n return;\n }\n\n // Handle custom toolbar buttons\n const customBtn = target.closest('[data-btn]') as HTMLElement | null;\n if (customBtn) {\n const btnId = customBtn.getAttribute('data-btn');\n if (btnId) {\n callbacks.onToolbarButtonClick(btnId);\n }\n }\n });\n }\n\n // Accordion header clicks\n const accordion = shadowRoot.querySelector('.tbw-accordion');\n if (accordion) {\n accordion.addEventListener('click', (e) => {\n const target = e.target as HTMLElement;\n const header = target.closest('.tbw-accordion-header') as HTMLElement | null;\n if (header) {\n const section = header.closest('[data-section]') as HTMLElement | null;\n const sectionId = section?.getAttribute('data-section');\n if (sectionId) {\n callbacks.onSectionToggle(sectionId);\n }\n }\n });\n }\n}\n\n/**\n * Set up resize handle for tool panel.\n * Returns a cleanup function to remove event listeners.\n */\nexport function setupToolPanelResize(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n onResize: (width: number) => void,\n): () => void {\n const panel = shadowRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n const handle = shadowRoot.querySelector('[data-resize-handle]') as HTMLElement | null;\n const shellBody = shadowRoot.querySelector('.tbw-shell-body') as HTMLElement | null;\n if (!panel || !handle || !shellBody) {\n // eslint-disable-next-line @typescript-eslint/no-empty-function\n return () => {};\n }\n\n const position = config?.toolPanel?.position ?? 'right';\n const minWidth = 200;\n\n let startX = 0;\n let startWidth = 0;\n let maxWidth = 0;\n let isResizing = false;\n\n const onMouseMove = (e: MouseEvent) => {\n if (!isResizing) return;\n e.preventDefault();\n\n // For right-positioned panel: dragging left (negative clientX change) should expand\n // For left-positioned panel: dragging right (positive clientX change) should expand\n const delta = position === 'left' ? e.clientX - startX : startX - e.clientX;\n const newWidth = Math.min(maxWidth, Math.max(minWidth, startWidth + delta));\n\n panel.style.width = `${newWidth}px`;\n };\n\n const onMouseUp = () => {\n if (!isResizing) return;\n isResizing = false;\n handle.classList.remove('resizing');\n panel.style.transition = ''; // Re-enable transition\n document.body.style.cursor = '';\n document.body.style.userSelect = '';\n\n // Get final width and notify\n const finalWidth = panel.getBoundingClientRect().width;\n onResize(finalWidth);\n\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n\n const onMouseDown = (e: MouseEvent) => {\n e.preventDefault();\n isResizing = true;\n startX = e.clientX;\n startWidth = panel.getBoundingClientRect().width;\n // Calculate max width dynamically based on grid container width\n maxWidth = shellBody.getBoundingClientRect().width - 20; // Leave 20px margin\n handle.classList.add('resizing');\n panel.style.transition = 'none'; // Disable transition for smooth resize\n document.body.style.cursor = 'col-resize';\n document.body.style.userSelect = 'none';\n\n document.addEventListener('mousemove', onMouseMove);\n document.addEventListener('mouseup', onMouseUp);\n };\n\n handle.addEventListener('mousedown', onMouseDown);\n\n // Return cleanup function\n return () => {\n handle.removeEventListener('mousedown', onMouseDown);\n document.removeEventListener('mousemove', onMouseMove);\n document.removeEventListener('mouseup', onMouseUp);\n };\n}\n\n/**\n * Render custom button elements/render functions into toolbar slots.\n */\nexport function renderCustomToolbarButtons(\n shadowRoot: ShadowRoot,\n config: ShellConfig | undefined,\n state: ShellState,\n): void {\n const allButtons = [...(config?.header?.toolbarButtons ?? []), ...state.toolbarButtons.values()];\n\n for (const btn of allButtons) {\n const slot = shadowRoot.querySelector(`[data-btn-slot=\"${btn.id}\"]`);\n if (!slot) continue;\n\n // Clean up previous render if any\n const existingCleanup = state.toolbarButtonCleanups.get(btn.id);\n if (existingCleanup) {\n existingCleanup();\n state.toolbarButtonCleanups.delete(btn.id);\n }\n\n if (btn.element) {\n slot.appendChild(btn.element);\n } else if (btn.render) {\n const cleanup = btn.render(slot as HTMLElement);\n if (cleanup) {\n state.toolbarButtonCleanups.set(btn.id, cleanup);\n }\n }\n }\n}\n\n/**\n * Render header content from plugins into the shell content area.\n */\nexport function renderHeaderContent(shadowRoot: ShadowRoot, state: ShellState): void {\n const contentArea = shadowRoot.querySelector('.tbw-shell-content');\n if (!contentArea) return;\n\n // Sort by order\n const sortedContents = [...state.headerContents.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Create containers for each content piece (before the slot)\n const slot = contentArea.querySelector('slot[name=\"header-content\"]');\n\n for (const content of sortedContents) {\n // Clean up previous render if any\n const existingCleanup = state.headerContentCleanups.get(content.id);\n if (existingCleanup) {\n existingCleanup();\n state.headerContentCleanups.delete(content.id);\n }\n\n // Check if container already exists\n let container = contentArea.querySelector(`[data-header-content=\"${content.id}\"]`) as HTMLElement | null;\n if (!container) {\n container = document.createElement('div');\n container.setAttribute('data-header-content', content.id);\n // Insert before the slot\n if (slot) {\n contentArea.insertBefore(container, slot);\n } else {\n contentArea.appendChild(container);\n }\n }\n\n const cleanup = content.render(container);\n if (cleanup) {\n state.headerContentCleanups.set(content.id, cleanup);\n }\n }\n}\n\n/**\n * Render content for expanded accordion sections.\n * @param icons - Optional icons for expand/collapse chevrons (from grid config)\n */\nexport function renderPanelContent(\n shadowRoot: ShadowRoot,\n state: ShellState,\n icons?: { expand?: IconValue; collapse?: IconValue },\n): void {\n if (!state.isPanelOpen) return;\n\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n for (const [panelId, panel] of state.toolPanels) {\n const isExpanded = state.expandedSections.has(panelId);\n const section = shadowRoot.querySelector(`[data-section=\"${panelId}\"]`);\n const contentArea = section?.querySelector('.tbw-accordion-content') as HTMLElement | null;\n\n if (!section || !contentArea) continue;\n\n // Update expanded state\n section.classList.toggle('expanded', isExpanded);\n const header = section.querySelector('.tbw-accordion-header');\n if (header) {\n header.setAttribute('aria-expanded', String(isExpanded));\n }\n const chevron = section.querySelector('.tbw-accordion-chevron');\n if (chevron) {\n chevron.innerHTML = isExpanded ? collapseIcon : expandIcon;\n }\n\n if (isExpanded) {\n // Check if content is already rendered\n if (contentArea.children.length === 0) {\n // Render panel content\n const cleanup = panel.render(contentArea);\n if (cleanup) {\n state.panelCleanups.set(panelId, cleanup);\n }\n }\n } else {\n // Clean up and clear content when collapsed\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n contentArea.innerHTML = '';\n }\n }\n}\n\n/**\n * Update toolbar button active states.\n */\nexport function updateToolbarActiveStates(shadowRoot: ShadowRoot, state: ShellState): void {\n // Update single panel toggle button\n const panelToggle = shadowRoot.querySelector('[data-panel-toggle]');\n if (panelToggle) {\n panelToggle.classList.toggle('active', state.isPanelOpen);\n panelToggle.setAttribute('aria-pressed', String(state.isPanelOpen));\n }\n}\n\n/**\n * Update tool panel open/close state.\n */\nexport function updatePanelState(shadowRoot: ShadowRoot, state: ShellState): void {\n const panel = shadowRoot.querySelector('.tbw-tool-panel') as HTMLElement | null;\n if (!panel) return;\n\n panel.classList.toggle('open', state.isPanelOpen);\n\n // Clear inline width when closing (resize sets inline style that overrides CSS)\n if (!state.isPanelOpen) {\n panel.style.width = '';\n }\n}\n\n/**\n * Get all toolbar button info.\n */\nexport function getToolbarButtonsInfo(config: ShellConfig | undefined, state: ShellState): ToolbarButtonInfo[] {\n const result: ToolbarButtonInfo[] = [];\n\n // Config buttons\n for (const btn of config?.header?.toolbarButtons ?? []) {\n result.push({\n id: btn.id,\n label: btn.label,\n disabled: btn.disabled ?? false,\n source: 'config',\n });\n }\n\n // API buttons\n for (const btn of state.toolbarButtons.values()) {\n result.push({\n id: btn.id,\n label: btn.label,\n disabled: btn.disabled ?? false,\n source: 'config',\n });\n }\n\n // Light DOM buttons (limited info since user provides DOM)\n for (let i = 0; i < state.lightDomButtons.length; i++) {\n const el = state.lightDomButtons[i];\n const btn = el.querySelector('button');\n result.push({\n id: `light-dom-${i}`,\n label: btn?.getAttribute('title') ?? btn?.getAttribute('aria-label') ?? '',\n disabled: btn?.disabled ?? false,\n source: 'light-dom',\n });\n }\n\n // Panel toggles\n for (const panel of state.toolPanels.values()) {\n result.push({\n id: `panel-toggle-${panel.id}`,\n label: panel.tooltip ?? panel.title,\n disabled: false,\n source: 'panel-toggle',\n panelId: panel.id,\n });\n }\n\n return result;\n}\n\n/**\n * Cleanup all shell state when grid disconnects.\n */\nexport function cleanupShellState(state: ShellState): void {\n // Clean up header content\n for (const cleanup of state.headerContentCleanups.values()) {\n cleanup();\n }\n state.headerContentCleanups.clear();\n\n // Clean up active panel\n if (state.activePanelCleanup) {\n state.activePanelCleanup();\n state.activePanelCleanup = null;\n }\n\n // Clean up toolbar buttons\n for (const cleanup of state.toolbarButtonCleanups.values()) {\n cleanup();\n }\n state.toolbarButtonCleanups.clear();\n\n // Invoke panel onClose if open\n if (state.activePanel) {\n const panel = state.toolPanels.get(state.activePanel);\n panel?.onClose?.();\n }\n\n // Clear registrations\n state.toolPanels.clear();\n state.headerContents.clear();\n state.toolbarButtons.clear();\n state.lightDomButtons = [];\n state.lightDomHeaderContent = [];\n state.activePanel = null;\n}\n\n// ============================================================================\n// ShellController - Encapsulates tool panel orchestration logic\n// ============================================================================\n\n/**\n * Callbacks for ShellController to communicate with the grid.\n * This interface decouples the controller from grid internals.\n */\nexport interface ShellControllerCallbacks {\n /** Get the shadow root for DOM queries */\n getShadow: () => ShadowRoot;\n /** Get the current shell config */\n getShellConfig: () => ShellConfig | undefined;\n /** Get accordion expand/collapse icons */\n getAccordionIcons: () => { expand: IconValue; collapse: IconValue };\n /** Emit an event from the grid */\n emit: (eventName: string, detail: unknown) => void;\n /** Refresh the shell header (re-parse light DOM and re-render) */\n refreshShellHeader: () => void;\n}\n\n/**\n * Controller interface for managing shell/tool panel behavior.\n */\nexport interface ShellController {\n /** Whether the shell has been initialized */\n readonly isInitialized: boolean;\n /** Set the initialized state */\n setInitialized(value: boolean): void;\n /** Whether the tool panel is currently open */\n readonly isPanelOpen: boolean;\n /** Get the currently active panel ID (deprecated) */\n readonly activePanel: string | null;\n /** Get IDs of expanded accordion sections */\n readonly expandedSections: string[];\n /** Open the tool panel */\n openToolPanel(): void;\n /** Close the tool panel */\n closeToolPanel(): void;\n /** Toggle the tool panel */\n toggleToolPanel(): void;\n /** Toggle an accordion section */\n toggleToolPanelSection(sectionId: string): void;\n /** Get registered tool panels */\n getToolPanels(): ToolPanelDefinition[];\n /** Register a tool panel */\n registerToolPanel(panel: ToolPanelDefinition): void;\n /** Unregister a tool panel */\n unregisterToolPanel(panelId: string): void;\n /** Get registered header contents */\n getHeaderContents(): HeaderContentDefinition[];\n /** Register header content */\n registerHeaderContent(content: HeaderContentDefinition): void;\n /** Unregister header content */\n unregisterHeaderContent(contentId: string): void;\n /** Get all toolbar buttons info */\n getToolbarButtons(): ToolbarButtonInfo[];\n /** Register a toolbar button */\n registerToolbarButton(button: ToolbarButtonConfig): void;\n /** Unregister a toolbar button */\n unregisterToolbarButton(buttonId: string): void;\n /** Enable/disable a toolbar button */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): void;\n}\n\n/**\n * Create a ShellController instance.\n * The controller encapsulates all tool panel orchestration logic.\n */\nexport function createShellController(state: ShellState, callbacks: ShellControllerCallbacks): ShellController {\n let initialized = false;\n\n const controller: ShellController = {\n get isInitialized() {\n return initialized;\n },\n setInitialized(value: boolean) {\n initialized = value;\n },\n\n get isPanelOpen() {\n return state.isPanelOpen;\n },\n\n get activePanel() {\n // For backward compatibility, return first expanded section if panel is open\n if (state.isPanelOpen && state.expandedSections.size > 0) {\n return [...state.expandedSections][0];\n }\n return null;\n },\n\n get expandedSections() {\n return [...state.expandedSections];\n },\n\n openToolPanel() {\n if (state.isPanelOpen) return;\n if (state.toolPanels.size === 0) {\n console.warn('[tbw-grid] No tool panels registered');\n return;\n }\n\n state.isPanelOpen = true;\n\n // Auto-expand first section if none expanded\n if (state.expandedSections.size === 0 && state.toolPanels.size > 0) {\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const firstPanel = sortedPanels[0];\n if (firstPanel) {\n state.expandedSections.add(firstPanel.id);\n }\n }\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Render accordion sections\n renderPanelContent(shadow, state, callbacks.getAccordionIcons());\n\n // Emit event\n callbacks.emit('tool-panel-open', { sections: controller.expandedSections });\n },\n\n closeToolPanel() {\n if (!state.isPanelOpen) return;\n\n // Clean up all panel content\n for (const cleanup of state.panelCleanups.values()) {\n cleanup();\n }\n state.panelCleanups.clear();\n\n // Legacy cleanup\n if (state.activePanelCleanup) {\n state.activePanelCleanup();\n state.activePanelCleanup = null;\n }\n\n // Call onClose for all panels\n for (const panel of state.toolPanels.values()) {\n panel.onClose?.();\n }\n\n state.isPanelOpen = false;\n\n // Update UI\n const shadow = callbacks.getShadow();\n updateToolbarActiveStates(shadow, state);\n updatePanelState(shadow, state);\n\n // Emit event\n callbacks.emit('tool-panel-close', {});\n },\n\n toggleToolPanel() {\n if (state.isPanelOpen) {\n controller.closeToolPanel();\n } else {\n controller.openToolPanel();\n }\n },\n\n toggleToolPanelSection(sectionId: string) {\n const panel = state.toolPanels.get(sectionId);\n if (!panel) {\n console.warn(`[tbw-grid] Tool panel section \"${sectionId}\" not found`);\n return;\n }\n\n // Don't allow toggling when there's only one panel (it should stay expanded)\n if (state.toolPanels.size === 1) {\n return;\n }\n\n const shadow = callbacks.getShadow();\n const isExpanded = state.expandedSections.has(sectionId);\n\n if (isExpanded) {\n // Collapsing current section\n const cleanup = state.panelCleanups.get(sectionId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(sectionId);\n }\n panel.onClose?.();\n state.expandedSections.delete(sectionId);\n updateAccordionSectionState(shadow, sectionId, false);\n } else {\n // Expanding - first collapse all others (exclusive accordion)\n for (const [otherId, otherPanel] of state.toolPanels) {\n if (otherId !== sectionId && state.expandedSections.has(otherId)) {\n const cleanup = state.panelCleanups.get(otherId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(otherId);\n }\n otherPanel.onClose?.();\n state.expandedSections.delete(otherId);\n updateAccordionSectionState(shadow, otherId, false);\n // Clear content of collapsed section\n const contentEl = shadow.querySelector(`[data-section=\"${otherId}\"] .tbw-accordion-content`);\n if (contentEl) contentEl.innerHTML = '';\n }\n }\n // Now expand the target section\n state.expandedSections.add(sectionId);\n updateAccordionSectionState(shadow, sectionId, true);\n renderAccordionSectionContent(shadow, state, sectionId);\n }\n\n // Emit event\n callbacks.emit('tool-panel-section-toggle', { id: sectionId, expanded: !isExpanded });\n },\n\n getToolPanels() {\n return [...state.toolPanels.values()];\n },\n\n registerToolPanel(panel: ToolPanelDefinition) {\n if (state.toolPanels.has(panel.id)) {\n console.warn(`[tbw-grid] Tool panel \"${panel.id}\" already registered`);\n return;\n }\n state.toolPanels.set(panel.id, panel);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolPanel(panelId: string) {\n // Close panel if open and this section is expanded\n if (state.expandedSections.has(panelId)) {\n const cleanup = state.panelCleanups.get(panelId);\n if (cleanup) {\n cleanup();\n state.panelCleanups.delete(panelId);\n }\n state.expandedSections.delete(panelId);\n }\n\n state.toolPanels.delete(panelId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n getHeaderContents() {\n return [...state.headerContents.values()];\n },\n\n registerHeaderContent(content: HeaderContentDefinition) {\n if (state.headerContents.has(content.id)) {\n console.warn(`[tbw-grid] Header content \"${content.id}\" already registered`);\n return;\n }\n state.headerContents.set(content.id, content);\n\n if (initialized) {\n renderHeaderContent(callbacks.getShadow(), state);\n }\n },\n\n unregisterHeaderContent(contentId: string) {\n // Clean up\n const cleanup = state.headerContentCleanups.get(contentId);\n if (cleanup) {\n cleanup();\n state.headerContentCleanups.delete(contentId);\n }\n\n // Call onDestroy\n const content = state.headerContents.get(contentId);\n content?.onDestroy?.();\n\n state.headerContents.delete(contentId);\n\n // Remove DOM element\n const el = callbacks.getShadow().querySelector(`[data-header-content=\"${contentId}\"]`);\n el?.remove();\n },\n\n getToolbarButtons() {\n return getToolbarButtonsInfo(callbacks.getShellConfig(), state);\n },\n\n registerToolbarButton(button: ToolbarButtonConfig) {\n if (state.toolbarButtons.has(button.id)) {\n console.warn(`[tbw-grid] Toolbar button \"${button.id}\" already registered`);\n return;\n }\n state.toolbarButtons.set(button.id, button);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n unregisterToolbarButton(buttonId: string) {\n // Clean up\n const cleanup = state.toolbarButtonCleanups.get(buttonId);\n if (cleanup) {\n cleanup();\n state.toolbarButtonCleanups.delete(buttonId);\n }\n\n state.toolbarButtons.delete(buttonId);\n\n if (initialized) {\n callbacks.refreshShellHeader();\n }\n },\n\n setToolbarButtonDisabled(buttonId: string, disabled: boolean) {\n // Check API-registered buttons\n const apiBtn = state.toolbarButtons.get(buttonId);\n if (apiBtn) {\n apiBtn.disabled = disabled;\n }\n\n // Update DOM\n const btn = callbacks.getShadow().querySelector(`[data-btn=\"${buttonId}\"]`) as HTMLButtonElement | null;\n if (btn) {\n btn.disabled = disabled;\n }\n },\n };\n\n return controller;\n}\n\n/**\n * Update accordion section visual state.\n */\nfunction updateAccordionSectionState(shadow: ShadowRoot, sectionId: string, expanded: boolean): void {\n const section = shadow.querySelector(`[data-section=\"${sectionId}\"]`);\n if (section) {\n section.classList.toggle('expanded', expanded);\n }\n}\n\n/**\n * Render content for a single accordion section.\n */\nfunction renderAccordionSectionContent(shadow: ShadowRoot, state: ShellState, sectionId: string): void {\n const panel = state.toolPanels.get(sectionId);\n if (!panel?.render) return;\n\n const contentEl = shadow.querySelector(`[data-section=\"${sectionId}\"] .tbw-accordion-content`);\n if (!contentEl) return;\n\n const cleanup = panel.render(contentEl as HTMLElement);\n if (cleanup) {\n state.panelCleanups.set(sectionId, cleanup);\n }\n}\n\n// ============================================================================\n// Grid Render HTML\n// ============================================================================\n\n/**\n * Core grid content HTML template.\n * Uses faux scrollbar pattern for smooth virtualized scrolling.\n */\nexport const GRID_CONTENT_HTML = `\n <div class=\"tbw-scroll-area\">\n <div class=\"rows-body-wrapper\">\n <div class=\"rows-body\" role=\"grid\">\n <div class=\"header\">\n <div class=\"header-row\" part=\"header-row\"></div>\n </div>\n <div class=\"rows-container\">\n <div class=\"rows-viewport\">\n <div class=\"rows\"></div>\n </div>\n </div>\n </div>\n </div>\n </div>\n <div class=\"faux-vscroll\">\n <div class=\"faux-vscroll-spacer\"></div>\n </div>\n`;\n\n/**\n * Render the complete grid HTML structure.\n * @returns HTML string to set as shadow root innerHTML\n * @deprecated Use buildGridDOM() for better performance\n */\nexport function renderGridHtml(\n shellConfig: ShellConfig | undefined,\n state: ShellState,\n icons?: { toolPanel?: IconValue; expand?: IconValue; collapse?: IconValue },\n): string {\n const hasShell = shouldRenderShellHeader(shellConfig, state);\n\n if (hasShell) {\n const toolPanelIcon = icons?.toolPanel ?? DEFAULT_GRID_ICONS.toolPanel;\n const accordionIcons = {\n expand: icons?.expand ?? DEFAULT_GRID_ICONS.expand,\n collapse: icons?.collapse ?? DEFAULT_GRID_ICONS.collapse,\n };\n const shellHeaderHtml = renderShellHeader(shellConfig, state, toolPanelIcon);\n const shellBodyHtml = renderShellBody(shellConfig, state, GRID_CONTENT_HTML, accordionIcons);\n\n return `\n <div class=\"tbw-grid-root has-shell\">\n ${shellHeaderHtml}\n ${shellBodyHtml}\n </div>\n `;\n }\n\n return `\n <div class=\"tbw-grid-root\">\n <div class=\"tbw-grid-content\">\n ${GRID_CONTENT_HTML}\n </div>\n </div>\n `;\n}\n\n// ============================================================================\n// DOM-based Grid Rendering (Performance Optimized)\n// ============================================================================\n\nimport {\n buildGridDOM,\n buildShellBody,\n buildShellHeader,\n type ShellBodyOptions,\n type ShellHeaderOptions,\n} from './dom-builder';\n\n/**\n * Build the complete grid DOM structure using direct DOM construction.\n * This is 2-3x faster than innerHTML for initial render.\n *\n * @param shadowRoot - The shadow root to render into (will be cleared)\n * @param shellConfig - Shell configuration\n * @param state - Shell state\n * @param icons - Optional icons\n * @returns Whether shell is active (for post-render setup)\n */\nexport function buildGridDOMIntoShadow(\n shadowRoot: ShadowRoot,\n shellConfig: ShellConfig | undefined,\n state: ShellState,\n icons?: { toolPanel?: IconValue; expand?: IconValue; collapse?: IconValue },\n): boolean {\n const hasShell = shouldRenderShellHeader(shellConfig, state);\n\n // Clear existing content\n shadowRoot.replaceChildren();\n\n if (hasShell) {\n const toolPanelIcon = iconToString(icons?.toolPanel ?? DEFAULT_GRID_ICONS.toolPanel);\n const expandIcon = iconToString(icons?.expand ?? DEFAULT_GRID_ICONS.expand);\n const collapseIcon = iconToString(icons?.collapse ?? DEFAULT_GRID_ICONS.collapse);\n\n // Prepare button arrays\n const configButtons = shellConfig?.header?.toolbarButtons ?? [];\n const sortedConfigButtons = [...configButtons].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const sortedApiButtons = [...state.toolbarButtons.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n\n // Build header options\n const headerOptions: ShellHeaderOptions = {\n title: shellConfig?.header?.title ?? state.lightDomTitle ?? undefined,\n hasLightDomButtons: state.lightDomButtons.length > 0,\n hasPanels: state.toolPanels.size > 0,\n isPanelOpen: state.isPanelOpen,\n toolPanelIcon,\n configButtons: sortedConfigButtons.map((b) => ({\n id: b.id,\n label: b.label,\n icon: iconToString(b.icon),\n disabled: b.disabled,\n hasElement: !!b.element,\n hasRender: !!b.render,\n action: b.action,\n })),\n apiButtons: sortedApiButtons.map((b) => ({\n id: b.id,\n label: b.label,\n icon: iconToString(b.icon),\n disabled: b.disabled,\n hasElement: !!b.element,\n hasRender: !!b.render,\n action: b.action,\n })),\n };\n\n // Build body options\n const sortedPanels = [...state.toolPanels.values()].sort((a, b) => (a.order ?? 100) - (b.order ?? 100));\n const bodyOptions: ShellBodyOptions = {\n position: shellConfig?.toolPanel?.position ?? 'right',\n isPanelOpen: state.isPanelOpen,\n expandIcon,\n collapseIcon,\n panels: sortedPanels.map((p) => ({\n id: p.id,\n title: p.title,\n icon: iconToString(p.icon),\n isExpanded: state.expandedSections.has(p.id),\n })),\n };\n\n // Build shell elements\n const shellHeader = buildShellHeader(headerOptions);\n const shellBody = buildShellBody(bodyOptions);\n\n // Build and append complete DOM\n const fragment = buildGridDOM({\n hasShell: true,\n shellHeader,\n shellBody,\n });\n shadowRoot.appendChild(fragment);\n } else {\n // No shell - just grid content\n const fragment = buildGridDOM({ hasShell: false });\n shadowRoot.appendChild(fragment);\n }\n\n return hasShell;\n}\n","/**\n * Touch scrolling controller for mobile devices.\n *\n * Handles touch events for scrolling with momentum physics.\n * Supports both vertical (faux scrollbar) and horizontal (scroll area) scrolling.\n */\n\nexport interface TouchScrollState {\n startY: number | null;\n startX: number | null;\n scrollTop: number | null;\n scrollLeft: number | null;\n lastY: number | null;\n lastX: number | null;\n lastTime: number | null;\n velocityY: number;\n velocityX: number;\n momentumRaf: number;\n}\n\nexport interface TouchScrollElements {\n fauxScrollbar: HTMLElement;\n scrollArea: HTMLElement | null;\n}\n\n/**\n * Create initial touch scroll state.\n */\nexport function createTouchScrollState(): TouchScrollState {\n return {\n startY: null,\n startX: null,\n scrollTop: null,\n scrollLeft: null,\n lastY: null,\n lastX: null,\n lastTime: null,\n velocityY: 0,\n velocityX: 0,\n momentumRaf: 0,\n };\n}\n\n/**\n * Reset touch scroll state (called on touchend or cleanup).\n */\nexport function resetTouchState(state: TouchScrollState): void {\n state.startY = null;\n state.startX = null;\n state.scrollTop = null;\n state.scrollLeft = null;\n state.lastY = null;\n state.lastX = null;\n state.lastTime = null;\n}\n\n/**\n * Cancel any ongoing momentum animation.\n */\nexport function cancelMomentum(state: TouchScrollState): void {\n if (state.momentumRaf) {\n cancelAnimationFrame(state.momentumRaf);\n state.momentumRaf = 0;\n }\n}\n\n/**\n * Handle touchstart event.\n */\nexport function handleTouchStart(e: TouchEvent, state: TouchScrollState, elements: TouchScrollElements): void {\n if (e.touches.length !== 1) return;\n\n // Cancel any ongoing momentum animation\n cancelMomentum(state);\n\n const touch = e.touches[0];\n state.startY = touch.clientY;\n state.startX = touch.clientX;\n state.lastY = touch.clientY;\n state.lastX = touch.clientX;\n state.lastTime = performance.now();\n state.scrollTop = elements.fauxScrollbar.scrollTop;\n state.scrollLeft = elements.scrollArea?.scrollLeft ?? 0;\n state.velocityY = 0;\n state.velocityX = 0;\n}\n\n/**\n * Handle touchmove event.\n * Returns true if the event should be prevented (grid scrolled).\n */\nexport function handleTouchMove(e: TouchEvent, state: TouchScrollState, elements: TouchScrollElements): boolean {\n if (\n e.touches.length !== 1 ||\n state.startY === null ||\n state.startX === null ||\n state.scrollTop === null ||\n state.scrollLeft === null\n ) {\n return false;\n }\n\n const touch = e.touches[0];\n const currentY = touch.clientY;\n const currentX = touch.clientX;\n const now = performance.now();\n\n const deltaY = state.startY - currentY;\n const deltaX = state.startX - currentX;\n\n // Calculate velocity for momentum scrolling\n if (state.lastTime !== null && state.lastY !== null && state.lastX !== null) {\n const dt = now - state.lastTime;\n if (dt > 0) {\n // Velocity in pixels per millisecond\n state.velocityY = (state.lastY - currentY) / dt;\n state.velocityX = (state.lastX - currentX) / dt;\n }\n }\n state.lastY = currentY;\n state.lastX = currentX;\n state.lastTime = now;\n\n // Check if grid can scroll in the requested directions\n const { scrollTop, scrollHeight, clientHeight } = elements.fauxScrollbar;\n const maxScrollY = scrollHeight - clientHeight;\n const canScrollVertically = (deltaY > 0 && scrollTop < maxScrollY) || (deltaY < 0 && scrollTop > 0);\n\n let canScrollHorizontally = false;\n if (elements.scrollArea) {\n const { scrollLeft, scrollWidth, clientWidth } = elements.scrollArea;\n const maxScrollX = scrollWidth - clientWidth;\n canScrollHorizontally = (deltaX > 0 && scrollLeft < maxScrollX) || (deltaX < 0 && scrollLeft > 0);\n }\n\n // Apply scroll if grid can scroll in that direction\n if (canScrollVertically) {\n elements.fauxScrollbar.scrollTop = state.scrollTop + deltaY;\n }\n if (canScrollHorizontally && elements.scrollArea) {\n elements.scrollArea.scrollLeft = state.scrollLeft + deltaX;\n }\n\n // Return true to prevent page scroll when we scrolled the grid\n return canScrollVertically || canScrollHorizontally;\n}\n\n/**\n * Handle touchend event.\n * Starts momentum scrolling if velocity is significant.\n */\nexport function handleTouchEnd(state: TouchScrollState, elements: TouchScrollElements): void {\n const minVelocity = 0.1; // pixels per ms threshold\n\n // Start momentum scrolling if there's significant velocity\n if (Math.abs(state.velocityY) > minVelocity || Math.abs(state.velocityX) > minVelocity) {\n startMomentumScroll(state, elements);\n }\n\n resetTouchState(state);\n}\n\n/**\n * Start momentum scrolling animation.\n */\nfunction startMomentumScroll(state: TouchScrollState, elements: TouchScrollElements): void {\n const friction = 0.95; // Deceleration factor per frame\n const minVelocity = 0.01; // Stop threshold in px/ms\n\n const animate = () => {\n // Apply friction\n state.velocityY *= friction;\n state.velocityX *= friction;\n\n // Convert velocity (px/ms) to per-frame scroll amount (~16ms per frame)\n const scrollY = state.velocityY * 16;\n const scrollX = state.velocityX * 16;\n\n // Apply scroll if above threshold\n if (Math.abs(state.velocityY) > minVelocity) {\n elements.fauxScrollbar.scrollTop += scrollY;\n }\n if (Math.abs(state.velocityX) > minVelocity && elements.scrollArea) {\n elements.scrollArea.scrollLeft += scrollX;\n }\n\n // Continue animation if still moving\n if (Math.abs(state.velocityY) > minVelocity || Math.abs(state.velocityX) > minVelocity) {\n state.momentumRaf = requestAnimationFrame(animate);\n } else {\n state.momentumRaf = 0;\n }\n };\n\n state.momentumRaf = requestAnimationFrame(animate);\n}\n\n/**\n * Set up touch scroll event listeners on the grid content element.\n * Returns a cleanup function that removes all listeners.\n */\nexport function setupTouchScrollListeners(\n gridContentEl: HTMLElement,\n state: TouchScrollState,\n elements: TouchScrollElements,\n signal: AbortSignal,\n): void {\n gridContentEl.addEventListener('touchstart', (e: TouchEvent) => handleTouchStart(e, state, elements), {\n passive: true,\n signal,\n });\n\n gridContentEl.addEventListener(\n 'touchmove',\n (e: TouchEvent) => {\n const shouldPrevent = handleTouchMove(e, state, elements);\n if (shouldPrevent) {\n e.preventDefault();\n }\n },\n { passive: false, signal },\n );\n\n gridContentEl.addEventListener('touchend', () => handleTouchEnd(state, elements), { passive: true, signal });\n}\n","/**\n * Plugin Manager\n *\n * Manages plugin instances for a single grid.\n * Each grid has its own PluginManager with its own set of plugin instances.\n */\n\nimport type { ColumnConfig } from '../types';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n HeaderClickEvent,\n HeaderRenderer,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './base-plugin';\n\n/**\n * Manages plugins for a single grid instance.\n */\nexport class PluginManager {\n /** Plugin instances in order of attachment */\n private plugins: BaseGridPlugin[] = [];\n\n /** Map from plugin class to instance for fast lookup */\n private pluginMap: Map<new (...args: unknown[]) => BaseGridPlugin, BaseGridPlugin> = new Map();\n\n /** Cell renderers registered by plugins */\n private cellRenderers: Map<string, CellRenderer> = new Map();\n\n /** Header renderers registered by plugins */\n private headerRenderers: Map<string, HeaderRenderer> = new Map();\n\n /** Cell editors registered by plugins */\n private cellEditors: Map<string, CellEditor> = new Map();\n\n constructor(private grid: any) {}\n\n /**\n * Attach all plugins from the config.\n */\n attachAll(plugins: BaseGridPlugin[]): void {\n for (const plugin of plugins) {\n this.attach(plugin);\n }\n }\n\n /**\n * Attach a plugin to this grid.\n */\n attach(plugin: BaseGridPlugin): void {\n // Store by constructor for type-safe lookup\n this.pluginMap.set(plugin.constructor as new (...args: unknown[]) => BaseGridPlugin, plugin);\n this.plugins.push(plugin);\n\n // Register renderers/editors\n if (plugin.cellRenderers) {\n for (const [type, renderer] of Object.entries(plugin.cellRenderers)) {\n this.cellRenderers.set(type, renderer);\n }\n }\n if (plugin.headerRenderers) {\n for (const [type, renderer] of Object.entries(plugin.headerRenderers)) {\n this.headerRenderers.set(type, renderer);\n }\n }\n if (plugin.cellEditors) {\n for (const [type, editor] of Object.entries(plugin.cellEditors)) {\n this.cellEditors.set(type, editor);\n }\n }\n\n // Call attach lifecycle method\n plugin.attach(this.grid);\n }\n\n /**\n * Detach all plugins and clean up.\n */\n detachAll(): void {\n // Detach in reverse order\n for (let i = this.plugins.length - 1; i >= 0; i--) {\n this.plugins[i].detach();\n }\n this.plugins = [];\n this.pluginMap.clear();\n this.cellRenderers.clear();\n this.headerRenderers.clear();\n this.cellEditors.clear();\n }\n\n /**\n * Get a plugin instance by its class.\n */\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.pluginMap.get(PluginClass) as T | undefined;\n }\n\n /**\n * Get a plugin instance by its name.\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.plugins.find((p) => p.name === name);\n }\n\n /**\n * Check if a plugin is attached.\n */\n hasPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): boolean {\n return this.pluginMap.has(PluginClass);\n }\n\n /**\n * Get all attached plugins.\n */\n getAll(): readonly BaseGridPlugin[] {\n return this.plugins;\n }\n\n /**\n * Get a cell renderer by type name.\n */\n getCellRenderer(type: string): CellRenderer | undefined {\n return this.cellRenderers.get(type);\n }\n\n /**\n * Get a header renderer by type name.\n */\n getHeaderRenderer(type: string): HeaderRenderer | undefined {\n return this.headerRenderers.get(type);\n }\n\n /**\n * Get a cell editor by type name.\n */\n getCellEditor(type: string): CellEditor | undefined {\n return this.cellEditors.get(type);\n }\n\n /**\n * Get all CSS styles from all plugins.\n */\n getAllStyles(): string {\n return this.plugins\n .filter((p) => p.styles)\n .map((p) => p.styles)\n .join('\\n');\n }\n\n // #region Hook execution methods\n\n /**\n * Execute processRows hook on all plugins.\n */\n processRows(rows: readonly any[]): any[] {\n let result = [...rows];\n for (const plugin of this.plugins) {\n if (plugin.processRows) {\n result = plugin.processRows(result);\n }\n }\n return result;\n }\n\n /**\n * Execute processColumns hook on all plugins.\n */\n processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n let result = [...columns];\n for (const plugin of this.plugins) {\n if (plugin.processColumns) {\n result = plugin.processColumns(result);\n }\n }\n return result;\n }\n\n /**\n * Execute beforeRender hook on all plugins.\n */\n beforeRender(): void {\n for (const plugin of this.plugins) {\n plugin.beforeRender?.();\n }\n }\n\n /**\n * Execute afterRender hook on all plugins.\n */\n afterRender(): void {\n for (const plugin of this.plugins) {\n plugin.afterRender?.();\n }\n }\n\n /**\n * Execute onScrollRender hook on all plugins.\n * Called after scroll-triggered row rendering for lightweight visual state updates.\n */\n onScrollRender(): void {\n for (const plugin of this.plugins) {\n plugin.onScrollRender?.();\n }\n }\n\n /**\n * Get total extra height contributed by plugins (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations.\n */\n getExtraHeight(): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeight === 'function') {\n total += plugin.getExtraHeight();\n }\n }\n return total;\n }\n\n /**\n * Get extra height from plugins that appears before a given row index.\n * Used by virtualization to correctly position the scroll window.\n */\n getExtraHeightBefore(beforeRowIndex: number): number {\n let total = 0;\n for (const plugin of this.plugins) {\n if (typeof plugin.getExtraHeightBefore === 'function') {\n total += plugin.getExtraHeightBefore(beforeRowIndex);\n }\n }\n return total;\n }\n\n /**\n * Adjust the virtualization start index based on plugin needs.\n * Returns the minimum start index from all plugins.\n */\n adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n let adjustedStart = start;\n for (const plugin of this.plugins) {\n if (typeof plugin.adjustVirtualStart === 'function') {\n const pluginStart = plugin.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginStart < adjustedStart) {\n adjustedStart = pluginStart;\n }\n }\n }\n return adjustedStart;\n }\n\n /**\n * Execute renderRow hook on all plugins.\n * Returns true if any plugin handled the row.\n */\n renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean {\n for (const plugin of this.plugins) {\n if (plugin.renderRow?.(row, rowEl, rowIndex)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n *\n * Common query types are defined in PLUGIN_QUERIES, but plugins can define their own.\n *\n * @param query - The query object containing type and context\n * @returns Array of non-undefined responses from plugins\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n const responses: T[] = [];\n for (const plugin of this.plugins) {\n const response = plugin.onPluginQuery?.(query);\n if (response !== undefined) {\n responses.push(response as T);\n }\n }\n return responses;\n }\n\n /**\n * Execute onKeyDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onKeyDown(event: KeyboardEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onKeyDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellClick(event: CellClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onRowClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onRowClick(event: RowClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onRowClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onHeaderClick hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onHeaderClick(event: HeaderClickEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onHeaderClick?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onScroll hook on all plugins.\n */\n onScroll(event: ScrollEvent): void {\n for (const plugin of this.plugins) {\n plugin.onScroll?.(event);\n }\n }\n\n /**\n * Execute onCellMouseDown hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseDown(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseDown?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseMove hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseMove(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseMove?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Execute onCellMouseUp hook on all plugins.\n * Returns true if any plugin handled the event.\n */\n onCellMouseUp(event: CellMouseEvent): boolean {\n for (const plugin of this.plugins) {\n if (plugin.onCellMouseUp?.(event)) {\n return true;\n }\n }\n return false;\n }\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Collect horizontal scroll boundary offsets from all plugins.\n * Combines offsets from all plugins that report them.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Combined left and right pixel offsets, plus skipScroll if any plugin requests it\n */\n getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n let left = 0;\n let right = 0;\n let skipScroll = false;\n for (const plugin of this.plugins) {\n const offsets = plugin.getHorizontalScrollOffsets?.(rowEl, focusedCell);\n if (offsets) {\n left += offsets.left;\n right += offsets.right;\n if (offsets.skipScroll) {\n skipScroll = true;\n }\n }\n }\n return { left, right, skipScroll };\n }\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Collect tool panels from all plugins.\n * Returns panels sorted by order (ascending).\n */\n getToolPanels(): {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] {\n const panels: {\n plugin: BaseGridPlugin;\n panel: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getToolPanel']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const panel = plugin.getToolPanel?.();\n if (panel) {\n panels.push({ plugin, panel });\n }\n }\n // Sort by order (ascending), default to 0\n return panels.sort((a, b) => (a.panel.order ?? 0) - (b.panel.order ?? 0));\n }\n\n /**\n * Collect header contents from all plugins.\n * Returns contents sorted by order (ascending).\n */\n getHeaderContents(): {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] {\n const contents: {\n plugin: BaseGridPlugin;\n content: NonNullable<ReturnType<NonNullable<BaseGridPlugin['getHeaderContent']>>>;\n }[] = [];\n for (const plugin of this.plugins) {\n const content = plugin.getHeaderContent?.();\n if (content) {\n contents.push({ plugin, content });\n }\n }\n // Sort by order (ascending), default to 0\n return contents.sort((a, b) => (a.content.order ?? 0) - (b.content.order ?? 0));\n }\n // #endregion\n}\n","import styles from './grid.css?inline';\nimport {\n applyColumnState,\n collectColumnState,\n createStateChangeHandler,\n getAllColumns,\n getColumnOrder,\n isColumnVisible,\n setColumnOrder,\n setColumnVisible,\n showAllColumns,\n toggleColumnVisibility,\n type VisibilityCallbacks,\n} from './internal/column-state';\nimport { autoSizeColumns, getColumnConfiguration, updateTemplate } from './internal/columns';\nimport {\n beginBulkEdit,\n cancelActiveRowEdit,\n commitActiveRowEdit,\n exitRowEdit,\n getChangedRowIndices,\n getChangedRows,\n resetChangedRows,\n} from './internal/editing';\nimport { setupCellEventDelegation } from './internal/event-delegation';\nimport { renderHeader } from './internal/header';\nimport { cancelIdle, scheduleIdle } from './internal/idle-scheduler';\nimport { inferColumns } from './internal/inference';\nimport { ensureCellVisible, handleGridKeyDown } from './internal/keyboard';\nimport { createResizeController } from './internal/resize';\nimport { invalidateCellCache, renderVisibleRows } from './internal/rows';\nimport {\n buildGridDOMIntoShadow,\n cleanupShellState,\n createShellController,\n createShellState,\n parseLightDomShell,\n parseLightDomToolPanels,\n renderCustomToolbarButtons,\n renderHeaderContent,\n renderShellHeader,\n setupShellEventListeners,\n setupToolPanelResize,\n shouldRenderShellHeader,\n type ShellController,\n type ShellState,\n type ToolPanelRendererFactory,\n} from './internal/shell';\nimport {\n cancelMomentum,\n createTouchScrollState,\n setupTouchScrollListeners,\n type TouchScrollState,\n} from './internal/touch-scroll';\nimport type { CellMouseEvent, ScrollEvent } from './plugin';\nimport type {\n BaseGridPlugin,\n CellClickEvent,\n HeaderClickEvent,\n PluginQuery,\n RowClickEvent,\n} from './plugin/base-plugin';\nimport { PluginManager } from './plugin/plugin-manager';\nimport type {\n ActivateCellDetail,\n AnimationConfig,\n CellCommitDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnInternal,\n ColumnResizeDetail,\n FitMode,\n FrameworkAdapter,\n GridColumnState,\n GridConfig,\n HeaderContentDefinition,\n InternalGrid,\n ResizeController,\n RowCommitDetail,\n SortChangeDetail,\n ToolbarButtonConfig,\n ToolbarButtonInfo,\n ToolPanelDefinition,\n VirtualState,\n} from './types';\nimport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS } from './types';\n\n/**\n * High-performance data grid web component.\n *\n * ## Configuration Architecture\n *\n * The grid follows a **single source of truth** pattern where all configuration\n * converges into `#effectiveConfig`. Users can set configuration via multiple inputs:\n *\n * **Input Sources (precedence low → high):**\n * 1. `gridConfig` property - base configuration object\n * 2. Light DOM elements:\n * - `<tbw-grid-column>` → `effectiveConfig.columns`\n * - `<tbw-grid-header title=\"...\">` → `effectiveConfig.shell.header.title`\n * - `<tbw-grid-header-content>` → `effectiveConfig.shell.header.content`\n * 3. `columns` property → merged into `effectiveConfig.columns`\n * 4. `fitMode` property → merged into `effectiveConfig.fitMode`\n * 5. `editOn` property → merged into `effectiveConfig.editOn`\n * 6. Column inference from first row (if no columns defined)\n *\n * **Derived State:**\n * - `_columns` - processed columns from `effectiveConfig.columns` after plugin hooks\n * - `_rows` - processed rows after plugin hooks (grouping, filtering, etc.)\n *\n * The `#mergeEffectiveConfig()` method is the single place where all inputs converge.\n * All rendering and logic should read from `effectiveConfig` or derived state.\n *\n * @element tbw-grid\n *\n * @csspart container - The main grid container\n * @csspart header - The header row container\n * @csspart body - The body/rows container\n *\n * @fires cell-commit - Fired when a cell value is committed\n * @fires row-commit - Fired when a bulk row edit session commits\n * @fires changed-rows-reset - Fired after resetChangedRows() unless silent\n * @fires mount-external-view - Fired to request mounting of an external view renderer\n * @fires mount-external-editor - Fired to request mounting of an external editor renderer\n * @fires sort-change - Fired when sort state changes for a column\n * @fires column-resize - Fired after a column resize drag completes\n * @fires activate-cell - Fired when a cell activation intent occurs\n * @fires group-toggle - Fired when a group row is toggled\n *\n * @cssprop --tbw-color-bg - Background color\n * @cssprop --tbw-color-fg - Foreground/text color\n */\n// Injected by Vite at build time from package.json\ndeclare const __GRID_VERSION__: string;\n\nexport class DataGridElement<T = any> extends HTMLElement implements InternalGrid<T> {\n // TODO: Rename to 'data-grid' when migration is complete\n static readonly tagName = 'tbw-grid';\n static readonly version = typeof __GRID_VERSION__ !== 'undefined' ? __GRID_VERSION__ : 'dev';\n\n // ---------------- Framework Adapters ----------------\n /**\n * Registry of framework adapters that handle converting light DOM elements\n * to functional renderers/editors. Framework libraries (Angular, React, Vue)\n * register adapters to enable zero-boilerplate component integration.\n */\n private static adapters: FrameworkAdapter[] = [];\n\n /**\n * Register a framework adapter for handling framework-specific components.\n * Adapters are checked in registration order when processing light DOM templates.\n *\n * @example\n * ```typescript\n * // In @toolbox-web/grid-angular\n * import { AngularGridAdapter } from '@toolbox-web/grid-angular';\n *\n * // One-time setup in app\n * GridElement.registerAdapter(new AngularGridAdapter(injector, appRef));\n * ```\n */\n static registerAdapter(adapter: FrameworkAdapter): void {\n this.adapters.push(adapter);\n }\n\n /**\n * Get all registered framework adapters.\n * Used internally by light DOM parsing to find adapters that can handle templates.\n */\n static getAdapters(): readonly FrameworkAdapter[] {\n return this.adapters;\n }\n\n /**\n * Clear all registered adapters (primarily for testing).\n */\n static clearAdapters(): void {\n this.adapters = [];\n }\n\n // ---------------- Observed Attributes ----------------\n static get observedAttributes(): string[] {\n return ['rows', 'columns', 'grid-config', 'fit-mode', 'edit-on'];\n }\n\n readonly #shadow: ShadowRoot;\n #initialized = false;\n\n // ---------------- Ready Promise ----------------\n #readyPromise: Promise<void>;\n #readyResolve?: () => void;\n\n // #region Input Properties\n // These backing fields store raw user input. They are merged into\n // #effectiveConfig by #mergeEffectiveConfig(). Never read directly\n // for rendering logic - always use effectiveConfig or derived state.\n #rows: T[] = [];\n #columns?: ColumnConfig<T>[] | ColumnConfigMap<T>;\n #gridConfig?: GridConfig<T>;\n #fitMode?: FitMode;\n #editOn?: string | boolean;\n // #endregion\n\n // #region Private properties\n // All input sources converge here. This is the canonical config\n // that all rendering and logic should read from.\n #effectiveConfig: GridConfig<T> = {};\n #connected = false;\n\n // ---------------- Batched Updates ----------------\n // When multiple properties are set in rapid succession (within same microtask),\n // we batch them into a single update to avoid redundant re-renders.\n #pendingUpdate = false;\n #pendingUpdateFlags = {\n rows: false,\n columns: false,\n gridConfig: false,\n fitMode: false,\n editMode: false,\n };\n\n #scrollRaf = 0;\n #pendingScrollTop: number | null = null;\n #hasScrollPlugins = false; // Cached flag for plugin scroll handlers\n #renderRowHook?: (row: any, rowEl: HTMLElement, rowIndex: number) => boolean; // Cached hook to avoid closures\n #isDragging = false;\n #touchState: TouchScrollState = createTouchScrollState();\n #eventAbortController?: AbortController;\n #resizeObserver?: ResizeObserver;\n #rowHeightObserver?: ResizeObserver; // Watches first row for size changes (CSS loading, custom renderers)\n #lightDomObserver?: MutationObserver;\n #idleCallbackHandle?: number; // Handle for cancelling deferred idle work\n\n // Pooled scroll event object (reused to avoid GC pressure during scroll)\n #pooledScrollEvent: ScrollEvent = {\n scrollTop: 0,\n scrollLeft: 0,\n scrollHeight: 0,\n scrollWidth: 0,\n clientHeight: 0,\n clientWidth: 0,\n };\n\n // ---------------- Plugin System ----------------\n #pluginManager!: PluginManager;\n\n // ---------------- Event Listeners ----------------\n #eventListenersAdded = false; // Guard against adding duplicate component-level listeners\n #scrollAbortController?: AbortController; // Separate controller for DOM scroll listeners (recreated on DOM changes)\n\n // ---------------- Column State ----------------\n #stateChangeHandler?: () => void;\n #initialColumnState?: GridColumnState;\n\n // ---------------- Shell State ----------------\n #shellState: ShellState = createShellState();\n #shellController!: ShellController;\n #resizeCleanup?: () => void;\n // #endregion\n\n // #region Derived State\n // _rows: result of applying plugin processRows hooks\n _rows: T[] = [];\n\n // _baseColumns: columns before plugin transformation (analogous to #rows for row processing)\n // This is the source of truth for processColumns - plugins transform these\n #baseColumns: ColumnInternal<T>[] = [];\n\n // _columns is a getter/setter that operates on effectiveConfig.columns\n // This ensures effectiveConfig.columns is the single source of truth for columns\n // _columns always contains ALL columns (including hidden)\n get _columns(): ColumnInternal<T>[] {\n return (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n }\n set _columns(value: ColumnInternal<T>[]) {\n this.#effectiveConfig.columns = value as ColumnConfig<T>[];\n }\n\n // visibleColumns returns only visible columns for rendering\n // This is what header/row rendering should use\n get _visibleColumns(): ColumnInternal<T>[] {\n return this._columns.filter((c) => !c.hidden);\n }\n // #endregion\n\n // #region Runtime State (Plugin-accessible)\n // DOM references\n _headerRowEl!: HTMLElement;\n _bodyEl!: HTMLElement;\n _rowPool: HTMLElement[] = [];\n _resizeController!: ResizeController;\n\n // Virtualization & scroll state\n _virtualization: VirtualState = {\n enabled: true,\n rowHeight: 28,\n bypassThreshold: 24,\n start: 0,\n end: 0,\n container: null,\n viewportEl: null,\n totalHeightEl: null,\n };\n\n // Focus & navigation\n _focusRow = 0;\n _focusCol = 0;\n\n // Sort state\n _sortState: { field: string; direction: 1 | -1 } | null = null;\n\n // Edit state\n _activeEditRows = -1;\n _rowEditSnapshots = new Map<number, T>();\n _changedRowIndices = new Set<number>();\n\n // Layout\n _gridTemplate = '';\n // #endregion\n\n // #region Implementation Details (Internal only)\n __rowRenderEpoch = 0;\n __didInitialAutoSize = false;\n __lightDomColumnsCache?: ColumnInternal[];\n __originalColumnNodes?: HTMLElement[];\n __originalOrder: T[] = [];\n\n // Cached DOM refs for hot path (refreshVirtualWindow) - avoid querySelector per scroll\n __rowsBodyEl: HTMLElement | null = null;\n __scrollAreaEl: HTMLElement | null = null;\n __footerEl: HTMLElement | null = null;\n // #endregion\n\n // #region Public API Props (getters/setters)\n // Getters return the EFFECTIVE value (after merging), not the raw input.\n // This is what consumers and plugins need - the current resolved state.\n // Setters update input properties which trigger re-merge into effectiveConfig.\n\n get rows(): T[] {\n return this._rows;\n }\n set rows(value: T[]) {\n const oldValue = this.#rows;\n this.#rows = value;\n if (oldValue !== value) {\n this.#queueUpdate('rows');\n }\n }\n\n /**\n * Get the original unfiltered/unprocessed rows.\n * Use this when you need access to all source data regardless of active filters.\n */\n get sourceRows(): T[] {\n return this.#rows;\n }\n\n get columns(): ColumnConfig<T>[] {\n return [...this._columns] as ColumnConfig<T>[];\n }\n set columns(value: ColumnConfig<T>[] | ColumnConfigMap<T> | undefined) {\n const oldValue = this.#columns;\n this.#columns = value;\n if (oldValue !== value) {\n this.#queueUpdate('columns');\n }\n }\n\n get gridConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n set gridConfig(value: GridConfig<T> | undefined) {\n const oldValue = this.#gridConfig;\n this.#gridConfig = value;\n if (oldValue !== value) {\n // Clear light DOM column cache so columns are re-parsed from light DOM\n // This is needed for frameworks like Angular that project content asynchronously\n this.__lightDomColumnsCache = undefined;\n this.#queueUpdate('gridConfig');\n }\n }\n\n get fitMode(): FitMode {\n return this.#effectiveConfig.fitMode ?? 'stretch';\n }\n set fitMode(value: FitMode | undefined) {\n const oldValue = this.#fitMode;\n this.#fitMode = value;\n if (oldValue !== value) {\n this.#queueUpdate('fitMode');\n }\n }\n\n get editOn(): string | boolean | undefined {\n return this.#effectiveConfig.editOn;\n }\n set editOn(value: string | boolean | undefined) {\n const oldValue = this.#editOn;\n this.#editOn = value;\n if (oldValue !== value) {\n this.#queueUpdate('editMode');\n }\n }\n\n /**\n * Effective config accessor for internal modules and plugins.\n * Returns the merged config (single source of truth) before plugin processing.\n * Use this when you need the raw merged config (e.g., for column definitions including hidden).\n * @internal Plugin API\n */\n get effectiveConfig(): GridConfig<T> {\n return this.#effectiveConfig;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Plugins and internal code can use this for automatic listener cleanup.\n * @internal Plugin API\n * @example\n * element.addEventListener('click', handler, { signal: this.grid.disconnectSignal });\n */\n get disconnectSignal(): AbortSignal {\n // Ensure AbortController exists (created in connectedCallback before plugins attach)\n if (!this.#eventAbortController) {\n this.#eventAbortController = new AbortController();\n }\n return this.#eventAbortController.signal;\n }\n // #endregion\n\n constructor() {\n super();\n this.#shadow = this.attachShadow({ mode: 'open' });\n void this.#injectStyles(); // Fire and forget - styles load asynchronously\n this.#readyPromise = new Promise((res) => (this.#readyResolve = res));\n\n // Initialize shell controller with callbacks\n this.#shellController = createShellController(this.#shellState, {\n getShadow: () => this.#shadow,\n getShellConfig: () => this.#effectiveConfig?.shell,\n getAccordionIcons: () => ({\n expand: this.#effectiveConfig?.icons?.expand ?? DEFAULT_GRID_ICONS.expand,\n collapse: this.#effectiveConfig?.icons?.collapse ?? DEFAULT_GRID_ICONS.collapse,\n }),\n emit: (eventName, detail) => this.#emit(eventName, detail),\n refreshShellHeader: () => this.refreshShellHeader(),\n });\n }\n\n async #injectStyles(): Promise<void> {\n const sheet = new CSSStyleSheet();\n\n // If styles is a string (from ?inline import in Vite builds), use it directly\n if (typeof styles === 'string' && styles.length > 0) {\n sheet.replaceSync(styles);\n this.#shadow.adoptedStyleSheets = [sheet];\n return;\n }\n\n // Fallback: styles is undefined (e.g., when imported in Angular from source without Vite processing)\n // Angular includes grid.css in global styles - extract it from document.styleSheets\n // Wait a bit for Angular to finish loading styles\n await new Promise((resolve) => setTimeout(resolve, 50));\n\n try {\n let gridCssText = '';\n\n // Try to find the stylesheet containing grid CSS\n // Angular bundles all CSS files from angular.json styles array into one stylesheet\n // We need to find the stylesheet with grid CSS and extract ALL of it (including plugin CSS)\n for (const stylesheet of Array.from(document.styleSheets)) {\n try {\n // For inline/bundled stylesheets, check if it contains grid CSS\n const rules = Array.from(stylesheet.cssRules || []);\n const cssText = rules.map((rule) => rule.cssText).join('\\n');\n\n // Check if this stylesheet contains grid CSS by looking for distinctive selectors\n // The grid.css uses :host selectors which appear as-is in cssText\n if (cssText.includes('.tbw-grid-root') && cssText.includes(':host')) {\n // Found the bundled stylesheet with grid CSS - use ALL of it\n // This includes core grid.css + all plugin CSS files\n gridCssText = cssText;\n break;\n }\n } catch (e) {\n // CORS or access restriction - skip\n continue;\n }\n }\n\n if (gridCssText) {\n sheet.replaceSync(gridCssText);\n this.#shadow.adoptedStyleSheets = [sheet];\n } else if (typeof process === 'undefined' || process.env?.['NODE_ENV'] !== 'test') {\n // Only warn in non-test environments - test environments (happy-dom, jsdom) don't load stylesheets\n console.warn(\n '[tbw-grid] Could not find grid.css in document.styleSheets. Grid styling will not work.',\n 'Available stylesheets:',\n Array.from(document.styleSheets).map((s) => s.href || '(inline)'),\n );\n }\n } catch (err) {\n console.warn('[tbw-grid] Failed to extract grid.css from document stylesheets:', err);\n }\n }\n\n // ---------------- Plugin System ----------------\n\n /**\n * Get a plugin instance by its class.\n * Used by plugins for inter-plugin communication.\n * @internal Plugin API\n */\n getPlugin<P extends BaseGridPlugin>(PluginClass: new (...args: any[]) => P): P | undefined {\n return this.#pluginManager?.getPlugin(PluginClass);\n }\n\n /**\n * Get a plugin instance by its name.\n * Used for loose coupling between plugins (avoids static imports).\n * @internal Plugin API\n */\n getPluginByName(name: string): BaseGridPlugin | undefined {\n return this.#pluginManager?.getPluginByName(name);\n }\n\n /**\n * Request a full re-render of the grid.\n * Called by plugins when they need the grid to update.\n * Note: This does NOT reset plugin state - just re-processes rows/columns and renders.\n * @internal Plugin API\n */\n requestRender(): void {\n this.#rebuildRowModel();\n this.#processColumns();\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n }\n\n /**\n * Update the grid's column template CSS.\n * Called by resize controller during column resize operations.\n * @internal\n */\n updateTemplate(): void {\n updateTemplate(this);\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Called by plugins when they only need to update CSS classes/styles.\n * This runs all plugin afterRender hooks without rebuilding row/column DOM.\n * @internal Plugin API\n */\n requestAfterRender(): void {\n this.#pluginManager?.afterRender();\n }\n\n /**\n * Initialize plugin system with instances from config.\n * Plugins are class instances passed in gridConfig.plugins[].\n */\n #initializePlugins(): void {\n // Create plugin manager for this grid\n this.#pluginManager = new PluginManager(this);\n\n // Get plugin instances from config - ensure it's an array\n const pluginsConfig = this.#effectiveConfig?.plugins;\n const plugins = Array.isArray(pluginsConfig) ? (pluginsConfig as BaseGridPlugin[]) : [];\n\n // Attach all plugins\n this.#pluginManager.attachAll(plugins);\n }\n\n /**\n * Inject all plugin styles into the shadow DOM.\n * Must be called after #render() since innerHTML wipes existing content.\n */\n #injectAllPluginStyles(): void {\n const allStyles = this.#pluginManager?.getAllStyles() ?? '';\n if (allStyles) {\n const styleEl = document.createElement('style');\n styleEl.setAttribute('data-plugin', 'all');\n styleEl.textContent = allStyles;\n this.#shadow.appendChild(styleEl);\n }\n }\n\n /**\n * Update plugins when grid config changes.\n * With class-based plugins, we need to detach old and attach new.\n */\n #updatePluginConfigs(): void {\n // With class-based plugins, config changes require re-initialization\n // The new plugins are in the new config - detach old, attach new\n if (this.#pluginManager) {\n this.#pluginManager.detachAll();\n }\n this.#initializePlugins();\n this.#injectAllPluginStyles();\n\n // Re-collect plugin shell contributions (tool panels, header content)\n // This is needed when gridConfig changes after initial render\n this.#collectPluginShellContributions();\n\n // Update cached scroll plugin flag\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n }\n\n /**\n * Clean up plugin states when grid disconnects.\n */\n #destroyPlugins(): void {\n this.#pluginManager?.detachAll();\n }\n\n /**\n * Collect tool panels and header content from all plugins.\n * Called after plugins are attached but before render.\n */\n #collectPluginShellContributions(): void {\n if (!this.#pluginManager) return;\n\n // Collect tool panels from plugins\n const pluginPanels = this.#pluginManager.getToolPanels();\n for (const { panel } of pluginPanels) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.toolPanels.has(panel.id)) {\n this.#shellState.toolPanels.set(panel.id, panel);\n }\n }\n\n // Collect header contents from plugins\n const pluginContents = this.#pluginManager.getHeaderContents();\n for (const { content } of pluginContents) {\n // Skip if already registered (light DOM or API takes precedence)\n if (!this.#shellState.headerContents.has(content.id)) {\n this.#shellState.headerContents.set(content.id, content);\n }\n }\n }\n\n /**\n * Gets a renderer factory for tool panels from registered framework adapters.\n * Returns a factory function that tries each adapter in order until one handles the element.\n */\n #getToolPanelRendererFactory(): ToolPanelRendererFactory | undefined {\n const adapters = DataGridElement.getAdapters();\n if (adapters.length === 0 && !(this as any).__frameworkAdapter) return undefined;\n\n // Also check for instance-level adapter (e.g., __frameworkAdapter from Angular Grid directive)\n const instanceAdapter = (this as any).__frameworkAdapter;\n\n return (element: HTMLElement) => {\n // Try instance adapter first (from Grid directive)\n if (instanceAdapter?.createToolPanelRenderer) {\n const renderer = instanceAdapter.createToolPanelRenderer(element);\n if (renderer) return renderer;\n }\n\n // Try global adapters\n for (const adapter of adapters) {\n if (adapter.createToolPanelRenderer) {\n const renderer = adapter.createToolPanelRenderer(element);\n if (renderer) return renderer;\n }\n }\n\n return undefined;\n };\n }\n\n // ---------------- Lifecycle ----------------\n connectedCallback(): void {\n if (!this.hasAttribute('tabindex')) (this as any).tabIndex = 0;\n if (!this.hasAttribute('version')) this.setAttribute('version', DataGridElement.version);\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Create AbortController for all event listeners (grid internal + plugins)\n // This must happen BEFORE plugins attach so they can use disconnectSignal\n // Abort any previous controller first (in case of re-connect)\n if (this.#eventAbortController) {\n this.#eventAbortController.abort();\n this.#eventListenersAdded = false; // Reset so listeners can be re-added\n }\n this.#eventAbortController = new AbortController();\n\n // Cancel any pending idle work from previous connection\n if (this.#idleCallbackHandle) {\n cancelIdle(this.#idleCallbackHandle);\n this.#idleCallbackHandle = undefined;\n }\n\n // === CRITICAL PATH (synchronous) - needed for first paint ===\n\n // Parse light DOM shell elements BEFORE merging config\n parseLightDomShell(this, this.#shellState);\n // Parse light DOM tool panels (framework adapters may not be ready yet, but vanilla JS works)\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Merge all config sources into effectiveConfig (including columns and shell)\n this.#mergeEffectiveConfig();\n\n // Initialize plugin system (now plugins can access disconnectSignal)\n this.#initializePlugins();\n\n // Collect tool panels and header content from plugins (must be before render)\n this.#collectPluginShellContributions();\n\n if (!this.#initialized) {\n this.#render();\n this.#injectAllPluginStyles(); // Inject plugin styles after render\n this.#initialized = true;\n }\n this.#afterConnect();\n\n // === DEFERRED WORK (idle) - not needed for first paint ===\n this.#idleCallbackHandle = scheduleIdle(\n () => {\n // Set up MutationObserver to watch for light DOM changes\n // This handles frameworks like Angular that project content asynchronously\n this.#setupLightDomObserver();\n },\n { timeout: 100 },\n );\n }\n\n disconnectedCallback(): void {\n // Cancel any pending idle work\n if (this.#idleCallbackHandle) {\n cancelIdle(this.#idleCallbackHandle);\n this.#idleCallbackHandle = undefined;\n }\n\n // Clean up plugin states\n this.#destroyPlugins();\n\n // Clean up shell state\n cleanupShellState(this.#shellState);\n this.#shellController.setInitialized(false);\n\n // Clean up tool panel resize handler\n this.#resizeCleanup?.();\n this.#resizeCleanup = undefined;\n\n // Cancel any ongoing touch momentum animation\n cancelMomentum(this.#touchState);\n\n // Abort all event listeners (internal + document-level)\n // This cleans up all listeners added with { signal } option\n if (this.#eventAbortController) {\n this.#eventAbortController.abort();\n this.#eventAbortController = undefined;\n }\n // Also abort scroll-specific listeners (separate controller)\n this.#scrollAbortController?.abort();\n this.#scrollAbortController = undefined;\n this.#eventListenersAdded = false; // Reset so listeners can be re-added on reconnect\n\n if (this._resizeController) {\n this._resizeController.dispose();\n }\n if (this.#resizeObserver) {\n this.#resizeObserver.disconnect();\n this.#resizeObserver = undefined;\n }\n if (this.#rowHeightObserver) {\n this.#rowHeightObserver.disconnect();\n this.#rowHeightObserver = undefined;\n this.#rowHeightObserverSetup = false;\n }\n if (this.#lightDomObserver) {\n this.#lightDomObserver.disconnect();\n this.#lightDomObserver = undefined;\n }\n\n // Clear caches to prevent memory leaks\n invalidateCellCache(this);\n this._rowEditSnapshots.clear();\n this._changedRowIndices.clear();\n this.#customStyles.clear();\n\n // Clear row pool - detach from DOM and release references\n for (const rowEl of this._rowPool) {\n rowEl.remove();\n }\n this._rowPool.length = 0;\n\n // Clear cached DOM refs to prevent stale references\n this.__rowsBodyEl = null;\n this.__scrollAreaEl = null;\n this.__footerEl = null;\n\n this.#connected = false;\n }\n\n /**\n * Handle HTML attribute changes.\n * Only processes attribute values when SET (non-null).\n * Removing an attribute does NOT clear JS-set properties.\n */\n attributeChangedCallback(name: string, oldValue: string | null, newValue: string | null): void {\n if (oldValue === newValue || !newValue || newValue === 'null' || newValue === 'undefined') return;\n\n // Map kebab-case attributes to camelCase properties\n const propMap: Record<string, keyof this> = {\n rows: 'rows',\n columns: 'columns',\n 'grid-config': 'gridConfig',\n 'fit-mode': 'fitMode',\n 'edit-on': 'editOn',\n };\n\n const prop = propMap[name];\n if (!prop) return;\n\n // JSON attributes need parsing\n if (name === 'rows' || name === 'columns' || name === 'grid-config') {\n try {\n (this as any)[prop] = JSON.parse(newValue);\n } catch {\n console.warn(`[tbw-grid] Invalid JSON for '${name}' attribute:`, newValue);\n }\n } else {\n // String attributes (fit-mode, edit-on)\n (this as any)[prop] = newValue;\n }\n }\n\n #afterConnect(): void {\n // Shell changes the DOM structure - need to find elements appropriately\n const gridContent = this.#shadow.querySelector('.tbw-grid-content');\n const gridRoot = gridContent ?? this.#shadow.querySelector('.tbw-grid-root');\n\n this._headerRowEl = gridRoot?.querySelector('.header-row') as HTMLElement;\n // Faux scrollbar pattern:\n // - .faux-vscroll-spacer sets virtual height\n // - .rows-viewport provides visible height for virtualization calculations\n this._virtualization.totalHeightEl = gridRoot?.querySelector('.faux-vscroll-spacer') as HTMLElement;\n this._virtualization.viewportEl = gridRoot?.querySelector('.rows-viewport') as HTMLElement;\n this._bodyEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Cache DOM refs for hot path (refreshVirtualWindow) - avoid querySelector per scroll\n this.__rowsBodyEl = gridRoot?.querySelector('.rows-body') as HTMLElement;\n this.__scrollAreaEl = gridRoot?.querySelector('.tbw-scroll-area') as HTMLElement;\n this.__footerEl = gridRoot?.querySelector('.tbw-footer') as HTMLElement;\n\n // Initialize shell header content and custom buttons if shell is active\n if (this.#shellController.isInitialized) {\n // Render plugin header content\n renderHeaderContent(this.#shadow, this.#shellState);\n // Render custom toolbar buttons (element/render modes)\n renderCustomToolbarButtons(this.#shadow, this.#effectiveConfig?.shell, this.#shellState);\n // Open default section if configured\n const defaultOpen = this.#effectiveConfig?.shell?.toolPanel?.defaultOpen;\n if (defaultOpen && this.#shellState.toolPanels.has(defaultOpen)) {\n this.openToolPanel();\n this.#shellState.expandedSections.add(defaultOpen);\n }\n }\n\n // Mark for tests that afterConnect ran\n this.setAttribute('data-upgraded', '');\n this.#connected = true;\n\n // Create resize controller BEFORE setup - renderHeader() needs it for resize handle mousedown events\n this._resizeController = createResizeController(this as any);\n\n // Run setup\n this.#setup();\n\n // Set up DOM-element scroll listeners (these need to be re-attached when DOM is recreated)\n this.#setupScrollListeners(gridRoot);\n\n // Only add component-level event listeners once (afterConnect can be called multiple times)\n // When shell state changes or refreshShellHeader is called, we re-run afterConnect\n // but component-level listeners should not be duplicated (they don't depend on DOM elements)\n // Scroll listeners are already set up above and handle DOM recreation via their own AbortController\n if (this.#eventListenersAdded) {\n return;\n }\n this.#eventListenersAdded = true;\n\n // Get the signal for event listener cleanup (AbortController created in connectedCallback)\n const signal = this.disconnectSignal;\n\n // Element-level keydown handler (uses signal for automatic cleanup)\n this.addEventListener('keydown', (e) => handleGridKeyDown(this as any, e), { signal });\n\n // Document-level listeners (also use signal for automatic cleanup)\n // Escape key to cancel row editing\n document.addEventListener(\n 'keydown',\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this._activeEditRows !== -1) {\n exitRowEdit(this, this._activeEditRows, true);\n }\n },\n { capture: true, signal },\n );\n\n // Click outside to commit row editing\n document.addEventListener(\n 'mousedown',\n (e: MouseEvent) => {\n if (this._activeEditRows === -1) return;\n const rowEl = this.findRenderedRowElement(this._activeEditRows);\n if (!rowEl) return;\n const path = (e.composedPath && e.composedPath()) || [];\n if (path.includes(rowEl)) return;\n exitRowEdit(this, this._activeEditRows, false);\n },\n { signal },\n );\n\n // Central mouse event handling for plugins (uses signal for automatic cleanup)\n this.#shadow.addEventListener('mousedown', (e) => this.#handleMouseDown(e as MouseEvent), { signal });\n\n // Track global mousemove/mouseup for drag operations (uses signal for automatic cleanup)\n document.addEventListener('mousemove', (e: MouseEvent) => this.#handleMouseMove(e), { signal });\n document.addEventListener('mouseup', (e: MouseEvent) => this.#handleMouseUp(e), { signal });\n\n // Determine row height for virtualization:\n // 1. User-configured rowHeight in gridConfig takes precedence\n // 2. Otherwise, measure actual row height from DOM (respects CSS variable --tbw-row-height)\n const userRowHeight = this.#effectiveConfig.rowHeight;\n if (userRowHeight && userRowHeight > 0) {\n this._virtualization.rowHeight = userRowHeight;\n } else {\n // Initial measurement after first paint (CSS is already loaded via Vite)\n // ResizeObserver in #setupScrollListeners handles subsequent dynamic changes\n requestAnimationFrame(() => this.#measureRowHeight());\n }\n\n // Initialize ARIA selection state\n queueMicrotask(() => this.#updateAriaSelection());\n\n requestAnimationFrame(() => requestAnimationFrame(() => this.#readyResolve?.()));\n }\n\n /**\n * Measure actual row height from DOM.\n * Finds the tallest cell to account for custom renderers that may push height.\n */\n #measureRowHeight(): void {\n const firstRow = this._bodyEl?.querySelector('.data-grid-row');\n if (!firstRow) return;\n\n // Find the tallest cell in the row (custom renderers may push some cells taller)\n const cells = firstRow.querySelectorAll('.cell');\n let maxCellHeight = 0;\n cells.forEach((cell) => {\n const h = (cell as HTMLElement).offsetHeight;\n if (h > maxCellHeight) maxCellHeight = h;\n });\n\n const rowRect = (firstRow as HTMLElement).getBoundingClientRect();\n\n // Use the larger of row height or max cell height\n const measuredHeight = Math.max(rowRect.height, maxCellHeight);\n if (measuredHeight > 0 && measuredHeight !== this._virtualization.rowHeight) {\n this._virtualization.rowHeight = measuredHeight;\n this.refreshVirtualWindow(true);\n }\n }\n\n /**\n * Set up scroll-related event listeners on DOM elements.\n * These need to be re-attached when the DOM is recreated (e.g., shell toggle).\n * Uses a separate AbortController that is recreated each time.\n */\n #setupScrollListeners(gridRoot: Element | null): void {\n // Abort any existing scroll listeners before adding new ones\n this.#scrollAbortController?.abort();\n this.#scrollAbortController = new AbortController();\n const scrollSignal = this.#scrollAbortController.signal;\n\n // Faux scrollbar pattern: scroll events come from the fake scrollbar element\n // Content area doesn't scroll - rows are positioned via transforms\n const fauxScrollbar = gridRoot?.querySelector('.faux-vscroll') as HTMLElement;\n const rowsEl = gridRoot?.querySelector('.rows') as HTMLElement;\n\n // Store reference for scroll position reading in refreshVirtualWindow\n this._virtualization.container = fauxScrollbar ?? this;\n\n // Cache whether any plugin has scroll handlers (checked once during setup)\n this.#hasScrollPlugins = this.#pluginManager?.getAll().some((p) => p.onScroll) ?? false;\n\n if (fauxScrollbar && rowsEl) {\n fauxScrollbar.addEventListener(\n 'scroll',\n () => {\n // Fast exit if no scroll processing needed\n if (!this._virtualization.enabled && !this.#hasScrollPlugins) return;\n\n const currentScrollTop = fauxScrollbar.scrollTop;\n const rowHeight = this._virtualization.rowHeight;\n\n // Bypass mode: all rows are rendered, just translate by scroll position\n // No need for virtual window calculations\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n rowsEl.style.transform = `translateY(${-currentScrollTop}px)`;\n } else {\n // Virtualized mode: calculate sub-pixel offset for smooth scrolling\n // Even-aligned start preserves zebra stripe parity\n // DOM nth-child(even) will always match data row parity\n const rawStart = Math.floor(currentScrollTop / rowHeight);\n const evenAlignedStart = rawStart - (rawStart % 2);\n const subPixelOffset = -(currentScrollTop - evenAlignedStart * rowHeight);\n rowsEl.style.transform = `translateY(${subPixelOffset}px)`;\n }\n\n // Batch content update with requestAnimationFrame\n // Old content stays visible with smooth offset until new content renders\n this.#pendingScrollTop = currentScrollTop;\n if (!this.#scrollRaf) {\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n if (this.#pendingScrollTop !== null) {\n this.#onScrollBatched(this.#pendingScrollTop);\n this.#pendingScrollTop = null;\n }\n });\n }\n },\n { passive: true, signal: scrollSignal },\n );\n\n // Forward wheel events from content area to faux scrollbar\n // Without this, mouse wheel over content wouldn't scroll\n // Listen on .tbw-grid-content to capture wheel events from entire grid area\n // Note: gridRoot may already BE .tbw-grid-content when shell is active, so search from shadow root\n const gridContentEl = this.#shadow.querySelector('.tbw-grid-content') as HTMLElement;\n const scrollArea = this.#shadow.querySelector('.tbw-scroll-area') as HTMLElement;\n if (gridContentEl) {\n gridContentEl.addEventListener(\n 'wheel',\n (e: WheelEvent) => {\n // SHIFT+wheel or trackpad deltaX = horizontal scroll\n const isHorizontal = e.shiftKey || Math.abs(e.deltaX) > Math.abs(e.deltaY);\n\n if (isHorizontal && scrollArea) {\n const delta = e.shiftKey ? e.deltaY : e.deltaX;\n const { scrollLeft, scrollWidth, clientWidth } = scrollArea;\n const canScroll = (delta > 0 && scrollLeft < scrollWidth - clientWidth) || (delta < 0 && scrollLeft > 0);\n if (canScroll) {\n e.preventDefault();\n scrollArea.scrollLeft += delta;\n }\n } else if (!isHorizontal) {\n const { scrollTop, scrollHeight, clientHeight } = fauxScrollbar;\n const canScroll =\n (e.deltaY > 0 && scrollTop < scrollHeight - clientHeight) || (e.deltaY < 0 && scrollTop > 0);\n if (canScroll) {\n e.preventDefault();\n fauxScrollbar.scrollTop += e.deltaY;\n }\n }\n // If can't scroll, event bubbles to scroll the page\n },\n { passive: false, signal: scrollSignal },\n );\n\n // Touch scrolling support for mobile devices\n // Supports both vertical (via faux scrollbar) and horizontal (via scroll area) scrolling\n // Includes momentum scrolling for natural \"flick\" behavior\n setupTouchScrollListeners(gridContentEl, this.#touchState, { fauxScrollbar, scrollArea }, scrollSignal);\n }\n }\n\n // Set up delegated event handlers for cell interactions (click, dblclick, keydown)\n // This replaces per-cell event listeners with a single set of delegated handlers\n // Dramatically reduces memory usage: 4 listeners total vs. 30,000+ for large grids\n if (this._bodyEl) {\n setupCellEventDelegation(this as any, this._bodyEl, scrollSignal);\n }\n\n // Disconnect existing resize observer before creating new one\n // This ensures we observe the NEW viewport element after DOM recreation\n this.#resizeObserver?.disconnect();\n\n // Resize observer to refresh virtualization when viewport size changes\n // (e.g., when footer is added, window resizes, or shell panel toggles)\n if (this._virtualization.viewportEl) {\n this.#resizeObserver = new ResizeObserver(() => {\n if (!this.#scrollRaf) {\n this.#scrollRaf = requestAnimationFrame(() => {\n this.#scrollRaf = 0;\n this.refreshVirtualWindow(true);\n ensureCellVisible(this as any);\n });\n }\n });\n this.#resizeObserver.observe(this._virtualization.viewportEl);\n }\n\n if (this._virtualization.enabled) {\n requestAnimationFrame(() => {\n this.refreshVirtualWindow(true);\n // Set up row height observer AFTER rows are rendered (not before)\n // Observing cells before refreshVirtualWindow replaces them is useless\n this.#setupRowHeightObserver();\n });\n }\n }\n\n /**\n * Set up ResizeObserver on first row's cells to detect height changes.\n * Called after rows are rendered to observe the actual content cells.\n * Handles dynamic CSS loading, lazy images, font loading, etc.\n */\n #rowHeightObserverSetup = false; // Only set up once per lifecycle\n #setupRowHeightObserver(): void {\n // Only set up once - row height measurement is one-time during initialization\n if (this.#rowHeightObserverSetup) return;\n\n const firstRow = this._bodyEl?.querySelector('.data-grid-row');\n if (!firstRow) return;\n\n this.#rowHeightObserverSetup = true;\n this.#rowHeightObserver?.disconnect();\n\n const cells = firstRow.querySelectorAll('.cell');\n if (cells.length > 0) {\n this.#rowHeightObserver = new ResizeObserver(() => {\n this.#measureRowHeight();\n });\n // Observe all cells - any one might have a custom renderer that changes size\n cells.forEach((cell) => this.#rowHeightObserver!.observe(cell));\n }\n }\n\n // ---------------- Event Emitters ----------------\n #emit<D>(eventName: string, detail: D): void {\n this.dispatchEvent(new CustomEvent(eventName, { detail, bubbles: true, composed: true }));\n }\n\n _emitCellCommit(detail: CellCommitDetail<T>): void {\n this.#emit('cell-commit', detail);\n }\n\n _emitRowCommit(detail: RowCommitDetail<T>): void {\n this.#emit('row-commit', detail);\n }\n\n _emitSortChange(detail: SortChangeDetail): void {\n this.#emit('sort-change', detail);\n }\n\n _emitColumnResize(detail: ColumnResizeDetail): void {\n this.#emit('column-resize', detail);\n }\n\n _emitActivateCell(detail: ActivateCellDetail): void {\n this.#emit('activate-cell', detail);\n }\n\n /** Update ARIA selection attributes on rendered rows/cells */\n #updateAriaSelection(): void {\n // Mark active row and cell with aria-selected\n const rows = this._bodyEl?.querySelectorAll('.data-grid-row');\n rows?.forEach((row, rowIdx) => {\n const isActiveRow = rowIdx === this._focusRow;\n row.setAttribute('aria-selected', String(isActiveRow));\n row.querySelectorAll('.cell').forEach((cell, colIdx) => {\n (cell as HTMLElement).setAttribute('aria-selected', String(isActiveRow && colIdx === this._focusCol));\n });\n });\n }\n\n // ---------------- Batched Update System ----------------\n // Allows multiple property changes within the same microtask to be coalesced\n // into a single update cycle, dramatically reducing redundant renders.\n\n /**\n * Queue an update for a specific property type.\n * All updates queued within the same microtask are batched together.\n */\n #queueUpdate(type: 'rows' | 'columns' | 'gridConfig' | 'fitMode' | 'editMode'): void {\n this.#pendingUpdateFlags[type] = true;\n\n // If already queued, skip scheduling\n if (this.#pendingUpdate) return;\n\n this.#pendingUpdate = true;\n // Use queueMicrotask to batch synchronous property sets\n queueMicrotask(() => this.#flushPendingUpdates());\n }\n\n /**\n * Process all pending updates in optimal order.\n * Priority: gridConfig first (may affect all), then columns, rows, fitMode, editMode\n */\n #flushPendingUpdates(): void {\n if (!this.#pendingUpdate || !this.#connected) {\n this.#pendingUpdate = false;\n return;\n }\n\n const flags = this.#pendingUpdateFlags;\n\n // Reset flags before processing to allow new updates during processing\n this.#pendingUpdate = false;\n this.#pendingUpdateFlags = {\n rows: false,\n columns: false,\n gridConfig: false,\n fitMode: false,\n editMode: false,\n };\n\n // If gridConfig changed, it supersedes columns/rows/fit/edit changes\n // because gridConfig includes all of those\n if (flags.gridConfig) {\n this.#applyGridConfigUpdate();\n return; // gridConfig handles everything\n }\n\n // Process remaining changes in dependency order\n if (flags.columns) {\n this.#applyColumnsUpdate();\n }\n if (flags.rows) {\n this.#applyRowsUpdate();\n }\n if (flags.fitMode) {\n this.#applyFitModeUpdate();\n }\n if (flags.editMode) {\n this.#applyEditModeUpdate();\n }\n }\n\n // Individual update applicators - these do the actual work\n #applyRowsUpdate(): void {\n this._rows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n this.#rebuildRowModel();\n const hasColumns =\n this._columns.length > 0 ||\n (Array.isArray(this.#effectiveConfig?.columns) && this.#effectiveConfig.columns.length > 0) ||\n (Array.isArray(this.#columns) && this.#columns.length > 0);\n if (!hasColumns) {\n this.#setup();\n } else {\n this.#processColumns();\n this.refreshVirtualWindow(true);\n }\n }\n\n #applyColumnsUpdate(): void {\n invalidateCellCache(this);\n this.#mergeEffectiveConfig();\n this.#setup();\n }\n\n #applyFitModeUpdate(): void {\n this.#mergeEffectiveConfig();\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed') {\n this.__didInitialAutoSize = false;\n autoSizeColumns(this);\n } else {\n this._columns.forEach((c: any) => {\n if (!c.__userResized && c.__autoSized) delete c.width;\n });\n updateTemplate(this);\n }\n }\n\n #applyEditModeUpdate(): void {\n this.#mergeEffectiveConfig();\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n this.refreshVirtualWindow(true);\n }\n\n #applyGridConfigUpdate(): void {\n const hadShell = !!this.#shadow.querySelector('.has-shell');\n const needsShell = shouldRenderShellHeader(this.#effectiveConfig as any, this.#shellState);\n\n getColumnConfiguration(this);\n this.#mergeEffectiveConfig();\n this.#updatePluginConfigs();\n\n const nowNeedsShell = shouldRenderShellHeader(this.#effectiveConfig as any, this.#shellState);\n\n if (hadShell !== nowNeedsShell || (!hadShell && nowNeedsShell)) {\n this.#render();\n this.#afterConnect();\n return;\n }\n\n this.#rebuildRowModel();\n this.#processColumns();\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n }\n\n // NOTE: Legacy watch handlers have been replaced by the batched update system.\n // The #queueUpdate() method schedules updates which are processed by #flushPendingUpdates()\n // and individual #apply*Update() methods. This coalesces rapid property changes\n // (e.g., setting rows, columns, gridConfig in quick succession) into a single update cycle.\n\n #processColumns(): void {\n // Let plugins process visible columns (column grouping, etc.)\n // Start from base columns (before any plugin transformation) - like #rebuildRowModel uses #rows\n if (this.#pluginManager) {\n // Use base columns as source of truth, falling back to current _columns if not set\n const sourceColumns = this.#baseColumns.length > 0 ? this.#baseColumns : this._columns;\n const visibleCols = sourceColumns.filter((c) => !c.hidden);\n const hiddenCols = sourceColumns.filter((c) => c.hidden);\n const processedColumns = this.#pluginManager.processColumns([...visibleCols] as any[]);\n\n // If plugins modified visible columns, update them\n if (processedColumns !== visibleCols) {\n // Build a map of processed columns by field for quick lookup\n const processedMap = new Map(processedColumns.map((c: any, i: number) => [c.field, { col: c, order: i }]));\n\n // Check if this is a complete column replacement (e.g., pivot mode)\n // If no processed columns match original columns, use processed columns directly\n const hasMatchingFields = visibleCols.some((c) => processedMap.has(c.field));\n\n if (!hasMatchingFields && processedColumns.length > 0) {\n // Complete replacement: use processed columns directly (pivot mode)\n // Preserve hidden columns at the end\n this._columns = [...processedColumns, ...hiddenCols] as ColumnInternal<T>[];\n } else {\n // Plugins returned original fields (possibly modified) - merge back\n // Use source columns as base, not current _columns\n const updatedColumns = sourceColumns.map((c) => {\n if (c.hidden) return c; // Keep hidden columns unchanged\n const processed = processedMap.get(c.field);\n return processed ? processed.col : c;\n });\n\n this._columns = updatedColumns as ColumnInternal<T>[];\n }\n } else {\n // Plugins returned columns unchanged, but we may need to restore from base\n this._columns = [...sourceColumns] as ColumnInternal<T>[];\n }\n }\n }\n\n /** Recompute row model via plugin hooks. */\n #rebuildRowModel(): void {\n // Invalidate cell display value cache - rows are changing\n invalidateCellCache(this);\n\n // Start fresh from original rows (plugins will transform them)\n const originalRows = Array.isArray(this.#rows) ? [...this.#rows] : [];\n\n // Let plugins process rows (they may add, remove, or transform rows)\n // Plugins can add markers for specialized rendering via the renderRow hook\n const processedRows = this.#pluginManager?.processRows(originalRows) ?? originalRows;\n\n // Store processed rows for rendering\n // Note: processedRows may contain group markers that plugins handle via renderRow hook\n this._rows = processedRows as T[];\n }\n\n /**\n * Build the canonical effective configuration by merging all input sources.\n *\n * This is the **single source of truth** for the grid's configuration.\n * All inputs (gridConfig, light DOM, individual props) converge here.\n *\n * **Precedence (lowest → highest):**\n * 1. `gridConfig` property - base config object\n * 2. Light DOM `<tbw-grid-column>` elements - declarative columns\n * 3. `columns` property - programmatic columns override\n * 4. Inferred columns - auto-detected from row data\n * 5. Individual props (`fitMode`, `editOn`) - convenience overrides\n *\n * After this method runs:\n * - `#effectiveConfig` contains the merged result\n * - `_columns` is NOT set here (done by #getColumnConfiguration + #processColumns)\n * - Plugins receive config via their attach() method\n */\n #mergeEffectiveConfig(): void {\n const base: GridConfig<T> = this.#gridConfig ? { ...this.#gridConfig } : {};\n let columns: ColumnConfig<T>[] = Array.isArray(base.columns) ? [...base.columns] : [];\n\n // Light DOM cached parse (if already parsed by columns pipeline); non-invasive merge (fill gaps only)\n const domCols: ColumnConfig<T>[] = ((this as any).__lightDomColumnsCache || []).map((c: ColumnConfig<T>) => ({\n ...c,\n }));\n if (domCols.length) {\n const map: Record<string, ColumnConfig<T>> = {};\n columns.forEach((c) => (map[(c as any).field] = c));\n domCols.forEach((c: any) => {\n const exist = map[c.field];\n if (!exist) {\n columns.push(c);\n map[c.field] = c;\n } else {\n if (c.header && !exist.header) exist.header = c.header;\n if (c.type && !exist.type) exist.type = c.type;\n exist.sortable = exist.sortable || c.sortable;\n if (c.resizable) exist.resizable = true;\n if (c.editable) exist.editable = true;\n }\n });\n }\n\n // Columns prop highest structural precedence\n if (this.#columns && (this.#columns as ColumnConfig<T>[]).length) {\n columns = [...(this.#columns as ColumnConfig<T>[])];\n }\n\n // Inference if still empty\n if ((!columns || columns.length === 0) && this._rows.length) {\n const result = inferColumns(this._rows as Record<string, unknown>[]);\n columns = result.columns as ColumnConfig<T>[];\n }\n\n if (columns.length) {\n // Apply per-column defaults (sortable/resizable default true unless explicitly false)\n columns.forEach((c) => {\n if (c.sortable === undefined) c.sortable = true;\n if (c.resizable === undefined) c.resizable = true;\n // Store original configured width for reset on double-click (only numeric widths)\n const internal = c as ColumnInternal<T>;\n if (internal.__originalWidth === undefined && typeof c.width === 'number') {\n internal.__originalWidth = c.width;\n }\n });\n // Preserve processed columns (with __compiledView etc.) if already set by #getColumnConfiguration\n // Only set base.columns if effectiveConfig.columns is empty or doesn't have compiled templates\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n const alreadyProcessed = existingCols?.some((c) => c.__compiledView || c.__compiledEditor);\n if (alreadyProcessed) {\n // Keep existing processed columns\n base.columns = existingCols as ColumnConfig<T>[];\n } else {\n base.columns = columns;\n }\n } else {\n // No new columns computed, but preserve existing if processed\n const existingCols = this.#effectiveConfig.columns as ColumnInternal<T>[] | undefined;\n if (existingCols?.some((c) => c.__compiledView || c.__compiledEditor)) {\n base.columns = existingCols as ColumnConfig<T>[];\n }\n }\n\n // Individual prop overrides (behavioral)\n if (this.#fitMode) base.fitMode = this.#fitMode;\n if (!base.fitMode) base.fitMode = 'stretch';\n if (this.#editOn) base.editOn = this.#editOn;\n\n // Merge light DOM shell configuration\n if (this.#shellState.lightDomTitle) {\n if (!base.shell) base.shell = {};\n if (!base.shell.header) base.shell.header = {};\n if (!base.shell.header.title) {\n base.shell.header.title = this.#shellState.lightDomTitle;\n }\n }\n\n // Apply rowHeight from config if specified\n if (base.rowHeight && base.rowHeight > 0) {\n this._virtualization.rowHeight = base.rowHeight;\n }\n\n // Store columnState from gridConfig if not already set\n if (base.columnState && !this.#initialColumnState) {\n this.#initialColumnState = base.columnState;\n }\n\n this.#effectiveConfig = base;\n // Note: _columns is a getter/setter for effectiveConfig.columns\n // #getColumnConfiguration() populates it, and we preserve those processed columns above\n // Plugins (like ReorderPlugin) modify effectiveConfig.columns via the _columns setter\n\n // If fixed mode and width not specified: assign default 80px\n if (base.fitMode === 'fixed') {\n this._columns.forEach((c) => {\n if (c.width == null) (c as ColumnConfig<T>).width = 80;\n });\n }\n\n // Apply animation configuration to CSS variables\n this.#applyAnimationConfig();\n }\n\n /**\n * Apply animation configuration to CSS custom properties on the host element.\n * This makes the grid's animation settings available to plugins via CSS variables.\n */\n #applyAnimationConfig(): void {\n const config: AnimationConfig = {\n ...DEFAULT_ANIMATION_CONFIG,\n ...this.#effectiveConfig.animation,\n };\n\n // Resolve animation mode\n const mode = config.mode ?? 'reduced-motion';\n let enabled: 0 | 1 = 1;\n\n if (mode === false || mode === 'off') {\n enabled = 0;\n } else if (mode === true || mode === 'on') {\n enabled = 1;\n }\n // For 'reduced-motion', we leave enabled=1 and let CSS @media query handle it\n\n // Set CSS custom properties\n this.style.setProperty('--tbw-animation-duration', `${config.duration}ms`);\n this.style.setProperty('--tbw-animation-easing', config.easing ?? 'ease-out');\n this.style.setProperty('--tbw-animation-enabled', String(enabled));\n\n // Set data attribute for mode-based CSS selectors\n this.dataset.animationMode = typeof mode === 'boolean' ? (mode ? 'on' : 'off') : mode;\n }\n\n // ---------------- Delegate Wrappers ----------------\n #renderVisibleRows(start: number, end: number, epoch = this.__rowRenderEpoch): void {\n // Use cached hook to avoid creating closures on every render (hot path optimization)\n if (!this.#renderRowHook) {\n this.#renderRowHook = (row: any, rowEl: HTMLElement, rowIndex: number): boolean => {\n return this.#pluginManager?.renderRow(row, rowEl, rowIndex) ?? false;\n };\n }\n renderVisibleRows(this as any, start, end, epoch, this.#renderRowHook);\n }\n\n // ---------------- Core Helpers ----------------\n #setup(): void {\n if (!this.isConnected) return;\n if (!this._headerRowEl || !this._bodyEl) {\n return;\n }\n\n // Seed effectiveConfig.columns from config sources before getColumnConfiguration\n // This ensures columns from gridConfig/columns prop are available for merging with light DOM\n // Preserve hidden state from existing columns (visibility is runtime state)\n const configCols = (this.#gridConfig?.columns || this.#columns || []) as ColumnConfig<T>[];\n if (configCols.length) {\n // Preserve hidden state from existing effectiveConfig.columns\n const existingHiddenMap = new Map(this._columns.filter((c) => c.hidden).map((c) => [c.field, true]));\n const seeded = configCols.map((c) => ({\n ...c,\n hidden: existingHiddenMap.get(c.field) ?? c.hidden,\n }));\n this._columns = seeded as ColumnInternal<T>[];\n }\n\n getColumnConfiguration(this);\n this.#mergeEffectiveConfig();\n this.#updatePluginConfigs(); // Sync plugin configs (including auto-detection) before processing\n\n // Store base columns before plugin transformation (like #rows for row processing)\n this.#baseColumns = [...this._columns];\n\n this.#rebuildRowModel(); // Runs processRows hooks (must run before processColumns for tree plugin)\n this.#processColumns(); // Runs processColumns hooks\n\n // Apply initial column state (from gridConfig.columnState or columnState setter)\n if (this.#initialColumnState) {\n const state = this.#initialColumnState;\n this.#initialColumnState = undefined; // Clear to avoid re-applying\n this.#applyColumnStateInternal(state);\n }\n\n renderHeader(this);\n updateTemplate(this);\n this.refreshVirtualWindow(true);\n\n const mode = this.#effectiveConfig.fitMode;\n if (mode === 'fixed' && !this.__didInitialAutoSize) {\n requestAnimationFrame(() => autoSizeColumns(this));\n }\n\n // Ensure legacy inline grid styles are cleared from container\n if (this._bodyEl) {\n this._bodyEl.style.display = '';\n this._bodyEl.style.gridTemplateColumns = '';\n }\n\n // Run plugin afterRender hooks (column groups, sticky, etc.)\n queueMicrotask(() => this.#pluginManager?.afterRender());\n }\n\n /** Internal method to apply column state without triggering setup loop */\n #applyColumnStateInternal(state: GridColumnState): void {\n // Get all columns from effectiveConfig (single source of truth)\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n applyColumnState(this, state, allCols, plugins);\n\n // Update hidden property on columns based on state\n for (const colState of state.columns) {\n const col = allCols.find((c) => c.field === colState.field);\n if (col) {\n col.hidden = !colState.visible;\n }\n }\n }\n\n #onScrollBatched(scrollTop: number): void {\n // Faux scrollbar pattern: content never scrolls, just update transforms\n // Old content stays visible until new transforms are applied\n this.refreshVirtualWindow(false);\n\n // Let plugins reapply visual state to recycled DOM elements\n this.#pluginManager?.onScrollRender();\n\n // Dispatch to plugins (using cached flag and pooled event object to avoid GC)\n if (this.#hasScrollPlugins) {\n const fauxScrollbar = this._virtualization.container;\n // Reuse pooled event object - update values in-place instead of allocating new object\n const scrollEvent = this.#pooledScrollEvent;\n scrollEvent.scrollTop = scrollTop;\n scrollEvent.scrollLeft = fauxScrollbar?.scrollLeft ?? 0;\n scrollEvent.scrollHeight = fauxScrollbar?.scrollHeight ?? 0;\n scrollEvent.scrollWidth = fauxScrollbar?.scrollWidth ?? 0;\n scrollEvent.clientHeight = fauxScrollbar?.clientHeight ?? 0;\n scrollEvent.clientWidth = fauxScrollbar?.clientWidth ?? 0;\n // Note: originalEvent removed to avoid allocation - plugins should not rely on it\n this.#pluginManager?.onScroll(scrollEvent);\n }\n }\n\n /**\n * Find the header row element in the shadow DOM.\n * Used by plugins that need to access header cells for styling or measurement.\n * @internal Plugin API\n */\n findHeaderRow(): HTMLElement {\n return this.#shadow.querySelector('.header-row') as HTMLElement;\n }\n\n /**\n * Find a rendered row element by its data row index.\n * Returns null if the row is not currently rendered (virtualized out of view).\n * Used by plugins that need to access specific row elements for styling or measurement.\n * @internal Plugin API\n * @param rowIndex - The data row index (not the DOM position)\n */\n findRenderedRowElement(rowIndex: number): HTMLElement | null {\n return (\n (Array.from(this._bodyEl.querySelectorAll('.data-grid-row')) as HTMLElement[]).find((r) => {\n const cell = r.querySelector('.cell[data-row]');\n return cell && Number(cell.getAttribute('data-row')) === rowIndex;\n }) || null\n );\n }\n\n /**\n * Dispatch a cell click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchCellClick(event: MouseEvent, rowIndex: number, colIndex: number, cellEl: HTMLElement): boolean {\n const row = this._rows[rowIndex];\n const col = this._columns[colIndex];\n if (!row || !col) return false;\n\n const cellClickEvent: CellClickEvent = {\n row,\n rowIndex,\n colIndex,\n field: col.field,\n value: (row as any)[col.field],\n cellEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onCellClick(cellClickEvent) ?? false;\n }\n\n /**\n * Dispatch a row click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchRowClick(event: MouseEvent, rowIndex: number, row: any, rowEl: HTMLElement): boolean {\n if (!row) return false;\n\n const rowClickEvent: RowClickEvent = {\n rowIndex,\n row,\n rowEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onRowClick(rowClickEvent) ?? false;\n }\n\n /**\n * Dispatch a header click event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchHeaderClick(event: MouseEvent, colIndex: number, headerEl: HTMLElement): boolean {\n const col = this._columns[colIndex];\n if (!col) return false;\n\n const headerClickEvent: HeaderClickEvent = {\n colIndex,\n field: col.field,\n column: col,\n headerEl,\n originalEvent: event,\n };\n\n return this.#pluginManager?.onHeaderClick(headerClickEvent) ?? false;\n }\n\n /**\n * Dispatch a keyboard event to the plugin system.\n * Returns true if any plugin handled the event.\n */\n _dispatchKeyDown(event: KeyboardEvent): boolean {\n return this.#pluginManager?.onKeyDown(event) ?? false;\n }\n\n /**\n * Get horizontal scroll boundary offsets from plugins.\n * Used by keyboard navigation to ensure focused cells are fully visible\n * when plugins like pinned columns obscure part of the scroll area.\n */\n _getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } {\n return this.#pluginManager?.getHorizontalScrollOffsets(rowEl, focusedCell) ?? { left: 0, right: 0 };\n }\n\n /**\n * Query all plugins with a generic query and collect responses.\n * This enables inter-plugin communication without the core knowing plugin-specific concepts.\n * @internal Plugin API\n *\n * @example\n * // Check if any plugin vetoes moving a column\n * const responses = grid.queryPlugins<boolean>({ type: PLUGIN_QUERIES.CAN_MOVE_COLUMN, context: column });\n * const canMove = !responses.includes(false);\n */\n queryPlugins<T>(query: PluginQuery): T[] {\n return this.#pluginManager?.queryPlugins<T>(query) ?? [];\n }\n\n /**\n * Build a CellMouseEvent from a native MouseEvent.\n * Extracts cell/row information from the event target.\n */\n #buildCellMouseEvent(e: MouseEvent, type: 'mousedown' | 'mousemove' | 'mouseup'): CellMouseEvent {\n // For document-level events (mousemove/mouseup during drag), e.target won't be inside shadow DOM.\n // Use composedPath to find elements inside shadow roots, or fall back to elementFromPoint.\n let target: Element | null = null;\n\n // composedPath gives us the full path including shadow DOM elements\n const path = e.composedPath?.() as Element[] | undefined;\n if (path && path.length > 0) {\n target = path[0];\n } else {\n target = e.target as Element;\n }\n\n // If target is still not inside our shadow root (e.g., for document-level events),\n // use elementFromPoint to find the actual element under the mouse\n if (target && !this.#shadow.contains(target)) {\n const elAtPoint = this.#shadow.elementFromPoint(e.clientX, e.clientY);\n if (elAtPoint) {\n target = elAtPoint;\n }\n }\n\n // Cells have data-col and data-row attributes\n const cellEl = target?.closest?.('[data-col]') as HTMLElement | null;\n const rowEl = target?.closest?.('.data-grid-row') as HTMLElement | null;\n const headerEl = target?.closest?.('.header-row') as HTMLElement | null;\n\n let rowIndex: number | undefined;\n let colIndex: number | undefined;\n let row: T | undefined;\n let field: string | undefined;\n let value: unknown;\n let column: any;\n\n if (cellEl) {\n // Get indices from cell attributes\n rowIndex = parseInt(cellEl.getAttribute('data-row') ?? '-1', 10);\n colIndex = parseInt(cellEl.getAttribute('data-col') ?? '-1', 10);\n if (rowIndex >= 0 && colIndex >= 0) {\n row = this._rows[rowIndex];\n column = this._columns[colIndex];\n field = column?.field;\n value = row && field ? (row as any)[field] : undefined;\n }\n }\n\n return {\n type,\n row,\n rowIndex: rowIndex !== undefined && rowIndex >= 0 ? rowIndex : undefined,\n colIndex: colIndex !== undefined && colIndex >= 0 ? colIndex : undefined,\n field,\n value,\n column,\n originalEvent: e,\n cellElement: cellEl ?? undefined,\n rowElement: rowEl ?? undefined,\n isHeader: !!headerEl,\n cell:\n rowIndex !== undefined && colIndex !== undefined && rowIndex >= 0 && colIndex >= 0\n ? { row: rowIndex, col: colIndex }\n : undefined,\n };\n }\n\n /**\n /**\n * Handle mousedown events and dispatch to plugin system.\n */\n #handleMouseDown(e: MouseEvent): void {\n const event = this.#buildCellMouseEvent(e, 'mousedown');\n const handled = this.#pluginManager?.onCellMouseDown(event) ?? false;\n\n // If any plugin handled mousedown, start tracking for drag\n if (handled) {\n this.#isDragging = true;\n }\n }\n\n /**\n * Handle mousemove events (only when dragging).\n */\n #handleMouseMove(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mousemove');\n this.#pluginManager?.onCellMouseMove(event);\n }\n\n /**\n * Handle mouseup events.\n */\n #handleMouseUp(e: MouseEvent): void {\n if (!this.#isDragging) return;\n\n const event = this.#buildCellMouseEvent(e, 'mouseup');\n this.#pluginManager?.onCellMouseUp(event);\n this.#isDragging = false;\n }\n\n // API consumed by internal utils (rows.ts) - delegates to editing.ts\n get changedRows(): T[] {\n return getChangedRows(this);\n }\n\n get changedRowIndices(): number[] {\n return getChangedRowIndices(this);\n }\n\n async resetChangedRows(silent?: boolean): Promise<void> {\n resetChangedRows(this, silent);\n }\n\n async beginBulkEdit(rowIndex: number): Promise<void> {\n beginBulkEdit(this, rowIndex, {\n findRenderedRowElement: (idx) => this.findRenderedRowElement?.(idx) ?? null,\n });\n }\n\n async commitActiveRowEdit(): Promise<void> {\n commitActiveRowEdit(this);\n }\n\n async cancelActiveRowEdit(): Promise<void> {\n cancelActiveRowEdit(this);\n }\n\n async ready(): Promise<void> {\n return this.#readyPromise;\n }\n\n async forceLayout(): Promise<void> {\n this.#setup();\n await new Promise((r) => requestAnimationFrame(() => requestAnimationFrame(r)));\n }\n\n /** Public method: returns a frozen snapshot of the merged effective configuration */\n async getConfig(): Promise<Readonly<GridConfig<T>>> {\n return Object.freeze({ ...(this.#effectiveConfig || {}) });\n }\n\n // ---------------- Column Visibility API ----------------\n // Delegates to column-state.ts pure functions\n\n /** Visibility callbacks for column-state.ts functions */\n #visibilityCallbacks: VisibilityCallbacks = {\n emit: (name, detail) => this.#emit(name, detail),\n clearRowPool: () => {\n this._rowPool.length = 0;\n if (this._bodyEl) this._bodyEl.innerHTML = '';\n this.__rowRenderEpoch++;\n },\n setup: () => this.#setup(),\n requestStateChange: () => this.requestStateChange(),\n };\n\n setColumnVisible(field: string, visible: boolean): boolean {\n return setColumnVisible(this, field, visible, this.#visibilityCallbacks);\n }\n\n toggleColumnVisibility(field: string): boolean {\n return toggleColumnVisibility(this, field, this.#visibilityCallbacks);\n }\n\n isColumnVisible(field: string): boolean {\n return isColumnVisible(this, field);\n }\n\n showAllColumns(): void {\n showAllColumns(this, this.#visibilityCallbacks);\n }\n\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n return getAllColumns(this);\n }\n\n setColumnOrder(order: string[]): void {\n setColumnOrder(this, order, {\n renderHeader: () => renderHeader(this),\n updateTemplate: () => updateTemplate(this),\n refreshVirtualWindow: () => this.refreshVirtualWindow(true),\n });\n }\n\n getColumnOrder(): string[] {\n return getColumnOrder(this);\n }\n\n // ---------------- Column State API ----------------\n\n /**\n * Get the current column state, including order, width, visibility, sort, and plugin state.\n * Returns a serializable object suitable for localStorage or database storage.\n */\n getColumnState(): GridColumnState {\n const plugins = this.#pluginManager?.getAll() ?? [];\n return collectColumnState(this, plugins as BaseGridPlugin[]);\n }\n\n /**\n * Set the column state, restoring order, width, visibility, sort, and plugin state.\n * Use this to restore previously saved column state.\n */\n set columnState(state: GridColumnState | undefined) {\n if (!state) return;\n\n // Store for use after initialization if called before ready\n this.#initialColumnState = state;\n\n // If already initialized, apply immediately\n if (this.#initialized) {\n this.#applyColumnState(state);\n }\n }\n\n /**\n * Get the current column state.\n */\n get columnState(): GridColumnState | undefined {\n return this.getColumnState();\n }\n\n /**\n * Apply column state internally.\n */\n #applyColumnState(state: GridColumnState): void {\n // Clear hidden flags before applying state\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n this.#applyColumnStateInternal(state);\n\n // Re-setup to apply changes\n this.#setup();\n }\n\n /**\n * Request a state change event to be emitted.\n * Called internally after resize, reorder, visibility, or sort changes.\n * Plugins should call this after changing their state.\n * The event is debounced to avoid excessive events during drag operations.\n * @internal Plugin API\n */\n requestStateChange(): void {\n if (!this.#stateChangeHandler) {\n this.#stateChangeHandler = createStateChangeHandler(\n this,\n () => (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[],\n (state) => this.#emit('column-state-change', state),\n );\n }\n this.#stateChangeHandler();\n }\n\n /**\n * Reset column state to initial configuration.\n * Clears all user modifications (order, width, visibility, sort).\n */\n resetColumnState(): void {\n // Clear initial state\n this.#initialColumnState = undefined;\n\n // Clear hidden flag on all columns\n const allCols = (this.#effectiveConfig.columns ?? []) as ColumnInternal<T>[];\n allCols.forEach((c) => {\n c.hidden = false;\n });\n\n // Reset sort state\n this._sortState = null;\n this.__originalOrder = [];\n\n // Re-initialize columns from config\n this.#mergeEffectiveConfig();\n this.#setup();\n\n // Notify plugins to reset their state\n const plugins = (this.#pluginManager?.getAll() ?? []) as BaseGridPlugin[];\n for (const plugin of plugins) {\n if (plugin.applyColumnState) {\n // Pass empty state to indicate reset\n for (const col of this._columns) {\n plugin.applyColumnState(col.field, {\n field: col.field,\n order: 0,\n visible: true,\n });\n }\n }\n }\n\n // Emit state change\n this.requestStateChange();\n }\n\n // ---------------- Shell / Tool Panel API ----------------\n // These methods delegate to ShellController for implementation.\n // The controller encapsulates all tool panel logic while grid.ts\n // exposes the public API surface.\n\n /** Check if the tool panel is currently open. */\n get isToolPanelOpen(): boolean {\n return this.#shellController.isPanelOpen;\n }\n\n /**\n * Get the currently active tool panel ID, or null if none is open.\n * @deprecated Use isToolPanelOpen and expandedToolPanelSections instead.\n */\n get activeToolPanel(): string | null {\n return this.#shellController.activePanel;\n }\n\n /** Get the IDs of expanded accordion sections. */\n get expandedToolPanelSections(): string[] {\n return this.#shellController.expandedSections;\n }\n\n /** Open the tool panel (accordion view with all registered panels). */\n openToolPanel(): void {\n this.#shellController.openToolPanel();\n }\n\n /** Close the tool panel. */\n closeToolPanel(): void {\n this.#shellController.closeToolPanel();\n }\n\n /** Toggle the tool panel open/closed. */\n toggleToolPanel(): void {\n this.#shellController.toggleToolPanel();\n }\n\n /** Toggle an accordion section expanded/collapsed. */\n toggleToolPanelSection(sectionId: string): void {\n this.#shellController.toggleToolPanelSection(sectionId);\n }\n\n /** Get registered tool panel definitions. */\n getToolPanels(): ToolPanelDefinition[] {\n return this.#shellController.getToolPanels();\n }\n\n /** Register a custom tool panel (without creating a plugin). */\n registerToolPanel(panel: ToolPanelDefinition): void {\n this.#shellController.registerToolPanel(panel);\n }\n\n /** Unregister a custom tool panel. */\n unregisterToolPanel(panelId: string): void {\n this.#shellController.unregisterToolPanel(panelId);\n }\n\n /** Get registered header content definitions. */\n getHeaderContents(): HeaderContentDefinition[] {\n return this.#shellController.getHeaderContents();\n }\n\n /** Register custom header content (without creating a plugin). */\n registerHeaderContent(content: HeaderContentDefinition): void {\n this.#shellController.registerHeaderContent(content);\n }\n\n /** Unregister custom header content. */\n unregisterHeaderContent(contentId: string): void {\n this.#shellController.unregisterHeaderContent(contentId);\n }\n\n /** Get all registered toolbar buttons. */\n getToolbarButtons(): ToolbarButtonInfo[] {\n return this.#shellController.getToolbarButtons();\n }\n\n /** Register a custom toolbar button programmatically. */\n registerToolbarButton(button: ToolbarButtonConfig): void {\n this.#shellController.registerToolbarButton(button);\n }\n\n /** Unregister a custom toolbar button. */\n unregisterToolbarButton(buttonId: string): void {\n this.#shellController.unregisterToolbarButton(buttonId);\n }\n\n /** Enable/disable a toolbar button by ID. */\n setToolbarButtonDisabled(buttonId: string, disabled: boolean): void {\n this.#shellController.setToolbarButtonDisabled(buttonId, disabled);\n }\n\n /**\n * Re-parse light DOM shell elements and refresh shell header.\n * Call this after dynamically modifying <tbw-grid-header> children.\n */\n refreshShellHeader(): void {\n // Re-parse light DOM (header and tool panels)\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Re-render the entire grid (shell structure may change)\n this.#render();\n this.#afterConnect();\n }\n\n // #region Custom Styles API\n /** Map of registered custom style elements by ID */\n #customStyles = new Map<string, HTMLStyleElement>();\n\n /**\n * Register custom CSS styles to be injected into the grid's shadow DOM.\n * Use this to style custom cell renderers, editors, or detail panels.\n *\n * @param id - Unique identifier for the style block (for removal/updates)\n * @param css - CSS string to inject\n *\n * @example\n * ```typescript\n * // Register custom styles for a detail panel\n * grid.registerStyles('my-detail-styles', `\n * .my-detail-panel { padding: 16px; }\n * .my-detail-table { width: 100%; }\n * `);\n *\n * // Update styles later\n * grid.registerStyles('my-detail-styles', updatedCss);\n *\n * // Remove styles\n * grid.unregisterStyles('my-detail-styles');\n * ```\n */\n registerStyles(id: string, css: string): void {\n // Remove existing style with same ID\n this.unregisterStyles(id);\n\n // Create and inject new style element\n const styleEl = document.createElement('style');\n styleEl.id = `tbw-custom-${id}`;\n styleEl.textContent = css;\n this.#shadow.appendChild(styleEl);\n this.#customStyles.set(id, styleEl);\n }\n\n /**\n * Remove previously registered custom styles.\n * @param id - The ID used when registering the styles\n */\n unregisterStyles(id: string): void {\n const existing = this.#customStyles.get(id);\n if (existing) {\n existing.remove();\n this.#customStyles.delete(id);\n }\n }\n\n /**\n * Get list of registered custom style IDs.\n */\n getRegisteredStyles(): string[] {\n return Array.from(this.#customStyles.keys());\n }\n // #endregion\n\n /**\n * Set up MutationObserver to watch for light DOM changes.\n * This handles frameworks like Angular that project content asynchronously.\n * When shell-related elements are added/changed, the shell header is updated.\n */\n #setupLightDomObserver(): void {\n // Clean up any existing observer\n if (this.#lightDomObserver) {\n this.#lightDomObserver.disconnect();\n }\n\n // Debounce multiple mutations into a single update\n let debounceTimer: ReturnType<typeof setTimeout> | null = null;\n let needsShellUpdate = false;\n let needsColumnUpdate = false;\n\n const processPendingUpdates = () => {\n debounceTimer = null;\n\n if (needsShellUpdate) {\n // Re-parse shell and update header if title changed\n const hadTitle = this.#shellState.lightDomTitle;\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n const hasTitle = this.#shellState.lightDomTitle;\n\n if (hasTitle && !hadTitle) {\n this.#mergeEffectiveConfig();\n const shellHeader = this.#shadow.querySelector('.tbw-shell-header');\n if (shellHeader) {\n const newHeaderHtml = renderShellHeader(\n this.#effectiveConfig.shell,\n this.#shellState,\n this.#effectiveConfig.icons?.toolPanel,\n );\n const temp = document.createElement('div');\n temp.innerHTML = newHeaderHtml;\n const newHeader = temp.firstElementChild;\n if (newHeader) {\n shellHeader.replaceWith(newHeader);\n this.#setupShellListeners();\n }\n }\n }\n needsShellUpdate = false;\n }\n\n if (needsColumnUpdate) {\n // Clear column cache and re-run setup\n this.__lightDomColumnsCache = undefined;\n this.#setup();\n needsColumnUpdate = false;\n }\n };\n\n this.#lightDomObserver = new MutationObserver((mutations) => {\n for (const mutation of mutations) {\n // Check added nodes for shell/column elements\n for (const node of mutation.addedNodes) {\n if (node.nodeType !== Node.ELEMENT_NODE) continue;\n const el = node as Element;\n const tagName = el.tagName.toLowerCase();\n\n if (tagName === 'tbw-grid-header') {\n needsShellUpdate = true;\n } else if (tagName === 'tbw-grid-column' || tagName === 'tbw-grid-detail') {\n needsColumnUpdate = true;\n }\n }\n\n // Check for attribute changes on shell elements\n if (mutation.type === 'attributes' && mutation.target.nodeType === Node.ELEMENT_NODE) {\n const el = mutation.target as Element;\n const tagName = el.tagName.toLowerCase();\n if (tagName === 'tbw-grid-header') {\n needsShellUpdate = true;\n } else if (tagName === 'tbw-grid-column') {\n needsColumnUpdate = true;\n }\n }\n }\n\n // Debounce updates\n if ((needsShellUpdate || needsColumnUpdate) && !debounceTimer) {\n debounceTimer = setTimeout(processPendingUpdates, 0);\n }\n });\n\n // Observe direct children and their attributes\n this.#lightDomObserver.observe(this, {\n childList: true,\n subtree: true,\n attributes: true,\n attributeFilter: ['title', 'field', 'header', 'width', 'hidden'],\n });\n }\n\n /**\n * Re-parse light DOM column elements and refresh the grid.\n * Call this after framework adapters have registered their templates.\n * @internal Used by framework integration libraries (Angular, React, Vue)\n */\n refreshColumns(): void {\n // Clear the column cache to force re-parsing\n this.__lightDomColumnsCache = undefined;\n\n // Re-parse light DOM shell elements (may have been rendered asynchronously by frameworks)\n const hadTitle = this.#shellState.lightDomTitle;\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n const hasTitle = this.#shellState.lightDomTitle;\n\n // If title was added via light DOM, update the shell header in place\n // The shell may already be rendered (due to plugins/panels), but without the title\n if (hasTitle && !hadTitle) {\n // Merge the new title into effectiveConfig\n this.#mergeEffectiveConfig();\n // Update the existing shell header element with new HTML\n const shellHeader = this.#shadow.querySelector('.tbw-shell-header');\n if (shellHeader) {\n const newHeaderHtml = renderShellHeader(\n this.#effectiveConfig.shell,\n this.#shellState,\n this.#effectiveConfig.icons?.toolPanel,\n );\n // Create a temporary container and extract the new header\n const temp = document.createElement('div');\n temp.innerHTML = newHeaderHtml;\n const newHeader = temp.firstElementChild;\n if (newHeader) {\n shellHeader.replaceWith(newHeader);\n // Re-attach event listeners to the new toolbar element\n this.#setupShellListeners();\n }\n }\n }\n\n // Re-run setup which handles column configuration, headers, and rows\n this.#setup();\n }\n\n // ---------------- Virtual Window ----------------\n /**\n * Calculate total height for the faux scrollbar spacer element.\n * Used by both bypass and virtualized rendering paths to ensure consistent scroll behavior.\n */\n #calculateTotalSpacerHeight(totalRows: number): number {\n const rowHeight = this._virtualization.rowHeight;\n const fauxScrollbar = this._virtualization.container ?? this;\n const viewportEl = this._virtualization.viewportEl ?? fauxScrollbar;\n const fauxScrollHeight = fauxScrollbar.clientHeight;\n const viewportHeight = viewportEl.clientHeight;\n\n // Get scroll-area height (may differ from faux when h-scrollbar present)\n const shadowRoot = (this as unknown as Element).shadowRoot;\n const scrollAreaEl = shadowRoot?.querySelector('.tbw-scroll-area');\n const scrollAreaHeight = scrollAreaEl ? (scrollAreaEl as HTMLElement).clientHeight : fauxScrollHeight;\n\n // Use scroll-area height as reference since it contains the actual content\n const containerHeight = scrollAreaHeight;\n const viewportHeightDiff = containerHeight - viewportHeight;\n\n // Add extra height from plugins (e.g., expanded master-detail rows)\n const pluginExtraHeight = this.#pluginManager?.getExtraHeight() ?? 0;\n\n // Horizontal scrollbar compensation: When a horizontal scrollbar appears inside scroll-area,\n // the faux scrollbar (sibling) is taller than scroll-area. Add the difference as padding.\n const hScrollbarPadding = Math.max(0, fauxScrollHeight - scrollAreaHeight);\n\n return totalRows * rowHeight + viewportHeightDiff + pluginExtraHeight + hScrollbarPadding;\n }\n\n /**\n * Core virtualization routine. Chooses between bypass (small datasets), grouped window rendering,\n * or standard row window rendering.\n * @internal Plugin API\n */\n refreshVirtualWindow(force = false): void {\n if (!this._bodyEl) return;\n\n const totalRows = this._rows.length;\n\n if (!this._virtualization.enabled) {\n this.#renderVisibleRows(0, totalRows);\n this.#pluginManager?.afterRender();\n return;\n }\n\n if (this._rows.length <= this._virtualization.bypassThreshold) {\n this._virtualization.start = 0;\n this._virtualization.end = totalRows;\n // Only reset transform on force refresh (initial render, data change)\n // Don't reset on scroll-triggered updates - the scroll handler manages transforms\n if (force) {\n this._bodyEl.style.transform = 'translateY(0px)';\n }\n this.#renderVisibleRows(0, totalRows, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${this.#calculateTotalSpacerHeight(totalRows)}px`;\n }\n // Set ARIA counts on inner grid element (not host, which may contain shell chrome)\n // Use cached ref to avoid querySelector per scroll\n this.__rowsBodyEl?.setAttribute('aria-rowcount', String(totalRows));\n this.__rowsBodyEl?.setAttribute('aria-colcount', String(this._visibleColumns.length));\n this.#pluginManager?.afterRender();\n return;\n }\n\n // --- Normal virtualization path with faux scrollbar pattern ---\n // Faux scrollbar provides scrollTop, viewport provides visible height\n const fauxScrollbar = this._virtualization.container ?? this;\n const viewportEl = this._virtualization.viewportEl ?? fauxScrollbar;\n const viewportHeight = viewportEl.clientHeight;\n const rowHeight = this._virtualization.rowHeight;\n const scrollTop = fauxScrollbar.scrollTop;\n\n // When plugins add extra height (e.g., expanded details), the scroll position\n // includes that extra height. We need to find the actual row at scrollTop\n // by iteratively accounting for cumulative extra heights.\n // This prevents jumping when scrolling past expanded content.\n let start = Math.floor(scrollTop / rowHeight);\n\n // Iteratively refine: the initial guess may be too high because scrollTop\n // includes extra heights from expanded rows before it. Adjust downward.\n let iterations = 0;\n const maxIterations = 10; // Prevent infinite loop\n while (iterations < maxIterations) {\n const extraHeightBefore = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const adjustedStart = Math.floor((scrollTop - extraHeightBefore) / rowHeight);\n if (adjustedStart >= start || adjustedStart < 0) break;\n start = adjustedStart;\n iterations++;\n }\n\n // Faux scrollbar pattern: calculate effective position for this start\n // With translateY(0), the first rendered row appears at viewport top\n // Round down to even number so DOM nth-child(even) always matches data row parity\n // This prevents zebra stripe flickering during scroll since rows shift in pairs\n start = start - (start % 2);\n if (start < 0) start = 0;\n\n // Allow plugins to extend the start index backwards\n // (e.g., to keep expanded detail rows visible as they scroll out)\n const pluginAdjustedStart = this.#pluginManager?.adjustVirtualStart(start, scrollTop, rowHeight);\n if (pluginAdjustedStart !== undefined && pluginAdjustedStart < start) {\n start = pluginAdjustedStart;\n // Re-apply even alignment after plugin adjustment\n start = start - (start % 2);\n if (start < 0) start = 0;\n }\n\n // Faux pattern buffer: render 2 extra rows below for smooth edge transition\n // This is smaller than traditional overscan since sub-pixel offset handles smoothness\n // +1 extra to account for the even-alignment above potentially showing 1 more row at top\n const visibleCount = Math.ceil(viewportHeight / rowHeight) + 3;\n let end = start + visibleCount;\n if (end > totalRows) end = totalRows;\n\n this._virtualization.start = start;\n this._virtualization.end = end;\n\n // Height spacer for scrollbar\n // The faux-vscroll is a sibling of .tbw-scroll-area, so it doesn't shrink when\n // elements inside scroll-area (header, column groups, footer, hScrollbar) take vertical space.\n // viewportHeightDiff captures ALL these differences - no extra buffer needed.\n const fauxScrollHeight = fauxScrollbar.clientHeight;\n\n // Guard: Skip height calculation if faux scrollbar has no height but viewport does\n // This indicates stale DOM references during recreation (e.g., shell toggle)\n // When both are 0 (test environment or not in DOM), proceed normally\n if (fauxScrollHeight === 0 && viewportHeight > 0) {\n // Stale refs detected, schedule retry after layout stabilizes\n requestAnimationFrame(() => this.refreshVirtualWindow(force));\n return;\n }\n\n const totalHeight = this.#calculateTotalSpacerHeight(totalRows);\n\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${totalHeight}px`;\n }\n\n // Smooth scroll: apply offset for fluid motion\n // Since start is even-aligned, offset is distance from that aligned position\n // This creates smooth sliding while preserving zebra stripe parity\n // Account for extra heights (expanded details) before the start row\n const extraHeightBeforeStart = this.#pluginManager?.getExtraHeightBefore?.(start) ?? 0;\n const subPixelOffset = -(scrollTop - start * rowHeight - extraHeightBeforeStart);\n this._bodyEl.style.transform = `translateY(${subPixelOffset}px)`;\n\n this.#renderVisibleRows(start, end, force ? ++this.__rowRenderEpoch : this.__rowRenderEpoch);\n\n // Set ARIA counts on inner grid element (not host, which may contain shell chrome)\n // Use cached ref to avoid querySelector per scroll\n this.__rowsBodyEl?.setAttribute('aria-rowcount', String(totalRows));\n this.__rowsBodyEl?.setAttribute('aria-colcount', String(this._visibleColumns.length));\n\n // Only run plugin afterRender hooks on force refresh (structural changes)\n // Skip on scroll-triggered renders for maximum performance\n if (force) {\n this.#pluginManager?.afterRender();\n\n // After plugins modify the DOM (e.g., add footer, column groups),\n // heights may have changed. Recalculate spacer height in a microtask\n // to catch these changes before the next paint.\n queueMicrotask(() => {\n const newFauxHeight = fauxScrollbar.clientHeight;\n const newViewportHeight = viewportEl.clientHeight;\n // Skip if faux scrollbar is stale (0 height but viewport has height)\n if (newFauxHeight === 0 && newViewportHeight > 0) return;\n\n // Recalculate using the shared helper\n const newTotalHeight = this.#calculateTotalSpacerHeight(totalRows);\n\n if (this._virtualization.totalHeightEl) {\n this._virtualization.totalHeightEl.style.height = `${newTotalHeight}px`;\n }\n });\n }\n }\n\n // ---------------- Render ----------------\n #render(): void {\n // Parse light DOM shell elements before rendering\n parseLightDomShell(this, this.#shellState);\n parseLightDomToolPanels(this, this.#shellState, this.#getToolPanelRendererFactory());\n\n // Re-merge config to pick up any newly parsed light DOM shell settings\n this.#mergeEffectiveConfig();\n\n const shellConfig = this.#effectiveConfig?.shell;\n\n // Render using direct DOM construction (2-3x faster than innerHTML)\n const hasShell = buildGridDOMIntoShadow(this.#shadow, shellConfig, this.#shellState, this.#effectiveConfig?.icons);\n\n if (hasShell) {\n this.#setupShellListeners();\n this.#shellController.setInitialized(true);\n }\n }\n\n /**\n * Set up shell event listeners after render.\n */\n #setupShellListeners(): void {\n setupShellEventListeners(this.#shadow, this.#effectiveConfig?.shell, this.#shellState, {\n onPanelToggle: () => this.toggleToolPanel(),\n onSectionToggle: (sectionId: string) => this.toggleToolPanelSection(sectionId),\n onToolbarButtonClick: (buttonId) => this.#handleToolbarButtonClick(buttonId),\n });\n\n // Set up tool panel resize\n this.#resizeCleanup?.();\n this.#resizeCleanup = setupToolPanelResize(this.#shadow, this.#effectiveConfig?.shell, (width: number) => {\n // Update the CSS variable to persist the new width\n this.style.setProperty('--tbw-tool-panel-width', `${width}px`);\n });\n }\n\n /**\n * Handle toolbar button click (for config buttons with action).\n */\n #handleToolbarButtonClick(buttonId: string): void {\n // Check config buttons\n const configButtons = this.#effectiveConfig?.shell?.header?.toolbarButtons ?? [];\n const configBtn = configButtons.find((b) => b.id === buttonId);\n if (configBtn?.action) {\n configBtn.action();\n return;\n }\n\n // Check API-registered buttons\n const apiBtn = this.#shellState.toolbarButtons.get(buttonId);\n if (apiBtn?.action) {\n apiBtn.action();\n }\n }\n}\n\n// Self-registering custom element\nif (!customElements.get(DataGridElement.tagName)) {\n customElements.define(DataGridElement.tagName, DataGridElement);\n}\n\n// Make DataGridElement accessible globally for framework adapters\n(globalThis as any).DataGridElement = DataGridElement;\n\n// Type augmentation for querySelector/createElement\ndeclare global {\n interface HTMLElementTagNameMap {\n 'tbw-grid': DataGridElement;\n }\n}\n","/**\n * Shared types for the plugin system.\n *\n * These types are used by both the base plugin class and the grid core.\n * Centralizing them here avoids circular imports and reduces duplication.\n */\n\nimport type { ColumnConfig, GridConfig } from '../types';\n\n/**\n * Keyboard modifier flags\n */\nexport interface KeyboardModifiers {\n ctrl?: boolean;\n shift?: boolean;\n alt?: boolean;\n meta?: boolean;\n}\n\n/**\n * Cell coordinates\n */\nexport interface CellCoords {\n row: number;\n col: number;\n}\n\n/**\n * Cell click event\n */\nexport interface CellClickEvent {\n rowIndex: number;\n colIndex: number;\n field: string;\n value: unknown;\n row: unknown;\n cellEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Row click event\n */\nexport interface RowClickEvent {\n rowIndex: number;\n row: unknown;\n rowEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Header click event\n */\nexport interface HeaderClickEvent {\n colIndex: number;\n field: string;\n column: ColumnConfig;\n headerEl: HTMLElement;\n originalEvent: MouseEvent;\n}\n\n/**\n * Scroll event\n */\nexport interface ScrollEvent {\n scrollTop: number;\n scrollLeft: number;\n scrollHeight: number;\n scrollWidth: number;\n clientHeight: number;\n clientWidth: number;\n originalEvent?: Event;\n}\n\n/**\n * Cell mouse event (for drag operations, selection, etc.)\n */\nexport interface CellMouseEvent {\n /** Event type: mousedown, mousemove, or mouseup */\n type: 'mousedown' | 'mousemove' | 'mouseup';\n /** Row index, undefined if not over a data cell */\n rowIndex?: number;\n /** Column index, undefined if not over a cell */\n colIndex?: number;\n /** Field name, undefined if not over a cell */\n field?: string;\n /** Cell value, undefined if not over a data cell */\n value?: unknown;\n /** Row data object, undefined if not over a data row */\n row?: unknown;\n /** Column configuration, undefined if not over a column */\n column?: ColumnConfig;\n /** The cell element, undefined if not over a cell */\n cellElement?: HTMLElement;\n /** The row element, undefined if not over a row */\n rowElement?: HTMLElement;\n /** Whether the event is over a header cell */\n isHeader: boolean;\n /** Cell coordinates if over a valid data cell */\n cell?: CellCoords;\n /** The original mouse event */\n originalEvent: MouseEvent;\n}\n\n/**\n * Context menu parameters\n */\nexport interface ContextMenuParams {\n x: number;\n y: number;\n rowIndex?: number;\n colIndex?: number;\n field?: string;\n value?: unknown;\n row?: unknown;\n column?: ColumnConfig;\n isHeader?: boolean;\n}\n\n/**\n * Context menu item (used by context-menu plugin query)\n */\nexport interface ContextMenuItem {\n id: string;\n label: string;\n icon?: string;\n disabled?: boolean;\n separator?: boolean;\n children?: ContextMenuItem[];\n action?: (params: ContextMenuParams) => void;\n}\n\n/**\n * Generic plugin query for inter-plugin communication.\n * Plugins can define their own query types as string constants\n * and respond to queries from other plugins.\n */\nexport interface PluginQuery<T = unknown> {\n /** Query type identifier (e.g., 'canMoveColumn', 'getContextMenuItems') */\n type: string;\n /** Query-specific context/parameters */\n context: T;\n}\n\n/**\n * Well-known plugin query types.\n * Plugins can define additional query types beyond these.\n */\nexport const PLUGIN_QUERIES = {\n /** Ask if a column can be moved. Context: ColumnConfig. Response: boolean | undefined */\n CAN_MOVE_COLUMN: 'canMoveColumn',\n /** Get context menu items. Context: ContextMenuParams. Response: ContextMenuItem[] */\n GET_CONTEXT_MENU_ITEMS: 'getContextMenuItems',\n} as const;\n\n/**\n * Cell render context for plugin cell renderers.\n * Provides full context including position and editing state.\n */\nexport interface PluginCellRenderContext {\n /** The cell value */\n value: unknown;\n /** The row data object */\n row: unknown;\n /** The row index in the data array */\n rowIndex: number;\n /** The column index */\n colIndex: number;\n /** The field name */\n field: string;\n /** The column configuration */\n column: ColumnConfig;\n /** Whether the cell is being edited */\n isEditing: boolean;\n}\n\n/**\n * Cell renderer function type for plugins.\n */\nexport type CellRenderer = (ctx: PluginCellRenderContext) => string | HTMLElement;\n\n/**\n * Header renderer function type for plugins.\n */\nexport type HeaderRenderer = (ctx: { column: ColumnConfig; colIndex: number }) => string | HTMLElement;\n\n/**\n * Cell editor interface for plugins.\n */\nexport interface CellEditor {\n create(ctx: PluginCellRenderContext, commitFn: (value: unknown) => void, cancelFn: () => void): HTMLElement;\n getValue?(element: HTMLElement): unknown;\n focus?(element: HTMLElement): void;\n}\n\n/**\n * Minimal grid interface for plugins.\n * This avoids circular imports with the full DataGridElement.\n */\nexport interface GridElementRef {\n shadowRoot: ShadowRoot | null;\n rows: unknown[];\n columns: ColumnConfig[];\n gridConfig: GridConfig;\n /** Current focused row index */\n _focusRow: number;\n /** Current focused column index */\n _focusCol: number;\n /** AbortSignal that is aborted when the grid disconnects from the DOM */\n disconnectSignal: AbortSignal;\n requestRender(): void;\n requestAfterRender(): void;\n forceLayout(): Promise<void>;\n dispatchEvent(event: Event): boolean;\n}\n","/**\n * Base Grid Plugin Class\n *\n * All plugins extend this abstract class.\n * Plugins are instantiated per-grid and manage their own state.\n */\n\nimport type {\n ColumnConfig,\n ColumnState,\n GridPlugin,\n HeaderContentDefinition,\n IconValue,\n ToolPanelDefinition,\n} from '../types';\nimport { DEFAULT_GRID_ICONS } from '../types';\n\n// Re-export shared plugin types for convenience\nexport { PLUGIN_QUERIES } from './types';\nexport type {\n CellClickEvent,\n CellCoords,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n ContextMenuItem,\n ContextMenuParams,\n GridElementRef,\n HeaderClickEvent,\n HeaderRenderer,\n KeyboardModifiers,\n PluginCellRenderContext,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './types';\n\nimport type {\n CellClickEvent,\n CellEditor,\n CellMouseEvent,\n CellRenderer,\n GridElementRef,\n HeaderClickEvent,\n HeaderRenderer,\n PluginQuery,\n RowClickEvent,\n ScrollEvent,\n} from './types';\n\n/**\n * Grid element interface for plugins.\n * Extends GridElementRef with plugin-specific methods.\n */\nexport interface GridElement extends GridElementRef {\n getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined;\n getPluginByName(name: string): BaseGridPlugin | undefined;\n}\n\n/**\n * Header render context for plugin header renderers.\n */\nexport interface PluginHeaderRenderContext {\n /** Column configuration */\n column: ColumnConfig;\n /** Column index */\n colIndex: number;\n}\n\n/**\n * Abstract base class for all grid plugins.\n *\n * @template TConfig - Configuration type for the plugin\n */\nexport abstract class BaseGridPlugin<TConfig = unknown> implements GridPlugin {\n /** Unique plugin identifier (derived from class name by default) */\n abstract readonly name: string;\n\n /** Plugin version - override in subclass if needed */\n readonly version: string = '1.0.0';\n\n /** CSS styles to inject into the grid's shadow DOM */\n readonly styles?: string;\n\n /** Custom cell renderers keyed by type name */\n readonly cellRenderers?: Record<string, CellRenderer>;\n\n /** Custom header renderers keyed by type name */\n readonly headerRenderers?: Record<string, HeaderRenderer>;\n\n /** Custom cell editors keyed by type name */\n readonly cellEditors?: Record<string, CellEditor>;\n\n /** The grid instance this plugin is attached to */\n protected grid!: GridElement;\n\n /** Plugin configuration - merged with defaults in attach() */\n protected config!: TConfig;\n\n /** User-provided configuration from constructor */\n protected readonly userConfig: Partial<TConfig>;\n\n /**\n * Default configuration - subclasses should override this getter.\n * Note: This must be a getter (not property initializer) for proper inheritance\n * since property initializers run after parent constructor.\n */\n protected get defaultConfig(): Partial<TConfig> {\n return {};\n }\n\n constructor(config: Partial<TConfig> = {}) {\n this.userConfig = config;\n }\n\n /**\n * Called when the plugin is attached to a grid.\n * Override to set up event listeners, initialize state, etc.\n */\n attach(grid: GridElement): void {\n this.grid = grid;\n // Merge config here (after subclass construction is complete)\n this.config = { ...this.defaultConfig, ...this.userConfig } as TConfig;\n }\n\n /**\n * Called when the plugin is detached from a grid.\n * Override to clean up event listeners, timers, etc.\n */\n detach(): void {\n // Override in subclass\n }\n\n /**\n * Get another plugin instance from the same grid.\n * Use for inter-plugin communication.\n */\n protected getPlugin<T extends BaseGridPlugin>(PluginClass: new (...args: any[]) => T): T | undefined {\n return this.grid?.getPlugin(PluginClass);\n }\n\n /**\n * Emit a custom event from the grid.\n */\n protected emit<T>(eventName: string, detail: T): void {\n this.grid?.dispatchEvent?.(new CustomEvent(eventName, { detail, bubbles: true }));\n }\n\n /**\n * Request a re-render of the grid.\n */\n protected requestRender(): void {\n this.grid?.requestRender?.();\n }\n\n /**\n * Request a lightweight style update without rebuilding DOM.\n * Use this instead of requestRender() when only CSS classes need updating.\n */\n protected requestAfterRender(): void {\n this.grid?.requestAfterRender?.();\n }\n\n /**\n * Get the current rows from the grid.\n */\n protected get rows(): any[] {\n return this.grid?.rows ?? [];\n }\n\n /**\n * Get the original unfiltered/unprocessed rows from the grid.\n * Use this when you need all source data regardless of active filters.\n */\n protected get sourceRows(): any[] {\n return (this.grid as any)?.sourceRows ?? [];\n }\n\n /**\n * Get the current columns from the grid.\n */\n protected get columns(): ColumnConfig[] {\n return this.grid?.columns ?? [];\n }\n\n /**\n * Get only visible columns from the grid (excludes hidden).\n * Use this for rendering that needs to match the grid template.\n */\n protected get visibleColumns(): ColumnConfig[] {\n return (this.grid as any)?._visibleColumns ?? [];\n }\n\n /**\n * Get the shadow root of the grid.\n */\n protected get shadowRoot(): ShadowRoot | null {\n return this.grid?.shadowRoot ?? null;\n }\n\n /**\n * Get the disconnect signal for event listener cleanup.\n * This signal is aborted when the grid disconnects from the DOM.\n * Use this when adding event listeners that should be cleaned up automatically.\n *\n * Best for:\n * - Document/window-level listeners added in attach()\n * - Listeners on the grid element itself\n * - Any listener that should persist across renders\n *\n * Not needed for:\n * - Listeners on elements created in afterRender() (removed with element)\n *\n * @example\n * element.addEventListener('click', handler, { signal: this.disconnectSignal });\n * document.addEventListener('keydown', handler, { signal: this.disconnectSignal });\n */\n protected get disconnectSignal(): AbortSignal {\n return this.grid?.disconnectSignal;\n }\n\n /**\n * Get the grid-level icons configuration.\n * Returns merged icons (user config + defaults).\n */\n protected get gridIcons(): typeof DEFAULT_GRID_ICONS {\n const userIcons = this.grid?.gridConfig?.icons ?? {};\n return { ...DEFAULT_GRID_ICONS, ...userIcons };\n }\n\n /**\n * Resolve an icon value to string or HTMLElement.\n * Checks plugin config first, then grid-level icons, then defaults.\n *\n * @param iconKey - The icon key in GridIcons (e.g., 'expand', 'collapse')\n * @param pluginOverride - Optional plugin-level override\n * @returns The resolved icon value\n */\n protected resolveIcon(iconKey: keyof typeof DEFAULT_GRID_ICONS, pluginOverride?: IconValue): IconValue {\n // Plugin override takes precedence\n if (pluginOverride !== undefined) {\n return pluginOverride;\n }\n // Then grid-level config\n return this.gridIcons[iconKey];\n }\n\n /**\n * Set an icon value on an element.\n * Handles both string (text/HTML) and HTMLElement values.\n *\n * @param element - The element to set the icon on\n * @param icon - The icon value (string or HTMLElement)\n */\n protected setIcon(element: HTMLElement, icon: IconValue): void {\n if (typeof icon === 'string') {\n element.innerHTML = icon;\n } else if (icon instanceof HTMLElement) {\n element.innerHTML = '';\n element.appendChild(icon.cloneNode(true));\n }\n }\n\n /**\n * Log a warning message.\n */\n protected warn(message: string): void {\n console.warn(`[tbw-grid:${this.name}] ${message}`);\n }\n\n // #region Lifecycle Hooks\n\n /**\n * Transform rows before rendering.\n * Called during each render cycle before rows are rendered to the DOM.\n * Use this to filter, sort, or add computed properties to rows.\n *\n * @param rows - The current rows array (readonly to encourage returning a new array)\n * @returns The modified rows array to render\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Filter out hidden rows\n * return rows.filter(row => !row._hidden);\n * }\n * ```\n *\n * @example\n * ```ts\n * processRows(rows: readonly any[]): any[] {\n * // Add computed properties\n * return rows.map(row => ({\n * ...row,\n * _fullName: `${row.firstName} ${row.lastName}`\n * }));\n * }\n * ```\n */\n processRows?(rows: readonly any[]): any[];\n\n /**\n * Transform columns before rendering.\n * Called during each render cycle before column headers and cells are rendered.\n * Use this to add, remove, or modify column definitions.\n *\n * @param columns - The current columns array (readonly to encourage returning a new array)\n * @returns The modified columns array to render\n *\n * @example\n * ```ts\n * processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n * // Add a selection checkbox column\n * return [\n * { field: '_select', header: '', width: 40 },\n * ...columns\n * ];\n * }\n * ```\n */\n processColumns?(columns: readonly ColumnConfig[]): ColumnConfig[];\n\n /**\n * Called before each render cycle begins.\n * Use this to prepare state or cache values needed during rendering.\n *\n * @example\n * ```ts\n * beforeRender(): void {\n * this.visibleRowCount = this.calculateVisibleRows();\n * }\n * ```\n */\n beforeRender?(): void;\n\n /**\n * Called after each render cycle completes.\n * Use this for DOM manipulation, adding event listeners to rendered elements,\n * or applying visual effects like selection highlights.\n *\n * @example\n * ```ts\n * afterRender(): void {\n * // Apply selection styling to rendered rows\n * const rows = this.shadowRoot?.querySelectorAll('.data-row');\n * rows?.forEach((row, i) => {\n * row.classList.toggle('selected', this.selectedRows.has(i));\n * });\n * }\n * ```\n */\n afterRender?(): void;\n\n /**\n * Called after scroll-triggered row rendering completes.\n * This is a lightweight hook for applying visual state to recycled DOM elements.\n * Use this instead of afterRender when you need to reapply styling during scroll.\n *\n * Performance note: This is called frequently during scroll. Keep implementation fast.\n *\n * @example\n * ```ts\n * onScrollRender(): void {\n * // Reapply selection state to visible cells\n * this.applySelectionToVisibleCells();\n * }\n * ```\n */\n onScrollRender?(): void;\n\n /**\n * Return extra height contributed by this plugin (e.g., expanded detail rows).\n * Used to adjust scrollbar height calculations for virtualization.\n *\n * @returns Total extra height in pixels\n *\n * @example\n * ```ts\n * getExtraHeight(): number {\n * return this.expandedRows.size * this.detailHeight;\n * }\n * ```\n */\n getExtraHeight?(): number;\n\n /**\n * Return extra height that appears before a given row index.\n * Used by virtualization to correctly calculate scroll positions when\n * there's variable height content (like expanded detail rows) above the viewport.\n *\n * @param beforeRowIndex - The row index to calculate extra height before\n * @returns Extra height in pixels that appears before this row\n *\n * @example\n * ```ts\n * getExtraHeightBefore(beforeRowIndex: number): number {\n * let height = 0;\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * if (expandedRowIndex < beforeRowIndex) {\n * height += this.getDetailHeight(expandedRowIndex);\n * }\n * }\n * return height;\n * }\n * ```\n */\n getExtraHeightBefore?(beforeRowIndex: number): number;\n\n /**\n * Adjust the virtualization start index to render additional rows before the visible range.\n * Use this when expanded content (like detail rows) needs its parent row to remain rendered\n * even when the parent row itself has scrolled above the viewport.\n *\n * @param start - The calculated start row index\n * @param scrollTop - The current scroll position\n * @param rowHeight - The height of a single row\n * @returns The adjusted start index (lower than or equal to original start)\n *\n * @example\n * ```ts\n * adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n * // If row 5 is expanded and scrolled partially, keep it rendered\n * for (const expandedRowIndex of this.expandedRowIndices) {\n * const expandedRowTop = expandedRowIndex * rowHeight;\n * const expandedRowBottom = expandedRowTop + rowHeight + this.detailHeight;\n * if (expandedRowBottom > scrollTop && expandedRowIndex < start) {\n * return expandedRowIndex;\n * }\n * }\n * return start;\n * }\n * ```\n */\n adjustVirtualStart?(start: number, scrollTop: number, rowHeight: number): number;\n\n /**\n * Render a custom row, bypassing the default row rendering.\n * Use this for special row types like group headers, detail rows, or footers.\n *\n * @param row - The row data object\n * @param rowEl - The row DOM element to render into\n * @param rowIndex - The index of the row in the data array\n * @returns `true` if the plugin handled rendering (prevents default), `false`/`void` for default rendering\n *\n * @example\n * ```ts\n * renderRow(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void {\n * if (row._isGroupHeader) {\n * rowEl.innerHTML = `<div class=\"group-header\">${row._groupLabel}</div>`;\n * return true; // Handled - skip default rendering\n * }\n * // Return void to let default rendering proceed\n * }\n * ```\n */\n renderRow?(row: any, rowEl: HTMLElement, rowIndex: number): boolean | void;\n\n // #endregion\n\n // #region Inter-Plugin Communication\n\n /**\n * Handle queries from other plugins.\n * This is the generic mechanism for inter-plugin communication.\n * Plugins can respond to well-known query types or define their own.\n *\n * @param query - The query object with type and context\n * @returns Query-specific response, or undefined if not handling this query\n *\n * @example\n * ```ts\n * onPluginQuery(query: PluginQuery): unknown {\n * switch (query.type) {\n * case PLUGIN_QUERIES.CAN_MOVE_COLUMN:\n * // Prevent moving pinned columns\n * const column = query.context as ColumnConfig;\n * if (column.sticky === 'left' || column.sticky === 'right') {\n * return false;\n * }\n * break;\n * case PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS:\n * const params = query.context as ContextMenuParams;\n * return [{ id: 'my-action', label: 'My Action', action: () => {} }];\n * }\n * }\n * ```\n */\n onPluginQuery?(query: PluginQuery): unknown;\n\n // #endregion\n\n // #region Interaction Hooks\n\n /**\n * Handle keyboard events on the grid.\n * Called when a key is pressed while the grid or a cell has focus.\n *\n * @param event - The native KeyboardEvent\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onKeyDown(event: KeyboardEvent): boolean | void {\n * // Handle Ctrl+A for select all\n * if (event.ctrlKey && event.key === 'a') {\n * this.selectAllRows();\n * return true; // Prevent default browser select-all\n * }\n * }\n * ```\n */\n onKeyDown?(event: KeyboardEvent): boolean | void;\n\n /**\n * Handle cell click events.\n * Called when a data cell is clicked (not headers).\n *\n * @param event - Cell click event with row/column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onCellClick(event: CellClickEvent): boolean | void {\n * if (event.field === '_select') {\n * this.toggleRowSelection(event.rowIndex);\n * return true; // Handled\n * }\n * }\n * ```\n */\n onCellClick?(event: CellClickEvent): boolean | void;\n\n /**\n * Handle row click events.\n * Called when any part of a data row is clicked.\n * Note: This is called in addition to onCellClick, not instead of.\n *\n * @param event - Row click event with row context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onRowClick(event: RowClickEvent): boolean | void {\n * if (this.config.mode === 'row') {\n * this.selectRow(event.rowIndex, event.originalEvent);\n * return true;\n * }\n * }\n * ```\n */\n onRowClick?(event: RowClickEvent): boolean | void;\n\n /**\n * Handle header click events.\n * Called when a column header is clicked. Commonly used for sorting.\n *\n * @param event - Header click event with column context\n * @returns `true` to prevent default behavior and stop propagation, `false`/`void` to allow default\n *\n * @example\n * ```ts\n * onHeaderClick(event: HeaderClickEvent): boolean | void {\n * if (event.column.sortable !== false) {\n * this.toggleSort(event.field);\n * return true;\n * }\n * }\n * ```\n */\n onHeaderClick?(event: HeaderClickEvent): boolean | void;\n\n /**\n * Handle scroll events on the grid viewport.\n * Called during scrolling. Note: This may be called frequently; debounce if needed.\n *\n * @param event - Scroll event with scroll position and viewport dimensions\n *\n * @example\n * ```ts\n * onScroll(event: ScrollEvent): void {\n * // Update sticky column positions\n * this.updateStickyPositions(event.scrollLeft);\n * }\n * ```\n */\n onScroll?(event: ScrollEvent): void;\n\n /**\n * Handle cell mousedown events.\n * Used for initiating drag operations like range selection or column resize.\n *\n * @param event - Mouse event with cell context\n * @returns `true` to indicate drag started (prevents text selection), `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseDown(event: CellMouseEvent): boolean | void {\n * if (event.rowIndex !== undefined && this.config.mode === 'range') {\n * this.startDragSelection(event.rowIndex, event.colIndex);\n * return true; // Prevent text selection\n * }\n * }\n * ```\n */\n onCellMouseDown?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mousemove events during drag operations.\n * Only called when a drag is in progress (after mousedown returned true).\n *\n * @param event - Mouse event with current cell context\n * @returns `true` to continue handling the drag, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseMove(event: CellMouseEvent): boolean | void {\n * if (this.isDragging && event.rowIndex !== undefined) {\n * this.extendSelection(event.rowIndex, event.colIndex);\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseMove?(event: CellMouseEvent): boolean | void;\n\n /**\n * Handle cell mouseup events to end drag operations.\n *\n * @param event - Mouse event with final cell context\n * @returns `true` if drag was finalized, `false`/`void` otherwise\n *\n * @example\n * ```ts\n * onCellMouseUp(event: CellMouseEvent): boolean | void {\n * if (this.isDragging) {\n * this.finalizeDragSelection();\n * this.isDragging = false;\n * return true;\n * }\n * }\n * ```\n */\n onCellMouseUp?(event: CellMouseEvent): boolean | void;\n\n // Note: Context menu items are now provided via onPluginQuery with PLUGIN_QUERIES.GET_CONTEXT_MENU_ITEMS\n // This keeps the core decoupled from the context-menu plugin specifics.\n\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Contribute plugin-specific state for a column.\n * Called by the grid when collecting column state for serialization.\n * Plugins can add their own properties to the column state.\n *\n * @param field - The field name of the column\n * @returns Partial column state with plugin-specific properties, or undefined if no state to contribute\n *\n * @example\n * ```ts\n * getColumnState(field: string): Partial<ColumnState> | undefined {\n * const filterModel = this.filterModels.get(field);\n * if (filterModel) {\n * // Uses module augmentation to add filter property to ColumnState\n * return { filter: filterModel } as Partial<ColumnState>;\n * }\n * return undefined;\n * }\n * ```\n */\n getColumnState?(field: string): Partial<ColumnState> | undefined;\n\n /**\n * Apply plugin-specific state to a column.\n * Called by the grid when restoring column state from serialized data.\n * Plugins should restore their internal state based on the provided state.\n *\n * @param field - The field name of the column\n * @param state - The column state to apply (may contain plugin-specific properties)\n *\n * @example\n * ```ts\n * applyColumnState(field: string, state: ColumnState): void {\n * // Check for filter property added via module augmentation\n * const filter = (state as any).filter;\n * if (filter) {\n * this.filterModels.set(field, filter);\n * this.applyFilter();\n * }\n * }\n * ```\n */\n applyColumnState?(field: string, state: ColumnState): void;\n\n // #endregion\n\n // #region Scroll Boundary Hooks\n\n /**\n * Report horizontal scroll boundary offsets for this plugin.\n * Plugins that obscure part of the scroll area (e.g., pinned/sticky columns)\n * should return how much space they occupy on each side.\n * The keyboard navigation uses this to ensure focused cells are fully visible.\n *\n * @param rowEl - The row element (optional, for calculating widths from rendered cells)\n * @param focusedCell - The currently focused cell element (optional, to determine if scrolling should be skipped)\n * @returns Object with left/right pixel offsets and optional skipScroll flag, or undefined if plugin has no offsets\n *\n * @example\n * ```ts\n * getHorizontalScrollOffsets(rowEl?: HTMLElement, focusedCell?: HTMLElement): { left: number; right: number; skipScroll?: boolean } | undefined {\n * // Calculate total width of left-pinned columns\n * const leftCells = rowEl?.querySelectorAll('.sticky-left') ?? [];\n * let left = 0;\n * leftCells.forEach(el => { left += (el as HTMLElement).offsetWidth; });\n * // Skip scroll if focused cell is pinned (always visible)\n * const skipScroll = focusedCell?.classList.contains('sticky-left');\n * return { left, right: 0, skipScroll };\n * }\n * ```\n */\n getHorizontalScrollOffsets?(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } | undefined;\n\n // #endregion\n\n // #region Shell Integration Hooks\n\n /**\n * Register a tool panel for this plugin.\n * Return undefined if plugin has no tool panel.\n * The shell will create a toolbar toggle button and render the panel content\n * when the user opens the panel.\n *\n * @returns Tool panel definition, or undefined if plugin has no panel\n *\n * @example\n * ```ts\n * getToolPanel(): ToolPanelDefinition | undefined {\n * return {\n * id: 'columns',\n * title: 'Columns',\n * icon: '☰',\n * tooltip: 'Show/hide columns',\n * order: 10,\n * render: (container) => {\n * this.renderColumnList(container);\n * return () => this.cleanup();\n * },\n * };\n * }\n * ```\n */\n getToolPanel?(): ToolPanelDefinition | undefined;\n\n /**\n * Register content for the shell header center section.\n * Return undefined if plugin has no header content.\n * Examples: search input, selection summary, status indicators.\n *\n * @returns Header content definition, or undefined if plugin has no header content\n *\n * @example\n * ```ts\n * getHeaderContent(): HeaderContentDefinition | undefined {\n * return {\n * id: 'quick-filter',\n * order: 10,\n * render: (container) => {\n * const input = document.createElement('input');\n * input.type = 'text';\n * input.placeholder = 'Search...';\n * input.addEventListener('input', this.handleInput);\n * container.appendChild(input);\n * return () => input.removeEventListener('input', this.handleInput);\n * },\n * };\n * }\n * ```\n */\n getHeaderContent?(): HeaderContentDefinition | undefined;\n\n // #endregion\n}\n","/**\n * Grid DOM Constants\n *\n * Centralized constants for CSS classes, data attributes, and selectors\n * used throughout the grid. Use these instead of magic strings.\n *\n * Note: Some constants here are used by plugins but defined in core because:\n * 1. The core CSS needs to style these elements (e.g., sticky positioning)\n * 2. Multiple plugins may share the same class names\n *\n * Plugins can define their own additional constants and export them.\n * See each plugin's index.ts for plugin-specific exports.\n */\n\n// #region CSS Classes\n\n/**\n * CSS class names used in the grid's shadow DOM.\n * Use these when adding/removing classes or querying elements.\n *\n * Classes are organized by:\n * - Core: Used by the grid core for structure and basic functionality\n * - Shared: Used by multiple features/plugins, styled by core CSS\n */\nexport const GridClasses = {\n // ─── Core Structure ───────────────────────────────────────────────\n ROOT: 'tbw-grid-root',\n HEADER: 'header',\n HEADER_ROW: 'header-row',\n HEADER_CELL: 'header-cell',\n\n // Body structure\n ROWS_VIEWPORT: 'rows-viewport',\n ROWS_SPACER: 'rows-spacer',\n ROWS_CONTAINER: 'rows',\n\n // Row elements\n DATA_ROW: 'data-row',\n GROUP_ROW: 'group-row',\n\n // Cell elements\n DATA_CELL: 'data-cell',\n\n // ─── Core States ──────────────────────────────────────────────────\n SELECTED: 'selected',\n FOCUSED: 'focused',\n EDITING: 'editing',\n EXPANDED: 'expanded',\n COLLAPSED: 'collapsed',\n DRAGGING: 'dragging',\n RESIZING: 'resizing',\n\n // Sorting (core feature)\n SORTABLE: 'sortable',\n SORTED_ASC: 'sorted-asc',\n SORTED_DESC: 'sorted-desc',\n\n // Visibility\n HIDDEN: 'hidden',\n\n // ─── Shared Classes (used by plugins, styled by core CSS) ────────\n // These are defined here because core CSS provides the base styling.\n // Plugins apply these classes; core CSS defines how they look.\n\n // Sticky positioning (PinnedColumnsPlugin applies, core CSS styles)\n STICKY_LEFT: 'sticky-left',\n STICKY_RIGHT: 'sticky-right',\n\n // Pinned rows (PinnedRowsPlugin applies, core CSS styles)\n PINNED_TOP: 'pinned-top',\n PINNED_BOTTOM: 'pinned-bottom',\n\n // Tree structure (TreePlugin applies, core CSS styles)\n TREE_TOGGLE: 'tree-toggle',\n TREE_INDENT: 'tree-indent',\n\n // Grouping (GroupingRowsPlugin applies, core CSS styles)\n GROUP_TOGGLE: 'group-toggle',\n GROUP_LABEL: 'group-label',\n GROUP_COUNT: 'group-count',\n\n // Selection (SelectionPlugin applies, core CSS styles)\n RANGE_SELECTION: 'range-selection',\n SELECTION_OVERLAY: 'selection-overlay',\n} as const;\n\n// #endregion\n\n// #region Data Attributes\n\n/**\n * Data attribute names used on grid elements.\n * Use these when getting/setting data attributes.\n */\nexport const GridDataAttrs = {\n // ─── Core Attributes ──────────────────────────────────────────────\n ROW_INDEX: 'data-row-index',\n COL_INDEX: 'data-col-index',\n FIELD: 'data-field',\n\n // ─── Shared Attributes (used by plugins) ──────────────────────────\n GROUP_KEY: 'data-group-key', // GroupingRowsPlugin\n TREE_LEVEL: 'data-tree-level', // TreePlugin\n STICKY: 'data-sticky', // PinnedColumnsPlugin\n} as const;\n\n// #endregion\n\n// #region Selectors\n\n/**\n * Common CSS selectors for querying grid elements.\n * Built from the class constants for consistency.\n */\nexport const GridSelectors = {\n ROOT: `.${GridClasses.ROOT}`,\n HEADER: `.${GridClasses.HEADER}`,\n HEADER_ROW: `.${GridClasses.HEADER_ROW}`,\n HEADER_CELL: `.${GridClasses.HEADER_CELL}`,\n ROWS_VIEWPORT: `.${GridClasses.ROWS_VIEWPORT}`,\n ROWS_CONTAINER: `.${GridClasses.ROWS_CONTAINER}`,\n DATA_ROW: `.${GridClasses.DATA_ROW}`,\n DATA_CELL: `.${GridClasses.DATA_CELL}`,\n GROUP_ROW: `.${GridClasses.GROUP_ROW}`,\n\n // By data attribute\n ROW_BY_INDEX: (index: number) => `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${index}\"]`,\n CELL_BY_FIELD: (field: string) => `.${GridClasses.DATA_CELL}[${GridDataAttrs.FIELD}=\"${field}\"]`,\n CELL_AT: (row: number, col: number) =>\n `.${GridClasses.DATA_ROW}[${GridDataAttrs.ROW_INDEX}=\"${row}\"] .${GridClasses.DATA_CELL}[${GridDataAttrs.COL_INDEX}=\"${col}\"]`,\n\n // State selectors\n SELECTED_ROWS: `.${GridClasses.DATA_ROW}.${GridClasses.SELECTED}`,\n EDITING_CELL: `.${GridClasses.DATA_CELL}.${GridClasses.EDITING}`,\n} as const;\n\n// #endregion\n\n// #region CSS Custom Properties\n\n/**\n * CSS custom property names for theming.\n * Use these when programmatically setting styles.\n */\nexport const GridCSSVars = {\n // Colors\n COLOR_BG: '--tbw-color-bg',\n COLOR_FG: '--tbw-color-fg',\n COLOR_FG_MUTED: '--tbw-color-fg-muted',\n COLOR_BORDER: '--tbw-color-border',\n COLOR_ACCENT: '--tbw-color-accent',\n COLOR_HEADER_BG: '--tbw-color-header-bg',\n COLOR_HEADER_FG: '--tbw-color-header-fg',\n COLOR_SELECTION: '--tbw-color-selection',\n COLOR_ROW_HOVER: '--tbw-color-row-hover',\n COLOR_ROW_ALT: '--tbw-color-row-alt',\n\n // Sizing\n ROW_HEIGHT: '--tbw-row-height',\n HEADER_HEIGHT: '--tbw-header-height',\n CELL_PADDING: '--tbw-cell-padding',\n\n // Typography\n FONT_FAMILY: '--tbw-font-family',\n FONT_SIZE: '--tbw-font-size',\n\n // Borders\n BORDER_RADIUS: '--tbw-border-radius',\n FOCUS_OUTLINE: '--tbw-focus-outline',\n} as const;\n\n// #endregion\n\n// Type helpers\nexport type GridClassName = (typeof GridClasses)[keyof typeof GridClasses];\nexport type GridDataAttr = (typeof GridDataAttrs)[keyof typeof GridDataAttrs];\nexport type GridCSSVar = (typeof GridCSSVars)[keyof typeof GridCSSVars];\n","// #region Public API surface - only export what consumers need\nexport { DataGridElement, DataGridElement as GridElement } from './lib/core/grid';\n\n// Event name constants for DataGrid (public API)\nexport const DGEvents = {\n CELL_COMMIT: 'cell-commit',\n ROW_COMMIT: 'row-commit',\n CHANGED_ROWS_RESET: 'changed-rows-reset',\n MOUNT_EXTERNAL_VIEW: 'mount-external-view',\n MOUNT_EXTERNAL_EDITOR: 'mount-external-editor',\n SORT_CHANGE: 'sort-change',\n COLUMN_RESIZE: 'column-resize',\n ACTIVATE_CELL: 'activate-cell',\n GROUP_TOGGLE: 'group-toggle',\n COLUMN_STATE_CHANGE: 'column-state-change',\n} as const;\n\nexport type DGEventName = (typeof DGEvents)[keyof typeof DGEvents];\n\n// Plugin event constants (mirrors DGEvents pattern)\nexport const PluginEvents = {\n // Selection plugin\n SELECTION_CHANGE: 'selection-change',\n // Tree plugin\n TREE_EXPAND: 'tree-expand',\n // Filtering plugin\n FILTER_CHANGE: 'filter-change',\n // Sorting plugin\n SORT_MODEL_CHANGE: 'sort-model-change',\n // Export plugin\n EXPORT_START: 'export-start',\n EXPORT_COMPLETE: 'export-complete',\n // Clipboard plugin\n CLIPBOARD_COPY: 'clipboard-copy',\n CLIPBOARD_PASTE: 'clipboard-paste',\n // Context menu plugin\n CONTEXT_MENU_OPEN: 'context-menu-open',\n CONTEXT_MENU_CLOSE: 'context-menu-close',\n // Undo/Redo plugin\n HISTORY_CHANGE: 'history-change',\n // Server-side plugin\n SERVER_LOADING: 'server-loading',\n SERVER_ERROR: 'server-error',\n // Visibility plugin\n COLUMN_VISIBILITY_CHANGE: 'column-visibility-change',\n // Reorder plugin\n COLUMN_REORDER: 'column-reorder',\n // Master-detail plugin\n DETAIL_EXPAND: 'detail-expand',\n // Grouping rows plugin\n GROUP_EXPAND: 'group-expand',\n} as const;\n\nexport type PluginEventName = (typeof PluginEvents)[keyof typeof PluginEvents];\n\n// Public type exports\nexport type {\n ActivateCellDetail,\n AggregatorRef,\n // Animation types\n AnimationConfig,\n AnimationMode,\n AnimationStyle,\n BaseColumnConfig,\n // Event detail types\n CellCommitDetail,\n CellRenderContext,\n ChangedRowsResetDetail,\n ColumnConfig,\n ColumnConfigMap,\n ColumnEditorContext,\n // Column features\n ColumnEditorSpec,\n ColumnResizeDetail,\n // Column state types\n ColumnSortState,\n ColumnState,\n ColumnViewRenderer,\n DataGridCustomEvent,\n DataGridElement as DataGridElementInterface,\n DataGridEventMap,\n ExpandCollapseAnimation,\n ExternalMountEditorDetail,\n ExternalMountViewDetail,\n FitMode,\n // Framework adapter interface\n FrameworkAdapter,\n GridColumnState,\n // Core configuration types\n GridConfig,\n // Icons\n GridIcons,\n // Plugin interface (minimal shape for type-checking)\n GridPlugin,\n // Shell types\n HeaderContentDefinition,\n IconValue,\n // Inference types\n InferredColumnResult,\n PrimitiveColumnType,\n // Public interface\n PublicGrid,\n RowCommitDetail,\n // Grouping & Footer types\n RowGroupRenderConfig,\n ShellConfig,\n ShellHeaderConfig,\n SortChangeDetail,\n // Sorting types\n SortHandler,\n SortState,\n ToolPanelConfig,\n ToolPanelDefinition,\n ToolbarButtonConfig,\n} from './lib/core/types';\n\n// Re-export FitModeEnum for runtime usage\nexport { DEFAULT_ANIMATION_CONFIG, DEFAULT_GRID_ICONS, FitModeEnum } from './lib/core/types';\n\n// Re-export sorting utilities for custom sort handlers\nexport { builtInSort, defaultComparator } from './lib/core/internal/sorting';\n// #endregion\n\n// #region Plugin Types\n// Only export types that consumers need to use plugins\n// Plugin classes are available via @toolbox-web/grid/plugins/<name> or from 'all.ts'\n\n// Selection plugin\nexport type { CellRange, SelectionChangeDetail, SelectionConfig, SelectionMode } from './lib/plugins/selection/types';\n\n// Tree plugin\nexport type { TreeConfig, TreeExpandDetail } from './lib/plugins/tree/types';\n\n// Filtering plugin\nexport type {\n FilterConfig,\n FilterHandler,\n FilterModel,\n FilterOperator,\n FilterType,\n FilterValuesHandler,\n} from './lib/plugins/filtering/types';\n\n// Multi-sort plugin\nexport type { MultiSortConfig, SortModel } from './lib/plugins/multi-sort/types';\n\n// Export plugin\nexport type { ExportFormat, ExportParams } from './lib/plugins/export/types';\n\n// Pinned rows plugin\nexport type { PinnedRowsContext, PinnedRowsPanel } from './lib/plugins/pinned-rows/types';\n\n// Pivot plugin\nexport type { PivotConfig, PivotResult, PivotValueField } from './lib/plugins/pivot/types';\n\n// Server-side plugin\nexport type { GetRowsParams, GetRowsResult, ServerSideDataSource } from './lib/plugins/server-side/types';\n\n// Undo/Redo plugin\nexport type { EditAction } from './lib/plugins/undo-redo/types';\n\n// Grouping rows plugin\nexport type { GroupingRowsConfig } from './lib/plugins/grouping-rows/types';\n\n// Plugin base class - for creating custom plugins\nexport { BaseGridPlugin, PLUGIN_QUERIES } from './lib/core/plugin';\nexport type { PluginQuery } from './lib/core/plugin';\n\n// DOM constants - for querying grid elements and styling\nexport { GridCSSVars, GridClasses, GridDataAttrs, GridSelectors } from './lib/core/constants';\nexport type { GridCSSVar, GridClassName, GridDataAttr } from './lib/core/constants';\n// #endregion\n","/**\n * 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 includeHeaders: false,\n delimiter: '\\t',\n newline: '\\n',\n quoteStrings: false,\n };\n }\n\n // #region Internal State\n /** The last copied text (for reference/debugging) */\n private lastCopied: { text: string; timestamp: number } | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.lastCopied = null;\n }\n // #endregion\n\n // #region Event Handlers\n\n override onKeyDown(event: KeyboardEvent): boolean {\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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n}\n\n// #region 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// #endregion\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 autoEnable: true,\n threshold: 30,\n overscan: 3,\n };\n }\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region Hooks\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n const isVirtualized = 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 // #endregion\n\n // #region 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 // #endregion\n}\n","/**\n * Context Menu Rendering Logic\n *\n * Pure functions for building and positioning context menus.\n */\n\nimport type { IconValue } from '../../core/types';\nimport { DEFAULT_GRID_ICONS } from '../../core/types';\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 * @param submenuArrow - Optional custom submenu arrow icon\n * @returns The created menu element\n */\nexport function createMenuElement(\n items: ContextMenuItem[],\n params: ContextMenuParams,\n onAction: (item: ContextMenuItem) => void,\n submenuArrow: IconValue = DEFAULT_GRID_ICONS.submenuArrow\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 // Use provided submenu arrow icon (string or HTMLElement)\n if (typeof submenuArrow === 'string') {\n arrow.innerHTML = submenuArrow;\n } else if (submenuArrow instanceof HTMLElement) {\n arrow.appendChild(submenuArrow.cloneNode(true));\n }\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, submenuArrow);\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 contextMenuStyles from './context-menu.css?inline';\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/** Reference count for instances using global handlers */\nlet globalHandlerRefCount = 0;\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 items: defaultItems,\n };\n }\n\n // #region 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 // #endregion\n\n // #region Lifecycle\n\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\n super.attach(grid);\n this.installGlobalHandlers();\n globalHandlerRefCount++;\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 this.uninstallGlobalHandlers();\n }\n // #endregion\n\n // #region Private Methods\n\n private installGlobalHandlers(): void {\n // Inject global stylesheet for context menu (once)\n // Only inject if we have valid CSS text (Vite's ?inline import)\n // When importing from source without Vite, the import is a module object, not a string\n if (\n !globalStyleSheet &&\n typeof document !== 'undefined' &&\n typeof contextMenuStyles === 'string' &&\n contextMenuStyles\n ) {\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 /**\n * Clean up global handlers when the last instance detaches.\n * Uses reference counting to ensure handlers persist while any grid uses the plugin.\n */\n private uninstallGlobalHandlers(): void {\n globalHandlerRefCount--;\n if (globalHandlerRefCount > 0) return;\n\n // Last instance - clean up all global resources\n if (globalClickHandler) {\n document.removeEventListener('click', globalClickHandler);\n globalClickHandler = null;\n }\n if (globalKeydownHandler) {\n document.removeEventListener('keydown', globalKeydownHandler);\n globalKeydownHandler = null;\n }\n if (globalStyleSheet) {\n globalStyleSheet.remove();\n globalStyleSheet = null;\n }\n }\n // #endregion\n\n // #region Hooks\n\n override afterRender(): void {\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 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(\n items,\n params,\n (item) => {\n if (item.action) {\n item.action(params);\n }\n this.menuElement?.remove();\n this.menuElement = null;\n this.isOpen = false;\n },\n this.gridIcons.submenuArrow,\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 // #endregion\n\n // #region 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(\n items,\n fullParams,\n (item) => {\n if (item.action) item.action(fullParams);\n this.menuElement?.remove();\n this.menuElement = null;\n this.isOpen = false;\n },\n this.gridIcons.submenuArrow,\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 // #endregion\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 fileName: 'export',\n includeHeaders: true,\n onlyVisible: true,\n onlySelected: false,\n };\n }\n\n // #region Internal State\n private isExportingFlag = false;\n private lastExportInfo: { format: ExportFormat; timestamp: Date } | null = null;\n // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\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 * 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 { computeVirtualWindow, shouldBypassVirtualization } from '../../core/internal/virtualization';\nimport { BaseGridPlugin, type GridElement } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, ColumnState } from '../../core/types';\nimport { computeFilterCacheKey, filterRows, getUniqueValues } from './filter-model';\nimport styles from './filtering.css?inline';\nimport filterPanelStyles from './FilteringPlugin.css?inline';\nimport type { FilterChangeDetail, FilterConfig, FilterModel, FilterPanelParams } from './types';\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 debounceMs: 300,\n caseSensitive: false,\n trimInput: true,\n useWorker: true,\n };\n }\n\n // #region 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 panelAbortController: AbortController | null = null; // For panel-scoped listeners\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 // #endregion\n\n // #region 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 // Abort panel-scoped listeners (document click handler, etc.)\n this.panelAbortController?.abort();\n this.panelAbortController = null;\n }\n // #endregion\n\n // #region Hooks\n\n override processRows(rows: readonly unknown[]): unknown[] {\n const filterList = [...this.filters.values()];\n if (!filterList.length) return [...rows];\n\n // If using async filterHandler, processRows becomes a passthrough\n // Actual filtering happens in applyFiltersAsync and rows are set directly on grid\n if (this.config.filterHandler) {\n // Return cached result if available (set by async handler)\n if (this.cachedResult) return this.cachedResult;\n // Otherwise return rows as-is (filtering happens async)\n return [...rows];\n }\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 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 // Use visibleColumns since data-col is the index within _visibleColumns\n const col = this.visibleColumns[parseInt(colIndex, 10)] as ColumnConfig;\n if (!col || col.filterable === false) return;\n\n const field = col.field;\n if (!field) return;\n\n const hasFilter = this.filters.has(field);\n\n // Check if button already exists\n let filterBtn = cell.querySelector('.tbw-filter-btn') as HTMLElement | null;\n\n if (filterBtn) {\n // Update active state of existing button\n filterBtn.classList.toggle('active', hasFilter);\n (cell as HTMLElement).classList.toggle('filtered', hasFilter);\n return;\n }\n\n // Create filter button\n 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 (hasFilter) {\n filterBtn.classList.add('active');\n (cell as HTMLElement).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 // #endregion\n\n // #region 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 this.excludedValues.delete(field);\n } else {\n this.filters.set(field, { ...filter, field });\n // Sync excludedValues for set filters so the panel reflects correct state\n if (filter.type === 'set' && filter.operator === 'notIn' && Array.isArray(filter.value)) {\n this.excludedValues.set(field, new Set(filter.value));\n } else if (filter.type === 'set') {\n // Other set operators may have different semantics; clear for safety\n this.excludedValues.delete(field);\n }\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 this.excludedValues.clear();\n for (const filter of filters) {\n this.filters.set(filter.field, filter);\n // Sync excludedValues for set filters so the panel reflects correct state\n if (filter.type === 'set' && filter.operator === 'notIn' && Array.isArray(filter.value)) {\n this.excludedValues.set(filter.field, new Set(filter.value));\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 * Clear all filters.\n */\n clearAllFilters(): void {\n this.filters.clear();\n this.excludedValues.clear();\n this.searchText.clear();\n\n this.applyFiltersInternal();\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.applyFiltersInternal();\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 // #endregion\n\n // #region 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 // Only inject if we have valid CSS text (Vite's ?inline import)\n // When importing from source without Vite, the import is a module object, not a string\n if (typeof filterPanelStyles !== 'string' || !filterPanelStyles) {\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 // If using async valuesHandler, show loading state and fetch values\n if (this.config.valuesHandler) {\n panel.innerHTML = '<div class=\"tbw-filter-loading\">Loading...</div>';\n document.body.appendChild(panel);\n this.positionPanel(panel, buttonEl);\n this.setupPanelCloseHandler(panel, buttonEl);\n\n this.config.valuesHandler(field, column).then((values) => {\n // Check if panel is still open for this field\n if (this.openPanelField !== field || !this.panelElement) return;\n panel.innerHTML = '';\n this.renderPanelContent(field, column, panel, values);\n });\n return;\n }\n\n // Sync path: get unique values from local rows\n const uniqueValues = getUniqueValues(this.sourceRows as Record<string, unknown>[], field);\n this.renderPanelContent(field, column, panel, uniqueValues);\n\n // Position and append to body\n document.body.appendChild(panel);\n this.positionPanel(panel, buttonEl);\n this.setupPanelCloseHandler(panel, buttonEl);\n }\n\n /**\n * Render filter panel content with given values\n */\n private renderPanelContent(field: string, column: ColumnConfig, panel: HTMLElement, uniqueValues: unknown[]): void {\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 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\n /**\n * Setup click-outside handler to close the panel\n */\n private setupPanelCloseHandler(panel: HTMLElement, buttonEl: HTMLElement): void {\n // Create abort controller for panel-scoped listeners\n // This allows cleanup when panel closes OR when grid disconnects\n this.panelAbortController = new AbortController();\n\n // Add global click handler to close on outside click\n // Defer to next tick to avoid immediate close from the click that opened the panel\n setTimeout(() => {\n document.addEventListener(\n 'click',\n (e: MouseEvent) => {\n if (!panel.contains(e.target as Node) && e.target !== buttonEl) {\n this.closeFilterPanel();\n }\n },\n { signal: this.panelAbortController?.signal },\n );\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 // Abort panel-scoped listeners (document click handler)\n this.panelAbortController?.abort();\n this.panelAbortController = null;\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.applyFiltersInternal();\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.applyFiltersInternal();\n }\n\n /**\n * Internal method to apply filters (sync or async based on config)\n */\n private applyFiltersInternal(): void {\n this.cachedResult = null;\n this.cacheKey = null;\n\n const filterList = [...this.filters.values()];\n\n // If using async filterHandler, delegate to server\n if (this.config.filterHandler) {\n const gridEl = this.grid as unknown as Element;\n gridEl.setAttribute('aria-busy', 'true');\n\n const result = this.config.filterHandler(filterList, this.sourceRows as unknown[]);\n\n // Handle async or sync result\n const handleResult = (rows: unknown[]) => {\n gridEl.removeAttribute('aria-busy');\n this.cachedResult = rows;\n\n // Update grid rows directly for async filtering\n (this.grid as unknown as { rows: unknown[] }).rows = rows;\n\n this.emit<FilterChangeDetail>('filter-change', {\n filters: filterList,\n filteredRowCount: rows.length,\n });\n\n // Trigger afterRender to update filter button active state\n this.requestRender();\n };\n\n if (result && typeof (result as Promise<unknown[]>).then === 'function') {\n (result as Promise<unknown[]>).then(handleResult);\n } else {\n handleResult(result as unknown[]);\n }\n return;\n }\n\n // Sync path: emit event and re-render (processRows will handle filtering)\n this.emit<FilterChangeDetail>('filter-change', {\n filters: filterList,\n filteredRowCount: 0,\n });\n this.requestRender();\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\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 styles from './grouping-columns.css?inline';\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 showGroupBorders: true,\n };\n }\n\n // #region Internal State\n private groups: ColumnGroup[] = [];\n private isActive = false;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.groups = [];\n this.isActive = false;\n }\n // #endregion\n\n // #region Static Detection\n\n /**\n * Auto-detect column groups from column configuration.\n * Detects both inline `column.group` properties and declarative `columnGroups` config.\n */\n static detect(rows: readonly any[], config: any): boolean {\n // Check for declarative columnGroups in config\n if (config?.columnGroups && Array.isArray(config.columnGroups) && config.columnGroups.length > 0) {\n return true;\n }\n // Check for inline group properties on columns\n const columns = config?.columns;\n if (!Array.isArray(columns)) return false;\n return hasColumnGroups(columns);\n }\n // #endregion\n\n // #region Hooks\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n // First, check if gridConfig.columnGroups is defined and apply to columns\n const columnGroups = this.grid?.gridConfig?.columnGroups;\n let processedColumns: ColumnConfig[];\n\n if (columnGroups && Array.isArray(columnGroups) && columnGroups.length > 0) {\n // Build a map of field -> group info from the declarative config\n const fieldToGroup = new Map<string, { id: string; label: string }>();\n for (const group of columnGroups) {\n for (const field of group.children) {\n fieldToGroup.set(field, { id: group.id, label: group.header });\n }\n }\n\n // Apply group property to columns that don't already have one\n processedColumns = columns.map((col) => {\n const groupInfo = fieldToGroup.get(col.field);\n if (groupInfo && !col.group) {\n return { ...col, group: groupInfo };\n }\n return col;\n });\n } else {\n processedColumns = [...columns];\n }\n\n // Compute groups from column definitions (now including applied groups)\n const groups = computeColumnGroups(processedColumns);\n\n if (groups.length === 0) {\n this.isActive = false;\n this.groups = [];\n return processedColumns;\n }\n\n this.isActive = true;\n this.groups = groups;\n\n // Return columns with group info applied\n return processedColumns;\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 // Toggle border visibility class\n groupRow.classList.toggle('no-borders', !this.config.showGroupBorders);\n\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 // Toggle border visibility on header cells\n headerRow.classList.toggle('no-group-borders', !this.config.showGroupBorders);\n applyGroupedHeaderCellClasses(headerRow, this.groups, this.columns as ColumnConfig[]);\n }\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\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// #region Value-based Aggregators\n// Used by plugins like Pivot that work with pre-extracted numeric values\n\nexport type ValueAggregatorFn = (values: number[]) => number;\n\n/**\n * Built-in value-based aggregators.\n * These operate on arrays of numbers (unlike row-based aggregators).\n */\nconst builtInValueAggregators: Record<string, ValueAggregatorFn> = {\n sum: (vals) => vals.reduce((a, b) => a + b, 0),\n avg: (vals) => (vals.length ? vals.reduce((a, b) => a + b, 0) / vals.length : 0),\n count: (vals) => vals.length,\n min: (vals) => (vals.length ? Math.min(...vals) : 0),\n max: (vals) => (vals.length ? Math.max(...vals) : 0),\n first: (vals) => vals[0] ?? 0,\n last: (vals) => vals[vals.length - 1] ?? 0,\n};\n\n/**\n * Get a value-based aggregator function.\n * Used by Pivot plugin and other features that aggregate pre-extracted values.\n *\n * @param aggFunc - Aggregation function name ('sum', 'avg', 'count', 'min', 'max', 'first', 'last')\n * @returns Aggregator function that takes number[] and returns number\n */\nexport function getValueAggregator(aggFunc: string): ValueAggregatorFn {\n return builtInValueAggregators[aggFunc] ?? builtInValueAggregators.sum;\n}\n\n/**\n * Run a value-based aggregator on a set of values.\n *\n * @param aggFunc - Aggregation function name\n * @param values - Array of numbers to aggregate\n * @returns Aggregated result\n */\nexport function runValueAggregator(aggFunc: string, values: number[]): number {\n return getValueAggregator(aggFunc)(values);\n}\n// #endregion\n\n// Legacy function exports for backward compatibility\nexport const registerAggregator = aggregatorRegistry.register.bind(aggregatorRegistry);\nexport const unregisterAggregator = aggregatorRegistry.unregister.bind(aggregatorRegistry);\nexport const getAggregator = aggregatorRegistry.get.bind(aggregatorRegistry);\nexport const runAggregator = aggregatorRegistry.run.bind(aggregatorRegistry);\nexport const listAggregators = aggregatorRegistry.list.bind(aggregatorRegistry);\n","/**\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 { getAggregator, listAggregators, registerAggregator, runAggregator } 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 groupOn not configured 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 (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 type { GridConfig } from '../../core/types';\nimport {\n buildGroupedRowModel,\n collapseAllGroups,\n expandAllGroups,\n getGroupRowCount,\n runAggregator,\n toggleGroupExpansion,\n} from './grouping-rows';\nimport styles from './grouping-rows.css?inline';\nimport type {\n ExpandCollapseAnimation,\n GroupingRowsConfig,\n GroupRowModelItem,\n GroupToggleDetail,\n RenderRow,\n} from './types';\n\ninterface GridWithConfig {\n effectiveConfig?: GridConfig;\n}\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 defaultExpanded: false,\n showRowCount: true,\n indentWidth: 20,\n aggregators: {},\n animation: 'slide',\n };\n }\n\n // #region Internal State\n private expandedKeys: Set<string> = new Set();\n private flattenedRows: RenderRow[] = [];\n private isActive = false;\n private previousVisibleKeys = new Set<string>();\n private keysToAnimate = new Set<string>();\n // #endregion\n\n // #region Animation\n\n private get animationStyle(): ExpandCollapseAnimation {\n const gridEl = this.grid as unknown as GridWithConfig;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode !== true && mode !== 'on') {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host && getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim() === '0') {\n return false;\n }\n }\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.expandedKeys.clear();\n this.flattenedRows = [];\n this.isActive = false;\n this.previousVisibleKeys.clear();\n this.keysToAnimate.clear();\n }\n // #endregion\n\n // #region 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 (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 // Track which data rows are newly visible (for animation)\n this.keysToAnimate.clear();\n const currentVisibleKeys = new Set<string>();\n grouped.forEach((item, idx) => {\n if (item.kind === 'data') {\n const key = `data-${idx}`;\n currentVisibleKeys.add(key);\n if (!this.previousVisibleKeys.has(key)) {\n this.keysToAnimate.add(key);\n }\n }\n });\n this.previousVisibleKeys = currentVisibleKeys;\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 as Record<string, unknown> | undefined;\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 as string);\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 const style = this.animationStyle;\n if (style === false || this.keysToAnimate.size === 0) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n const animClass = style === 'fade' ? 'tbw-group-fade-in' : 'tbw-group-slide-in';\n for (const rowEl of body.querySelectorAll('.data-grid-row:not(.group-row)')) {\n const cell = rowEl.querySelector('.cell[data-row]');\n const idx = cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n const item = this.flattenedRows[idx];\n const key = item?.kind === 'data' ? `data-${idx}` : undefined;\n\n if (key && this.keysToAnimate.has(key)) {\n rowEl.classList.add(animClass);\n rowEl.addEventListener('animationend', () => rowEl.classList.remove(animClass), { once: true });\n }\n }\n this.keysToAnimate.clear();\n }\n // #endregion\n\n // #region 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${row.__groupExpanded ? ' expanded' : ''}`;\n btn.setAttribute('aria-label', row.__groupExpanded ? 'Collapse group' : 'Expand group');\n this.setIcon(btn, this.resolveIcon(row.__groupExpanded ? 'collapse' : 'expand'));\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 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${row.__groupExpanded ? ' expanded' : ''}`;\n btn.setAttribute('aria-label', row.__groupExpanded ? 'Collapse group' : 'Expand group');\n this.setIcon(btn, this.resolveIcon(row.__groupExpanded ? 'collapse' : 'expand'));\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 // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\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 * Animation style is plugin-configured; respects grid-level animation.mode.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { evalTemplateString, sanitizeHTML } from '../../core/internal/sanitize';\nimport { BaseGridPlugin, GridElement, RowClickEvent } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridConfig } from '../../core/types';\nimport {\n collapseDetailRow,\n createDetailElement,\n expandDetailRow,\n isDetailExpanded,\n toggleDetailRow,\n} from './master-detail';\nimport styles from './master-detail.css?inline';\nimport type { DetailExpandDetail, ExpandCollapseAnimation, MasterDetailConfig } from './types';\n\n/** Extended grid interface for accessing effective config */\ninterface GridWithConfig {\n effectiveConfig?: GridConfig;\n}\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 detailHeight: 'auto',\n expandOnRowClick: false,\n collapseOnClickOutside: false,\n showExpandColumn: true,\n animation: 'slide', // Plugin's own default\n };\n }\n\n // #region Light DOM Parsing\n\n /**\n * Called when plugin is attached to the grid.\n * Parses light DOM for `<tbw-grid-detail>` elements to configure detail templates.\n */\n override attach(grid: GridElement): void {\n super.attach(grid);\n this.parseLightDomDetail();\n }\n\n /**\n * Parse `<tbw-grid-detail>` elements from the grid's light DOM.\n *\n * Allows declarative configuration:\n * ```html\n * <tbw-grid [rows]=\"data\">\n * <tbw-grid-detail>\n * <div class=\"detail-content\">\n * <p>Name: {{ row.name }}</p>\n * <p>Email: {{ row.email }}</p>\n * </div>\n * </tbw-grid-detail>\n * </tbw-grid>\n * ```\n *\n * Attributes:\n * - `animation`: 'slide' | 'fade' | 'false' (default: 'slide')\n * - `show-expand-column`: 'true' | 'false' (default: 'true')\n * - `expand-on-row-click`: 'true' | 'false' (default: 'false')\n * - `collapse-on-click-outside`: 'true' | 'false' (default: 'false')\n * - `height`: number (pixels) or 'auto' (default: 'auto')\n */\n private parseLightDomDetail(): void {\n const gridEl = this.grid as unknown as Element;\n if (!gridEl || typeof gridEl.querySelector !== 'function') return;\n\n const detailEl = gridEl.querySelector('tbw-grid-detail');\n if (!detailEl) return;\n\n // Check if a framework adapter wants to handle this element\n // (e.g., Angular adapter intercepts for ng-template rendering)\n const gridWithAdapter = gridEl as unknown as {\n __frameworkAdapter?: {\n parseDetailElement?: (el: Element) => ((row: any, rowIndex: number) => HTMLElement | string) | undefined;\n };\n };\n if (gridWithAdapter.__frameworkAdapter?.parseDetailElement) {\n const adapterRenderer = gridWithAdapter.__frameworkAdapter.parseDetailElement(detailEl);\n if (adapterRenderer) {\n this.config = { ...this.config, detailRenderer: adapterRenderer };\n return;\n }\n }\n\n // Parse attributes for configuration\n const animation = detailEl.getAttribute('animation');\n const showExpandColumn = detailEl.getAttribute('show-expand-column');\n const expandOnRowClick = detailEl.getAttribute('expand-on-row-click');\n const collapseOnClickOutside = detailEl.getAttribute('collapse-on-click-outside');\n const heightAttr = detailEl.getAttribute('height');\n\n const configUpdates: Partial<MasterDetailConfig> = {};\n\n if (animation !== null) {\n configUpdates.animation = animation === 'false' ? false : (animation as 'slide' | 'fade');\n }\n if (showExpandColumn !== null) {\n configUpdates.showExpandColumn = showExpandColumn !== 'false';\n }\n if (expandOnRowClick !== null) {\n configUpdates.expandOnRowClick = expandOnRowClick === 'true';\n }\n if (collapseOnClickOutside !== null) {\n configUpdates.collapseOnClickOutside = collapseOnClickOutside === 'true';\n }\n if (heightAttr !== null) {\n configUpdates.detailHeight = heightAttr === 'auto' ? 'auto' : parseInt(heightAttr, 10);\n }\n\n // Get template content from innerHTML\n const templateHTML = detailEl.innerHTML.trim();\n if (templateHTML && !this.config.detailRenderer) {\n // Create a template-based renderer using the inner HTML\n configUpdates.detailRenderer = (row: any, _rowIndex: number): string => {\n // Evaluate template expressions like {{ row.field }}\n const evaluated = evalTemplateString(templateHTML, { value: row, row });\n // Sanitize the result to prevent XSS\n return sanitizeHTML(evaluated);\n };\n }\n\n // Merge updates into config\n if (Object.keys(configUpdates).length > 0) {\n this.config = { ...this.config, ...configUpdates };\n }\n }\n\n // #endregion\n\n // #region Animation Helpers\n\n /**\n * Check if animations are enabled at the grid level.\n * Respects gridConfig.animation.mode and CSS variable.\n */\n private get isAnimationEnabled(): boolean {\n const gridEl = this.grid as unknown as GridWithConfig;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode === true || mode === 'on') return true;\n\n // reduced-motion: check CSS variable\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const enabled = getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim();\n return enabled !== '0';\n }\n return true;\n }\n\n /**\n * Get expand/collapse animation style from plugin config.\n */\n private get animationStyle(): ExpandCollapseAnimation {\n if (!this.isAnimationEnabled) return false;\n return this.config.animation ?? 'slide'; // Plugin default\n }\n\n /**\n * Get animation duration from CSS variable (set by grid).\n */\n private get animationDuration(): number {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const durationStr = getComputedStyle(host).getPropertyValue('--tbw-animation-duration').trim();\n const parsed = parseInt(durationStr, 10);\n if (!isNaN(parsed)) return parsed;\n }\n return 200; // Default\n }\n\n /**\n * Apply expand animation to a detail element.\n */\n private animateExpand(detailEl: HTMLElement): void {\n if (!this.isAnimationEnabled || this.animationStyle === false) return;\n\n detailEl.classList.add('tbw-expanding');\n detailEl.addEventListener(\n 'animationend',\n () => {\n detailEl.classList.remove('tbw-expanding');\n },\n { once: true },\n );\n }\n\n /**\n * Apply collapse animation to a detail element and remove after animation.\n */\n private animateCollapse(detailEl: HTMLElement, onComplete: () => void): void {\n if (!this.isAnimationEnabled || this.animationStyle === false) {\n onComplete();\n return;\n }\n\n detailEl.classList.add('tbw-collapsing');\n const cleanup = () => {\n detailEl.classList.remove('tbw-collapsing');\n onComplete();\n };\n detailEl.addEventListener('animationend', cleanup, { once: true });\n // Fallback timeout in case animation doesn't fire\n setTimeout(cleanup, this.animationDuration + 50);\n }\n\n // #endregion\n\n // #region Internal State\n private expandedRows: Set<any> = new Set();\n private detailElements: Map<any, HTMLElement> = new Map();\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.expandedRows.clear();\n this.detailElements.clear();\n }\n // #endregion\n\n // #region Hooks\n\n override processColumns(columns: readonly ColumnConfig[]): 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 // Skip if already wrapped by this plugin (prevents double-wrapping on re-render)\n if ((originalRenderer as any)?.__masterDetailWrapped) {\n return cols;\n }\n\n const wrappedRenderer = (renderCtx: Parameters<NonNullable<typeof originalRenderer>>[0]) => {\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${isExpanded ? ' expanded' : ''}`;\n // Use grid-level icons (fall back to defaults)\n this.setIcon(toggle, this.resolveIcon(isExpanded ? 'collapse' : 'expand'));\n // role=\"button\" is required for aria-expanded to be valid\n toggle.setAttribute('role', 'button');\n toggle.setAttribute('tabindex', '0');\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 // Mark renderer as wrapped to prevent double-wrapping\n (wrappedRenderer as any).__masterDetailWrapped = true;\n firstCol.viewRenderer = wrappedRenderer;\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 as object);\n\n this.emit<DetailExpandDetail>('detail-expand', {\n rowIndex: event.rowIndex,\n row: event.row,\n expanded: this.expandedRows.has(event.row as object),\n });\n\n this.requestRender();\n return false;\n }\n\n override onCellClick(): boolean | void {\n // Sync detail rows after cell click triggers refreshVirtualWindow\n // This runs in microtask to ensure DOM updates are complete\n if (this.expandedRows.size > 0) {\n queueMicrotask(() => this.#syncDetailRows());\n }\n return; // Don't prevent default\n }\n\n override afterRender(): void {\n this.#syncDetailRows();\n }\n\n /**\n * Called on scroll to sync detail elements with visible rows.\n * Removes details for rows that scrolled out of view and reattaches for visible rows.\n */\n override onScrollRender(): void {\n if (!this.config.detailRenderer || this.expandedRows.size === 0) return;\n // Full sync needed on scroll to clean up orphaned details\n this.#syncDetailRows();\n }\n\n /**\n * Full sync of detail rows - cleans up stale elements and creates new ones.\n * Detail rows are inserted as siblings AFTER their master row to survive row rebuilds.\n */\n #syncDetailRows(): void {\n if (!this.config.detailRenderer) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n // Build a map of row index -> row element for visible rows\n const visibleRowMap = new Map<number, Element>();\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) {\n visibleRowMap.set(rowIndex, rowEl);\n }\n }\n\n // Remove detail rows whose parent row is no longer visible or no longer expanded\n const existingDetails = body.querySelectorAll('.master-detail-row');\n for (const detailEl of existingDetails) {\n const forIndex = parseInt(detailEl.getAttribute('data-detail-for') ?? '-1', 10);\n const row = forIndex >= 0 ? this.rows[forIndex] : undefined;\n const isStillExpanded = row && this.expandedRows.has(row);\n const isRowVisible = visibleRowMap.has(forIndex);\n\n // Remove detail if not expanded or if parent row scrolled out\n if (!isStillExpanded || !isRowVisible) {\n detailEl.remove();\n if (row) this.detailElements.delete(row);\n }\n }\n\n // Insert detail rows for expanded rows that are visible\n for (const [rowIndex, rowEl] of visibleRowMap) {\n const row = this.rows[rowIndex];\n if (!row || !this.expandedRows.has(row)) continue;\n\n // Check if detail already exists for this row\n const existingDetail = this.detailElements.get(row);\n if (existingDetail) {\n // Ensure it's positioned correctly (as next sibling of row element)\n if (existingDetail.previousElementSibling !== rowEl) {\n rowEl.after(existingDetail);\n }\n continue;\n }\n\n // Create new detail element\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 // Insert as sibling after the row element (not as child)\n rowEl.after(detailEl);\n this.detailElements.set(row, detailEl);\n\n // Apply expand animation\n this.animateExpand(detailEl);\n }\n }\n\n /**\n * Return total extra height from all expanded detail rows.\n * Used by grid virtualization to adjust scrollbar height.\n */\n override getExtraHeight(): number {\n let totalHeight = 0;\n for (const row of this.expandedRows) {\n const detailEl = this.detailElements.get(row);\n if (detailEl) {\n totalHeight += detailEl.offsetHeight;\n } else {\n // Detail not yet rendered - estimate based on config or default\n const configHeight = this.config?.detailHeight;\n totalHeight += typeof configHeight === 'number' ? configHeight : 150;\n }\n }\n return totalHeight;\n }\n\n /**\n * Return extra height that appears before a given row index.\n * This is the sum of heights of all expanded details whose parent row is before the given index.\n */\n override getExtraHeightBefore(beforeRowIndex: number): number {\n let totalHeight = 0;\n for (const row of this.expandedRows) {\n const rowIndex = this.rows.indexOf(row);\n // Include detail if it's for a row before the given index\n if (rowIndex >= 0 && rowIndex < beforeRowIndex) {\n const detailEl = this.detailElements.get(row);\n if (detailEl) {\n totalHeight += detailEl.offsetHeight;\n } else {\n const configHeight = this.config?.detailHeight;\n totalHeight += typeof configHeight === 'number' ? configHeight : 150;\n }\n }\n }\n return totalHeight;\n }\n\n /**\n * Adjust the virtualization start index to keep expanded row visible while its detail is visible.\n * This ensures the detail scrolls smoothly out of view instead of disappearing abruptly.\n */\n override adjustVirtualStart(start: number, scrollTop: number, rowHeight: number): number {\n if (this.expandedRows.size === 0) return start;\n\n // Build sorted list of expanded row indices for cumulative height calculation\n const expandedIndices: Array<{ index: number; row: any }> = [];\n for (const row of this.expandedRows) {\n const index = this.rows.indexOf(row);\n if (index >= 0) {\n expandedIndices.push({ index, row });\n }\n }\n expandedIndices.sort((a, b) => a.index - b.index);\n\n let minStart = start;\n\n // Calculate actual scroll position for each expanded row,\n // accounting for cumulative detail heights before it\n let cumulativeExtraHeight = 0;\n\n for (const { index: rowIndex, row } of expandedIndices) {\n // Actual position includes all detail heights before this row\n const actualRowTop = rowIndex * rowHeight + cumulativeExtraHeight;\n const detailEl = this.detailElements.get(row);\n const detailHeight =\n detailEl?.offsetHeight ?? (typeof this.config?.detailHeight === 'number' ? this.config.detailHeight : 150);\n const actualDetailBottom = actualRowTop + rowHeight + detailHeight;\n\n // Update cumulative height for next iteration\n cumulativeExtraHeight += detailHeight;\n\n // Skip rows that are at or after the calculated start\n if (rowIndex >= start) continue;\n\n // If any part of the detail is still visible (below the scroll position),\n // we need to keep the parent row in the render range\n if (actualDetailBottom > scrollTop) {\n if (rowIndex < minStart) {\n minStart = rowIndex;\n }\n }\n }\n\n return minStart;\n }\n // #endregion\n\n // #region 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 /**\n * Re-parse light DOM to refresh the detail renderer.\n * Call this after framework templates are registered (e.g., Angular ngAfterContentInit).\n *\n * This allows frameworks to register templates asynchronously and then\n * update the plugin's detailRenderer.\n */\n refreshDetailRenderer(): void {\n // Force re-parse by temporarily clearing the renderer\n const currentRenderer = this.config.detailRenderer;\n this.config = { ...this.config, detailRenderer: undefined };\n this.parseLightDomDetail();\n\n // If no new renderer was found, restore the original\n if (!this.config.detailRenderer && currentRenderer) {\n this.config = { ...this.config, detailRenderer: currentRenderer };\n }\n }\n // #endregion\n\n // #region Styles\n override readonly styles = styles;\n // #endregion\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","/**\n * Multi-Sort Plugin (Class-based)\n *\n * Provides multi-column sorting capabilities for tbw-grid.\n * Supports shift+click for adding secondary sort columns.\n */\n\nimport { BaseGridPlugin, HeaderClickEvent } from '../../core/plugin/base-plugin';\nimport type { ColumnState } from '../../core/types';\nimport { applySorts, getSortDirection, getSortIndex, toggleSort } from './multi-sort';\nimport styles from './multi-sort.css?inline';\nimport type { MultiSortConfig, SortModel } from './types';\n\n/**\n * Multi-Sort Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new MultiSortPlugin({ maxSortColumns: 3, showSortIndex: true })\n * ```\n */\nexport class MultiSortPlugin extends BaseGridPlugin<MultiSortConfig> {\n readonly name = 'multiSort';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<MultiSortConfig> {\n return {\n maxSortColumns: 3,\n showSortIndex: true,\n };\n }\n\n // #region Internal State\n private sortModel: SortModel[] = [];\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.sortModel = [];\n }\n // #endregion\n\n // #region Hooks\n\n override processRows(rows: readonly unknown[]): unknown[] {\n if (this.sortModel.length === 0) {\n return [...rows];\n }\n return applySorts([...rows], this.sortModel, [...this.columns]);\n }\n\n override onHeaderClick(event: HeaderClickEvent): boolean {\n const column = this.columns.find((c) => c.field === event.field);\n if (!column?.sortable) return false;\n\n const shiftKey = event.originalEvent.shiftKey;\n const maxColumns = this.config.maxSortColumns ?? 3;\n\n this.sortModel = toggleSort(this.sortModel, event.field, shiftKey, maxColumns);\n\n this.emit('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n\n return true;\n }\n\n override afterRender(): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const showIndex = this.config.showSortIndex !== false;\n\n // Update all sortable header cells with sort indicators\n const headerCells = shadowRoot.querySelectorAll('.header-row .cell[data-field]');\n headerCells.forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n\n const sortIndex = getSortIndex(this.sortModel, field);\n const sortDir = getSortDirection(this.sortModel, field);\n\n // Remove existing sort index badge (always clean up)\n const existingBadge = cell.querySelector('.sort-index');\n existingBadge?.remove();\n\n if (sortDir) {\n // Column is sorted - remove base indicator and add our own\n const existingIndicator = cell.querySelector('[part~=\"sort-indicator\"], .sort-indicator');\n existingIndicator?.remove();\n\n cell.setAttribute('data-sort', sortDir);\n\n // Add sort arrow indicator\n const indicator = document.createElement('span');\n indicator.className = 'sort-indicator';\n // Use grid-level icons (fall back to defaults)\n this.setIcon(indicator, this.resolveIcon(sortDir === 'asc' ? 'sortAsc' : 'sortDesc'));\n cell.appendChild(indicator);\n\n // Add sort index badge if multiple columns sorted and showSortIndex is enabled\n if (showIndex && this.sortModel.length > 1 && sortIndex !== undefined) {\n const badge = document.createElement('span');\n badge.className = 'sort-index';\n badge.textContent = String(sortIndex);\n cell.appendChild(badge);\n }\n } else {\n cell.removeAttribute('data-sort');\n // For unsorted columns, leave the base indicator (⇅) alone\n }\n });\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Get the current sort model.\n * @returns Copy of the current sort model\n */\n getSortModel(): SortModel[] {\n return [...this.sortModel];\n }\n\n /**\n * Set the sort model programmatically.\n * @param model - New sort model to apply\n */\n setSortModel(model: SortModel[]): void {\n this.sortModel = [...model];\n this.emit('sort-change', { sortModel: [...model] });\n this.requestRender();\n }\n\n /**\n * Clear all sorting.\n */\n clearSort(): void {\n this.sortModel = [];\n this.emit('sort-change', { sortModel: [] });\n this.requestRender();\n }\n\n /**\n * Get the sort index (1-based) for a specific field.\n * @param field - Field to check\n * @returns 1-based index or undefined if not sorted\n */\n getSortIndex(field: string): number | undefined {\n return getSortIndex(this.sortModel, field);\n }\n\n /**\n * Get the sort direction for a specific field.\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\n getSortDirection(field: string): 'asc' | 'desc' | undefined {\n return getSortDirection(this.sortModel, field);\n }\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Return sort state for a column if it's in the sort model.\n */\n override getColumnState(field: string): Partial<ColumnState> | undefined {\n const index = this.sortModel.findIndex((s) => s.field === field);\n if (index === -1) return undefined;\n\n const sortEntry = this.sortModel[index];\n return {\n sort: {\n direction: sortEntry.direction,\n priority: index,\n },\n };\n }\n\n /**\n * Apply sort state from column state.\n * Rebuilds the sort model from all column states.\n */\n override applyColumnState(field: string, state: ColumnState): void {\n // Only process if the column has sort state\n if (!state.sort) {\n // Remove this field from sortModel if it exists\n this.sortModel = this.sortModel.filter((s) => s.field !== field);\n return;\n }\n\n // Find existing entry or add new one\n const existingIndex = this.sortModel.findIndex((s) => s.field === field);\n const newEntry: SortModel = {\n field,\n direction: state.sort.direction,\n };\n\n if (existingIndex !== -1) {\n // Update existing entry\n this.sortModel[existingIndex] = newEntry;\n } else {\n // Add at the correct priority position\n this.sortModel.splice(state.sort.priority, 0, newEntry);\n }\n\n // Re-sort the model by priority to ensure correct order\n // This is handled after all columns are processed, but we maintain order here\n }\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\n}\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.position = 'sticky';\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.position = 'sticky';\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.position = 'sticky';\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.position = 'sticky';\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.position = '';\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, PLUGIN_QUERIES, type PluginQuery } 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 }\n\n // #region Internal State\n private isApplied = false;\n private leftOffsets = new Map<string, number>();\n private rightOffsets = new Map<string, number>();\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.leftOffsets.clear();\n this.rightOffsets.clear();\n this.isApplied = false;\n }\n // #endregion\n\n // #region 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 // #endregion\n\n // #region Hooks\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\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.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 /**\n * Handle inter-plugin queries.\n */\n override onPluginQuery(query: PluginQuery): unknown {\n switch (query.type) {\n case PLUGIN_QUERIES.CAN_MOVE_COLUMN: {\n // Prevent pinned columns from being moved/reordered.\n // Pinned columns have fixed positions and should not be draggable.\n const column = query.context as ColumnConfig;\n const sticky = (column as ColumnConfig & { sticky?: 'left' | 'right' }).sticky;\n if (sticky === 'left' || sticky === 'right') {\n return false;\n }\n // Also check meta.sticky for backwards compatibility\n const metaSticky = (column.meta as { sticky?: 'left' | 'right' } | undefined)?.sticky;\n if (metaSticky === 'left' || metaSticky === 'right') {\n return false;\n }\n return undefined; // Let other plugins or default behavior decide\n }\n default:\n return undefined;\n }\n }\n // #endregion\n\n // #region 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 * Report horizontal scroll boundary offsets for pinned columns.\n * Used by keyboard navigation to ensure focused cells aren't hidden behind sticky columns.\n */\n override getHorizontalScrollOffsets(\n rowEl?: HTMLElement,\n focusedCell?: HTMLElement,\n ): { left: number; right: number; skipScroll?: boolean } | undefined {\n if (!this.isApplied) {\n return undefined;\n }\n\n let left = 0;\n let right = 0;\n\n if (rowEl) {\n // Calculate from rendered cells in the row\n const stickyLeftCells = rowEl.querySelectorAll('.sticky-left');\n const stickyRightCells = rowEl.querySelectorAll('.sticky-right');\n stickyLeftCells.forEach((el) => {\n left += (el as HTMLElement).offsetWidth;\n });\n stickyRightCells.forEach((el) => {\n right += (el as HTMLElement).offsetWidth;\n });\n } else {\n // Fall back to header row if no row element provided\n const host = this.grid as unknown as HTMLElement;\n const shadowRoot = host.shadowRoot;\n if (shadowRoot) {\n const headerCells = shadowRoot.querySelectorAll('.header-row .cell');\n headerCells.forEach((cell) => {\n if (cell.classList.contains('sticky-left')) {\n left += (cell as HTMLElement).offsetWidth;\n } else if (cell.classList.contains('sticky-right')) {\n right += (cell as HTMLElement).offsetWidth;\n }\n });\n }\n }\n\n // Skip horizontal scrolling if focused cell is pinned (it's always visible)\n const skipScroll =\n focusedCell?.classList.contains('sticky-left') || focusedCell?.classList.contains('sticky-right');\n\n return { left, right, skipScroll };\n }\n // #endregion\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 { getAggregator } from '../../core/internal/aggregators';\nimport type { ColumnConfig } from '../../core/types';\nimport type {\n AggregationRowConfig,\n AggregatorConfig,\n AggregatorDefinition,\n PinnedRowsConfig,\n PinnedRowsContext,\n PinnedRowsPanel,\n} from './types';\n\n/**\n * Check if an aggregator definition is a full config object (with aggFunc and optional formatter).\n */\nfunction isAggregatorConfig(def: AggregatorDefinition): def is AggregatorConfig {\n return typeof def === 'object' && def !== null && 'aggFunc' in def;\n}\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', 'presentation');\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 // Use presentation role since aggregation rows are outside the role=\"grid\" element for layout reasons\n container.setAttribute('role', 'presentation');\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 // Use presentation role since aggregation rows are outside the role=\"grid\" element\n rowEl.setAttribute('role', 'presentation');\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 let formatter: ((value: unknown, field: string, column?: ColumnConfig) => string) | undefined;\n\n // Check for aggregator first\n const aggDef = rowConfig.aggregators?.[col.field];\n if (aggDef) {\n // Handle both simple ref and full config object\n if (isAggregatorConfig(aggDef)) {\n const aggFn = getAggregator(aggDef.aggFunc);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\n formatter = aggDef.formatter;\n } else {\n const aggFn = getAggregator(aggDef);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\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 // Apply formatter if provided, otherwise convert to string\n if (value != null) {\n cell.textContent = formatter ? formatter(value, col.field, col) : String(value);\n } else {\n cell.textContent = '';\n }\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 styles from './pinned-rows.css?inline';\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 position: 'bottom',\n showRowCount: true,\n showSelectedCount: true,\n showFilteredCount: true,\n };\n }\n\n // #region 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 // #endregion\n\n // #region Lifecycle\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 // #endregion\n\n // #region Hooks\n override afterRender(): void {\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 // #region 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 // #endregion\n }\n // #endregion\n\n // #region Private Methods\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 // #endregion\n\n // #region Public API\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 // #endregion\n\n // #region Styles\n override readonly styles = styles;\n // #endregion\n}\n","import { getValueAggregator } from '../../core/internal/aggregators';\nimport type { PivotConfig } from './types';\n\n// Re-export for backward compatibility within pivot plugin\nexport const getPivotAggregator = getValueAggregator;\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\nexport function createValueKey(columnValues: string[], valueField: string): string {\n return [...columnValues, valueField].join('|');\n}\n","import { createValueKey, getPivotAggregator } from './pivot-model';\nimport type { PivotConfig, PivotResult, PivotRow, PivotValueField } from './types';\n\nexport type PivotDataRow = Record<string, unknown>;\n\n/**\n * Build a hierarchical pivot result from flat data.\n * Supports multiple row group fields for nested hierarchy.\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 // Build hierarchical pivot rows\n const pivotRows = buildHierarchicalPivotRows(\n rows,\n rowGroupFields,\n columnGroupFields,\n columnKeys,\n valueFields,\n 0, // starting depth\n '', // parent key prefix\n );\n\n // Calculate grand 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\n/**\n * Get unique column key combinations from the data.\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\n/**\n * Group rows by a single field.\n */\nexport function groupByField(rows: PivotDataRow[], field: string): Map<string, PivotDataRow[]> {\n const groups = new Map<string, PivotDataRow[]>();\n\n for (const row of rows) {\n const key = String(row[field] ?? '');\n const existing = groups.get(key);\n if (existing) {\n existing.push(row);\n } else {\n groups.set(key, [row]);\n }\n }\n\n return groups;\n}\n\n/**\n * Group rows by multiple fields (legacy flat grouping).\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 const existing = groups.get(key);\n if (existing) {\n existing.push(row);\n } else {\n groups.set(key, [row]);\n }\n }\n\n return groups;\n}\n\n/**\n * Build hierarchical pivot rows recursively.\n * Each level of rowGroupFields creates a new depth level.\n */\nexport function buildHierarchicalPivotRows(\n rows: PivotDataRow[],\n rowGroupFields: string[],\n columnFields: string[],\n columnKeys: string[],\n valueFields: PivotValueField[],\n depth: number,\n parentKey: string,\n): PivotRow[] {\n const result: PivotRow[] = [];\n\n // If no more row group fields, we're at the leaf level - aggregate the data\n if (rowGroupFields.length === 0) {\n // This shouldn't normally happen as we need at least one grouping field\n // But handle it by creating a single aggregated row\n const values = aggregateValues(rows, columnFields, columnKeys, valueFields);\n const total = calculateRowTotal(values);\n result.push({\n rowKey: parentKey || 'all',\n rowLabel: parentKey || 'All',\n depth,\n values,\n total,\n isGroup: false,\n rowCount: rows.length,\n });\n return result;\n }\n\n // Get the current grouping field\n const currentField = rowGroupFields[0];\n const remainingFields = rowGroupFields.slice(1);\n const hasChildren = remainingFields.length > 0;\n\n // Group rows by current field\n const grouped = groupByField(rows, currentField);\n\n for (const [groupValue, groupRows] of grouped) {\n const rowKey = parentKey ? `${parentKey}|${groupValue}` : groupValue;\n\n // Aggregate values for this group (sum of all child rows)\n const values = aggregateValues(groupRows, columnFields, columnKeys, valueFields);\n const total = calculateRowTotal(values);\n\n // Build children if there are more grouping levels\n let children: PivotRow[] | undefined;\n if (hasChildren) {\n children = buildHierarchicalPivotRows(\n groupRows,\n remainingFields,\n columnFields,\n columnKeys,\n valueFields,\n depth + 1,\n rowKey,\n );\n }\n\n result.push({\n rowKey,\n rowLabel: groupValue || '(blank)',\n depth,\n values,\n total,\n isGroup: hasChildren,\n children,\n rowCount: groupRows.length,\n });\n }\n\n return result;\n}\n\n/**\n * Aggregate values for a set of rows across all column keys.\n */\nexport function aggregateValues(\n rows: PivotDataRow[],\n columnFields: string[],\n columnKeys: string[],\n valueFields: PivotValueField[],\n): Record<string, number | null> {\n const values: Record<string, number | null> = {};\n\n for (const colKey of columnKeys) {\n for (const vf of valueFields) {\n // Filter rows that match this column key\n const matchingRows =\n columnFields.length > 0\n ? rows.filter((r) => columnFields.map((f) => String(r[f] ?? '')).join('|') === colKey)\n : rows;\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 }\n\n return values;\n}\n\n/**\n * Calculate the total for a row's values.\n */\nexport function calculateRowTotal(values: Record<string, number | null>): number {\n let sum = 0;\n for (const val of Object.values(values)) {\n sum += val ?? 0;\n }\n return sum;\n}\n\n/**\n * Legacy flat pivot row building (for backwards compatibility).\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 = aggregateValues(groupRows, columnFields, columnKeys, valueFields);\n const total = calculateRowTotal(values);\n\n result.push({\n rowKey,\n rowLabel: rowKey || '(blank)',\n depth,\n values,\n total,\n isGroup: false,\n rowCount: groupRows.length,\n });\n }\n\n return result;\n}\n\n/**\n * Calculate grand totals across all pivot rows.\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 // Recursively sum all rows (including nested children)\n function sumRows(rows: PivotRow[]) {\n for (const row of rows) {\n // Only count leaf rows to avoid double-counting\n if (!row.isGroup || !row.children?.length) {\n for (const colKey of columnKeys) {\n for (const vf of valueFields) {\n const valueKey = createValueKey([colKey], vf.field);\n totals[valueKey] = (totals[valueKey] ?? 0) + (row.values[valueKey] ?? 0);\n }\n }\n } else if (row.children) {\n sumRows(row.children);\n }\n }\n }\n\n sumRows(pivotRows);\n return totals;\n}\n\n/**\n * Flatten hierarchical pivot rows for rendering.\n * Respects expanded state - only includes children of expanded groups.\n */\nexport function flattenPivotRows(rows: PivotRow[], expandedKeys?: Set<string>, defaultExpanded = true): PivotRow[] {\n const result: PivotRow[] = [];\n\n function flatten(row: PivotRow) {\n result.push(row);\n\n // Check if this group is expanded\n const isExpanded = expandedKeys ? expandedKeys.has(row.rowKey) : defaultExpanded;\n\n // Only include children if expanded\n if (row.children && isExpanded) {\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/**\n * Get all group keys from pivot rows (for expand all / collapse all).\n */\nexport function getAllGroupKeys(rows: PivotRow[]): string[] {\n const keys: string[] = [];\n\n function collectKeys(row: PivotRow) {\n if (row.isGroup) {\n keys.push(row.rowKey);\n }\n if (row.children) {\n for (const child of row.children) {\n collectKeys(child);\n }\n }\n }\n\n for (const row of rows) {\n collectKeys(row);\n }\n\n return keys;\n}\n","/**\n * Pivot Tool Panel Rendering\n *\n * Pure functions for rendering the pivot configuration panel.\n * Separated from PivotPlugin for better code organization.\n */\n\nimport type { AggFunc, PivotConfig, PivotValueField } from './types';\n\n/** All available aggregation functions */\nexport const AGG_FUNCS: AggFunc[] = ['sum', 'avg', 'count', 'min', 'max', 'first', 'last'];\n\n/** Field info for available fields */\nexport interface FieldInfo {\n field: string;\n header: string;\n}\n\n/** Callbacks for panel interactions */\nexport interface PanelCallbacks {\n onTogglePivot: (enabled: boolean) => void;\n onAddFieldToZone: (field: string, zone: 'rowGroups' | 'columnGroups') => void;\n onRemoveFieldFromZone: (field: string, zone: 'rowGroups' | 'columnGroups') => void;\n onAddValueField: (field: string, aggFunc: AggFunc) => void;\n onRemoveValueField: (field: string) => void;\n onUpdateValueAggFunc: (field: string, aggFunc: AggFunc) => void;\n onOptionChange: (option: 'showTotals' | 'showGrandTotal', value: boolean) => void;\n getAvailableFields: () => FieldInfo[];\n}\n\n/** Internal context passed to rendering functions */\ninterface RenderContext {\n config: PivotConfig;\n callbacks: PanelCallbacks;\n signal: AbortSignal;\n}\n\n/**\n * Render the complete pivot panel content.\n * Returns a cleanup function that removes all event listeners and DOM elements.\n */\nexport function renderPivotPanel(\n container: HTMLElement,\n config: PivotConfig,\n isActive: boolean,\n callbacks: PanelCallbacks,\n): () => void {\n // Create AbortController for automatic listener cleanup\n const controller = new AbortController();\n const ctx: RenderContext = { config, callbacks, signal: controller.signal };\n\n const wrapper = document.createElement('div');\n wrapper.className = 'tbw-pivot-panel';\n\n // Options section (at top, includes pivot toggle)\n wrapper.appendChild(createSection('Options', () => createOptionsPanel(isActive, ctx)));\n\n // Row Groups section\n wrapper.appendChild(createSection('Row Groups', () => createFieldZone('rowGroups', ctx)));\n\n // Column Groups section\n wrapper.appendChild(createSection('Column Groups', () => createFieldZone('columnGroups', ctx)));\n\n // Values section\n wrapper.appendChild(createSection('Values', () => createValuesZone(ctx)));\n\n // Available fields section\n wrapper.appendChild(createSection('Available Fields', () => createAvailableFieldsZone(ctx)));\n\n container.appendChild(wrapper);\n\n // Cleanup: abort all listeners, then remove DOM\n return () => {\n controller.abort();\n wrapper.remove();\n };\n}\n\n/**\n * Create a collapsible section wrapper.\n */\nfunction createSection(title: string, contentFactory: () => HTMLElement): HTMLElement {\n const section = document.createElement('div');\n section.className = 'tbw-pivot-section';\n\n const header = document.createElement('div');\n header.className = 'tbw-pivot-section-header';\n header.textContent = title;\n\n const content = document.createElement('div');\n content.className = 'tbw-pivot-section-content';\n content.appendChild(contentFactory());\n\n section.appendChild(header);\n section.appendChild(content);\n\n return section;\n}\n\n/**\n * Create a drop zone for row/column group fields.\n */\nfunction createFieldZone(zoneType: 'rowGroups' | 'columnGroups', ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const zone = document.createElement('div');\n zone.className = 'tbw-pivot-drop-zone';\n zone.setAttribute('data-zone', zoneType);\n\n const currentFields = zoneType === 'rowGroups' ? (config.rowGroupFields ?? []) : (config.columnGroupFields ?? []);\n\n if (currentFields.length === 0) {\n const placeholder = document.createElement('div');\n placeholder.className = 'tbw-pivot-placeholder';\n placeholder.textContent = 'Drag fields here or click to add';\n zone.appendChild(placeholder);\n } else {\n for (const field of currentFields) {\n zone.appendChild(createFieldChip(field, zoneType, ctx));\n }\n }\n\n // Drop handling\n zone.addEventListener(\n 'dragover',\n (e) => {\n e.preventDefault();\n zone.classList.add('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'dragleave',\n () => {\n zone.classList.remove('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'drop',\n (e) => {\n e.preventDefault();\n zone.classList.remove('drag-over');\n\n const field = e.dataTransfer?.getData('text/plain');\n if (field) {\n callbacks.onAddFieldToZone(field, zoneType);\n }\n },\n { signal },\n );\n\n return zone;\n}\n\n/**\n * Create a field chip for row/column zones.\n */\nfunction createFieldChip(field: string, zoneType: 'rowGroups' | 'columnGroups', ctx: RenderContext): HTMLElement {\n const { callbacks, signal } = ctx;\n const chip = document.createElement('div');\n chip.className = 'tbw-pivot-field-chip';\n chip.draggable = true;\n\n const fieldInfo = callbacks.getAvailableFields().find((f) => f.field === field);\n const label = document.createElement('span');\n label.className = 'tbw-pivot-chip-label';\n label.textContent = fieldInfo?.header ?? field;\n\n const removeBtn = document.createElement('button');\n removeBtn.className = 'tbw-pivot-chip-remove';\n removeBtn.innerHTML = '×';\n removeBtn.title = 'Remove field';\n removeBtn.addEventListener(\n 'click',\n (e) => {\n e.stopPropagation();\n callbacks.onRemoveFieldFromZone(field, zoneType);\n },\n { signal },\n );\n\n chip.appendChild(label);\n chip.appendChild(removeBtn);\n\n // Drag handling for reordering\n chip.addEventListener(\n 'dragstart',\n (e) => {\n e.dataTransfer?.setData('text/plain', field);\n e.dataTransfer?.setData('source-zone', zoneType);\n chip.classList.add('dragging');\n },\n { signal },\n );\n\n chip.addEventListener(\n 'dragend',\n () => {\n chip.classList.remove('dragging');\n },\n { signal },\n );\n\n return chip;\n}\n\n/**\n * Create the values zone with aggregation controls.\n */\nfunction createValuesZone(ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const zone = document.createElement('div');\n zone.className = 'tbw-pivot-drop-zone tbw-pivot-values-zone';\n zone.setAttribute('data-zone', 'values');\n\n const currentValues = config.valueFields ?? [];\n\n if (currentValues.length === 0) {\n const placeholder = document.createElement('div');\n placeholder.className = 'tbw-pivot-placeholder';\n placeholder.textContent = 'Drag numeric fields here for aggregation';\n zone.appendChild(placeholder);\n } else {\n for (const valueField of currentValues) {\n zone.appendChild(createValueChip(valueField, ctx));\n }\n }\n\n // Drop handling with signal for cleanup\n zone.addEventListener(\n 'dragover',\n (e) => {\n e.preventDefault();\n zone.classList.add('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'dragleave',\n () => {\n zone.classList.remove('drag-over');\n },\n { signal },\n );\n\n zone.addEventListener(\n 'drop',\n (e) => {\n e.preventDefault();\n zone.classList.remove('drag-over');\n const field = e.dataTransfer?.getData('text/plain');\n if (field) {\n callbacks.onAddValueField(field, 'sum');\n }\n },\n { signal },\n );\n\n return zone;\n}\n\n/**\n * Create a value chip with aggregation selector.\n */\nfunction createValueChip(valueField: PivotValueField, ctx: RenderContext): HTMLElement {\n const { callbacks, signal } = ctx;\n const chip = document.createElement('div');\n chip.className = 'tbw-pivot-field-chip tbw-pivot-value-chip';\n\n const fieldInfo = callbacks.getAvailableFields().find((f) => f.field === valueField.field);\n\n const labelWrapper = document.createElement('div');\n labelWrapper.className = 'tbw-pivot-value-label-wrapper';\n\n const label = document.createElement('span');\n label.className = 'tbw-pivot-chip-label';\n label.textContent = fieldInfo?.header ?? valueField.field;\n\n const aggSelect = document.createElement('select');\n aggSelect.className = 'tbw-pivot-agg-select';\n aggSelect.title = 'Aggregation function';\n\n for (const aggFunc of AGG_FUNCS) {\n const option = document.createElement('option');\n option.value = aggFunc;\n option.textContent = aggFunc.toUpperCase();\n option.selected = aggFunc === valueField.aggFunc;\n aggSelect.appendChild(option);\n }\n\n aggSelect.addEventListener(\n 'change',\n () => {\n callbacks.onUpdateValueAggFunc(valueField.field, aggSelect.value as AggFunc);\n },\n { signal },\n );\n\n const removeBtn = document.createElement('button');\n removeBtn.className = 'tbw-pivot-chip-remove';\n removeBtn.innerHTML = '×';\n removeBtn.title = 'Remove value field';\n removeBtn.addEventListener(\n 'click',\n (e) => {\n e.stopPropagation();\n callbacks.onRemoveValueField(valueField.field);\n },\n { signal },\n );\n\n labelWrapper.appendChild(label);\n labelWrapper.appendChild(aggSelect);\n\n chip.appendChild(labelWrapper);\n chip.appendChild(removeBtn);\n\n return chip;\n}\n\n/**\n * Create the available fields zone.\n */\nfunction createAvailableFieldsZone(ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const zone = document.createElement('div');\n zone.className = 'tbw-pivot-available-fields';\n\n const allFields = callbacks.getAvailableFields();\n const usedFields = new Set([\n ...(config.rowGroupFields ?? []),\n ...(config.columnGroupFields ?? []),\n ...(config.valueFields?.map((v) => v.field) ?? []),\n ]);\n\n // Filter to show only unused fields\n const availableFields = allFields.filter((f) => !usedFields.has(f.field));\n\n if (availableFields.length === 0) {\n const empty = document.createElement('div');\n empty.className = 'tbw-pivot-placeholder';\n empty.textContent = 'All fields are in use';\n zone.appendChild(empty);\n } else {\n for (const field of availableFields) {\n const chip = document.createElement('div');\n chip.className = 'tbw-pivot-field-chip available';\n chip.textContent = field.header;\n chip.draggable = true;\n chip.title = `Drag to add \"${field.field}\" to a zone`;\n\n chip.addEventListener(\n 'dragstart',\n (e) => {\n e.dataTransfer?.setData('text/plain', field.field);\n chip.classList.add('dragging');\n },\n { signal },\n );\n\n chip.addEventListener(\n 'dragend',\n () => {\n chip.classList.remove('dragging');\n },\n { signal },\n );\n\n zone.appendChild(chip);\n }\n }\n\n return zone;\n}\n\n/**\n * Create the options panel with pivot toggle and checkboxes for totals.\n */\nfunction createOptionsPanel(isActive: boolean, ctx: RenderContext): HTMLElement {\n const { config, callbacks, signal } = ctx;\n const panel = document.createElement('div');\n panel.className = 'tbw-pivot-options';\n\n // Pivot Mode toggle\n panel.appendChild(\n createCheckbox(\n 'Enable Pivot View',\n isActive,\n (checked) => {\n callbacks.onTogglePivot(checked);\n },\n signal,\n ),\n );\n\n // Show Totals checkbox\n panel.appendChild(\n createCheckbox(\n 'Show Row Totals',\n config.showTotals ?? true,\n (checked) => {\n callbacks.onOptionChange('showTotals', checked);\n },\n signal,\n ),\n );\n\n // Show Grand Total checkbox\n panel.appendChild(\n createCheckbox(\n 'Show Grand Total',\n config.showGrandTotal ?? true,\n (checked) => {\n callbacks.onOptionChange('showGrandTotal', checked);\n },\n signal,\n ),\n );\n\n return panel;\n}\n\n/**\n * Create a checkbox with label.\n */\nfunction createCheckbox(\n label: string,\n checked: boolean,\n onChange: (checked: boolean) => void,\n signal: AbortSignal,\n): HTMLElement {\n const wrapper = document.createElement('label');\n wrapper.className = 'tbw-pivot-checkbox';\n\n const input = document.createElement('input');\n input.type = 'checkbox';\n input.checked = checked;\n input.addEventListener('change', () => onChange(input.checked), { signal });\n\n const span = document.createElement('span');\n span.textContent = label;\n\n wrapper.appendChild(input);\n wrapper.appendChild(span);\n\n return wrapper;\n}\n","/**\n * Pivot Row Rendering\n *\n * Pure functions for rendering pivot rows (group rows, leaf rows, grand total).\n * Separated from PivotPlugin for better code organization.\n */\n\nimport type { ColumnConfig, IconValue } from '../../core/types';\n\n/** Row data with pivot metadata */\nexport interface PivotRowData {\n __pivotRowKey?: string;\n __pivotLabel?: string;\n __pivotDepth?: number;\n __pivotIndent?: number;\n __pivotExpanded?: boolean;\n __pivotHasChildren?: boolean;\n __pivotRowCount?: number;\n __pivotIsGrandTotal?: boolean;\n [key: string]: unknown;\n}\n\n/** Context for row rendering */\nexport interface RowRenderContext {\n columns: ColumnConfig[];\n onToggle: (key: string) => void;\n resolveIcon: (iconKey: 'expand' | 'collapse') => IconValue;\n setIcon: (element: HTMLElement, icon: IconValue) => void;\n}\n\n/**\n * Render a pivot group row (has children, can expand/collapse).\n */\nexport function renderPivotGroupRow(row: PivotRowData, rowEl: HTMLElement, ctx: RowRenderContext): boolean {\n rowEl.className = 'pivot-group-row';\n rowEl.setAttribute('data-pivot-depth', String(row.__pivotDepth ?? 0));\n rowEl.setAttribute('data-pivot-key', String(row.__pivotRowKey ?? ''));\n rowEl.setAttribute('role', 'row');\n // Note: aria-expanded is not set here because it's only valid in treegrid, not grid\n // The expand/collapse state is conveyed via the toggle button's aria-label\n rowEl.innerHTML = '';\n\n ctx.columns.forEach((col, colIdx) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n cell.setAttribute('data-col', String(colIdx));\n cell.setAttribute('role', 'gridcell');\n\n if (colIdx === 0) {\n // First column: indent + toggle + label + count\n const indent = Number(row.__pivotIndent) || 0;\n cell.style.paddingLeft = `${indent}px`;\n\n // Toggle button\n const rowKey = String(row.__pivotRowKey);\n const btn = document.createElement('button');\n btn.type = 'button';\n btn.className = 'pivot-toggle';\n btn.setAttribute('aria-label', row.__pivotExpanded ? 'Collapse group' : 'Expand group');\n ctx.setIcon(btn, ctx.resolveIcon(row.__pivotExpanded ? 'collapse' : 'expand'));\n btn.addEventListener('click', (e) => {\n e.stopPropagation();\n ctx.onToggle(rowKey);\n });\n cell.appendChild(btn);\n\n // Group label\n const label = document.createElement('span');\n label.className = 'pivot-label';\n label.textContent = String(row.__pivotLabel ?? '');\n cell.appendChild(label);\n\n // Row count\n const count = document.createElement('span');\n count.className = 'pivot-count';\n count.textContent = ` (${Number(row.__pivotRowCount) || 0})`;\n cell.appendChild(count);\n } else {\n // Other columns: render value\n const value = row[col.field];\n cell.textContent = value != null ? String(value) : '';\n }\n\n rowEl.appendChild(cell);\n });\n\n return true;\n}\n\n/**\n * Render a pivot leaf row (no children, just indentation).\n */\nexport function renderPivotLeafRow(row: PivotRowData, rowEl: HTMLElement, columns: ColumnConfig[]): boolean {\n rowEl.className = 'pivot-leaf-row';\n rowEl.setAttribute('data-pivot-depth', String(row.__pivotDepth ?? 0));\n rowEl.setAttribute('data-pivot-key', String(row.__pivotRowKey ?? ''));\n rowEl.innerHTML = '';\n\n columns.forEach((col, colIdx) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n cell.setAttribute('data-col', String(colIdx));\n cell.setAttribute('role', 'gridcell');\n\n if (colIdx === 0) {\n // First column: indent + label (no toggle for leaves)\n const indent = Number(row.__pivotIndent) || 0;\n // Add extra indent for alignment with toggle button\n cell.style.paddingLeft = `${indent + 20}px`;\n\n const label = document.createElement('span');\n label.className = 'pivot-label';\n label.textContent = String(row.__pivotLabel ?? '');\n cell.appendChild(label);\n } else {\n // Other columns: render value\n const value = row[col.field];\n cell.textContent = value != null ? String(value) : '';\n }\n\n rowEl.appendChild(cell);\n });\n\n return true;\n}\n\n/**\n * Render the grand total row.\n */\nexport function renderPivotGrandTotalRow(row: PivotRowData, rowEl: HTMLElement, columns: ColumnConfig[]): boolean {\n rowEl.className = 'pivot-grand-total-row';\n // Use role=presentation since grand total is rendered outside the role=grid element\n rowEl.setAttribute('role', 'presentation');\n rowEl.innerHTML = '';\n\n columns.forEach((col, colIdx) => {\n const cell = document.createElement('div');\n cell.className = 'cell';\n cell.setAttribute('data-col', String(colIdx));\n // No role attribute - parent row has role=presentation so children don't need grid semantics\n\n if (colIdx === 0) {\n // First column: Grand Total label\n const label = document.createElement('span');\n label.className = 'pivot-label';\n label.textContent = 'Grand Total';\n cell.appendChild(label);\n } else {\n // Other columns: render totals\n const value = row[col.field];\n cell.textContent = value != null ? String(value) : '';\n }\n\n rowEl.appendChild(cell);\n });\n\n return true;\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 * Includes a tool panel for interactive pivot configuration.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridConfig, ToolPanelDefinition } from '../../core/types';\nimport { buildPivot, flattenPivotRows, getAllGroupKeys, type PivotDataRow } from './pivot-engine';\nimport { createValueKey, validatePivotConfig } from './pivot-model';\nimport { renderPivotPanel, type FieldInfo, type PanelCallbacks } from './pivot-panel';\nimport { renderPivotGrandTotalRow, renderPivotGroupRow, renderPivotLeafRow, type PivotRowData } from './pivot-rows';\nimport type { AggFunc, ExpandCollapseAnimation, PivotConfig, PivotResult, PivotValueField } from './types';\n\n// Import CSS as inline string (Vite handles this)\nimport styles from './pivot.css?inline';\n\n/** Extended grid interface with column access */\ninterface GridWithColumns {\n shadowRoot: ShadowRoot | null;\n effectiveConfig?: GridConfig;\n getAllColumns(): Array<{ field: string; header: string; visible: boolean }>;\n columns: unknown[];\n rows: unknown[];\n requestRender(): void;\n openToolPanel(id: string): void;\n closeToolPanel(): void;\n toggleToolPanel(id: string): void;\n activeToolPanel: string | undefined;\n}\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 /** Tool panel ID for shell integration */\n static readonly PANEL_ID = 'pivot';\n\n protected override get defaultConfig(): Partial<PivotConfig> {\n return {\n active: true,\n showTotals: true,\n showGrandTotal: true,\n showToolPanel: true,\n animation: 'slide',\n };\n }\n\n // #region Internal State\n private isActive = false;\n private hasInitialized = false;\n private pivotResult: PivotResult | null = null;\n private fieldHeaderMap: Map<string, string> = new Map();\n private expandedKeys: Set<string> = new Set();\n private defaultExpanded = true;\n private originalColumns: Array<{ field: string; header: string }> = [];\n private panelContainer: HTMLElement | null = null;\n private grandTotalFooter: HTMLElement | null = null;\n private previousVisibleKeys = new Set<string>();\n private keysToAnimate = new Set<string>();\n\n /**\n * Check if the plugin has valid pivot configuration (at least value fields).\n */\n private hasValidPivotConfig(): boolean {\n return (this.config.valueFields?.length ?? 0) > 0;\n }\n\n /**\n * Get animation style respecting grid-level animation mode.\n */\n private get animationStyle(): ExpandCollapseAnimation {\n const gridEl = this.grid as unknown as GridWithColumns;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode !== true && mode !== 'on') {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host && getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim() === '0') {\n return false;\n }\n }\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.isActive = false;\n this.hasInitialized = false;\n this.pivotResult = null;\n this.fieldHeaderMap.clear();\n this.originalColumns = [];\n this.panelContainer = null;\n this.cleanupGrandTotalFooter();\n this.previousVisibleKeys.clear();\n this.keysToAnimate.clear();\n }\n\n // #endregion\n\n // #region Shell Integration\n\n override getToolPanel(): ToolPanelDefinition | undefined {\n // Allow users to disable the tool panel for programmatic-only pivot\n // Check userConfig first (works before attach), then merged config\n const showToolPanel = this.config?.showToolPanel ?? this.userConfig?.showToolPanel ?? true;\n if (showToolPanel === false) {\n return undefined;\n }\n\n return {\n id: PivotPlugin.PANEL_ID,\n title: 'Pivot',\n icon: '⊞',\n tooltip: 'Configure pivot table',\n order: 90,\n render: (container) => this.renderPanel(container),\n };\n }\n\n // #endregion\n\n // #region Hooks\n\n override processRows(rows: readonly unknown[]): PivotDataRow[] {\n // Auto-enable pivot if config.active is true and we have valid pivot fields\n if (!this.hasInitialized && this.config.active !== false && this.hasValidPivotConfig()) {\n this.hasInitialized = true;\n this.isActive = true;\n }\n\n if (!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 this.buildFieldHeaderMap();\n this.defaultExpanded = this.config.defaultExpanded ?? true;\n\n // Initialize expanded state with defaults if first build\n if (this.expandedKeys.size === 0 && this.defaultExpanded && this.pivotResult) {\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n }\n\n // Build pivot\n this.pivotResult = buildPivot(rows as PivotDataRow[], this.config);\n\n // If default expanded and we just built the pivot, add all group keys\n if (this.expandedKeys.size === 0 && this.defaultExpanded) {\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n }\n\n // Return flattened pivot rows respecting expanded state\n const indentWidth = this.config.indentWidth ?? 20;\n const flatRows: PivotDataRow[] = flattenPivotRows(\n this.pivotResult.rows,\n this.expandedKeys,\n this.defaultExpanded,\n ).map((pr) => ({\n __pivotRowKey: pr.rowKey,\n __pivotLabel: pr.rowLabel,\n __pivotDepth: pr.depth,\n __pivotIsGroup: pr.isGroup,\n __pivotHasChildren: Boolean(pr.children?.length),\n __pivotExpanded: this.expandedKeys.has(pr.rowKey),\n __pivotRowCount: pr.rowCount ?? 0,\n __pivotIndent: pr.depth * indentWidth,\n __pivotTotal: pr.total,\n ...pr.values,\n }));\n\n // Track which rows are newly visible (for animation)\n this.keysToAnimate.clear();\n const currentVisibleKeys = new Set<string>();\n for (const row of flatRows) {\n const key = row.__pivotRowKey as string;\n currentVisibleKeys.add(key);\n // Animate non-root rows that weren't previously visible\n if (!this.previousVisibleKeys.has(key) && (row.__pivotDepth as number) > 0) {\n this.keysToAnimate.add(key);\n }\n }\n this.previousVisibleKeys = currentVisibleKeys;\n\n // Grand total is rendered as a pinned footer row in afterRender,\n // not as part of the scrolling row data\n\n return flatRows;\n }\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.isActive || !this.pivotResult) {\n return [...columns];\n }\n\n const pivotColumns: ColumnConfig[] = [];\n\n // Row label column\n const rowGroupHeaders = (this.config.rowGroupFields ?? []).map((f) => this.fieldHeaderMap.get(f) ?? f).join(' / ');\n pivotColumns.push({\n field: '__pivotLabel',\n header: rowGroupHeaders || '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 const valueHeader = vf.header || this.fieldHeaderMap.get(vf.field) || vf.field;\n pivotColumns.push({\n field: valueKey,\n header: `${colKey} - ${valueHeader} (${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 override renderRow(row: Record<string, unknown>, rowEl: HTMLElement): boolean {\n const pivotRow = row as PivotRowData;\n\n // Handle pivot group row (has children)\n if (pivotRow.__pivotRowKey && pivotRow.__pivotHasChildren) {\n return renderPivotGroupRow(pivotRow, rowEl, {\n columns: this.gridColumns,\n onToggle: (key) => this.toggle(key),\n resolveIcon: (iconKey) => this.resolveIcon(iconKey),\n setIcon: (el, icon) => this.setIcon(el, icon),\n });\n }\n\n // Handle pivot leaf row (no children but in pivot mode)\n if (pivotRow.__pivotRowKey !== undefined && this.isActive) {\n return renderPivotLeafRow(pivotRow, rowEl, this.gridColumns);\n }\n\n // Clean up any leftover pivot styling from pooled row elements\n this.cleanupPivotStyling(rowEl);\n\n return false;\n }\n\n /**\n * Remove pivot-specific classes, attributes, and inline styles from a row element.\n * Called when pivot mode is disabled to clean up reused DOM elements.\n * Clears innerHTML so the grid's default renderer can rebuild the row.\n */\n private cleanupPivotStyling(rowEl: HTMLElement): void {\n // Check if this row was previously rendered by pivot (has pivot classes)\n const wasPivotRow =\n rowEl.classList.contains('pivot-group-row') ||\n rowEl.classList.contains('pivot-leaf-row') ||\n rowEl.classList.contains('pivot-grand-total-row');\n\n if (wasPivotRow) {\n // Remove pivot row classes and restore the default grid row class\n rowEl.classList.remove('pivot-group-row', 'pivot-leaf-row', 'pivot-grand-total-row');\n rowEl.classList.add('data-grid-row');\n\n // Remove pivot-specific attributes\n rowEl.removeAttribute('data-pivot-depth');\n\n // Clear the row content so the default renderer can rebuild it\n rowEl.innerHTML = '';\n }\n }\n\n override afterRender(): void {\n // Render grand total as a sticky pinned footer when pivot is active\n if (this.isActive && this.config.showGrandTotal && this.pivotResult) {\n this.renderGrandTotalFooter();\n } else {\n this.cleanupGrandTotalFooter();\n }\n\n // Apply animations to newly visible rows\n const style = this.animationStyle;\n if (style === false || this.keysToAnimate.size === 0) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n const animClass = style === 'fade' ? 'tbw-pivot-fade-in' : 'tbw-pivot-slide-in';\n for (const rowEl of body.querySelectorAll('.pivot-group-row, .pivot-leaf-row')) {\n const key = (rowEl as HTMLElement).dataset.pivotKey;\n if (key && this.keysToAnimate.has(key)) {\n rowEl.classList.add(animClass);\n rowEl.addEventListener('animationend', () => rowEl.classList.remove(animClass), { once: true });\n }\n }\n this.keysToAnimate.clear();\n }\n\n /**\n * Render the grand total row as a sticky footer pinned to the bottom.\n */\n private renderGrandTotalFooter(): void {\n if (!this.pivotResult) return;\n\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n // Find the scroll container to append the footer\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 // Create footer if it doesn't exist\n if (!this.grandTotalFooter) {\n this.grandTotalFooter = document.createElement('div');\n this.grandTotalFooter.className = 'pivot-grand-total-footer';\n container.appendChild(this.grandTotalFooter);\n }\n\n // Build the row data for grand total\n const grandTotalRow: PivotRowData = {\n __pivotRowKey: '__grandTotal',\n __pivotLabel: 'Grand Total',\n __pivotIsGrandTotal: true,\n __pivotTotal: this.pivotResult.grandTotal,\n ...this.pivotResult.totals,\n };\n\n // Render the grand total row into the footer\n renderPivotGrandTotalRow(grandTotalRow, this.grandTotalFooter, this.gridColumns);\n }\n\n /**\n * Remove the grand total footer element.\n */\n private cleanupGrandTotalFooter(): void {\n if (this.grandTotalFooter) {\n this.grandTotalFooter.remove();\n this.grandTotalFooter = null;\n }\n }\n\n // #endregion\n\n // #region Expand/Collapse API\n\n toggle(key: string): void {\n if (this.expandedKeys.has(key)) {\n this.expandedKeys.delete(key);\n } else {\n this.expandedKeys.add(key);\n }\n this.requestRender();\n }\n\n expand(key: string): void {\n this.expandedKeys.add(key);\n this.requestRender();\n }\n\n collapse(key: string): void {\n this.expandedKeys.delete(key);\n this.requestRender();\n }\n\n expandAll(): void {\n if (this.pivotResult) {\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n this.requestRender();\n }\n }\n\n collapseAll(): void {\n this.expandedKeys.clear();\n this.requestRender();\n }\n\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n // #endregion\n\n // #region Public API\n\n enablePivot(): void {\n if (this.originalColumns.length === 0) {\n this.captureOriginalColumns();\n }\n this.isActive = true;\n this.requestRender();\n }\n\n disablePivot(): void {\n this.isActive = false;\n this.pivotResult = null;\n this.requestRender();\n }\n\n isPivotActive(): boolean {\n return this.isActive;\n }\n\n getPivotResult(): PivotResult | null {\n return this.pivotResult;\n }\n\n setRowGroupFields(fields: string[]): void {\n this.config.rowGroupFields = fields;\n this.requestRender();\n }\n\n setColumnGroupFields(fields: string[]): void {\n this.config.columnGroupFields = fields;\n this.requestRender();\n }\n\n setValueFields(fields: PivotValueField[]): void {\n this.config.valueFields = fields;\n this.requestRender();\n }\n\n refresh(): void {\n this.pivotResult = null;\n this.requestRender();\n }\n\n // #endregion\n\n // #region Tool Panel API\n\n showPanel(): void {\n const grid = this.grid as unknown as GridWithColumns;\n grid.openToolPanel(PivotPlugin.PANEL_ID);\n }\n\n hidePanel(): void {\n const grid = this.grid as unknown as GridWithColumns;\n grid.closeToolPanel();\n }\n\n togglePanel(): void {\n const grid = this.grid as unknown as GridWithColumns;\n grid.toggleToolPanel(PivotPlugin.PANEL_ID);\n }\n\n isPanelVisible(): boolean {\n const grid = this.grid as unknown as GridWithColumns;\n return grid.activeToolPanel === PivotPlugin.PANEL_ID;\n }\n\n // #endregion\n\n // #region Private Helpers\n\n private get gridColumns(): ColumnConfig[] {\n const grid = this.grid as unknown as GridWithColumns;\n return (grid.columns ?? []) as ColumnConfig[];\n }\n\n private buildFieldHeaderMap(): void {\n const availableFields = this.getAvailableFields();\n this.fieldHeaderMap.clear();\n for (const field of availableFields) {\n this.fieldHeaderMap.set(field.field, field.header);\n }\n }\n\n private getAvailableFields(): FieldInfo[] {\n if (this.originalColumns.length > 0) {\n return this.originalColumns;\n }\n return this.captureOriginalColumns();\n }\n\n private captureOriginalColumns(): FieldInfo[] {\n const grid = this.grid as unknown as GridWithColumns;\n try {\n const columns = grid.getAllColumns?.() ?? grid.columns ?? [];\n this.originalColumns = columns\n .filter((col: { field: string }) => !col.field.startsWith('__pivot'))\n .map((col: { field: string; header?: string }) => ({\n field: col.field,\n header: col.header ?? col.field,\n }));\n return this.originalColumns;\n } catch {\n return [];\n }\n }\n\n private renderPanel(container: HTMLElement): (() => void) | void {\n this.panelContainer = container;\n\n if (this.originalColumns.length === 0) {\n this.captureOriginalColumns();\n }\n\n const callbacks: PanelCallbacks = {\n onTogglePivot: (enabled) => {\n if (enabled) {\n this.enablePivot();\n } else {\n this.disablePivot();\n }\n this.refreshPanel();\n },\n onAddFieldToZone: (field, zone) => this.addFieldToZone(field, zone),\n onRemoveFieldFromZone: (field, zone) => this.removeFieldFromZone(field, zone),\n onAddValueField: (field, aggFunc) => this.addValueField(field, aggFunc),\n onRemoveValueField: (field) => this.removeValueField(field),\n onUpdateValueAggFunc: (field, aggFunc) => this.updateValueAggFunc(field, aggFunc),\n onOptionChange: (option, value) => {\n this.config[option] = value;\n if (this.isActive) this.refresh();\n },\n getAvailableFields: () => this.getAvailableFields(),\n };\n\n return renderPivotPanel(container, this.config, this.isActive, callbacks);\n }\n\n private refreshPanel(): void {\n if (!this.panelContainer) return;\n this.panelContainer.innerHTML = '';\n this.renderPanel(this.panelContainer);\n }\n\n private addFieldToZone(field: string, zoneType: 'rowGroups' | 'columnGroups'): void {\n if (zoneType === 'rowGroups') {\n const current = this.config.rowGroupFields ?? [];\n if (!current.includes(field)) {\n this.config.rowGroupFields = [...current, field];\n }\n } else {\n const current = this.config.columnGroupFields ?? [];\n if (!current.includes(field)) {\n this.config.columnGroupFields = [...current, field];\n }\n }\n\n this.removeFromOtherZones(field, zoneType);\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private removeFieldFromZone(field: string, zoneType: 'rowGroups' | 'columnGroups'): void {\n if (zoneType === 'rowGroups') {\n this.config.rowGroupFields = (this.config.rowGroupFields ?? []).filter((f) => f !== field);\n } else {\n this.config.columnGroupFields = (this.config.columnGroupFields ?? []).filter((f) => f !== field);\n }\n\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private removeFromOtherZones(field: string, targetZone: 'rowGroups' | 'columnGroups' | 'values'): void {\n if (targetZone !== 'rowGroups') {\n this.config.rowGroupFields = (this.config.rowGroupFields ?? []).filter((f) => f !== field);\n }\n if (targetZone !== 'columnGroups') {\n this.config.columnGroupFields = (this.config.columnGroupFields ?? []).filter((f) => f !== field);\n }\n if (targetZone !== 'values') {\n this.config.valueFields = (this.config.valueFields ?? []).filter((v) => v.field !== field);\n }\n }\n\n private addValueField(field: string, aggFunc: AggFunc): void {\n const current = this.config.valueFields ?? [];\n if (!current.some((v) => v.field === field)) {\n this.config.valueFields = [...current, { field, aggFunc }];\n }\n\n this.removeFromOtherZones(field, 'values');\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private removeValueField(field: string): void {\n this.config.valueFields = (this.config.valueFields ?? []).filter((v) => v.field !== field);\n if (this.isActive) this.refresh();\n this.refreshPanel();\n }\n\n private updateValueAggFunc(field: string, aggFunc: AggFunc): void {\n const valueFields = this.config.valueFields ?? [];\n const fieldIndex = valueFields.findIndex((v) => v.field === field);\n if (fieldIndex >= 0) {\n valueFields[fieldIndex] = { ...valueFields[fieldIndex], aggFunc };\n this.config.valueFields = [...valueFields];\n }\n if (this.isActive) this.refresh();\n }\n\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n\n // #endregion\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 based on its own metadata.\n * This checks column-level properties like lockPosition and suppressMovable.\n *\n * Note: For full movability checks including plugin constraints (e.g., pinned columns),\n * use `grid.queryPlugins({ type: PLUGIN_QUERIES.CAN_MOVE_COLUMN, context: column })`\n * which queries all plugins via the generic plugin query system.\n *\n * @param column - The column configuration to check\n * @returns True if the column can be moved based on its metadata\n */\nexport function canMoveColumn<TRow = unknown>(column: ColumnConfig<TRow>): boolean {\n // Check for lockPosition or suppressMovable properties in the column config\n const meta = column.meta ?? {};\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","/**\n * Column Reordering Plugin (Class-based)\n *\n * Provides drag-and-drop column reordering functionality for tbw-grid.\n * Supports keyboard and mouse interactions with visual feedback.\n * Uses FLIP animation technique for smooth column transitions.\n *\n * Animation respects grid-level animation.mode setting but style is plugin-configured.\n */\n\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { BaseGridPlugin, PLUGIN_QUERIES } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridConfig } from '../../core/types';\nimport { canMoveColumn, moveColumn } from './column-drag';\nimport styles from './reorder.css?inline';\nimport type { ColumnMoveDetail, ReorderConfig } from './types';\n\n/** Extended grid interface with column order methods */\ninterface GridWithColumnOrder {\n setColumnOrder(order: string[]): void;\n getColumnOrder(): string[];\n requestStateChange?: () => void;\n /** Query plugins for inter-plugin communication */\n queryPlugins<T>(query: { type: string; context: unknown }): T[];\n /** Effective grid config */\n effectiveConfig?: GridConfig;\n}\n\n/**\n * Column Reordering Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ReorderPlugin()\n * ```\n */\nexport class ReorderPlugin extends BaseGridPlugin<ReorderConfig> {\n readonly name = 'reorder';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ReorderConfig> {\n return {\n animation: 'flip', // Plugin's own default\n };\n }\n\n /**\n * Resolve animation type from plugin config.\n * Respects grid-level animation.mode (disabled = no animation).\n */\n private get animationType(): false | 'flip' | 'fade' {\n // Check if animations are globally disabled\n if (!this.isAnimationEnabled) return false;\n\n // Plugin config (with default from defaultConfig)\n if (this.config.animation !== undefined) return this.config.animation;\n\n // Legacy viewTransition fallback\n if (this.config.viewTransition === false) return false;\n if (this.config.viewTransition === true) return 'flip';\n\n return 'flip'; // Plugin default\n }\n\n /**\n * Check if animations are enabled at the grid level.\n * Respects gridConfig.animation.mode and CSS variable.\n */\n private get isAnimationEnabled(): boolean {\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n // Explicit off = disabled\n if (mode === false || mode === 'off') return false;\n\n // Explicit on = always enabled\n if (mode === true || mode === 'on') return true;\n\n // reduced-motion: check CSS variable (set by grid based on media query)\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const enabled = getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim();\n return enabled !== '0';\n }\n\n return true; // Default to enabled\n }\n\n /**\n * Get animation duration from CSS variable (set by grid config).\n */\n private get animationDuration(): number {\n // Plugin config override\n if (this.config.animationDuration !== undefined) {\n return this.config.animationDuration;\n }\n\n // Read from CSS variable (already set by grid from gridConfig.animation.duration)\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host) {\n const durationStr = getComputedStyle(host).getPropertyValue('--tbw-animation-duration').trim();\n const parsed = parseInt(durationStr, 10);\n if (!isNaN(parsed)) return parsed;\n }\n\n return 200; // Default\n }\n\n // #region Internal State\n private isDragging = false;\n private draggedField: string | null = null;\n private draggedIndex: number | null = null;\n private dropIndex: number | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\n super.attach(grid);\n\n // Listen for reorder requests from other plugins (e.g., VisibilityPlugin)\n // Uses disconnectSignal for automatic cleanup - no need for manual removeEventListener\n (grid as unknown as HTMLElement).addEventListener(\n 'column-reorder-request',\n (e: Event) => {\n const detail = (e as CustomEvent).detail;\n if (detail?.field && typeof detail.toIndex === 'number') {\n this.moveColumn(detail.field, detail.toIndex);\n }\n },\n { signal: this.disconnectSignal },\n );\n }\n\n override detach(): void {\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n }\n // #endregion\n\n // #region Hooks\n\n override afterRender(): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const headers = shadowRoot.querySelectorAll('.header-row > .cell');\n\n headers.forEach((header) => {\n const headerEl = header as HTMLElement;\n const field = headerEl.getAttribute('data-field');\n if (!field) return;\n\n const column = this.columns.find((c) => c.field === field);\n // Check both local metadata and plugin queries (e.g., PinnedColumnsPlugin)\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const pluginResponses = gridEl.queryPlugins<boolean>({\n type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n context: column as ColumnConfig,\n });\n const pluginAllows = !pluginResponses.includes(false);\n if (!column || !canMoveColumn(column) || !pluginAllows) {\n headerEl.draggable = false;\n return;\n }\n\n headerEl.draggable = true;\n\n // Remove existing listeners to prevent duplicates\n if (headerEl.getAttribute('data-dragstart-bound')) return;\n headerEl.setAttribute('data-dragstart-bound', 'true');\n\n headerEl.addEventListener('dragstart', (e: DragEvent) => {\n const currentOrder = this.getColumnOrder();\n const orderIndex = currentOrder.indexOf(field);\n this.isDragging = true;\n this.draggedField = field;\n this.draggedIndex = orderIndex;\n\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', field);\n }\n\n headerEl.classList.add('dragging');\n });\n\n headerEl.addEventListener('dragend', () => {\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n\n shadowRoot.querySelectorAll('.header-row > .cell').forEach((h) => {\n h.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\n });\n });\n\n headerEl.addEventListener('dragover', (e: DragEvent) => {\n e.preventDefault();\n if (!this.isDragging || this.draggedField === field) return;\n\n const rect = headerEl.getBoundingClientRect();\n const midX = rect.left + rect.width / 2;\n\n const currentOrder = this.getColumnOrder();\n const orderIndex = currentOrder.indexOf(field);\n this.dropIndex = e.clientX < midX ? orderIndex : orderIndex + 1;\n\n headerEl.classList.add('drop-target');\n headerEl.classList.toggle('drop-before', e.clientX < midX);\n headerEl.classList.toggle('drop-after', e.clientX >= midX);\n });\n\n headerEl.addEventListener('dragleave', () => {\n headerEl.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n headerEl.addEventListener('drop', (e: DragEvent) => {\n e.preventDefault();\n const draggedField = this.draggedField;\n const draggedIndex = this.draggedIndex;\n const dropIndex = this.dropIndex;\n\n if (!this.isDragging || draggedField === null || draggedIndex === null || dropIndex === null) {\n return;\n }\n\n const effectiveToIndex = dropIndex > draggedIndex ? dropIndex - 1 : dropIndex;\n const currentOrder = this.getColumnOrder();\n const newOrder = moveColumn(currentOrder, draggedIndex, effectiveToIndex);\n\n const detail: ColumnMoveDetail = {\n field: draggedField,\n fromIndex: draggedIndex,\n toIndex: effectiveToIndex,\n columnOrder: newOrder,\n };\n\n // Update the grid's column order (with optional view transition)\n this.updateColumnOrder(newOrder);\n\n this.emit('column-move', detail);\n });\n });\n }\n\n /**\n * Handle Alt+Arrow keyboard shortcuts for column reordering.\n */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n if (!event.altKey || (event.key !== 'ArrowLeft' && event.key !== 'ArrowRight')) {\n return;\n }\n\n const grid = this.grid as unknown as { _focusCol: number; _visibleColumns: ColumnConfig[] };\n const focusCol = grid._focusCol;\n const columns = grid._visibleColumns;\n\n if (focusCol < 0 || focusCol >= columns.length) return;\n\n const column = columns[focusCol];\n if (!column || !canMoveColumn(column)) return;\n\n // Check plugin queries (e.g., PinnedColumnsPlugin)\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const pluginResponses = gridEl.queryPlugins<boolean>({\n type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n context: column,\n });\n if (pluginResponses.includes(false)) return;\n\n const currentOrder = this.getColumnOrder();\n const fromIndex = currentOrder.indexOf(column.field);\n if (fromIndex === -1) return;\n\n const toIndex = event.key === 'ArrowLeft' ? fromIndex - 1 : fromIndex + 1;\n\n // Check bounds\n if (toIndex < 0 || toIndex >= currentOrder.length) return;\n\n // Check if target position is allowed (e.g., not into pinned area)\n const targetColumn = columns.find((c) => c.field === currentOrder[toIndex]);\n if (targetColumn) {\n const targetResponses = gridEl.queryPlugins<boolean>({\n type: PLUGIN_QUERIES.CAN_MOVE_COLUMN,\n context: targetColumn,\n });\n if (targetResponses.includes(false)) return;\n }\n\n this.moveColumn(column.field, toIndex);\n\n // Update focus to follow the moved column and refresh visual focus state\n grid._focusCol = toIndex;\n ensureCellVisible(this.grid as any);\n\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Get the current column order from the grid.\n * @returns Array of field names in display order\n */\n getColumnOrder(): string[] {\n return (this.grid as unknown as GridWithColumnOrder).getColumnOrder();\n }\n\n /**\n * Move a column to a new position.\n * @param field - The field name of the column to move\n * @param toIndex - The target index\n */\n moveColumn(field: string, toIndex: number): void {\n const currentOrder = this.getColumnOrder();\n const fromIndex = currentOrder.indexOf(field);\n if (fromIndex === -1) return;\n\n const newOrder = moveColumn(currentOrder, fromIndex, toIndex);\n\n // Update with view transition\n this.updateColumnOrder(newOrder);\n\n this.emit<ColumnMoveDetail>('column-move', {\n field,\n fromIndex,\n toIndex,\n columnOrder: newOrder,\n });\n }\n\n /**\n * Set a specific column order.\n * @param order - Array of field names in desired order\n */\n setColumnOrder(order: string[]): void {\n this.updateColumnOrder(order);\n }\n\n /**\n * Reset column order to the original configuration order.\n */\n resetColumnOrder(): void {\n const originalOrder = this.columns.map((c) => c.field);\n this.updateColumnOrder(originalOrder);\n }\n // #endregion\n\n // #region View Transition\n\n /**\n * Capture header cell positions before reorder.\n */\n private captureHeaderPositions(): Map<string, number> {\n const positions = new Map<string, number>();\n this.shadowRoot?.querySelectorAll('.header-row > .cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (field) positions.set(field, cell.getBoundingClientRect().left);\n });\n return positions;\n }\n\n /**\n * Apply FLIP animation for column reorder.\n * Uses CSS transitions - JS sets initial transform and toggles class.\n * @param oldPositions - Header positions captured before DOM change\n */\n private animateFLIP(oldPositions: Map<string, number>): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot || oldPositions.size === 0) return;\n\n // Compute deltas from header cells (stable reference points)\n const deltas = new Map<string, number>();\n shadowRoot.querySelectorAll('.header-row > .cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n const oldLeft = oldPositions.get(field);\n if (oldLeft === undefined) return;\n const deltaX = oldLeft - cell.getBoundingClientRect().left;\n if (Math.abs(deltaX) > 1) deltas.set(field, deltaX);\n });\n\n if (deltas.size === 0) return;\n\n // Set initial transform (First → Last position offset)\n const cells: HTMLElement[] = [];\n shadowRoot.querySelectorAll('.cell[data-field]').forEach((cell) => {\n const deltaX = deltas.get(cell.getAttribute('data-field') ?? '');\n if (deltaX !== undefined) {\n const el = cell as HTMLElement;\n el.style.transform = `translateX(${deltaX}px)`;\n cells.push(el);\n }\n });\n\n if (cells.length === 0) return;\n\n // Force reflow then animate to final position via CSS transition\n void (shadowRoot.host as HTMLElement).offsetHeight;\n\n const duration = this.animationDuration;\n\n requestAnimationFrame(() => {\n cells.forEach((el) => {\n el.classList.add('flip-animating');\n el.style.transform = '';\n });\n\n // Cleanup after animation\n setTimeout(() => {\n cells.forEach((el) => {\n el.style.transform = '';\n el.classList.remove('flip-animating');\n });\n }, duration + 50);\n });\n }\n\n /**\n * Apply crossfade animation for moved columns.\n * Uses CSS keyframes - JS just toggles classes.\n */\n private animateFade(applyChange: () => void): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) {\n applyChange();\n return;\n }\n\n // Capture old positions to detect which columns moved\n const oldPositions = this.captureHeaderPositions();\n\n // Apply the change first\n applyChange();\n\n // Find which columns changed position\n const movedFields = new Set<string>();\n shadowRoot.querySelectorAll('.header-row > .cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n const oldLeft = oldPositions.get(field);\n if (oldLeft === undefined) return;\n const newLeft = cell.getBoundingClientRect().left;\n if (Math.abs(oldLeft - newLeft) > 1) {\n movedFields.add(field);\n }\n });\n\n if (movedFields.size === 0) return;\n\n // Add animation class to moved columns (headers + body cells)\n const cells: HTMLElement[] = [];\n shadowRoot.querySelectorAll('.cell[data-field]').forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (field && movedFields.has(field)) {\n const el = cell as HTMLElement;\n el.classList.add('fade-animating');\n cells.push(el);\n }\n });\n\n if (cells.length === 0) return;\n\n // Remove class after animation completes\n const duration = this.animationDuration;\n setTimeout(() => {\n cells.forEach((el) => el.classList.remove('fade-animating'));\n }, duration + 50);\n }\n\n /**\n * Update column order with configured animation.\n */\n private updateColumnOrder(newOrder: string[]): void {\n const gridEl = this.grid as unknown as GridWithColumnOrder;\n const animation = this.animationType;\n\n if (animation === 'flip' && this.shadowRoot) {\n const oldPositions = this.captureHeaderPositions();\n gridEl.setColumnOrder(newOrder);\n void (this.shadowRoot.host as HTMLElement).offsetHeight;\n this.animateFLIP(oldPositions);\n } else if (animation === 'fade') {\n this.animateFade(() => gridEl.setColumnOrder(newOrder));\n } else {\n gridEl.setColumnOrder(newOrder);\n }\n\n gridEl.requestStateChange?.();\n }\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\n}\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 { clearCellFocus, getRowIndexFromCell } from '../../core/internal/utils';\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 styles from './selection.css?inline';\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 // #region 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 /** Pending keyboard navigation update (processed in afterRender) */\n private pendingKeyboardUpdate: { shiftKey: boolean } | null = null;\n\n /** Cell selection state (cell mode) */\n private selectedCell: { row: number; col: number } | null = null;\n\n // #endregion\n\n // #region 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 this.pendingKeyboardUpdate = null;\n }\n\n // #endregion\n\n // #region 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 const navKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight', 'Tab', 'Home', 'End', 'PageUp', 'PageDown'];\n const isNavKey = navKeys.includes(event.key);\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 // CELL MODE: Selection follows focus\n if (mode === 'cell' && isNavKey) {\n // Use queueMicrotask so grid's handler runs first and updates focusRow/focusCol\n queueMicrotask(() => {\n this.selectedCell = { row: this.grid._focusRow, col: this.grid._focusCol };\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n });\n return false; // Let grid handle navigation\n }\n\n // ROW MODE: Only Up/Down arrows move row selection\n if (mode === 'row' && (event.key === 'ArrowUp' || event.key === 'ArrowDown')) {\n // Let grid move focus first, then sync row selection\n queueMicrotask(() => {\n this.selected.clear();\n this.selected.add(this.grid._focusRow);\n this.lastSelected = this.grid._focusRow;\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n this.requestAfterRender();\n });\n return false; // Let grid handle navigation\n }\n\n // RANGE MODE: Shift+Arrow extends, plain Arrow resets\n // Tab key always navigates without extending (even with Shift)\n if (mode === 'range' && isNavKey) {\n // Tab should not extend selection - it just navigates to the next/previous cell\n const isTabKey = event.key === 'Tab';\n const shouldExtend = event.shiftKey && !isTabKey;\n\n // Capture anchor BEFORE grid moves focus (synchronous)\n // This ensures the anchor is the starting point, not the destination\n if (shouldExtend && !this.cellAnchor) {\n this.cellAnchor = { row: this.grid._focusRow, col: this.grid._focusCol };\n }\n\n // Mark pending update - will be processed in afterRender when grid updates focus\n this.pendingKeyboardUpdate = { shiftKey: shouldExtend };\n\n // Schedule afterRender to run after grid's keyboard handler completes\n // Grid's refreshVirtualWindow(false) skips afterRender for performance,\n // so we explicitly request it to process pendingKeyboardUpdate\n queueMicrotask(() => this.requestAfterRender());\n\n return false; // Let grid handle navigation\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 /**\n * Apply selection classes to visible cells/rows.\n * Shared by afterRender and onScrollRender.\n */\n #applySelectionClasses(): void {\n const shadowRoot = this.shadowRoot;\n if (!shadowRoot) return;\n\n const { mode } = this.config;\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', 'row-focus');\n });\n\n // ROW MODE: Add row-focus class to selected rows, disable cell-focus\n if (mode === 'row') {\n // In row mode, disable ALL cell-focus styling - row selection takes precedence\n clearCellFocus(shadowRoot);\n\n allRows.forEach((row) => {\n const firstCell = row.querySelector('.cell[data-row]');\n const rowIndex = getRowIndexFromCell(firstCell);\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 // Clear all cell-focus first - selection plugin manages focus styling in range mode\n clearCellFocus(shadowRoot);\n\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 // CELL MODE: Remove cell-focus when selection plugin handles focus\n if (mode === 'cell' && this.selectedCell) {\n // Remove all cell-focus - the selection plugin manages focus styling\n clearCellFocus(shadowRoot);\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 // Process pending keyboard navigation update (range mode)\n // This runs AFTER the grid has updated focusRow/focusCol\n if (this.pendingKeyboardUpdate && mode === 'range') {\n const { shiftKey } = this.pendingKeyboardUpdate;\n this.pendingKeyboardUpdate = null;\n\n const currentRow = this.grid._focusRow;\n const currentCol = this.grid._focusCol;\n\n if (shiftKey && this.cellAnchor) {\n // Extend selection from anchor to current focus\n const newRange = createRangeFromAnchor(this.cellAnchor, { row: currentRow, col: currentCol });\n this.ranges = [newRange];\n this.activeRange = newRange;\n } else if (!shiftKey) {\n // Without shift, clear selection (cell-focus will show instead)\n this.ranges = [];\n this.activeRange = null;\n this.cellAnchor = { row: currentRow, col: currentCol }; // Reset anchor to current position\n }\n\n this.emit<SelectionChangeDetail>('selection-change', this.#buildEvent());\n }\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 this.#applySelectionClasses();\n }\n\n /**\n * Called after scroll-triggered row rendering.\n * Reapplies selection classes to recycled DOM elements.\n */\n override onScrollRender(): void {\n this.#applySelectionClasses();\n }\n\n // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n\n // #endregion\n}\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 pageSize: 100,\n cacheBlockSize: 100,\n maxConcurrentRequests: 2,\n };\n }\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\n\n // #region 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 // #endregion\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\n *\n * Enables hierarchical tree data with expand/collapse, sorting, and auto-detection.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin, CellClickEvent, HeaderClickEvent } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridConfig } from '../../core/types';\nimport { collapseAll, expandAll, expandToKey, toggleExpand } from './tree-data';\nimport { detectTreeStructure, inferChildrenField } from './tree-detect';\nimport styles from './tree.css?inline';\nimport type { ExpandCollapseAnimation, FlattenedTreeRow, TreeConfig, TreeExpandDetail } from './types';\n\ninterface GridWithConfig {\n effectiveConfig?: GridConfig;\n _sortState?: { field: string; direction: 1 | -1 } | null;\n}\n\n/**\n * Tree Data Plugin for tbw-grid\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 override readonly styles = styles;\n\n protected override get defaultConfig(): Partial<TreeConfig> {\n return {\n childrenField: 'children',\n autoDetect: true,\n defaultExpanded: false,\n indentWidth: 20,\n showExpandIcons: true,\n animation: 'slide',\n };\n }\n\n // #region State\n\n private expandedKeys = new Set<string>();\n private initialExpansionDone = false;\n private flattenedRows: FlattenedTreeRow[] = [];\n private rowKeyMap = new Map<string, FlattenedTreeRow>();\n private previousVisibleKeys = new Set<string>();\n private keysToAnimate = new Set<string>();\n private sortState: { field: string; direction: 1 | -1 } | null = null;\n\n override detach(): void {\n this.expandedKeys.clear();\n this.initialExpansionDone = false;\n this.flattenedRows = [];\n this.rowKeyMap.clear();\n this.previousVisibleKeys.clear();\n this.keysToAnimate.clear();\n this.sortState = null;\n }\n\n // #endregion\n\n // #region Animation\n\n private get animationStyle(): ExpandCollapseAnimation {\n const gridEl = this.grid as unknown as GridWithConfig;\n const mode = gridEl.effectiveConfig?.animation?.mode ?? 'reduced-motion';\n\n if (mode === false || mode === 'off') return false;\n if (mode !== true && mode !== 'on') {\n const host = this.shadowRoot?.host as HTMLElement | undefined;\n if (host && getComputedStyle(host).getPropertyValue('--tbw-animation-enabled').trim() === '0') {\n return false;\n }\n }\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Auto-Detection\n\n detect(rows: readonly unknown[]): boolean {\n if (!this.config.autoDetect) return false;\n const field = this.config.childrenField ?? inferChildrenField(rows as any[]) ?? 'children';\n return detectTreeStructure(rows as any[], field);\n }\n\n // #endregion\n\n // #region Data Processing\n\n override processRows(rows: readonly unknown[]): any[] {\n const childrenField = this.config.childrenField ?? 'children';\n\n if (!detectTreeStructure(rows as any[], childrenField)) {\n this.flattenedRows = [];\n this.rowKeyMap.clear();\n this.previousVisibleKeys.clear();\n return [...rows];\n }\n\n // Assign stable keys, then optionally sort\n let data = this.withStableKeys(rows as any[]);\n if (this.sortState) {\n data = this.sortTree(data, this.sortState.field, this.sortState.direction);\n }\n\n // Initialize expansion if needed\n if (this.config.defaultExpanded && !this.initialExpansionDone) {\n this.expandedKeys = expandAll(data, this.config);\n this.initialExpansionDone = true;\n }\n\n // Flatten and track animations\n this.flattenedRows = this.flattenTree(data, this.expandedKeys);\n this.rowKeyMap.clear();\n this.keysToAnimate.clear();\n const currentKeys = new Set<string>();\n\n for (const row of this.flattenedRows) {\n this.rowKeyMap.set(row.key, row);\n currentKeys.add(row.key);\n if (!this.previousVisibleKeys.has(row.key) && row.depth > 0) {\n this.keysToAnimate.add(row.key);\n }\n }\n this.previousVisibleKeys = currentKeys;\n\n return this.flattenedRows.map((r) => ({\n ...r.data,\n __treeKey: r.key,\n __treeDepth: r.depth,\n __treeHasChildren: r.hasChildren,\n __treeExpanded: r.isExpanded,\n }));\n }\n\n /** Assign stable keys to rows (preserves key across sort operations) */\n private withStableKeys(rows: any[], parentKey: string | null = null): any[] {\n const childrenField = this.config.childrenField ?? 'children';\n return rows.map((row, i) => {\n const key =\n row.id !== undefined ? String(row.id) : (row.__stableKey ?? (parentKey ? `${parentKey}-${i}` : String(i)));\n const children = row[childrenField];\n const hasChildren = Array.isArray(children) && children.length > 0;\n return {\n ...row,\n __stableKey: key,\n ...(hasChildren ? { [childrenField]: this.withStableKeys(children, key) } : {}),\n };\n });\n }\n\n /** Flatten tree using stable keys */\n private flattenTree(rows: any[], expanded: Set<string>, depth = 0): FlattenedTreeRow[] {\n const childrenField = this.config.childrenField ?? 'children';\n const result: FlattenedTreeRow[] = [];\n\n for (const row of rows) {\n const key = row.__stableKey ?? row.id ?? '?';\n const children = row[childrenField];\n const hasChildren = Array.isArray(children) && children.length > 0;\n const isExpanded = expanded.has(key);\n\n result.push({\n key,\n data: row,\n depth,\n hasChildren,\n isExpanded,\n parentKey: depth > 0 ? key.substring(0, key.lastIndexOf('-')) || null : null,\n });\n\n if (hasChildren && isExpanded) {\n result.push(...this.flattenTree(children, expanded, depth + 1));\n }\n }\n return result;\n }\n\n /** Sort tree recursively, keeping children with parents */\n private sortTree(rows: any[], field: string, dir: 1 | -1): any[] {\n const childrenField = this.config.childrenField ?? 'children';\n const sorted = [...rows].sort((a, b) => {\n const aVal = a[field],\n bVal = b[field];\n if (aVal == null && bVal == null) return 0;\n if (aVal == null) return -1;\n if (bVal == null) return 1;\n return aVal > bVal ? dir : aVal < bVal ? -dir : 0;\n });\n return sorted.map((row) => {\n const children = row[childrenField];\n return Array.isArray(children) && children.length > 0\n ? { ...row, [childrenField]: this.sortTree(children, field, dir) }\n : row;\n });\n }\n\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (this.flattenedRows.length === 0) return [...columns];\n\n const cols = [...columns] as ColumnConfig[];\n if (cols.length === 0) return cols;\n\n const firstCol = { ...cols[0] };\n const original = firstCol.viewRenderer;\n if ((original as any)?.__treeWrapped) return cols;\n\n const getConfig = () => this.config;\n const setIcon = this.setIcon.bind(this);\n const resolveIcon = this.resolveIcon.bind(this);\n\n const wrapped = (ctx: Parameters<NonNullable<typeof original>>[0]) => {\n const { value, row } = ctx;\n const { indentWidth = 20, showExpandIcons = true } = getConfig();\n\n const container = document.createElement('span');\n container.className = 'tree-cell';\n container.style.setProperty('--tree-depth', String(row.__treeDepth ?? 0));\n container.style.setProperty('--tbw-tree-indent', `${indentWidth}px`);\n\n if (row.__treeHasChildren && showExpandIcons) {\n const icon = document.createElement('span');\n icon.className = `tree-toggle${row.__treeExpanded ? ' expanded' : ''}`;\n setIcon(icon, resolveIcon(row.__treeExpanded ? 'collapse' : 'expand'));\n icon.setAttribute('data-tree-key', row.__treeKey);\n container.appendChild(icon);\n } else if (showExpandIcons) {\n const spacer = document.createElement('span');\n spacer.className = 'tree-spacer';\n container.appendChild(spacer);\n }\n\n const content = document.createElement('span');\n if (original) {\n const rendered = original(ctx);\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 return container;\n };\n\n (wrapped as any).__treeWrapped = true;\n firstCol.viewRenderer = wrapped;\n cols[0] = firstCol;\n return cols;\n }\n\n // #endregion\n\n // #region 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 const flatRow = key ? this.rowKeyMap.get(key) : null;\n if (!flatRow) return false;\n\n this.expandedKeys = toggleExpand(this.expandedKeys, key!);\n this.emit<TreeExpandDetail>('tree-expand', {\n key: key!,\n row: flatRow.data,\n expanded: this.expandedKeys.has(key!),\n depth: flatRow.depth,\n });\n this.requestRender();\n return true;\n }\n\n override onHeaderClick(event: HeaderClickEvent): boolean {\n if (this.flattenedRows.length === 0 || !event.column.sortable) return false;\n\n const { field } = event.column;\n if (!this.sortState || this.sortState.field !== field) {\n this.sortState = { field, direction: 1 };\n } else if (this.sortState.direction === 1) {\n this.sortState = { field, direction: -1 };\n } else {\n this.sortState = null;\n }\n\n // Sync grid sort indicator\n const gridEl = this.grid as unknown as GridWithConfig;\n if (gridEl._sortState !== undefined) {\n gridEl._sortState = this.sortState ? { ...this.sortState } : null;\n }\n\n this.emit('sort-change', { field, direction: this.sortState?.direction ?? 0 });\n this.requestRender();\n return true;\n }\n\n override afterRender(): void {\n const style = this.animationStyle;\n if (style === false || this.keysToAnimate.size === 0) return;\n\n const body = this.shadowRoot?.querySelector('.rows');\n if (!body) return;\n\n const animClass = style === 'fade' ? 'tbw-tree-fade-in' : 'tbw-tree-slide-in';\n for (const rowEl of body.querySelectorAll('.data-grid-row')) {\n const cell = rowEl.querySelector('.cell[data-row]');\n const idx = cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n const key = this.flattenedRows[idx]?.key;\n\n if (key && this.keysToAnimate.has(key)) {\n rowEl.classList.add(animClass);\n rowEl.addEventListener('animationend', () => rowEl.classList.remove(animClass), { once: true });\n }\n }\n this.keysToAnimate.clear();\n }\n\n // #endregion\n\n // #region Public API\n\n expand(key: string): void {\n this.expandedKeys.add(key);\n this.requestRender();\n }\n\n collapse(key: string): void {\n this.expandedKeys.delete(key);\n this.requestRender();\n }\n\n toggle(key: string): void {\n this.expandedKeys = toggleExpand(this.expandedKeys, key);\n this.requestRender();\n }\n\n expandAll(): void {\n this.expandedKeys = expandAll(this.rows as any[], this.config);\n this.requestRender();\n }\n\n collapseAll(): void {\n this.expandedKeys = collapseAll();\n this.requestRender();\n }\n\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n getExpandedKeys(): string[] {\n return [...this.expandedKeys];\n }\n\n getFlattenedRows(): FlattenedTreeRow[] {\n return [...this.flattenedRows];\n }\n\n getRowByKey(key: string): any | undefined {\n return this.rowKeyMap.get(key)?.data;\n }\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 // #endregion\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 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 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 // #region 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 // #endregion\n}\n","/**\n * Column Visibility Plugin (Class-based)\n *\n * Provides a UI for column visibility control via the shell's tool panel system.\n * Column visibility is a core grid feature - this plugin provides:\n * - A tool panel for column visibility management (registered with the shell)\n * - Backward-compatible API methods that delegate to grid.setColumnVisible(), etc.\n *\n * The grid emits 'column-visibility' events when columns are shown/hidden,\n * allowing consumers to save user preferences.\n *\n * When a reorder plugin is present, column rows become draggable for reordering.\n * Drag-drop emits 'column-reorder-request' events that the ReorderPlugin can listen for.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, ToolPanelDefinition } from '../../core/types';\nimport type { VisibilityConfig } from './types';\nimport styles from './visibility.css?inline';\n\n/**\n * Detail for column-reorder-request events emitted when users drag-drop in the visibility panel.\n */\nexport interface ColumnReorderRequestDetail {\n /** The field name of the column to move */\n field: string;\n /** The source index (before move) */\n fromIndex: number;\n /** The target index (after move) */\n toIndex: number;\n}\n\n/**\n * Check if a column can be moved (respects lockPosition/suppressMovable).\n * Inlined to avoid importing from reorder plugin.\n */\nfunction canMoveColumn(column: ColumnConfig): boolean {\n const meta = column.meta ?? {};\n return meta.lockPosition !== true && meta.suppressMovable !== true;\n}\n\n/** Extended grid interface with visibility methods */\ninterface GridWithVisibility {\n shadowRoot: ShadowRoot | null;\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }>;\n setColumnVisible(field: string, visible: boolean): void;\n toggleColumnVisibility(field: string): void;\n showAllColumns(): void;\n isColumnVisible(field: string): boolean;\n requestRender(): void;\n openToolPanel(id: string): void;\n closeToolPanel(): void;\n toggleToolPanel(id: string): void;\n activeToolPanel: string | undefined;\n}\n\n/**\n * Column Visibility Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new VisibilityPlugin({ enabled: true, allowHideAll: false })\n * ```\n */\nexport class VisibilityPlugin extends BaseGridPlugin<VisibilityConfig> {\n readonly name = 'visibility';\n override readonly version = '1.0.0';\n\n /** Tool panel ID for shell integration */\n static readonly PANEL_ID = 'columns';\n\n protected override get defaultConfig(): Partial<VisibilityConfig> {\n return {\n allowHideAll: false,\n };\n }\n\n // #region Internal State\n private columnListElement: HTMLElement | null = null;\n\n // Drag state for reorder integration\n private isDragging = false;\n private draggedField: string | null = null;\n private draggedIndex: number | null = null;\n private dropIndex: number | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.columnListElement = null;\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n }\n // #endregion\n\n // #region Shell Integration\n\n /**\n * Register the column visibility tool panel with the shell.\n */\n override getToolPanel(): ToolPanelDefinition | undefined {\n return {\n id: VisibilityPlugin.PANEL_ID,\n title: 'Columns',\n icon: '☰',\n tooltip: 'Column visibility',\n order: 100, // High order so it appears last\n render: (container) => this.renderPanelContent(container),\n };\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Show the visibility sidebar panel.\n */\n show(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.openToolPanel(VisibilityPlugin.PANEL_ID);\n }\n\n /**\n * Hide the visibility sidebar panel.\n */\n hide(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.closeToolPanel();\n }\n\n /**\n * Toggle the visibility sidebar panel.\n */\n toggle(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.toggleToolPanel(VisibilityPlugin.PANEL_ID);\n }\n\n /**\n * Check if a specific column is visible.\n * Delegates to grid.isColumnVisible().\n * @param field - The field name to check\n * @returns True if the column is visible\n */\n isColumnVisible(field: string): boolean {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid.isColumnVisible(field);\n }\n\n /**\n * Set visibility for a specific column.\n * Delegates to grid.setColumnVisible().\n * @param field - The field name of the column\n * @param visible - Whether the column should be visible\n */\n setColumnVisible(field: string, visible: boolean): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.setColumnVisible(field, visible);\n }\n\n /**\n * Get list of all visible column fields.\n * @returns Array of visible field names\n */\n getVisibleColumns(): string[] {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid\n .getAllColumns()\n .filter((c) => c.visible)\n .map((c) => c.field);\n }\n\n /**\n * Get list of all hidden column fields.\n * @returns Array of hidden field names\n */\n getHiddenColumns(): string[] {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid\n .getAllColumns()\n .filter((c) => !c.visible)\n .map((c) => c.field);\n }\n\n /**\n * Show all columns.\n * Delegates to grid.showAllColumns().\n */\n showAll(): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.showAllColumns();\n }\n\n /**\n * Toggle visibility for a specific column.\n * Delegates to grid.toggleColumnVisibility().\n * @param field - The field name of the column\n */\n toggleColumn(field: string): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.toggleColumnVisibility(field);\n }\n\n /**\n * Show a specific column.\n * Delegates to grid.setColumnVisible().\n * @param field - The field name of the column to show\n */\n showColumn(field: string): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.setColumnVisible(field, true);\n }\n\n /**\n * Hide a specific column.\n * Delegates to grid.setColumnVisible().\n * @param field - The field name of the column to hide\n */\n hideColumn(field: string): void {\n const grid = this.grid as unknown as GridWithVisibility;\n grid.setColumnVisible(field, false);\n }\n\n /**\n * Get all columns with their visibility status.\n * Useful for building visibility UI.\n * @returns Array of column info with visibility status\n */\n getAllColumns(): Array<{ field: string; header: string; visible: boolean; lockVisible?: boolean }> {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid.getAllColumns();\n }\n\n /**\n * Check if the sidebar panel is currently open.\n * @returns True if the panel is open\n */\n isPanelVisible(): boolean {\n const grid = this.grid as unknown as GridWithVisibility;\n return grid.activeToolPanel === VisibilityPlugin.PANEL_ID;\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Render the panel content into the shell's tool panel container.\n * Returns a cleanup function.\n */\n private renderPanelContent(container: HTMLElement): (() => void) | void {\n const grid = this.grid as unknown as GridWithVisibility;\n\n // Create content wrapper\n const wrapper = document.createElement('div');\n wrapper.className = 'tbw-visibility-content';\n\n // Column list container\n const columnList = document.createElement('div');\n columnList.className = 'tbw-visibility-list';\n wrapper.appendChild(columnList);\n\n // Show all button\n const showAllBtn = document.createElement('button');\n showAllBtn.className = 'tbw-visibility-show-all';\n showAllBtn.textContent = 'Show All';\n showAllBtn.addEventListener('click', () => {\n grid.showAllColumns();\n this.rebuildToggles(columnList);\n });\n wrapper.appendChild(showAllBtn);\n\n // Store reference\n this.columnListElement = columnList;\n\n // Build initial toggles\n this.rebuildToggles(columnList);\n\n // Append to container\n container.appendChild(wrapper);\n\n // Return cleanup function\n return () => {\n this.columnListElement = null;\n wrapper.remove();\n };\n }\n\n /**\n * Check if a reorder plugin is present (by name to avoid static import).\n */\n private hasReorderPlugin(): boolean {\n const plugin = this.grid?.getPluginByName?.('reorder');\n // Duck-type check - just verify the plugin exists and has a moveColumn method\n return !!(plugin && typeof (plugin as { moveColumn?: unknown }).moveColumn === 'function');\n }\n\n /**\n * Build the column toggle checkboxes.\n * When a reorder plugin is present, adds drag handles for reordering.\n */\n private rebuildToggles(columnList: HTMLElement): void {\n const grid = this.grid as unknown as GridWithVisibility;\n const reorderEnabled = this.hasReorderPlugin();\n\n columnList.innerHTML = '';\n\n // getAllColumns() now returns columns in their effective display order\n const allColumns = grid.getAllColumns();\n\n for (let i = 0; i < allColumns.length; i++) {\n const col = allColumns[i];\n const label = col.header || col.field;\n\n const row = document.createElement('div');\n row.className = col.lockVisible ? 'tbw-visibility-row locked' : 'tbw-visibility-row';\n row.setAttribute('data-field', col.field);\n row.setAttribute('data-index', String(i));\n\n // Add drag handle if reorder is enabled\n if (reorderEnabled && canMoveColumn(col as unknown as ColumnConfig)) {\n row.draggable = true;\n row.classList.add('reorderable');\n\n this.setupDragListeners(row, col.field, i, columnList);\n }\n\n const labelWrapper = document.createElement('label');\n labelWrapper.className = 'tbw-visibility-label';\n\n const checkbox = document.createElement('input');\n checkbox.type = 'checkbox';\n checkbox.checked = col.visible;\n checkbox.disabled = col.lockVisible ?? false;\n checkbox.addEventListener('change', () => {\n grid.toggleColumnVisibility(col.field);\n // Refresh after toggle (grid may re-render)\n setTimeout(() => this.rebuildToggles(columnList), 0);\n });\n\n const text = document.createElement('span');\n text.textContent = label;\n\n labelWrapper.appendChild(checkbox);\n labelWrapper.appendChild(text);\n\n // Add drag handle icon if reorderable\n if (reorderEnabled && canMoveColumn(col as unknown as ColumnConfig)) {\n const handle = document.createElement('span');\n handle.className = 'tbw-visibility-handle';\n // Use grid-level icons (fall back to defaults)\n this.setIcon(handle, this.resolveIcon('dragHandle'));\n handle.title = 'Drag to reorder';\n row.appendChild(handle);\n }\n\n row.appendChild(labelWrapper);\n columnList.appendChild(row);\n }\n }\n\n /**\n * Set up drag-and-drop event listeners for a row.\n * On drop, emits a 'column-reorder-request' event for other plugins to handle.\n */\n private setupDragListeners(row: HTMLElement, field: string, index: number, columnList: HTMLElement): void {\n row.addEventListener('dragstart', (e: DragEvent) => {\n this.isDragging = true;\n this.draggedField = field;\n this.draggedIndex = index;\n\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', field);\n }\n\n row.classList.add('dragging');\n });\n\n row.addEventListener('dragend', () => {\n this.isDragging = false;\n this.draggedField = null;\n this.draggedIndex = null;\n this.dropIndex = null;\n\n columnList.querySelectorAll('.tbw-visibility-row').forEach((r) => {\n r.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\n });\n });\n\n row.addEventListener('dragover', (e: DragEvent) => {\n e.preventDefault();\n if (!this.isDragging || this.draggedField === field) return;\n\n const rect = row.getBoundingClientRect();\n const midY = rect.top + rect.height / 2;\n\n this.dropIndex = e.clientY < midY ? index : index + 1;\n\n // Clear other row highlights\n columnList.querySelectorAll('.tbw-visibility-row').forEach((r) => {\n if (r !== row) r.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n row.classList.add('drop-target');\n row.classList.toggle('drop-before', e.clientY < midY);\n row.classList.toggle('drop-after', e.clientY >= midY);\n });\n\n row.addEventListener('dragleave', () => {\n row.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n row.addEventListener('drop', (e: DragEvent) => {\n e.preventDefault();\n const draggedField = this.draggedField;\n const draggedIndex = this.draggedIndex;\n const dropIndex = this.dropIndex;\n\n if (!this.isDragging || draggedField === null || draggedIndex === null || dropIndex === null) {\n return;\n }\n\n // Calculate the effective target index\n const effectiveToIndex = dropIndex > draggedIndex ? dropIndex - 1 : dropIndex;\n\n if (effectiveToIndex !== draggedIndex) {\n // Emit a request event - other plugins (like ReorderPlugin) can listen and handle\n const detail: ColumnReorderRequestDetail = {\n field: draggedField,\n fromIndex: draggedIndex,\n toIndex: effectiveToIndex,\n };\n this.emit<ColumnReorderRequestDetail>('column-reorder-request', detail);\n\n // Rebuild the panel after reorder (deferred to allow re-render)\n setTimeout(() => {\n this.rebuildToggles(columnList);\n }, 0);\n }\n });\n }\n // #endregion\n\n // #region Styles\n\n override readonly styles = styles;\n // #endregion\n}\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","setColumnVisible","field","visible","callbacks","allCols","c","toggleColumnVisibility","isColumnVisible","showAllColumns","getAllColumns","getColumnOrder","setColumnOrder","order","columnMap","reordered","FitModeEnum","DEFAULT_ANIMATION_CONFIG","DEFAULT_GRID_ICONS","inferType","value","inferColumns","rows","provided","typeMap","sample","k","v","type","EXPR_RE","EMPTY_SENTINEL","SAFE_EXPR","FORBIDDEN","DANGEROUS_TAGS","DANGEROUS_ATTR_PATTERN","URL_ATTRS","DANGEROUS_URL_PROTOCOL","sanitizeHTML","html","template","sanitizeNode","root","toRemove","elements","el","tagName","attr","attrsToRemove","attrName","name","evalTemplateString","raw","ctx","parts","evaluated","_m","expr","res","evalSingle","finalStr","postProcess","allEmpty","p","key","dotChain","out","str","finalCellScrub","cell","n","compileTemplate","forceBlank","fn","parseLightDomColumns","host","rawType","header","sortable","editable","config","widthAttr","numericWidth","minWidthAttr","numericMinWidth","editorName","rendererName","optionsAttr","item","label","viewTpl","editorTpl","headerTpl","adapter","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","booleanCellHTML","formatDateValue","getRowIndexFromCell","getColIndexFromCell","clearCellFocus","handleGridKeyDown","maxRow","maxCol","editing","colType","path","target","isFormField","tag","ensureCellVisible","options","rowHeight","container","viewportEl","scrollEl","visibleHeight","y","isEditing","rowIndex","vStart","vEnd","scrollArea","offsets","cellRect","scrollAreaRect","cellLeft","cellRight","visibleLeft","visibleRight","focusTarget","FOCUSABLE_EDITOR_SELECTOR","cellTemplate","rowTemplate","createCellFromTemplate","createRowFromTemplate","CELL_CACHE_KEY","CELL_CACHE_EPOCH_KEY","invalidateCellCache","renderVisibleRows","start","end","epoch","renderRowHook","needed","bodyEl","colLen","headerRowCount","handleRowClick","hasRenderRowPlugins","rowData","rowEpoch","prevRef","cellCount","structureValid","dataRefChanged","needsExternalRebuild","hasEditing","hasEditingCells","isActivelyEditedRow","clearEditingState","renderInlineRow","fastPatchRow","children","inlineEnterEdit","isChanged","hasChangedClass","colsLen","childLen","minLen","focusRow","focusCol","hasSpecialCols","rowIndexStr","shouldHaveFocus","hasFocus","produced","displayStr","formatted","gridEl","fragment","colIndex","format","compiled","tplHolder","viewRenderer","externalView","needsSanitization","spec","placeholder","context","output","blocked","rawTpl","textContent","isDbl","firstCell","cellEl","focusChanged","editor","rawMode","startRowEdit","targetCell","isSafePropertyKey","incrementEditingCount","count","exitRowEdit","revert","snapshot","current","val","commitCellValue","rowIdx","colIdx","rowEl2","newValue","firstTime","skipFocus","originalValue","editFinalized","commit","cancel","inputLike","editorHost","editorSpec","clone","compiledEditor","node","g","hasHTMLInput","emitEvent","eventName","detail","getChangedRows","getChangedRowIndices","resetChangedRows","silent","r","beginBulkEdit","commitActiveRowEdit","cancelActiveRowEdit","getCellContext","handleCellMousedown","handleCellClick","handleCellDblclick","col2","handleCellKeydown","selectEl","newVal","setupCellEventDelegation","signal","getEditMode","editMode","defaultComparator","builtInSort","comparator","direction","rA","rB","finalizeSortResult","sortedRows","dir","renderHeader","toggleSort","applySort","h","result","setIcon","element","icon","headerRow","maybeTpl","span","active","icons","iconValue","handle","hasIdleCallback","scheduleIdle","callback","cancelIdle","createResizeController","resizeState","pendingRaf","prevCursor","prevUserSelect","onMove","delta","width","justFinishedResize","onUp","hadResize","rect","createElement","attrs","div","className","button","content","slot","gridContentTemplate","cloneGridContent","buildGridDOM","contentWrapper","buildShellHeader","titleEl","toolbar","btn","btnEl","toggleBtn","buildShellBody","body","hasPanel","isSinglePanel","gridContent","panelEl","resizeHandlePosition","panelContent","accordion","panel","sectionClasses","section","headerBtn","iconSpan","titleSpan","chevronSpan","iconToString","createShellState","shouldRenderShellHeader","renderShellHeader","toolPanelIcon","title","hasTitle","iconStr","configButtons","hasConfigButtons","hasApiButtons","hasLightDomButtons","hasPanels","showSeparator","sortedConfigButtons","sortedApiButtons","toolbarHtml","isOpen","parseLightDomShell","headerEl","headerContents","toolButtonPlaceholders","existingButtons","id","parseLightDomToolPanels","rendererFactory","tooltip","render","adapterRenderer","wrapper","existingPanel","cleanup","setupShellEventListeners","shadowRoot","customBtn","btnId","sectionId","setupToolPanelResize","onResize","shellBody","position","minWidth","startX","startWidth","maxWidth","isResizing","onMouseMove","newWidth","onMouseUp","finalWidth","onMouseDown","renderCustomToolbarButtons","allButtons","existingCleanup","renderHeaderContent","contentArea","sortedContents","renderPanelContent","expandIcon","collapseIcon","panelId","isExpanded","chevron","updateToolbarActiveStates","panelToggle","updatePanelState","getToolbarButtonsInfo","cleanupShellState","createShellController","initialized","controller","firstPanel","shadow","updateAccordionSectionState","otherId","otherPanel","contentEl","renderAccordionSectionContent","contentId","buttonId","disabled","apiBtn","expanded","buildGridDOMIntoShadow","shellConfig","hasShell","headerOptions","sortedPanels","bodyOptions","shellHeader","createTouchScrollState","resetTouchState","cancelMomentum","handleTouchStart","touch","handleTouchMove","currentY","currentX","now","deltaY","deltaX","dt","scrollTop","scrollHeight","clientHeight","maxScrollY","canScrollVertically","canScrollHorizontally","scrollLeft","scrollWidth","clientWidth","maxScrollX","handleTouchEnd","startMomentumScroll","animate","scrollY","scrollX","setupTouchScrollListeners","gridContentEl","PluginManager","renderer","PluginClass","total","beforeRowIndex","adjustedStart","pluginStart","row","query","responses","response","event","focusedCell","left","right","skipScroll","panels","contents","DataGridElement","#shadow","#initialized","#readyPromise","#readyResolve","#rows","#columns","#gridConfig","#fitMode","#editOn","#effectiveConfig","#connected","#pendingUpdate","#pendingUpdateFlags","#scrollRaf","#pendingScrollTop","#hasScrollPlugins","#renderRowHook","#isDragging","#touchState","#eventAbortController","#resizeObserver","#rowHeightObserver","#lightDomObserver","#idleCallbackHandle","#pooledScrollEvent","#pluginManager","#eventListenersAdded","#scrollAbortController","#stateChangeHandler","#initialColumnState","#shellState","#shellController","#resizeCleanup","#baseColumns","oldValue","#queueUpdate","#injectStyles","#emit","sheet","styles","resolve","gridCssText","stylesheet","cssText","rule","err","#rebuildRowModel","#processColumns","#initializePlugins","pluginsConfig","#injectAllPluginStyles","allStyles","styleEl","#updatePluginConfigs","#collectPluginShellContributions","#destroyPlugins","pluginPanels","pluginContents","#getToolPanelRendererFactory","adapters","instanceAdapter","#mergeEffectiveConfig","#render","#afterConnect","#setupLightDomObserver","#rowHeightObserverSetup","#customStyles","prop","gridRoot","defaultOpen","#setup","#setupScrollListeners","#handleMouseDown","#handleMouseMove","#handleMouseUp","userRowHeight","#measureRowHeight","#updateAriaSelection","firstRow","cells","maxCellHeight","rowRect","measuredHeight","scrollSignal","fauxScrollbar","rowsEl","currentScrollTop","rawStart","evenAlignedStart","subPixelOffset","#onScrollBatched","isHorizontal","#setupRowHeightObserver","isActiveRow","#flushPendingUpdates","flags","#applyGridConfigUpdate","#applyColumnsUpdate","#applyRowsUpdate","#applyFitModeUpdate","#applyEditModeUpdate","hadShell","nowNeedsShell","sourceColumns","visibleCols","hiddenCols","processedColumns","processedMap","processed","originalRows","processedRows","base","domCols","map","exist","internal","existingCols","#applyAnimationConfig","enabled","#renderVisibleRows","configCols","existingHiddenMap","seeded","#applyColumnStateInternal","scrollEvent","cellClickEvent","rowClickEvent","headerClickEvent","#buildCellMouseEvent","elAtPoint","idx","#visibilityCallbacks","#applyColumnState","css","debounceTimer","needsShellUpdate","needsColumnUpdate","processPendingUpdates","hadTitle","newHeaderHtml","temp","newHeader","#setupShellListeners","mutations","mutation","#calculateTotalSpacerHeight","totalRows","fauxScrollHeight","viewportHeight","scrollAreaEl","scrollAreaHeight","viewportHeightDiff","pluginExtraHeight","hScrollbarPadding","force","iterations","maxIterations","extraHeightBefore","pluginAdjustedStart","visibleCount","totalHeight","extraHeightBeforeStart","newFauxHeight","newViewportHeight","newTotalHeight","#handleToolbarButtonClick","configBtn","PLUGIN_QUERIES","BaseGridPlugin","userIcons","iconKey","pluginOverride","message","GridClasses","GridDataAttrs","GridSelectors","GridCSSVars","DGEvents","PluginEvents","formatCellValue","delimiter","newline","buildClipboardText","params","selectedIndices","visibleColumns","lines","sortedIndices","copyToClipboard","text","textarea","success","parseClipboardText","normalizedText","currentRow","currentCell","inQuotes","char","readFromClipboard","ClipboardPlugin","isCopy","isPaste","#handleCopy","#handlePaste","selectionPlugin","#getSelectionPlugin","selectedRows","hasRowSelection","ranges","hasRangeSelection","hasCellSelection","rowCount","columnCount","range","#buildRangeText","#buildCellText","#buildSingleCellText","parsed","formattedValue","startRow","startCol","endRow","endCol","minRow","minCol","rangeColumns","indices","DEFAULT_COLUMN_WIDTH","parseColumnWidth","numeric","getColumnWidths","computeColumnOffsets","offset","computeTotalWidth","sum","getVisibleColumnRange","viewportWidth","columnOffsets","columnWidths","overscan","binarySearchFirstVisible","rightEdge","low","high","mid","shouldVirtualize","threshold","autoEnable","ColumnVirtualizationPlugin","isVirtualized","viewport","leftPadding","bodyRows","rowsContainer","columnIndex","buildMenuItems","items","isItemDisabled","createMenuElement","onAction","submenuArrow","menu","separator","menuItem","shortcut","arrow","subMenuItems","subMenu","positionMenu","x","menuRect","top","globalClickHandler","globalKeydownHandler","globalStyleSheet","globalHandlerRefCount","defaultItems","ContextMenuPlugin","contextMenuStyles","fullParams","formatCsvValue","quote","buildCsv","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","FilteringPlugin","filterList","newCacheKey","hasFilter","filterBtn","style","filterPanelStyles","buttonEl","uniqueValues","excludedSet","currentSearchText","excluded","operator","valueTo","usedCustomRenderer","panelRect","excludedValues","searchContainer","searchInput","actionsRow","selectAllLabel","selectAllCheckbox","selectAllText","updateSelectAllState","checkState","allChecked","noneChecked","newState","renderVisibleItems","valuesContainer","spacer","contentContainer","filteredValues","createItem","strValue","checkbox","totalItems","window","renderValues","filterText","lowerFilter","noMatch","buttonRow","applyBtn","isChecked","original","clearBtn","handleResult","filterModel","computeColumnGroups","explicitMap","groupsOrdered","pushImplicit","startIdx","cols","prev","run","runStart","group","applyGroupedHeaderCellClasses","headerRowEl","groups","fieldToGroup","gid","last","buildGroupHeaderRow","groupRow","startIndex","isImplicit","hasColumnGroups","GroupingColumnsPlugin","columnGroups","groupInfo","existingGroupRow","groupId","builtInAggregators","acc","customAggregators","aggregatorRegistry","ref","builtInValueAggregators","vals","getValueAggregator","aggFunc","getAggregator","runAggregator","buildGroupedRowModel","groupOn","parent","rawVal","depthIdx","seg","composite","flat","visit","toggleGroupExpansion","expandedKeys","newSet","expandAllGroups","keys","collapseAllGroups","getGroupRowCount","GroupingRowsPlugin","grouped","currentVisibleKeys","_rowIndex","toggleExpand","handleToggle","animClass","labelText","aggregators","groupRows","gridTemplate","firstColAgg","aggResult","aggRef","newKeys","toggleDetailRow","expandedRows","newExpanded","expandDetailRow","collapseDetailRow","isDetailExpanded","createDetailElement","detailRow","detailCell","MasterDetailPlugin","detailEl","gridWithAdapter","animation","showExpandColumn","expandOnRowClick","collapseOnClickOutside","heightAttr","configUpdates","templateHTML","durationStr","onComplete","firstCol","originalRenderer","wrappedRenderer","renderCtx","toggle","rendered","#syncDetailRows","visibleRowMap","dataRows","existingDetails","forIndex","isStillExpanded","isRowVisible","existingDetail","configHeight","expandedIndices","minStart","cumulativeExtraHeight","actualRowTop","detailHeight","actualDetailBottom","currentRenderer","applySorts","sorts","sort","aVal","bVal","shiftKey","maxColumns","getSortIndex","sortModel","getSortDirection","MultiSortPlugin","showIndex","sortIndex","sortDir","indicator","badge","model","existingIndex","newEntry","getLeftStickyColumns","getRightStickyColumns","hasStickyColumns","applyStickyOffsets","fieldToIndex","clearStickyOffsets","PinnedColumnsPlugin","sticky","metaSticky","stickyLeftCells","stickyRightCells","isAggregatorConfig","def","createInfoBarElement","pinnedRows","center","filteredCount","selectedCount","renderCustomPanel","createAggregationContainer","renderAggregationRows","rowConfig","formatter","aggDef","aggFn","staticVal","buildContext","filterState","PinnedRowsPlugin","aggregationRows","topRows","bottomRows","hasInfoContent","hasBottomInfoBar","needsFooter","newInfoBar","getPivotAggregator","validatePivotConfig","errors","createValueKey","columnValues","valueField","buildPivot","rowGroupFields","columnGroupFields","valueFields","columnKeys","getUniqueColumnKeys","pivotRows","buildHierarchicalPivotRows","totals","calculateTotals","grandTotal","columnFields","groupByField","depth","parentKey","aggregateValues","calculateRowTotal","currentField","remainingFields","hasChildren","groupValue","rowKey","colKey","vf","nums","aggregator","aggregatedResult","valueKey","sumRows","flattenPivotRows","defaultExpanded","flatten","child","getAllGroupKeys","collectKeys","AGG_FUNCS","renderPivotPanel","isActive","createSection","createOptionsPanel","createFieldZone","createValuesZone","createAvailableFieldsZone","contentFactory","zoneType","zone","currentFields","createFieldChip","chip","fieldInfo","removeBtn","currentValues","createValueChip","labelWrapper","aggSelect","option","allFields","usedFields","availableFields","empty","createCheckbox","checked","onChange","renderPivotGroupRow","indent","renderPivotLeafRow","renderPivotGrandTotalRow","PivotPlugin","allKeys","indentWidth","flatRows","pr","pivotColumns","rowGroupHeaders","valueHeader","pivotRow","grandTotalRow","fields","targetZone","fieldIndex","canMoveColumn","meta","moveColumn","fromIndex","toIndex","removed","ReorderPlugin","pluginAllows","orderIndex","midX","draggedField","draggedIndex","dropIndex","effectiveToIndex","currentOrder","newOrder","targetColumn","originalOrder","positions","oldPositions","deltas","oldLeft","duration","applyChange","movedFields","newLeft","normalizeRange","toPublicRange","normalized","toPublicRanges","isCellInRange","isCellInAnyRange","getCellsInRange","getAllCellsInRanges","cellMap","createRangeFromAnchor","anchor","buildSelectionEvent","colCount","SelectionPlugin","originalEvent","#buildEvent","ctrlKey","newRange","isNavKey","isTabKey","shouldExtend","allRange","_event","#applySelectionClasses","allRows","currentCol","getBlockNumber","blockSize","getBlockRange","blockNumber","getRequiredBlocks","startBlock","endBlock","blocks","loadBlock","dataSource","getRowFromCache","loadedBlocks","block","indexInBlock","SCROLL_DEBOUNCE_MS","ServerSidePlugin","gridRef","requiredBlocks","blockNum","cached","generateRowKey","expandAll","childrenField","childKeys","collapseAll","getPathToKey","targetKey","childPath","expandToKey","existingExpanded","detectTreeStructure","inferChildrenField","commonArrayFields","TreePlugin","data","currentKeys","getConfig","resolveIcon","wrapped","showExpandIcons","flatRow","pushAction","action","maxSize","undoStack","undo","redo","redoStack","canUndo","canRedo","clearHistory","createEditAction","UndoRedoPlugin","isUndo","isRedo","VisibilityPlugin","columnList","showAllBtn","reorderEnabled","midY"],"mappings":"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"}