@toolbox-web/grid 1.13.0 → 1.14.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 +1483 -1403
- package/all.js.map +1 -1
- package/index.js +11 -4
- package/index.js.map +1 -1
- package/lib/core/internal/row-animation.d.ts.map +1 -1
- package/lib/core/plugin/types.d.ts +1 -1
- package/lib/core/plugin/types.d.ts.map +1 -1
- package/lib/core/types.d.ts +44 -1
- package/lib/core/types.d.ts.map +1 -1
- package/lib/plugins/clipboard/ClipboardPlugin.d.ts +69 -8
- package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
- package/lib/plugins/clipboard/index.d.ts +1 -1
- package/lib/plugins/clipboard/index.d.ts.map +1 -1
- package/lib/plugins/clipboard/index.js +257 -192
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/clipboard/types.d.ts +31 -0
- package/lib/plugins/clipboard/types.d.ts.map +1 -1
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +8 -0
- package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
- package/lib/plugins/context-menu/index.js +75 -60
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/context-menu/types.d.ts +7 -0
- package/lib/plugins/context-menu/types.d.ts.map +1 -1
- package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -1
- package/lib/plugins/editing/editors.d.ts +2 -2
- package/lib/plugins/editing/editors.d.ts.map +1 -1
- package/lib/plugins/editing/index.js +420 -381
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/editing/types.d.ts +6 -23
- package/lib/plugins/editing/types.d.ts.map +1 -1
- package/lib/plugins/export/ExportPlugin.d.ts.map +1 -1
- package/lib/plugins/export/index.js +75 -66
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/index.d.ts +1 -1
- package/lib/plugins/filtering/index.d.ts.map +1 -1
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts +1 -0
- package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -1
- package/lib/plugins/pinned-rows/index.js +118 -87
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pinned-rows/pinned-rows.d.ts +2 -1
- package/lib/plugins/pinned-rows/pinned-rows.d.ts.map +1 -1
- package/lib/plugins/pinned-rows/types.d.ts +23 -2
- package/lib/plugins/pinned-rows/types.d.ts.map +1 -1
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/reorder/index.js.map +1 -1
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/row-reorder/index.js.map +1 -1
- package/lib/plugins/selection/SelectionPlugin.d.ts.map +1 -1
- package/lib/plugins/selection/index.js +147 -139
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/shared/data-collection.d.ts +33 -0
- package/lib/plugins/shared/data-collection.d.ts.map +1 -0
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/umd/grid.all.umd.js +23 -23
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +2 -2
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/clipboard.umd.js +5 -5
- package/umd/plugins/clipboard.umd.js.map +1 -1
- package/umd/plugins/context-menu.umd.js +1 -1
- package/umd/plugins/context-menu.umd.js.map +1 -1
- package/umd/plugins/editing.umd.js +1 -1
- package/umd/plugins/editing.umd.js.map +1 -1
- package/umd/plugins/export.umd.js +7 -7
- package/umd/plugins/export.umd.js.map +1 -1
- package/umd/plugins/pinned-rows.umd.js +1 -1
- package/umd/plugins/pinned-rows.umd.js.map +1 -1
- package/umd/plugins/selection.umd.js +2 -2
- package/umd/plugins/selection.umd.js.map +1 -1
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
(function(
|
|
2
|
-
`)||e.includes("\r"))?`"${e.replace(/"/g,'""')}"`:e}function
|
|
3
|
-
`,s=[],l=
|
|
1
|
+
(function(d,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/plugin/base-plugin"],p):(d=typeof globalThis<"u"?globalThis:d||self,p(d.TbwGridPlugin_export={},d.TbwGrid))})(this,(function(d,p){"use strict";function S(o,n,e=!0){let t=o;if(e&&(t=t.filter(i=>!i.hidden&&!i.field.startsWith("__")&&i.meta?.utility!==!0)),n?.length){const i=new Set(n);t=t.filter(c=>i.has(c.field))}return t}function m(o,n){return n?.length?[...n].sort((e,t)=>e-t).map(e=>o[e]).filter(e=>e!=null):o}function b(o,n=!0){if(o==null)return"";if(o instanceof Date)return o.toISOString();if(typeof o=="object")return JSON.stringify(o);const e=String(o);return n&&(e.includes(",")||e.includes('"')||e.includes(`
|
|
2
|
+
`)||e.includes("\r"))?`"${e.replace(/"/g,'""')}"`:e}function C(o,n,e,t={}){const i=t.delimiter??",",c=t.newline??`
|
|
3
|
+
`,s=[],l=t.bom?"\uFEFF":"";if(e.includeHeaders!==!1){const r=n.map(u=>{const a=u.header||u.field,f=e.processHeader?e.processHeader(a,u.field):a;return b(f)});s.push(r.join(i))}for(const r of o){const u=n.map(a=>{let f=r[a.field];return e.processCell&&(f=e.processCell(f,a.field,r)),b(f)});s.push(u.join(i))}return l+s.join(c)}function h(o,n){const e=URL.createObjectURL(o),t=document.createElement("a");t.href=e,t.download=n,t.style.display="none",document.body.appendChild(t),t.click(),document.body.removeChild(t),URL.revokeObjectURL(e)}function E(o,n){const e=new Blob([o],{type:"text/csv;charset=utf-8;"});h(e,n)}function w(o){return o.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function j(o,n,e){let t=`<?xml version="1.0" encoding="UTF-8"?>
|
|
4
4
|
<?mso-application progid="Excel.Sheet"?>
|
|
5
5
|
<Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
|
|
6
6
|
xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
|
|
7
7
|
<Worksheet ss:Name="Sheet1">
|
|
8
|
-
<Table>`;if(e.includeHeaders!==!1){
|
|
9
|
-
<Row>`;for(const
|
|
10
|
-
<Row>`;for(const
|
|
8
|
+
<Table>`;if(e.includeHeaders!==!1){t+=`
|
|
9
|
+
<Row>`;for(const i of n){const c=i.header||i.field,s=e.processHeader?e.processHeader(c,i.field):c;t+=`<Cell><Data ss:Type="String">${w(s)}</Data></Cell>`}t+="</Row>"}for(const i of o){t+=`
|
|
10
|
+
<Row>`;for(const c of n){let s=i[c.field];e.processCell&&(s=e.processCell(s,c.field,i));let l="String",r="";s==null?r="":typeof s=="number"&&!isNaN(s)?(l="Number",r=String(s)):s instanceof Date?(l="DateTime",r=s.toISOString()):r=w(String(s)),t+=`<Cell><Data ss:Type="${l}">${r}</Data></Cell>`}t+="</Row>"}return t+=`
|
|
11
11
|
</Table>
|
|
12
12
|
</Worksheet>
|
|
13
|
-
</Workbook>`,
|
|
13
|
+
</Workbook>`,t}function v(o,n){const e=n.endsWith(".xls")?n:`${n}.xls`,t=new Blob([o],{type:"application/vnd.ms-excel;charset=utf-8;"});h(t,e)}class k extends p.BaseGridPlugin{name="export";get defaultConfig(){return{fileName:"export",includeHeaders:!0,onlyVisible:!0,onlySelected:!1}}isExportingFlag=!1;lastExportInfo=null;performExport(n,e){const t=this.config,i={format:n,fileName:e?.fileName??t.fileName??"export",includeHeaders:e?.includeHeaders??t.includeHeaders,processCell:e?.processCell,processHeader:e?.processHeader,columns:e?.columns,rowIndices:e?.rowIndices},c=S(this.columns,e?.columns,t.onlyVisible);let s;if(e?.rowIndices)s=m(this.rows,e.rowIndices);else if(t.onlySelected){const r=this.getSelectionState();r?.selected?.size?s=m(this.rows,[...r.selected]):s=[...this.rows]}else s=[...this.rows];this.isExportingFlag=!0;let l=i.fileName;try{switch(n){case"csv":{const r=C(s,c,i,{bom:!0});l=l.endsWith(".csv")?l:`${l}.csv`,E(r,l);break}case"excel":{const r=j(s,c,i);l=l.endsWith(".xls")?l:`${l}.xls`,v(r,l);break}case"json":{const r=s.map(f=>{const y={};for(const g of c){let x=f[g.field];i.processCell&&(x=i.processCell(x,g.field,f)),y[g.field]=x}return y}),u=JSON.stringify(r,null,2);l=l.endsWith(".json")?l:`${l}.json`;const a=new Blob([u],{type:"application/json"});h(a,l);break}}this.lastExportInfo={format:n,timestamp:new Date},this.emit("export-complete",{format:n,fileName:l,rowCount:s.length,columnCount:c.length})}finally{this.isExportingFlag=!1}}getSelectionState(){try{return this.grid?.getPluginState?.("selection")??null}catch{return null}}exportCsv(n){this.performExport("csv",n)}exportExcel(n){this.performExport("excel",n)}exportJson(n){this.performExport("json",n)}isExporting(){return this.isExportingFlag}getLastExport(){return this.lastExportInfo}}d.ExportPlugin=k,Object.defineProperty(d,Symbol.toStringTag,{value:"Module"})}));
|
|
14
14
|
//# sourceMappingURL=export.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"export.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/export/csv.ts","../../../../../libs/grid/src/lib/plugins/export/excel.ts","../../../../../libs/grid/src/lib/plugins/export/ExportPlugin.ts"],"sourcesContent":["/**\n * CSV Export Utilities\n *\n * Functions for building and downloading CSV content.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\n\n/** CSV export options */\nexport interface CsvOptions {\n /** Field delimiter (default: ',') */\n delimiter?: string;\n /** Line separator (default: '\\n') */\n newline?: string;\n /** Whether to quote strings containing special characters (default: true) */\n quoteStrings?: boolean;\n /** Add UTF-8 BOM for Excel compatibility (default: false) */\n bom?: boolean;\n}\n\n/**\n * Format a value for CSV output.\n * Handles null, Date, objects, and strings with special characters.\n */\nexport function formatCsvValue(value: any, quote = true): string {\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n\n // Quote if contains special characters (comma, quote, newline)\n if (quote && (str.includes(',') || str.includes('\"') || str.includes('\\n') || str.includes('\\r'))) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build CSV content from rows and columns.\n */\nexport function buildCsv(rows: any[], columns: ColumnConfig[], params: ExportParams, options: CsvOptions = {}): string {\n const delimiter = options.delimiter ?? ',';\n const newline = options.newline ?? '\\n';\n const lines: string[] = [];\n\n // UTF-8 BOM for Excel compatibility\n const bom = options.bom ? '\\uFEFF' : '';\n\n // Build header row\n if (params.includeHeaders !== false) {\n const headerRow = columns.map((col) => {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n return formatCsvValue(processed);\n });\n lines.push(headerRow.join(delimiter));\n }\n\n // Build data rows\n for (const row of rows) {\n const cells = columns.map((col) => {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n return formatCsvValue(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return bom + lines.join(newline);\n}\n\n/**\n * Download a Blob as a file.\n */\nexport function downloadBlob(blob: Blob, fileName: string): void {\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n link.style.display = 'none';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Download CSV content as a file.\n */\nexport function downloadCsv(content: string, fileName: string): void {\n const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });\n downloadBlob(blob, fileName);\n}\n","/**\n * Excel Export Utilities\n *\n * Simple Excel XML format export (no external dependencies).\n * Produces XML Spreadsheet 2003 format which opens in Excel.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\nimport { downloadBlob } from './csv';\n\n/**\n * Escape XML special characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Build Excel XML content from rows and columns.\n * Uses XML Spreadsheet 2003 format for broad compatibility.\n */\nexport function buildExcelXml(rows: any[], columns: ColumnConfig[], params: ExportParams): string {\n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?mso-application progid=\"Excel.Sheet\"?>\n<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"\n xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\">\n<Worksheet ss:Name=\"Sheet1\">\n<Table>`;\n\n // Build header row\n if (params.includeHeaders !== false) {\n xml += '\\n<Row>';\n for (const col of columns) {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n xml += `<Cell><Data ss:Type=\"String\">${escapeXml(processed)}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n // Build data rows\n for (const row of rows) {\n xml += '\\n<Row>';\n for (const col of columns) {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n\n // Determine cell type based on value\n let type: 'Number' | 'String' | 'DateTime' = 'String';\n let displayValue = '';\n\n if (value == null) {\n displayValue = '';\n } else if (typeof value === 'number' && !isNaN(value)) {\n type = 'Number';\n displayValue = String(value);\n } else if (value instanceof Date) {\n type = 'DateTime';\n displayValue = value.toISOString();\n } else {\n displayValue = escapeXml(String(value));\n }\n\n xml += `<Cell><Data ss:Type=\"${type}\">${displayValue}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n xml += '\\n</Table>\\n</Worksheet>\\n</Workbook>';\n return xml;\n}\n\n/**\n * Download Excel XML content as a file.\n */\nexport function downloadExcel(content: string, fileName: string): void {\n const finalName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n const blob = new Blob([content], {\n type: 'application/vnd.ms-excel;charset=utf-8;',\n });\n downloadBlob(blob, finalName);\n}\n","/**\n * Export Plugin (Class-based)\n *\n * Provides data export functionality for tbw-grid.\n * Supports CSV, Excel (XML), and JSON formats.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildCsv, downloadBlob, downloadCsv } from './csv';\nimport { buildExcelXml, downloadExcel } from './excel';\nimport type { ExportCompleteDetail, ExportConfig, ExportFormat, ExportParams } from './types';\n\n/** Selection plugin state interface for type safety */\ninterface SelectionPluginState {\n selected: Set<number>;\n}\n\n/**\n * Export Plugin for tbw-grid\n *\n * Lets users download grid data as CSV, Excel (XML), or JSON with a single click\n * or API call. Great for reporting, data backup, or letting users work with data\n * in Excel. Integrates with SelectionPlugin to export only selected rows.\n *\n * ## Installation\n *\n * ```ts\n * import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `fileName` | `string` | `'export'` | Base filename (without extension) |\n * | `includeHeaders` | `boolean` | `true` | Include column headers in export |\n * | `onlyVisible` | `boolean` | `true` | Export only visible columns |\n * | `onlySelected` | `boolean` | `false` | Export only selected rows (requires SelectionPlugin) |\n *\n * ## Supported Formats\n *\n * | Format | Method | Description |\n * |--------|--------|-------------|\n * | CSV | `exportToCSV()` | Comma-separated values |\n * | Excel | `exportToExcel()` | Excel XML format (.xlsx) |\n * | JSON | `exportToJSON()` | JSON array of objects |\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `exportToCSV` | `(params?) => void` | Export as CSV file |\n * | `exportToExcel` | `(params?) => void` | Export as Excel file |\n * | `exportToJSON` | `(params?) => void` | Export as JSON file |\n * | `isExporting` | `() => boolean` | Check if export is in progress |\n *\n * @example Basic Export with Button\n * ```ts\n * import '@toolbox-web/grid';\n * import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\n *\n * const grid = document.querySelector('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'name', header: 'Name' },\n * { field: 'email', header: 'Email' },\n * ],\n * plugins: [new ExportPlugin({ fileName: 'employees', includeHeaders: true })],\n * };\n *\n * // Trigger export via button\n * document.getElementById('export-btn').addEventListener('click', () => {\n * grid.getPlugin(ExportPlugin).exportToCSV();\n * });\n * ```\n *\n * @example Export Selected Rows Only\n * ```ts\n * import { SelectionPlugin } from '@toolbox-web/grid/plugins/selection';\n *\n * grid.gridConfig = {\n * plugins: [\n * new SelectionPlugin({ mode: 'row' }),\n * new ExportPlugin({ onlySelected: true }),\n * ],\n * };\n * ```\n *\n * @see {@link ExportConfig} for all configuration options\n * @see {@link ExportParams} for method parameters\n * @see {@link SelectionPlugin} for exporting selected rows\n *\n * @internal Extends BaseGridPlugin\n */\nexport class ExportPlugin extends BaseGridPlugin<ExportConfig> {\n /** @internal */\n readonly name = 'export';\n\n /** @internal */\n protected override get defaultConfig(): Partial<ExportConfig> {\n return {\n fileName: 'export',\n includeHeaders: true,\n onlyVisible: true,\n onlySelected: false,\n };\n }\n\n // #region Internal State\n private isExportingFlag = false;\n private lastExportInfo: { format: ExportFormat; timestamp: Date } | null = null;\n // #endregion\n\n // #region Private Methods\n\n private performExport(format: ExportFormat, params?: Partial<ExportParams>): void {\n const config = this.config;\n\n // Build full params with defaults\n const fullParams: ExportParams = {\n format,\n fileName: params?.fileName ?? config.fileName ?? 'export',\n includeHeaders: params?.includeHeaders ?? config.includeHeaders,\n processCell: params?.processCell,\n processHeader: params?.processHeader,\n columns: params?.columns,\n rowIndices: params?.rowIndices,\n };\n\n // Get columns to export\n let columns = [...this.columns] as ColumnConfig[];\n if (config.onlyVisible) {\n columns = columns.filter((c) => !c.hidden && !c.field.startsWith('__'));\n }\n if (params?.columns) {\n const colSet = new Set(params.columns);\n columns = columns.filter((c) => colSet.has(c.field));\n }\n\n // Get rows to export\n let rows = [...this.rows] as Record<string, unknown>[];\n if (config.onlySelected) {\n const selectionState = this.getSelectionState();\n if (selectionState?.selected?.size) {\n const sortedIndices = [...selectionState.selected].sort((a, b) => a - b);\n rows = sortedIndices.map((i) => this.rows[i]).filter(Boolean) as Record<string, unknown>[];\n }\n }\n if (params?.rowIndices) {\n rows = params.rowIndices.map((i) => this.rows[i]).filter(Boolean) as Record<string, unknown>[];\n }\n\n this.isExportingFlag = true;\n let fileName = fullParams.fileName!;\n\n try {\n switch (format) {\n case 'csv': {\n const content = buildCsv(rows, columns, fullParams, { bom: true });\n fileName = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`;\n downloadCsv(content, fileName);\n break;\n }\n\n case 'excel': {\n const content = buildExcelXml(rows, columns, fullParams);\n fileName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n downloadExcel(content, fileName);\n break;\n }\n\n case 'json': {\n const jsonData = rows.map((row) => {\n const obj: Record<string, any> = {};\n for (const col of columns) {\n let value = row[col.field];\n if (fullParams.processCell) {\n value = fullParams.processCell(value, col.field, row);\n }\n obj[col.field] = value;\n }\n return obj;\n });\n const content = JSON.stringify(jsonData, null, 2);\n fileName = fileName.endsWith('.json') ? fileName : `${fileName}.json`;\n const blob = new Blob([content], { type: 'application/json' });\n downloadBlob(blob, fileName);\n break;\n }\n }\n\n this.lastExportInfo = { format, timestamp: new Date() };\n\n this.emit<ExportCompleteDetail>('export-complete', {\n format,\n fileName,\n rowCount: rows.length,\n columnCount: columns.length,\n });\n } finally {\n this.isExportingFlag = false;\n }\n }\n\n private getSelectionState(): SelectionPluginState | null {\n try {\n return (this.grid?.getPluginState?.('selection') as SelectionPluginState | null) ?? null;\n } catch {\n return null;\n }\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Export data to CSV format.\n * @param params - Optional export parameters\n */\n exportCsv(params?: Partial<ExportParams>): void {\n this.performExport('csv', params);\n }\n\n /**\n * Export data to Excel format (XML Spreadsheet).\n * @param params - Optional export parameters\n */\n exportExcel(params?: Partial<ExportParams>): void {\n this.performExport('excel', params);\n }\n\n /**\n * Export data to JSON format.\n * @param params - Optional export parameters\n */\n exportJson(params?: Partial<ExportParams>): void {\n this.performExport('json', params);\n }\n\n /**\n * Check if an export is currently in progress.\n * @returns Whether export is in progress\n */\n isExporting(): boolean {\n return this.isExportingFlag;\n }\n\n /**\n * Get information about the last export.\n * @returns Export info or null if no export has occurred\n */\n getLastExport(): { format: ExportFormat; timestamp: Date } | null {\n return this.lastExportInfo;\n }\n // #endregion\n}\n"],"names":["formatCsvValue","value","quote","str","buildCsv","rows","columns","params","options","delimiter","newline","lines","bom","headerRow","col","header","processed","row","cells","downloadBlob","blob","fileName","url","link","downloadCsv","content","escapeXml","buildExcelXml","xml","type","displayValue","downloadExcel","finalName","ExportPlugin","BaseGridPlugin","format","config","fullParams","c","colSet","selectionState","a","b","i","jsonData","obj"],"mappings":"kUAyBO,SAASA,EAAeC,EAAYC,EAAQ,GAAc,CAC/D,GAAID,GAAS,KAAM,MAAO,GAC1B,GAAIA,aAAiB,KAAM,OAAOA,EAAM,YAAA,EACxC,GAAI,OAAOA,GAAU,SAAU,OAAO,KAAK,UAAUA,CAAK,EAE1D,MAAME,EAAM,OAAOF,CAAK,EAGxB,OAAIC,IAAUC,EAAI,SAAS,GAAG,GAAKA,EAAI,SAAS,GAAG,GAAKA,EAAI,SAAS;AAAA,CAAI,GAAKA,EAAI,SAAS,IAAI,GACtF,IAAIA,EAAI,QAAQ,KAAM,IAAI,CAAC,IAG7BA,CACT,CAKO,SAASC,EAASC,EAAaC,EAAyBC,EAAsBC,EAAsB,CAAA,EAAY,CACrH,MAAMC,EAAYD,EAAQ,WAAa,IACjCE,EAAUF,EAAQ,SAAW;AAAA,EAC7BG,EAAkB,CAAA,EAGlBC,EAAMJ,EAAQ,IAAM,SAAW,GAGrC,GAAID,EAAO,iBAAmB,GAAO,CACnC,MAAMM,EAAYP,EAAQ,IAAKQ,GAAQ,CACrC,MAAMC,EAASD,EAAI,QAAUA,EAAI,MAC3BE,EAAYT,EAAO,cAAgBA,EAAO,cAAcQ,EAAQD,EAAI,KAAK,EAAIC,EACnF,OAAOf,EAAegB,CAAS,CACjC,CAAC,EACDL,EAAM,KAAKE,EAAU,KAAKJ,CAAS,CAAC,CACtC,CAGA,UAAWQ,KAAOZ,EAAM,CACtB,MAAMa,EAAQZ,EAAQ,IAAKQ,GAAQ,CACjC,IAAIb,EAAQgB,EAAIH,EAAI,KAAK,EACzB,OAAIP,EAAO,cACTN,EAAQM,EAAO,YAAYN,EAAOa,EAAI,MAAOG,CAAG,GAE3CjB,EAAeC,CAAK,CAC7B,CAAC,EACDU,EAAM,KAAKO,EAAM,KAAKT,CAAS,CAAC,CAClC,CAEA,OAAOG,EAAMD,EAAM,KAAKD,CAAO,CACjC,CAKO,SAASS,EAAaC,EAAYC,EAAwB,CAC/D,MAAMC,EAAM,IAAI,gBAAgBF,CAAI,EAC9BG,EAAO,SAAS,cAAc,GAAG,EACvCA,EAAK,KAAOD,EACZC,EAAK,SAAWF,EAChBE,EAAK,MAAM,QAAU,OACrB,SAAS,KAAK,YAAYA,CAAI,EAC9BA,EAAK,MAAA,EACL,SAAS,KAAK,YAAYA,CAAI,EAC9B,IAAI,gBAAgBD,CAAG,CACzB,CAKO,SAASE,EAAYC,EAAiBJ,EAAwB,CACnE,MAAMD,EAAO,IAAI,KAAK,CAACK,CAAO,EAAG,CAAE,KAAM,0BAA2B,EACpEN,EAAaC,EAAMC,CAAQ,CAC7B,CCnFA,SAASK,EAAUvB,EAAqB,CACtC,OAAOA,EACJ,QAAQ,KAAM,OAAO,EACrB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,MAAM,EACpB,QAAQ,KAAM,QAAQ,EACtB,QAAQ,KAAM,QAAQ,CAC3B,CAMO,SAASwB,EAActB,EAAaC,EAAyBC,EAA8B,CAChG,IAAIqB,EAAM;AAAA;AAAA;AAAA;AAAA;AAAA,SAQV,GAAIrB,EAAO,iBAAmB,GAAO,CACnCqB,GAAO;AAAA,OACP,UAAWd,KAAOR,EAAS,CACzB,MAAMS,EAASD,EAAI,QAAUA,EAAI,MAC3BE,EAAYT,EAAO,cAAgBA,EAAO,cAAcQ,EAAQD,EAAI,KAAK,EAAIC,EACnFa,GAAO,gCAAgCF,EAAUV,CAAS,CAAC,gBAC7D,CACAY,GAAO,QACT,CAGA,UAAWX,KAAOZ,EAAM,CACtBuB,GAAO;AAAA,OACP,UAAWd,KAAOR,EAAS,CACzB,IAAIL,EAAQgB,EAAIH,EAAI,KAAK,EACrBP,EAAO,cACTN,EAAQM,EAAO,YAAYN,EAAOa,EAAI,MAAOG,CAAG,GAIlD,IAAIY,EAAyC,SACzCC,EAAe,GAEf7B,GAAS,KACX6B,EAAe,GACN,OAAO7B,GAAU,UAAY,CAAC,MAAMA,CAAK,GAClD4B,EAAO,SACPC,EAAe,OAAO7B,CAAK,GAClBA,aAAiB,MAC1B4B,EAAO,WACPC,EAAe7B,EAAM,YAAA,GAErB6B,EAAeJ,EAAU,OAAOzB,CAAK,CAAC,EAGxC2B,GAAO,wBAAwBC,CAAI,KAAKC,CAAY,gBACtD,CACAF,GAAO,QACT,CAEA,OAAAA,GAAO;AAAA;AAAA;AAAA,aACAA,CACT,CAKO,SAASG,EAAcN,EAAiBJ,EAAwB,CACrE,MAAMW,EAAYX,EAAS,SAAS,MAAM,EAAIA,EAAW,GAAGA,CAAQ,OAC9DD,EAAO,IAAI,KAAK,CAACK,CAAO,EAAG,CAC/B,KAAM,yCAAA,CACP,EACDN,EAAaC,EAAMY,CAAS,CAC9B,CCQO,MAAMC,UAAqBC,EAAAA,cAA6B,CAEpD,KAAO,SAGhB,IAAuB,eAAuC,CAC5D,MAAO,CACL,SAAU,SACV,eAAgB,GAChB,YAAa,GACb,aAAc,EAAA,CAElB,CAGQ,gBAAkB,GAClB,eAAmE,KAKnE,cAAcC,EAAsB5B,EAAsC,CAChF,MAAM6B,EAAS,KAAK,OAGdC,EAA2B,CAC/B,OAAAF,EACA,SAAU5B,GAAQ,UAAY6B,EAAO,UAAY,SACjD,eAAgB7B,GAAQ,gBAAkB6B,EAAO,eACjD,YAAa7B,GAAQ,YACrB,cAAeA,GAAQ,cACvB,QAASA,GAAQ,QACjB,WAAYA,GAAQ,UAAA,EAItB,IAAID,EAAU,CAAC,GAAG,KAAK,OAAO,EAI9B,GAHI8B,EAAO,cACT9B,EAAUA,EAAQ,OAAQgC,GAAM,CAACA,EAAE,QAAU,CAACA,EAAE,MAAM,WAAW,IAAI,CAAC,GAEpE/B,GAAQ,QAAS,CACnB,MAAMgC,EAAS,IAAI,IAAIhC,EAAO,OAAO,EACrCD,EAAUA,EAAQ,OAAQgC,GAAMC,EAAO,IAAID,EAAE,KAAK,CAAC,CACrD,CAGA,IAAIjC,EAAO,CAAC,GAAG,KAAK,IAAI,EACxB,GAAI+B,EAAO,aAAc,CACvB,MAAMI,EAAiB,KAAK,kBAAA,EACxBA,GAAgB,UAAU,OAE5BnC,EADsB,CAAC,GAAGmC,EAAe,QAAQ,EAAE,KAAK,CAACC,EAAGC,IAAMD,EAAIC,CAAC,EAClD,IAAKC,GAAM,KAAK,KAAKA,CAAC,CAAC,EAAE,OAAO,OAAO,EAEhE,CACIpC,GAAQ,aACVF,EAAOE,EAAO,WAAW,IAAKoC,GAAM,KAAK,KAAKA,CAAC,CAAC,EAAE,OAAO,OAAO,GAGlE,KAAK,gBAAkB,GACvB,IAAItB,EAAWgB,EAAW,SAE1B,GAAI,CACF,OAAQF,EAAA,CACN,IAAK,MAAO,CACV,MAAMV,EAAUrB,EAASC,EAAMC,EAAS+B,EAAY,CAAE,IAAK,GAAM,EACjEhB,EAAWA,EAAS,SAAS,MAAM,EAAIA,EAAW,GAAGA,CAAQ,OAC7DG,EAAYC,EAASJ,CAAQ,EAC7B,KACF,CAEA,IAAK,QAAS,CACZ,MAAMI,EAAUE,EAActB,EAAMC,EAAS+B,CAAU,EACvDhB,EAAWA,EAAS,SAAS,MAAM,EAAIA,EAAW,GAAGA,CAAQ,OAC7DU,EAAcN,EAASJ,CAAQ,EAC/B,KACF,CAEA,IAAK,OAAQ,CACX,MAAMuB,EAAWvC,EAAK,IAAKY,GAAQ,CACjC,MAAM4B,EAA2B,CAAA,EACjC,UAAW/B,KAAOR,EAAS,CACzB,IAAIL,EAAQgB,EAAIH,EAAI,KAAK,EACrBuB,EAAW,cACbpC,EAAQoC,EAAW,YAAYpC,EAAOa,EAAI,MAAOG,CAAG,GAEtD4B,EAAI/B,EAAI,KAAK,EAAIb,CACnB,CACA,OAAO4C,CACT,CAAC,EACKpB,EAAU,KAAK,UAAUmB,EAAU,KAAM,CAAC,EAChDvB,EAAWA,EAAS,SAAS,OAAO,EAAIA,EAAW,GAAGA,CAAQ,QAC9D,MAAMD,EAAO,IAAI,KAAK,CAACK,CAAO,EAAG,CAAE,KAAM,mBAAoB,EAC7DN,EAAaC,EAAMC,CAAQ,EAC3B,KACF,CAAA,CAGF,KAAK,eAAiB,CAAE,OAAAc,EAAQ,UAAW,IAAI,IAAK,EAEpD,KAAK,KAA2B,kBAAmB,CACjD,OAAAA,EACA,SAAAd,EACA,SAAUhB,EAAK,OACf,YAAaC,EAAQ,MAAA,CACtB,CACH,QAAA,CACE,KAAK,gBAAkB,EACzB,CACF,CAEQ,mBAAiD,CACvD,GAAI,CACF,OAAQ,KAAK,MAAM,iBAAiB,WAAW,GAAqC,IACtF,MAAQ,CACN,OAAO,IACT,CACF,CASA,UAAUC,EAAsC,CAC9C,KAAK,cAAc,MAAOA,CAAM,CAClC,CAMA,YAAYA,EAAsC,CAChD,KAAK,cAAc,QAASA,CAAM,CACpC,CAMA,WAAWA,EAAsC,CAC/C,KAAK,cAAc,OAAQA,CAAM,CACnC,CAMA,aAAuB,CACrB,OAAO,KAAK,eACd,CAMA,eAAkE,CAChE,OAAO,KAAK,cACd,CAEF"}
|
|
1
|
+
{"version":3,"file":"export.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/shared/data-collection.ts","../../../../../libs/grid/src/lib/plugins/export/csv.ts","../../../../../libs/grid/src/lib/plugins/export/excel.ts","../../../../../libs/grid/src/lib/plugins/export/ExportPlugin.ts"],"sourcesContent":["/**\n * Shared Data Collection Utilities\n *\n * Pure functions for resolving columns and formatting values, shared between\n * the Clipboard and Export plugins. Each plugin bundles its own copy of this\n * module (no chunk splitting) since plugin builds inline sibling imports.\n *\n * @internal\n */\n\nimport type { ColumnConfig } from '../../core/types';\n\n/**\n * Resolve which columns to include in a data export or copy operation.\n *\n * Filters out hidden columns, utility columns (`meta.utility`), and\n * internal columns (`__`-prefixed fields). Optionally restricts to an\n * explicit set of field names.\n *\n * @param columns - All column configurations\n * @param fields - If provided, only include columns whose field is in this list\n * @param onlyVisible - When `true` (default), exclude hidden and internal columns\n * @returns Filtered column array preserving original order\n */\nexport function resolveColumns(\n columns: readonly ColumnConfig[],\n fields?: string[],\n onlyVisible = true,\n): ColumnConfig[] {\n let result = columns as ColumnConfig[];\n\n if (onlyVisible) {\n result = result.filter((c) => !c.hidden && !c.field.startsWith('__') && c.meta?.utility !== true);\n }\n\n if (fields?.length) {\n const fieldSet = new Set(fields);\n result = result.filter((c) => fieldSet.has(c.field));\n }\n\n return result;\n}\n\n/**\n * Resolve which rows to include, optionally filtered to specific indices.\n *\n * @param rows - All row data\n * @param indices - If provided, only include rows at these indices (sorted ascending)\n * @returns Filtered row array\n */\nexport function resolveRows<T>(rows: readonly T[], indices?: number[]): T[] {\n if (!indices?.length) return rows as T[];\n\n return [...indices]\n .sort((a, b) => a - b)\n .map((i) => rows[i])\n .filter((r): r is T => r != null);\n}\n\n/**\n * Format a raw cell value as a text string.\n *\n * Provides the common null / Date / object → string conversion shared by\n * both clipboard and export output builders.\n *\n * @param value - The cell value to format\n * @returns A plain-text representation of the value\n */\nexport function formatValueAsText(value: unknown): string {\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n return String(value);\n}\n","/**\n * CSV Export Utilities\n *\n * Functions for building and downloading CSV content.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\n\n/** CSV export options */\nexport interface CsvOptions {\n /** Field delimiter (default: ',') */\n delimiter?: string;\n /** Line separator (default: '\\n') */\n newline?: string;\n /** Whether to quote strings containing special characters (default: true) */\n quoteStrings?: boolean;\n /** Add UTF-8 BOM for Excel compatibility (default: false) */\n bom?: boolean;\n}\n\n/**\n * Format a value for CSV output.\n * Handles null, Date, objects, and strings with special characters.\n */\nexport function formatCsvValue(value: any, quote = true): string {\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n\n // Quote if contains special characters (comma, quote, newline)\n if (quote && (str.includes(',') || str.includes('\"') || str.includes('\\n') || str.includes('\\r'))) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build CSV content from rows and columns.\n */\nexport function buildCsv(rows: any[], columns: ColumnConfig[], params: ExportParams, options: CsvOptions = {}): string {\n const delimiter = options.delimiter ?? ',';\n const newline = options.newline ?? '\\n';\n const lines: string[] = [];\n\n // UTF-8 BOM for Excel compatibility\n const bom = options.bom ? '\\uFEFF' : '';\n\n // Build header row\n if (params.includeHeaders !== false) {\n const headerRow = columns.map((col) => {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n return formatCsvValue(processed);\n });\n lines.push(headerRow.join(delimiter));\n }\n\n // Build data rows\n for (const row of rows) {\n const cells = columns.map((col) => {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n return formatCsvValue(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return bom + lines.join(newline);\n}\n\n/**\n * Download a Blob as a file.\n */\nexport function downloadBlob(blob: Blob, fileName: string): void {\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n link.style.display = 'none';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Download CSV content as a file.\n */\nexport function downloadCsv(content: string, fileName: string): void {\n const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });\n downloadBlob(blob, fileName);\n}\n","/**\n * Excel Export Utilities\n *\n * Simple Excel XML format export (no external dependencies).\n * Produces XML Spreadsheet 2003 format which opens in Excel.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\nimport { downloadBlob } from './csv';\n\n/**\n * Escape XML special characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&')\n .replace(/</g, '<')\n .replace(/>/g, '>')\n .replace(/\"/g, '"')\n .replace(/'/g, ''');\n}\n\n/**\n * Build Excel XML content from rows and columns.\n * Uses XML Spreadsheet 2003 format for broad compatibility.\n */\nexport function buildExcelXml(rows: any[], columns: ColumnConfig[], params: ExportParams): string {\n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?mso-application progid=\"Excel.Sheet\"?>\n<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"\n xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\">\n<Worksheet ss:Name=\"Sheet1\">\n<Table>`;\n\n // Build header row\n if (params.includeHeaders !== false) {\n xml += '\\n<Row>';\n for (const col of columns) {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n xml += `<Cell><Data ss:Type=\"String\">${escapeXml(processed)}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n // Build data rows\n for (const row of rows) {\n xml += '\\n<Row>';\n for (const col of columns) {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n\n // Determine cell type based on value\n let type: 'Number' | 'String' | 'DateTime' = 'String';\n let displayValue = '';\n\n if (value == null) {\n displayValue = '';\n } else if (typeof value === 'number' && !isNaN(value)) {\n type = 'Number';\n displayValue = String(value);\n } else if (value instanceof Date) {\n type = 'DateTime';\n displayValue = value.toISOString();\n } else {\n displayValue = escapeXml(String(value));\n }\n\n xml += `<Cell><Data ss:Type=\"${type}\">${displayValue}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n xml += '\\n</Table>\\n</Worksheet>\\n</Workbook>';\n return xml;\n}\n\n/**\n * Download Excel XML content as a file.\n */\nexport function downloadExcel(content: string, fileName: string): void {\n const finalName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n const blob = new Blob([content], {\n type: 'application/vnd.ms-excel;charset=utf-8;',\n });\n downloadBlob(blob, finalName);\n}\n","/**\n * Export Plugin (Class-based)\n *\n * Provides data export functionality for tbw-grid.\n * Supports CSV, Excel (XML), and JSON formats.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { resolveColumns, resolveRows } from '../shared/data-collection';\nimport { buildCsv, downloadBlob, downloadCsv } from './csv';\nimport { buildExcelXml, downloadExcel } from './excel';\nimport type { ExportCompleteDetail, ExportConfig, ExportFormat, ExportParams } from './types';\n\n/** Selection plugin state interface for type safety */\ninterface SelectionPluginState {\n selected: Set<number>;\n}\n\n/**\n * Export Plugin for tbw-grid\n *\n * Lets users download grid data as CSV, Excel (XML), or JSON with a single click\n * or API call. Great for reporting, data backup, or letting users work with data\n * in Excel. Integrates with SelectionPlugin to export only selected rows.\n *\n * ## Installation\n *\n * ```ts\n * import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `fileName` | `string` | `'export'` | Base filename (without extension) |\n * | `includeHeaders` | `boolean` | `true` | Include column headers in export |\n * | `onlyVisible` | `boolean` | `true` | Export only visible columns |\n * | `onlySelected` | `boolean` | `false` | Export only selected rows (requires SelectionPlugin) |\n *\n * ## Supported Formats\n *\n * | Format | Method | Description |\n * |--------|--------|-------------|\n * | CSV | `exportToCSV()` | Comma-separated values |\n * | Excel | `exportToExcel()` | Excel XML format (.xlsx) |\n * | JSON | `exportToJSON()` | JSON array of objects |\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `exportToCSV` | `(params?) => void` | Export as CSV file |\n * | `exportToExcel` | `(params?) => void` | Export as Excel file |\n * | `exportToJSON` | `(params?) => void` | Export as JSON file |\n * | `isExporting` | `() => boolean` | Check if export is in progress |\n *\n * @example Basic Export with Button\n * ```ts\n * import '@toolbox-web/grid';\n * import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\n *\n * const grid = document.querySelector('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'name', header: 'Name' },\n * { field: 'email', header: 'Email' },\n * ],\n * plugins: [new ExportPlugin({ fileName: 'employees', includeHeaders: true })],\n * };\n *\n * // Trigger export via button\n * document.getElementById('export-btn').addEventListener('click', () => {\n * grid.getPlugin(ExportPlugin).exportToCSV();\n * });\n * ```\n *\n * @example Export Selected Rows Only\n * ```ts\n * import { SelectionPlugin } from '@toolbox-web/grid/plugins/selection';\n *\n * grid.gridConfig = {\n * plugins: [\n * new SelectionPlugin({ mode: 'row' }),\n * new ExportPlugin({ onlySelected: true }),\n * ],\n * };\n * ```\n *\n * @see {@link ExportConfig} for all configuration options\n * @see {@link ExportParams} for method parameters\n * @see {@link SelectionPlugin} for exporting selected rows\n *\n * @internal Extends BaseGridPlugin\n */\nexport class ExportPlugin extends BaseGridPlugin<ExportConfig> {\n /** @internal */\n readonly name = 'export';\n\n /** @internal */\n protected override get defaultConfig(): Partial<ExportConfig> {\n return {\n fileName: 'export',\n includeHeaders: true,\n onlyVisible: true,\n onlySelected: false,\n };\n }\n\n // #region Internal State\n private isExportingFlag = false;\n private lastExportInfo: { format: ExportFormat; timestamp: Date } | null = null;\n // #endregion\n\n // #region Private Methods\n\n private performExport(format: ExportFormat, params?: Partial<ExportParams>): void {\n const config = this.config;\n\n // Build full params with defaults\n const fullParams: ExportParams = {\n format,\n fileName: params?.fileName ?? config.fileName ?? 'export',\n includeHeaders: params?.includeHeaders ?? config.includeHeaders,\n processCell: params?.processCell,\n processHeader: params?.processHeader,\n columns: params?.columns,\n rowIndices: params?.rowIndices,\n };\n\n // Get columns to export (shared utility handles hidden/utility filtering)\n const columns = resolveColumns(this.columns, params?.columns, config.onlyVisible) as ColumnConfig[];\n\n // Get rows to export\n let rows: Record<string, unknown>[];\n if (params?.rowIndices) {\n rows = resolveRows(this.rows as Record<string, unknown>[], params.rowIndices);\n } else if (config.onlySelected) {\n const selectionState = this.getSelectionState();\n if (selectionState?.selected?.size) {\n rows = resolveRows(this.rows as Record<string, unknown>[], [...selectionState.selected]);\n } else {\n rows = [...this.rows] as Record<string, unknown>[];\n }\n } else {\n rows = [...this.rows] as Record<string, unknown>[];\n }\n\n this.isExportingFlag = true;\n let fileName = fullParams.fileName!;\n\n try {\n switch (format) {\n case 'csv': {\n const content = buildCsv(rows, columns, fullParams, { bom: true });\n fileName = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`;\n downloadCsv(content, fileName);\n break;\n }\n\n case 'excel': {\n const content = buildExcelXml(rows, columns, fullParams);\n fileName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n downloadExcel(content, fileName);\n break;\n }\n\n case 'json': {\n const jsonData = rows.map((row) => {\n const obj: Record<string, any> = {};\n for (const col of columns) {\n let value = row[col.field];\n if (fullParams.processCell) {\n value = fullParams.processCell(value, col.field, row);\n }\n obj[col.field] = value;\n }\n return obj;\n });\n const content = JSON.stringify(jsonData, null, 2);\n fileName = fileName.endsWith('.json') ? fileName : `${fileName}.json`;\n const blob = new Blob([content], { type: 'application/json' });\n downloadBlob(blob, fileName);\n break;\n }\n }\n\n this.lastExportInfo = { format, timestamp: new Date() };\n\n this.emit<ExportCompleteDetail>('export-complete', {\n format,\n fileName,\n rowCount: rows.length,\n columnCount: columns.length,\n });\n } finally {\n this.isExportingFlag = false;\n }\n }\n\n private getSelectionState(): SelectionPluginState | null {\n try {\n return (this.grid?.getPluginState?.('selection') as SelectionPluginState | null) ?? null;\n } catch {\n return null;\n }\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Export data to CSV format.\n * @param params - Optional export parameters\n */\n exportCsv(params?: Partial<ExportParams>): void {\n this.performExport('csv', params);\n }\n\n /**\n * Export data to Excel format (XML Spreadsheet).\n * @param params - Optional export parameters\n */\n exportExcel(params?: Partial<ExportParams>): void {\n this.performExport('excel', params);\n }\n\n /**\n * Export data to JSON format.\n * @param params - Optional export parameters\n */\n exportJson(params?: Partial<ExportParams>): void {\n this.performExport('json', params);\n }\n\n /**\n * Check if an export is currently in progress.\n * @returns Whether export is in progress\n */\n isExporting(): boolean {\n return this.isExportingFlag;\n }\n\n /**\n * Get information about the last export.\n * @returns Export info or null if no export has occurred\n */\n getLastExport(): { format: ExportFormat; timestamp: Date } | null {\n return this.lastExportInfo;\n }\n // #endregion\n}\n"],"names":["resolveColumns","columns","fields","onlyVisible","result","c","fieldSet","resolveRows","rows","indices","a","b","i","r","formatCsvValue","value","quote","str","buildCsv","params","options","delimiter","newline","lines","bom","headerRow","col","header","processed","row","cells","downloadBlob","blob","fileName","url","link","downloadCsv","content","escapeXml","buildExcelXml","xml","type","displayValue","downloadExcel","finalName","ExportPlugin","BaseGridPlugin","format","config","fullParams","selectionState","jsonData","obj"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
(function(f,h){typeof exports=="object"&&typeof module<"u"?h(exports,require("../../core/plugin/base-plugin"),require("../../core/internal/aggregators")):typeof define=="function"&&define.amd?define(["exports","../../core/plugin/base-plugin","../../core/internal/aggregators"],h):(f=typeof globalThis<"u"?globalThis:f||self,h(f.TbwGridPlugin_pinnedRows={},f.TbwGrid,f.TbwGrid))})(this,(function(f,h,w){"use strict";function v(o){return typeof o=="object"&&o!==null&&"aggFunc"in o}function u(o,e){const n=document.createElement("div");n.className="tbw-pinned-rows",n.setAttribute("role","presentation"),n.setAttribute("aria-live","polite");const r=document.createElement("div");r.className="tbw-pinned-rows-left";const s=document.createElement("div");s.className="tbw-pinned-rows-center";const i=document.createElement("div");if(i.className="tbw-pinned-rows-right",o.showRowCount!==!1){const t=document.createElement("span");t.className="tbw-status-panel tbw-status-panel-row-count",t.textContent=`Total: ${e.totalRows} rows`,r.appendChild(t)}if(o.showFilteredCount&&e.filteredRows!==e.totalRows){const t=document.createElement("span");t.className="tbw-status-panel tbw-status-panel-filtered-count",t.textContent=`Filtered: ${e.filteredRows}`,r.appendChild(t)}if(o.showSelectedCount&&e.selectedRows>0){const t=document.createElement("span");t.className="tbw-status-panel tbw-status-panel-selected-count",t.textContent=`Selected: ${e.selectedRows}`,i.appendChild(t)}if(o.customPanels)for(const t of o.customPanels){const a=A(t,e);switch(t.position){case"left":r.appendChild(a);break;case"center":s.appendChild(a);break;case"right":i.appendChild(a);break}}return n.appendChild(r),n.appendChild(s),n.appendChild(i),n}function m(o){const e=document.createElement("div");return e.className=`tbw-aggregation-rows tbw-aggregation-rows-${o}`,e.setAttribute("role","presentation"),e}function b(o,e,n,r){o.innerHTML="";for(const s of e){const i=document.createElement("div");if(i.className="tbw-aggregation-row",i.setAttribute("role","presentation"),s.id&&i.setAttribute("data-aggregation-id",s.id),s.fullWidth){const t=document.createElement("div");t.className="tbw-aggregation-cell tbw-aggregation-cell-full",t.style.gridColumn="1 / -1",t.textContent=s.label||"",i.appendChild(t)}else for(const t of n){const a=document.createElement("div");a.className="tbw-aggregation-cell",a.setAttribute("data-field",t.field);let l,p;const d=s.aggregators?.[t.field];if(d)if(v(d)){const g=w.getAggregator(d.aggFunc);g&&(l=g(r,t.field,t)),p=d.formatter}else{const g=w.getAggregator(d);g&&(l=g(r,t.field,t))}else if(s.cells&&Object.prototype.hasOwnProperty.call(s.cells,t.field)){const g=s.cells[t.field];typeof g=="function"?l=g(r,t.field,t):l=g}l!=null?a.textContent=p?p(l,t.field,t):String(l):a.textContent="",i.appendChild(a)}o.appendChild(i)}}function A(o,e){const n=document.createElement("div");n.className="tbw-status-panel tbw-status-panel-custom",n.id=`status-panel-${o.id}`;const r=o.render(e);return typeof r=="string"?n.innerHTML=r:n.appendChild(r),n}function C(o,e,n,r,s){return{totalRows:o.length,filteredRows:s?.cachedResult?.length??o.length,selectedRows:r?.selected?.size??0,columns:e,rows:o,grid:n}}const E="@layer tbw-plugins{.tbw-footer{flex-shrink:0;z-index:var(--tbw-z-layer-pinned-rows, 20);background:var(--tbw-color-panel-bg)}.tbw-pinned-rows{display:flex;align-items:center;justify-content:space-between;padding:var(--tbw-button-padding, var(--tbw-spacing-md, .5rem) var(--tbw-spacing-lg, .75rem));background:var(--tbw-pinned-rows-bg, var(--tbw-color-panel-bg));border-top:1px solid var(--tbw-pinned-rows-border, var(--tbw-color-border));font-size:var(--tbw-font-size-xs, .75rem);color:var(--tbw-pinned-rows-color, var(--tbw-color-fg-muted));min-height:32px;box-sizing:border-box;min-width:fit-content}.tbw-pinned-rows-left,.tbw-pinned-rows-center,.tbw-pinned-rows-right{display:flex;align-items:center;gap:var(--tbw-spacing-xl, 1rem)}.tbw-pinned-rows-left{justify-content:flex-start}.tbw-pinned-rows-center{justify-content:center;flex:1}.tbw-pinned-rows-right{justify-content:flex-end}.tbw-status-panel{white-space:nowrap}.tbw-aggregation-rows{min-width:fit-content;background:var(--tbw-aggregation-bg, var(--tbw-color-header-bg))}.tbw-aggregation-rows-top{border-bottom:1px solid var(--tbw-aggregation-border, var(--tbw-color-border))}.tbw-aggregation-rows-bottom{border-top:1px solid var(--tbw-aggregation-border, var(--tbw-color-border))}.tbw-aggregation-row{display:grid;grid-template-columns:var(--tbw-column-template);font-size:var(--tbw-aggregation-font-size, .8em);font-weight:var(--tbw-aggregation-font-weight, 600)}.tbw-aggregation-cell{padding:var(--tbw-cell-padding, .125rem .5rem);min-height:var(--tbw-row-height, 1.75rem);display:block;align-items:center;border-right:1px solid var(--tbw-color-border-cell);overflow:hidden;text-overflow:ellipsis;white-space:var(--tbw-cell-white-space, nowrap)}.tbw-aggregation-cell:last-child{border-right:0}.tbw-aggregation-cell-full{grid-column:1 / -1;border-right:0}}";class R extends h.BaseGridPlugin{name="pinnedRows";styles=E;get defaultConfig(){return{position:"bottom",showRowCount:!0,showSelectedCount:!0,showFilteredCount:!0}}infoBarElement=null;topAggregationContainer=null;bottomAggregationContainer=null;footerWrapper=null;detach(){this.infoBarElement&&(this.infoBarElement.remove(),this.infoBarElement=null),this.topAggregationContainer&&(this.topAggregationContainer.remove(),this.topAggregationContainer=null),this.bottomAggregationContainer&&(this.bottomAggregationContainer.remove(),this.bottomAggregationContainer=null),this.footerWrapper&&(this.footerWrapper.remove(),this.footerWrapper=null)}afterRender(){const e=this.gridElement;if(!e)return;const n=e.querySelector(".tbw-scroll-area")??e.querySelector(".tbw-grid-content")??e.children[0];if(!n)return;this.footerWrapper&&!n.contains(this.footerWrapper)&&(this.footerWrapper=null,this.bottomAggregationContainer=null,this.infoBarElement=null),this.topAggregationContainer&&!n.contains(this.topAggregationContainer)&&(this.topAggregationContainer=null),this.infoBarElement&&!n.contains(this.infoBarElement)&&(this.infoBarElement=null);const r=this.getSelectionState(),s=this.getFilterState(),i=C(this.sourceRows,this.columns,this.grid,r,s),t=this.config.aggregationRows||[],a=t.filter(c=>c.position==="top"),l=t.filter(c=>c.position!=="top");if(a.length>0){if(!this.topAggregationContainer){this.topAggregationContainer=m("top");const c=e.querySelector(".header");c&&c.nextSibling?n.insertBefore(this.topAggregationContainer,c.nextSibling):n.appendChild(this.topAggregationContainer)}b(this.topAggregationContainer,a,this.visibleColumns,this.sourceRows)}else this.topAggregationContainer&&(this.topAggregationContainer.remove(),this.topAggregationContainer=null);const p=this.config.showRowCount!==!1||this.config.showSelectedCount&&i.selectedRows>0||this.config.showFilteredCount&&i.filteredRows!==i.totalRows||this.config.customPanels&&this.config.customPanels.length>0,d=p&&this.config.position!=="top",g=l.length>0||d;if(p&&this.config.position==="top")if(!this.infoBarElement)this.infoBarElement=u(this.config,i),n.insertBefore(this.infoBarElement,n.firstChild);else{const c=u(this.config,i);this.infoBarElement.replaceWith(c),this.infoBarElement=c}else this.config.position==="top"&&this.infoBarElement&&(this.infoBarElement.remove(),this.infoBarElement=null);g?(this.footerWrapper||(this.footerWrapper=document.createElement("div"),this.footerWrapper.className="tbw-footer",n.appendChild(this.footerWrapper)),this.footerWrapper.innerHTML="",l.length>0&&(this.bottomAggregationContainer||(this.bottomAggregationContainer=m("bottom")),this.footerWrapper.appendChild(this.bottomAggregationContainer),b(this.bottomAggregationContainer,l,this.visibleColumns,this.sourceRows)),d&&(this.infoBarElement=u(this.config,i),this.footerWrapper.appendChild(this.infoBarElement))):this.cleanupFooter()}cleanup(){this.infoBarElement&&(this.infoBarElement.remove(),this.infoBarElement=null),this.topAggregationContainer&&(this.topAggregationContainer.remove(),this.topAggregationContainer=null),this.bottomAggregationContainer&&(this.bottomAggregationContainer.remove(),this.bottomAggregationContainer=null),this.footerWrapper&&(this.footerWrapper.remove(),this.footerWrapper=null)}cleanupFooter(){this.footerWrapper&&(this.footerWrapper.remove(),this.footerWrapper=null),this.bottomAggregationContainer&&(this.bottomAggregationContainer.remove(),this.bottomAggregationContainer=null),this.infoBarElement&&this.config.position!=="top"&&(this.infoBarElement.remove(),this.infoBarElement=null)}getSelectionState(){try{return this.grid?.getPluginState?.("selection")??null}catch{return null}}getFilterState(){try{return this.grid?.getPluginState?.("filtering")??null}catch{return null}}refresh(){this.requestRender()}getContext(){const e=this.getSelectionState(),n=this.getFilterState();return C(this.rows,this.columns,this.grid,e,n)}addPanel(e){this.config.customPanels||(this.config.customPanels=[]),this.config.customPanels.push(e),this.requestRender()}removePanel(e){this.config.customPanels&&(this.config.customPanels=this.config.customPanels.filter(n=>n.id!==e),this.requestRender())}addAggregationRow(e){this.config.aggregationRows||(this.config.aggregationRows=[]),this.config.aggregationRows.push(e),this.requestRender()}removeAggregationRow(e){this.config.aggregationRows&&(this.config.aggregationRows=this.config.aggregationRows.filter(n=>n.id!==e),this.requestRender())}}f.PinnedRowsPlugin=R,Object.defineProperty(f,Symbol.toStringTag,{value:"Module"})}));
|
|
1
|
+
(function(g,f){typeof exports=="object"&&typeof module<"u"?f(exports,require("../../core/plugin/base-plugin"),require("../../core/internal/aggregators")):typeof define=="function"&&define.amd?define(["exports","../../core/plugin/base-plugin","../../core/internal/aggregators"],f):(g=typeof globalThis<"u"?globalThis:g||self,f(g.TbwGridPlugin_pinnedRows={},g.TbwGrid,g.TbwGrid))})(this,(function(g,f,m){"use strict";function A(o){return typeof o=="object"&&o!==null&&"aggFunc"in o}function h(o,t){const n=document.createElement("div");n.className="tbw-pinned-rows",n.setAttribute("role","presentation"),n.setAttribute("aria-live","polite");const r=document.createElement("div");r.className="tbw-pinned-rows-left";const a=document.createElement("div");a.className="tbw-pinned-rows-center";const i=document.createElement("div");if(i.className="tbw-pinned-rows-right",o.showRowCount!==!1){const e=document.createElement("span");e.className="tbw-status-panel tbw-status-panel-row-count",e.textContent=`Total: ${t.totalRows} rows`,r.appendChild(e)}if(o.showFilteredCount&&t.filteredRows!==t.totalRows){const e=document.createElement("span");e.className="tbw-status-panel tbw-status-panel-filtered-count",e.textContent=`Filtered: ${t.filteredRows}`,r.appendChild(e)}if(o.showSelectedCount&&t.selectedRows>0){const e=document.createElement("span");e.className="tbw-status-panel tbw-status-panel-selected-count",e.textContent=`Selected: ${t.selectedRows}`,i.appendChild(e)}if(o.customPanels)for(const e of o.customPanels){const s=B(e,t);switch(e.position){case"left":r.appendChild(s);break;case"center":a.appendChild(s);break;case"right":i.appendChild(s);break}}return n.appendChild(r),n.appendChild(a),n.appendChild(i),n}function w(o){const t=document.createElement("div");return t.className=`tbw-aggregation-rows tbw-aggregation-rows-${o}`,t.setAttribute("role","presentation"),t}function b(o,t,n,r,a=!1){o.innerHTML="";for(const i of t){const e=document.createElement("div");e.className="tbw-aggregation-row",e.setAttribute("role","presentation"),i.id&&e.setAttribute("data-aggregation-id",i.id),i.fullWidth??a?E(e,i,n,r):R(e,i,n,r),o.appendChild(e)}}function E(o,t,n,r){const a=document.createElement("div");a.className="tbw-aggregation-cell tbw-aggregation-cell-full",a.style.gridColumn="1 / -1";const i=typeof t.label=="function"?t.label(r,n):t.label;if(i){const s=document.createElement("span");s.className="tbw-aggregation-label",s.textContent=i,a.appendChild(s)}const e=y(t,n,r);e&&a.appendChild(e),o.appendChild(a)}function R(o,t,n,r){for(const a of n){const i=document.createElement("div");i.className="tbw-aggregation-cell",i.setAttribute("data-field",a.field);const{value:e,formatter:s}=C(t,a,r);e!=null?i.textContent=s?s(e,a.field,a):String(e):i.textContent="",o.appendChild(i)}}function C(o,t,n){let r,a;const i=o.aggregators?.[t.field];if(i)if(A(i)){const e=m.getAggregator(i.aggFunc);e&&(r=e(n,t.field,t)),a=i.formatter}else{const e=m.getAggregator(i);e&&(r=e(n,t.field,t))}else if(o.cells&&Object.prototype.hasOwnProperty.call(o.cells,t.field)){const e=o.cells[t.field];typeof e=="function"?r=e(n,t.field,t):r=e}return{value:r,formatter:a}}function y(o,t,n){const r=o.aggregators&&Object.keys(o.aggregators).length>0,a=o.cells&&Object.keys(o.cells).length>0;if(!r&&!a)return null;const i=document.createElement("span");i.className="tbw-aggregation-aggregates";for(const e of t){const{value:s,formatter:d}=C(o,e,n);if(s!=null){const c=document.createElement("span");c.className="tbw-aggregation-aggregate",c.setAttribute("data-field",e.field);const p=e.header??e.field,u=d?d(s,e.field,e):String(s);c.textContent=`${p}: ${u}`,i.appendChild(c)}}return i.children.length>0?i:null}function B(o,t){const n=document.createElement("div");n.className="tbw-status-panel tbw-status-panel-custom",n.id=`status-panel-${o.id}`;const r=o.render(t);return typeof r=="string"?n.innerHTML=r:n.appendChild(r),n}function v(o,t,n,r,a){return{totalRows:o.length,filteredRows:a?.cachedResult?.length??o.length,selectedRows:r?.selected?.size??0,columns:t,rows:o,grid:n}}const S="@layer tbw-plugins{.tbw-footer{flex-shrink:0;z-index:var(--tbw-z-layer-pinned-rows, 20);background:var(--tbw-color-panel-bg)}.tbw-pinned-rows{display:flex;align-items:center;justify-content:space-between;padding:var(--tbw-button-padding, var(--tbw-spacing-md, .5rem) var(--tbw-spacing-lg, .75rem));background:var(--tbw-pinned-rows-bg, var(--tbw-color-panel-bg));border-top:1px solid var(--tbw-pinned-rows-border, var(--tbw-color-border));font-size:var(--tbw-font-size-xs, .75rem);color:var(--tbw-pinned-rows-color, var(--tbw-color-fg-muted));min-height:32px;box-sizing:border-box;min-width:fit-content}.tbw-pinned-rows-left,.tbw-pinned-rows-center,.tbw-pinned-rows-right{display:flex;align-items:center;gap:var(--tbw-spacing-xl, 1rem)}.tbw-pinned-rows-left{justify-content:flex-start}.tbw-pinned-rows-center{justify-content:center;flex:1}.tbw-pinned-rows-right{justify-content:flex-end}.tbw-status-panel{white-space:nowrap}.tbw-aggregation-rows{min-width:fit-content;background:var(--tbw-aggregation-bg, var(--tbw-color-header-bg))}.tbw-aggregation-rows-top{border-bottom:1px solid var(--tbw-aggregation-border, var(--tbw-color-border))}.tbw-aggregation-rows-bottom{border-top:1px solid var(--tbw-aggregation-border, var(--tbw-color-border))}.tbw-aggregation-row{display:grid;grid-template-columns:var(--tbw-column-template);font-size:var(--tbw-aggregation-font-size, .8em);font-weight:var(--tbw-aggregation-font-weight, 600)}.tbw-aggregation-cell{padding:var(--tbw-cell-padding, .125rem .5rem);min-height:var(--tbw-row-height, 1.75rem);display:block;align-items:center;align-content:center;border-right:1px solid var(--tbw-color-border-cell);overflow:hidden;text-overflow:ellipsis;white-space:var(--tbw-cell-white-space, nowrap)}.tbw-aggregation-cell:last-child{border-right:0}.tbw-aggregation-cell-full{grid-column:1 / -1;border-right:0;display:flex;align-items:center;gap:var(--tbw-spacing-lg, .75rem)}.tbw-aggregation-label{white-space:nowrap}.tbw-aggregation-aggregates{display:flex;align-items:center;gap:var(--tbw-spacing-lg, .75rem);font-weight:400;opacity:.85}.tbw-aggregation-aggregate{white-space:nowrap}}";class x extends f.BaseGridPlugin{name="pinnedRows";styles=S;get defaultConfig(){return{position:"bottom",showRowCount:!0,showSelectedCount:!0,showFilteredCount:!0}}infoBarElement=null;topAggregationContainer=null;bottomAggregationContainer=null;footerWrapper=null;detach(){this.infoBarElement&&(this.infoBarElement.remove(),this.infoBarElement=null),this.topAggregationContainer&&(this.topAggregationContainer.remove(),this.topAggregationContainer=null),this.bottomAggregationContainer&&(this.bottomAggregationContainer.remove(),this.bottomAggregationContainer=null),this.footerWrapper&&(this.footerWrapper.remove(),this.footerWrapper=null)}afterRender(){const t=this.gridElement;if(!t)return;const n=t.querySelector(".tbw-scroll-area")??t.querySelector(".tbw-grid-content")??t.children[0];if(!n)return;this.footerWrapper&&!n.contains(this.footerWrapper)&&(this.footerWrapper=null,this.bottomAggregationContainer=null,this.infoBarElement=null),this.topAggregationContainer&&!n.contains(this.topAggregationContainer)&&(this.topAggregationContainer=null),this.infoBarElement&&!n.contains(this.infoBarElement)&&(this.infoBarElement=null);const r=this.getSelectionState(),a=this.getFilterState(),i=v(this.sourceRows,this.columns,this.grid,r,a),e=this.config.aggregationRows||[],s=e.filter(l=>l.position==="top"),d=e.filter(l=>l.position!=="top");if(s.length>0){if(!this.topAggregationContainer){this.topAggregationContainer=w("top");const l=t.querySelector(".header");l&&l.nextSibling?n.insertBefore(this.topAggregationContainer,l.nextSibling):n.appendChild(this.topAggregationContainer)}b(this.topAggregationContainer,s,this.visibleColumns,this.sourceRows,this.config.fullWidth)}else this.topAggregationContainer&&(this.topAggregationContainer.remove(),this.topAggregationContainer=null);const c=this.config.showRowCount!==!1||this.config.showSelectedCount&&i.selectedRows>0||this.config.showFilteredCount&&i.filteredRows!==i.totalRows||this.config.customPanels&&this.config.customPanels.length>0,p=c&&this.config.position!=="top",u=d.length>0||p;if(c&&this.config.position==="top")if(!this.infoBarElement)this.infoBarElement=h(this.config,i),n.insertBefore(this.infoBarElement,n.firstChild);else{const l=h(this.config,i);this.infoBarElement.replaceWith(l),this.infoBarElement=l}else this.config.position==="top"&&this.infoBarElement&&(this.infoBarElement.remove(),this.infoBarElement=null);u?(this.footerWrapper||(this.footerWrapper=document.createElement("div"),this.footerWrapper.className="tbw-footer",n.appendChild(this.footerWrapper)),this.footerWrapper.innerHTML="",d.length>0&&(this.bottomAggregationContainer||(this.bottomAggregationContainer=w("bottom")),this.footerWrapper.appendChild(this.bottomAggregationContainer),b(this.bottomAggregationContainer,d,this.visibleColumns,this.sourceRows,this.config.fullWidth)),p&&(this.infoBarElement=h(this.config,i),this.footerWrapper.appendChild(this.infoBarElement))):this.cleanupFooter()}cleanup(){this.infoBarElement&&(this.infoBarElement.remove(),this.infoBarElement=null),this.topAggregationContainer&&(this.topAggregationContainer.remove(),this.topAggregationContainer=null),this.bottomAggregationContainer&&(this.bottomAggregationContainer.remove(),this.bottomAggregationContainer=null),this.footerWrapper&&(this.footerWrapper.remove(),this.footerWrapper=null)}cleanupFooter(){this.footerWrapper&&(this.footerWrapper.remove(),this.footerWrapper=null),this.bottomAggregationContainer&&(this.bottomAggregationContainer.remove(),this.bottomAggregationContainer=null),this.infoBarElement&&this.config.position!=="top"&&(this.infoBarElement.remove(),this.infoBarElement=null)}getSelectionState(){try{return this.grid?.getPluginState?.("selection")??null}catch{return null}}getFilterState(){try{return this.grid?.getPluginState?.("filtering")??null}catch{return null}}refresh(){this.requestRender()}getContext(){const t=this.getSelectionState(),n=this.getFilterState();return v(this.rows,this.columns,this.grid,t,n)}addPanel(t){this.config.customPanels||(this.config.customPanels=[]),this.config.customPanels.push(t),this.requestRender()}removePanel(t){this.config.customPanels&&(this.config.customPanels=this.config.customPanels.filter(n=>n.id!==t),this.requestRender())}addAggregationRow(t){this.config.aggregationRows||(this.config.aggregationRows=[]),this.config.aggregationRows.push(t),this.requestRender()}removeAggregationRow(t){this.config.aggregationRows&&(this.config.aggregationRows=this.config.aggregationRows.filter(n=>n.id!==t),this.requestRender())}}g.PinnedRowsPlugin=x,Object.defineProperty(g,Symbol.toStringTag,{value:"Module"})}));
|
|
2
2
|
//# sourceMappingURL=pinned-rows.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pinned-rows.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/pinned-rows/pinned-rows.ts","../../../../../libs/grid/src/lib/plugins/pinned-rows/PinnedRowsPlugin.ts"],"sourcesContent":["/**\n * Status Bar Rendering Logic\n *\n * Pure functions for creating and updating the status bar UI.\n * Includes both info bar and aggregation row rendering.\n */\n\nimport { getAggregator } from '../../core/internal/aggregators';\nimport type { ColumnConfig } from '../../core/types';\nimport type {\n AggregationRowConfig,\n AggregatorConfig,\n AggregatorDefinition,\n PinnedRowsConfig,\n PinnedRowsContext,\n PinnedRowsPanel,\n} from './types';\n\n/**\n * Check if an aggregator definition is a full config object (with aggFunc and optional formatter).\n */\nfunction isAggregatorConfig(def: AggregatorDefinition): def is AggregatorConfig {\n return typeof def === 'object' && def !== null && 'aggFunc' in def;\n}\n\n/**\n * Creates the info bar DOM element with all configured panels.\n *\n * @param config - The status bar configuration\n * @param context - The current grid context for rendering\n * @returns The complete info bar element\n */\nexport function createInfoBarElement(config: PinnedRowsConfig, context: PinnedRowsContext): HTMLElement {\n const pinnedRows = document.createElement('div');\n pinnedRows.className = 'tbw-pinned-rows';\n pinnedRows.setAttribute('role', 'presentation');\n pinnedRows.setAttribute('aria-live', 'polite');\n\n const left = document.createElement('div');\n left.className = 'tbw-pinned-rows-left';\n\n const center = document.createElement('div');\n center.className = 'tbw-pinned-rows-center';\n\n const right = document.createElement('div');\n right.className = 'tbw-pinned-rows-right';\n\n // Default panels - row count\n if (config.showRowCount !== false) {\n const rowCount = document.createElement('span');\n rowCount.className = 'tbw-status-panel tbw-status-panel-row-count';\n rowCount.textContent = `Total: ${context.totalRows} rows`;\n left.appendChild(rowCount);\n }\n\n // Filtered count panel (only shows when filter is active)\n if (config.showFilteredCount && context.filteredRows !== context.totalRows) {\n const filteredCount = document.createElement('span');\n filteredCount.className = 'tbw-status-panel tbw-status-panel-filtered-count';\n filteredCount.textContent = `Filtered: ${context.filteredRows}`;\n left.appendChild(filteredCount);\n }\n\n // Selected count panel (only shows when rows are selected)\n if (config.showSelectedCount && context.selectedRows > 0) {\n const selectedCount = document.createElement('span');\n selectedCount.className = 'tbw-status-panel tbw-status-panel-selected-count';\n selectedCount.textContent = `Selected: ${context.selectedRows}`;\n right.appendChild(selectedCount);\n }\n\n // Render custom panels\n if (config.customPanels) {\n for (const panel of config.customPanels) {\n const panelEl = renderCustomPanel(panel, context);\n switch (panel.position) {\n case 'left':\n left.appendChild(panelEl);\n break;\n case 'center':\n center.appendChild(panelEl);\n break;\n case 'right':\n right.appendChild(panelEl);\n break;\n }\n }\n }\n\n pinnedRows.appendChild(left);\n pinnedRows.appendChild(center);\n pinnedRows.appendChild(right);\n\n return pinnedRows;\n}\n\n/**\n * Creates a container for aggregation rows at top or bottom.\n *\n * @param position - 'top' or 'bottom'\n * @returns The container element\n */\nexport function createAggregationContainer(position: 'top' | 'bottom'): HTMLElement {\n const container = document.createElement('div');\n container.className = `tbw-aggregation-rows tbw-aggregation-rows-${position}`;\n // Use presentation role since aggregation rows are outside the role=\"grid\" element for layout reasons\n container.setAttribute('role', 'presentation');\n return container;\n}\n\n/**\n * Renders aggregation rows into a container.\n *\n * @param container - The container to render into\n * @param rows - Aggregation row configurations\n * @param columns - Current column configuration\n * @param dataRows - Current row data for aggregation calculations\n */\nexport function renderAggregationRows(\n container: HTMLElement,\n rows: AggregationRowConfig[],\n columns: ColumnConfig[],\n dataRows: unknown[],\n): void {\n container.innerHTML = '';\n\n for (const rowConfig of rows) {\n const rowEl = document.createElement('div');\n rowEl.className = 'tbw-aggregation-row';\n // Use presentation role since aggregation rows are outside the role=\"grid\" element\n rowEl.setAttribute('role', 'presentation');\n if (rowConfig.id) {\n rowEl.setAttribute('data-aggregation-id', rowConfig.id);\n }\n\n if (rowConfig.fullWidth) {\n // Full-width mode: single cell spanning all columns\n const cell = document.createElement('div');\n cell.className = 'tbw-aggregation-cell tbw-aggregation-cell-full';\n cell.style.gridColumn = '1 / -1';\n cell.textContent = rowConfig.label || '';\n rowEl.appendChild(cell);\n } else {\n // Per-column mode: one cell per column with aggregated/static values\n for (const col of columns) {\n const cell = document.createElement('div');\n cell.className = 'tbw-aggregation-cell';\n cell.setAttribute('data-field', col.field);\n\n let value: unknown;\n let formatter: ((value: unknown, field: string, column?: ColumnConfig) => string) | undefined;\n\n // Check for aggregator first\n const aggDef = rowConfig.aggregators?.[col.field];\n if (aggDef) {\n // Handle both simple ref and full config object\n if (isAggregatorConfig(aggDef)) {\n const aggFn = getAggregator(aggDef.aggFunc);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\n formatter = aggDef.formatter;\n } else {\n const aggFn = getAggregator(aggDef);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\n }\n } else if (rowConfig.cells && Object.prototype.hasOwnProperty.call(rowConfig.cells, col.field)) {\n // Static or computed cell value\n const staticVal = rowConfig.cells[col.field];\n if (typeof staticVal === 'function') {\n value = staticVal(dataRows, col.field, col);\n } else {\n value = staticVal;\n }\n }\n\n // Apply formatter if provided, otherwise convert to string\n if (value != null) {\n cell.textContent = formatter ? formatter(value, col.field, col) : String(value);\n } else {\n cell.textContent = '';\n }\n rowEl.appendChild(cell);\n }\n }\n\n container.appendChild(rowEl);\n }\n}\n\n/**\n * Renders a custom panel element.\n *\n * @param panel - The panel definition\n * @param context - The current grid context\n * @returns The panel DOM element\n */\nfunction renderCustomPanel(panel: PinnedRowsPanel, context: PinnedRowsContext): HTMLElement {\n const panelEl = document.createElement('div');\n panelEl.className = 'tbw-status-panel tbw-status-panel-custom';\n panelEl.id = `status-panel-${panel.id}`;\n\n const content = panel.render(context);\n\n if (typeof content === 'string') {\n panelEl.innerHTML = content;\n } else {\n panelEl.appendChild(content);\n }\n\n return panelEl;\n}\n\n/**\n * Builds the status bar context from grid state and plugin states.\n *\n * @param rows - Current row data\n * @param columns - Current column configuration\n * @param grid - Grid element reference\n * @param selectionState - Optional selection plugin state\n * @param filterState - Optional filtering plugin state\n * @returns The status bar context\n */\nexport function buildContext(\n rows: unknown[],\n columns: unknown[],\n grid: HTMLElement,\n selectionState?: { selected: Set<number> } | null,\n filterState?: { cachedResult: unknown[] | null } | null,\n): PinnedRowsContext {\n return {\n totalRows: rows.length,\n filteredRows: filterState?.cachedResult?.length ?? rows.length,\n selectedRows: selectionState?.selected?.size ?? 0,\n columns: columns as PinnedRowsContext['columns'],\n rows,\n grid,\n };\n}\n\n// Keep old name as alias for backwards compatibility\nexport const createPinnedRowsElement = createInfoBarElement;\n","/**\n * Pinned Rows Plugin (Class-based)\n *\n * Adds info bars and aggregation rows to the grid.\n * - Info bar: Shows row counts, selection info, and custom panels\n * - Aggregation rows: Footer/header rows with computed values (sum, avg, etc.)\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildContext, createAggregationContainer, createInfoBarElement, renderAggregationRows } from './pinned-rows';\nimport styles from './pinned-rows.css?inline';\nimport type { AggregationRowConfig, PinnedRowsConfig, PinnedRowsContext, PinnedRowsPanel } from './types';\n\n/**\n * Pinned Rows (Status Bar) Plugin for tbw-grid\n *\n * Creates fixed status bars at the top or bottom of the grid for displaying aggregations,\n * row counts, or custom content. Think of it as the \"totals row\" you'd see in a spreadsheet—\n * always visible regardless of scroll position.\n *\n * ## Installation\n *\n * ```ts\n * import { PinnedRowsPlugin } from '@toolbox-web/grid/plugins/pinned-rows';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `position` | `'top' \\| 'bottom'` | `'bottom'` | Status bar position |\n * | `showRowCount` | `boolean` | `true` | Show total row count |\n * | `showSelectedCount` | `boolean` | `true` | Show selected row count |\n * | `showFilteredCount` | `boolean` | `true` | Show filtered row count |\n * | `aggregationRows` | `AggregationRowConfig[]` | - | Aggregation row configs |\n *\n * ## Built-in Aggregation Functions\n *\n * | Function | Description |\n * |----------|-------------|\n * | `sum` | Sum of values |\n * | `avg` | Average of values |\n * | `count` | Count of rows |\n * | `min` | Minimum value |\n * | `max` | Maximum value |\n *\n * ## CSS Custom Properties\n *\n * | Property | Default | Description |\n * |----------|---------|-------------|\n * | `--tbw-pinned-rows-bg` | `var(--tbw-color-panel-bg)` | Status bar background |\n * | `--tbw-pinned-rows-border` | `var(--tbw-color-border)` | Status bar border |\n *\n * @example Status Bar with Aggregation\n * ```ts\n * import '@toolbox-web/grid';\n * import { PinnedRowsPlugin } from '@toolbox-web/grid/plugins/pinned-rows';\n *\n * grid.gridConfig = {\n * columns: [\n * { field: 'product', header: 'Product' },\n * { field: 'quantity', header: 'Qty', type: 'number' },\n * { field: 'price', header: 'Price', type: 'currency' },\n * ],\n * plugins: [\n * new PinnedRowsPlugin({\n * position: 'bottom',\n * showRowCount: true,\n * aggregationRows: [\n * {\n * id: 'totals',\n * aggregators: { quantity: 'sum', price: 'sum' },\n * cells: { product: 'Totals:' },\n * },\n * ],\n * }),\n * ],\n * };\n * ```\n *\n * @see {@link PinnedRowsConfig} for all configuration options\n * @see {@link AggregationRowConfig} for aggregation row structure\n *\n * @internal Extends BaseGridPlugin\n */\nexport class PinnedRowsPlugin extends BaseGridPlugin<PinnedRowsConfig> {\n /** @internal */\n readonly name = 'pinnedRows';\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n protected override get defaultConfig(): Partial<PinnedRowsConfig> {\n return {\n position: 'bottom',\n showRowCount: true,\n showSelectedCount: true,\n showFilteredCount: true,\n };\n }\n\n // #region Internal State\n private infoBarElement: HTMLElement | null = null;\n private topAggregationContainer: HTMLElement | null = null;\n private bottomAggregationContainer: HTMLElement | null = null;\n private footerWrapper: HTMLElement | null = null;\n // #endregion\n\n // #region Lifecycle\n /** @internal */\n override detach(): void {\n if (this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n }\n // #endregion\n\n // #region Hooks\n /** @internal */\n override afterRender(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n // Use .tbw-scroll-area so footer is inside the horizontal scroll area,\n // otherwise fall back to .tbw-grid-content or root container\n const container =\n gridEl.querySelector('.tbw-scroll-area') ?? gridEl.querySelector('.tbw-grid-content') ?? gridEl.children[0];\n if (!container) return;\n\n // Clear orphaned element references if they were removed from the DOM\n // (e.g., by buildGridDOMIntoShadow calling replaceChildren())\n // We check if the element is still inside the container rather than isConnected,\n // because in unit tests the mock grid may not be attached to document.body\n if (this.footerWrapper && !container.contains(this.footerWrapper)) {\n this.footerWrapper = null;\n this.bottomAggregationContainer = null;\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer && !container.contains(this.topAggregationContainer)) {\n this.topAggregationContainer = null;\n }\n if (this.infoBarElement && !container.contains(this.infoBarElement)) {\n this.infoBarElement = null;\n }\n\n // Build context with plugin states\n const selectionState = this.getSelectionState();\n const filterState = this.getFilterState();\n\n const context = buildContext(\n this.sourceRows as unknown[],\n this.columns as unknown[],\n this.grid as unknown as HTMLElement,\n selectionState,\n filterState,\n );\n\n // #region Handle Aggregation Rows\n const aggregationRows = this.config.aggregationRows || [];\n const topRows = aggregationRows.filter((r) => r.position === 'top');\n const bottomRows = aggregationRows.filter((r) => r.position !== 'top');\n\n // Top aggregation rows\n if (topRows.length > 0) {\n if (!this.topAggregationContainer) {\n this.topAggregationContainer = createAggregationContainer('top');\n const header = gridEl.querySelector('.header');\n if (header && header.nextSibling) {\n container.insertBefore(this.topAggregationContainer, header.nextSibling);\n } else {\n container.appendChild(this.topAggregationContainer);\n }\n }\n renderAggregationRows(\n this.topAggregationContainer,\n topRows,\n this.visibleColumns as ColumnConfig[],\n this.sourceRows as unknown[],\n );\n } else if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n\n // Handle footer\n const hasInfoContent =\n this.config.showRowCount !== false ||\n (this.config.showSelectedCount && context.selectedRows > 0) ||\n (this.config.showFilteredCount && context.filteredRows !== context.totalRows) ||\n (this.config.customPanels && this.config.customPanels.length > 0);\n const hasBottomInfoBar = hasInfoContent && this.config.position !== 'top';\n const needsFooter = bottomRows.length > 0 || hasBottomInfoBar;\n\n // Handle top info bar\n if (hasInfoContent && this.config.position === 'top') {\n if (!this.infoBarElement) {\n this.infoBarElement = createInfoBarElement(this.config, context);\n container.insertBefore(this.infoBarElement, container.firstChild);\n } else {\n const newInfoBar = createInfoBarElement(this.config, context);\n this.infoBarElement.replaceWith(newInfoBar);\n this.infoBarElement = newInfoBar;\n }\n } else if (this.config.position === 'top' && this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n\n // Create/manage footer wrapper\n if (needsFooter) {\n if (!this.footerWrapper) {\n this.footerWrapper = document.createElement('div');\n this.footerWrapper.className = 'tbw-footer';\n container.appendChild(this.footerWrapper);\n }\n\n this.footerWrapper.innerHTML = '';\n\n if (bottomRows.length > 0) {\n if (!this.bottomAggregationContainer) {\n this.bottomAggregationContainer = createAggregationContainer('bottom');\n }\n this.footerWrapper.appendChild(this.bottomAggregationContainer);\n renderAggregationRows(\n this.bottomAggregationContainer,\n bottomRows,\n this.visibleColumns as ColumnConfig[],\n this.sourceRows as unknown[],\n );\n }\n\n if (hasBottomInfoBar) {\n this.infoBarElement = createInfoBarElement(this.config, context);\n this.footerWrapper.appendChild(this.infoBarElement);\n }\n } else {\n this.cleanupFooter();\n }\n // #endregion\n }\n // #endregion\n\n // #region Private Methods\n private cleanup(): void {\n if (this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n }\n\n private cleanupFooter(): void {\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.infoBarElement && this.config.position !== 'top') {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n }\n\n private getSelectionState(): { selected: Set<number> } | null {\n // Try to get selection plugin state\n try {\n return (this.grid?.getPluginState?.('selection') as { selected: Set<number> } | null) ?? null;\n } catch {\n return null;\n }\n }\n\n private getFilterState(): { cachedResult: unknown[] | null } | null {\n try {\n return (this.grid?.getPluginState?.('filtering') as { cachedResult: unknown[] | null } | null) ?? null;\n } catch {\n return null;\n }\n }\n // #endregion\n\n // #region Public API\n /**\n * Refresh the status bar to reflect current grid state.\n */\n refresh(): void {\n this.requestRender();\n }\n\n /**\n * Get the current status bar context.\n * @returns The context with row counts and other info\n */\n getContext(): PinnedRowsContext {\n const selectionState = this.getSelectionState();\n const filterState = this.getFilterState();\n\n return buildContext(\n this.rows as unknown[],\n this.columns as unknown[],\n this.grid as unknown as HTMLElement,\n selectionState,\n filterState,\n );\n }\n\n /**\n * Add a custom panel to the info bar.\n * @param panel - The panel configuration to add\n */\n addPanel(panel: PinnedRowsPanel): void {\n if (!this.config.customPanels) {\n this.config.customPanels = [];\n }\n this.config.customPanels.push(panel);\n this.requestRender();\n }\n\n /**\n * Remove a custom panel by ID.\n * @param id - The panel ID to remove\n */\n removePanel(id: string): void {\n if (this.config.customPanels) {\n this.config.customPanels = this.config.customPanels.filter((p) => p.id !== id);\n this.requestRender();\n }\n }\n\n /**\n * Add an aggregation row.\n * @param row - The aggregation row configuration\n */\n addAggregationRow(row: AggregationRowConfig): void {\n if (!this.config.aggregationRows) {\n this.config.aggregationRows = [];\n }\n this.config.aggregationRows.push(row);\n this.requestRender();\n }\n\n /**\n * Remove an aggregation row by ID.\n * @param id - The aggregation row ID to remove\n */\n removeAggregationRow(id: string): void {\n if (this.config.aggregationRows) {\n this.config.aggregationRows = this.config.aggregationRows.filter((r) => r.id !== id);\n this.requestRender();\n }\n }\n // #endregion\n}\n"],"names":["isAggregatorConfig","def","createInfoBarElement","config","context","pinnedRows","left","center","right","rowCount","filteredCount","selectedCount","panel","panelEl","renderCustomPanel","createAggregationContainer","position","container","renderAggregationRows","rows","columns","dataRows","rowConfig","rowEl","cell","col","value","formatter","aggDef","aggFn","getAggregator","staticVal","content","buildContext","grid","selectionState","filterState","PinnedRowsPlugin","BaseGridPlugin","styles","gridEl","aggregationRows","topRows","r","bottomRows","header","hasInfoContent","hasBottomInfoBar","needsFooter","newInfoBar","id","p","row"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"pinned-rows.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/pinned-rows/pinned-rows.ts","../../../../../libs/grid/src/lib/plugins/pinned-rows/PinnedRowsPlugin.ts"],"sourcesContent":["/**\n * Status Bar Rendering Logic\n *\n * Pure functions for creating and updating the status bar UI.\n * Includes both info bar and aggregation row rendering.\n */\n\nimport { getAggregator } from '../../core/internal/aggregators';\nimport type { ColumnConfig } from '../../core/types';\nimport type {\n AggregationRowConfig,\n AggregatorConfig,\n AggregatorDefinition,\n PinnedRowsConfig,\n PinnedRowsContext,\n PinnedRowsPanel,\n} from './types';\n\n/**\n * Check if an aggregator definition is a full config object (with aggFunc and optional formatter).\n */\nfunction isAggregatorConfig(def: AggregatorDefinition): def is AggregatorConfig {\n return typeof def === 'object' && def !== null && 'aggFunc' in def;\n}\n\n/**\n * Creates the info bar DOM element with all configured panels.\n *\n * @param config - The status bar configuration\n * @param context - The current grid context for rendering\n * @returns The complete info bar element\n */\nexport function createInfoBarElement(config: PinnedRowsConfig, context: PinnedRowsContext): HTMLElement {\n const pinnedRows = document.createElement('div');\n pinnedRows.className = 'tbw-pinned-rows';\n pinnedRows.setAttribute('role', 'presentation');\n pinnedRows.setAttribute('aria-live', 'polite');\n\n const left = document.createElement('div');\n left.className = 'tbw-pinned-rows-left';\n\n const center = document.createElement('div');\n center.className = 'tbw-pinned-rows-center';\n\n const right = document.createElement('div');\n right.className = 'tbw-pinned-rows-right';\n\n // Default panels - row count\n if (config.showRowCount !== false) {\n const rowCount = document.createElement('span');\n rowCount.className = 'tbw-status-panel tbw-status-panel-row-count';\n rowCount.textContent = `Total: ${context.totalRows} rows`;\n left.appendChild(rowCount);\n }\n\n // Filtered count panel (only shows when filter is active)\n if (config.showFilteredCount && context.filteredRows !== context.totalRows) {\n const filteredCount = document.createElement('span');\n filteredCount.className = 'tbw-status-panel tbw-status-panel-filtered-count';\n filteredCount.textContent = `Filtered: ${context.filteredRows}`;\n left.appendChild(filteredCount);\n }\n\n // Selected count panel (only shows when rows are selected)\n if (config.showSelectedCount && context.selectedRows > 0) {\n const selectedCount = document.createElement('span');\n selectedCount.className = 'tbw-status-panel tbw-status-panel-selected-count';\n selectedCount.textContent = `Selected: ${context.selectedRows}`;\n right.appendChild(selectedCount);\n }\n\n // Render custom panels\n if (config.customPanels) {\n for (const panel of config.customPanels) {\n const panelEl = renderCustomPanel(panel, context);\n switch (panel.position) {\n case 'left':\n left.appendChild(panelEl);\n break;\n case 'center':\n center.appendChild(panelEl);\n break;\n case 'right':\n right.appendChild(panelEl);\n break;\n }\n }\n }\n\n pinnedRows.appendChild(left);\n pinnedRows.appendChild(center);\n pinnedRows.appendChild(right);\n\n return pinnedRows;\n}\n\n/**\n * Creates a container for aggregation rows at top or bottom.\n *\n * @param position - 'top' or 'bottom'\n * @returns The container element\n */\nexport function createAggregationContainer(position: 'top' | 'bottom'): HTMLElement {\n const container = document.createElement('div');\n container.className = `tbw-aggregation-rows tbw-aggregation-rows-${position}`;\n // Use presentation role since aggregation rows are outside the role=\"grid\" element for layout reasons\n container.setAttribute('role', 'presentation');\n return container;\n}\n\n/**\n * Renders aggregation rows into a container.\n *\n * @param container - The container to render into\n * @param rows - Aggregation row configurations\n * @param columns - Current column configuration\n * @param dataRows - Current row data for aggregation calculations\n * @param globalFullWidth - Global fullWidth default from PinnedRowsConfig (default: false)\n */\nexport function renderAggregationRows(\n container: HTMLElement,\n rows: AggregationRowConfig[],\n columns: ColumnConfig[],\n dataRows: unknown[],\n globalFullWidth = false,\n): void {\n container.innerHTML = '';\n\n for (const rowConfig of rows) {\n const rowEl = document.createElement('div');\n rowEl.className = 'tbw-aggregation-row';\n // Use presentation role since aggregation rows are outside the role=\"grid\" element\n rowEl.setAttribute('role', 'presentation');\n if (rowConfig.id) {\n rowEl.setAttribute('data-aggregation-id', rowConfig.id);\n }\n\n // Per-row fullWidth overrides global default\n const isFullWidth = rowConfig.fullWidth ?? globalFullWidth;\n\n if (isFullWidth) {\n renderFullWidthAggregationRow(rowEl, rowConfig, columns, dataRows);\n } else {\n renderPerColumnAggregationRow(rowEl, rowConfig, columns, dataRows);\n }\n\n container.appendChild(rowEl);\n }\n}\n\n/**\n * Renders a full-width aggregation row: single spanning cell with label and inline aggregated values.\n */\nfunction renderFullWidthAggregationRow(\n rowEl: HTMLElement,\n rowConfig: AggregationRowConfig,\n columns: ColumnConfig[],\n dataRows: unknown[],\n): void {\n const cell = document.createElement('div');\n cell.className = 'tbw-aggregation-cell tbw-aggregation-cell-full';\n cell.style.gridColumn = '1 / -1';\n\n // Label (static string or dynamic function)\n const labelValue = typeof rowConfig.label === 'function' ? rowConfig.label(dataRows, columns) : rowConfig.label;\n if (labelValue) {\n const labelSpan = document.createElement('span');\n labelSpan.className = 'tbw-aggregation-label';\n labelSpan.textContent = labelValue;\n cell.appendChild(labelSpan);\n }\n\n // Inline aggregated values\n const aggregatesContainer = renderInlineAggregates(rowConfig, columns, dataRows);\n if (aggregatesContainer) {\n cell.appendChild(aggregatesContainer);\n }\n\n // If nothing was added (no label, no aggregates), ensure cell is empty but present\n rowEl.appendChild(cell);\n}\n\n/**\n * Renders per-column aggregation cells aligned to the grid template.\n */\nfunction renderPerColumnAggregationRow(\n rowEl: HTMLElement,\n rowConfig: AggregationRowConfig,\n columns: ColumnConfig[],\n dataRows: unknown[],\n): void {\n for (const col of columns) {\n const cell = document.createElement('div');\n cell.className = 'tbw-aggregation-cell';\n cell.setAttribute('data-field', col.field);\n\n const { value, formatter } = resolveAggregatedValue(rowConfig, col, dataRows);\n\n if (value != null) {\n cell.textContent = formatter ? formatter(value, col.field, col) : String(value);\n } else {\n cell.textContent = '';\n }\n rowEl.appendChild(cell);\n }\n}\n\n/**\n * Resolves the aggregated value for a single column in an aggregation row.\n * Returns the computed value and an optional formatter function.\n */\nfunction resolveAggregatedValue(\n rowConfig: AggregationRowConfig,\n col: ColumnConfig,\n dataRows: unknown[],\n): { value: unknown; formatter?: (value: unknown, field: string, column?: ColumnConfig) => string } {\n let value: unknown;\n let formatter: ((value: unknown, field: string, column?: ColumnConfig) => string) | undefined;\n\n // Check for aggregator first\n const aggDef = rowConfig.aggregators?.[col.field];\n if (aggDef) {\n if (isAggregatorConfig(aggDef)) {\n const aggFn = getAggregator(aggDef.aggFunc);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\n formatter = aggDef.formatter;\n } else {\n const aggFn = getAggregator(aggDef);\n if (aggFn) {\n value = aggFn(dataRows, col.field, col);\n }\n }\n } else if (rowConfig.cells && Object.prototype.hasOwnProperty.call(rowConfig.cells, col.field)) {\n const staticVal = rowConfig.cells[col.field];\n if (typeof staticVal === 'function') {\n value = staticVal(dataRows, col.field, col);\n } else {\n value = staticVal;\n }\n }\n\n return { value, formatter };\n}\n\n/**\n * Renders inline aggregate values for a full-width aggregation row.\n * Returns a container element with aggregate spans, or null if no aggregates are defined.\n */\nfunction renderInlineAggregates(\n rowConfig: AggregationRowConfig,\n columns: ColumnConfig[],\n dataRows: unknown[],\n): HTMLElement | null {\n // Collect fields that have aggregators or cell values\n const hasAggregators = rowConfig.aggregators && Object.keys(rowConfig.aggregators).length > 0;\n const hasCells = rowConfig.cells && Object.keys(rowConfig.cells).length > 0;\n if (!hasAggregators && !hasCells) return null;\n\n const container = document.createElement('span');\n container.className = 'tbw-aggregation-aggregates';\n\n for (const col of columns) {\n const { value, formatter } = resolveAggregatedValue(rowConfig, col, dataRows);\n if (value != null) {\n const span = document.createElement('span');\n span.className = 'tbw-aggregation-aggregate';\n span.setAttribute('data-field', col.field);\n const header = col.header ?? col.field;\n const displayValue = formatter ? formatter(value, col.field, col) : String(value);\n span.textContent = `${header}: ${displayValue}`;\n container.appendChild(span);\n }\n }\n\n return container.children.length > 0 ? container : null;\n}\n\n/**\n * Renders a custom panel element.\n *\n * @param panel - The panel definition\n * @param context - The current grid context\n * @returns The panel DOM element\n */\nfunction renderCustomPanel(panel: PinnedRowsPanel, context: PinnedRowsContext): HTMLElement {\n const panelEl = document.createElement('div');\n panelEl.className = 'tbw-status-panel tbw-status-panel-custom';\n panelEl.id = `status-panel-${panel.id}`;\n\n const content = panel.render(context);\n\n if (typeof content === 'string') {\n panelEl.innerHTML = content;\n } else {\n panelEl.appendChild(content);\n }\n\n return panelEl;\n}\n\n/**\n * Builds the status bar context from grid state and plugin states.\n *\n * @param rows - Current row data\n * @param columns - Current column configuration\n * @param grid - Grid element reference\n * @param selectionState - Optional selection plugin state\n * @param filterState - Optional filtering plugin state\n * @returns The status bar context\n */\nexport function buildContext(\n rows: unknown[],\n columns: unknown[],\n grid: HTMLElement,\n selectionState?: { selected: Set<number> } | null,\n filterState?: { cachedResult: unknown[] | null } | null,\n): PinnedRowsContext {\n return {\n totalRows: rows.length,\n filteredRows: filterState?.cachedResult?.length ?? rows.length,\n selectedRows: selectionState?.selected?.size ?? 0,\n columns: columns as PinnedRowsContext['columns'],\n rows,\n grid,\n };\n}\n\n// Keep old name as alias for backwards compatibility\nexport const createPinnedRowsElement = createInfoBarElement;\n","/**\n * Pinned Rows Plugin (Class-based)\n *\n * Adds info bars and aggregation rows to the grid.\n * - Info bar: Shows row counts, selection info, and custom panels\n * - Aggregation rows: Footer/header rows with computed values (sum, avg, etc.)\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildContext, createAggregationContainer, createInfoBarElement, renderAggregationRows } from './pinned-rows';\nimport styles from './pinned-rows.css?inline';\nimport type { AggregationRowConfig, PinnedRowsConfig, PinnedRowsContext, PinnedRowsPanel } from './types';\n\n/**\n * Pinned Rows (Status Bar) Plugin for tbw-grid\n *\n * Creates fixed status bars at the top or bottom of the grid for displaying aggregations,\n * row counts, or custom content. Think of it as the \"totals row\" you'd see in a spreadsheet—\n * always visible regardless of scroll position.\n *\n * ## Installation\n *\n * ```ts\n * import { PinnedRowsPlugin } from '@toolbox-web/grid/plugins/pinned-rows';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `position` | `'top' \\| 'bottom'` | `'bottom'` | Status bar position |\n * | `showRowCount` | `boolean` | `true` | Show total row count |\n * | `showSelectedCount` | `boolean` | `true` | Show selected row count |\n * | `showFilteredCount` | `boolean` | `true` | Show filtered row count |\n * | `fullWidth` | `boolean` | `false` | Default fullWidth for aggregation rows |\n * | `aggregationRows` | `AggregationRowConfig[]` | - | Aggregation row configs |\n *\n * ## Built-in Aggregation Functions\n *\n * | Function | Description |\n * |----------|-------------|\n * | `sum` | Sum of values |\n * | `avg` | Average of values |\n * | `count` | Count of rows |\n * | `min` | Minimum value |\n * | `max` | Maximum value |\n *\n * ## CSS Custom Properties\n *\n * | Property | Default | Description |\n * |----------|---------|-------------|\n * | `--tbw-pinned-rows-bg` | `var(--tbw-color-panel-bg)` | Status bar background |\n * | `--tbw-pinned-rows-border` | `var(--tbw-color-border)` | Status bar border |\n *\n * @example Status Bar with Aggregation\n * ```ts\n * import '@toolbox-web/grid';\n * import { PinnedRowsPlugin } from '@toolbox-web/grid/plugins/pinned-rows';\n *\n * grid.gridConfig = {\n * columns: [\n * { field: 'product', header: 'Product' },\n * { field: 'quantity', header: 'Qty', type: 'number' },\n * { field: 'price', header: 'Price', type: 'currency' },\n * ],\n * plugins: [\n * new PinnedRowsPlugin({\n * position: 'bottom',\n * showRowCount: true,\n * aggregationRows: [\n * {\n * id: 'totals',\n * aggregators: { quantity: 'sum', price: 'sum' },\n * cells: { product: 'Totals:' },\n * },\n * ],\n * }),\n * ],\n * };\n * ```\n *\n * @see {@link PinnedRowsConfig} for all configuration options\n * @see {@link AggregationRowConfig} for aggregation row structure\n *\n * @internal Extends BaseGridPlugin\n */\nexport class PinnedRowsPlugin extends BaseGridPlugin<PinnedRowsConfig> {\n /** @internal */\n readonly name = 'pinnedRows';\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n protected override get defaultConfig(): Partial<PinnedRowsConfig> {\n return {\n position: 'bottom',\n showRowCount: true,\n showSelectedCount: true,\n showFilteredCount: true,\n };\n }\n\n // #region Internal State\n private infoBarElement: HTMLElement | null = null;\n private topAggregationContainer: HTMLElement | null = null;\n private bottomAggregationContainer: HTMLElement | null = null;\n private footerWrapper: HTMLElement | null = null;\n // #endregion\n\n // #region Lifecycle\n /** @internal */\n override detach(): void {\n if (this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n }\n // #endregion\n\n // #region Hooks\n /** @internal */\n override afterRender(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n // Use .tbw-scroll-area so footer is inside the horizontal scroll area,\n // otherwise fall back to .tbw-grid-content or root container\n const container =\n gridEl.querySelector('.tbw-scroll-area') ?? gridEl.querySelector('.tbw-grid-content') ?? gridEl.children[0];\n if (!container) return;\n\n // Clear orphaned element references if they were removed from the DOM\n // (e.g., by buildGridDOMIntoShadow calling replaceChildren())\n // We check if the element is still inside the container rather than isConnected,\n // because in unit tests the mock grid may not be attached to document.body\n if (this.footerWrapper && !container.contains(this.footerWrapper)) {\n this.footerWrapper = null;\n this.bottomAggregationContainer = null;\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer && !container.contains(this.topAggregationContainer)) {\n this.topAggregationContainer = null;\n }\n if (this.infoBarElement && !container.contains(this.infoBarElement)) {\n this.infoBarElement = null;\n }\n\n // Build context with plugin states\n const selectionState = this.getSelectionState();\n const filterState = this.getFilterState();\n\n const context = buildContext(\n this.sourceRows as unknown[],\n this.columns as unknown[],\n this.grid as unknown as HTMLElement,\n selectionState,\n filterState,\n );\n\n // #region Handle Aggregation Rows\n const aggregationRows = this.config.aggregationRows || [];\n const topRows = aggregationRows.filter((r) => r.position === 'top');\n const bottomRows = aggregationRows.filter((r) => r.position !== 'top');\n\n // Top aggregation rows\n if (topRows.length > 0) {\n if (!this.topAggregationContainer) {\n this.topAggregationContainer = createAggregationContainer('top');\n const header = gridEl.querySelector('.header');\n if (header && header.nextSibling) {\n container.insertBefore(this.topAggregationContainer, header.nextSibling);\n } else {\n container.appendChild(this.topAggregationContainer);\n }\n }\n renderAggregationRows(\n this.topAggregationContainer,\n topRows,\n this.visibleColumns as ColumnConfig[],\n this.sourceRows as unknown[],\n this.config.fullWidth,\n );\n } else if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n\n // Handle footer\n const hasInfoContent =\n this.config.showRowCount !== false ||\n (this.config.showSelectedCount && context.selectedRows > 0) ||\n (this.config.showFilteredCount && context.filteredRows !== context.totalRows) ||\n (this.config.customPanels && this.config.customPanels.length > 0);\n const hasBottomInfoBar = hasInfoContent && this.config.position !== 'top';\n const needsFooter = bottomRows.length > 0 || hasBottomInfoBar;\n\n // Handle top info bar\n if (hasInfoContent && this.config.position === 'top') {\n if (!this.infoBarElement) {\n this.infoBarElement = createInfoBarElement(this.config, context);\n container.insertBefore(this.infoBarElement, container.firstChild);\n } else {\n const newInfoBar = createInfoBarElement(this.config, context);\n this.infoBarElement.replaceWith(newInfoBar);\n this.infoBarElement = newInfoBar;\n }\n } else if (this.config.position === 'top' && this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n\n // Create/manage footer wrapper\n if (needsFooter) {\n if (!this.footerWrapper) {\n this.footerWrapper = document.createElement('div');\n this.footerWrapper.className = 'tbw-footer';\n container.appendChild(this.footerWrapper);\n }\n\n this.footerWrapper.innerHTML = '';\n\n if (bottomRows.length > 0) {\n if (!this.bottomAggregationContainer) {\n this.bottomAggregationContainer = createAggregationContainer('bottom');\n }\n this.footerWrapper.appendChild(this.bottomAggregationContainer);\n renderAggregationRows(\n this.bottomAggregationContainer,\n bottomRows,\n this.visibleColumns as ColumnConfig[],\n this.sourceRows as unknown[],\n this.config.fullWidth,\n );\n }\n\n if (hasBottomInfoBar) {\n this.infoBarElement = createInfoBarElement(this.config, context);\n this.footerWrapper.appendChild(this.infoBarElement);\n }\n } else {\n this.cleanupFooter();\n }\n // #endregion\n }\n // #endregion\n\n // #region Private Methods\n private cleanup(): void {\n if (this.infoBarElement) {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n if (this.topAggregationContainer) {\n this.topAggregationContainer.remove();\n this.topAggregationContainer = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n }\n\n private cleanupFooter(): void {\n if (this.footerWrapper) {\n this.footerWrapper.remove();\n this.footerWrapper = null;\n }\n if (this.bottomAggregationContainer) {\n this.bottomAggregationContainer.remove();\n this.bottomAggregationContainer = null;\n }\n if (this.infoBarElement && this.config.position !== 'top') {\n this.infoBarElement.remove();\n this.infoBarElement = null;\n }\n }\n\n private getSelectionState(): { selected: Set<number> } | null {\n // Try to get selection plugin state\n try {\n return (this.grid?.getPluginState?.('selection') as { selected: Set<number> } | null) ?? null;\n } catch {\n return null;\n }\n }\n\n private getFilterState(): { cachedResult: unknown[] | null } | null {\n try {\n return (this.grid?.getPluginState?.('filtering') as { cachedResult: unknown[] | null } | null) ?? null;\n } catch {\n return null;\n }\n }\n // #endregion\n\n // #region Public API\n /**\n * Refresh the status bar to reflect current grid state.\n */\n refresh(): void {\n this.requestRender();\n }\n\n /**\n * Get the current status bar context.\n * @returns The context with row counts and other info\n */\n getContext(): PinnedRowsContext {\n const selectionState = this.getSelectionState();\n const filterState = this.getFilterState();\n\n return buildContext(\n this.rows as unknown[],\n this.columns as unknown[],\n this.grid as unknown as HTMLElement,\n selectionState,\n filterState,\n );\n }\n\n /**\n * Add a custom panel to the info bar.\n * @param panel - The panel configuration to add\n */\n addPanel(panel: PinnedRowsPanel): void {\n if (!this.config.customPanels) {\n this.config.customPanels = [];\n }\n this.config.customPanels.push(panel);\n this.requestRender();\n }\n\n /**\n * Remove a custom panel by ID.\n * @param id - The panel ID to remove\n */\n removePanel(id: string): void {\n if (this.config.customPanels) {\n this.config.customPanels = this.config.customPanels.filter((p) => p.id !== id);\n this.requestRender();\n }\n }\n\n /**\n * Add an aggregation row.\n * @param row - The aggregation row configuration\n */\n addAggregationRow(row: AggregationRowConfig): void {\n if (!this.config.aggregationRows) {\n this.config.aggregationRows = [];\n }\n this.config.aggregationRows.push(row);\n this.requestRender();\n }\n\n /**\n * Remove an aggregation row by ID.\n * @param id - The aggregation row ID to remove\n */\n removeAggregationRow(id: string): void {\n if (this.config.aggregationRows) {\n this.config.aggregationRows = this.config.aggregationRows.filter((r) => r.id !== id);\n this.requestRender();\n }\n }\n // #endregion\n}\n"],"names":["isAggregatorConfig","def","createInfoBarElement","config","context","pinnedRows","left","center","right","rowCount","filteredCount","selectedCount","panel","panelEl","renderCustomPanel","createAggregationContainer","position","container","renderAggregationRows","rows","columns","dataRows","globalFullWidth","rowConfig","rowEl","renderFullWidthAggregationRow","renderPerColumnAggregationRow","cell","labelValue","labelSpan","aggregatesContainer","renderInlineAggregates","col","value","formatter","resolveAggregatedValue","aggDef","aggFn","getAggregator","staticVal","hasAggregators","hasCells","span","header","displayValue","content","buildContext","grid","selectionState","filterState","PinnedRowsPlugin","BaseGridPlugin","styles","gridEl","aggregationRows","topRows","r","bottomRows","hasInfoContent","hasBottomInfoBar","needsFooter","newInfoBar","id","p","row"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(u,
|
|
1
|
+
(function(u,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("../../core/internal/utils"),require("../../core/plugin/base-plugin"),require("../../core/plugin/expander-column")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/utils","../../core/plugin/base-plugin","../../core/plugin/expander-column"],g):(u=typeof globalThis<"u"?globalThis:u||self,g(u.TbwGridPlugin_selection={},u.TbwGrid,u.TbwGrid,u.TbwGrid))})(this,(function(u,g,S,w){"use strict";function b(o){return{startRow:Math.min(o.startRow,o.endRow),startCol:Math.min(o.startCol,o.endCol),endRow:Math.max(o.startRow,o.endRow),endCol:Math.max(o.startCol,o.endCol)}}function x(o){const e=b(o);return{from:{row:e.startRow,col:e.startCol},to:{row:e.endRow,col:e.endCol}}}function C(o){return o.map(x)}function A(o,e,t){const s=b(t);return o>=s.startRow&&o<=s.endRow&&e>=s.startCol&&e<=s.endCol}function k(o,e,t){return t.some(s=>A(o,e,s))}function v(o){const e=[],t=b(o);for(let s=t.startRow;s<=t.endRow;s++)for(let i=t.startCol;i<=t.endCol;i++)e.push({row:s,col:i});return e}function I(o){const e=new Map;for(const t of o)for(const s of v(t))e.set(`${s.row},${s.col}`,s);return[...e.values()]}function p(o,e){return{startRow:o.row,startCol:o.col,endRow:e.row,endCol:e.col}}function m(o,e){const t=b(o),s=b(e);return t.startRow===s.startRow&&t.startCol===s.startCol&&t.endRow===s.endRow&&t.endCol===s.endCol}const E='@layer tbw-plugins{tbw-grid.selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}tbw-grid:has(.selection){-webkit-user-select:none;user-select:none}tbw-grid .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%));outline:none;position:relative}tbw-grid .data-grid-row.row-focus:after{content:"";position:absolute;inset:0;pointer-events:none;border-width:0;border-style:var(--tbw-selection-border-style, var(--tbw-border-style));border-color:var(--tbw-range-border-color, var(--tbw-color-accent));border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width));border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width));z-index:1}tbw-grid .data-grid-row.row-focus+.data-grid-row.row-focus:after{border-top-width:0}tbw-grid .data-grid-row.row-focus:has(+.data-grid-row.row-focus):after{border-bottom-width:0}tbw-grid[data-selection-mode=row] .cell-focus,tbw-grid[data-selection-mode=row] .row-focus,tbw-grid[data-selection-mode=range] .cell-focus{outline:none}tbw-grid .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg);position:relative}tbw-grid .data-grid-row>.cell.selected:after{content:"";position:absolute;inset:0;pointer-events:none;border:0 var(--tbw-selection-border-style, var(--tbw-border-style)) var(--tbw-range-border-color);z-index:1}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg, rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm, .8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}tbw-grid .data-grid-row>.cell[data-field=__tbw_checkbox],tbw-grid .header-row>.cell[data-field=__tbw_checkbox]{text-align:center;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}tbw-grid .tbw-select-row-checkbox{pointer-events:none;margin:0;cursor:pointer}tbw-grid .tbw-checkbox-header{display:flex;justify-content:center;align-items:center;height:100%}tbw-grid .tbw-select-all-checkbox{margin:0;cursor:pointer}}',y="__tbw_checkbox";function K(o,e,t){if(o==="cell"&&e.selectedCell)return{mode:o,ranges:[{from:{row:e.selectedCell.row,col:e.selectedCell.col},to:{row:e.selectedCell.row,col:e.selectedCell.col}}]};if(o==="row"&&e.selected.size>0){const s=[...e.selected].sort((n,h)=>n-h),i=[];let r=s[0],l=r;for(let n=1;n<s.length;n++)s[n]===l+1?l=s[n]:(i.push({from:{row:r,col:0},to:{row:l,col:t-1}}),r=s[n],l=r);return i.push({from:{row:r,col:0},to:{row:l,col:t-1}}),{mode:o,ranges:i}}return o==="range"&&e.ranges.length>0?{mode:o,ranges:C(e.ranges)}:{mode:o,ranges:[]}}class q extends S.BaseGridPlugin{static manifest={queries:[{type:"getSelection",description:"Get the current selection state"},{type:"selectRows",description:"Select specific rows by index (row mode only)"},{type:"getSelectedRowIndices",description:"Get sorted array of selected row indices"}],configRules:[{id:"selection/range-dblclick",severity:"warn",message:`"triggerOn: 'dblclick'" has no effect when mode is "range".
|
|
2
2
|
→ Range selection uses drag interaction (mousedown → mousemove), not click events.
|
|
3
|
-
→ The "triggerOn" option only affects "cell" and "row" selection modes.`,check:e=>e.mode==="range"&&e.triggerOn==="dblclick"}]};name="selection";styles=K;get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0}}selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;selectedCell=null;lastSyncedFocusRow=-1;lastSyncedFocusCol=-1;explicitSelection=!1;isSelectionEnabled(){return this.config.enabled===!1?!1:this.grid.effectiveConfig?.selectable!==!1}checkSelectable(e,t){const{isSelectable:s}=this.config;if(!s)return!0;const i=this.rows[e];if(!i)return!1;const o=t!==void 0?this.columns[t]:void 0;return s(i,e,o,t)}isRowSelectable(e){return this.checkSelectable(e)}isCellSelectable(e,t){return this.checkSelectable(e,t)}attach(e){super.attach(e),this.on("filter-applied",()=>this.clearSelectionSilent()),this.on("grouping-state-change",()=>this.clearSelectionSilent()),this.on("tree-state-change",()=>this.clearSelectionSilent())}handleQuery(e){if(e.type==="getSelection")return this.getSelection()}detach(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.pendingKeyboardUpdate=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1}clearSelectionSilent(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedCell=null,this.lastSelected=null,this.anchor=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,this.requestAfterRender()}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{rowIndex:t,colIndex:s,originalEvent:i}=e,{mode:o,triggerOn:l="click"}=this.config;if(i.type!==l)return!1;const n=this.columns[s],h=n&&w.isUtilityColumn(n);if(o==="cell"){if(h||!this.isCellSelectable(t,s))return!1;const a=this.selectedCell;return a&&a.row===t&&a.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()),this.requestAfterRender()),!1}if(o==="row"){if(!this.isRowSelectable(t))return!1;const a=i.shiftKey,d=i.ctrlKey||i.metaKey,c=n?.meta?.checkboxColumn===!0;if(a&&this.anchor!==null){const g=Math.min(this.anchor,t),_=Math.max(this.anchor,t);d||this.selected.clear();for(let R=g;R<=_;R++)this.isRowSelectable(R)&&this.selected.add(R)}else if(d||c)this.selected.has(t)?this.selected.delete(t):this.selected.add(t),this.anchor=t;else{if(this.selected.size===1&&this.selected.has(t))return!1;this.selected.clear(),this.selected.add(t),this.anchor=t}return this.lastSelected=t,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}if(o==="range"){if(h||!this.isCellSelectable(t,s))return!1;const a=i.shiftKey,d=i.ctrlKey||i.metaKey;if(a&&this.cellAnchor){const c=p(this.cellAnchor,{row:t,col:s}),g=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(g&&m(g,c))return!1;d?this.ranges.length>0?this.ranges[this.ranges.length-1]=c:this.ranges.push(c):this.ranges=[c],this.activeRange=c}else if(d){const c={startRow:t,startCol:s,endRow:t,endCol:s};this.ranges.push(c),this.activeRange=c,this.cellAnchor={row:t,col:s}}else{const c={startRow:t,startCol:s,endRow:t,endCol:s};if(this.ranges.length===1&&m(this.ranges[0],c))return!1;this.ranges=[c],this.activeRange=c,this.cellAnchor={row:t,col:s}}return this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}return!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;const{mode:t}=this.config,i=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if(e.key==="Escape")return t==="cell"?this.selectedCell=null:t==="row"?(this.selected.clear(),this.anchor=null):t==="range"&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#e()),this.requestAfterRender(),!0;if(t==="cell"&&i)return queueMicrotask(()=>{const o=this.grid._focusRow,l=this.grid._focusCol;this.isCellSelectable(o,l)?this.selectedCell={row:o,col:l}:this.selectedCell=null,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="row"){if(e.key==="ArrowUp"||e.key==="ArrowDown"){const o=e.shiftKey;return o&&this.anchor===null&&(this.anchor=this.grid._focusRow),queueMicrotask(()=>{const l=this.grid._focusRow;if(o&&this.anchor!==null){this.selected.clear();const n=Math.min(this.anchor,l),h=Math.max(this.anchor,l);for(let a=n;a<=h;a++)this.isRowSelectable(a)&&this.selected.add(a)}else this.isRowSelectable(l)?(this.selected.clear(),this.selected.add(l),this.anchor=l):this.selected.clear();this.lastSelected=l,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1}if(e.key==="a"&&(e.ctrlKey||e.metaKey))return e.preventDefault(),e.stopPropagation(),this.selectAll(),!0}if(t==="range"&&i){const o=e.key==="Tab",l=e.shiftKey&&!o;return l&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:l},queueMicrotask(()=>this.requestAfterRender()),!1}return t==="range"&&e.key==="a"&&(e.ctrlKey||e.metaKey)?(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0):!1}onCellMouseDown(e){if(!this.isSelectionEnabled()||this.config.mode!=="range"||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;const t=this.columns[e.colIndex];if(t&&w.isUtilityColumn(t)||!this.isCellSelectable(e.rowIndex,e.colIndex)||e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const s=e.rowIndex,i=e.colIndex,o=e.originalEvent.ctrlKey||e.originalEvent.metaKey,l={startRow:s,startCol:i,endRow:s,endCol:i};return!o&&this.ranges.length===1&&m(this.ranges[0],l)?(this.cellAnchor={row:s,col:i},!0):(this.cellAnchor={row:s,col:i},o||(this.ranges=[]),this.ranges.push(l),this.activeRange=l,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0)}onCellMouseMove(e){if(!this.isSelectionEnabled()||this.config.mode!=="range"||!this.isDragging||!this.cellAnchor||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;let t=e.colIndex;const s=this.columns[t];if(s&&w.isUtilityColumn(s)){const l=this.columns.findIndex(n=>!w.isUtilityColumn(n));l>=0&&(t=l)}const i=p(this.cellAnchor,{row:e.rowIndex,col:t}),o=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return o&&m(o,i)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i),this.activeRange=i,this.emit("selection-change",this.#e()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&this.config.mode==="range"&&this.isDragging)return this.isDragging=!1,!0}processColumns(e){if(this.config.checkbox&&this.config.mode==="row"){if(e.some(o=>o.field===S))return e;const t=this.#s(),s=e.findIndex(w.isExpanderColumn),i=s>=0?s+1:0;return[...e.slice(0,i),t,...e.slice(i)]}return e}#s(){return{field:S,header:"",width:32,resizable:!1,sortable:!1,meta:{lockPosition:!0,suppressMovable:!0,utility:!0,checkboxColumn:!0},headerRenderer:()=>{const e=document.createElement("div");e.className="tbw-checkbox-header";const t=document.createElement("input");return t.type="checkbox",t.className="tbw-select-all-checkbox",t.addEventListener("click",s=>{s.stopPropagation(),s.target.checked?this.selectAll():this.clearSelection()}),e.appendChild(t),e},renderer:e=>{const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-row-checkbox";const s=e.cellEl;if(s){const i=parseInt(s.getAttribute("data-row")??"-1",10);i>=0&&(t.checked=this.selected.has(i))}return t}}}#i(e){e.querySelectorAll(".tbw-select-row-checkbox").forEach(i=>{const o=i.closest(".cell"),l=o?f.getRowIndexFromCell(o):-1;l>=0&&(i.checked=this.selected.has(l))});const s=e.querySelector(".tbw-select-all-checkbox");if(s){const i=this.rows.length;let o=0;if(this.config.isSelectable)for(let h=0;h<i;h++)this.isRowSelectable(h)&&o++;else o=i;const l=o>0&&this.selected.size>=o,n=this.selected.size>0;s.checked=l,s.indeterminate=n&&!l}}#l(e){const t=this.grid._focusRow,s=this.grid._focusCol;if(e==="row"){if(this.explicitSelection){this.explicitSelection=!1,this.lastSyncedFocusRow=t;return}t!==this.lastSyncedFocusRow&&(this.lastSyncedFocusRow=t,this.isRowSelectable(t)&&(!this.selected.has(t)||this.selected.size!==1)&&(this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.anchor=t,this.emit("selection-change",this.#e())))}if(e==="cell"){if(this.explicitSelection){this.explicitSelection=!1,this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=s;return}if((t!==this.lastSyncedFocusRow||s!==this.lastSyncedFocusCol)&&(this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=s,this.isCellSelectable(t,s))){const i=this.selectedCell;(!i||i.row!==t||i.col!==s)&&(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()))}}}#t(){const e=this.gridElement;if(!e)return;const{mode:t}=this.config,s=!!this.config.isSelectable;e.querySelectorAll(".cell").forEach(l=>{l.classList.remove("selected","top","bottom","first","last"),s&&l.removeAttribute("data-selectable")});const o=e.querySelectorAll(".data-grid-row");if(o.forEach(l=>{l.classList.remove("selected","row-focus"),s&&l.removeAttribute("data-selectable")}),t==="row"&&(f.clearCellFocus(e),o.forEach(l=>{const n=l.querySelector(".cell[data-row]"),h=f.getRowIndexFromCell(n);h>=0&&(s&&!this.isRowSelectable(h)&&l.setAttribute("data-selectable","false"),this.selected.has(h)&&l.classList.add("selected","row-focus"))}),this.config.checkbox&&this.#i(e)),(t==="cell"||t==="range")&&s&&e.querySelectorAll(".cell[data-row][data-col]").forEach(n=>{const h=parseInt(n.getAttribute("data-row")??"-1",10),a=parseInt(n.getAttribute("data-col")??"-1",10);h>=0&&a>=0&&(this.isCellSelectable(h,a)||n.setAttribute("data-selectable","false"))}),t==="range"&&this.ranges.length>0){f.clearCellFocus(e);const l=this.ranges.map(b),n=(a,d)=>{for(const c of l)if(a>=c.startRow&&a<=c.endRow&&d>=c.startCol&&d<=c.endCol)return!0;return!1};e.querySelectorAll(".cell[data-row][data-col]").forEach(a=>{const d=parseInt(a.getAttribute("data-row")??"-1",10),c=parseInt(a.getAttribute("data-col")??"-1",10);if(d>=0&&c>=0){const g=this.columns[c];if(g&&w.isUtilityColumn(g))return;n(d,c)&&(a.classList.add("selected"),n(d-1,c)||a.classList.add("top"),n(d+1,c)||a.classList.add("bottom"),n(d,c-1)||a.classList.add("first"),n(d,c+1)||a.classList.add("last"))}})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.children[0],{mode:s}=this.config;if(this.pendingKeyboardUpdate&&s==="range"){const{shiftKey:i}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const o=this.grid._focusRow,l=this.grid._focusCol;if(i&&this.cellAnchor){const n=p(this.cellAnchor,{row:o,col:l});this.ranges=[n],this.activeRange=n}else i||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:o,col:l});this.emit("selection-change",this.#e())}this.#l(s),this.grid.setAttribute("data-selection-mode",s),t&&t.classList.toggle("selecting",this.isDragging),this.#t()}onScrollRender(){this.isSelectionEnabled()&&this.#t()}getSelection(){return{mode:this.config.mode,ranges:this.#e().ranges,anchor:this.cellAnchor}}getSelectedCells(){return E(this.ranges)}isCellSelected(e,t){return k(e,t,this.ranges)}selectAll(){const{mode:e}=this.config;if(e==="row"){this.selected.clear();for(let t=0;t<this.rows.length;t++)this.isRowSelectable(t)&&this.selected.add(t);this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}else if(e==="range"){const t=this.rows.length,s=this.columns.length;if(t>0&&s>0){const i={startRow:0,startCol:0,endRow:t-1,endCol:s-1};this.ranges=[i],this.activeRange=i,this.emit("selection-change",this.#e()),this.requestAfterRender()}}}selectRows(e){if(this.config.mode==="row"){this.selected.clear();for(const t of e)t>=0&&t<this.rows.length&&this.isRowSelectable(t)&&this.selected.add(t);this.anchor=e.length>0?e[e.length-1]:null,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}}getSelectedRowIndices(){return[...this.selected].sort((e,t)=>e-t)}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(t=>({startRow:t.from.row,startCol:t.from.col,endRow:t.to.row,endCol:t.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",{mode:this.config.mode,ranges:C(this.ranges)}),this.requestAfterRender()}#e(){return v(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length)}}u.SelectionPlugin=q,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
|
|
3
|
+
→ The "triggerOn" option only affects "cell" and "row" selection modes.`,check:e=>e.mode==="range"&&e.triggerOn==="dblclick"}]};name="selection";styles=E;get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0}}selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;selectedCell=null;lastSyncedFocusRow=-1;lastSyncedFocusCol=-1;explicitSelection=!1;isSelectionEnabled(){return this.config.enabled===!1?!1:this.grid.effectiveConfig?.selectable!==!1}checkSelectable(e,t){const{isSelectable:s}=this.config;if(!s)return!0;const i=this.rows[e];if(!i)return!1;const r=t!==void 0?this.columns[t]:void 0;return s(i,e,r,t)}isRowSelectable(e){return this.checkSelectable(e)}isCellSelectable(e,t){return this.checkSelectable(e,t)}attach(e){super.attach(e),this.on("filter-applied",()=>this.clearSelectionSilent()),this.on("grouping-state-change",()=>this.clearSelectionSilent()),this.on("tree-state-change",()=>this.clearSelectionSilent())}handleQuery(e){if(e.type==="getSelection")return this.getSelection();if(e.type==="getSelectedRowIndices")return this.getSelectedRowIndices();if(e.type==="selectRows")return this.selectRows(e.context),!0}detach(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.pendingKeyboardUpdate=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1}clearSelectionSilent(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedCell=null,this.lastSelected=null,this.anchor=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,this.requestAfterRender()}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{rowIndex:t,colIndex:s,originalEvent:i}=e,{mode:r,triggerOn:l="click"}=this.config;if(i.type!==l)return!1;const n=this.columns[s],h=n&&w.isUtilityColumn(n);if(r==="cell"){if(h||!this.isCellSelectable(t,s))return!1;const a=this.selectedCell;return a&&a.row===t&&a.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()),this.requestAfterRender()),!1}if(r==="row"){if(!this.isRowSelectable(t))return!1;const a=i.shiftKey,d=i.ctrlKey||i.metaKey,c=n?.meta?.checkboxColumn===!0;if(a&&this.anchor!==null){const f=Math.min(this.anchor,t),_=Math.max(this.anchor,t);d||this.selected.clear();for(let R=f;R<=_;R++)this.isRowSelectable(R)&&this.selected.add(R)}else if(d||c)this.selected.has(t)?this.selected.delete(t):this.selected.add(t),this.anchor=t;else{if(this.selected.size===1&&this.selected.has(t))return!1;this.selected.clear(),this.selected.add(t),this.anchor=t}return this.lastSelected=t,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}if(r==="range"){if(h||!this.isCellSelectable(t,s))return!1;const a=i.shiftKey,d=i.ctrlKey||i.metaKey;if(a&&this.cellAnchor){const c=p(this.cellAnchor,{row:t,col:s}),f=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(f&&m(f,c))return!1;d?this.ranges.length>0?this.ranges[this.ranges.length-1]=c:this.ranges.push(c):this.ranges=[c],this.activeRange=c}else if(d){const c={startRow:t,startCol:s,endRow:t,endCol:s};this.ranges.push(c),this.activeRange=c,this.cellAnchor={row:t,col:s}}else{const c={startRow:t,startCol:s,endRow:t,endCol:s};if(this.ranges.length===1&&m(this.ranges[0],c))return!1;this.ranges=[c],this.activeRange=c,this.cellAnchor={row:t,col:s}}return this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}return!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;const{mode:t}=this.config,i=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if(e.key==="Escape")return this.grid.query("isEditing").some(Boolean)?!1:(t==="cell"?this.selectedCell=null:t==="row"?(this.selected.clear(),this.anchor=null):t==="range"&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#e()),this.requestAfterRender(),!0);if(t==="cell"&&i)return queueMicrotask(()=>{const r=this.grid._focusRow,l=this.grid._focusCol;this.isCellSelectable(r,l)?this.selectedCell={row:r,col:l}:this.selectedCell=null,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="row"){if(e.key==="ArrowUp"||e.key==="ArrowDown"){const r=e.shiftKey;return r&&this.anchor===null&&(this.anchor=this.grid._focusRow),queueMicrotask(()=>{const l=this.grid._focusRow;if(r&&this.anchor!==null){this.selected.clear();const n=Math.min(this.anchor,l),h=Math.max(this.anchor,l);for(let a=n;a<=h;a++)this.isRowSelectable(a)&&this.selected.add(a)}else this.isRowSelectable(l)?(this.selected.clear(),this.selected.add(l),this.anchor=l):this.selected.clear();this.lastSelected=l,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1}if(e.key==="a"&&(e.ctrlKey||e.metaKey))return e.preventDefault(),e.stopPropagation(),this.selectAll(),!0}if(t==="range"&&i){const r=e.key==="Tab",l=e.shiftKey&&!r;return l&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:l},queueMicrotask(()=>this.requestAfterRender()),!1}return t==="range"&&e.key==="a"&&(e.ctrlKey||e.metaKey)?(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0):!1}onCellMouseDown(e){if(!this.isSelectionEnabled()||this.config.mode!=="range"||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;const t=this.columns[e.colIndex];if(t&&w.isUtilityColumn(t)||!this.isCellSelectable(e.rowIndex,e.colIndex)||e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const s=e.rowIndex,i=e.colIndex,r=e.originalEvent.ctrlKey||e.originalEvent.metaKey,l={startRow:s,startCol:i,endRow:s,endCol:i};return!r&&this.ranges.length===1&&m(this.ranges[0],l)?(this.cellAnchor={row:s,col:i},!0):(this.cellAnchor={row:s,col:i},r||(this.ranges=[]),this.ranges.push(l),this.activeRange=l,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0)}onCellMouseMove(e){if(!this.isSelectionEnabled()||this.config.mode!=="range"||!this.isDragging||!this.cellAnchor||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;let t=e.colIndex;const s=this.columns[t];if(s&&w.isUtilityColumn(s)){const l=this.columns.findIndex(n=>!w.isUtilityColumn(n));l>=0&&(t=l)}const i=p(this.cellAnchor,{row:e.rowIndex,col:t}),r=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return r&&m(r,i)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i),this.activeRange=i,this.emit("selection-change",this.#e()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&this.config.mode==="range"&&this.isDragging)return this.isDragging=!1,!0}processColumns(e){if(this.config.checkbox&&this.config.mode==="row"){if(e.some(r=>r.field===y))return e;const t=this.#s(),s=e.findIndex(w.isExpanderColumn),i=s>=0?s+1:0;return[...e.slice(0,i),t,...e.slice(i)]}return e}#s(){return{field:y,header:"",width:32,resizable:!1,sortable:!1,meta:{lockPosition:!0,suppressMovable:!0,utility:!0,checkboxColumn:!0},headerRenderer:()=>{const e=document.createElement("div");e.className="tbw-checkbox-header";const t=document.createElement("input");return t.type="checkbox",t.className="tbw-select-all-checkbox",t.addEventListener("click",s=>{s.stopPropagation(),s.target.checked?this.selectAll():this.clearSelection()}),e.appendChild(t),e},renderer:e=>{const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-row-checkbox";const s=e.cellEl;if(s){const i=parseInt(s.getAttribute("data-row")??"-1",10);i>=0&&(t.checked=this.selected.has(i))}return t}}}#i(e){e.querySelectorAll(".tbw-select-row-checkbox").forEach(i=>{const r=i.closest(".cell"),l=r?g.getRowIndexFromCell(r):-1;l>=0&&(i.checked=this.selected.has(l))});const s=e.querySelector(".tbw-select-all-checkbox");if(s){const i=this.rows.length;let r=0;if(this.config.isSelectable)for(let h=0;h<i;h++)this.isRowSelectable(h)&&r++;else r=i;const l=r>0&&this.selected.size>=r,n=this.selected.size>0;s.checked=l,s.indeterminate=n&&!l}}#l(e){const t=this.grid._focusRow,s=this.grid._focusCol;if(e==="row"){if(this.explicitSelection){this.explicitSelection=!1,this.lastSyncedFocusRow=t;return}t!==this.lastSyncedFocusRow&&(this.lastSyncedFocusRow=t,this.isRowSelectable(t)&&(!this.selected.has(t)||this.selected.size!==1)&&(this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.anchor=t,this.emit("selection-change",this.#e())))}if(e==="cell"){if(this.explicitSelection){this.explicitSelection=!1,this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=s;return}if((t!==this.lastSyncedFocusRow||s!==this.lastSyncedFocusCol)&&(this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=s,this.isCellSelectable(t,s))){const i=this.selectedCell;(!i||i.row!==t||i.col!==s)&&(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()))}}}#t(){const e=this.gridElement;if(!e)return;const{mode:t}=this.config,s=!!this.config.isSelectable;e.querySelectorAll(".cell").forEach(l=>{l.classList.remove("selected","top","bottom","first","last"),s&&l.removeAttribute("data-selectable")});const r=e.querySelectorAll(".data-grid-row");if(r.forEach(l=>{l.classList.remove("selected","row-focus"),s&&l.removeAttribute("data-selectable")}),t==="row"&&(g.clearCellFocus(e),r.forEach(l=>{const n=l.querySelector(".cell[data-row]"),h=g.getRowIndexFromCell(n);h>=0&&(s&&!this.isRowSelectable(h)&&l.setAttribute("data-selectable","false"),this.selected.has(h)&&l.classList.add("selected","row-focus"))}),this.config.checkbox&&this.#i(e)),(t==="cell"||t==="range")&&s&&e.querySelectorAll(".cell[data-row][data-col]").forEach(n=>{const h=parseInt(n.getAttribute("data-row")??"-1",10),a=parseInt(n.getAttribute("data-col")??"-1",10);h>=0&&a>=0&&(this.isCellSelectable(h,a)||n.setAttribute("data-selectable","false"))}),t==="range"&&this.ranges.length>0){g.clearCellFocus(e);const l=this.ranges.map(b),n=(a,d)=>{for(const c of l)if(a>=c.startRow&&a<=c.endRow&&d>=c.startCol&&d<=c.endCol)return!0;return!1};e.querySelectorAll(".cell[data-row][data-col]").forEach(a=>{const d=parseInt(a.getAttribute("data-row")??"-1",10),c=parseInt(a.getAttribute("data-col")??"-1",10);if(d>=0&&c>=0){const f=this.columns[c];if(f&&w.isUtilityColumn(f))return;n(d,c)&&(a.classList.add("selected"),n(d-1,c)||a.classList.add("top"),n(d+1,c)||a.classList.add("bottom"),n(d,c-1)||a.classList.add("first"),n(d,c+1)||a.classList.add("last"))}})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.children[0],{mode:s}=this.config;if(this.pendingKeyboardUpdate&&s==="range"){const{shiftKey:i}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const r=this.grid._focusRow,l=this.grid._focusCol;if(i&&this.cellAnchor){const n=p(this.cellAnchor,{row:r,col:l});this.ranges=[n],this.activeRange=n}else i||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:r,col:l});this.emit("selection-change",this.#e())}this.#l(s),this.grid.setAttribute("data-selection-mode",s),t&&t.classList.toggle("selecting",this.isDragging),this.#t()}onScrollRender(){this.isSelectionEnabled()&&this.#t()}getSelection(){return{mode:this.config.mode,ranges:this.#e().ranges,anchor:this.cellAnchor}}getSelectedCells(){return I(this.ranges)}isCellSelected(e,t){return k(e,t,this.ranges)}selectAll(){const{mode:e}=this.config;if(e==="row"){this.selected.clear();for(let t=0;t<this.rows.length;t++)this.isRowSelectable(t)&&this.selected.add(t);this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}else if(e==="range"){const t=this.rows.length,s=this.columns.length;if(t>0&&s>0){const i={startRow:0,startCol:0,endRow:t-1,endCol:s-1};this.ranges=[i],this.activeRange=i,this.emit("selection-change",this.#e()),this.requestAfterRender()}}}selectRows(e){if(this.config.mode==="row"){this.selected.clear();for(const t of e)t>=0&&t<this.rows.length&&this.isRowSelectable(t)&&this.selected.add(t);this.anchor=e.length>0?e[e.length-1]:null,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}}getSelectedRowIndices(){return[...this.selected].sort((e,t)=>e-t)}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(t=>({startRow:t.from.row,startCol:t.from.col,endRow:t.to.row,endCol:t.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",{mode:this.config.mode,ranges:C(this.ranges)}),this.requestAfterRender()}#e(){return K(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length)}}u.SelectionPlugin=q,Object.defineProperty(u,Symbol.toStringTag,{value:"Module"})}));
|
|
4
4
|
//# sourceMappingURL=selection.umd.js.map
|