@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":"pinned-rows.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/pinned-rows/pinned-rows.ts","../../../../../libs/grid/src/lib/plugins/pinned-rows/PinnedRowsPlugin.ts"],"sourcesContent":["/**\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"],"names":["isAggregatorConfig","def","createInfoBarElement","config","context","pinnedRows","left","center","right","rowCount","filteredCount","selectedCount","panel","panelEl","renderCustomPanel","createAggregationContainer","position","container","renderAggregationRows","rows","columns","dataRows","rowConfig","rowEl","cell","col","value","formatter","aggDef","aggFn","getAggregator","staticVal","content","buildContext","grid","selectionState","filterState","PinnedRowsPlugin","BaseGridPlugin","shadowRoot","aggregationRows","topRows","r","bottomRows","header","hasInfoContent","hasBottomInfoBar","needsFooter","newInfoBar","id","p","row","styles"],"mappings":"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"}
1
+ {"version":3,"file":"pinned-rows.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/pinned-rows/pinned-rows.ts","../../../../../libs/grid/src/lib/plugins/pinned-rows/PinnedRowsPlugin.ts"],"sourcesContent":["/**\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"],"names":["isAggregatorConfig","def","createInfoBarElement","config","context","pinnedRows","left","center","right","rowCount","filteredCount","selectedCount","panel","panelEl","renderCustomPanel","createAggregationContainer","position","container","renderAggregationRows","rows","columns","dataRows","rowConfig","rowEl","cell","col","value","formatter","aggDef","aggFn","getAggregator","staticVal","content","buildContext","grid","selectionState","filterState","PinnedRowsPlugin","BaseGridPlugin","shadowRoot","aggregationRows","topRows","r","bottomRows","header","hasInfoContent","hasBottomInfoBar","needsFooter","newInfoBar","id","p","row","styles"],"mappings":"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"}
@@ -1 +1 @@
1
- {"version":3,"file":"pivot.umd.js","sources":["../../../../../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"],"sourcesContent":["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"],"names":["getPivotAggregator","getValueAggregator","validatePivotConfig","config","errors","createValueKey","columnValues","valueField","buildPivot","rows","rowGroupFields","columnGroupFields","valueFields","columnKeys","getUniqueColumnKeys","pivotRows","buildHierarchicalPivotRows","totals","calculateTotals","grandTotal","a","b","columnFields","keys","row","key","f","groupByField","field","groups","existing","depth","parentKey","result","values","aggregateValues","total","calculateRowTotal","currentField","remainingFields","hasChildren","grouped","groupValue","groupRows","rowKey","children","colKey","vf","nums","r","aggregator","aggregatedResult","valueKey","sum","val","sumRows","flattenPivotRows","expandedKeys","defaultExpanded","flatten","isExpanded","child","getAllGroupKeys","collectKeys","AGG_FUNCS","renderPivotPanel","container","isActive","callbacks","controller","ctx","wrapper","createSection","createOptionsPanel","createFieldZone","createValuesZone","createAvailableFieldsZone","title","contentFactory","section","header","content","zoneType","signal","zone","currentFields","placeholder","createFieldChip","e","chip","fieldInfo","label","removeBtn","currentValues","createValueChip","labelWrapper","aggSelect","aggFunc","option","allFields","usedFields","v","availableFields","empty","panel","createCheckbox","checked","onChange","input","span","renderPivotGroupRow","rowEl","col","colIdx","cell","indent","btn","count","value","renderPivotLeafRow","columns","renderPivotGrandTotalRow","PivotPlugin","BaseGridPlugin","mode","host","allKeys","indentWidth","flatRows","pr","currentVisibleKeys","pivotColumns","rowGroupHeaders","valueHeader","pivotRow","iconKey","el","icon","style","body","animClass","shadowRoot","grandTotalRow","fields","grid","enabled","current","targetZone","fieldIndex","styles"],"mappings":"0ZAIO,MAAMA,EAAqBC,EAAAA,mBAE3B,SAASC,EAAoBC,EAA+B,CACjE,MAAMC,EAAmB,CAAA,EAEzB,MAAI,CAACD,EAAO,gBAAgB,QAAU,CAACA,EAAO,mBAAmB,QAC/DC,EAAO,KAAK,oDAAoD,EAG7DD,EAAO,aAAa,QACvBC,EAAO,KAAK,sCAAsC,EAG7CA,CACT,CAEO,SAASC,EAAeC,EAAwBC,EAA4B,CACjF,MAAO,CAAC,GAAGD,EAAcC,CAAU,EAAE,KAAK,GAAG,CAC/C,CCbO,SAASC,EAAWC,EAAsBN,EAAkC,CACjF,MAAMO,EAAiBP,EAAO,gBAAkB,CAAA,EAC1CQ,EAAoBR,EAAO,mBAAqB,CAAA,EAChDS,EAAcT,EAAO,aAAe,CAAA,EAGpCU,EAAaC,EAAoBL,EAAME,CAAiB,EAGxDI,EAAYC,EAChBP,EACAC,EACAC,EACAE,EACAD,EACA,EACA,EAAA,EAIIK,EAASC,EAAgBH,EAAWF,EAAYD,CAAW,EAC3DO,EAAa,OAAO,OAAOF,CAAM,EAAE,OAAO,CAACG,EAAGC,IAAMD,EAAIC,EAAG,CAAC,EAElE,MAAO,CACL,KAAMN,EACN,WAAAF,EACA,OAAAI,EACA,WAAAE,CAAA,CAEJ,CAKO,SAASL,EAAoBL,EAAsBa,EAAkC,CAC1F,GAAIA,EAAa,SAAW,EAAG,MAAO,CAAC,OAAO,EAE9C,MAAMC,MAAW,IACjB,UAAWC,KAAOf,EAAM,CACtB,MAAMgB,EAAMH,EAAa,IAAKI,GAAM,OAAOF,EAAIE,CAAC,GAAK,EAAE,CAAC,EAAE,KAAK,GAAG,EAClEH,EAAK,IAAIE,CAAG,CACd,CACA,MAAO,CAAC,GAAGF,CAAI,EAAE,KAAA,CACnB,CAKO,SAASI,EAAalB,EAAsBmB,EAA4C,CAC7F,MAAMC,MAAa,IAEnB,UAAWL,KAAOf,EAAM,CACtB,MAAMgB,EAAM,OAAOD,EAAII,CAAK,GAAK,EAAE,EAC7BE,EAAWD,EAAO,IAAIJ,CAAG,EAC3BK,EACFA,EAAS,KAAKN,CAAG,EAEjBK,EAAO,IAAIJ,EAAK,CAACD,CAAG,CAAC,CAEzB,CAEA,OAAOK,CACT,CAyBO,SAASb,EACdP,EACAC,EACAY,EACAT,EACAD,EACAmB,EACAC,EACY,CACZ,MAAMC,EAAqB,CAAA,EAG3B,GAAIvB,EAAe,SAAW,EAAG,CAG/B,MAAMwB,EAASC,EAAgB1B,EAAMa,EAAcT,EAAYD,CAAW,EACpEwB,EAAQC,EAAkBH,CAAM,EACtC,OAAAD,EAAO,KAAK,CACV,OAAQD,GAAa,MACrB,SAAUA,GAAa,MACvB,MAAAD,EACA,OAAAG,EACA,MAAAE,EACA,QAAS,GACT,SAAU3B,EAAK,MAAA,CAChB,EACMwB,CACT,CAGA,MAAMK,EAAe5B,EAAe,CAAC,EAC/B6B,EAAkB7B,EAAe,MAAM,CAAC,EACxC8B,EAAcD,EAAgB,OAAS,EAGvCE,EAAUd,EAAalB,EAAM6B,CAAY,EAE/C,SAAW,CAACI,EAAYC,CAAS,IAAKF,EAAS,CAC7C,MAAMG,EAASZ,EAAY,GAAGA,CAAS,IAAIU,CAAU,GAAKA,EAGpDR,EAASC,EAAgBQ,EAAWrB,EAAcT,EAAYD,CAAW,EACzEwB,EAAQC,EAAkBH,CAAM,EAGtC,IAAIW,EACAL,IACFK,EAAW7B,EACT2B,EACAJ,EACAjB,EACAT,EACAD,EACAmB,EAAQ,EACRa,CAAA,GAIJX,EAAO,KAAK,CACV,OAAAW,EACA,SAAUF,GAAc,UACxB,MAAAX,EACA,OAAAG,EACA,MAAAE,EACA,QAASI,EACT,SAAAK,EACA,SAAUF,EAAU,MAAA,CACrB,CACH,CAEA,OAAOV,CACT,CAKO,SAASE,EACd1B,EACAa,EACAT,EACAD,EAC+B,CAC/B,MAAMsB,EAAwC,CAAA,EAE9C,UAAWY,KAAUjC,EACnB,UAAWkC,KAAMnC,EAAa,CAO5B,MAAMoC,GAJJ1B,EAAa,OAAS,EAClBb,EAAK,OAAQwC,GAAM3B,EAAa,IAAKI,GAAM,OAAOuB,EAAEvB,CAAC,GAAK,EAAE,CAAC,EAAE,KAAK,GAAG,IAAMoB,CAAM,EACnFrC,GAEoB,IAAKwC,GAAM,OAAOA,EAAEF,EAAG,KAAK,CAAC,GAAK,CAAC,EACvDG,EAAalD,EAAmB+C,EAAG,OAAO,EAC1CI,EAAmBH,EAAK,OAAS,EAAIE,EAAWF,CAAI,EAAI,KAExDI,EAAW/C,EAAe,CAACyC,CAAM,EAAGC,EAAG,KAAK,EAClDb,EAAOkB,CAAQ,EAAID,CACrB,CAGF,OAAOjB,CACT,CAKO,SAASG,EAAkBH,EAA+C,CAC/E,IAAImB,EAAM,EACV,UAAWC,KAAO,OAAO,OAAOpB,CAAM,EACpCmB,GAAOC,GAAO,EAEhB,OAAOD,CACT,CAmCO,SAASnC,EACdH,EACAF,EACAD,EACwB,CACxB,MAAMK,EAAiC,CAAA,EAGvC,SAASsC,EAAQ9C,EAAkB,CACjC,UAAWe,KAAOf,EAEhB,GAAI,CAACe,EAAI,SAAW,CAACA,EAAI,UAAU,OACjC,UAAWsB,KAAUjC,EACnB,UAAWkC,KAAMnC,EAAa,CAC5B,MAAMwC,EAAW/C,EAAe,CAACyC,CAAM,EAAGC,EAAG,KAAK,EAClD9B,EAAOmC,CAAQ,GAAKnC,EAAOmC,CAAQ,GAAK,IAAM5B,EAAI,OAAO4B,CAAQ,GAAK,EACxE,MAEO5B,EAAI,UACb+B,EAAQ/B,EAAI,QAAQ,CAG1B,CAEA,OAAA+B,EAAQxC,CAAS,EACVE,CACT,CAMO,SAASuC,EAAiB/C,EAAkBgD,EAA4BC,EAAkB,GAAkB,CACjH,MAAMzB,EAAqB,CAAA,EAE3B,SAAS0B,EAAQnC,EAAe,CAC9BS,EAAO,KAAKT,CAAG,EAGf,MAAMoC,EAAaH,EAAeA,EAAa,IAAIjC,EAAI,MAAM,EAAIkC,EAGjE,GAAIlC,EAAI,UAAYoC,EAClB,UAAWC,KAASrC,EAAI,SACtBmC,EAAQE,CAAK,CAGnB,CAEA,UAAWrC,KAAOf,EAChBkD,EAAQnC,CAAG,EAGb,OAAOS,CACT,CAKO,SAAS6B,EAAgBrD,EAA4B,CAC1D,MAAMc,EAAiB,CAAA,EAEvB,SAASwC,EAAYvC,EAAe,CAIlC,GAHIA,EAAI,SACND,EAAK,KAAKC,EAAI,MAAM,EAElBA,EAAI,SACN,UAAWqC,KAASrC,EAAI,SACtBuC,EAAYF,CAAK,CAGvB,CAEA,UAAWrC,KAAOf,EAChBsD,EAAYvC,CAAG,EAGjB,OAAOD,CACT,CCxTO,MAAMyC,EAAuB,CAAC,MAAO,MAAO,QAAS,MAAO,MAAO,QAAS,MAAM,EA+BlF,SAASC,EACdC,EACA/D,EACAgE,EACAC,EACY,CAEZ,MAAMC,EAAa,IAAI,gBACjBC,EAAqB,CAAE,OAAAnE,EAAQ,UAAAiE,EAAW,OAAQC,EAAW,MAAA,EAE7DE,EAAU,SAAS,cAAc,KAAK,EAC5C,OAAAA,EAAQ,UAAY,kBAGpBA,EAAQ,YAAYC,EAAc,UAAW,IAAMC,EAAmBN,EAAUG,CAAG,CAAC,CAAC,EAGrFC,EAAQ,YAAYC,EAAc,aAAc,IAAME,EAAgB,YAAaJ,CAAG,CAAC,CAAC,EAGxFC,EAAQ,YAAYC,EAAc,gBAAiB,IAAME,EAAgB,eAAgBJ,CAAG,CAAC,CAAC,EAG9FC,EAAQ,YAAYC,EAAc,SAAU,IAAMG,EAAiBL,CAAG,CAAC,CAAC,EAGxEC,EAAQ,YAAYC,EAAc,mBAAoB,IAAMI,EAA0BN,CAAG,CAAC,CAAC,EAE3FJ,EAAU,YAAYK,CAAO,EAGtB,IAAM,CACXF,EAAW,MAAA,EACXE,EAAQ,OAAA,CACV,CACF,CAKA,SAASC,EAAcK,EAAeC,EAAgD,CACpF,MAAMC,EAAU,SAAS,cAAc,KAAK,EAC5CA,EAAQ,UAAY,oBAEpB,MAAMC,EAAS,SAAS,cAAc,KAAK,EAC3CA,EAAO,UAAY,2BACnBA,EAAO,YAAcH,EAErB,MAAMI,EAAU,SAAS,cAAc,KAAK,EAC5C,OAAAA,EAAQ,UAAY,4BACpBA,EAAQ,YAAYH,GAAgB,EAEpCC,EAAQ,YAAYC,CAAM,EAC1BD,EAAQ,YAAYE,CAAO,EAEpBF,CACT,CAKA,SAASL,EAAgBQ,EAAwCZ,EAAiC,CAChG,KAAM,CAAE,OAAAnE,EAAQ,UAAAiE,EAAW,OAAAe,CAAA,EAAWb,EAChCc,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,sBACjBA,EAAK,aAAa,YAAaF,CAAQ,EAEvC,MAAMG,EAAgBH,IAAa,YAAe/E,EAAO,gBAAkB,CAAA,EAAOA,EAAO,mBAAqB,CAAA,EAE9G,GAAIkF,EAAc,SAAW,EAAG,CAC9B,MAAMC,EAAc,SAAS,cAAc,KAAK,EAChDA,EAAY,UAAY,wBACxBA,EAAY,YAAc,mCAC1BF,EAAK,YAAYE,CAAW,CAC9B,KACE,WAAW1D,KAASyD,EAClBD,EAAK,YAAYG,EAAgB3D,EAAOsD,EAAUZ,CAAG,CAAC,EAK1D,OAAAc,EAAK,iBACH,WACCI,GAAM,CACLA,EAAE,eAAA,EACFJ,EAAK,UAAU,IAAI,WAAW,CAChC,EACA,CAAE,OAAAD,CAAA,CAAO,EAGXC,EAAK,iBACH,YACA,IAAM,CACJA,EAAK,UAAU,OAAO,WAAW,CACnC,EACA,CAAE,OAAAD,CAAA,CAAO,EAGXC,EAAK,iBACH,OACCI,GAAM,CACLA,EAAE,eAAA,EACFJ,EAAK,UAAU,OAAO,WAAW,EAEjC,MAAMxD,EAAQ4D,EAAE,cAAc,QAAQ,YAAY,EAC9C5D,GACFwC,EAAU,iBAAiBxC,EAAOsD,CAAQ,CAE9C,EACA,CAAE,OAAAC,CAAA,CAAO,EAGJC,CACT,CAKA,SAASG,EAAgB3D,EAAesD,EAAwCZ,EAAiC,CAC/G,KAAM,CAAE,UAAAF,EAAW,OAAAe,CAAA,EAAWb,EACxBmB,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,uBACjBA,EAAK,UAAY,GAEjB,MAAMC,EAAYtB,EAAU,qBAAqB,KAAM1C,GAAMA,EAAE,QAAUE,CAAK,EACxE+D,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,uBAClBA,EAAM,YAAcD,GAAW,QAAU9D,EAEzC,MAAMgE,EAAY,SAAS,cAAc,QAAQ,EACjD,OAAAA,EAAU,UAAY,wBACtBA,EAAU,UAAY,IACtBA,EAAU,MAAQ,eAClBA,EAAU,iBACR,QACCJ,GAAM,CACLA,EAAE,gBAAA,EACFpB,EAAU,sBAAsBxC,EAAOsD,CAAQ,CACjD,EACA,CAAE,OAAAC,CAAA,CAAO,EAGXM,EAAK,YAAYE,CAAK,EACtBF,EAAK,YAAYG,CAAS,EAG1BH,EAAK,iBACH,YACCD,GAAM,CACLA,EAAE,cAAc,QAAQ,aAAc5D,CAAK,EAC3C4D,EAAE,cAAc,QAAQ,cAAeN,CAAQ,EAC/CO,EAAK,UAAU,IAAI,UAAU,CAC/B,EACA,CAAE,OAAAN,CAAA,CAAO,EAGXM,EAAK,iBACH,UACA,IAAM,CACJA,EAAK,UAAU,OAAO,UAAU,CAClC,EACA,CAAE,OAAAN,CAAA,CAAO,EAGJM,CACT,CAKA,SAASd,EAAiBL,EAAiC,CACzD,KAAM,CAAE,OAAAnE,EAAQ,UAAAiE,EAAW,OAAAe,CAAA,EAAWb,EAChCc,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,4CACjBA,EAAK,aAAa,YAAa,QAAQ,EAEvC,MAAMS,EAAgB1F,EAAO,aAAe,CAAA,EAE5C,GAAI0F,EAAc,SAAW,EAAG,CAC9B,MAAMP,EAAc,SAAS,cAAc,KAAK,EAChDA,EAAY,UAAY,wBACxBA,EAAY,YAAc,2CAC1BF,EAAK,YAAYE,CAAW,CAC9B,KACE,WAAW/E,KAAcsF,EACvBT,EAAK,YAAYU,EAAgBvF,EAAY+D,CAAG,CAAC,EAKrD,OAAAc,EAAK,iBACH,WACCI,GAAM,CACLA,EAAE,eAAA,EACFJ,EAAK,UAAU,IAAI,WAAW,CAChC,EACA,CAAE,OAAAD,CAAA,CAAO,EAGXC,EAAK,iBACH,YACA,IAAM,CACJA,EAAK,UAAU,OAAO,WAAW,CACnC,EACA,CAAE,OAAAD,CAAA,CAAO,EAGXC,EAAK,iBACH,OACCI,GAAM,CACLA,EAAE,eAAA,EACFJ,EAAK,UAAU,OAAO,WAAW,EACjC,MAAMxD,EAAQ4D,EAAE,cAAc,QAAQ,YAAY,EAC9C5D,GACFwC,EAAU,gBAAgBxC,EAAO,KAAK,CAE1C,EACA,CAAE,OAAAuD,CAAA,CAAO,EAGJC,CACT,CAKA,SAASU,EAAgBvF,EAA6B+D,EAAiC,CACrF,KAAM,CAAE,UAAAF,EAAW,OAAAe,CAAA,EAAWb,EACxBmB,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,4CAEjB,MAAMC,EAAYtB,EAAU,mBAAA,EAAqB,KAAM1C,GAAMA,EAAE,QAAUnB,EAAW,KAAK,EAEnFwF,EAAe,SAAS,cAAc,KAAK,EACjDA,EAAa,UAAY,gCAEzB,MAAMJ,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,uBAClBA,EAAM,YAAcD,GAAW,QAAUnF,EAAW,MAEpD,MAAMyF,EAAY,SAAS,cAAc,QAAQ,EACjDA,EAAU,UAAY,uBACtBA,EAAU,MAAQ,uBAElB,UAAWC,KAAWjC,EAAW,CAC/B,MAAMkC,EAAS,SAAS,cAAc,QAAQ,EAC9CA,EAAO,MAAQD,EACfC,EAAO,YAAcD,EAAQ,YAAA,EAC7BC,EAAO,SAAWD,IAAY1F,EAAW,QACzCyF,EAAU,YAAYE,CAAM,CAC9B,CAEAF,EAAU,iBACR,SACA,IAAM,CACJ5B,EAAU,qBAAqB7D,EAAW,MAAOyF,EAAU,KAAgB,CAC7E,EACA,CAAE,OAAAb,CAAA,CAAO,EAGX,MAAMS,EAAY,SAAS,cAAc,QAAQ,EACjD,OAAAA,EAAU,UAAY,wBACtBA,EAAU,UAAY,IACtBA,EAAU,MAAQ,qBAClBA,EAAU,iBACR,QACCJ,GAAM,CACLA,EAAE,gBAAA,EACFpB,EAAU,mBAAmB7D,EAAW,KAAK,CAC/C,EACA,CAAE,OAAA4E,CAAA,CAAO,EAGXY,EAAa,YAAYJ,CAAK,EAC9BI,EAAa,YAAYC,CAAS,EAElCP,EAAK,YAAYM,CAAY,EAC7BN,EAAK,YAAYG,CAAS,EAEnBH,CACT,CAKA,SAASb,EAA0BN,EAAiC,CAClE,KAAM,CAAE,OAAAnE,EAAQ,UAAAiE,EAAW,OAAAe,CAAA,EAAWb,EAChCc,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,6BAEjB,MAAMe,EAAY/B,EAAU,mBAAA,EACtBgC,MAAiB,IAAI,CACzB,GAAIjG,EAAO,gBAAkB,CAAA,EAC7B,GAAIA,EAAO,mBAAqB,CAAA,EAChC,GAAIA,EAAO,aAAa,IAAKkG,GAAMA,EAAE,KAAK,GAAK,CAAA,CAAC,CACjD,EAGKC,EAAkBH,EAAU,OAAQzE,GAAM,CAAC0E,EAAW,IAAI1E,EAAE,KAAK,CAAC,EAExE,GAAI4E,EAAgB,SAAW,EAAG,CAChC,MAAMC,EAAQ,SAAS,cAAc,KAAK,EAC1CA,EAAM,UAAY,wBAClBA,EAAM,YAAc,wBACpBnB,EAAK,YAAYmB,CAAK,CACxB,KACE,WAAW3E,KAAS0E,EAAiB,CACnC,MAAMb,EAAO,SAAS,cAAc,KAAK,EACzCA,EAAK,UAAY,iCACjBA,EAAK,YAAc7D,EAAM,OACzB6D,EAAK,UAAY,GACjBA,EAAK,MAAQ,gBAAgB7D,EAAM,KAAK,cAExC6D,EAAK,iBACH,YACCD,GAAM,CACLA,EAAE,cAAc,QAAQ,aAAc5D,EAAM,KAAK,EACjD6D,EAAK,UAAU,IAAI,UAAU,CAC/B,EACA,CAAE,OAAAN,CAAA,CAAO,EAGXM,EAAK,iBACH,UACA,IAAM,CACJA,EAAK,UAAU,OAAO,UAAU,CAClC,EACA,CAAE,OAAAN,CAAA,CAAO,EAGXC,EAAK,YAAYK,CAAI,CACvB,CAGF,OAAOL,CACT,CAKA,SAASX,EAAmBN,EAAmBG,EAAiC,CAC9E,KAAM,CAAE,OAAAnE,EAAQ,UAAAiE,EAAW,OAAAe,CAAA,EAAWb,EAChCkC,EAAQ,SAAS,cAAc,KAAK,EAC1C,OAAAA,EAAM,UAAY,oBAGlBA,EAAM,YACJC,EACE,oBACAtC,EACCuC,GAAY,CACXtC,EAAU,cAAcsC,CAAO,CACjC,EACAvB,CAAA,CACF,EAIFqB,EAAM,YACJC,EACE,kBACAtG,EAAO,YAAc,GACpBuG,GAAY,CACXtC,EAAU,eAAe,aAAcsC,CAAO,CAChD,EACAvB,CAAA,CACF,EAIFqB,EAAM,YACJC,EACE,mBACAtG,EAAO,gBAAkB,GACxBuG,GAAY,CACXtC,EAAU,eAAe,iBAAkBsC,CAAO,CACpD,EACAvB,CAAA,CACF,EAGKqB,CACT,CAKA,SAASC,EACPd,EACAe,EACAC,EACAxB,EACa,CACb,MAAMZ,EAAU,SAAS,cAAc,OAAO,EAC9CA,EAAQ,UAAY,qBAEpB,MAAMqC,EAAQ,SAAS,cAAc,OAAO,EAC5CA,EAAM,KAAO,WACbA,EAAM,QAAUF,EAChBE,EAAM,iBAAiB,SAAU,IAAMD,EAASC,EAAM,OAAO,EAAG,CAAE,OAAAzB,EAAQ,EAE1E,MAAM0B,EAAO,SAAS,cAAc,MAAM,EAC1C,OAAAA,EAAK,YAAclB,EAEnBpB,EAAQ,YAAYqC,CAAK,EACzBrC,EAAQ,YAAYsC,CAAI,EAEjBtC,CACT,CChaO,SAASuC,EAAoBtF,EAAmBuF,EAAoBzC,EAAgC,CACzG,OAAAyC,EAAM,UAAY,kBAClBA,EAAM,aAAa,mBAAoB,OAAOvF,EAAI,cAAgB,CAAC,CAAC,EACpEuF,EAAM,aAAa,iBAAkB,OAAOvF,EAAI,eAAiB,EAAE,CAAC,EACpEuF,EAAM,aAAa,OAAQ,KAAK,EAGhCA,EAAM,UAAY,GAElBzC,EAAI,QAAQ,QAAQ,CAAC0C,EAAKC,IAAW,CACnC,MAAMC,EAAO,SAAS,cAAc,KAAK,EAKzC,GAJAA,EAAK,UAAY,OACjBA,EAAK,aAAa,WAAY,OAAOD,CAAM,CAAC,EAC5CC,EAAK,aAAa,OAAQ,UAAU,EAEhCD,IAAW,EAAG,CAEhB,MAAME,EAAS,OAAO3F,EAAI,aAAa,GAAK,EAC5C0F,EAAK,MAAM,YAAc,GAAGC,CAAM,KAGlC,MAAMvE,EAAS,OAAOpB,EAAI,aAAa,EACjC4F,EAAM,SAAS,cAAc,QAAQ,EAC3CA,EAAI,KAAO,SACXA,EAAI,UAAY,eAChBA,EAAI,aAAa,aAAc5F,EAAI,gBAAkB,iBAAmB,cAAc,EACtF8C,EAAI,QAAQ8C,EAAK9C,EAAI,YAAY9C,EAAI,gBAAkB,WAAa,QAAQ,CAAC,EAC7E4F,EAAI,iBAAiB,QAAU5B,GAAM,CACnCA,EAAE,gBAAA,EACFlB,EAAI,SAAS1B,CAAM,CACrB,CAAC,EACDsE,EAAK,YAAYE,CAAG,EAGpB,MAAMzB,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,cAClBA,EAAM,YAAc,OAAOnE,EAAI,cAAgB,EAAE,EACjD0F,EAAK,YAAYvB,CAAK,EAGtB,MAAM0B,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,cAClBA,EAAM,YAAc,KAAK,OAAO7F,EAAI,eAAe,GAAK,CAAC,IACzD0F,EAAK,YAAYG,CAAK,CACxB,KAAO,CAEL,MAAMC,EAAQ9F,EAAIwF,EAAI,KAAK,EAC3BE,EAAK,YAAcI,GAAS,KAAO,OAAOA,CAAK,EAAI,EACrD,CAEAP,EAAM,YAAYG,CAAI,CACxB,CAAC,EAEM,EACT,CAKO,SAASK,EAAmB/F,EAAmBuF,EAAoBS,EAAkC,CAC1G,OAAAT,EAAM,UAAY,iBAClBA,EAAM,aAAa,mBAAoB,OAAOvF,EAAI,cAAgB,CAAC,CAAC,EACpEuF,EAAM,aAAa,iBAAkB,OAAOvF,EAAI,eAAiB,EAAE,CAAC,EACpEuF,EAAM,UAAY,GAElBS,EAAQ,QAAQ,CAACR,EAAKC,IAAW,CAC/B,MAAMC,EAAO,SAAS,cAAc,KAAK,EAKzC,GAJAA,EAAK,UAAY,OACjBA,EAAK,aAAa,WAAY,OAAOD,CAAM,CAAC,EAC5CC,EAAK,aAAa,OAAQ,UAAU,EAEhCD,IAAW,EAAG,CAEhB,MAAME,EAAS,OAAO3F,EAAI,aAAa,GAAK,EAE5C0F,EAAK,MAAM,YAAc,GAAGC,EAAS,EAAE,KAEvC,MAAMxB,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,cAClBA,EAAM,YAAc,OAAOnE,EAAI,cAAgB,EAAE,EACjD0F,EAAK,YAAYvB,CAAK,CACxB,KAAO,CAEL,MAAM2B,EAAQ9F,EAAIwF,EAAI,KAAK,EAC3BE,EAAK,YAAcI,GAAS,KAAO,OAAOA,CAAK,EAAI,EACrD,CAEAP,EAAM,YAAYG,CAAI,CACxB,CAAC,EAEM,EACT,CAKO,SAASO,EAAyBjG,EAAmBuF,EAAoBS,EAAkC,CAChH,OAAAT,EAAM,UAAY,wBAElBA,EAAM,aAAa,OAAQ,cAAc,EACzCA,EAAM,UAAY,GAElBS,EAAQ,QAAQ,CAACR,EAAKC,IAAW,CAC/B,MAAMC,EAAO,SAAS,cAAc,KAAK,EAKzC,GAJAA,EAAK,UAAY,OACjBA,EAAK,aAAa,WAAY,OAAOD,CAAM,CAAC,EAGxCA,IAAW,EAAG,CAEhB,MAAMtB,EAAQ,SAAS,cAAc,MAAM,EAC3CA,EAAM,UAAY,cAClBA,EAAM,YAAc,cACpBuB,EAAK,YAAYvB,CAAK,CACxB,KAAO,CAEL,MAAM2B,EAAQ9F,EAAIwF,EAAI,KAAK,EAC3BE,EAAK,YAAcI,GAAS,KAAO,OAAOA,CAAK,EAAI,EACrD,CAEAP,EAAM,YAAYG,CAAI,CACxB,CAAC,EAEM,EACT,g6LChHO,MAAMQ,UAAoBC,EAAAA,cAA4B,CAClD,KAAO,QACE,QAAU,QAG5B,OAAgB,SAAW,QAE3B,IAAuB,eAAsC,CAC3D,MAAO,CACL,OAAQ,GACR,WAAY,GACZ,eAAgB,GAChB,cAAe,GACf,UAAW,OAAA,CAEf,CAGQ,SAAW,GACX,eAAiB,GACjB,YAAkC,KAClC,mBAA0C,IAC1C,iBAAgC,IAChC,gBAAkB,GAClB,gBAA4D,CAAA,EAC5D,eAAqC,KACrC,iBAAuC,KACvC,wBAA0B,IAC1B,kBAAoB,IAKpB,qBAA+B,CACrC,OAAQ,KAAK,OAAO,aAAa,QAAU,GAAK,CAClD,CAKA,IAAY,gBAA0C,CAEpD,MAAMC,EADS,KAAK,KACA,iBAAiB,WAAW,MAAQ,iBAExD,GAAIA,IAAS,IAASA,IAAS,MAAO,MAAO,GAC7C,GAAIA,IAAS,IAAQA,IAAS,KAAM,CAClC,MAAMC,EAAO,KAAK,YAAY,KAC9B,GAAIA,GAAQ,iBAAiBA,CAAI,EAAE,iBAAiB,yBAAyB,EAAE,KAAA,IAAW,IACxF,MAAO,EAEX,CACA,OAAO,KAAK,OAAO,WAAa,OAClC,CAMS,QAAe,CACtB,KAAK,SAAW,GAChB,KAAK,eAAiB,GACtB,KAAK,YAAc,KACnB,KAAK,eAAe,MAAA,EACpB,KAAK,gBAAkB,CAAA,EACvB,KAAK,eAAiB,KACtB,KAAK,wBAAA,EACL,KAAK,oBAAoB,MAAA,EACzB,KAAK,cAAc,MAAA,CACrB,CAMS,cAAgD,CAIvD,IADsB,KAAK,QAAQ,eAAiB,KAAK,YAAY,eAAiB,MAChE,GAItB,MAAO,CACL,GAAIH,EAAY,SAChB,MAAO,QACP,KAAM,IACN,QAAS,wBACT,MAAO,GACP,OAASxD,GAAc,KAAK,YAAYA,CAAS,CAAA,CAErD,CAMS,YAAYzD,EAA0C,CAO7D,GALI,CAAC,KAAK,gBAAkB,KAAK,OAAO,SAAW,IAAS,KAAK,wBAC/D,KAAK,eAAiB,GACtB,KAAK,SAAW,IAGd,CAAC,KAAK,SACR,MAAO,CAAC,GAAGA,CAAI,EAGjB,MAAML,EAASF,EAAoB,KAAK,MAAM,EAC9C,GAAIE,EAAO,OAAS,EAClB,YAAK,KAAK,kBAAkBA,EAAO,KAAK,IAAI,CAAC,EAAE,EACxC,CAAC,GAAGK,CAAI,EAOjB,GAJA,KAAK,oBAAA,EACL,KAAK,gBAAkB,KAAK,OAAO,iBAAmB,GAGlD,KAAK,aAAa,OAAS,GAAK,KAAK,iBAAmB,KAAK,YAAa,CAC5E,MAAMqH,EAAUhE,EAAgB,KAAK,YAAY,IAAI,EACrD,UAAWrC,KAAOqG,EAChB,KAAK,aAAa,IAAIrG,CAAG,CAE7B,CAMA,GAHA,KAAK,YAAcjB,EAAWC,EAAwB,KAAK,MAAM,EAG7D,KAAK,aAAa,OAAS,GAAK,KAAK,gBAAiB,CACxD,MAAMqH,EAAUhE,EAAgB,KAAK,YAAY,IAAI,EACrD,UAAWrC,KAAOqG,EAChB,KAAK,aAAa,IAAIrG,CAAG,CAE7B,CAGA,MAAMsG,EAAc,KAAK,OAAO,aAAe,GACzCC,EAA2BxE,EAC/B,KAAK,YAAY,KACjB,KAAK,aACL,KAAK,eAAA,EACL,IAAKyE,IAAQ,CACb,cAAeA,EAAG,OAClB,aAAcA,EAAG,SACjB,aAAcA,EAAG,MACjB,eAAgBA,EAAG,QACnB,mBAAoB,EAAQA,EAAG,UAAU,OACzC,gBAAiB,KAAK,aAAa,IAAIA,EAAG,MAAM,EAChD,gBAAiBA,EAAG,UAAY,EAChC,cAAeA,EAAG,MAAQF,EAC1B,aAAcE,EAAG,MACjB,GAAGA,EAAG,MAAA,EACN,EAGF,KAAK,cAAc,MAAA,EACnB,MAAMC,MAAyB,IAC/B,UAAW1G,KAAOwG,EAAU,CAC1B,MAAMvG,EAAMD,EAAI,cAChB0G,EAAmB,IAAIzG,CAAG,EAEtB,CAAC,KAAK,oBAAoB,IAAIA,CAAG,GAAKD,EAAI,aAAe,GAC3D,KAAK,cAAc,IAAIC,CAAG,CAE9B,CACA,YAAK,oBAAsByG,EAKpBF,CACT,CAES,eAAeR,EAAkD,CACxE,GAAI,CAAC,KAAK,UAAY,CAAC,KAAK,YAC1B,MAAO,CAAC,GAAGA,CAAO,EAGpB,MAAMW,EAA+B,CAAA,EAG/BC,GAAmB,KAAK,OAAO,gBAAkB,CAAA,GAAI,IAAK1G,GAAM,KAAK,eAAe,IAAIA,CAAC,GAAKA,CAAC,EAAE,KAAK,KAAK,EACjHyG,EAAa,KAAK,CAChB,MAAO,eACP,OAAQC,GAAmB,QAC3B,MAAO,GAAA,CACR,EAGD,UAAWtF,KAAU,KAAK,YAAY,WACpC,UAAWC,KAAM,KAAK,OAAO,aAAe,CAAA,EAAI,CAC9C,MAAMK,EAAW/C,EAAe,CAACyC,CAAM,EAAGC,EAAG,KAAK,EAC5CsF,EAActF,EAAG,QAAU,KAAK,eAAe,IAAIA,EAAG,KAAK,GAAKA,EAAG,MACzEoF,EAAa,KAAK,CAChB,MAAO/E,EACP,OAAQ,GAAGN,CAAM,MAAMuF,CAAW,KAAKtF,EAAG,OAAO,IACjD,MAAO,IACP,KAAM,QAAA,CACP,CACH,CAIF,OAAI,KAAK,OAAO,YACdoF,EAAa,KAAK,CAChB,MAAO,eACP,OAAQ,QACR,MAAO,IACP,KAAM,QAAA,CACP,EAGIA,CACT,CAES,UAAU3G,EAA8BuF,EAA6B,CAC5E,MAAMuB,EAAW9G,EAGjB,OAAI8G,EAAS,eAAiBA,EAAS,mBAC9BxB,EAAoBwB,EAAUvB,EAAO,CAC1C,QAAS,KAAK,YACd,SAAWtF,GAAQ,KAAK,OAAOA,CAAG,EAClC,YAAc8G,GAAY,KAAK,YAAYA,CAAO,EAClD,QAAS,CAACC,EAAIC,IAAS,KAAK,QAAQD,EAAIC,CAAI,CAAA,CAC7C,EAICH,EAAS,gBAAkB,QAAa,KAAK,SACxCf,EAAmBe,EAAUvB,EAAO,KAAK,WAAW,GAI7D,KAAK,oBAAoBA,CAAK,EAEvB,GACT,CAOQ,oBAAoBA,EAA0B,EAGlDA,EAAM,UAAU,SAAS,iBAAiB,GAC1CA,EAAM,UAAU,SAAS,gBAAgB,GACzCA,EAAM,UAAU,SAAS,uBAAuB,KAIhDA,EAAM,UAAU,OAAO,kBAAmB,iBAAkB,uBAAuB,EACnFA,EAAM,UAAU,IAAI,eAAe,EAGnCA,EAAM,gBAAgB,kBAAkB,EAGxCA,EAAM,UAAY,GAEtB,CAES,aAAoB,CAEvB,KAAK,UAAY,KAAK,OAAO,gBAAkB,KAAK,YACtD,KAAK,uBAAA,EAEL,KAAK,wBAAA,EAIP,MAAM2B,EAAQ,KAAK,eACnB,GAAIA,IAAU,IAAS,KAAK,cAAc,OAAS,EAAG,OAEtD,MAAMC,EAAO,KAAK,YAAY,cAAc,OAAO,EACnD,GAAI,CAACA,EAAM,OAEX,MAAMC,EAAYF,IAAU,OAAS,oBAAsB,qBAC3D,UAAW3B,KAAS4B,EAAK,iBAAiB,mCAAmC,EAAG,CAC9E,MAAMlH,EAAOsF,EAAsB,QAAQ,SACvCtF,GAAO,KAAK,cAAc,IAAIA,CAAG,IACnCsF,EAAM,UAAU,IAAI6B,CAAS,EAC7B7B,EAAM,iBAAiB,eAAgB,IAAMA,EAAM,UAAU,OAAO6B,CAAS,EAAG,CAAE,KAAM,EAAA,CAAM,EAElG,CACA,KAAK,cAAc,MAAA,CACrB,CAKQ,wBAA+B,CACrC,GAAI,CAAC,KAAK,YAAa,OAEvB,MAAMC,EAAa,KAAK,WACxB,GAAI,CAACA,EAAY,OAGjB,MAAM3E,EACJ2E,EAAW,cAAc,kBAAkB,GAC3CA,EAAW,cAAc,mBAAmB,GAC5CA,EAAW,SAAS,CAAC,EACvB,GAAI,CAAC3E,EAAW,OAGX,KAAK,mBACR,KAAK,iBAAmB,SAAS,cAAc,KAAK,EACpD,KAAK,iBAAiB,UAAY,2BAClCA,EAAU,YAAY,KAAK,gBAAgB,GAI7C,MAAM4E,EAA8B,CAClC,cAAe,eACf,aAAc,cACd,oBAAqB,GACrB,aAAc,KAAK,YAAY,WAC/B,GAAG,KAAK,YAAY,MAAA,EAItBrB,EAAyBqB,EAAe,KAAK,iBAAkB,KAAK,WAAW,CACjF,CAKQ,yBAAgC,CAClC,KAAK,mBACP,KAAK,iBAAiB,OAAA,EACtB,KAAK,iBAAmB,KAE5B,CAMA,OAAOrH,EAAmB,CACpB,KAAK,aAAa,IAAIA,CAAG,EAC3B,KAAK,aAAa,OAAOA,CAAG,EAE5B,KAAK,aAAa,IAAIA,CAAG,EAE3B,KAAK,cAAA,CACP,CAEA,OAAOA,EAAmB,CACxB,KAAK,aAAa,IAAIA,CAAG,EACzB,KAAK,cAAA,CACP,CAEA,SAASA,EAAmB,CAC1B,KAAK,aAAa,OAAOA,CAAG,EAC5B,KAAK,cAAA,CACP,CAEA,WAAkB,CAChB,GAAI,KAAK,YAAa,CACpB,MAAMqG,EAAUhE,EAAgB,KAAK,YAAY,IAAI,EACrD,UAAWrC,KAAOqG,EAChB,KAAK,aAAa,IAAIrG,CAAG,EAE3B,KAAK,cAAA,CACP,CACF,CAEA,aAAoB,CAClB,KAAK,aAAa,MAAA,EAClB,KAAK,cAAA,CACP,CAEA,WAAWA,EAAsB,CAC/B,OAAO,KAAK,aAAa,IAAIA,CAAG,CAClC,CAMA,aAAoB,CACd,KAAK,gBAAgB,SAAW,GAClC,KAAK,uBAAA,EAEP,KAAK,SAAW,GAChB,KAAK,cAAA,CACP,CAEA,cAAqB,CACnB,KAAK,SAAW,GAChB,KAAK,YAAc,KACnB,KAAK,cAAA,CACP,CAEA,eAAyB,CACvB,OAAO,KAAK,QACd,CAEA,gBAAqC,CACnC,OAAO,KAAK,WACd,CAEA,kBAAkBsH,EAAwB,CACxC,KAAK,OAAO,eAAiBA,EAC7B,KAAK,cAAA,CACP,CAEA,qBAAqBA,EAAwB,CAC3C,KAAK,OAAO,kBAAoBA,EAChC,KAAK,cAAA,CACP,CAEA,eAAeA,EAAiC,CAC9C,KAAK,OAAO,YAAcA,EAC1B,KAAK,cAAA,CACP,CAEA,SAAgB,CACd,KAAK,YAAc,KACnB,KAAK,cAAA,CACP,CAMA,WAAkB,CACH,KAAK,KACb,cAAcrB,EAAY,QAAQ,CACzC,CAEA,WAAkB,CACH,KAAK,KACb,eAAA,CACP,CAEA,aAAoB,CACL,KAAK,KACb,gBAAgBA,EAAY,QAAQ,CAC3C,CAEA,gBAA0B,CAExB,OADa,KAAK,KACN,kBAAoBA,EAAY,QAC9C,CAMA,IAAY,aAA8B,CAExC,OADa,KAAK,KACN,SAAW,CAAA,CACzB,CAEQ,qBAA4B,CAClC,MAAMpB,EAAkB,KAAK,mBAAA,EAC7B,KAAK,eAAe,MAAA,EACpB,UAAW1E,KAAS0E,EAClB,KAAK,eAAe,IAAI1E,EAAM,MAAOA,EAAM,MAAM,CAErD,CAEQ,oBAAkC,CACxC,OAAI,KAAK,gBAAgB,OAAS,EACzB,KAAK,gBAEP,KAAK,uBAAA,CACd,CAEQ,wBAAsC,CAC5C,MAAMoH,EAAO,KAAK,KAClB,GAAI,CACF,MAAMxB,EAAUwB,EAAK,gBAAA,GAAqBA,EAAK,SAAW,CAAA,EAC1D,YAAK,gBAAkBxB,EACpB,OAAQR,GAA2B,CAACA,EAAI,MAAM,WAAW,SAAS,CAAC,EACnE,IAAKA,IAA6C,CACjD,MAAOA,EAAI,MACX,OAAQA,EAAI,QAAUA,EAAI,KAAA,EAC1B,EACG,KAAK,eACd,MAAQ,CACN,MAAO,CAAA,CACT,CACF,CAEQ,YAAY9C,EAA6C,CAC/D,KAAK,eAAiBA,EAElB,KAAK,gBAAgB,SAAW,GAClC,KAAK,uBAAA,EAGP,MAAME,EAA4B,CAChC,cAAgB6E,GAAY,CACtBA,EACF,KAAK,YAAA,EAEL,KAAK,aAAA,EAEP,KAAK,aAAA,CACP,EACA,iBAAkB,CAACrH,EAAOwD,IAAS,KAAK,eAAexD,EAAOwD,CAAI,EAClE,sBAAuB,CAACxD,EAAOwD,IAAS,KAAK,oBAAoBxD,EAAOwD,CAAI,EAC5E,gBAAiB,CAACxD,EAAOqE,IAAY,KAAK,cAAcrE,EAAOqE,CAAO,EACtE,mBAAqBrE,GAAU,KAAK,iBAAiBA,CAAK,EAC1D,qBAAsB,CAACA,EAAOqE,IAAY,KAAK,mBAAmBrE,EAAOqE,CAAO,EAChF,eAAgB,CAACC,EAAQoB,IAAU,CACjC,KAAK,OAAOpB,CAAM,EAAIoB,EAClB,KAAK,UAAU,KAAK,QAAA,CAC1B,EACA,mBAAoB,IAAM,KAAK,mBAAA,CAAmB,EAGpD,OAAOrD,EAAiBC,EAAW,KAAK,OAAQ,KAAK,SAAUE,CAAS,CAC1E,CAEQ,cAAqB,CACtB,KAAK,iBACV,KAAK,eAAe,UAAY,GAChC,KAAK,YAAY,KAAK,cAAc,EACtC,CAEQ,eAAexC,EAAesD,EAA8C,CAClF,GAAIA,IAAa,YAAa,CAC5B,MAAMgE,EAAU,KAAK,OAAO,gBAAkB,CAAA,EACzCA,EAAQ,SAAStH,CAAK,IACzB,KAAK,OAAO,eAAiB,CAAC,GAAGsH,EAAStH,CAAK,EAEnD,KAAO,CACL,MAAMsH,EAAU,KAAK,OAAO,mBAAqB,CAAA,EAC5CA,EAAQ,SAAStH,CAAK,IACzB,KAAK,OAAO,kBAAoB,CAAC,GAAGsH,EAAStH,CAAK,EAEtD,CAEA,KAAK,qBAAqBA,EAAOsD,CAAQ,EACrC,KAAK,UAAU,KAAK,QAAA,EACxB,KAAK,aAAA,CACP,CAEQ,oBAAoBtD,EAAesD,EAA8C,CACnFA,IAAa,YACf,KAAK,OAAO,gBAAkB,KAAK,OAAO,gBAAkB,CAAA,GAAI,OAAQxD,GAAMA,IAAME,CAAK,EAEzF,KAAK,OAAO,mBAAqB,KAAK,OAAO,mBAAqB,CAAA,GAAI,OAAQF,GAAMA,IAAME,CAAK,EAG7F,KAAK,UAAU,KAAK,QAAA,EACxB,KAAK,aAAA,CACP,CAEQ,qBAAqBA,EAAeuH,EAA2D,CACjGA,IAAe,cACjB,KAAK,OAAO,gBAAkB,KAAK,OAAO,gBAAkB,CAAA,GAAI,OAAQzH,GAAMA,IAAME,CAAK,GAEvFuH,IAAe,iBACjB,KAAK,OAAO,mBAAqB,KAAK,OAAO,mBAAqB,CAAA,GAAI,OAAQzH,GAAMA,IAAME,CAAK,GAE7FuH,IAAe,WACjB,KAAK,OAAO,aAAe,KAAK,OAAO,aAAe,CAAA,GAAI,OAAQ9C,GAAMA,EAAE,QAAUzE,CAAK,EAE7F,CAEQ,cAAcA,EAAeqE,EAAwB,CAC3D,MAAMiD,EAAU,KAAK,OAAO,aAAe,CAAA,EACtCA,EAAQ,KAAM7C,GAAMA,EAAE,QAAUzE,CAAK,IACxC,KAAK,OAAO,YAAc,CAAC,GAAGsH,EAAS,CAAE,MAAAtH,EAAO,QAAAqE,EAAS,GAG3D,KAAK,qBAAqBrE,EAAO,QAAQ,EACrC,KAAK,UAAU,KAAK,QAAA,EACxB,KAAK,aAAA,CACP,CAEQ,iBAAiBA,EAAqB,CAC5C,KAAK,OAAO,aAAe,KAAK,OAAO,aAAe,CAAA,GAAI,OAAQyE,GAAMA,EAAE,QAAUzE,CAAK,EACrF,KAAK,UAAU,KAAK,QAAA,EACxB,KAAK,aAAA,CACP,CAEQ,mBAAmBA,EAAeqE,EAAwB,CAChE,MAAMrF,EAAc,KAAK,OAAO,aAAe,CAAA,EACzCwI,EAAaxI,EAAY,UAAWyF,GAAMA,EAAE,QAAUzE,CAAK,EAC7DwH,GAAc,IAChBxI,EAAYwI,CAAU,EAAI,CAAE,GAAGxI,EAAYwI,CAAU,EAAG,QAAAnD,CAAA,EACxD,KAAK,OAAO,YAAc,CAAC,GAAGrF,CAAW,GAEvC,KAAK,UAAU,KAAK,QAAA,CAC1B,CAMkB,OAASyI,CAG7B"}
1
+ {"version":3,"file":"pivot.umd.js","sources":["../../../../../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"],"sourcesContent":["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"],"names":["getPivotAggregator","getValueAggregator","validatePivotConfig","config","errors","createValueKey","columnValues","valueField","buildPivot","rows","rowGroupFields","columnGroupFields","valueFields","columnKeys","getUniqueColumnKeys","pivotRows","buildHierarchicalPivotRows","totals","calculateTotals","grandTotal","a","b","columnFields","keys","row","key","f","groupByField","field","groups","existing","depth","parentKey","result","values","aggregateValues","total","calculateRowTotal","currentField","remainingFields","hasChildren","grouped","groupValue","groupRows","rowKey","children","colKey","vf","nums","r","aggregator","aggregatedResult","valueKey","sum","val","sumRows","flattenPivotRows","expandedKeys","defaultExpanded","flatten","isExpanded","child","getAllGroupKeys","collectKeys","AGG_FUNCS","renderPivotPanel","container","isActive","callbacks","controller","ctx","wrapper","createSection","createOptionsPanel","createFieldZone","createValuesZone","createAvailableFieldsZone","title","contentFactory","section","header","content","zoneType","signal","zone","currentFields","placeholder","createFieldChip","e","chip","fieldInfo","label","removeBtn","currentValues","createValueChip","labelWrapper","aggSelect","aggFunc","option","allFields","usedFields","v","availableFields","empty","panel","createCheckbox","checked","onChange","input","span","renderPivotGroupRow","rowEl","col","colIdx","cell","indent","btn","count","value","renderPivotLeafRow","columns","renderPivotGrandTotalRow","PivotPlugin","BaseGridPlugin","mode","host","allKeys","indentWidth","flatRows","pr","currentVisibleKeys","pivotColumns","rowGroupHeaders","valueHeader","pivotRow","iconKey","el","icon","style","body","animClass","shadowRoot","grandTotalRow","fields","grid","enabled","current","targetZone","fieldIndex","styles"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(a,d){typeof exports=="object"&&typeof module<"u"?d(exports,require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/plugin/base-plugin"],d):(a=typeof globalThis<"u"?globalThis:a||self,d(a.TbwGridPlugin_selection={},a.TbwGrid))})(this,(function(a,d){"use strict";function g(s){return{startRow:Math.min(s.startRow,s.endRow),startCol:Math.min(s.startCol,s.endCol),endRow:Math.max(s.startRow,s.endRow),endCol:Math.max(s.startCol,s.endCol)}}function R(s){const e=g(s);return{from:{row:e.startRow,col:e.startCol},to:{row:e.endRow,col:e.endCol}}}function u(s){return s.map(R)}function C(s,e,t){const l=g(t);return s>=l.startRow&&s<=l.endRow&&e>=l.startCol&&e<=l.endCol}function w(s,e,t){return t.some(l=>C(s,e,l))}function m(s){const e=[],t=g(s);for(let l=t.startRow;l<=t.endRow;l++)for(let o=t.startCol;o<=t.endCol;o++)e.push({row:l,col:o});return e}function p(s){const e=new Map;for(const t of s)for(const l of m(t))e.set(`${l.row},${l.col}`,l);return[...e.values()]}function f(s,e){return{startRow:s.row,startCol:s.col,endRow:e.row,endCol:e.col}}const A=':host .selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}:host .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%))}:host([data-selection-mode="row"]) .cell-focus{outline:none}:host .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg)}:host .data-grid-row>.cell.selected.top{border-top:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.bottom{border-bottom:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.first{border-left:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.last{border-right:2px solid var(--tbw-range-border-color)}';function y(s,e,t){if(s==="cell"&&e.selectedCell)return{mode:s,ranges:[{from:{row:e.selectedCell.row,col:e.selectedCell.col},to:{row:e.selectedCell.row,col:e.selectedCell.col}}]};if(s==="row"&&e.selected.size>0){const l=[...e.selected].map(o=>({from:{row:o,col:0},to:{row:o,col:t-1}}));return{mode:s,ranges:l}}return s==="range"&&e.ranges.length>0?{mode:s,ranges:u(e.ranges)}:{mode:s,ranges:[]}}class b extends d.BaseGridPlugin{name="selection";version="1.0.0";get defaultConfig(){return{mode:"cell"}}selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;selectedCell=null;detach(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.pendingKeyboardUpdate=null}onCellClick(e){const{rowIndex:t,colIndex:l,originalEvent:o}=e,{mode:n}=this.config;if(n==="cell")return this.selectedCell={row:t,col:l},this.emit("selection-change",this.#e()),this.requestAfterRender(),!1;if(n==="row")return this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.emit("selection-change",this.#e()),this.requestAfterRender(),!1;if(n==="range"){const c=o.shiftKey,r=o.ctrlKey||o.metaKey;if(c&&this.cellAnchor){const i=f(this.cellAnchor,{row:t,col:l});r?this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i):this.ranges=[i],this.activeRange=i}else if(r){const i={startRow:t,startCol:l,endRow:t,endCol:l};this.ranges.push(i),this.activeRange=i,this.cellAnchor={row:t,col:l}}else{const i={startRow:t,startCol:l,endRow:t,endCol:l};this.ranges=[i],this.activeRange=i,this.cellAnchor={row:t,col:l}}return this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}return!1}onKeyDown(e){const{mode:t}=this.config,o=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if(e.key==="Escape")return t==="cell"?this.selectedCell=null:t==="row"?(this.selected.clear(),this.anchor=null):t==="range"&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#e()),this.requestAfterRender(),!0;if(t==="cell"&&o)return queueMicrotask(()=>{this.selectedCell={row:this.grid._focusRow,col:this.grid._focusCol},this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="row"&&(e.key==="ArrowUp"||e.key==="ArrowDown"))return queueMicrotask(()=>{this.selected.clear(),this.selected.add(this.grid._focusRow),this.lastSelected=this.grid._focusRow,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="range"&&o)return e.shiftKey&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:e.shiftKey},queueMicrotask(()=>this.requestAfterRender()),!1;if(t==="range"&&e.key==="a"&&(e.ctrlKey||e.metaKey)){const n=this.rows.length,c=this.columns.length;if(n>0&&c>0){const r={startRow:0,startCol:0,endRow:n-1,endCol:c-1};return this.ranges=[r],this.activeRange=r,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0}}return!1}onCellMouseDown(e){if(this.config.mode!=="range"||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0||e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const t=e.rowIndex,l=e.colIndex;this.cellAnchor={row:t,col:l},e.originalEvent.ctrlKey||e.originalEvent.metaKey||(this.ranges=[]);const n={startRow:t,startCol:l,endRow:t,endCol:l};return this.ranges.push(n),this.activeRange=n,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0}onCellMouseMove(e){if(this.config.mode!=="range"||!this.isDragging||!this.cellAnchor||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;const t=f(this.cellAnchor,{row:e.rowIndex,col:e.colIndex});return this.ranges.length>0?this.ranges[this.ranges.length-1]=t:this.ranges.push(t),this.activeRange=t,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0}onCellMouseUp(e){if(this.config.mode==="range"&&this.isDragging)return this.isDragging=!1,!0}#t(){const e=this.shadowRoot;if(!e)return;const{mode:t}=this.config;e.querySelectorAll(".cell").forEach(n=>{n.classList.remove("selected","top","bottom","first","last")});const o=e.querySelectorAll(".data-grid-row");if(o.forEach(n=>{n.classList.remove("selected","row-focus")}),t==="row"&&(e.querySelectorAll(".cell-focus").forEach(n=>n.classList.remove("cell-focus")),o.forEach(n=>{const c=n.querySelector(".cell[data-row]"),r=parseInt(c?.getAttribute("data-row")??"-1",10);r>=0&&this.selected.has(r)&&n.classList.add("selected","row-focus")})),t==="range"&&this.ranges.length>0){const n=this.activeRange?g(this.activeRange):null;e.querySelectorAll(".cell[data-row][data-col]").forEach(r=>{const i=parseInt(r.getAttribute("data-row")??"-1",10),h=parseInt(r.getAttribute("data-col")??"-1",10);i>=0&&h>=0&&w(i,h,this.ranges)&&(r.classList.add("selected"),r.classList.remove("cell-focus"),n&&(i===n.startRow&&r.classList.add("top"),i===n.endRow&&r.classList.add("bottom"),h===n.startCol&&r.classList.add("first"),h===n.endCol&&r.classList.add("last")))})}t==="cell"&&this.selectedCell&&e.querySelectorAll(".cell-focus").forEach(n=>n.classList.remove("cell-focus"))}afterRender(){const e=this.shadowRoot;if(!e)return;const t=e.children[0],{mode:l}=this.config;if(this.pendingKeyboardUpdate&&l==="range"){const{shiftKey:o}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const n=this.grid._focusRow,c=this.grid._focusCol;if(o&&this.cellAnchor){const r=f(this.cellAnchor,{row:n,col:c});this.ranges=[r],this.activeRange=r}else o||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:n,col:c});this.emit("selection-change",this.#e())}this.grid.setAttribute("data-selection-mode",l),t&&t.classList.toggle("selecting",this.isDragging),this.#t()}onScrollRender(){this.#t()}getSelectedCell(){return this.selectedCell}getSelectedRows(){return[...this.selected]}getRanges(){return u(this.ranges)}getSelectedCells(){return p(this.ranges)}isCellSelected(e,t){return w(e,t,this.ranges)}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(t=>({startRow:t.from.row,startCol:t.from.col,endRow:t.to.row,endCol:t.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",{mode:this.config.mode,ranges:u(this.ranges)}),this.requestAfterRender()}#e(){return y(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length)}styles=A}function K(s,e,t,l){const o=new Set(s.selected);let n=s.anchor;if(t==="single")o.clear(),o.add(e),n=e;else if(t==="multiple"){const c=l.ctrlKey||l.metaKey;if(l.shiftKey&&s.anchor!==null){const r=Math.min(s.anchor,e),i=Math.max(s.anchor,e);for(let h=r;h<=i;h++)o.add(h)}else c?(o.has(e)?o.delete(e):o.add(e),n=e):(o.clear(),o.add(e),n=e)}return{selected:o,lastSelected:e,anchor:n}}function v(s){const e=new Set;for(let t=0;t<s;t++)e.add(t);return e}function S(s,e){const t=[],l=[];for(const o of e)s.has(o)||t.push(o);for(const o of s)e.has(o)||l.push(o);return{added:t,removed:l}}a.SelectionPlugin=b,a.computeSelectionDiff=S,a.handleRowClick=K,a.selectAll=v,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(a,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("../../core/internal/utils"),require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/utils","../../core/plugin/base-plugin"],h):(a=typeof globalThis<"u"?globalThis:a||self,h(a.TbwGridPlugin_selection={},a.TbwGrid,a.TbwGrid))})(this,(function(a,h,R){"use strict";function g(s){return{startRow:Math.min(s.startRow,s.endRow),startCol:Math.min(s.startCol,s.endCol),endRow:Math.max(s.startRow,s.endRow),endCol:Math.max(s.startCol,s.endCol)}}function C(s){const e=g(s);return{from:{row:e.startRow,col:e.startCol},to:{row:e.endRow,col:e.endCol}}}function u(s){return s.map(C)}function m(s,e,t){const l=g(t);return s>=l.startRow&&s<=l.endRow&&e>=l.startCol&&e<=l.endCol}function w(s,e,t){return t.some(l=>m(s,e,l))}function p(s){const e=[],t=g(s);for(let l=t.startRow;l<=t.endRow;l++)for(let n=t.startCol;n<=t.endCol;n++)e.push({row:l,col:n});return e}function b(s){const e=new Map;for(const t of s)for(const l of p(t))e.set(`${l.row},${l.col}`,l);return[...e.values()]}function f(s,e){return{startRow:s.row,startCol:s.col,endRow:e.row,endCol:e.col}}const y=':host .selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}:host .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%))}:host([data-selection-mode="row"]) .cell-focus{outline:none}:host .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg)}:host .data-grid-row>.cell.selected.top{border-top:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.bottom{border-bottom:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.first{border-left:2px solid var(--tbw-range-border-color)}:host .data-grid-row>.cell.selected.last{border-right:2px solid var(--tbw-range-border-color)}:host .tbw-selection-summary{font-size:13px;color:var(--tbw-color-fg-muted);white-space:nowrap}';function A(s,e,t){if(s==="cell"&&e.selectedCell)return{mode:s,ranges:[{from:{row:e.selectedCell.row,col:e.selectedCell.col},to:{row:e.selectedCell.row,col:e.selectedCell.col}}]};if(s==="row"&&e.selected.size>0){const l=[...e.selected].map(n=>({from:{row:n,col:0},to:{row:n,col:t-1}}));return{mode:s,ranges:l}}return s==="range"&&e.ranges.length>0?{mode:s,ranges:u(e.ranges)}:{mode:s,ranges:[]}}class K extends R.BaseGridPlugin{name="selection";version="1.0.0";get defaultConfig(){return{mode:"cell"}}selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;selectedCell=null;detach(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.pendingKeyboardUpdate=null}onCellClick(e){const{rowIndex:t,colIndex:l,originalEvent:n}=e,{mode:o}=this.config;if(o==="cell")return this.selectedCell={row:t,col:l},this.emit("selection-change",this.#e()),this.requestAfterRender(),!1;if(o==="row")return this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.emit("selection-change",this.#e()),this.requestAfterRender(),!1;if(o==="range"){const c=n.shiftKey,r=n.ctrlKey||n.metaKey;if(c&&this.cellAnchor){const i=f(this.cellAnchor,{row:t,col:l});r?this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i):this.ranges=[i],this.activeRange=i}else if(r){const i={startRow:t,startCol:l,endRow:t,endCol:l};this.ranges.push(i),this.activeRange=i,this.cellAnchor={row:t,col:l}}else{const i={startRow:t,startCol:l,endRow:t,endCol:l};this.ranges=[i],this.activeRange=i,this.cellAnchor={row:t,col:l}}return this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}return!1}onKeyDown(e){const{mode:t}=this.config,n=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if(e.key==="Escape")return t==="cell"?this.selectedCell=null:t==="row"?(this.selected.clear(),this.anchor=null):t==="range"&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#e()),this.requestAfterRender(),!0;if(t==="cell"&&n)return queueMicrotask(()=>{this.selectedCell={row:this.grid._focusRow,col:this.grid._focusCol},this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="row"&&(e.key==="ArrowUp"||e.key==="ArrowDown"))return queueMicrotask(()=>{this.selected.clear(),this.selected.add(this.grid._focusRow),this.lastSelected=this.grid._focusRow,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="range"&&n){const o=e.key==="Tab",c=e.shiftKey&&!o;return c&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:c},queueMicrotask(()=>this.requestAfterRender()),!1}if(t==="range"&&e.key==="a"&&(e.ctrlKey||e.metaKey)){const o=this.rows.length,c=this.columns.length;if(o>0&&c>0){const r={startRow:0,startCol:0,endRow:o-1,endCol:c-1};return this.ranges=[r],this.activeRange=r,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0}}return!1}onCellMouseDown(e){if(this.config.mode!=="range"||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0||e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const t=e.rowIndex,l=e.colIndex;this.cellAnchor={row:t,col:l},e.originalEvent.ctrlKey||e.originalEvent.metaKey||(this.ranges=[]);const o={startRow:t,startCol:l,endRow:t,endCol:l};return this.ranges.push(o),this.activeRange=o,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0}onCellMouseMove(e){if(this.config.mode!=="range"||!this.isDragging||!this.cellAnchor||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;const t=f(this.cellAnchor,{row:e.rowIndex,col:e.colIndex});return this.ranges.length>0?this.ranges[this.ranges.length-1]=t:this.ranges.push(t),this.activeRange=t,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0}onCellMouseUp(e){if(this.config.mode==="range"&&this.isDragging)return this.isDragging=!1,!0}#t(){const e=this.shadowRoot;if(!e)return;const{mode:t}=this.config;e.querySelectorAll(".cell").forEach(o=>{o.classList.remove("selected","top","bottom","first","last")});const n=e.querySelectorAll(".data-grid-row");if(n.forEach(o=>{o.classList.remove("selected","row-focus")}),t==="row"&&(h.clearCellFocus(e),n.forEach(o=>{const c=o.querySelector(".cell[data-row]"),r=h.getRowIndexFromCell(c);r>=0&&this.selected.has(r)&&o.classList.add("selected","row-focus")})),t==="range"&&this.ranges.length>0){h.clearCellFocus(e);const o=this.activeRange?g(this.activeRange):null;e.querySelectorAll(".cell[data-row][data-col]").forEach(r=>{const i=parseInt(r.getAttribute("data-row")??"-1",10),d=parseInt(r.getAttribute("data-col")??"-1",10);i>=0&&d>=0&&w(i,d,this.ranges)&&(r.classList.add("selected"),o&&(i===o.startRow&&r.classList.add("top"),i===o.endRow&&r.classList.add("bottom"),d===o.startCol&&r.classList.add("first"),d===o.endCol&&r.classList.add("last")))})}t==="cell"&&this.selectedCell&&h.clearCellFocus(e)}afterRender(){const e=this.shadowRoot;if(!e)return;const t=e.children[0],{mode:l}=this.config;if(this.pendingKeyboardUpdate&&l==="range"){const{shiftKey:n}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const o=this.grid._focusRow,c=this.grid._focusCol;if(n&&this.cellAnchor){const r=f(this.cellAnchor,{row:o,col:c});this.ranges=[r],this.activeRange=r}else n||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:o,col:c});this.emit("selection-change",this.#e())}this.grid.setAttribute("data-selection-mode",l),t&&t.classList.toggle("selecting",this.isDragging),this.#t()}onScrollRender(){this.#t()}getSelectedCell(){return this.selectedCell}getSelectedRows(){return[...this.selected]}getRanges(){return u(this.ranges)}getSelectedCells(){return b(this.ranges)}isCellSelected(e,t){return w(e,t,this.ranges)}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(t=>({startRow:t.from.row,startCol:t.from.col,endRow:t.to.row,endCol:t.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",{mode:this.config.mode,ranges:u(this.ranges)}),this.requestAfterRender()}#e(){return A(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length)}styles=y}function v(s,e,t,l){const n=new Set(s.selected);let o=s.anchor;if(t==="single")n.clear(),n.add(e),o=e;else if(t==="multiple"){const c=l.ctrlKey||l.metaKey;if(l.shiftKey&&s.anchor!==null){const r=Math.min(s.anchor,e),i=Math.max(s.anchor,e);for(let d=r;d<=i;d++)n.add(d)}else c?(n.has(e)?n.delete(e):n.add(e),o=e):(n.clear(),n.add(e),o=e)}return{selected:n,lastSelected:e,anchor:o}}function S(s){const e=new Set;for(let t=0;t<s;t++)e.add(t);return e}function q(s,e){const t=[],l=[];for(const n of e)s.has(n)||t.push(n);for(const n of s)e.has(n)||l.push(n);return{added:t,removed:l}}a.SelectionPlugin=K,a.computeSelectionDiff=q,a.handleRowClick=v,a.selectAll=S,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=selection.umd.js.map