@toolbox-web/grid 1.28.2 → 1.29.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/all.js +2 -2
- package/all.js.map +1 -1
- package/custom-elements.json +43 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/lib/core/constants.d.ts +1 -7
- package/lib/core/grid.d.ts +40 -3
- package/lib/core/internal/render-scheduler.d.ts +3 -1
- package/lib/core/plugin/base-plugin.d.ts +1 -1
- package/lib/core/plugin/plugin-manager.d.ts +2 -0
- package/lib/core/types.d.ts +53 -0
- package/lib/plugins/clipboard/ClipboardPlugin.d.ts +0 -21
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/ColumnVirtualizationPlugin.d.ts +0 -16
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/editing/index.js +1 -1
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/export/ExportPlugin.d.ts +0 -18
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/FilteringPlugin.d.ts +0 -21
- package/lib/plugins/filtering/filter-model.d.ts +8 -1
- package/lib/plugins/filtering/index.js +1 -1
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/GroupingColumnsPlugin.d.ts +0 -16
- package/lib/plugins/grouping-columns/grouping-columns.d.ts +23 -3
- package/lib/plugins/grouping-columns/index.js +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts +0 -23
- package/lib/plugins/grouping-rows/index.js +2 -2
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +0 -22
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/MultiSortPlugin.d.ts +0 -18
- package/lib/plugins/multi-sort/index.js +1 -1
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/multi-sort/multi-sort.d.ts +7 -0
- package/lib/plugins/pinned-columns/index.js +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts +0 -11
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/PivotPlugin.d.ts +0 -24
- package/lib/plugins/pivot/index.js +1 -1
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/print/PrintPlugin.d.ts +2 -21
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/reorder-columns/ReorderPlugin.d.ts +0 -7
- package/lib/plugins/reorder-columns/index.js +1 -1
- package/lib/plugins/reorder-columns/index.js.map +1 -1
- package/lib/plugins/reorder-rows/RowReorderPlugin.d.ts +1 -13
- package/lib/plugins/reorder-rows/index.js +1 -1
- package/lib/plugins/reorder-rows/index.js.map +1 -1
- package/lib/plugins/responsive/index.js +1 -1
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/selection/index.js +1 -1
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/ServerSidePlugin.d.ts +0 -16
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/tooltip/index.js.map +1 -1
- package/lib/plugins/tree/TreePlugin.d.ts +0 -22
- package/lib/plugins/tree/index.js +1 -1
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +0 -19
- package/lib/plugins/undo-redo/index.js +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/VisibilityPlugin.d.ts +0 -16
- package/lib/plugins/visibility/index.js +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/themes/dg-theme-material.css +5 -0
- package/umd/grid.all.umd.js +1 -1
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +1 -1
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/clipboard.umd.js.map +1 -1
- package/umd/plugins/column-virtualization.umd.js.map +1 -1
- package/umd/plugins/export.umd.js.map +1 -1
- package/umd/plugins/filtering.umd.js +1 -1
- package/umd/plugins/filtering.umd.js.map +1 -1
- package/umd/plugins/grouping-columns.umd.js +1 -1
- package/umd/plugins/grouping-columns.umd.js.map +1 -1
- package/umd/plugins/grouping-rows.umd.js +1 -1
- package/umd/plugins/grouping-rows.umd.js.map +1 -1
- package/umd/plugins/master-detail.umd.js.map +1 -1
- package/umd/plugins/multi-sort.umd.js +1 -1
- package/umd/plugins/multi-sort.umd.js.map +1 -1
- package/umd/plugins/pinned-columns.umd.js +1 -1
- package/umd/plugins/pinned-columns.umd.js.map +1 -1
- package/umd/plugins/pinned-rows.umd.js.map +1 -1
- package/umd/plugins/pivot.umd.js +1 -1
- package/umd/plugins/pivot.umd.js.map +1 -1
- package/umd/plugins/print.umd.js.map +1 -1
- package/umd/plugins/reorder-columns.umd.js +1 -1
- package/umd/plugins/reorder-columns.umd.js.map +1 -1
- package/umd/plugins/reorder-rows.umd.js +1 -1
- package/umd/plugins/reorder-rows.umd.js.map +1 -1
- package/umd/plugins/selection.umd.js +1 -1
- package/umd/plugins/selection.umd.js.map +1 -1
- package/umd/plugins/server-side.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +1 -1
- package/umd/plugins/tree.umd.js.map +1 -1
- package/umd/plugins/undo-redo.umd.js +1 -1
- package/umd/plugins/undo-redo.umd.js.map +1 -1
- package/umd/plugins/visibility.umd.js +1 -1
- package/umd/plugins/visibility.umd.js.map +1 -1
|
@@ -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/PivotPlugin.ts","../../../../../libs/grid/src/lib/plugins/pivot/pivot-rows.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 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, type PluginManifest } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, 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/**\n * Pivot Table Plugin for tbw-grid\n *\n * Transforms flat data into a pivot table view with grouped rows, grouped columns,\n * and aggregated values. Includes an interactive tool panel for configuring\n * row groups, column groups, and value aggregations at runtime.\n *\n * ## Installation\n *\n * ```ts\n * import { PivotPlugin } from '@toolbox-web/grid/plugins/pivot';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `active` | `boolean` | `true` | Whether pivot is active on load |\n * | `rowGroupFields` | `string[]` | `[]` | Fields for row grouping |\n * | `columnGroupFields` | `string[]` | `[]` | Fields for column grouping |\n * | `valueFields` | `ValueField[]` | `[]` | Aggregation value fields |\n * | `showTotals` | `boolean` | `true` | Show row subtotals |\n * | `showGrandTotal` | `boolean` | `true` | Show grand total row |\n * | `showToolPanel` | `boolean` | `true` | Show interactive pivot panel |\n * | `defaultExpanded` | `boolean` | `true` | Groups expanded by default |\n * | `indentWidth` | `number` | `20` | Indent per depth level (px) |\n * | `animation` | `false \\| 'slide' \\| 'fade'` | `'slide'` | Expand/collapse animation |\n *\n * ## Aggregation Functions\n *\n * `sum`, `avg`, `count`, `min`, `max`, `first`, `last`\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `expandGroup` | `(path: string[]) => void` | Expand a specific group |\n * | `collapseGroup` | `(path: string[]) => void` | Collapse a specific group |\n * | `expandAll` | `() => void` | Expand all groups |\n * | `collapseAll` | `() => void` | Collapse all groups |\n *\n * ## CSS Custom Properties\n *\n * | Property | Default | Description |\n * |----------|---------|-------------|\n * | `--tbw-pivot-group-bg` | `var(--tbw-color-row-alt)` | Group row background |\n * | `--tbw-pivot-grand-total-bg` | `var(--tbw-color-header-bg)` | Grand total row |\n *\n * @example Basic Pivot Table\n * ```ts\n * import '@toolbox-web/grid';\n * import { PivotPlugin } from '@toolbox-web/grid/plugins/pivot';\n *\n * grid.gridConfig = {\n * columns: [...],\n * plugins: [\n * new PivotPlugin({\n * rowGroupFields: ['region', 'product'],\n * columnGroupFields: ['quarter'],\n * valueFields: [{ field: 'sales', aggFunc: 'sum', header: 'Total' }],\n * }),\n * ],\n * };\n * ```\n *\n * @example Programmatic-Only (No Tool Panel)\n * ```ts\n * new PivotPlugin({\n * showToolPanel: false,\n * rowGroupFields: ['category'],\n * valueFields: [{ field: 'amount', aggFunc: 'sum' }],\n * })\n * ```\n *\n * @see {@link PivotConfig} for all configuration options\n * @see {@link PivotValueField} for value field structure\n *\n * @internal Extends BaseGridPlugin\n */\nexport class PivotPlugin extends BaseGridPlugin<PivotConfig> {\n /**\n * Plugin manifest declaring incompatibilities with other plugins.\n * @internal\n */\n static override readonly manifest: PluginManifest = {\n incompatibleWith: [\n {\n name: 'groupingRows',\n reason:\n 'PivotPlugin creates its own aggregated row and column structure. ' +\n 'Row grouping cannot be applied on top of pivot-generated rows.',\n },\n {\n name: 'tree',\n reason:\n 'PivotPlugin replaces the entire row and column structure with aggregated pivot data. ' +\n 'Tree hierarchy cannot coexist with pivot aggregation.',\n },\n {\n name: 'serverSide',\n reason:\n 'PivotPlugin requires the full dataset to compute aggregations. ' +\n 'ServerSidePlugin lazy-loads rows in blocks, so pivot aggregation cannot be performed client-side.',\n },\n ],\n };\n\n /** @internal */\n readonly name = 'pivot';\n /** @internal */\n override readonly styles = styles;\n\n /** Tool panel ID for shell integration */\n static readonly PANEL_ID = 'pivot';\n\n /** @internal */\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 /** Tracks whether user has manually interacted with expand/collapse */\n private userHasToggledExpand = false;\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 expand/collapse animation style from plugin config.\n * Uses base class isAnimationEnabled to respect grid-level settings.\n */\n private get animationStyle(): ExpandCollapseAnimation {\n if (!this.isAnimationEnabled) return false;\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\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 this.userHasToggledExpand = false;\n }\n\n // #endregion\n\n // #region Shell Integration\n\n /** @internal */\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 /** @internal */\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 // Build pivot first so we have the rows structure\n this.pivotResult = buildPivot(rows as PivotDataRow[], this.config);\n\n // Initialize expanded state with defaults if first build AND user hasn't manually toggled\n // This prevents re-expanding when user collapses all groups\n if (this.expandedKeys.size === 0 && this.defaultExpanded && !this.userHasToggledExpand) {\n this.expandAllKeys();\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 /** @internal */\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 /** @internal */\n override renderRow(row: Record<string, unknown>, rowEl: HTMLElement, rowIndex: number): 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 rowIndex,\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, rowIndex);\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 /** @internal */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n // SPACE toggles expansion on pivot group rows\n if (event.key !== ' ') return;\n if (!this.isActive) return;\n\n const focusRow = this.grid._focusRow;\n const row = this.rows[focusRow] as Record<string, unknown> | undefined;\n\n // Only handle SPACE on pivot group rows with children\n if (!row?.__pivotIsGroup || !row.__pivotHasChildren) return;\n\n event.preventDefault();\n this.toggle(row.__pivotRowKey as string);\n\n // Restore focus styling after render completes via render pipeline\n this.requestRenderWithFocus();\n return true;\n }\n\n /** @internal */\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.gridElement?.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 gridEl = this.gridElement;\n if (!gridEl) return;\n\n // Find the scroll container to append the footer\n const container =\n gridEl.querySelector('.tbw-scroll-area') ??\n gridEl.querySelector('.tbw-grid-content') ??\n gridEl.querySelector('.tbw-grid-root');\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 this.userHasToggledExpand = true;\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 /**\n * Expand a specific pivot group row, revealing its children.\n *\n * @param key - The pivot row key (hierarchical path, e.g. `'Engineering'` or `'Engineering||Frontend'`)\n */\n expand(key: string): void {\n this.userHasToggledExpand = true;\n this.expandedKeys.add(key);\n this.requestRender();\n }\n\n /**\n * Collapse a specific pivot group row, hiding its children.\n *\n * @param key - The pivot row key to collapse\n */\n collapse(key: string): void {\n this.userHasToggledExpand = true;\n this.expandedKeys.delete(key);\n this.requestRender();\n }\n\n /**\n * Expand all pivot group rows, revealing the full hierarchy.\n */\n expandAll(): void {\n this.userHasToggledExpand = true;\n this.expandAllKeys();\n this.requestRender();\n }\n\n /**\n * Collapse all pivot group rows, showing only top-level groups.\n */\n collapseAll(): void {\n this.userHasToggledExpand = true;\n this.expandedKeys.clear();\n this.requestRender();\n }\n\n /**\n * Add all group keys from the current pivot result to expandedKeys.\n */\n private expandAllKeys(): void {\n if (!this.pivotResult) return;\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n }\n\n /**\n * Check whether a specific pivot group row is currently expanded.\n *\n * @param key - The pivot row key to check\n * @returns `true` if the group is expanded\n */\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n // #endregion\n\n // #region Public API\n\n /**\n * Enable pivot mode.\n *\n * Captures the original column set (if not already captured) and activates\n * the pivot transformation. The grid will re-render with pivot columns and rows.\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.enablePivot();\n * ```\n */\n enablePivot(): void {\n if (this.originalColumns.length === 0) {\n this.captureOriginalColumns();\n }\n this.isActive = true;\n this.requestRender();\n }\n\n /**\n * Disable pivot mode and restore the original grid columns.\n *\n * The grid reverts to its normal tabular layout with the original column definitions.\n */\n disablePivot(): void {\n this.isActive = false;\n this.pivotResult = null;\n this.requestRender();\n }\n\n /**\n * Check whether pivot mode is currently active.\n *\n * @returns `true` if the grid is in pivot mode\n */\n isPivotActive(): boolean {\n return this.isActive;\n }\n\n /**\n * Get the current pivot computation result.\n *\n * Returns the full {@link PivotResult} with rows, column keys, totals,\n * and grand total. Returns `null` if pivot is inactive or not yet computed.\n *\n * @returns The computed pivot result, or `null`\n */\n getPivotResult(): PivotResult | null {\n return this.pivotResult;\n }\n\n /**\n * Set the fields used for row grouping (vertical axis).\n *\n * Triggers a re-render with the new pivot structure.\n *\n * @param fields - Array of field names to group rows by\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.setRowGroupFields(['region', 'department']);\n * ```\n */\n setRowGroupFields(fields: string[]): void {\n this.config.rowGroupFields = fields;\n this.requestRender();\n }\n\n /**\n * Set the fields whose unique values become column headers (horizontal axis).\n *\n * Triggers a re-render with the new pivot column structure.\n *\n * @param fields - Array of field names for column grouping\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.setColumnGroupFields(['quarter']);\n * ```\n */\n setColumnGroupFields(fields: string[]): void {\n this.config.columnGroupFields = fields;\n this.requestRender();\n }\n\n /**\n * Set the value fields and their aggregation functions.\n *\n * Each value field defines which data field to aggregate and how.\n * Triggers a re-render with the new aggregation.\n *\n * @param fields - Array of {@link PivotValueField} definitions\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.setValueFields([\n * { field: 'revenue', aggFunc: 'sum', header: 'Total Revenue' },\n * { field: 'orders', aggFunc: 'count', header: '# Orders' },\n * ]);\n * ```\n */\n setValueFields(fields: PivotValueField[]): void {\n this.config.valueFields = fields;\n this.requestRender();\n }\n\n /**\n * Force re-computation of the pivot result.\n *\n * Clears the cached pivot result and triggers a full re-render.\n * Call this after changing the underlying row data.\n */\n refresh(): void {\n this.pivotResult = null;\n this.requestRender();\n }\n\n // #endregion\n\n // #region Tool Panel API\n\n /**\n * Show the pivot tool panel.\n * Opens the tool panel and ensures this section is expanded.\n */\n showPanel(): void {\n this.grid.openToolPanel();\n // Ensure our section is expanded\n if (!this.grid.expandedToolPanelSections.includes(PivotPlugin.PANEL_ID)) {\n this.grid.toggleToolPanelSection(PivotPlugin.PANEL_ID);\n }\n }\n\n /**\n * Hide the tool panel.\n */\n hidePanel(): void {\n this.grid.closeToolPanel();\n }\n\n /**\n * Toggle the pivot tool panel section.\n */\n togglePanel(): void {\n // If tool panel is closed, open it first\n if (!this.grid.isToolPanelOpen) {\n this.grid.openToolPanel();\n }\n this.grid.toggleToolPanelSection(PivotPlugin.PANEL_ID);\n }\n\n /**\n * Check if the pivot panel section is currently expanded.\n */\n isPanelVisible(): boolean {\n return this.grid.isToolPanelOpen && this.grid.expandedToolPanelSections.includes(PivotPlugin.PANEL_ID);\n }\n\n // #endregion\n\n // #region Private Helpers\n\n private get gridColumns(): ColumnConfig[] {\n return (this.grid.columns ?? []) as ColumnConfig[];\n }\n\n /**\n * Refresh pivot and update tool panel if active.\n */\n private refreshIfActive(): void {\n if (this.isActive) this.refresh();\n this.refreshPanel();\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 try {\n const columns = this.grid.getAllColumns?.() ?? this.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 this.refreshIfActive();\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 this.refreshIfActive();\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 this.refreshIfActive();\n }\n\n private removeValueField(field: string): void {\n this.config.valueFields = (this.config.valueFields ?? []).filter((v) => v.field !== field);\n this.refreshIfActive();\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","/**\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 rowIndex: number;\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 = 'data-grid-row 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('data-row', String(ctx.rowIndex));\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(\n row: PivotRowData,\n rowEl: HTMLElement,\n columns: ColumnConfig[],\n rowIndex: number,\n): boolean {\n rowEl.className = 'data-grid-row 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('data-row', String(rowIndex));\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"],"names":["getPivotAggregator","getValueAggregator","createValueKey","columnValues","valueField","join","buildPivot","rows","config","rowGroupFields","columnGroupFields","valueFields","columnKeys","columnFields","length","keys","Set","row","key","map","f","String","add","sort","getUniqueColumnKeys","pivotRows","buildHierarchicalPivotRows","totals","sumRows","isGroup","children","colKey","vf","valueKey","field","values","calculateTotals","grandTotal","Object","reduce","a","b","depth","parentKey","result","aggregateValues","total","calculateRowTotal","push","rowKey","rowLabel","rowCount","currentField","remainingFields","slice","hasChildren","grouped","groups","Map","existing","get","set","groupByField","groupValue","groupRows","nums","filter","r","Number","aggregator","aggFunc","aggregatedResult","sum","val","AGG_FUNCS","renderPivotPanel","container","isActive","callbacks","controller","AbortController","ctx","signal","wrapper","document","createElement","className","appendChild","createSection","panel","createCheckbox","checked","onTogglePivot","showTotals","onOptionChange","showGrandTotal","createOptionsPanel","createFieldZone","zone","setAttribute","currentValues","placeholder","textContent","createValueChip","addEventListener","e","preventDefault","classList","remove","dataTransfer","getData","onAddValueField","createValuesZone","allFields","getAvailableFields","usedFields","v","availableFields","has","empty","chip","header","draggable","title","setData","createAvailableFieldsZone","abort","contentFactory","section","content","zoneType","currentFields","createFieldChip","onAddFieldToZone","fieldInfo","find","label","removeBtn","innerHTML","stopPropagation","onRemoveFieldFromZone","labelWrapper","aggSelect","option","value","toUpperCase","selected","onUpdateValueAggFunc","onRemoveValueField","onChange","input","type","span","PivotPlugin","BaseGridPlugin","static","incompatibleWith","name","reason","styles","defaultConfig","active","showToolPanel","animation","hasInitialized","pivotResult","fieldHeaderMap","expandedKeys","defaultExpanded","userHasToggledExpand","originalColumns","panelContainer","grandTotalFooter","previousVisibleKeys","keysToAnimate","hasValidPivotConfig","this","animationStyle","isAnimationEnabled","detach","clear","cleanupGrandTotalFooter","getToolPanel","userConfig","id","PANEL_ID","icon","tooltip","order","render","renderPanel","processRows","errors","validatePivotConfig","warn","buildFieldHeaderMap","size","expandAllKeys","indentWidth","flatRows","flatten","isExpanded","child","flattenPivotRows","pr","__pivotRowKey","__pivotLabel","__pivotDepth","__pivotIsGroup","__pivotHasChildren","Boolean","__pivotExpanded","__pivotRowCount","__pivotIndent","__pivotTotal","currentVisibleKeys","processColumns","columns","pivotColumns","rowGroupHeaders","width","valueHeader","renderRow","rowEl","rowIndex","pivotRow","forEach","col","colIdx","cell","indent","style","paddingLeft","btn","setIcon","resolveIcon","onToggle","count","renderPivotGroupRow","gridColumns","toggle","iconKey","el","renderPivotLeafRow","cleanupPivotStyling","contains","removeAttribute","onKeyDown","event","focusRow","grid","_focusRow","requestRenderWithFocus","afterRender","renderGrandTotalFooter","body","gridElement","querySelector","animClass","querySelectorAll","dataset","pivotKey","once","gridEl","grandTotalRow","__pivotIsGrandTotal","delete","requestRender","expand","collapse","expandAll","collapseAll","allKeys","collectKeys","getAllGroupKeys","enablePivot","captureOriginalColumns","disablePivot","isPivotActive","getPivotResult","setRowGroupFields","fields","setColumnGroupFields","setValueFields","refresh","showPanel","openToolPanel","expandedToolPanelSections","includes","toggleToolPanelSection","hidePanel","closeToolPanel","togglePanel","isToolPanelOpen","isPanelVisible","refreshIfActive","refreshPanel","getAllColumns","startsWith","enabled","addFieldToZone","removeFieldFromZone","addValueField","removeValueField","updateValueAggFunc","current","removeFromOtherZones","targetZone","some","fieldIndex","findIndex"],"mappings":"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"}
|
|
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/PivotPlugin.ts","../../../../../libs/grid/src/lib/plugins/pivot/pivot-rows.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 { GridClasses } from '../../core/constants';\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(GridClasses.DRAGGING);\n },\n { signal },\n );\n\n chip.addEventListener(\n 'dragend',\n () => {\n chip.classList.remove(GridClasses.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(GridClasses.DRAGGING);\n },\n { signal },\n );\n\n chip.addEventListener(\n 'dragend',\n () => {\n chip.classList.remove(GridClasses.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 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, type PluginManifest } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, 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/**\n * Pivot Table Plugin for tbw-grid\n *\n * Transforms flat data into a pivot table view with grouped rows, grouped columns,\n * and aggregated values. Includes an interactive tool panel for configuring\n * row groups, column groups, and value aggregations at runtime.\n *\n * ## Installation\n *\n * ```ts\n * import { PivotPlugin } from '@toolbox-web/grid/plugins/pivot';\n * ```\n *\n * ## Aggregation Functions\n *\n * `sum`, `avg`, `count`, `min`, `max`, `first`, `last`\n *\n * ## CSS Custom Properties\n *\n * | Property | Default | Description |\n * |----------|---------|-------------|\n * | `--tbw-pivot-group-bg` | `var(--tbw-color-row-alt)` | Group row background |\n * | `--tbw-pivot-grand-total-bg` | `var(--tbw-color-header-bg)` | Grand total row |\n *\n * @example Basic Pivot Table\n * ```ts\n * import '@toolbox-web/grid';\n * import { PivotPlugin } from '@toolbox-web/grid/plugins/pivot';\n *\n * grid.gridConfig = {\n * columns: [...],\n * plugins: [\n * new PivotPlugin({\n * rowGroupFields: ['region', 'product'],\n * columnGroupFields: ['quarter'],\n * valueFields: [{ field: 'sales', aggFunc: 'sum', header: 'Total' }],\n * }),\n * ],\n * };\n * ```\n *\n * @example Programmatic-Only (No Tool Panel)\n * ```ts\n * new PivotPlugin({\n * showToolPanel: false,\n * rowGroupFields: ['category'],\n * valueFields: [{ field: 'amount', aggFunc: 'sum' }],\n * })\n * ```\n *\n * @see {@link PivotConfig} for all configuration options\n * @see {@link PivotValueField} for value field structure\n *\n * @internal Extends BaseGridPlugin\n */\nexport class PivotPlugin extends BaseGridPlugin<PivotConfig> {\n /**\n * Plugin manifest declaring incompatibilities with other plugins.\n * @internal\n */\n static override readonly manifest: PluginManifest = {\n incompatibleWith: [\n {\n name: 'groupingRows',\n reason:\n 'PivotPlugin creates its own aggregated row and column structure. ' +\n 'Row grouping cannot be applied on top of pivot-generated rows.',\n },\n {\n name: 'tree',\n reason:\n 'PivotPlugin replaces the entire row and column structure with aggregated pivot data. ' +\n 'Tree hierarchy cannot coexist with pivot aggregation.',\n },\n {\n name: 'serverSide',\n reason:\n 'PivotPlugin requires the full dataset to compute aggregations. ' +\n 'ServerSidePlugin lazy-loads rows in blocks, so pivot aggregation cannot be performed client-side.',\n },\n ],\n };\n\n /** @internal */\n readonly name = 'pivot';\n /** @internal */\n override readonly styles = styles;\n\n /** Tool panel ID for shell integration */\n static readonly PANEL_ID = 'pivot';\n\n /** @internal */\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 /** Tracks whether user has manually interacted with expand/collapse */\n private userHasToggledExpand = false;\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 expand/collapse animation style from plugin config.\n * Uses base class isAnimationEnabled to respect grid-level settings.\n */\n private get animationStyle(): ExpandCollapseAnimation {\n if (!this.isAnimationEnabled) return false;\n return this.config.animation ?? 'slide';\n }\n\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\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 this.userHasToggledExpand = false;\n }\n\n // #endregion\n\n // #region Shell Integration\n\n /** @internal */\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 /** @internal */\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 // Build pivot first so we have the rows structure\n this.pivotResult = buildPivot(rows as PivotDataRow[], this.config);\n\n // Initialize expanded state with defaults if first build AND user hasn't manually toggled\n // This prevents re-expanding when user collapses all groups\n if (this.expandedKeys.size === 0 && this.defaultExpanded && !this.userHasToggledExpand) {\n this.expandAllKeys();\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 /** @internal */\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 /** @internal */\n override renderRow(row: Record<string, unknown>, rowEl: HTMLElement, rowIndex: number): 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 rowIndex,\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, rowIndex);\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 /** @internal */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n // SPACE toggles expansion on pivot group rows\n if (event.key !== ' ') return;\n if (!this.isActive) return;\n\n const focusRow = this.grid._focusRow;\n const row = this.rows[focusRow] as Record<string, unknown> | undefined;\n\n // Only handle SPACE on pivot group rows with children\n if (!row?.__pivotIsGroup || !row.__pivotHasChildren) return;\n\n event.preventDefault();\n this.toggle(row.__pivotRowKey as string);\n\n // Restore focus styling after render completes via render pipeline\n this.requestRenderWithFocus();\n return true;\n }\n\n /** @internal */\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.gridElement?.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 gridEl = this.gridElement;\n if (!gridEl) return;\n\n // Find the scroll container to append the footer\n const container =\n gridEl.querySelector('.tbw-scroll-area') ??\n gridEl.querySelector('.tbw-grid-content') ??\n gridEl.querySelector('.tbw-grid-root');\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 this.userHasToggledExpand = true;\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 /**\n * Expand a specific pivot group row, revealing its children.\n *\n * @param key - The pivot row key (hierarchical path, e.g. `'Engineering'` or `'Engineering||Frontend'`)\n */\n expand(key: string): void {\n this.userHasToggledExpand = true;\n this.expandedKeys.add(key);\n this.requestRender();\n }\n\n /**\n * Collapse a specific pivot group row, hiding its children.\n *\n * @param key - The pivot row key to collapse\n */\n collapse(key: string): void {\n this.userHasToggledExpand = true;\n this.expandedKeys.delete(key);\n this.requestRender();\n }\n\n /**\n * Expand all pivot group rows, revealing the full hierarchy.\n */\n expandAll(): void {\n this.userHasToggledExpand = true;\n this.expandAllKeys();\n this.requestRender();\n }\n\n /**\n * Collapse all pivot group rows, showing only top-level groups.\n */\n collapseAll(): void {\n this.userHasToggledExpand = true;\n this.expandedKeys.clear();\n this.requestRender();\n }\n\n /**\n * Add all group keys from the current pivot result to expandedKeys.\n */\n private expandAllKeys(): void {\n if (!this.pivotResult) return;\n const allKeys = getAllGroupKeys(this.pivotResult.rows);\n for (const key of allKeys) {\n this.expandedKeys.add(key);\n }\n }\n\n /**\n * Check whether a specific pivot group row is currently expanded.\n *\n * @param key - The pivot row key to check\n * @returns `true` if the group is expanded\n */\n isExpanded(key: string): boolean {\n return this.expandedKeys.has(key);\n }\n\n // #endregion\n\n // #region Public API\n\n /**\n * Enable pivot mode.\n *\n * Captures the original column set (if not already captured) and activates\n * the pivot transformation. The grid will re-render with pivot columns and rows.\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.enablePivot();\n * ```\n */\n enablePivot(): void {\n if (this.originalColumns.length === 0) {\n this.captureOriginalColumns();\n }\n this.isActive = true;\n this.requestRender();\n }\n\n /**\n * Disable pivot mode and restore the original grid columns.\n *\n * The grid reverts to its normal tabular layout with the original column definitions.\n */\n disablePivot(): void {\n this.isActive = false;\n this.pivotResult = null;\n this.requestRender();\n }\n\n /**\n * Check whether pivot mode is currently active.\n *\n * @returns `true` if the grid is in pivot mode\n */\n isPivotActive(): boolean {\n return this.isActive;\n }\n\n /**\n * Get the current pivot computation result.\n *\n * Returns the full {@link PivotResult} with rows, column keys, totals,\n * and grand total. Returns `null` if pivot is inactive or not yet computed.\n *\n * @returns The computed pivot result, or `null`\n */\n getPivotResult(): PivotResult | null {\n return this.pivotResult;\n }\n\n /**\n * Set the fields used for row grouping (vertical axis).\n *\n * Triggers a re-render with the new pivot structure.\n *\n * @param fields - Array of field names to group rows by\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.setRowGroupFields(['region', 'department']);\n * ```\n */\n setRowGroupFields(fields: string[]): void {\n this.config.rowGroupFields = fields;\n this.requestRender();\n }\n\n /**\n * Set the fields whose unique values become column headers (horizontal axis).\n *\n * Triggers a re-render with the new pivot column structure.\n *\n * @param fields - Array of field names for column grouping\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.setColumnGroupFields(['quarter']);\n * ```\n */\n setColumnGroupFields(fields: string[]): void {\n this.config.columnGroupFields = fields;\n this.requestRender();\n }\n\n /**\n * Set the value fields and their aggregation functions.\n *\n * Each value field defines which data field to aggregate and how.\n * Triggers a re-render with the new aggregation.\n *\n * @param fields - Array of {@link PivotValueField} definitions\n *\n * @example\n * ```ts\n * const pivot = grid.getPluginByName('pivot');\n * pivot.setValueFields([\n * { field: 'revenue', aggFunc: 'sum', header: 'Total Revenue' },\n * { field: 'orders', aggFunc: 'count', header: '# Orders' },\n * ]);\n * ```\n */\n setValueFields(fields: PivotValueField[]): void {\n this.config.valueFields = fields;\n this.requestRender();\n }\n\n /**\n * Force re-computation of the pivot result.\n *\n * Clears the cached pivot result and triggers a full re-render.\n * Call this after changing the underlying row data.\n */\n refresh(): void {\n this.pivotResult = null;\n this.requestRender();\n }\n\n // #endregion\n\n // #region Tool Panel API\n\n /**\n * Show the pivot tool panel.\n * Opens the tool panel and ensures this section is expanded.\n */\n showPanel(): void {\n this.grid.openToolPanel();\n // Ensure our section is expanded\n if (!this.grid.expandedToolPanelSections.includes(PivotPlugin.PANEL_ID)) {\n this.grid.toggleToolPanelSection(PivotPlugin.PANEL_ID);\n }\n }\n\n /**\n * Hide the tool panel.\n */\n hidePanel(): void {\n this.grid.closeToolPanel();\n }\n\n /**\n * Toggle the pivot tool panel section.\n */\n togglePanel(): void {\n // If tool panel is closed, open it first\n if (!this.grid.isToolPanelOpen) {\n this.grid.openToolPanel();\n }\n this.grid.toggleToolPanelSection(PivotPlugin.PANEL_ID);\n }\n\n /**\n * Check if the pivot panel section is currently expanded.\n */\n isPanelVisible(): boolean {\n return this.grid.isToolPanelOpen && this.grid.expandedToolPanelSections.includes(PivotPlugin.PANEL_ID);\n }\n\n // #endregion\n\n // #region Private Helpers\n\n private get gridColumns(): ColumnConfig[] {\n return (this.grid.columns ?? []) as ColumnConfig[];\n }\n\n /**\n * Refresh pivot and update tool panel if active.\n */\n private refreshIfActive(): void {\n if (this.isActive) this.refresh();\n this.refreshPanel();\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 try {\n const columns = this.grid.getAllColumns?.() ?? this.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 this.refreshIfActive();\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 this.refreshIfActive();\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 this.refreshIfActive();\n }\n\n private removeValueField(field: string): void {\n this.config.valueFields = (this.config.valueFields ?? []).filter((v) => v.field !== field);\n this.refreshIfActive();\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","/**\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 rowIndex: number;\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 = 'data-grid-row 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('data-row', String(ctx.rowIndex));\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(\n row: PivotRowData,\n rowEl: HTMLElement,\n columns: ColumnConfig[],\n rowIndex: number,\n): boolean {\n rowEl.className = 'data-grid-row 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('data-row', String(rowIndex));\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"],"names":["getPivotAggregator","getValueAggregator","createValueKey","columnValues","valueField","join","buildPivot","rows","config","rowGroupFields","columnGroupFields","valueFields","columnKeys","columnFields","length","keys","Set","row","key","map","f","String","add","sort","getUniqueColumnKeys","pivotRows","buildHierarchicalPivotRows","totals","sumRows","isGroup","children","colKey","vf","valueKey","field","values","calculateTotals","grandTotal","Object","reduce","a","b","depth","parentKey","result","aggregateValues","total","calculateRowTotal","push","rowKey","rowLabel","rowCount","currentField","remainingFields","slice","hasChildren","grouped","groups","Map","existing","get","set","groupByField","groupValue","groupRows","nums","filter","r","Number","aggregator","aggFunc","aggregatedResult","sum","val","AGG_FUNCS","renderPivotPanel","container","isActive","callbacks","controller","AbortController","ctx","signal","wrapper","document","createElement","className","appendChild","createSection","panel","createCheckbox","checked","onTogglePivot","showTotals","onOptionChange","showGrandTotal","createOptionsPanel","createFieldZone","zone","setAttribute","currentValues","placeholder","textContent","createValueChip","addEventListener","e","preventDefault","classList","remove","dataTransfer","getData","onAddValueField","createValuesZone","allFields","getAvailableFields","usedFields","v","availableFields","has","empty","chip","header","draggable","title","setData","GridClasses","DRAGGING","createAvailableFieldsZone","abort","contentFactory","section","content","zoneType","currentFields","createFieldChip","onAddFieldToZone","fieldInfo","find","label","removeBtn","innerHTML","stopPropagation","onRemoveFieldFromZone","labelWrapper","aggSelect","option","value","toUpperCase","selected","onUpdateValueAggFunc","onRemoveValueField","onChange","input","type","span","PivotPlugin","BaseGridPlugin","static","incompatibleWith","name","reason","styles","defaultConfig","active","showToolPanel","animation","hasInitialized","pivotResult","fieldHeaderMap","expandedKeys","defaultExpanded","userHasToggledExpand","originalColumns","panelContainer","grandTotalFooter","previousVisibleKeys","keysToAnimate","hasValidPivotConfig","this","animationStyle","isAnimationEnabled","detach","clear","cleanupGrandTotalFooter","getToolPanel","userConfig","id","PANEL_ID","icon","tooltip","order","render","renderPanel","processRows","errors","validatePivotConfig","warn","buildFieldHeaderMap","size","expandAllKeys","indentWidth","flatRows","flatten","isExpanded","child","flattenPivotRows","pr","__pivotRowKey","__pivotLabel","__pivotDepth","__pivotIsGroup","__pivotHasChildren","Boolean","__pivotExpanded","__pivotRowCount","__pivotIndent","__pivotTotal","currentVisibleKeys","processColumns","columns","pivotColumns","rowGroupHeaders","width","valueHeader","renderRow","rowEl","rowIndex","pivotRow","forEach","col","colIdx","cell","indent","style","paddingLeft","btn","setIcon","resolveIcon","onToggle","count","renderPivotGroupRow","gridColumns","toggle","iconKey","el","renderPivotLeafRow","cleanupPivotStyling","contains","removeAttribute","onKeyDown","event","focusRow","grid","_focusRow","requestRenderWithFocus","afterRender","renderGrandTotalFooter","body","gridElement","querySelector","animClass","querySelectorAll","dataset","pivotKey","once","gridEl","grandTotalRow","__pivotIsGrandTotal","delete","requestRender","expand","collapse","expandAll","collapseAll","allKeys","collectKeys","getAllGroupKeys","enablePivot","captureOriginalColumns","disablePivot","isPivotActive","getPivotResult","setRowGroupFields","fields","setColumnGroupFields","setValueFields","refresh","showPanel","openToolPanel","expandedToolPanelSections","includes","toggleToolPanelSection","hidePanel","closeToolPanel","togglePanel","isToolPanelOpen","isPanelVisible","refreshIfActive","refreshPanel","getAllColumns","startsWith","enabled","addFieldToZone","removeFieldFromZone","addValueField","removeValueField","updateValueAggFunc","current","removeFromOtherZones","targetZone","some","fieldIndex","findIndex"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"print.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/print/print-isolated.ts","../../../../../libs/grid/src/lib/plugins/print/PrintPlugin.ts"],"sourcesContent":["/**\n * Utility for printing a grid in isolation by hiding all other page content.\n *\n * This approach keeps the grid in place (with virtualization disabled by PrintPlugin)\n * and uses CSS to hide everything else on the page during printing.\n */\n\nimport { PRINT_DUPLICATE_ID, warnDiagnostic } from '../../core/internal/diagnostics';\nimport type { PrintOrientation } from './types';\n\nexport interface PrintIsolatedOptions {\n /** Page orientation hint */\n orientation?: PrintOrientation;\n}\n\n/** ID for the isolation stylesheet */\nconst ISOLATION_STYLE_ID = 'tbw-print-isolation-style';\n\n/**\n * Create a stylesheet that hides everything except the target grid.\n * Uses the grid's ID to target it specifically.\n */\nfunction createIsolationStylesheet(gridId: string, orientation: PrintOrientation): HTMLStyleElement {\n const style = document.createElement('style');\n style.id = ISOLATION_STYLE_ID;\n style.textContent = `\n /* Print isolation: hide everything except the target grid */\n @media print {\n /* Hide all body children by default */\n body > *:not(#${gridId}) {\n display: none !important;\n }\n\n /* But show the grid and ensure it's not hidden by ancestor rules */\n #${gridId} {\n display: block !important;\n position: static !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n width: 100% !important;\n max-height: none !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n }\n\n /* If grid is nested, we need to show its ancestors too */\n #${gridId},\n #${gridId} * {\n visibility: visible !important;\n }\n\n /* Walk up the DOM and show all ancestors of the grid */\n body *:has(> #${gridId}),\n body *:has(#${gridId}) {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n position: static !important;\n transform: none !important;\n background: transparent !important;\n border: none !important;\n padding: 0 !important;\n margin: 0 !important;\n }\n\n /* Hide siblings of ancestors (everything that's not in the path to the grid) */\n body *:has(#${gridId}) > *:not(:has(#${gridId})):not(#${gridId}) {\n display: none !important;\n }\n\n /* Page settings */\n @page {\n size: ${orientation};\n margin: 1cm;\n }\n\n /* Ensure proper print styling */\n body {\n margin: 0 !important;\n padding: 0 !important;\n background: white !important;\n color-scheme: light !important;\n }\n }\n\n /* Screen: also apply isolation for print preview */\n @media screen {\n /* When this stylesheet is active, we're about to print */\n /* No screen-specific rules needed - isolation only applies to print */\n }\n `;\n return style;\n}\n\n/**\n * Print a grid in isolation by hiding all other page content.\n *\n * This function adds a temporary stylesheet that uses CSS to hide everything\n * on the page except the target grid during printing. The grid stays in place\n * with all its data (virtualization should be disabled separately).\n *\n * @param gridElement - The tbw-grid element to print (must have an ID)\n * @param options - Optional configuration\n * @returns Promise that resolves when the print dialog closes\n *\n * @example\n * ```typescript\n * import { queryGrid } from '@toolbox-web/grid';\n * import { printGridIsolated } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = queryGrid('tbw-grid');\n * await printGridIsolated(grid, { orientation: 'landscape' });\n * ```\n */\nexport async function printGridIsolated(gridElement: HTMLElement, options: PrintIsolatedOptions = {}): Promise<void> {\n const { orientation = 'landscape' } = options;\n\n const gridId = gridElement.id;\n\n // Warn if multiple elements share this ID (user-set IDs could collide)\n const elementsWithId = document.querySelectorAll(`#${CSS.escape(gridId)}`);\n if (elementsWithId.length > 1) {\n warnDiagnostic(\n PRINT_DUPLICATE_ID,\n `Multiple elements found with id=\"${gridId}\". ` +\n `Print isolation may not work correctly. Ensure each grid has a unique ID.`,\n gridId,\n 'print',\n );\n }\n\n // Remove any existing isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n\n // Add the isolation stylesheet\n const isolationStyle = createIsolationStylesheet(gridId, orientation);\n document.head.appendChild(isolationStyle);\n\n return new Promise((resolve) => {\n // Listen for afterprint event to cleanup\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n // Remove isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n window.removeEventListener('afterprint', onAfterPrint);\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n }, 5000);\n });\n}\n","/**\n * Print Plugin (Class-based)\n *\n * Provides print layout functionality for tbw-grid.\n * Temporarily disables virtualization to render all rows and uses\n * @media print CSS for print-optimized styling.\n */\n\nimport { PRINT_FAILED, PRINT_IN_PROGRESS, PRINT_NO_GRID, errorDiagnostic } from '../../core/internal/diagnostics';\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { InternalGrid, ToolbarContentDefinition } from '../../core/types';\nimport { printGridIsolated } from './print-isolated';\nimport styles from './print.css?inline';\nimport type { PrintCompleteDetail, PrintConfig, PrintParams, PrintStartDetail } from './types';\n\n/**\n * Extended grid interface for PrintPlugin internal access.\n * Includes registerToolbarContent which is available on the grid class\n * but not exposed in the standard plugin API.\n */\ninterface PrintGridRef extends InternalGrid {\n registerToolbarContent?(content: ToolbarContentDefinition): void;\n unregisterToolbarContent?(contentId: string): void;\n}\n\n/** Default configuration */\nconst DEFAULT_CONFIG: Required<PrintConfig> = {\n button: false,\n orientation: 'landscape',\n warnThreshold: 500,\n maxRows: 0,\n includeTitle: true,\n includeTimestamp: true,\n title: '',\n isolate: false,\n};\n\n/**\n * Print Plugin for tbw-grid\n *\n * Enables printing the full grid content by temporarily disabling virtualization\n * and applying print-optimized styles. Handles large datasets gracefully with\n * configurable row limits.\n *\n * ## Installation\n *\n * ```ts\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `button` | `boolean` | `false` | Show print button in toolbar |\n * | `orientation` | `'portrait' \\| 'landscape'` | `'landscape'` | Page orientation |\n * | `warnThreshold` | `number` | `500` | Show confirmation dialog when rows exceed this (0 = no warning) |\n * | `maxRows` | `number` | `0` | Hard limit on printed rows (0 = unlimited) |\n * | `includeTitle` | `boolean` | `true` | Include grid title in print |\n * | `includeTimestamp` | `boolean` | `true` | Include timestamp in footer |\n * | `title` | `string` | `''` | Custom print title |\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `print` | `(params?) => Promise<void>` | Trigger print dialog |\n * | `isPrinting` | `() => boolean` | Check if print is in progress |\n *\n * ## Events\n *\n * | Event | Detail | Description |\n * |-------|--------|-------------|\n * | `print-start` | `PrintStartDetail` | Fired when print begins |\n * | `print-complete` | `PrintCompleteDetail` | Fired when print completes |\n *\n * @example Basic Print\n * ```ts\n * import { queryGrid } from '@toolbox-web/grid';\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = queryGrid('tbw-grid');\n * grid.gridConfig = {\n * plugins: [new PrintPlugin()],\n * };\n *\n * // Trigger print\n * const printPlugin = grid.getPluginByName('print');\n * await printPlugin.print();\n * ```\n *\n * @example With Toolbar Button\n * ```ts\n * grid.gridConfig = {\n * plugins: [new PrintPlugin({ button: true, orientation: 'landscape' })],\n * };\n * ```\n *\n * @see {@link PrintConfig} for all configuration options\n */\nexport class PrintPlugin extends BaseGridPlugin<PrintConfig> {\n /** @internal */\n readonly name = 'print';\n\n /** @internal */\n override readonly version = '1.0.0';\n\n /** CSS styles for print mode */\n override readonly styles = styles;\n\n /** Current print state */\n #printing = false;\n\n /** Saved column visibility state */\n #savedHiddenColumns: Map<string, boolean> | null = null;\n\n /** Saved virtualization state */\n #savedVirtualization: { bypassThreshold: number } | null = null;\n\n /** Saved rows when maxRows limit is applied */\n #savedRows: unknown[] | null = null;\n\n /** Print header element */\n #printHeader: HTMLElement | null = null;\n\n /** Print footer element */\n #printFooter: HTMLElement | null = null;\n\n /** Applied scale factor (legacy, used for cleanup) */\n #appliedScale: number | null = null;\n\n /**\n * Get the grid typed as PrintGridRef for internal access.\n */\n get #internalGrid(): PrintGridRef {\n return this.grid as unknown as PrintGridRef;\n }\n\n /**\n * Check if print is currently in progress\n */\n isPrinting(): boolean {\n return this.#printing;\n }\n\n /**\n * Trigger the browser print dialog\n *\n * This method:\n * 1. Validates row count against maxRows limit\n * 2. Disables virtualization to render all rows\n * 3. Applies print-specific CSS classes\n * 4. Opens the browser print dialog (or isolated window if `isolate: true`)\n * 5. Restores normal state after printing\n *\n * @param params - Optional parameters to override config for this print\n * @param params.isolate - If true, prints in an isolated window containing only the grid\n * @returns Promise that resolves when print dialog closes\n */\n async print(params?: PrintParams): Promise<void> {\n if (this.#printing) {\n this.warn(PRINT_IN_PROGRESS, 'Print already in progress');\n return;\n }\n\n const grid = this.gridElement;\n if (!grid) {\n this.warn(PRINT_NO_GRID, 'Grid not available');\n return;\n }\n\n const config = { ...DEFAULT_CONFIG, ...this.config, ...params };\n const rows = this.rows;\n const originalRowCount = rows.length;\n let rowCount = originalRowCount;\n let limitApplied = false;\n\n // Check if we should warn about large datasets\n if (config.warnThreshold > 0 && originalRowCount > config.warnThreshold) {\n const limitInfo =\n config.maxRows > 0 ? `\\n\\nNote: Output will be limited to ${config.maxRows.toLocaleString()} rows.` : '';\n const proceed = confirm(\n `This grid has ${originalRowCount.toLocaleString()} rows. ` +\n `Printing large datasets may cause performance issues or browser slowdowns.${limitInfo}\\n\\n` +\n `Click OK to continue, or Cancel to abort.`,\n );\n if (!proceed) {\n return;\n }\n }\n\n // Apply hard row limit if configured\n if (config.maxRows > 0 && originalRowCount > config.maxRows) {\n rowCount = config.maxRows;\n limitApplied = true;\n }\n\n this.#printing = true;\n\n // Track timing for duration reporting\n const startTime = performance.now();\n\n // Emit print-start event\n this.emit<PrintStartDetail>('print-start', {\n rowCount,\n limitApplied,\n originalRowCount,\n });\n\n try {\n // Save current virtualization state\n const internalGrid = this.#internalGrid;\n this.#savedVirtualization = {\n bypassThreshold: internalGrid._virtualization?.bypassThreshold ?? 24,\n };\n\n // Hide columns marked with printHidden\n this.#hidePrintColumns();\n\n // Apply row limit if configured\n if (limitApplied) {\n this.#savedRows = this.sourceRows;\n // Set limited rows on the grid\n this.grid.rows = this.sourceRows.slice(0, rowCount);\n // Wait for grid to process new rows\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Add print header if configured\n if (config.includeTitle || config.includeTimestamp) {\n this.#addPrintHeader(config);\n }\n\n // Disable virtualization to render all rows\n // This forces the grid to render all rows in the DOM\n await this.#disableVirtualization();\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Add orientation class for @page rules\n grid.classList.add(`print-${config.orientation}`);\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Trigger browser print dialog (isolated or inline)\n if (config.isolate) {\n await this.#printInIsolatedWindow(config);\n } else {\n await this.#triggerPrint();\n }\n\n // Emit print-complete event\n this.emit<PrintCompleteDetail>('print-complete', {\n success: true,\n rowCount,\n duration: Math.round(performance.now() - startTime),\n });\n } catch (error) {\n errorDiagnostic(PRINT_FAILED, `Print failed: ${error}`, this.gridElement?.id, this.name);\n this.emit<PrintCompleteDetail>('print-complete', {\n success: false,\n rowCount: 0,\n duration: Math.round(performance.now() - startTime),\n });\n } finally {\n // Restore normal state\n this.#cleanup();\n this.#printing = false;\n }\n }\n\n /**\n * Add print header with title and timestamp\n */\n #addPrintHeader(config: Required<PrintConfig>): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Create print header\n this.#printHeader = document.createElement('div');\n this.#printHeader.className = 'tbw-print-header';\n\n // Title\n if (config.includeTitle) {\n const title = config.title || this.grid.effectiveConfig?.shell?.header?.title || 'Grid Data';\n const titleEl = document.createElement('div');\n titleEl.className = 'tbw-print-header-title';\n titleEl.textContent = title;\n this.#printHeader.appendChild(titleEl);\n }\n\n // Timestamp\n if (config.includeTimestamp) {\n const timestampEl = document.createElement('div');\n timestampEl.className = 'tbw-print-header-timestamp';\n timestampEl.textContent = `Printed: ${new Date().toLocaleString()}`;\n this.#printHeader.appendChild(timestampEl);\n }\n\n // Insert at the beginning of the grid\n grid.insertBefore(this.#printHeader, grid.firstChild);\n\n // Create print footer\n this.#printFooter = document.createElement('div');\n this.#printFooter.className = 'tbw-print-footer';\n this.#printFooter.textContent = `Page generated from ${window.location.hostname}`;\n grid.appendChild(this.#printFooter);\n }\n\n /**\n * Disable virtualization to render all rows\n */\n async #disableVirtualization(): Promise<void> {\n const internalGrid = this.#internalGrid;\n if (!internalGrid._virtualization) return;\n\n // Set bypass threshold higher than total row count to disable virtualization\n // This makes the grid render all rows (up to maxRows) instead of just visible ones\n const totalRows = this.rows.length;\n internalGrid._virtualization.bypassThreshold = totalRows + 100;\n\n // Force a full refresh to re-render with virtualization disabled\n internalGrid.refreshVirtualWindow(true);\n\n // Wait for render to complete\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n /**\n * Trigger the browser print dialog\n */\n async #triggerPrint(): Promise<void> {\n return new Promise((resolve) => {\n // Listen for afterprint event\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n // Guard against test environment teardown where window may be undefined\n if (typeof window !== 'undefined') {\n window.removeEventListener('afterprint', onAfterPrint);\n }\n resolve();\n }, 1000);\n });\n }\n\n /**\n * Print in isolation by hiding all other page content.\n * This excludes navigation, sidebars, etc. while keeping the grid in place.\n */\n async #printInIsolatedWindow(config: Required<PrintConfig>): Promise<void> {\n const grid = this.gridElement;\n if (!grid) return;\n\n await printGridIsolated(grid, {\n orientation: config.orientation,\n });\n }\n\n /**\n * Hide columns marked with printHidden: true\n */\n #hidePrintColumns(): void {\n const columns = this.columns;\n if (!columns) return;\n\n // Save current hidden state and hide print columns\n this.#savedHiddenColumns = new Map();\n\n for (const col of columns) {\n if (col.printHidden && col.field) {\n // Save current visibility state (true = visible, false = hidden)\n this.#savedHiddenColumns.set(col.field, !col.hidden);\n // Hide the column for printing\n this.grid.setColumnVisible(col.field, false);\n }\n }\n }\n\n /**\n * Restore columns that were hidden for printing\n */\n #restorePrintColumns(): void {\n if (!this.#savedHiddenColumns) return;\n\n for (const [field, wasVisible] of this.#savedHiddenColumns) {\n // Restore original visibility\n this.grid.setColumnVisible(field, wasVisible);\n }\n\n this.#savedHiddenColumns = null;\n }\n\n /**\n * Cleanup after printing\n */\n #cleanup(): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Restore columns that were hidden for printing\n this.#restorePrintColumns();\n\n // Remove orientation classes (both original and possibly switched)\n grid.classList.remove('print-portrait', 'print-landscape');\n\n // Remove scaling transform if applied (legacy)\n if (this.#appliedScale !== null) {\n grid.style.transform = '';\n grid.style.transformOrigin = '';\n grid.style.width = '';\n this.#appliedScale = null;\n }\n\n // Remove print header/footer\n if (this.#printHeader) {\n this.#printHeader.remove();\n this.#printHeader = null;\n }\n if (this.#printFooter) {\n this.#printFooter.remove();\n this.#printFooter = null;\n }\n\n // Restore virtualization\n const internalGrid = this.#internalGrid;\n if (this.#savedVirtualization && internalGrid._virtualization) {\n internalGrid._virtualization.bypassThreshold = this.#savedVirtualization.bypassThreshold;\n internalGrid.refreshVirtualWindow(true);\n this.#savedVirtualization = null;\n }\n\n // Restore original rows if they were limited\n if (this.#savedRows !== null) {\n this.grid.rows = this.#savedRows;\n this.#savedRows = null;\n }\n }\n\n /**\n * Register toolbar button if configured\n * @internal\n */\n override afterRender(): void {\n // Register toolbar on first render when button is enabled\n if (this.config?.button && !this.#toolbarRegistered) {\n this.#registerToolbarButton();\n this.#toolbarRegistered = true;\n }\n }\n\n /** Track if toolbar button is registered */\n #toolbarRegistered = false;\n\n /**\n * Register print button in toolbar\n */\n #registerToolbarButton(): void {\n const grid = this.#internalGrid;\n\n // Register toolbar content\n grid.registerToolbarContent?.({\n id: 'print-button',\n order: 900, // High order to appear at the end\n render: (container: HTMLElement) => {\n const button = document.createElement('button');\n button.className = 'tbw-toolbar-btn tbw-print-btn';\n button.title = 'Print grid';\n button.type = 'button';\n\n // Use print icon\n const icon = this.resolveIcon('print') || '🖨️';\n this.setIcon(button, icon);\n\n button.addEventListener(\n 'click',\n () => {\n this.print();\n },\n { signal: this.disconnectSignal },\n );\n\n container.appendChild(button);\n },\n });\n }\n}\n"],"names":["ISOLATION_STYLE_ID","async","printGridIsolated","gridElement","options","orientation","gridId","id","document","querySelectorAll","CSS","escape","length","warnDiagnostic","PRINT_DUPLICATE_ID","getElementById","remove","isolationStyle","style","createElement","textContent","createIsolationStylesheet","head","appendChild","Promise","resolve","onAfterPrint","window","removeEventListener","addEventListener","print","setTimeout","DEFAULT_CONFIG","button","warnThreshold","maxRows","includeTitle","includeTimestamp","title","isolate","PrintPlugin","BaseGridPlugin","name","version","styles","printing","savedHiddenColumns","savedVirtualization","savedRows","printHeader","printFooter","appliedScale","internalGrid","this","grid","isPrinting","params","warn","PRINT_IN_PROGRESS","PRINT_NO_GRID","config","originalRowCount","rows","rowCount","limitApplied","limitInfo","toLocaleString","confirm","startTime","performance","now","emit","bypassThreshold","_virtualization","hidePrintColumns","sourceRows","slice","addPrintHeader","disableVirtualization","requestAnimationFrame","classList","add","printInIsolatedWindow","triggerPrint","success","duration","Math","round","error","errorDiagnostic","PRINT_FAILED","cleanup","className","effectiveConfig","shell","header","titleEl","timestampEl","Date","insertBefore","firstChild","location","hostname","totalRows","refreshVirtualWindow","columns","Map","col","printHidden","field","set","hidden","setColumnVisible","restorePrintColumns","wasVisible","transform","transformOrigin","width","afterRender","toolbarRegistered","registerToolbarButton","registerToolbarContent","order","render","container","type","icon","resolveIcon","setIcon","signal","disconnectSignal"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"print.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/print/print-isolated.ts","../../../../../libs/grid/src/lib/plugins/print/PrintPlugin.ts"],"sourcesContent":["/**\n * Utility for printing a grid in isolation by hiding all other page content.\n *\n * This approach keeps the grid in place (with virtualization disabled by PrintPlugin)\n * and uses CSS to hide everything else on the page during printing.\n */\n\nimport { PRINT_DUPLICATE_ID, warnDiagnostic } from '../../core/internal/diagnostics';\nimport type { PrintOrientation } from './types';\n\nexport interface PrintIsolatedOptions {\n /** Page orientation hint */\n orientation?: PrintOrientation;\n}\n\n/** ID for the isolation stylesheet */\nconst ISOLATION_STYLE_ID = 'tbw-print-isolation-style';\n\n/**\n * Create a stylesheet that hides everything except the target grid.\n * Uses the grid's ID to target it specifically.\n */\nfunction createIsolationStylesheet(gridId: string, orientation: PrintOrientation): HTMLStyleElement {\n const style = document.createElement('style');\n style.id = ISOLATION_STYLE_ID;\n style.textContent = `\n /* Print isolation: hide everything except the target grid */\n @media print {\n /* Hide all body children by default */\n body > *:not(#${gridId}) {\n display: none !important;\n }\n\n /* But show the grid and ensure it's not hidden by ancestor rules */\n #${gridId} {\n display: block !important;\n position: static !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n width: 100% !important;\n max-height: none !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n }\n\n /* If grid is nested, we need to show its ancestors too */\n #${gridId},\n #${gridId} * {\n visibility: visible !important;\n }\n\n /* Walk up the DOM and show all ancestors of the grid */\n body *:has(> #${gridId}),\n body *:has(#${gridId}) {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n position: static !important;\n transform: none !important;\n background: transparent !important;\n border: none !important;\n padding: 0 !important;\n margin: 0 !important;\n }\n\n /* Hide siblings of ancestors (everything that's not in the path to the grid) */\n body *:has(#${gridId}) > *:not(:has(#${gridId})):not(#${gridId}) {\n display: none !important;\n }\n\n /* Page settings */\n @page {\n size: ${orientation};\n margin: 1cm;\n }\n\n /* Ensure proper print styling */\n body {\n margin: 0 !important;\n padding: 0 !important;\n background: white !important;\n color-scheme: light !important;\n }\n }\n\n /* Screen: also apply isolation for print preview */\n @media screen {\n /* When this stylesheet is active, we're about to print */\n /* No screen-specific rules needed - isolation only applies to print */\n }\n `;\n return style;\n}\n\n/**\n * Print a grid in isolation by hiding all other page content.\n *\n * This function adds a temporary stylesheet that uses CSS to hide everything\n * on the page except the target grid during printing. The grid stays in place\n * with all its data (virtualization should be disabled separately).\n *\n * @param gridElement - The tbw-grid element to print (must have an ID)\n * @param options - Optional configuration\n * @returns Promise that resolves when the print dialog closes\n *\n * @example\n * ```typescript\n * import { queryGrid } from '@toolbox-web/grid';\n * import { printGridIsolated } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = queryGrid('tbw-grid');\n * await printGridIsolated(grid, { orientation: 'landscape' });\n * ```\n */\nexport async function printGridIsolated(gridElement: HTMLElement, options: PrintIsolatedOptions = {}): Promise<void> {\n const { orientation = 'landscape' } = options;\n\n const gridId = gridElement.id;\n\n // Warn if multiple elements share this ID (user-set IDs could collide)\n const elementsWithId = document.querySelectorAll(`#${CSS.escape(gridId)}`);\n if (elementsWithId.length > 1) {\n warnDiagnostic(\n PRINT_DUPLICATE_ID,\n `Multiple elements found with id=\"${gridId}\". ` +\n `Print isolation may not work correctly. Ensure each grid has a unique ID.`,\n gridId,\n 'print',\n );\n }\n\n // Remove any existing isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n\n // Add the isolation stylesheet\n const isolationStyle = createIsolationStylesheet(gridId, orientation);\n document.head.appendChild(isolationStyle);\n\n return new Promise((resolve) => {\n // Listen for afterprint event to cleanup\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n // Remove isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n window.removeEventListener('afterprint', onAfterPrint);\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n }, 5000);\n });\n}\n","/**\n * Print Plugin (Class-based)\n *\n * Provides print layout functionality for tbw-grid.\n * Temporarily disables virtualization to render all rows and uses\n * @media print CSS for print-optimized styling.\n */\n\nimport { PRINT_FAILED, PRINT_IN_PROGRESS, PRINT_NO_GRID, errorDiagnostic } from '../../core/internal/diagnostics';\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { InternalGrid, ToolbarContentDefinition } from '../../core/types';\nimport { printGridIsolated } from './print-isolated';\nimport styles from './print.css?inline';\nimport type { PrintCompleteDetail, PrintConfig, PrintParams, PrintStartDetail } from './types';\n\n/**\n * Extended grid interface for PrintPlugin internal access.\n * Includes registerToolbarContent which is available on the grid class\n * but not exposed in the standard plugin API.\n */\ninterface PrintGridRef extends InternalGrid {\n registerToolbarContent?(content: ToolbarContentDefinition): void;\n unregisterToolbarContent?(contentId: string): void;\n}\n\n/** Default configuration */\nconst DEFAULT_CONFIG: Required<PrintConfig> = {\n button: false,\n orientation: 'landscape',\n warnThreshold: 500,\n maxRows: 0,\n includeTitle: true,\n includeTimestamp: true,\n title: '',\n isolate: false,\n};\n\n/**\n * Print Plugin for tbw-grid\n *\n * Enables printing the full grid content by temporarily disabling virtualization\n * and applying print-optimized styles. Handles large datasets gracefully with\n * configurable row limits.\n *\n * ## Installation\n *\n * ```ts\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n * ```\n *\n * ## Events\n *\n * | Event | Detail | Description |\n * |-------|--------|-------------|\n * | `print-start` | {@link PrintStartDetail} | Fired when print begins |\n * | `print-complete` | {@link PrintCompleteDetail} | Fired when print completes |\n *\n * @example Basic Print\n * ```ts\n * import { queryGrid } from '@toolbox-web/grid';\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = queryGrid('tbw-grid');\n * grid.gridConfig = {\n * plugins: [new PrintPlugin()],\n * };\n *\n * // Trigger print\n * const printPlugin = grid.getPluginByName('print');\n * await printPlugin.print();\n * ```\n *\n * @example With Toolbar Button\n * ```ts\n * grid.gridConfig = {\n * plugins: [new PrintPlugin({ button: true, orientation: 'landscape' })],\n * };\n * ```\n *\n * @see {@link PrintConfig} for all configuration options\n */\nexport class PrintPlugin extends BaseGridPlugin<PrintConfig> {\n /** @internal */\n readonly name = 'print';\n\n /** @internal */\n override readonly version = '1.0.0';\n\n /** CSS styles for print mode */\n override readonly styles = styles;\n\n /** Current print state */\n #printing = false;\n\n /** Saved column visibility state */\n #savedHiddenColumns: Map<string, boolean> | null = null;\n\n /** Saved virtualization state */\n #savedVirtualization: { bypassThreshold: number } | null = null;\n\n /** Saved rows when maxRows limit is applied */\n #savedRows: unknown[] | null = null;\n\n /** Print header element */\n #printHeader: HTMLElement | null = null;\n\n /** Print footer element */\n #printFooter: HTMLElement | null = null;\n\n /** Applied scale factor (legacy, used for cleanup) */\n #appliedScale: number | null = null;\n\n /**\n * Get the grid typed as PrintGridRef for internal access.\n */\n get #internalGrid(): PrintGridRef {\n return this.grid as unknown as PrintGridRef;\n }\n\n /**\n * Check if print is currently in progress\n */\n isPrinting(): boolean {\n return this.#printing;\n }\n\n /**\n * Trigger the browser print dialog\n *\n * This method:\n * 1. Validates row count against maxRows limit\n * 2. Disables virtualization to render all rows\n * 3. Applies print-specific CSS classes\n * 4. Opens the browser print dialog (or isolated window if `isolate: true`)\n * 5. Restores normal state after printing\n *\n * @param params - Optional parameters to override config for this print\n * @param params.isolate - If true, prints in an isolated window containing only the grid\n * @returns Promise that resolves when print dialog closes\n */\n async print(params?: PrintParams): Promise<void> {\n if (this.#printing) {\n this.warn(PRINT_IN_PROGRESS, 'Print already in progress');\n return;\n }\n\n const grid = this.gridElement;\n if (!grid) {\n this.warn(PRINT_NO_GRID, 'Grid not available');\n return;\n }\n\n const config = { ...DEFAULT_CONFIG, ...this.config, ...params };\n const rows = this.rows;\n const originalRowCount = rows.length;\n let rowCount = originalRowCount;\n let limitApplied = false;\n\n // Check if we should warn about large datasets\n if (config.warnThreshold > 0 && originalRowCount > config.warnThreshold) {\n const limitInfo =\n config.maxRows > 0 ? `\\n\\nNote: Output will be limited to ${config.maxRows.toLocaleString()} rows.` : '';\n const proceed = confirm(\n `This grid has ${originalRowCount.toLocaleString()} rows. ` +\n `Printing large datasets may cause performance issues or browser slowdowns.${limitInfo}\\n\\n` +\n `Click OK to continue, or Cancel to abort.`,\n );\n if (!proceed) {\n return;\n }\n }\n\n // Apply hard row limit if configured\n if (config.maxRows > 0 && originalRowCount > config.maxRows) {\n rowCount = config.maxRows;\n limitApplied = true;\n }\n\n this.#printing = true;\n\n // Track timing for duration reporting\n const startTime = performance.now();\n\n // Emit print-start event\n this.emit<PrintStartDetail>('print-start', {\n rowCount,\n limitApplied,\n originalRowCount,\n });\n\n try {\n // Save current virtualization state\n const internalGrid = this.#internalGrid;\n this.#savedVirtualization = {\n bypassThreshold: internalGrid._virtualization?.bypassThreshold ?? 24,\n };\n\n // Hide columns marked with printHidden\n this.#hidePrintColumns();\n\n // Apply row limit if configured\n if (limitApplied) {\n this.#savedRows = this.sourceRows;\n // Set limited rows on the grid\n this.grid.rows = this.sourceRows.slice(0, rowCount);\n // Wait for grid to process new rows\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Add print header if configured\n if (config.includeTitle || config.includeTimestamp) {\n this.#addPrintHeader(config);\n }\n\n // Disable virtualization to render all rows\n // This forces the grid to render all rows in the DOM\n await this.#disableVirtualization();\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Add orientation class for @page rules\n grid.classList.add(`print-${config.orientation}`);\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Trigger browser print dialog (isolated or inline)\n if (config.isolate) {\n await this.#printInIsolatedWindow(config);\n } else {\n await this.#triggerPrint();\n }\n\n // Emit print-complete event\n this.emit<PrintCompleteDetail>('print-complete', {\n success: true,\n rowCount,\n duration: Math.round(performance.now() - startTime),\n });\n } catch (error) {\n errorDiagnostic(PRINT_FAILED, `Print failed: ${error}`, this.gridElement?.id, this.name);\n this.emit<PrintCompleteDetail>('print-complete', {\n success: false,\n rowCount: 0,\n duration: Math.round(performance.now() - startTime),\n });\n } finally {\n // Restore normal state\n this.#cleanup();\n this.#printing = false;\n }\n }\n\n /**\n * Add print header with title and timestamp\n */\n #addPrintHeader(config: Required<PrintConfig>): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Create print header\n this.#printHeader = document.createElement('div');\n this.#printHeader.className = 'tbw-print-header';\n\n // Title\n if (config.includeTitle) {\n const title = config.title || this.grid.effectiveConfig?.shell?.header?.title || 'Grid Data';\n const titleEl = document.createElement('div');\n titleEl.className = 'tbw-print-header-title';\n titleEl.textContent = title;\n this.#printHeader.appendChild(titleEl);\n }\n\n // Timestamp\n if (config.includeTimestamp) {\n const timestampEl = document.createElement('div');\n timestampEl.className = 'tbw-print-header-timestamp';\n timestampEl.textContent = `Printed: ${new Date().toLocaleString()}`;\n this.#printHeader.appendChild(timestampEl);\n }\n\n // Insert at the beginning of the grid\n grid.insertBefore(this.#printHeader, grid.firstChild);\n\n // Create print footer\n this.#printFooter = document.createElement('div');\n this.#printFooter.className = 'tbw-print-footer';\n this.#printFooter.textContent = `Page generated from ${window.location.hostname}`;\n grid.appendChild(this.#printFooter);\n }\n\n /**\n * Disable virtualization to render all rows\n */\n async #disableVirtualization(): Promise<void> {\n const internalGrid = this.#internalGrid;\n if (!internalGrid._virtualization) return;\n\n // Set bypass threshold higher than total row count to disable virtualization\n // This makes the grid render all rows (up to maxRows) instead of just visible ones\n const totalRows = this.rows.length;\n internalGrid._virtualization.bypassThreshold = totalRows + 100;\n\n // Force a full refresh to re-render with virtualization disabled\n internalGrid.refreshVirtualWindow(true);\n\n // Wait for render to complete\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n /**\n * Trigger the browser print dialog\n */\n async #triggerPrint(): Promise<void> {\n return new Promise((resolve) => {\n // Listen for afterprint event\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n // Guard against test environment teardown where window may be undefined\n if (typeof window !== 'undefined') {\n window.removeEventListener('afterprint', onAfterPrint);\n }\n resolve();\n }, 1000);\n });\n }\n\n /**\n * Print in isolation by hiding all other page content.\n * This excludes navigation, sidebars, etc. while keeping the grid in place.\n */\n async #printInIsolatedWindow(config: Required<PrintConfig>): Promise<void> {\n const grid = this.gridElement;\n if (!grid) return;\n\n await printGridIsolated(grid, {\n orientation: config.orientation,\n });\n }\n\n /**\n * Hide columns marked with printHidden: true\n */\n #hidePrintColumns(): void {\n const columns = this.columns;\n if (!columns) return;\n\n // Save current hidden state and hide print columns\n this.#savedHiddenColumns = new Map();\n\n for (const col of columns) {\n if (col.printHidden && col.field) {\n // Save current visibility state (true = visible, false = hidden)\n this.#savedHiddenColumns.set(col.field, !col.hidden);\n // Hide the column for printing\n this.grid.setColumnVisible(col.field, false);\n }\n }\n }\n\n /**\n * Restore columns that were hidden for printing\n */\n #restorePrintColumns(): void {\n if (!this.#savedHiddenColumns) return;\n\n for (const [field, wasVisible] of this.#savedHiddenColumns) {\n // Restore original visibility\n this.grid.setColumnVisible(field, wasVisible);\n }\n\n this.#savedHiddenColumns = null;\n }\n\n /**\n * Cleanup after printing\n */\n #cleanup(): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Restore columns that were hidden for printing\n this.#restorePrintColumns();\n\n // Remove orientation classes (both original and possibly switched)\n grid.classList.remove('print-portrait', 'print-landscape');\n\n // Remove scaling transform if applied (legacy)\n if (this.#appliedScale !== null) {\n grid.style.transform = '';\n grid.style.transformOrigin = '';\n grid.style.width = '';\n this.#appliedScale = null;\n }\n\n // Remove print header/footer\n if (this.#printHeader) {\n this.#printHeader.remove();\n this.#printHeader = null;\n }\n if (this.#printFooter) {\n this.#printFooter.remove();\n this.#printFooter = null;\n }\n\n // Restore virtualization\n const internalGrid = this.#internalGrid;\n if (this.#savedVirtualization && internalGrid._virtualization) {\n internalGrid._virtualization.bypassThreshold = this.#savedVirtualization.bypassThreshold;\n internalGrid.refreshVirtualWindow(true);\n this.#savedVirtualization = null;\n }\n\n // Restore original rows if they were limited\n if (this.#savedRows !== null) {\n this.grid.rows = this.#savedRows;\n this.#savedRows = null;\n }\n }\n\n /**\n * Register toolbar button if configured\n * @internal\n */\n override afterRender(): void {\n // Register toolbar on first render when button is enabled\n if (this.config?.button && !this.#toolbarRegistered) {\n this.#registerToolbarButton();\n this.#toolbarRegistered = true;\n }\n }\n\n /** Track if toolbar button is registered */\n #toolbarRegistered = false;\n\n /**\n * Register print button in toolbar\n */\n #registerToolbarButton(): void {\n const grid = this.#internalGrid;\n\n // Register toolbar content\n grid.registerToolbarContent?.({\n id: 'print-button',\n order: 900, // High order to appear at the end\n render: (container: HTMLElement) => {\n const button = document.createElement('button');\n button.className = 'tbw-toolbar-btn tbw-print-btn';\n button.title = 'Print grid';\n button.type = 'button';\n\n // Use print icon\n const icon = this.resolveIcon('print') || '🖨️';\n this.setIcon(button, icon);\n\n button.addEventListener(\n 'click',\n () => {\n this.print();\n },\n { signal: this.disconnectSignal },\n );\n\n container.appendChild(button);\n },\n });\n }\n}\n"],"names":["ISOLATION_STYLE_ID","async","printGridIsolated","gridElement","options","orientation","gridId","id","document","querySelectorAll","CSS","escape","length","warnDiagnostic","PRINT_DUPLICATE_ID","getElementById","remove","isolationStyle","style","createElement","textContent","createIsolationStylesheet","head","appendChild","Promise","resolve","onAfterPrint","window","removeEventListener","addEventListener","print","setTimeout","DEFAULT_CONFIG","button","warnThreshold","maxRows","includeTitle","includeTimestamp","title","isolate","PrintPlugin","BaseGridPlugin","name","version","styles","printing","savedHiddenColumns","savedVirtualization","savedRows","printHeader","printFooter","appliedScale","internalGrid","this","grid","isPrinting","params","warn","PRINT_IN_PROGRESS","PRINT_NO_GRID","config","originalRowCount","rows","rowCount","limitApplied","limitInfo","toLocaleString","confirm","startTime","performance","now","emit","bypassThreshold","_virtualization","hidePrintColumns","sourceRows","slice","addPrintHeader","disableVirtualization","requestAnimationFrame","classList","add","printInIsolatedWindow","triggerPrint","success","duration","Math","round","error","errorDiagnostic","PRINT_FAILED","cleanup","className","effectiveConfig","shell","header","titleEl","timestampEl","Date","insertBefore","firstChild","location","hostname","totalRows","refreshVirtualWindow","columns","Map","col","printHidden","field","set","hidden","setColumnVisible","restorePrintColumns","wasVisible","transform","transformOrigin","width","afterRender","toolbarRegistered","registerToolbarButton","registerToolbarContent","order","render","container","type","icon","resolveIcon","setIcon","signal","disconnectSignal"],"mappings":"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"}
|