@toolbox-web/grid 2.3.0 → 2.4.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.d.ts +1 -0
- package/all.js +2 -2
- package/all.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/lib/core/grid.d.ts +2 -1
- package/lib/core/internal/diagnostics.d.ts +5 -1
- package/lib/core/internal/dom-builder.d.ts +0 -25
- package/lib/core/internal/drag-drop-registry.d.ts +66 -0
- package/lib/core/internal/render-scheduler.d.ts +9 -8
- package/lib/core/plugin/base-plugin.d.ts +23 -0
- package/lib/core/plugin/plugin-manager.d.ts +9 -0
- package/lib/core/types.d.ts +67 -46
- package/lib/features/registry.js.map +1 -1
- package/lib/features/reorder-rows.d.ts +3 -3
- package/lib/features/reorder-rows.js +1 -1
- package/lib/features/reorder-rows.js.map +1 -1
- package/lib/features/row-drag-drop.d.ts +9 -0
- package/lib/features/row-drag-drop.js +2 -0
- package/lib/features/row-drag-drop.js.map +1 -0
- package/lib/features/server-side.js.map +1 -1
- package/lib/plugins/clipboard/index.js +1 -1
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/index.js +1 -1
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/index.js +1 -1
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/editing/index.js +1 -1
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/export/ExportPlugin.d.ts +89 -0
- package/lib/plugins/export/index.d.ts +3 -2
- package/lib/plugins/export/index.js +1 -1
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/export/types.d.ts +30 -0
- package/lib/plugins/filtering/index.js +1 -1
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/index.js +2 -2
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/index.js +1 -1
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js +1 -1
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/index.js +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/index.js +1 -1
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/index.js +1 -1
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/print/index.js +1 -1
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/reorder-columns/index.js +1 -1
- package/lib/plugins/reorder-columns/index.js.map +1 -1
- package/lib/plugins/reorder-rows/RowReorderPlugin.d.ts +14 -156
- package/lib/plugins/reorder-rows/index.d.ts +10 -4
- package/lib/plugins/reorder-rows/index.js +1 -1
- package/lib/plugins/reorder-rows/index.js.map +1 -1
- package/lib/plugins/reorder-rows/types.d.ts +9 -86
- package/lib/plugins/responsive/index.js +1 -1
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/row-drag-drop/RowDragDropPlugin.d.ts +106 -0
- package/lib/plugins/row-drag-drop/index.d.ts +9 -0
- package/lib/plugins/row-drag-drop/index.js +2 -0
- package/lib/plugins/row-drag-drop/index.js.map +1 -0
- package/lib/plugins/row-drag-drop/types.d.ts +255 -0
- package/lib/plugins/selection/index.js +1 -1
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/ServerSidePlugin.d.ts +13 -0
- package/lib/plugins/server-side/datasource-types.d.ts +54 -7
- package/lib/plugins/server-side/datasource.d.ts +10 -2
- package/lib/plugins/server-side/index.d.ts +1 -1
- package/lib/plugins/server-side/index.js +1 -1
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/server-side/types.d.ts +1 -1
- package/lib/plugins/shared/drag-drop-protocol.d.ts +98 -0
- package/lib/plugins/tooltip/index.js +1 -1
- package/lib/plugins/tooltip/index.js.map +1 -1
- package/lib/plugins/tree/TreePlugin.d.ts +19 -6
- package/lib/plugins/tree/index.js +1 -1
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/index.js +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +1 -1
- package/umd/grid.all.umd.js +1 -1
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +1 -1
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/export.umd.js +1 -1
- package/umd/plugins/export.umd.js.map +1 -1
- package/umd/plugins/reorder-rows.umd.js +1 -1
- package/umd/plugins/reorder-rows.umd.js.map +1 -1
- package/umd/plugins/row-drag-drop.umd.js +2 -0
- package/umd/plugins/row-drag-drop.umd.js.map +1 -0
- package/umd/plugins/selection.umd.js +1 -1
- package/umd/plugins/selection.umd.js.map +1 -1
- package/umd/plugins/server-side.umd.js +1 -1
- package/umd/plugins/server-side.umd.js.map +1 -1
- package/umd/plugins/tree.umd.js +1 -1
- package/umd/plugins/tree.umd.js.map +1 -1
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("../../core/internal/value-accessor"),require("../../core/plugin/base-plugin")):"function"==typeof define&&define.amd?define(["exports","../../core/internal/value-accessor","../../core/plugin/base-plugin"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_export={},e.TbwGrid,e.TbwGrid)}(this,function(e,t,
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("../../core/internal/utils"),require("../../core/internal/value-accessor"),require("../../core/plugin/base-plugin")):"function"==typeof define&&define.amd?define(["exports","../../core/internal/utils","../../core/internal/value-accessor","../../core/plugin/base-plugin"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_export={},e.TbwGrid,e.TbwGrid,e.TbwGrid)}(this,function(e,t,r,o){"use strict";function s(e,t,r=!0){let o=e;if(r&&(o=o.filter(e=>!e.hidden&&!e.field.startsWith("__")&&!0!==e.utility)),t?.length){const e=new Set(t);o=o.filter(t=>e.has(t.field))}return o}function n(e,t){return t?.length?[...t].sort((e,t)=>e-t).map(t=>e[t]).filter(e=>null!=e):e}function l(e,t=!0){if(null==e)return"";if(e instanceof Date)return e.toISOString();if("object"==typeof e)return JSON.stringify(e);const r=String(e);return t&&(r.includes(",")||r.includes('"')||r.includes("\n")||r.includes("\r"))?`"${r.replace(/"/g,'""')}"`:r}function i(e,t,o,s={}){const n=s.delimiter??",",i=s.newline??"\n",c=s.quoteStrings??!0,a=[],u=s.bom?"\ufeff":"";if(!1!==o.includeHeaders){const e=t.map(e=>{const t=e.header||e.field;return l(o.processHeader?o.processHeader(t,e.field):t,c)});a.push(e.join(n))}for(const f of e){const e=t.map(e=>{let t=r.resolveCellValue(f,e);return o.processCell&&(t=o.processCell(t,e.field,f)),l(t,c)});a.push(e.join(n))}return u+a.join(i)}function c(e,t){const r=URL.createObjectURL(e),o=document.createElement("a");o.href=r,o.download=t,o.style.display="none",document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(r)}function a(e){return JSON.stringify(u(e))}function u(e){if(null==e||"object"!=typeof e)return e;if(Array.isArray(e))return e.map(u);const t={};for(const r of Object.keys(e).sort())t[r]=u(e[r]);return t}class f{#e=new Map;#t=0;register(e){const t=a(e),r=this.#e.get(t);if(r)return r.id;this.#t++;const o=`s${this.#t}`;return this.#e.set(t,{id:o,style:e}),o}getStyleId(e){return this.#e.get(a(e))?.id}get size(){return this.#e.size}toXml(){if(0===this.#e.size)return"";let e="\n<Styles>";for(const{id:t,style:r}of this.#e.values())e+=d(t,r);return e+="\n</Styles>",e}}function d(e,t){let r=`\n<Style ss:ID="${e}">`;if(t.font&&(r+="<Font",t.font.name&&(r+=` ss:FontName="${t.font.name}"`),t.font.size&&(r+=` ss:Size="${t.font.size}"`),t.font.bold&&(r+=' ss:Bold="1"'),t.font.italic&&(r+=' ss:Italic="1"'),t.font.color&&(r+=` ss:Color="${t.font.color}"`),r+="/>"),t.fill){const e=t.fill.pattern??"Solid";r+=`<Interior ss:Color="${t.fill.color}" ss:Pattern="${e}"/>`}return t.numberFormat&&(r+=`<NumberFormat ss:Format="${t.numberFormat}"/>`),t.alignment&&(r+="<Alignment",t.alignment.horizontal&&(r+=` ss:Horizontal="${t.alignment.horizontal}"`),t.alignment.vertical&&(r+=` ss:Vertical="${t.alignment.vertical}"`),t.alignment.wrapText&&(r+=' ss:WrapText="1"'),r+="/>"),t.borders&&(r+="<Borders>",t.borders.top&&(r+=p("Top",t.borders.top)),t.borders.bottom&&(r+=p("Bottom",t.borders.bottom)),t.borders.left&&(r+=p("Left",t.borders.left)),t.borders.right&&(r+=p("Right",t.borders.right)),r+="</Borders>"),r+="</Style>",r}function p(e,t){let r=`<Border ss:Position="${e}" ss:LineStyle="Continuous" ss:Weight="${function(e){switch(e){case"Thin":return 1;case"Medium":return 2;case"Thick":return 3}}(t.style)}"`;return t.color&&(r+=` ss:Color="${t.color}"`),r+="/>",r}function m(e,t,r,o,s){if(t.cellStyle){const n=t.cellStyle(r,o,s);if(n)return e.register(n)}const n=t.columnStyles?.[o];return n?e.getStyleId(n):t.defaultStyle?e.getStyleId(t.defaultStyle):void 0}function h(e,t){const r=Math.min(t.length,50);let o=(e.header??e.field).length;for(let s=0;s<r;s++){const r=t[s][e.field],n=null==r?0:String(r).length;n>o&&(o=n)}return o+2}function g(e){return e.replace(/&/g,"&").replace(/</g,"<").replace(/>/g,">").replace(/"/g,""").replace(/'/g,"'")}function y(e,t,o){const s=o.excelStyles,n=s?function(e){const t=new f;if(e.headerStyle&&t.register(e.headerStyle),e.defaultStyle&&t.register(e.defaultStyle),e.columnStyles)for(const r of Object.values(e.columnStyles))t.register(r);return t}(s):void 0;let l='<?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">';if(n){if(s.cellStyle)for(const o of e)for(const e of t){const t=r.resolveCellValue(o,e),l=s.cellStyle(t,e.field,o);l&&n.register(l)}l+=n.toXml()}l+='\n<Worksheet ss:Name="Sheet1">\n<Table>',s&&(l+=function(e,t,r){const o=r.columnWidths,s=r.autoFitColumns;if(!o&&!s)return"";let n="";for(const l of e){let e=o?.[l.field];null==e&&s&&(e=h(l,t)),n+=null!=e?`\n<Column ss:Width="${7*e}"/>`:"\n<Column/>"}return n}(t,e,s));const i=s?.headerStyle&&n?n.getStyleId(s.headerStyle):void 0;if(!1!==o.includeHeaders){l+="\n<Row>";for(const e of t){const t=e.header||e.field;l+=`<Cell${i?` ss:StyleID="${i}"`:""}><Data ss:Type="String">${g(o.processHeader?o.processHeader(t,e.field):t)}</Data></Cell>`}l+="</Row>"}for(const c of e){l+="\n<Row>";for(const e of t){let t=r.resolveCellValue(c,e);o.processCell&&(t=o.processCell(t,e.field,c));let i="String",a="";null==t?a="":"number"!=typeof t||isNaN(t)?t instanceof Date?(i="DateTime",a=t.toISOString()):a=g(String(t)):(i="Number",a=String(t));const u=n&&s?m(n,s,t,e.field,c):void 0;l+=`<Cell${u?` ss:StyleID="${u}"`:""}><Data ss:Type="${i}">${a}</Data></Cell>`}l+="</Row>"}return l+="\n</Table>\n</Worksheet>\n</Workbook>",l}class x extends o.BaseGridPlugin{static manifest={queries:[{type:"export:csv",description:"Triggers a CSV export"}]};name="export";get defaultConfig(){return{fileName:"export",includeHeaders:!0,onlyVisible:!0,onlySelected:!1}}isExportingFlag=!1;lastExportInfo=null;handleQuery(e){if("export:csv"===e.type)return this.exportCsv(),!0}resolveExportData(e,t){const r=this.config,o={format:e,fileName:t?.fileName??r.fileName??"export",includeHeaders:t?.includeHeaders??r.includeHeaders,processCell:t?.processCell,processHeader:t?.processHeader,mode:t?.mode??"raw",columns:t?.columns,rowIndices:t?.rowIndices,excelStyles:t?.excelStyles,fileExtension:t?.fileExtension},l=s(this.columns,t?.columns,r.onlyVisible);let i;if(t?.rowIndices)i=n(this.rows,t.rowIndices);else if(r.onlySelected){const e=this.getSelectionState();i=e?.selected?.size?n(this.rows,[...e.selected]):[...this.rows]}else i=[...this.rows];return{columns:l,rows:i,fullParams:o}}resolveCellOutput(e,r,o,s,n){let l=e;if("formatted"===s){const s=this.#r(r);if(s)try{const t=s(e,o);l=null==t?"":String(t)}catch{l=null==e?"":String(e)}else l="date"===r.type?t.formatDateValue(e):"boolean"===r.type?!!e:e}return n&&(l=n(l,r.field,o)),l}performExport(e,t){const{columns:r,rows:o,fullParams:s}=this.resolveExportData(e,t);this.isExportingFlag=!0;let n=s.fileName??"export";const l=this.#o(r);try{switch(e){case"csv":{const e=i(this.#s(o,r,s),l,{...s,processCell:void 0},{bom:!0});n=n.endsWith(".csv")?n:`${n}.csv`,function(e,t){c(new Blob([e],{type:"text/csv;charset=utf-8;"}),t)}(e,n);break}case"excel":{const e=y(this.#s(o,r,s),l,{...s,processCell:void 0}),t=s.fileExtension??".xls",i=t.startsWith(".")?t:`.${t}`;n=n.endsWith(i)?n:`${n}${i}`,function(e,t){c(new Blob([e],{type:"application/vnd.ms-excel;charset=utf-8;"}),t)}(e,n);break}case"json":{const e=this.#s(o,r,s),t=JSON.stringify(e,null,2);n=n.endsWith(".json")?n:`${n}.json`;c(new Blob([t],{type:"application/json"}),n);break}}this.lastExportInfo={format:e,timestamp:new Date},this.emit("export-complete",{format:e,fileName:n,rowCount:o.length,columnCount:r.length})}finally{this.isExportingFlag=!1}}#s(e,t,o){const s=o.mode??"raw";return e.map(e=>{const n={};for(const l of t){const t=r.resolveCellValue(e,l);n[l.field]=this.resolveCellOutput(t,l,e,s,o.processCell)}return n})}getSelectionState(){try{return this.grid?.getPluginState?.("selection")??null}catch{return null}}export(e){const{columns:t,rows:r,fullParams:o}=this.resolveExportData("json",e);return this.#s(r,t,o)}getResolvedColumns(e){return s(this.columns,e?.columns,this.config.onlyVisible)}formatCsv(e,t,r){const{columns:o,fullParams:s}=this.resolveExportData("csv",t);return i(e,this.#o(o),s,r)}formatExcel(e,t){const{columns:r,fullParams:o}=this.resolveExportData("excel",t);return y(e,this.#o(r),o)}#o(e){return e.map(e=>e.valueAccessor?{...e,valueAccessor:void 0}:e)}#r(e){if(e.format)return e.format;if(!e.type)return;const t=this.grid,r=t?.__frameworkAdapter;if(!r?.getTypeDefault)return;const o=r.getTypeDefault(e.type,t?._hostElement);return o?.format}exportCsv(e){this.performExport("csv",e)}exportExcel(e){this.performExport("excel",e)}exportJson(e){this.performExport("json",e)}isExporting(){return this.isExportingFlag}getLastExport(){return this.lastExportInfo}}e.ExportPlugin=x,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
2
2
|
//# sourceMappingURL=export.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
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-styles.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.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 { resolveCellValue } from '../../core/internal/value-accessor';\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 = resolveCellValue(row, col);\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 Style Engine\n *\n * Builds a deduplicated `<Styles>` block for XML Spreadsheet 2003.\n * Collects unique ExcelCellStyle objects, assigns ss:StyleID values,\n * and generates the corresponding XML.\n */\n\nimport type { ExcelBorder, ExcelCellStyle, ExcelStyleConfig } from './types';\n\n// #region Style Hashing\n\n/**\n * Produce a deterministic string key for a style object.\n * Two structurally identical styles produce the same hash.\n * Recursively sorts object keys for order-independent comparison.\n */\nfunction hashStyle(style: ExcelCellStyle): string {\n return JSON.stringify(sortKeys(style));\n}\n\n/** Recursively sort object keys for deterministic serialization. */\nfunction sortKeys(obj: unknown): unknown {\n if (obj == null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(sortKeys);\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = sortKeys((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n}\n\n// #endregion\n\n// #region Style Registry\n\n/** Maps a hash → style ID and the original style. */\nexport interface StyleEntry {\n id: string;\n style: ExcelCellStyle;\n}\n\n/**\n * Collects unique styles and assigns each an `ss:StyleID`.\n * Register styles via `register()`, then call `getStyleId()` to look up\n * the ID for a given style, and `toXml()` to emit the `<Styles>` block.\n */\nexport class StyleRegistry {\n /** hash → StyleEntry */\n #entries = new Map<string, StyleEntry>();\n #counter = 0;\n\n /**\n * Register a style and return its assigned ID.\n * If an identical style was already registered, returns the existing ID.\n */\n register(style: ExcelCellStyle): string {\n const hash = hashStyle(style);\n const existing = this.#entries.get(hash);\n if (existing) return existing.id;\n\n this.#counter++;\n const id = `s${this.#counter}`;\n this.#entries.set(hash, { id, style });\n return id;\n }\n\n /** Look up the ID previously assigned to a style (or undefined). */\n getStyleId(style: ExcelCellStyle): string | undefined {\n return this.#entries.get(hashStyle(style))?.id;\n }\n\n /** Number of unique styles registered so far. */\n get size(): number {\n return this.#entries.size;\n }\n\n /** Emit the full `<Styles>…</Styles>` XML fragment, or empty string if no styles. */\n toXml(): string {\n if (this.#entries.size === 0) return '';\n\n let xml = '\\n<Styles>';\n for (const { id, style } of this.#entries.values()) {\n xml += buildStyleElement(id, style);\n }\n xml += '\\n</Styles>';\n return xml;\n }\n}\n\n// #endregion\n\n// #region XML Builders\n\nfunction buildStyleElement(id: string, style: ExcelCellStyle): string {\n let xml = `\\n<Style ss:ID=\"${id}\">`;\n\n if (style.font) {\n xml += '<Font';\n if (style.font.name) xml += ` ss:FontName=\"${style.font.name}\"`;\n if (style.font.size) xml += ` ss:Size=\"${style.font.size}\"`;\n if (style.font.bold) xml += ' ss:Bold=\"1\"';\n if (style.font.italic) xml += ' ss:Italic=\"1\"';\n if (style.font.color) xml += ` ss:Color=\"${style.font.color}\"`;\n xml += '/>';\n }\n\n if (style.fill) {\n const pattern = style.fill.pattern ?? 'Solid';\n xml += `<Interior ss:Color=\"${style.fill.color}\" ss:Pattern=\"${pattern}\"/>`;\n }\n\n if (style.numberFormat) {\n xml += `<NumberFormat ss:Format=\"${style.numberFormat}\"/>`;\n }\n\n if (style.alignment) {\n xml += '<Alignment';\n if (style.alignment.horizontal) xml += ` ss:Horizontal=\"${style.alignment.horizontal}\"`;\n if (style.alignment.vertical) xml += ` ss:Vertical=\"${style.alignment.vertical}\"`;\n if (style.alignment.wrapText) xml += ' ss:WrapText=\"1\"';\n xml += '/>';\n }\n\n if (style.borders) {\n xml += '<Borders>';\n if (style.borders.top) xml += buildBorderElement('Top', style.borders.top);\n if (style.borders.bottom) xml += buildBorderElement('Bottom', style.borders.bottom);\n if (style.borders.left) xml += buildBorderElement('Left', style.borders.left);\n if (style.borders.right) xml += buildBorderElement('Right', style.borders.right);\n xml += '</Borders>';\n }\n\n xml += '</Style>';\n return xml;\n}\n\nfunction buildBorderElement(position: string, border: ExcelBorder): string {\n let xml = `<Border ss:Position=\"${position}\" ss:LineStyle=\"Continuous\" ss:Weight=\"${borderWeight(border.style)}\"`;\n if (border.color) xml += ` ss:Color=\"${border.color}\"`;\n xml += '/>';\n return xml;\n}\n\nfunction borderWeight(style: ExcelBorder['style']): number {\n switch (style) {\n case 'Thin':\n return 1;\n case 'Medium':\n return 2;\n case 'Thick':\n return 3;\n }\n}\n\n// #endregion\n\n// #region Style Resolution\n\n/**\n * Build a StyleRegistry pre-populated with all styles declared in the config.\n * This covers headerStyle, defaultStyle, and columnStyles.\n * (cellStyle callbacks are resolved per-cell at render time.)\n */\nexport function buildStyleRegistry(config: ExcelStyleConfig): StyleRegistry {\n const registry = new StyleRegistry();\n\n if (config.headerStyle) registry.register(config.headerStyle);\n\n if (config.defaultStyle) registry.register(config.defaultStyle);\n\n if (config.columnStyles) {\n for (const style of Object.values(config.columnStyles)) {\n registry.register(style);\n }\n }\n\n return registry;\n}\n\n/**\n * Resolve the style ID for a data cell. Precedence (highest → lowest):\n * 1. cellStyle callback return value\n * 2. columnStyles[field]\n * 3. defaultStyle\n */\nexport function resolveDataStyleId(\n registry: StyleRegistry,\n config: ExcelStyleConfig,\n value: unknown,\n field: string,\n row: unknown,\n): string | undefined {\n // 1. Dynamic cell callback\n if (config.cellStyle) {\n const dynamic = config.cellStyle(value, field, row);\n if (dynamic) {\n // Register on-the-fly (dedup handles repeats)\n return registry.register(dynamic);\n }\n }\n\n // 2. Per-column override\n const colStyle = config.columnStyles?.[field];\n if (colStyle) return registry.getStyleId(colStyle);\n\n // 3. Default\n if (config.defaultStyle) return registry.getStyleId(config.defaultStyle);\n\n return undefined;\n}\n\n// #endregion\n\n// #region Column Widths\n\n/** Character width → Excel column width approximation (px ≈ chars × 7) */\nconst CHAR_WIDTH = 7;\n\n/**\n * Build `<Column>` elements for explicit or auto-fit widths.\n */\nexport function buildColumnWidthsXml(\n columns: { field: string; header?: string }[],\n rows: Record<string, unknown>[],\n config: ExcelStyleConfig,\n): string {\n const widths = config.columnWidths;\n const autoFit = config.autoFitColumns;\n\n if (!widths && !autoFit) return '';\n\n let xml = '';\n for (const col of columns) {\n let width: number | undefined = widths?.[col.field];\n\n if (width == null && autoFit) {\n width = autoFitWidth(col, rows);\n }\n\n if (width != null) {\n xml += `\\n<Column ss:Width=\"${width * CHAR_WIDTH}\"/>`;\n } else {\n // Emit an empty column entry to keep ordinal alignment\n xml += '\\n<Column/>';\n }\n }\n\n return xml;\n}\n\n/** Estimate width from header + first N data rows (capped at 50). */\nfunction autoFitWidth(col: { field: string; header?: string }, rows: Record<string, unknown>[]): number {\n const sampleSize = Math.min(rows.length, 50);\n let maxLen = (col.header ?? col.field).length;\n\n for (let i = 0; i < sampleSize; i++) {\n const val = rows[i][col.field];\n const len = val == null ? 0 : String(val).length;\n if (len > maxLen) maxLen = len;\n }\n\n // Add a small padding\n return maxLen + 2;\n}\n\n// #endregion\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 { resolveCellValue } from '../../core/internal/value-accessor';\nimport type { ColumnConfig } from '../../core/types';\nimport { downloadBlob } from './csv';\nimport { buildColumnWidthsXml, buildStyleRegistry, resolveDataStyleId } from './excel-styles';\nimport type { ExportParams } from './types';\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 const styles = params.excelStyles;\n const registry = styles ? buildStyleRegistry(styles) : undefined;\n\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\n // Emit <Styles> block (only when styles are configured)\n if (registry) {\n // Pre-register dynamic cellStyle entries so they appear in <Styles>\n if (styles!.cellStyle) {\n for (const row of rows) {\n for (const col of columns) {\n const value = resolveCellValue(row, col);\n const dynamic = styles!.cellStyle(value, col.field, row);\n if (dynamic) registry.register(dynamic);\n }\n }\n }\n xml += registry.toXml();\n }\n\n xml += '\\n<Worksheet ss:Name=\"Sheet1\">\\n<Table>';\n\n // Column widths\n if (styles) {\n xml += buildColumnWidthsXml(columns, rows as Record<string, unknown>[], styles);\n }\n\n // Header style ID\n const headerStyleId = styles?.headerStyle && registry ? registry.getStyleId(styles.headerStyle) : undefined;\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 const styleAttr = headerStyleId ? ` ss:StyleID=\"${headerStyleId}\"` : '';\n xml += `<Cell${styleAttr}><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 = resolveCellValue(row, col);\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 // Resolve data cell style\n const dataStyleId = registry && styles ? resolveDataStyleId(registry, styles, value, col.field, row) : undefined;\n const styleAttr = dataStyleId ? ` ss:StyleID=\"${dataStyleId}\"` : '';\n\n xml += `<Cell${styleAttr}><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 blob = new Blob([content], {\n type: 'application/vnd.ms-excel;charset=utf-8;',\n });\n downloadBlob(blob, fileName);\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 { resolveCellValue } from '../../core/internal/value-accessor';\nimport { BaseGridPlugin, type PluginManifest, type PluginQuery } 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 * ## 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 * @example Basic Export with Button\n * ```ts\n * import { queryGrid } from '@toolbox-web/grid';\n * import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\n *\n * const grid = queryGrid('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.getPluginByName('export').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 SelectionPlugin for exporting selected rows\n *\n * @internal Extends BaseGridPlugin\n */\nexport class ExportPlugin extends BaseGridPlugin<ExportConfig> {\n /**\n * Plugin manifest — declares queries for inter-plugin communication.\n * @internal\n */\n static override readonly manifest: PluginManifest = {\n queries: [{ type: 'export:csv', description: 'Triggers a CSV export' }],\n };\n\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 Query System\n\n /** @internal */\n override handleQuery(query: PluginQuery): unknown {\n if (query.type === 'export:csv') {\n this.exportCsv();\n return true;\n }\n return undefined;\n }\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 excelStyles: params?.excelStyles,\n fileExtension: params?.fileExtension,\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 const ext = fullParams.fileExtension ?? '.xls';\n const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;\n fileName = fileName.endsWith(normalizedExt) ? fileName : `${fileName}${normalizedExt}`;\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 = resolveCellValue(row, col);\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":["resolveRows","rows","indices","length","sort","a","b","map","i","filter","r","formatCsvValue","value","quote","Date","toISOString","JSON","stringify","str","String","includes","replace","downloadBlob","blob","fileName","url","URL","createObjectURL","link","document","createElement","href","download","style","display","body","appendChild","click","removeChild","revokeObjectURL","hashStyle","sortKeys","obj","Array","isArray","sorted","key","Object","keys","StyleRegistry","entries","Map","counter","register","hash","existing","this","get","id","set","getStyleId","size","toXml","xml","values","buildStyleElement","font","name","bold","italic","color","fill","pattern","numberFormat","alignment","horizontal","vertical","wrapText","borders","top","buildBorderElement","bottom","left","right","position","border","borderWeight","resolveDataStyleId","registry","config","field","row","cellStyle","dynamic","colStyle","columnStyles","defaultStyle","autoFitWidth","col","sampleSize","Math","min","maxLen","header","val","len","escapeXml","buildExcelXml","columns","params","styles","excelStyles","headerStyle","buildStyleRegistry","resolveCellValue","widths","columnWidths","autoFit","autoFitColumns","width","buildColumnWidthsXml","headerStyleId","includeHeaders","processHeader","processCell","type","displayValue","isNaN","dataStyleId","ExportPlugin","BaseGridPlugin","static","queries","description","defaultConfig","onlyVisible","onlySelected","isExportingFlag","lastExportInfo","handleQuery","query","exportCsv","performExport","format","fullParams","rowIndices","fileExtension","fields","result","c","hidden","startsWith","utility","fieldSet","Set","has","resolveColumns","selectionState","getSelectionState","selected","content","options","delimiter","newline","lines","bom","headerRow","push","join","cells","buildCsv","endsWith","Blob","downloadCsv","ext","normalizedExt","downloadExcel","jsonData","timestamp","emit","rowCount","columnCount","grid","getPluginState","exportExcel","exportJson","isExporting","getLastExport"],"mappings":"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"}
|
|
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-styles.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.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 { resolveCellValue } from '../../core/internal/value-accessor';\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 quote = options.quoteStrings ?? true;\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, quote);\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 = resolveCellValue(row, col);\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n return formatCsvValue(value, quote);\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 Style Engine\n *\n * Builds a deduplicated `<Styles>` block for XML Spreadsheet 2003.\n * Collects unique ExcelCellStyle objects, assigns ss:StyleID values,\n * and generates the corresponding XML.\n */\n\nimport type { ExcelBorder, ExcelCellStyle, ExcelStyleConfig } from './types';\n\n// #region Style Hashing\n\n/**\n * Produce a deterministic string key for a style object.\n * Two structurally identical styles produce the same hash.\n * Recursively sorts object keys for order-independent comparison.\n */\nfunction hashStyle(style: ExcelCellStyle): string {\n return JSON.stringify(sortKeys(style));\n}\n\n/** Recursively sort object keys for deterministic serialization. */\nfunction sortKeys(obj: unknown): unknown {\n if (obj == null || typeof obj !== 'object') return obj;\n if (Array.isArray(obj)) return obj.map(sortKeys);\n const sorted: Record<string, unknown> = {};\n for (const key of Object.keys(obj).sort()) {\n sorted[key] = sortKeys((obj as Record<string, unknown>)[key]);\n }\n return sorted;\n}\n\n// #endregion\n\n// #region Style Registry\n\n/** Maps a hash → style ID and the original style. */\nexport interface StyleEntry {\n id: string;\n style: ExcelCellStyle;\n}\n\n/**\n * Collects unique styles and assigns each an `ss:StyleID`.\n * Register styles via `register()`, then call `getStyleId()` to look up\n * the ID for a given style, and `toXml()` to emit the `<Styles>` block.\n */\nexport class StyleRegistry {\n /** hash → StyleEntry */\n #entries = new Map<string, StyleEntry>();\n #counter = 0;\n\n /**\n * Register a style and return its assigned ID.\n * If an identical style was already registered, returns the existing ID.\n */\n register(style: ExcelCellStyle): string {\n const hash = hashStyle(style);\n const existing = this.#entries.get(hash);\n if (existing) return existing.id;\n\n this.#counter++;\n const id = `s${this.#counter}`;\n this.#entries.set(hash, { id, style });\n return id;\n }\n\n /** Look up the ID previously assigned to a style (or undefined). */\n getStyleId(style: ExcelCellStyle): string | undefined {\n return this.#entries.get(hashStyle(style))?.id;\n }\n\n /** Number of unique styles registered so far. */\n get size(): number {\n return this.#entries.size;\n }\n\n /** Emit the full `<Styles>…</Styles>` XML fragment, or empty string if no styles. */\n toXml(): string {\n if (this.#entries.size === 0) return '';\n\n let xml = '\\n<Styles>';\n for (const { id, style } of this.#entries.values()) {\n xml += buildStyleElement(id, style);\n }\n xml += '\\n</Styles>';\n return xml;\n }\n}\n\n// #endregion\n\n// #region XML Builders\n\nfunction buildStyleElement(id: string, style: ExcelCellStyle): string {\n let xml = `\\n<Style ss:ID=\"${id}\">`;\n\n if (style.font) {\n xml += '<Font';\n if (style.font.name) xml += ` ss:FontName=\"${style.font.name}\"`;\n if (style.font.size) xml += ` ss:Size=\"${style.font.size}\"`;\n if (style.font.bold) xml += ' ss:Bold=\"1\"';\n if (style.font.italic) xml += ' ss:Italic=\"1\"';\n if (style.font.color) xml += ` ss:Color=\"${style.font.color}\"`;\n xml += '/>';\n }\n\n if (style.fill) {\n const pattern = style.fill.pattern ?? 'Solid';\n xml += `<Interior ss:Color=\"${style.fill.color}\" ss:Pattern=\"${pattern}\"/>`;\n }\n\n if (style.numberFormat) {\n xml += `<NumberFormat ss:Format=\"${style.numberFormat}\"/>`;\n }\n\n if (style.alignment) {\n xml += '<Alignment';\n if (style.alignment.horizontal) xml += ` ss:Horizontal=\"${style.alignment.horizontal}\"`;\n if (style.alignment.vertical) xml += ` ss:Vertical=\"${style.alignment.vertical}\"`;\n if (style.alignment.wrapText) xml += ' ss:WrapText=\"1\"';\n xml += '/>';\n }\n\n if (style.borders) {\n xml += '<Borders>';\n if (style.borders.top) xml += buildBorderElement('Top', style.borders.top);\n if (style.borders.bottom) xml += buildBorderElement('Bottom', style.borders.bottom);\n if (style.borders.left) xml += buildBorderElement('Left', style.borders.left);\n if (style.borders.right) xml += buildBorderElement('Right', style.borders.right);\n xml += '</Borders>';\n }\n\n xml += '</Style>';\n return xml;\n}\n\nfunction buildBorderElement(position: string, border: ExcelBorder): string {\n let xml = `<Border ss:Position=\"${position}\" ss:LineStyle=\"Continuous\" ss:Weight=\"${borderWeight(border.style)}\"`;\n if (border.color) xml += ` ss:Color=\"${border.color}\"`;\n xml += '/>';\n return xml;\n}\n\nfunction borderWeight(style: ExcelBorder['style']): number {\n switch (style) {\n case 'Thin':\n return 1;\n case 'Medium':\n return 2;\n case 'Thick':\n return 3;\n }\n}\n\n// #endregion\n\n// #region Style Resolution\n\n/**\n * Build a StyleRegistry pre-populated with all styles declared in the config.\n * This covers headerStyle, defaultStyle, and columnStyles.\n * (cellStyle callbacks are resolved per-cell at render time.)\n */\nexport function buildStyleRegistry(config: ExcelStyleConfig): StyleRegistry {\n const registry = new StyleRegistry();\n\n if (config.headerStyle) registry.register(config.headerStyle);\n\n if (config.defaultStyle) registry.register(config.defaultStyle);\n\n if (config.columnStyles) {\n for (const style of Object.values(config.columnStyles)) {\n registry.register(style);\n }\n }\n\n return registry;\n}\n\n/**\n * Resolve the style ID for a data cell. Precedence (highest → lowest):\n * 1. cellStyle callback return value\n * 2. columnStyles[field]\n * 3. defaultStyle\n */\nexport function resolveDataStyleId(\n registry: StyleRegistry,\n config: ExcelStyleConfig,\n value: unknown,\n field: string,\n row: unknown,\n): string | undefined {\n // 1. Dynamic cell callback\n if (config.cellStyle) {\n const dynamic = config.cellStyle(value, field, row);\n if (dynamic) {\n // Register on-the-fly (dedup handles repeats)\n return registry.register(dynamic);\n }\n }\n\n // 2. Per-column override\n const colStyle = config.columnStyles?.[field];\n if (colStyle) return registry.getStyleId(colStyle);\n\n // 3. Default\n if (config.defaultStyle) return registry.getStyleId(config.defaultStyle);\n\n return undefined;\n}\n\n// #endregion\n\n// #region Column Widths\n\n/** Character width → Excel column width approximation (px ≈ chars × 7) */\nconst CHAR_WIDTH = 7;\n\n/**\n * Build `<Column>` elements for explicit or auto-fit widths.\n */\nexport function buildColumnWidthsXml(\n columns: { field: string; header?: string }[],\n rows: Record<string, unknown>[],\n config: ExcelStyleConfig,\n): string {\n const widths = config.columnWidths;\n const autoFit = config.autoFitColumns;\n\n if (!widths && !autoFit) return '';\n\n let xml = '';\n for (const col of columns) {\n let width: number | undefined = widths?.[col.field];\n\n if (width == null && autoFit) {\n width = autoFitWidth(col, rows);\n }\n\n if (width != null) {\n xml += `\\n<Column ss:Width=\"${width * CHAR_WIDTH}\"/>`;\n } else {\n // Emit an empty column entry to keep ordinal alignment\n xml += '\\n<Column/>';\n }\n }\n\n return xml;\n}\n\n/** Estimate width from header + first N data rows (capped at 50). */\nfunction autoFitWidth(col: { field: string; header?: string }, rows: Record<string, unknown>[]): number {\n const sampleSize = Math.min(rows.length, 50);\n let maxLen = (col.header ?? col.field).length;\n\n for (let i = 0; i < sampleSize; i++) {\n const val = rows[i][col.field];\n const len = val == null ? 0 : String(val).length;\n if (len > maxLen) maxLen = len;\n }\n\n // Add a small padding\n return maxLen + 2;\n}\n\n// #endregion\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 { resolveCellValue } from '../../core/internal/value-accessor';\nimport type { ColumnConfig } from '../../core/types';\nimport { downloadBlob } from './csv';\nimport { buildColumnWidthsXml, buildStyleRegistry, resolveDataStyleId } from './excel-styles';\nimport type { ExportParams } from './types';\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 const styles = params.excelStyles;\n const registry = styles ? buildStyleRegistry(styles) : undefined;\n\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\n // Emit <Styles> block (only when styles are configured)\n if (registry) {\n // Pre-register dynamic cellStyle entries so they appear in <Styles>\n if (styles!.cellStyle) {\n for (const row of rows) {\n for (const col of columns) {\n const value = resolveCellValue(row, col);\n const dynamic = styles!.cellStyle(value, col.field, row);\n if (dynamic) registry.register(dynamic);\n }\n }\n }\n xml += registry.toXml();\n }\n\n xml += '\\n<Worksheet ss:Name=\"Sheet1\">\\n<Table>';\n\n // Column widths\n if (styles) {\n xml += buildColumnWidthsXml(columns, rows as Record<string, unknown>[], styles);\n }\n\n // Header style ID\n const headerStyleId = styles?.headerStyle && registry ? registry.getStyleId(styles.headerStyle) : undefined;\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 const styleAttr = headerStyleId ? ` ss:StyleID=\"${headerStyleId}\"` : '';\n xml += `<Cell${styleAttr}><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 = resolveCellValue(row, col);\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 // Resolve data cell style\n const dataStyleId = registry && styles ? resolveDataStyleId(registry, styles, value, col.field, row) : undefined;\n const styleAttr = dataStyleId ? ` ss:StyleID=\"${dataStyleId}\"` : '';\n\n xml += `<Cell${styleAttr}><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 blob = new Blob([content], {\n type: 'application/vnd.ms-excel;charset=utf-8;',\n });\n downloadBlob(blob, fileName);\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 { formatDateValue } from '../../core/internal/utils';\nimport { resolveCellValue } from '../../core/internal/value-accessor';\nimport { BaseGridPlugin, type PluginManifest, type PluginQuery } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, InternalGrid } from '../../core/types';\nimport { resolveColumns, resolveRows } from '../shared/data-collection';\nimport { buildCsv, type CsvOptions, downloadBlob, downloadCsv } from './csv';\nimport { buildExcelXml, downloadExcel } from './excel';\nimport type { ExportCompleteDetail, ExportConfig, ExportFormat, ExportMode, ExportParams } from './types';\n\n/**\n * Subset of {@link ExportParams} accepted by the pure formatters\n * ({@link ExportPlugin.formatCsv} / {@link ExportPlugin.formatExcel}).\n *\n * The formatters operate on already-resolved data, so options that affect\n * value resolution (`mode`, `rowIndices`, `format`, `fileName`,\n * `fileExtension`) are intentionally excluded — if you need them, run them\n * through {@link ExportPlugin.export} first or call the download/export\n * methods.\n *\n * `processCell` **is** honoured: it runs once per cell on the values you pass\n * in, just like the download methods.\n */\nexport type FormatCsvParams = Pick<ExportParams, 'columns' | 'includeHeaders' | 'processCell' | 'processHeader'>;\n\n/**\n * Subset of {@link ExportParams} accepted by {@link ExportPlugin.formatExcel}.\n * Same shape as {@link FormatCsvParams} plus `excelStyles`.\n */\nexport type FormatExcelParams = FormatCsvParams & Pick<ExportParams, 'excelStyles'>;\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 * ## 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 * @example Basic Export with Button\n * ```ts\n * import { queryGrid } from '@toolbox-web/grid';\n * import { ExportPlugin } from '@toolbox-web/grid/plugins/export';\n *\n * const grid = queryGrid('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.getPluginByName('export').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 SelectionPlugin for exporting selected rows\n *\n * @internal Extends BaseGridPlugin\n */\nexport class ExportPlugin extends BaseGridPlugin<ExportConfig> {\n /**\n * Plugin manifest — declares queries for inter-plugin communication.\n * @internal\n */\n static override readonly manifest: PluginManifest = {\n queries: [{ type: 'export:csv', description: 'Triggers a CSV export' }],\n };\n\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 Query System\n\n /** @internal */\n override handleQuery(query: PluginQuery): unknown {\n if (query.type === 'export:csv') {\n this.exportCsv();\n return true;\n }\n return undefined;\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Resolve the columns and rows that an export with these params would\n * include, plus a fully-defaulted ExportParams for downstream consumers.\n *\n * Honours `config.onlyVisible`, `config.onlySelected`, `params.columns`, and\n * `params.rowIndices`.\n */\n private resolveExportData(\n format: ExportFormat,\n params?: Partial<ExportParams>,\n ): { columns: ColumnConfig[]; rows: Record<string, unknown>[]; fullParams: ExportParams } {\n const config = this.config;\n\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 mode: params?.mode ?? 'raw',\n columns: params?.columns,\n rowIndices: params?.rowIndices,\n excelStyles: params?.excelStyles,\n fileExtension: params?.fileExtension,\n };\n\n const columns = resolveColumns(this.columns, params?.columns, config.onlyVisible) as ColumnConfig[];\n\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 return { columns, rows, fullParams };\n }\n\n /**\n * Apply the configured export `mode` to a single cell value.\n *\n * - `'raw'` : returns the underlying value (after optional `processCell`).\n * - `'formatted'` : applies the column-type default formatter and `column.format`,\n * returning the same string the grid displays. `processCell`\n * runs last on the formatted value.\n */\n private resolveCellOutput(\n value: unknown,\n col: ColumnConfig,\n row: Record<string, unknown>,\n mode: ExportMode,\n processCell?: (value: any, field: string, row: any) => any,\n ): unknown {\n let out: unknown = value;\n\n if (mode === 'formatted') {\n const formatFn = this.#resolveFormatFn(col);\n if (formatFn) {\n try {\n const formatted = formatFn(value, row);\n out = formatted == null ? '' : String(formatted);\n } catch {\n out = value == null ? '' : String(value);\n }\n } else if (col.type === 'date') {\n out = formatDateValue(value);\n } else if (col.type === 'boolean') {\n out = !!value;\n } else {\n out = value;\n }\n }\n\n if (processCell) {\n out = processCell(out, col.field, row);\n }\n\n return out;\n }\n\n private performExport(format: ExportFormat, params?: Partial<ExportParams>): void {\n const { columns, rows, fullParams } = this.resolveExportData(format, params);\n\n this.isExportingFlag = true;\n let fileName = fullParams.fileName ?? 'export';\n\n // buildCsv / buildExcelXml read each cell via `resolveCellValue(row, col)`.\n // Our pre-resolved row objects are keyed by `column.field`, so any\n // `valueAccessor` on the column would misfire on these synthetic rows.\n // Strip accessors before delegating to keep the lookup as a plain field read.\n const downstreamColumns = this.#stripAccessors(columns);\n\n try {\n switch (format) {\n case 'csv': {\n const data = this.#buildExportData(rows, columns, fullParams);\n // processCell already applied during data resolution — strip it so buildCsv\n // doesn't double-apply.\n const content = buildCsv(data, downstreamColumns, { ...fullParams, processCell: undefined }, { bom: true });\n fileName = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`;\n downloadCsv(content, fileName);\n break;\n }\n\n case 'excel': {\n const data = this.#buildExportData(rows, columns, fullParams);\n const content = buildExcelXml(data, downstreamColumns, { ...fullParams, processCell: undefined });\n const ext = fullParams.fileExtension ?? '.xls';\n const normalizedExt = ext.startsWith('.') ? ext : `.${ext}`;\n fileName = fileName.endsWith(normalizedExt) ? fileName : `${fileName}${normalizedExt}`;\n downloadExcel(content, fileName);\n break;\n }\n\n case 'json': {\n const data = this.#buildExportData(rows, columns, fullParams);\n const content = JSON.stringify(data, 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 /**\n * Resolve every cell into a plain object keyed by `column.field`, applying\n * the configured `mode` and `processCell` once. The result can be fed to\n * `buildCsv` / `buildExcelXml` directly — they read values via\n * `resolveCellValue`, which on plain key/value objects is a simple\n * `row[field]` lookup, so no double-resolution occurs.\n */\n #buildExportData(\n rows: Record<string, unknown>[],\n columns: ColumnConfig[],\n params: ExportParams,\n ): Record<string, unknown>[] {\n const mode = params.mode ?? 'raw';\n return rows.map((row) => {\n const obj: Record<string, unknown> = {};\n for (const col of columns) {\n const raw = resolveCellValue(row, col);\n obj[col.field] = this.resolveCellOutput(raw, col, row, mode, params.processCell);\n }\n return obj;\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 * Returns the row data that would be included in an export, without\n * producing a file. Honours `mode`, `onlyVisible`, `onlySelected`,\n * `columns`, `rowIndices`, `processCell`, and (for `mode: 'formatted'`)\n * `column.format`.\n *\n * Each returned object is keyed by `column.field` in column order.\n *\n * @example\n * ```ts\n * // Underlying typed values (Date stays Date, number stays number)\n * const raw = exporter.export();\n *\n * // What the user sees in each cell\n * const display = exporter.export({ mode: 'formatted' });\n * ```\n */\n export(params?: Partial<ExportParams>): Record<string, unknown>[] {\n const { columns, rows, fullParams } = this.resolveExportData('json', params);\n return this.#buildExportData(rows, columns, fullParams);\n }\n\n /**\n * Returns the columns (in order) that an export with these params would\n * include. Useful when handing rows to a third-party serializer that needs\n * header labels, widths, or types alongside the data.\n */\n getResolvedColumns(params?: Partial<ExportParams>): ColumnConfig[] {\n return resolveColumns(this.columns, params?.columns, this.config.onlyVisible) as ColumnConfig[];\n }\n\n /**\n * Format an array of row objects as a CSV string. Column order, headers,\n * and which fields to emit are taken from the plugin's resolved columns\n * (respecting `onlyVisible` and `params.columns`).\n *\n * This is a **pure formatter** — it does not re-resolve values from the\n * grid. Pass `data` produced by {@link ExportPlugin.export} (or any\n * compatible row objects keyed by `column.field`).\n *\n * `params.processCell` is honoured: it runs once per cell on the values in\n * `data`. `mode` is **not** accepted here — apply it upstream via\n * `export({ mode: 'formatted' })`.\n *\n * @example\n * ```ts\n * const csv = exporter.formatCsv(exporter.export());\n * await navigator.clipboard.writeText(csv);\n * ```\n */\n formatCsv(data: Record<string, unknown>[], params?: FormatCsvParams, options?: CsvOptions): string {\n const { columns, fullParams } = this.resolveExportData('csv', params);\n return buildCsv(data, this.#stripAccessors(columns), fullParams, options);\n }\n\n /**\n * Format an array of row objects as an Excel XML Spreadsheet 2003 string.\n * See {@link formatCsv} for the data-shape contract — this method is also a\n * pure formatter and `params.processCell` is honoured the same way.\n */\n formatExcel(data: Record<string, unknown>[], params?: FormatExcelParams): string {\n const { columns, fullParams } = this.resolveExportData('excel', params);\n return buildExcelXml(data, this.#stripAccessors(columns), fullParams);\n }\n\n /** @internal Drop `valueAccessor` so downstream builders read pre-resolved fields. */\n #stripAccessors(columns: ColumnConfig[]): ColumnConfig[] {\n return columns.map((c) => (c.valueAccessor ? ({ ...c, valueAccessor: undefined } as ColumnConfig) : c));\n }\n\n /**\n * @internal Resolve the format function for a column for `mode: 'formatted'`.\n * Mirrors the priority chain in core/internal/rows.ts (`column.format` → adapter\n * type default), but inlined so this module stays free of browser-only imports.\n */\n #resolveFormatFn(col: ColumnConfig): ((value: unknown, row: unknown) => string) | undefined {\n if (col.format) return col.format as (value: unknown, row: unknown) => string;\n if (!col.type) return undefined;\n // The grid host carries optional `__frameworkAdapter` and `_hostElement` from\n // InternalGrid. Narrow with a structural guard rather than an `as unknown as`\n // cast — these keys are documented public-internal in core/types.ts.\n const grid = this.grid as Partial<Pick<InternalGrid<unknown>, '__frameworkAdapter' | '_hostElement'>> | undefined;\n const adapter = grid?.__frameworkAdapter;\n if (!adapter?.getTypeDefault) return undefined;\n const appDefault = adapter.getTypeDefault(col.type, grid?._hostElement);\n return appDefault?.format as ((value: unknown, row: unknown) => string) | undefined;\n }\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","filter","c","hidden","field","startsWith","utility","length","fieldSet","Set","has","resolveRows","rows","indices","sort","a","b","map","i","r","formatCsvValue","value","quote","Date","toISOString","JSON","stringify","str","String","includes","replace","buildCsv","params","options","delimiter","newline","quoteStrings","lines","bom","includeHeaders","headerRow","col","header","processHeader","push","join","row","cells","resolveCellValue","processCell","downloadBlob","blob","fileName","url","URL","createObjectURL","link","document","createElement","href","download","style","display","body","appendChild","click","removeChild","revokeObjectURL","hashStyle","sortKeys","obj","Array","isArray","sorted","key","Object","keys","StyleRegistry","entries","Map","counter","register","hash","existing","this","get","id","set","getStyleId","size","toXml","xml","values","buildStyleElement","font","name","bold","italic","color","fill","pattern","numberFormat","alignment","horizontal","vertical","wrapText","borders","top","buildBorderElement","bottom","left","right","position","border","borderWeight","resolveDataStyleId","registry","config","cellStyle","dynamic","colStyle","columnStyles","defaultStyle","autoFitWidth","sampleSize","Math","min","maxLen","val","len","escapeXml","buildExcelXml","styles","excelStyles","headerStyle","buildStyleRegistry","widths","columnWidths","autoFit","autoFitColumns","width","buildColumnWidthsXml","headerStyleId","type","displayValue","isNaN","dataStyleId","ExportPlugin","BaseGridPlugin","static","queries","description","defaultConfig","onlySelected","isExportingFlag","lastExportInfo","handleQuery","query","exportCsv","resolveExportData","format","fullParams","mode","rowIndices","fileExtension","selectionState","getSelectionState","selected","resolveCellOutput","out","formatFn","resolveFormatFn","formatted","formatDateValue","performExport","downstreamColumns","stripAccessors","content","buildExportData","endsWith","Blob","downloadCsv","ext","normalizedExt","downloadExcel","data","timestamp","emit","rowCount","columnCount","raw","grid","getPluginState","getResolvedColumns","formatCsv","formatExcel","valueAccessor","adapter","__frameworkAdapter","getTypeDefault","appDefault","_hostElement","exportExcel","exportJson","isExporting","getLastExport"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("../../core/constants"),require("../../core/internal/keyboard"),require("../../core/plugin/base-plugin")):"function"==typeof define&&define.amd?define(["exports","../../core/constants","../../core/internal/keyboard","../../core/plugin/base-plugin"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_reorderRows={},e.TbwGrid,e.TbwGrid,e.TbwGrid)}(this,function(e,t,r,o){"use strict";const n="__tbw_row_drag";class i extends o.BaseGridPlugin{name="reorderRows";aliases=["rowReorder"];styles='@layer tbw-plugins{[data-field=__tbw_row_drag]{display:flex;align-items:center;justify-content:center}.dg-row-drag-handle{display:flex;align-items:center;justify-content:center;min-width:1em;min-height:1em;cursor:grab;-webkit-user-select:none;user-select:none;color:var(--tbw-row-reorder-handle-color, var(--tbw-color-fg-muted));transition:color var(--tbw-transition-duration, .12s) var(--tbw-transition-ease, ease);font-size:var(--tbw-font-size, 1em);letter-spacing:-2px}.dg-row-drag-handle:hover{color:var(--tbw-row-reorder-handle-hover, var(--tbw-color-fg))}.dg-row-drag-handle:active{cursor:grabbing}.data-grid-row.dragging{opacity:.6}.data-grid-row.drop-target{position:relative}.data-grid-row.drop-target.drop-before:before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background-color:var(--tbw-row-reorder-indicator, var(--tbw-color-accent));z-index:10}.data-grid-row.drop-target.drop-after:after{content:"";position:absolute;bottom:0;left:0;right:0;height:2px;background-color:var(--tbw-row-reorder-indicator, var(--tbw-color-accent));z-index:10}.data-grid-row.keyboard-moving{background-color:var(--tbw-row-reorder-moving-bg, var(--tbw-focus-background));box-shadow:0 0 0 1px var(--tbw-row-reorder-moving-border, var(--tbw-color-accent)) inset}.data-grid-row.flip-animating{transition:transform var(--tbw-animation-duration, .2s) ease-out;will-change:transform;z-index:1}}';get defaultConfig(){return{enableKeyboard:!0,showDragHandle:!0,dragHandlePosition:"left",dragHandleWidth:40,debounceMs:150,animation:"flip"}}get animationType(){return!!this.isAnimationEnabled&&(void 0!==this.config.animation?this.config.animation:"flip")}isDragging=!1;draggedRowIndex=null;dropRowIndex=null;pendingMove=null;debounceTimer=null;lastFocusCol=0;get#e(){return this.grid}attach(e){super.attach(e),this.setupDelegatedDragListeners()}detach(){this.clearDebounceTimer(),this.isDragging=!1,this.draggedRowIndex=null,this.dropRowIndex=null,this.pendingMove=null}processColumns(e){if(!this.config.showDragHandle)return[...e];const t={field:n,header:"",width:this.config.dragHandleWidth??40,resizable:!1,sortable:!1,filterable:!1,lockPosition:!0,utility:!0,viewRenderer:()=>{const e=document.createElement("div");return e.className="dg-row-drag-handle",e.setAttribute("aria-label","Drag to reorder"),e.setAttribute("role","button"),e.setAttribute("tabindex","-1"),e.draggable=!0,this.setIcon(e,"dragHandle"),e}};return"right"===this.config.dragHandlePosition?[...e,t]:[t,...e]}afterRender(){}onKeyDown(e){if(!this.config.enableKeyboard)return;if(!e.ctrlKey||"ArrowUp"!==e.key&&"ArrowDown"!==e.key)return;const t=this.#e,r=t._focusRow,o=t._rows??this.sourceRows;if(r<0||r>=o.length)return;const n="ArrowUp"===e.key?"up":"down",i="up"===n?r-1:r+1;if(i<0||i>=o.length)return;const s=o[r];return this.canMoveRow(r,i)?(this.handleKeyboardMove(s,r,i,n,t._focusCol),e.preventDefault(),e.stopPropagation(),!0):void 0}onCellClick(){this.flushPendingMove()}moveRow(e,t){const r=[...this.sourceRows];if(e<0||e>=r.length)return;if(t<0||t>=r.length)return;if(e===t)return;const o=r[e];this.canMoveRow(e,t)&&this.executeMove(o,e,t,"keyboard")}canMoveRow(e,t){const r=this.sourceRows;if(e<0||e>=r.length)return!1;if(t<0||t>=r.length)return!1;if(e===t)return!1;const o=r[e],n=this.grid?.query?.("canMoveRow",o);if(Array.isArray(n)&&n.includes(!1))return!1;if(!this.config.canMove)return!0;const i=t<e?"up":"down";return this.config.canMove(r[e],e,t,i)}setupDelegatedDragListeners(){const e=this.gridElement;if(!e)return;const r=this.disconnectSignal;e.addEventListener("dragstart",e=>{const r=e,o=r.target.closest(".dg-row-drag-handle");if(!o)return;const n=o.closest(".data-grid-row");if(!n)return;const i=this.getRowIndex(n);i<0||(this.isDragging=!0,this.draggedRowIndex=i,r.dataTransfer&&(r.dataTransfer.effectAllowed="move",r.dataTransfer.setData("text/plain",String(i))),n.classList.add(t.GridClasses.DRAGGING))},{signal:r}),e.addEventListener("dragend",()=>{this.isDragging=!1,this.draggedRowIndex=null,this.dropRowIndex=null,this.clearDragClasses()},{signal:r}),e.addEventListener("dragover",e=>{const t=e;if(!this.isDragging||null===this.draggedRowIndex)return;const r=t.target.closest(".data-grid-row");if(!r)return;t.preventDefault();const o=this.getRowIndex(r);if(o<0||o===this.draggedRowIndex)return;const n=r.getBoundingClientRect(),i=n.top+n.height/2,s=t.clientY<i;this.dropRowIndex=s?o:o+1,r.classList.add("drop-target"),r.classList.toggle("drop-before",s),r.classList.toggle("drop-after",!s)},{signal:r}),e.addEventListener("dragleave",e=>{const t=e.target.closest(".data-grid-row");t&&t.classList.remove("drop-target","drop-before","drop-after")},{signal:r}),e.addEventListener("drop",e=>{e.preventDefault();const t=this.draggedRowIndex;let r=this.dropRowIndex;if(this.isDragging&&null!==t&&null!==r&&(r>t&&r--,t!==r)){const e=this.sourceRows[t];this.canMoveRow(t,r)&&this.executeMove(e,t,r,"drag")}},{signal:r})}handleKeyboardMove(e,t,o,n,i){this.pendingMove?this.pendingMove.currentIndex=o:this.pendingMove={originalIndex:t,currentIndex:o,row:e},this.lastFocusCol=i;const s=this.#e,a=[...s._rows??this.sourceRows],[d]=a.splice(t,1);a.splice(o,0,d),s._rows=a,s._focusRow=o,s._focusCol=i,s.refreshVirtualWindow(!0),r.ensureCellVisible(s),this.clearDebounceTimer(),this.debounceTimer=setTimeout(()=>{this.flushPendingMove()},this.config.debounceMs??300)}flushPendingMove(){if(this.clearDebounceTimer(),!this.pendingMove)return;const{originalIndex:e,currentIndex:t,row:o}=this.pendingMove;if(this.pendingMove=null,e===t)return;const n={row:o,fromIndex:e,toIndex:t,rows:[...this.sourceRows],source:"keyboard"};if(this.emitCancelable("row-move",n)){const o=[...this.sourceRows],[n]=o.splice(t,1);o.splice(e,0,n);const i=this.#e;i._rows=o,i._focusRow=e,i._focusCol=this.lastFocusCol,i.refreshVirtualWindow(!0),r.ensureCellVisible(i)}}executeMove(e,t,r,o){const n=[...this.sourceRows],[i]=n.splice(t,1);n.splice(r,0,i);const s={row:e,fromIndex:t,toIndex:r,rows:n,source:o};if(!this.emitCancelable("row-move",s))if("flip"===this.animationType&&this.gridElement){const e=this.captureRowPositions();this.grid.rows=n,requestAnimationFrame(()=>{this.gridElement.offsetHeight,this.animateFLIP(e,t,r)})}else this.grid.rows=n}captureRowPositions(){const e=new Map;return this.gridElement?.querySelectorAll(".data-grid-row").forEach(t=>{const r=this.getRowIndex(t);r>=0&&e.set(r,t.getBoundingClientRect().top)}),e}animateFLIP(e,t,r){const o=this.gridElement;if(!o||0===e.size)return;const n=Math.min(t,r),i=Math.max(t,r),s=[];if(o.querySelectorAll(".data-grid-row").forEach(o=>{const a=o,d=this.getRowIndex(a);if(d<0||d<n||d>i)return;let l;l=d===r?t:t<r?d+1:d-1;const c=e.get(l);if(void 0===c)return;const g=c-a.getBoundingClientRect().top;Math.abs(g)>1&&s.push({el:a,deltaY:g})}),0===s.length)return;s.forEach(({el:e,deltaY:t})=>{e.style.transform=`translateY(${t}px)`}),o.offsetHeight;const a=this.animationDuration;requestAnimationFrame(()=>{s.forEach(({el:e})=>{e.classList.add("flip-animating"),e.style.transform=""}),setTimeout(()=>{s.forEach(({el:e})=>{e.style.transform="",e.classList.remove("flip-animating")})},a+50)})}getRowIndex(e){const t=e.querySelector(".cell[data-row]");return t?parseInt(t.getAttribute("data-row")??"-1",10):-1}clearDragClasses(){this.gridElement?.querySelectorAll(".data-grid-row").forEach(e=>{e.classList.remove(t.GridClasses.DRAGGING,"drop-target","drop-before","drop-after")})}clearDebounceTimer(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null)}}e.ROW_DRAG_HANDLE_FIELD=n,e.RowReorderPlugin=i,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
1
|
+
!function(e,r){"object"==typeof exports&&"undefined"!=typeof module?r(exports,require("../../core/constants"),require("../../core/internal/drag-drop-registry"),require("../../core/internal/keyboard"),require("../../core/plugin/base-plugin")):"function"==typeof define&&define.amd?define(["exports","../../core/constants","../../core/internal/drag-drop-registry","../../core/internal/keyboard","../../core/plugin/base-plugin"],r):r((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_reorderRows={},e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid)}(this,function(e,r,t,o,i){"use strict";let n=null;function s(){return n}function a(){n=null}const d="application/x-tbw-grid-rows+json";function l(e){const r=e.match(/^application\/x-tbw-grid-rows\+json;zone=(.*)$/);if(!r)return null;try{return decodeURIComponent(r[1])}catch{return r[1]}}function c(e){return JSON.stringify(e)}function g(e,r,t,o){if(!e)return{overIndex:null,insertIndex:o,isBefore:!1};const i=t(e);if(i<0)return{overIndex:null,insertIndex:o,isBefore:!1};const n=e.getBoundingClientRect(),s=r<n.top+n.height/2;return{overIndex:i,insertIndex:s?i:i+1,isBefore:s}}class u extends i.BaseGridPlugin{name="rowDragDrop";aliases=["reorderRows","rowReorder"];styles='@layer tbw-plugins{[data-field=__tbw_row_drag]{display:flex;align-items:center;justify-content:center}.dg-row-drag-handle{display:flex;align-items:center;justify-content:center;min-width:1em;min-height:1em;cursor:grab;-webkit-user-select:none;user-select:none;color:var(--tbw-row-reorder-handle-color, var(--tbw-color-fg-muted));transition:color var(--tbw-transition-duration, .12s) var(--tbw-transition-ease, ease);font-size:var(--tbw-font-size, 1em);letter-spacing:-2px}.dg-row-drag-handle:hover{color:var(--tbw-row-reorder-handle-hover, var(--tbw-color-fg))}.dg-row-drag-handle:active{cursor:grabbing}.data-grid-row.dragging{opacity:.6}.tbw-row-drag-count{position:fixed;top:-1000px;left:-1000px;pointer-events:none;background:var(--tbw-row-reorder-indicator, var(--tbw-color-accent));color:var(--tbw-color-accent-fg);font:600 12px/1.4 system-ui,sans-serif;border-radius:999px;padding:2px 8px;box-shadow:0 2px 6px var(--tbw-color-shadow);z-index:9999}.data-grid-row.drop-target{position:relative}.data-grid-row.drop-target.drop-before:before,.data-grid-row.drop-target.drop-after:after{content:"";position:absolute;left:0;right:0;height:var(--tbw-drop-indicator-width, 2px);background-color:var(--tbw-drop-indicator-color, var(--tbw-row-reorder-indicator, var(--tbw-color-accent)));z-index:10;pointer-events:none}.data-grid-row.drop-target.drop-before:before{top:0}.data-grid-row.drop-target.drop-after:after{bottom:0}.tbw-grid--drop-target-active{outline:2px dashed var(--tbw-drop-indicator-color, var(--tbw-color-accent));outline-offset:-2px}.tbw-grid--drop-target-rejected{outline:2px dashed var(--tbw-color-danger);outline-offset:-2px}.tbw-grid--auto-scrolling{cursor:ns-resize}.data-grid-row.flip-animating{transition:transform var(--tbw-animation-duration, .2s) ease-out;will-change:transform;z-index:1}}';static manifest={events:[{type:"row-move",description:"Intra-grid row reorder.",cancelable:!0},{type:"row-drag-start",description:"Cross-grid drag started on this grid.",cancelable:!0},{type:"row-drag-end",description:"Drag finished on this grid (regardless of outcome)."},{type:"row-drop",description:"Cross-grid drop landing on this grid.",cancelable:!0},{type:"row-transfer",description:"Cross-grid transfer completed (fires on both grids)."}]};get defaultConfig(){return{enableKeyboard:!0,showDragHandle:!0,dragHandlePosition:"left",dragHandleWidth:40,debounceMs:150,animation:"flip",operation:"move",autoScroll:!0}}get animationType(){return!!this.isAnimationEnabled&&(void 0!==this.config.animation?this.config.animation:"flip")}isDragging=!1;draggedRowIndex=null;draggedRows=[];draggedIndices=[];dragSessionId=null;dragAccepted=!1;dropRowIndex=null;pendingMove=null;debounceTimer=null;lastFocusCol=0;autoScroller=null;gridId="";get internalGrid(){return this.grid}attach(e){super.attach(e);const r=this.gridElement;r&&(this.gridId=r.id||`tbw-grid-${t.newDragSessionId().slice(0,8)}`,r.id||(r.id=this.gridId),this.setupDelegatedDragListeners())}detach(){this.clearDebounceTimer(),this.autoScroller?.stop(),this.autoScroller=null,this.dragSessionId&&t.clearDragSession(this.dragSessionId),a(),this.resetDragState(),super.detach()}processColumns(e){if(!this.config.showDragHandle)return[...e];const r={field:"__tbw_row_drag",header:"",width:this.config.dragHandleWidth??40,resizable:!1,sortable:!1,filterable:!1,lockPosition:!0,utility:!0,viewRenderer:()=>{const e=document.createElement("div");return e.className="dg-row-drag-handle",e.setAttribute("aria-label","Drag to reorder"),e.setAttribute("role","button"),e.setAttribute("tabindex","-1"),e.draggable=!0,this.setIcon(e,"dragHandle"),e}};return"right"===this.config.dragHandlePosition?[...e,r]:[r,...e]}afterRender(){}onKeyDown(e){if(!this.config.enableKeyboard)return;if(!e.ctrlKey||"ArrowUp"!==e.key&&"ArrowDown"!==e.key)return;const r=this.internalGrid,t=r._focusRow,o=r._rows??this.sourceRows;if(t<0||t>=o.length)return;const i="up"===("ArrowUp"===e.key?"up":"down")?t-1:t+1;if(i<0||i>=o.length)return;const n=o[t];return this.canMoveRow(t,i)?(this.handleKeyboardMove(n,t,i,r._focusCol),e.preventDefault(),e.stopPropagation(),!0):void 0}onCellClick(){this.flushPendingMove()}moveRow(e,r){const t=[...this.sourceRows];e<0||e>=t.length||r<0||r>=t.length||e!==r&&this.canMoveRow(e,r)&&this.executeIntraGridMove(t[e],e,r,"keyboard")}canMoveRow(e,r){const t=this.internalGrid._rows??this.sourceRows;if(e<0||e>=t.length)return!1;if(r<0||r>=t.length)return!1;if(e===r)return!1;const o=t[e],i=this.grid?.query?.("canMoveRow",o);if(Array.isArray(i)&&i.includes(!1))return!1;if(this.config.canDrag&&!this.config.canDrag(o,e))return!1;if(this.config.canMove){const t=r<e?"up":"down";if(!this.config.canMove(o,e,r,t))return!1}if(this.config.canDrop){const t={sessionId:"intra",sourceGridId:this.gridId,dropZone:this.config.dropZone??"",rows:[o],rowIndices:[e],operation:"move"};if(!this.config.canDrop(t,r))return!1}return!0}setupDelegatedDragListeners(){const e=this.gridElement;if(!e)return;const r=this.disconnectSignal;e.addEventListener("dragstart",e=>this.onDragStart(e),{signal:r}),e.addEventListener("dragend",()=>this.onDragEnd(),{signal:r}),e.addEventListener("dragover",e=>this.onDragOver(e),{signal:r}),e.addEventListener("dragleave",e=>this.onDragLeave(e),{signal:r}),e.addEventListener("drop",e=>this.onDrop(e),{signal:r})}onDragStart(e){const o=e.target.closest(".dg-row-drag-handle");if(!o)return;const i=o.closest(".data-grid-row");if(!i)return;const s=this.getRowIndex(i);if(s<0)return;const{rows:a,indices:l}=this.resolveDraggedRows(s);if(0===a.length)return;if(this.config.canDrag&&!this.config.canDrag(a[0],s))return void e.preventDefault();const g=this.config.operation??"move",u=this.config.dropZone??"",h=t.newDragSessionId(),f={rows:a,indices:l,operation:g,dropZone:u};if(this.emitCancelable("row-drag-start",f))return void e.preventDefault();this.isDragging=!0,this.draggedRowIndex=s,this.draggedRows=a,this.draggedIndices=l,this.dragSessionId=h,this.dragAccepted=!1;const p=this.config.serializeRow??(e=>e),w={sessionId:h,sourceGridId:this.gridId,dropZone:u,rows:a.map(p),rowIndices:l,operation:g};if(e.dataTransfer){e.dataTransfer.effectAllowed="copy"===g?"copyMove":"move";try{e.dataTransfer.setData(d,c(w)),u&&e.dataTransfer.setData(`${d};zone=${encodeURIComponent(u)}`,c(w)),e.dataTransfer.setData("text/plain",function(e,r){const t=r.filter(e=>!e.utility&&"string"==typeof e.field&&""!==e.field),o=[];for(const i of e){const e=t.map(e=>{const r=i[e.field];return null==r?"":String(r).replace(/[\t\r\n]+/g," ")});o.push(e.join("\t"))}return o.join("\n")}(a,this.columns))}catch{}if(a.length>1){const r=document.createElement("div");r.className="tbw-row-drag-count",r.textContent=`${a.length} rows`,document.body.appendChild(r);try{e.dataTransfer.setDragImage(r,10,10)}catch{}setTimeout(()=>r.remove(),0)}}t.registerDragSession(h,a),function(e,r){n={sessionId:e,payload:r}}(h,w),i.classList.add(r.GridClasses.DRAGGING),this.gridElement.classList.add("tbw-grid--drag-source")}onDragOver(e){const r=e.dataTransfer;if(!r)return;const t=r.types?Array.from(r.types):[];if(!function(e){for(const r of e)if(r===d||r.startsWith(`${d};`))return!0;return!1}(t)&&!this.isDragging)return;const o=this.config.dropZone??"",i=s(),n=this.isDragging&&i?.payload.sourceGridId===this.gridId;if(!n){if(!o)return;const e=function(e,r){for(const t of e)if(l(t)===r)return t;return null}(t,o);if(!(e||i&&i.payload.dropZone===o))return}e.preventDefault(),r&&(r.dropEffect=i?.payload.operation??this.config.operation??"move");const a=e.target.closest(".data-grid-row"),c=this.internalGrid._rows??[],u=g(a,e.clientY,e=>this.getRowIndex(e),c.length);if(n&&null!==u.overIndex&&u.overIndex===this.draggedRowIndex)this.clearDropTargetClasses();else{if(i&&this.config.canDrop){const e=this.config.canDrop(i.payload,u.insertIndex);if(this.gridElement.classList.toggle("tbw-grid--drop-target-active",e),this.gridElement.classList.toggle("tbw-grid--drop-target-rejected",!e),!e)return void this.clearDropTargetClasses()}else this.gridElement.classList.add("tbw-grid--drop-target-active");this.dropRowIndex=u.insertIndex,this.applyDropPositionClasses(a,u.isBefore),!1!==this.config.autoScroll&&(this.ensureAutoScroller(),this.autoScroller?.onPointerMove(e.clientY))}}onDragLeave(e){const r=e.target.closest(".data-grid-row");r&&r.classList.remove("drop-target","drop-before","drop-after"),e.currentTarget&&!this.gridElement.contains(e.relatedTarget)&&(this.gridElement.classList.remove("tbw-grid--drop-target-active","tbw-grid--drop-target-rejected"),this.autoScroller?.stop())}onDrop(e){e.preventDefault(),this.autoScroller?.stop(),this.gridElement.classList.remove("tbw-grid--drop-target-active","tbw-grid--drop-target-rejected"),this.clearDropTargetClasses();const r=e.dataTransfer;if(!r)return;const o=s();let i=o?.payload??null,n=null;if(i){const e=t.lookupDragSession(i.sessionId);e&&(n=e)}else{if(i=function(e){if(!e)return null;try{const r=JSON.parse(e);if("string"!=typeof r?.sessionId||"string"!=typeof r?.sourceGridId||"string"!=typeof r?.dropZone||!Array.isArray(r?.rows)||!Array.isArray(r?.rowIndices)||"move"!==r.operation&&"copy"!==r.operation)return null;for(const e of r.rowIndices)if("number"!=typeof e||!Number.isInteger(e)||e<0)return null;return r}catch{return null}}(r.getData(d)),i){const e=t.lookupDragSession(i.sessionId);e&&(n=e)}}if(!i)return;const a=e.target.closest(".data-grid-row"),l=this.internalGrid._rows??[],c=g(a,e.clientY,e=>this.getRowIndex(e),l.length);let u=this.dropRowIndex??c.insertIndex;const h=i.sourceGridId===this.gridId,f=this.config.dropZone??"";if(h){const e=i.rowIndices[0];if(1===i.rowIndices.length&&u>e&&u--,e===u)return;const r=(n??i.rows)[0];if(!this.canMoveRow(e,u))return;return void this.executeIntraGridMove(r,e,u,"drag")}if(!f||f!==i.dropZone)return;if(this.config.canDrop&&!this.config.canDrop(i,u))return this.gridElement.classList.add("tbw-grid--drop-target-rejected"),void setTimeout(()=>this.gridElement.classList.remove("tbw-grid--drop-target-rejected"),200);const p=this.config.deserializeRow??(e=>e),w=n??i.rows.map(e=>p(e)),m={payload:i,sourceGridId:i.sourceGridId,targetIndex:u,operation:i.operation};if(this.emitCancelable("row-drop",m))return;const b=[...l];if(b.splice(u,0,...w),this.grid.rows=b,"move"===i.operation){const e=document.getElementById(i.sourceGridId);if(e){const r=(e._rows??e.rows??[]).slice(),t=[...i.rowIndices].sort((e,r)=>r-e);for(const e of t)e>=0&&e<r.length&&r.splice(e,1);e.rows=r}}const v=this.findPeerOnGrid(i.sourceGridId);v&&(v.dragAccepted=!0);const I={rows:w,fromGridId:i.sourceGridId,toGridId:this.gridId,fromIndices:i.rowIndices,toIndex:u,operation:i.operation};this.emit("row-transfer",I),v?.emitTransfer(I)}onDragEnd(){if(this.dragSessionId&&t.clearDragSession(this.dragSessionId),a(),this.autoScroller?.stop(),this.gridElement.classList.remove("tbw-grid--drag-source"),this.isDragging){const e={rows:this.draggedRows,indices:this.draggedIndices,accepted:this.dragAccepted};this.emit("row-drag-end",e)}this.clearDragClasses(),this.resetDragState()}emitTransfer(e){this.emit("row-transfer",e)}findPeerOnGrid(e){const r=document.getElementById(e);return r?.getPluginByName?r.getPluginByName("rowDragDrop")??null:null}resolveDraggedRows(e){const r=this.internalGrid._rows??this.sourceRows,t=r[e],o=this.grid?.getPluginByName?.("selection");if(o?.getSelectedRowIndices){const t=o.getSelectedRowIndices();if(t.includes(e)&&t.length>1){const e=[...t].sort((e,r)=>e-r);return{rows:e.map(e=>r[e]),indices:e}}}return{rows:[t],indices:[e]}}ensureAutoScroller(){if(this.autoScroller)return;const e=this.gridElement.querySelector(".rows-viewport");if(!e)return;const r="object"==typeof this.config.autoScroll?this.config.autoScroll:void 0;this.autoScroller=function(e,r={},t){const o=r.edgeSize??60,i=r.speed??8,n=r.maxSpeed??24;let s=null,a=null,d=!1;const l=e=>{e!==d&&(d=e,t?.(e))},c=()=>{if(s=null,null===a)return void l(!1);const r=e.getBoundingClientRect();let t=0;if(a<r.top+o){const e=1-Math.max(0,a-r.top)/o;t=-Math.round(i+(n-i)*e)}else if(a>r.bottom-o){const e=1-Math.max(0,r.bottom-a)/o;t=Math.round(i+(n-i)*e)}if(0===t)return void l(!1);const d=e.scrollTop;e.scrollTop=d+t,e.scrollTop!==d?(l(!0),s=requestAnimationFrame(c)):l(!1)};return{onPointerMove(e){a=e,null===s&&(s=requestAnimationFrame(c))},stop(){null!==s&&(cancelAnimationFrame(s),s=null),a=null,l(!1)},get isScrolling(){return d}}}(e,r,e=>{this.gridElement.classList.toggle("tbw-grid--auto-scrolling",e)})}applyDropPositionClasses(e,r){this.clearDropTargetClasses(),e&&(e.classList.add("drop-target"),e.classList.toggle("drop-before",r),e.classList.toggle("drop-after",!r))}clearDropTargetClasses(){this.gridElement?.querySelectorAll(".data-grid-row.drop-target").forEach(e=>{e.classList.remove("drop-target","drop-before","drop-after")})}clearDragClasses(){this.gridElement?.querySelectorAll(".data-grid-row").forEach(e=>{e.classList.remove(r.GridClasses.DRAGGING,"drop-target","drop-before","drop-after")})}resetDragState(){this.isDragging=!1,this.draggedRowIndex=null,this.draggedRows=[],this.draggedIndices=[],this.dragSessionId=null,this.dragAccepted=!1,this.dropRowIndex=null,this.pendingMove=null}getRowIndex(e){const r=e.querySelector(".cell[data-row]");return r?parseInt(r.getAttribute("data-row")??"-1",10):-1}clearDebounceTimer(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null)}handleKeyboardMove(e,r,t,i){this.pendingMove?this.pendingMove.currentIndex=t:this.pendingMove={originalIndex:r,currentIndex:t,row:e},this.lastFocusCol=i;const n=this.internalGrid,s=[...n._rows??this.sourceRows],[a]=s.splice(r,1);s.splice(t,0,a),n._rows=s,n._focusRow=t,n._focusCol=i,n.refreshVirtualWindow(!0),o.ensureCellVisible(n),this.clearDebounceTimer(),this.debounceTimer=setTimeout(()=>this.flushPendingMove(),this.config.debounceMs??300)}flushPendingMove(){if(this.clearDebounceTimer(),!this.pendingMove)return;const{originalIndex:e,currentIndex:r,row:t}=this.pendingMove;if(this.pendingMove=null,e===r)return;const i=this.internalGrid,n={row:t,fromIndex:e,toIndex:r,rows:[...i._rows??this.sourceRows],source:"keyboard"};this.emitCancelable("row-move",n)&&(i._rows=[...this.sourceRows],i._focusRow=e,i._focusCol=this.lastFocusCol,i.refreshVirtualWindow(!0),o.ensureCellVisible(i))}executeIntraGridMove(e,r,t,o){const i=[...this.sourceRows],[n]=i.splice(r,1);i.splice(t,0,n);const s={row:e,fromIndex:r,toIndex:t,rows:i,source:o};if(!this.emitCancelable("row-move",s))if("flip"===this.animationType&&this.gridElement){const e=this.captureRowPositions();this.grid.rows=i,requestAnimationFrame(()=>{this.gridElement.offsetHeight,this.animateFLIP(e,r,t)})}else this.grid.rows=i}captureRowPositions(){const e=new Map;return this.gridElement?.querySelectorAll(".data-grid-row").forEach(r=>{const t=this.getRowIndex(r);t>=0&&e.set(t,r.getBoundingClientRect().top)}),e}animateFLIP(e,r,t){const o=this.gridElement;if(!o||0===e.size)return;const i=Math.min(r,t),n=Math.max(r,t),s=[];if(o.querySelectorAll(".data-grid-row").forEach(o=>{const a=o,d=this.getRowIndex(a);if(d<0||d<i||d>n)return;let l;l=d===t?r:r<t?d+1:d-1;const c=e.get(l);if(void 0===c)return;const g=c-a.getBoundingClientRect().top;Math.abs(g)>1&&s.push({el:a,deltaY:g})}),0===s.length)return;s.forEach(({el:e,deltaY:r})=>{e.style.transform=`translateY(${r}px)`}),o.offsetHeight;const a=this.animationDuration;requestAnimationFrame(()=>{s.forEach(({el:e})=>{e.classList.add("flip-animating"),e.style.transform=""}),setTimeout(()=>{s.forEach(({el:e})=>{e.style.transform="",e.classList.remove("flip-animating")})},a+50)})}}e.RowReorderPlugin=u,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
|
|
2
2
|
//# sourceMappingURL=reorder-rows.umd.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reorder-rows.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/reorder-rows/RowReorderPlugin.ts"],"sourcesContent":["/**\n * Row Reordering Plugin\n *\n * Provides keyboard and drag-drop row reordering functionality for tbw-grid.\n * Supports Ctrl+Up/Down keyboard shortcuts and optional drag handle column.\n */\n\nimport { GridClasses } from '../../core/constants';\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridHost } from '../../core/types';\nimport styles from './row-reorder.css?inline';\nimport type { PendingMove, RowMoveDetail, RowReorderConfig } from './types';\n\n/** Field name for the drag handle column */\nexport const ROW_DRAG_HANDLE_FIELD = '__tbw_row_drag';\n\n/**\n * Row Reorder Plugin for tbw-grid\n *\n * Enables row reordering via keyboard shortcuts (Ctrl+Up/Down) and drag-drop.\n * Supports validation callbacks and debounced keyboard moves.\n *\n * ## Installation\n *\n * ```ts\n * import { RowReorderPlugin } from '@toolbox-web/grid/plugins/reorder-rows';\n * ```\n *\n * ## Keyboard Shortcuts\n *\n * | Key | Action |\n * |-----|--------|\n * | `Ctrl + ↑` | Move focused row up |\n * | `Ctrl + ↓` | Move focused row down |\n *\n * ## Events\n *\n * | Event | Detail | Cancelable | Description |\n * |-------|--------|------------|-------------|\n * | `row-move` | {@link RowMoveDetail} | Yes | Fired when a row move is attempted |\n *\n * @example Basic Row Reordering\n * ```ts\n * import { queryGrid } from '@toolbox-web/grid';\n * import { RowReorderPlugin } from '@toolbox-web/grid/plugins/reorder-rows';\n *\n * const grid = queryGrid('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'id', header: 'ID' },\n * { field: 'name', header: 'Name' },\n * ],\n * plugins: [new RowReorderPlugin()],\n * };\n *\n * grid.on('row-move', ({ fromIndex, toIndex }) => {\n * console.log('Row moved from', fromIndex, 'to', toIndex);\n * });\n * ```\n *\n * @example With Validation\n * ```ts\n * new RowReorderPlugin({\n * canMove: (row, fromIndex, toIndex, direction) => {\n * // Prevent moving locked rows\n * return !row.locked;\n * },\n * })\n * ```\n *\n * @see {@link RowReorderConfig} for all configuration options\n * @see {@link RowMoveDetail} for the event detail structure\n */\nexport class RowReorderPlugin extends BaseGridPlugin<RowReorderConfig> {\n /** @internal */\n readonly name = 'reorderRows';\n /** @internal */\n override readonly aliases = ['rowReorder'] as const;\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n protected override get defaultConfig(): Partial<RowReorderConfig> {\n return {\n enableKeyboard: true,\n showDragHandle: true,\n dragHandlePosition: 'left',\n dragHandleWidth: 40,\n debounceMs: 150,\n animation: 'flip',\n };\n }\n\n /**\n * Resolve animation type from plugin config.\n * Uses base class isAnimationEnabled to respect grid-level settings.\n */\n private get animationType(): false | 'flip' {\n // Check if animations are globally disabled\n if (!this.isAnimationEnabled) return false;\n\n // Plugin config (with default from defaultConfig)\n if (this.config.animation !== undefined) return this.config.animation;\n\n return 'flip'; // Plugin default\n }\n\n // #region Internal State\n private isDragging = false;\n private draggedRowIndex: number | null = null;\n private dropRowIndex: number | null = null;\n private pendingMove: PendingMove | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n /** Column index to use when flushing pending move */\n private lastFocusCol = 0;\n\n /** Typed internal grid accessor. */\n get #internalGrid(): GridHost {\n return this.grid as unknown as GridHost;\n }\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\n override attach(grid: import('../../core/plugin/base-plugin').GridElement): void {\n super.attach(grid);\n this.setupDelegatedDragListeners();\n }\n\n /** @internal */\n override detach(): void {\n this.clearDebounceTimer();\n this.isDragging = false;\n this.draggedRowIndex = null;\n this.dropRowIndex = null;\n this.pendingMove = null;\n }\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.showDragHandle) {\n return [...columns];\n }\n\n const dragHandleColumn: ColumnConfig = {\n field: ROW_DRAG_HANDLE_FIELD,\n header: '',\n width: this.config.dragHandleWidth ?? 40,\n resizable: false,\n sortable: false,\n filterable: false,\n lockPosition: true,\n utility: true,\n viewRenderer: () => {\n const container = document.createElement('div');\n container.className = 'dg-row-drag-handle';\n container.setAttribute('aria-label', 'Drag to reorder');\n container.setAttribute('role', 'button');\n container.setAttribute('tabindex', '-1');\n // Set draggable as property (not just attribute) for proper HTML5 drag-drop\n container.draggable = true;\n\n // Use the grid's configured dragHandle icon\n this.setIcon(container, 'dragHandle');\n\n return container;\n },\n };\n\n // Position the drag handle column\n if (this.config.dragHandlePosition === 'right') {\n return [...columns, dragHandleColumn];\n }\n return [dragHandleColumn, ...columns];\n }\n\n /** @internal */\n override afterRender(): void {\n // No-op: drag listeners are set up via event delegation in attach()\n }\n\n /**\n * Handle Ctrl+Arrow keyboard shortcuts for row reordering.\n * @internal\n */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n if (!this.config.enableKeyboard) return;\n if (!event.ctrlKey || (event.key !== 'ArrowUp' && event.key !== 'ArrowDown')) {\n return;\n }\n\n const grid = this.#internalGrid;\n const focusRow = grid._focusRow;\n // Use _rows (current visual state) for keyboard moves, not sourceRows\n // This ensures rapid moves work correctly since we update _rows directly\n // Fallback to sourceRows for compatibility with tests\n const rows = grid._rows ?? this.sourceRows;\n\n if (focusRow < 0 || focusRow >= rows.length) return;\n\n const direction = event.key === 'ArrowUp' ? 'up' : 'down';\n const toIndex = direction === 'up' ? focusRow - 1 : focusRow + 1;\n\n // Check bounds\n if (toIndex < 0 || toIndex >= rows.length) return;\n\n const row = rows[focusRow];\n\n // Validate move against both plugin queries and user callback\n if (!this.canMoveRow(focusRow, toIndex)) {\n return;\n }\n\n // Debounce keyboard moves\n this.handleKeyboardMove(row, focusRow, toIndex, direction, grid._focusCol);\n\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n\n /**\n * Flush pending keyboard moves when user clicks a cell.\n * This commits the move immediately so focus works correctly.\n * @internal\n */\n override onCellClick(): void {\n // If there's a pending keyboard move, flush it immediately\n // so the user's click focus isn't overridden\n this.flushPendingMove();\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Move a row to a new position programmatically.\n * @param fromIndex - Current index of the row\n * @param toIndex - Target index\n */\n moveRow(fromIndex: number, toIndex: number): void {\n const rows = [...this.sourceRows];\n if (fromIndex < 0 || fromIndex >= rows.length) return;\n if (toIndex < 0 || toIndex >= rows.length) return;\n if (fromIndex === toIndex) return;\n\n const direction = toIndex < fromIndex ? 'up' : 'down';\n const row = rows[fromIndex];\n\n // Validate move against both plugin queries and user callback\n if (!this.canMoveRow(fromIndex, toIndex)) {\n return;\n }\n\n this.executeMove(row, fromIndex, toIndex, 'keyboard');\n }\n\n /**\n * Check if a row can be moved to a position.\n * Consults both the user-provided `canMove` callback and the plugin query system\n * (`canMoveRow` query) — GroupingRows blocks group header moves, Tree blocks\n * parent node moves.\n * @param fromIndex - Current index of the row\n * @param toIndex - Target index\n */\n canMoveRow(fromIndex: number, toIndex: number): boolean {\n const rows = this.sourceRows;\n if (fromIndex < 0 || fromIndex >= rows.length) return false;\n if (toIndex < 0 || toIndex >= rows.length) return false;\n if (fromIndex === toIndex) return false;\n\n // Ask plugins via query system (Tree blocks parent nodes, GroupingRows blocks group headers)\n const row = rows[fromIndex];\n const queryResults = this.grid?.query?.('canMoveRow', row);\n if (Array.isArray(queryResults) && queryResults.includes(false)) return false;\n\n if (!this.config.canMove) return true;\n\n const direction = toIndex < fromIndex ? 'up' : 'down';\n return this.config.canMove(rows[fromIndex], fromIndex, toIndex, direction);\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Set up delegated drag-and-drop listeners on the grid element.\n * Uses event delegation so recycled/virtualized rows work without rebinding.\n */\n private setupDelegatedDragListeners(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n const signal = this.disconnectSignal;\n\n // dragstart — only from .dg-row-drag-handle\n gridEl.addEventListener(\n 'dragstart',\n (e: Event) => {\n const de = e as DragEvent;\n const handle = (de.target as HTMLElement).closest('.dg-row-drag-handle') as HTMLElement | null;\n if (!handle) return;\n const rowEl = handle.closest('.data-grid-row') as HTMLElement;\n if (!rowEl) return;\n\n const rowIndex = this.getRowIndex(rowEl);\n if (rowIndex < 0) return;\n\n this.isDragging = true;\n this.draggedRowIndex = rowIndex;\n\n if (de.dataTransfer) {\n de.dataTransfer.effectAllowed = 'move';\n de.dataTransfer.setData('text/plain', String(rowIndex));\n }\n\n rowEl.classList.add(GridClasses.DRAGGING);\n },\n { signal },\n );\n\n // dragend — clean up\n gridEl.addEventListener(\n 'dragend',\n () => {\n this.isDragging = false;\n this.draggedRowIndex = null;\n this.dropRowIndex = null;\n this.clearDragClasses();\n },\n { signal },\n );\n\n // dragover — highlight drop target\n gridEl.addEventListener(\n 'dragover',\n (e: Event) => {\n const de = e as DragEvent;\n if (!this.isDragging || this.draggedRowIndex === null) return;\n\n const rowEl = (de.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n if (!rowEl) return;\n\n de.preventDefault();\n\n const targetIndex = this.getRowIndex(rowEl);\n if (targetIndex < 0 || targetIndex === this.draggedRowIndex) return;\n\n const rect = rowEl.getBoundingClientRect();\n const midY = rect.top + rect.height / 2;\n const isBefore = de.clientY < midY;\n\n this.dropRowIndex = isBefore ? targetIndex : targetIndex + 1;\n\n rowEl.classList.add('drop-target');\n rowEl.classList.toggle('drop-before', isBefore);\n rowEl.classList.toggle('drop-after', !isBefore);\n },\n { signal },\n );\n\n // dragleave — remove highlight\n gridEl.addEventListener(\n 'dragleave',\n (e: Event) => {\n const rowEl = (e.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n if (rowEl) {\n rowEl.classList.remove('drop-target', 'drop-before', 'drop-after');\n }\n },\n { signal },\n );\n\n // drop — execute the row move\n gridEl.addEventListener(\n 'drop',\n (e: Event) => {\n const de = e as DragEvent;\n de.preventDefault();\n\n const fromIndex = this.draggedRowIndex;\n let toIndex = this.dropRowIndex;\n\n if (!this.isDragging || fromIndex === null || toIndex === null) return;\n\n // Adjust toIndex if dropping after the dragged row\n if (toIndex > fromIndex) {\n toIndex--;\n }\n\n if (fromIndex !== toIndex) {\n const rows = this.sourceRows;\n const row = rows[fromIndex];\n\n if (this.canMoveRow(fromIndex, toIndex)) {\n this.executeMove(row, fromIndex, toIndex, 'drag');\n }\n }\n },\n { signal },\n );\n }\n\n /**\n * Handle debounced keyboard moves.\n * Rows move immediately for visual feedback, but the event emission is debounced.\n */\n private handleKeyboardMove(\n row: unknown,\n fromIndex: number,\n toIndex: number,\n direction: 'up' | 'down',\n focusCol: number,\n ): void {\n // Track move for debounced event emission\n if (!this.pendingMove) {\n this.pendingMove = {\n originalIndex: fromIndex,\n currentIndex: toIndex,\n row,\n };\n } else {\n // Update the current index for rapid moves\n this.pendingMove.currentIndex = toIndex;\n }\n\n // Store focus column for flush\n this.lastFocusCol = focusCol;\n\n // Move rows immediately for visual feedback\n // Use _rows (current visual state) for rapid moves, not sourceRows\n // Fallback to sourceRows for compatibility with tests\n const grid = this.#internalGrid;\n const rows = [...(grid._rows ?? this.sourceRows)];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n\n // Update grid rows immediately (without triggering change events)\n grid._rows = rows;\n\n // Update focus to follow the row\n grid._focusRow = toIndex;\n grid._focusCol = focusCol;\n\n // Refresh virtual window directly - this re-renders from _rows\n // without overwriting _rows from #rows (which requestRender does)\n grid.refreshVirtualWindow(true);\n\n // Ensure focus styling is applied after the row rebuild\n ensureCellVisible(grid);\n\n // Debounce the event emission only\n this.clearDebounceTimer();\n this.debounceTimer = setTimeout(() => {\n this.flushPendingMove();\n }, this.config.debounceMs ?? 300);\n }\n\n /**\n * Flush the pending move by emitting the event.\n * Called when debounce timer fires or user clicks elsewhere.\n */\n private flushPendingMove(): void {\n this.clearDebounceTimer();\n\n if (!this.pendingMove) return;\n\n const { originalIndex, currentIndex, row: movedRow } = this.pendingMove;\n this.pendingMove = null;\n\n if (originalIndex === currentIndex) return;\n\n // Emit cancelable event\n const detail: RowMoveDetail = {\n row: movedRow,\n fromIndex: originalIndex,\n toIndex: currentIndex,\n rows: [...this.sourceRows],\n source: 'keyboard',\n };\n\n const cancelled = this.emitCancelable('row-move', detail);\n if (cancelled) {\n // Revert to original position\n const rows = [...this.sourceRows];\n const [row] = rows.splice(currentIndex, 1);\n rows.splice(originalIndex, 0, row);\n\n const grid = this.#internalGrid;\n grid._rows = rows;\n grid._focusRow = originalIndex;\n grid._focusCol = this.lastFocusCol;\n grid.refreshVirtualWindow(true);\n ensureCellVisible(grid);\n }\n }\n\n /**\n * Execute a row move and emit the event.\n */\n private executeMove(row: unknown, fromIndex: number, toIndex: number, source: 'keyboard' | 'drag'): void {\n const rows = [...this.sourceRows];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n\n const detail: RowMoveDetail = {\n row,\n fromIndex,\n toIndex,\n rows,\n source,\n };\n\n // Emit cancelable event\n const cancelled = this.emitCancelable('row-move', detail);\n if (!cancelled) {\n // Apply with animation if enabled\n if (this.animationType === 'flip' && this.gridElement) {\n const oldPositions = this.captureRowPositions();\n this.grid.rows = rows;\n // Wait for the scheduler to process the virtual window update (RAF)\n // before running FLIP animation on the new rows\n requestAnimationFrame(() => {\n void this.gridElement.offsetHeight;\n this.animateFLIP(oldPositions, fromIndex, toIndex);\n });\n } else {\n // No animation, just update rows\n this.grid.rows = rows;\n }\n }\n }\n\n /**\n * Capture row positions before reorder.\n * Maps visual row index to its top position.\n */\n private captureRowPositions(): Map<number, number> {\n const positions = new Map<number, number>();\n this.gridElement?.querySelectorAll('.data-grid-row').forEach((row) => {\n const rowIndex = this.getRowIndex(row as HTMLElement);\n if (rowIndex >= 0) {\n positions.set(rowIndex, row.getBoundingClientRect().top);\n }\n });\n return positions;\n }\n\n /**\n * Apply FLIP animation for row reorder.\n * Uses CSS transitions - JS sets initial transform and toggles class.\n * @param oldPositions - Row positions captured before DOM change\n * @param fromIndex - Original index of moved row\n * @param toIndex - New index of moved row\n */\n private animateFLIP(oldPositions: Map<number, number>, fromIndex: number, toIndex: number): void {\n const gridEl = this.gridElement;\n if (!gridEl || oldPositions.size === 0) return;\n\n // Calculate which row indices were affected and their new positions\n const minIndex = Math.min(fromIndex, toIndex);\n const maxIndex = Math.max(fromIndex, toIndex);\n\n // Build a map of new row index -> delta Y\n const rowsToAnimate: { el: HTMLElement; deltaY: number }[] = [];\n\n gridEl.querySelectorAll('.data-grid-row').forEach((row) => {\n const rowEl = row as HTMLElement;\n const newRowIndex = this.getRowIndex(rowEl);\n if (newRowIndex < 0 || newRowIndex < minIndex || newRowIndex > maxIndex) return;\n\n // Figure out what this row's old index was\n let oldIndex: number;\n if (newRowIndex === toIndex) {\n // This is the moved row\n oldIndex = fromIndex;\n } else if (fromIndex < toIndex) {\n // Row moved down: rows in between shifted up by 1\n oldIndex = newRowIndex + 1;\n } else {\n // Row moved up: rows in between shifted down by 1\n oldIndex = newRowIndex - 1;\n }\n\n const oldTop = oldPositions.get(oldIndex);\n if (oldTop === undefined) return;\n\n const newTop = rowEl.getBoundingClientRect().top;\n const deltaY = oldTop - newTop;\n\n if (Math.abs(deltaY) > 1) {\n rowsToAnimate.push({ el: rowEl, deltaY });\n }\n });\n\n if (rowsToAnimate.length === 0) return;\n\n // Set initial transform (First → Last position offset)\n rowsToAnimate.forEach(({ el, deltaY }) => {\n el.style.transform = `translateY(${deltaY}px)`;\n });\n\n // Force reflow then animate to final position via CSS transition\n void gridEl.offsetHeight;\n\n const duration = this.animationDuration;\n\n requestAnimationFrame(() => {\n rowsToAnimate.forEach(({ el }) => {\n el.classList.add('flip-animating');\n el.style.transform = '';\n });\n\n // Cleanup after animation\n setTimeout(() => {\n rowsToAnimate.forEach(({ el }) => {\n el.style.transform = '';\n el.classList.remove('flip-animating');\n });\n }, duration + 50);\n });\n }\n\n /**\n * Get the row index from a row element by checking data-row attribute on cells.\n * This is consistent with how other plugins retrieve row indices.\n */\n private getRowIndex(rowEl: HTMLElement): number {\n const cell = rowEl.querySelector('.cell[data-row]');\n return cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n }\n\n /**\n * Clear all drag-related classes from rows.\n */\n private clearDragClasses(): void {\n this.gridElement?.querySelectorAll('.data-grid-row').forEach((row) => {\n row.classList.remove(GridClasses.DRAGGING, 'drop-target', 'drop-before', 'drop-after');\n });\n }\n\n /**\n * Clear the debounce timer.\n */\n private clearDebounceTimer(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n // #endregion\n}\n"],"names":["ROW_DRAG_HANDLE_FIELD","RowReorderPlugin","BaseGridPlugin","name","aliases","styles","defaultConfig","enableKeyboard","showDragHandle","dragHandlePosition","dragHandleWidth","debounceMs","animation","animationType","this","isAnimationEnabled","config","isDragging","draggedRowIndex","dropRowIndex","pendingMove","debounceTimer","lastFocusCol","internalGrid","grid","attach","super","setupDelegatedDragListeners","detach","clearDebounceTimer","processColumns","columns","dragHandleColumn","field","header","width","resizable","sortable","filterable","lockPosition","utility","viewRenderer","container","document","createElement","className","setAttribute","draggable","setIcon","afterRender","onKeyDown","event","ctrlKey","key","focusRow","_focusRow","rows","_rows","sourceRows","length","direction","toIndex","row","canMoveRow","handleKeyboardMove","_focusCol","preventDefault","stopPropagation","onCellClick","flushPendingMove","moveRow","fromIndex","executeMove","queryResults","query","Array","isArray","includes","canMove","gridEl","gridElement","signal","disconnectSignal","addEventListener","e","de","handle","target","closest","rowEl","rowIndex","getRowIndex","dataTransfer","effectAllowed","setData","String","classList","add","GridClasses","DRAGGING","clearDragClasses","targetIndex","rect","getBoundingClientRect","midY","top","height","isBefore","clientY","toggle","remove","focusCol","currentIndex","originalIndex","movedRow","splice","refreshVirtualWindow","ensureCellVisible","setTimeout","detail","source","emitCancelable","oldPositions","captureRowPositions","requestAnimationFrame","offsetHeight","animateFLIP","positions","Map","querySelectorAll","forEach","set","size","minIndex","Math","min","maxIndex","max","rowsToAnimate","newRowIndex","oldIndex","oldTop","get","deltaY","abs","push","el","style","transform","duration","animationDuration","cell","querySelector","parseInt","getAttribute","clearTimeout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"reorder-rows.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/shared/drag-drop-protocol.ts","../../../../../libs/grid/src/lib/plugins/row-drag-drop/RowDragDropPlugin.ts"],"sourcesContent":["/**\n * Drag-Drop Protocol — shared utilities for cross-grid row drag-and-drop.\n *\n * Used by `RowDragDropPlugin` (and any future plugin that participates in the\n * tbw row-drag protocol). Centralises:\n *\n * - MIME type constants and zone tagging (visible in `dataTransfer.types`)\n * - Payload codec for cross-window / cross-iframe transfers\n * - Drop-position math (midpoint algorithm)\n * - Auto-scroll engine (rAF loop, edge detection, boundary clamp)\n * - Plain-text TSV fallback for drags out to Notepad / Excel / Slack\n *\n * @internal Plugin shared utility (not part of the public API).\n */\n\nimport type { ColumnConfig } from '../../core/types';\n\n// ---------------------------------------------------------------------------\n// Same-window current-session tracker\n// ---------------------------------------------------------------------------\n\n/**\n * Module-level \"active drag session\" — set on dragstart by the source grid,\n * cleared on dragend. Lets target grids read the full payload synchronously\n * during `dragover` (browsers hide `dataTransfer.getData()` during dragover\n * for security). Cross-window drags don't have access to this module and\n * fall back to a generic accept-check followed by a drop-time canDrop call.\n *\n * @internal\n */\nlet currentSession: { sessionId: string; payload: RowDragPayload<unknown> } | null = null;\n\n/** @internal */\nexport function setCurrentDragSession<T>(sessionId: string, payload: RowDragPayload<T>): void {\n currentSession = { sessionId, payload: payload as RowDragPayload<unknown> };\n}\n\n/** @internal */\nexport function getCurrentDragSession<T = unknown>(): { sessionId: string; payload: RowDragPayload<T> } | null {\n return currentSession as { sessionId: string; payload: RowDragPayload<T> } | null;\n}\n\n/** @internal */\nexport function clearCurrentDragSession(): void {\n currentSession = null;\n}\n\n// ---------------------------------------------------------------------------\n// MIME types\n// ---------------------------------------------------------------------------\n\n/** Base MIME type carried on `dataTransfer` for tbw row drags. */\nexport const TBW_ROW_DRAG_MIME = 'application/x-tbw-grid-rows+json';\n\n/** Build a zone-tagged MIME type, visible in `dataTransfer.types` during dragover. */\nexport function mimeForZone(zone: string): string {\n return `${TBW_ROW_DRAG_MIME};zone=${encodeURIComponent(zone)}`;\n}\n\n/** Extract the zone from a tagged MIME, or `null` if untagged. */\nexport function parseZoneFromMime(mime: string): string | null {\n const match = mime.match(/^application\\/x-tbw-grid-rows\\+json;zone=(.*)$/);\n if (!match) return null;\n try {\n return decodeURIComponent(match[1]);\n } catch {\n return match[1];\n }\n}\n\n/**\n * Find the first MIME entry in `types` whose zone matches `zone`.\n * Used during `dragover` for the accept-check (browsers hide `getData()`\n * during dragover, but `types` is always visible).\n */\nexport function findMatchingZoneMime(types: readonly string[], zone: string): string | null {\n for (const t of types) {\n if (parseZoneFromMime(t) === zone) return t;\n }\n return null;\n}\n\n/** Returns true if any tbw row-drag MIME is present in `types`. */\nexport function hasAnyRowDragMime(types: readonly string[]): boolean {\n for (const t of types) {\n if (t === TBW_ROW_DRAG_MIME || t.startsWith(`${TBW_ROW_DRAG_MIME};`)) return true;\n }\n return false;\n}\n\n// ---------------------------------------------------------------------------\n// Payload\n// ---------------------------------------------------------------------------\n\n/**\n * Cross-grid drag payload, carried on `dataTransfer` and (for same-window\n * recovery) keyed in the WeakRef registry by `sessionId`.\n */\nexport interface RowDragPayload<T = unknown> {\n /** Drag session id (matches the WeakRef registry key). */\n sessionId: string;\n /** Source grid id (`grid.id` or auto-generated UUID). */\n sourceGridId: string;\n /** Drop zone the source grid is participating in. */\n dropZone: string;\n /** Serialized row payload (JSON-safe). For same-window drops, recovered live via the registry. */\n rows: T[];\n /** Original indices in the source grid's `_rows` array. */\n rowIndices: number[];\n /** Move (default) removes from source; copy leaves source intact. */\n operation: 'move' | 'copy';\n}\n\n/** Encode a payload to the JSON string written to `dataTransfer`. */\nexport function encodePayload<T>(payload: RowDragPayload<T>): string {\n return JSON.stringify(payload);\n}\n\n/** Decode a payload from the JSON string read off `dataTransfer`. */\nexport function decodePayload<T = unknown>(raw: string): RowDragPayload<T> | null {\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as RowDragPayload<T>;\n if (\n typeof parsed?.sessionId !== 'string' ||\n typeof parsed?.sourceGridId !== 'string' ||\n typeof parsed?.dropZone !== 'string' ||\n !Array.isArray(parsed?.rows) ||\n !Array.isArray(parsed?.rowIndices) ||\n (parsed.operation !== 'move' && parsed.operation !== 'copy')\n ) {\n return null;\n }\n // `rowIndices` is later used for sorting and splicing — reject anything\n // that isn't a finite, non-negative integer to avoid JS coercion bugs\n // (NaN ordering, float splice indices, string concatenation, etc.).\n for (const idx of parsed.rowIndices) {\n if (typeof idx !== 'number' || !Number.isInteger(idx) || idx < 0) return null;\n }\n return parsed;\n } catch {\n return null;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Drop position math\n// ---------------------------------------------------------------------------\n\nexport interface DropPosition {\n /** Target visual row index that the cursor is over, or `null` for empty area. */\n overIndex: number | null;\n /** Final insertion index in the target grid's `_rows`. */\n insertIndex: number;\n /** True when the cursor is above the row midpoint. */\n isBefore: boolean;\n}\n\n/**\n * Compute the insertion index for a drop, given the row element under the\n * cursor (if any), the cursor's clientY, and the total row count when the\n * cursor is in empty space below the last row.\n *\n * - Cursor above row midpoint → insert at `targetIndex`\n * - Cursor below row midpoint → insert at `targetIndex + 1`\n * - Cursor over empty area below the last row → append (`insertIndex = totalRows`)\n */\nexport function computeDropPosition(\n rowEl: HTMLElement | null,\n clientY: number,\n rowIndexResolver: (el: HTMLElement) => number,\n totalRows: number,\n): DropPosition {\n if (!rowEl) {\n return { overIndex: null, insertIndex: totalRows, isBefore: false };\n }\n const targetIndex = rowIndexResolver(rowEl);\n if (targetIndex < 0) {\n return { overIndex: null, insertIndex: totalRows, isBefore: false };\n }\n const rect = rowEl.getBoundingClientRect();\n const midY = rect.top + rect.height / 2;\n const isBefore = clientY < midY;\n return {\n overIndex: targetIndex,\n insertIndex: isBefore ? targetIndex : targetIndex + 1,\n isBefore,\n };\n}\n\n// ---------------------------------------------------------------------------\n// Auto-scroll engine\n// ---------------------------------------------------------------------------\n\nexport interface AutoScrollOptions {\n /** Pixels from the top/bottom edge that activate auto-scroll. */\n edgeSize?: number;\n /** Base scroll speed in pixels-per-frame near the edge. */\n speed?: number;\n /** Max speed at the very edge (linear ramp from `speed` → `maxSpeed`). */\n maxSpeed?: number;\n}\n\nexport interface AutoScroller {\n /** Update pointer position; starts the rAF loop if needed. */\n onPointerMove(clientY: number): void;\n /** Stop the rAF loop and reset state. */\n stop(): void;\n /** True when actively auto-scrolling. */\n readonly isScrolling: boolean;\n}\n\n/**\n * Create an auto-scroller for a viewport element.\n *\n * The scroller runs a `requestAnimationFrame` loop only while the pointer is\n * within `edgeSize` of the viewport's top or bottom and there is room to\n * scroll. Speed ramps linearly from `speed` (at the edge boundary) to\n * `maxSpeed` (at the very edge).\n */\nexport function createAutoScroller(\n viewport: HTMLElement,\n options: AutoScrollOptions = {},\n onScrollChange?: (active: boolean) => void,\n): AutoScroller {\n const edgeSize = options.edgeSize ?? 60;\n const baseSpeed = options.speed ?? 8;\n const maxSpeed = options.maxSpeed ?? 24;\n\n let rafId: number | null = null;\n let pointerY: number | null = null;\n let active = false;\n\n const setActive = (next: boolean): void => {\n if (next === active) return;\n active = next;\n onScrollChange?.(next);\n };\n\n const tick = (): void => {\n rafId = null;\n if (pointerY === null) {\n setActive(false);\n return;\n }\n const rect = viewport.getBoundingClientRect();\n let delta = 0;\n\n if (pointerY < rect.top + edgeSize) {\n const distance = Math.max(0, pointerY - rect.top);\n const ratio = 1 - distance / edgeSize;\n delta = -Math.round(baseSpeed + (maxSpeed - baseSpeed) * ratio);\n } else if (pointerY > rect.bottom - edgeSize) {\n const distance = Math.max(0, rect.bottom - pointerY);\n const ratio = 1 - distance / edgeSize;\n delta = Math.round(baseSpeed + (maxSpeed - baseSpeed) * ratio);\n }\n\n if (delta === 0) {\n setActive(false);\n return;\n }\n\n const before = viewport.scrollTop;\n viewport.scrollTop = before + delta;\n\n // Stop if we hit a boundary (no movement happened)\n if (viewport.scrollTop === before) {\n setActive(false);\n return;\n }\n\n setActive(true);\n rafId = requestAnimationFrame(tick);\n };\n\n return {\n onPointerMove(clientY: number): void {\n pointerY = clientY;\n if (rafId === null) {\n rafId = requestAnimationFrame(tick);\n }\n },\n stop(): void {\n if (rafId !== null) {\n cancelAnimationFrame(rafId);\n rafId = null;\n }\n pointerY = null;\n setActive(false);\n },\n get isScrolling(): boolean {\n return active;\n },\n };\n}\n\n// ---------------------------------------------------------------------------\n// TSV fallback\n// ---------------------------------------------------------------------------\n\n/**\n * Format rows as a TSV string for the `text/plain` drag fallback.\n * Used when the drop target is an external app (Excel, Notepad, Slack).\n *\n * - One row per line, fields separated by `\\t`.\n * - Tabs and newlines in cell values are replaced with spaces.\n * - Skips columns flagged as `utility` (drag handle, checkbox, etc.).\n */\nexport function formatRowsAsTSV<T extends Record<string, unknown>>(\n rows: readonly T[],\n columns: readonly ColumnConfig[],\n): string {\n const dataColumns = columns.filter(\n (c) => !(c as { utility?: boolean }).utility && typeof c.field === 'string' && c.field !== '',\n );\n const lines: string[] = [];\n for (const row of rows) {\n const cells = dataColumns.map((col) => {\n const value = row[col.field];\n if (value === null || value === undefined) return '';\n const str = String(value);\n return str.replace(/[\\t\\r\\n]+/g, ' ');\n });\n lines.push(cells.join('\\t'));\n }\n return lines.join('\\n');\n}\n","/**\n * Row Drag-Drop Plugin\n *\n * Drag rows within a single grid (parity with the deprecated\n * `RowReorderPlugin`) **and** between grids that share a `dropZone`.\n *\n * Architecture overview is documented in the issue body and in\n * `.github/knowledge/grid-plugins.md`. Key invariants:\n *\n * - Mutations write to `grid._rows`; the user's input `sourceRows` array is\n * never mutated on either side. Persistence is consumer-driven via the\n * `row-move` (intra-grid) and `row-transfer` (cross-grid) events.\n * - Same-window cross-grid drops use the WeakRef registry in\n * `core/internal/drag-drop-registry` to recover live row references.\n * Cross-window drops fall back to `JSON.parse(JSON.stringify(row))` (or the\n * `serializeRow`/`deserializeRow` hooks for non-JSON values).\n * - `RowReorderPlugin` is an alias re-export of this class. The PluginManager\n * alias-collapse pre-pass merges configs when both names are instantiated.\n *\n * @see {@link RowDragDropConfig} for all configuration options.\n */\n\nimport { GridClasses } from '../../core/constants';\nimport {\n clearDragSession,\n lookupDragSession,\n newDragSessionId,\n registerDragSession,\n} from '../../core/internal/drag-drop-registry';\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { BaseGridPlugin, type GridElement, type PluginManifest } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, GridHost } from '../../core/types';\nimport {\n type AutoScroller,\n type RowDragPayload,\n TBW_ROW_DRAG_MIME,\n clearCurrentDragSession,\n computeDropPosition,\n createAutoScroller,\n decodePayload,\n encodePayload,\n findMatchingZoneMime,\n formatRowsAsTSV,\n getCurrentDragSession,\n hasAnyRowDragMime,\n mimeForZone,\n setCurrentDragSession,\n} from '../shared/drag-drop-protocol';\nimport styles from './row-drag-drop.css?inline';\nimport type {\n PendingMove,\n RowDragDropConfig,\n RowDragEndDetail,\n RowDragStartDetail,\n RowDropDetail,\n RowMoveDetail,\n RowTransferDetail,\n} from './types';\n\n/** Field name for the drag handle column. */\nexport const ROW_DRAG_HANDLE_FIELD = '__tbw_row_drag';\n\n/**\n * Row Drag-Drop Plugin for `<tbw-grid>`.\n *\n * @example Intra-grid (parity with deprecated `RowReorderPlugin`)\n * ```ts\n * import { RowDragDropPlugin } from '@toolbox-web/grid/plugins/row-drag-drop';\n *\n * grid.gridConfig = {\n * plugins: [new RowDragDropPlugin()],\n * };\n * ```\n *\n * @example Cross-grid transfer list\n * ```ts\n * gridA.gridConfig = { plugins: [new RowDragDropPlugin({ dropZone: 'tasks' })] };\n * gridB.gridConfig = { plugins: [new RowDragDropPlugin({ dropZone: 'tasks' })] };\n *\n * gridA.addEventListener('row-transfer', (e) => persist(e.detail));\n * gridB.addEventListener('row-transfer', (e) => persist(e.detail));\n * ```\n *\n * @category Plugin\n */\nexport class RowDragDropPlugin<T = unknown> extends BaseGridPlugin<RowDragDropConfig<T>> {\n /** @internal */\n readonly name = 'rowDragDrop';\n\n /**\n * Backwards-compatible aliases. `RowReorderPlugin`'s legacy plugin name\n * (`reorderRows`) and short alias (`rowReorder`) both resolve here so that\n * `getPluginByName('reorderRows')` keeps working.\n * @internal\n */\n override readonly aliases = ['reorderRows', 'rowReorder'] as const;\n\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n static override readonly manifest: PluginManifest<RowDragDropConfig> = {\n events: [\n { type: 'row-move', description: 'Intra-grid row reorder.', cancelable: true },\n { type: 'row-drag-start', description: 'Cross-grid drag started on this grid.', cancelable: true },\n { type: 'row-drag-end', description: 'Drag finished on this grid (regardless of outcome).' },\n { type: 'row-drop', description: 'Cross-grid drop landing on this grid.', cancelable: true },\n { type: 'row-transfer', description: 'Cross-grid transfer completed (fires on both grids).' },\n ],\n };\n\n /** @internal */\n protected override get defaultConfig(): Partial<RowDragDropConfig<T>> {\n return {\n enableKeyboard: true,\n showDragHandle: true,\n dragHandlePosition: 'left',\n dragHandleWidth: 40,\n debounceMs: 150,\n animation: 'flip',\n operation: 'move',\n autoScroll: true,\n };\n }\n\n /** Resolve animation type from plugin config (respects grid-level reduced-motion). */\n private get animationType(): false | 'flip' {\n if (!this.isAnimationEnabled) return false;\n if (this.config.animation !== undefined) return this.config.animation;\n return 'flip';\n }\n\n // #region Internal State\n private isDragging = false;\n private draggedRowIndex: number | null = null;\n private draggedRows: T[] = [];\n private draggedIndices: number[] = [];\n private dragSessionId: string | null = null;\n private dragAccepted = false;\n private dropRowIndex: number | null = null;\n private pendingMove: PendingMove | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n private lastFocusCol = 0;\n private autoScroller: AutoScroller | null = null;\n /** Stable id for this grid instance (used as `sourceGridId` in payloads). */\n private gridId = '';\n\n /** Typed internal grid accessor. */\n private get internalGrid(): GridHost {\n return this.grid as unknown as GridHost;\n }\n // #endregion\n\n // #region Lifecycle\n /** @internal */\n override attach(grid: GridElement): void {\n super.attach(grid);\n const host = this.gridElement;\n if (host) {\n this.gridId = host.id || `tbw-grid-${newDragSessionId().slice(0, 8)}`;\n if (!host.id) host.id = this.gridId;\n this.setupDelegatedDragListeners();\n }\n }\n\n /** @internal */\n override detach(): void {\n this.clearDebounceTimer();\n this.autoScroller?.stop();\n this.autoScroller = null;\n if (this.dragSessionId) clearDragSession(this.dragSessionId);\n clearCurrentDragSession();\n this.resetDragState();\n super.detach();\n }\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.showDragHandle) return [...columns];\n\n const dragHandleColumn: ColumnConfig = {\n field: ROW_DRAG_HANDLE_FIELD,\n header: '',\n width: this.config.dragHandleWidth ?? 40,\n resizable: false,\n sortable: false,\n filterable: false,\n lockPosition: true,\n utility: true,\n viewRenderer: () => {\n const container = document.createElement('div');\n container.className = 'dg-row-drag-handle';\n container.setAttribute('aria-label', 'Drag to reorder');\n container.setAttribute('role', 'button');\n container.setAttribute('tabindex', '-1');\n container.draggable = true;\n this.setIcon(container, 'dragHandle');\n return container;\n },\n };\n\n return this.config.dragHandlePosition === 'right' ? [...columns, dragHandleColumn] : [dragHandleColumn, ...columns];\n }\n\n /** @internal */\n override afterRender(): void {\n /* drag listeners are delegated; nothing to do per render */\n }\n\n /** @internal */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n if (!this.config.enableKeyboard) return;\n if (!event.ctrlKey || (event.key !== 'ArrowUp' && event.key !== 'ArrowDown')) return;\n\n const grid = this.internalGrid;\n const focusRow = grid._focusRow;\n const rows = grid._rows ?? this.sourceRows;\n if (focusRow < 0 || focusRow >= rows.length) return;\n\n const direction = event.key === 'ArrowUp' ? 'up' : 'down';\n const toIndex = direction === 'up' ? focusRow - 1 : focusRow + 1;\n if (toIndex < 0 || toIndex >= rows.length) return;\n\n const row = rows[focusRow];\n if (!this.canMoveRow(focusRow, toIndex)) return;\n\n this.handleKeyboardMove(row, focusRow, toIndex, grid._focusCol);\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n\n /** @internal */\n override onCellClick(): void {\n this.flushPendingMove();\n }\n // #endregion\n\n // #region Public API\n /** Move a row to a new position programmatically (intra-grid). */\n moveRow(fromIndex: number, toIndex: number): void {\n const rows = [...this.sourceRows];\n if (fromIndex < 0 || fromIndex >= rows.length) return;\n if (toIndex < 0 || toIndex >= rows.length) return;\n if (fromIndex === toIndex) return;\n if (!this.canMoveRow(fromIndex, toIndex)) return;\n this.executeIntraGridMove(rows[fromIndex], fromIndex, toIndex, 'keyboard');\n }\n\n /**\n * Check if a row can be moved within this grid.\n * Consults the user-provided `canMove` callback (or `canDrag` veto for the\n * source row), the plugin query system (`canMoveRow`), and `canDrop` for\n * the target.\n */\n canMoveRow(fromIndex: number, toIndex: number): boolean {\n // During debounced keyboard moves, `grid._rows` diverges from\n // `sourceRows` (the user-facing snapshot) because the plugin mutates\n // `_rows` per keystroke and only commits on flush. `onKeyDown` resolves\n // the focused row from `_rows ?? sourceRows`, so validation must read\n // from the same array — otherwise we'd run `canDrag`/`canMove`/queries\n // against the wrong row.\n const rows = this.internalGrid._rows ?? this.sourceRows;\n if (fromIndex < 0 || fromIndex >= rows.length) return false;\n if (toIndex < 0 || toIndex >= rows.length) return false;\n if (fromIndex === toIndex) return false;\n\n // Plugin query veto (Tree, GroupingRows)\n const row = rows[fromIndex] as T;\n const queryResults = this.grid?.query?.<boolean>('canMoveRow', row);\n if (Array.isArray(queryResults) && queryResults.includes(false)) return false;\n\n // canDrag veto (dragstart side)\n if (this.config.canDrag && !this.config.canDrag(row, fromIndex)) return false;\n\n // Legacy canMove callback\n if (this.config.canMove) {\n const direction = toIndex < fromIndex ? 'up' : 'down';\n if (!this.config.canMove(row, fromIndex, toIndex, direction)) return false;\n }\n\n // canDrop callback (intra-grid synthesised payload)\n if (this.config.canDrop) {\n const payload: RowDragPayload<T> = {\n sessionId: 'intra',\n sourceGridId: this.gridId,\n dropZone: this.config.dropZone ?? '',\n rows: [row],\n rowIndices: [fromIndex],\n operation: 'move',\n };\n if (!this.config.canDrop(payload, toIndex)) return false;\n }\n\n return true;\n }\n // #endregion\n\n // #region Drag Setup\n\n private setupDelegatedDragListeners(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n const signal = this.disconnectSignal;\n\n gridEl.addEventListener('dragstart', (e) => this.onDragStart(e as DragEvent), { signal });\n gridEl.addEventListener('dragend', () => this.onDragEnd(), { signal });\n gridEl.addEventListener('dragover', (e) => this.onDragOver(e as DragEvent), { signal });\n gridEl.addEventListener('dragleave', (e) => this.onDragLeave(e as DragEvent), { signal });\n gridEl.addEventListener('drop', (e) => this.onDrop(e as DragEvent), { signal });\n }\n\n private onDragStart(de: DragEvent): void {\n const handle = (de.target as HTMLElement).closest('.dg-row-drag-handle') as HTMLElement | null;\n if (!handle) return;\n const rowEl = handle.closest('.data-grid-row') as HTMLElement | null;\n if (!rowEl) return;\n\n const rowIndex = this.getRowIndex(rowEl);\n if (rowIndex < 0) return;\n\n // Resolve the rows being dragged: whole selection if dragged row is selected.\n const { rows, indices } = this.resolveDraggedRows(rowIndex);\n if (rows.length === 0) return;\n\n // canDrag veto on the originating row\n if (this.config.canDrag && !this.config.canDrag(rows[0], rowIndex)) {\n de.preventDefault();\n return;\n }\n\n const operation = this.config.operation ?? 'move';\n const dropZone = this.config.dropZone ?? '';\n const sessionId = newDragSessionId();\n\n // Emit cancelable row-drag-start (source-side veto)\n const startDetail: RowDragStartDetail<T> = { rows, indices, operation, dropZone };\n if (this.emitCancelable('row-drag-start', startDetail)) {\n de.preventDefault();\n return;\n }\n\n this.isDragging = true;\n this.draggedRowIndex = rowIndex;\n this.draggedRows = rows;\n this.draggedIndices = indices;\n this.dragSessionId = sessionId;\n this.dragAccepted = false;\n\n // Build the cross-grid payload (always built, even when dropZone is empty —\n // intra-grid drops still benefit from the registry round-trip)\n const serialize = this.config.serializeRow ?? ((r: T) => r);\n const payload: RowDragPayload<T> = {\n sessionId,\n sourceGridId: this.gridId,\n dropZone,\n rows: rows.map(serialize) as T[],\n rowIndices: indices,\n operation,\n };\n\n if (de.dataTransfer) {\n de.dataTransfer.effectAllowed = operation === 'copy' ? 'copyMove' : 'move';\n try {\n de.dataTransfer.setData(TBW_ROW_DRAG_MIME, encodePayload(payload));\n if (dropZone) de.dataTransfer.setData(mimeForZone(dropZone), encodePayload(payload));\n // Plain-text TSV fallback for external drop targets\n de.dataTransfer.setData('text/plain', formatRowsAsTSV(rows as Record<string, unknown>[], this.columns));\n } catch {\n /* JSDOM/happy-dom may throw on setData; harmless */\n }\n\n // Multi-row drag count badge\n if (rows.length > 1) {\n const badge = document.createElement('div');\n badge.className = 'tbw-row-drag-count';\n badge.textContent = `${rows.length} rows`;\n document.body.appendChild(badge);\n try {\n de.dataTransfer.setDragImage(badge, 10, 10);\n } catch {\n /* ignore */\n }\n // Remove the badge after the browser has snapshotted it\n setTimeout(() => badge.remove(), 0);\n }\n }\n\n // Same-window registry + current-session marker\n registerDragSession(sessionId, rows as unknown[]);\n setCurrentDragSession(sessionId, payload);\n\n rowEl.classList.add(GridClasses.DRAGGING);\n this.gridElement.classList.add('tbw-grid--drag-source');\n }\n\n private onDragOver(de: DragEvent): void {\n const dt = de.dataTransfer;\n if (!dt) return;\n\n // Identify whether a tbw row drag is in progress and whether it matches our zone.\n const types = dt.types ? Array.from(dt.types) : [];\n if (!hasAnyRowDragMime(types) && !this.isDragging) return;\n\n const dropZone = this.config.dropZone ?? '';\n const session = getCurrentDragSession<T>();\n\n // For cross-grid drags we require a matching zone-tagged MIME OR the\n // session payload's dropZone must match ours.\n const isIntra = this.isDragging && session?.payload.sourceGridId === this.gridId;\n if (!isIntra) {\n if (!dropZone) return; // intra-grid only — ignore external drags\n const matchingMime = findMatchingZoneMime(types, dropZone);\n if (!matchingMime && !(session && session.payload.dropZone === dropZone)) return;\n }\n\n de.preventDefault();\n if (dt) dt.dropEffect = (session?.payload.operation ?? this.config.operation ?? 'move') as 'copy' | 'move';\n\n // Compute drop position\n const rowEl = (de.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n const rows = this.internalGrid._rows ?? [];\n const pos = computeDropPosition(rowEl, de.clientY, (el) => this.getRowIndex(el), rows.length);\n\n // Same-row no-op for intra-grid\n if (isIntra && pos.overIndex !== null && pos.overIndex === this.draggedRowIndex) {\n this.clearDropTargetClasses();\n return;\n }\n\n // canDrop check (same-window only — payload visible)\n if (session && this.config.canDrop) {\n const accepted = this.config.canDrop(session.payload, pos.insertIndex);\n this.gridElement.classList.toggle('tbw-grid--drop-target-active', accepted);\n this.gridElement.classList.toggle('tbw-grid--drop-target-rejected', !accepted);\n if (!accepted) {\n this.clearDropTargetClasses();\n return;\n }\n } else {\n this.gridElement.classList.add('tbw-grid--drop-target-active');\n }\n\n this.dropRowIndex = pos.insertIndex;\n this.applyDropPositionClasses(rowEl, pos.isBefore);\n\n // Auto-scroll the target viewport\n if (this.config.autoScroll !== false) {\n this.ensureAutoScroller();\n this.autoScroller?.onPointerMove(de.clientY);\n }\n }\n\n private onDragLeave(de: DragEvent): void {\n const rowEl = (de.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n if (rowEl) rowEl.classList.remove('drop-target', 'drop-before', 'drop-after');\n // Tear down grid-level state when the cursor leaves the grid entirely\n if (de.currentTarget && !this.gridElement.contains(de.relatedTarget as Node)) {\n this.gridElement.classList.remove('tbw-grid--drop-target-active', 'tbw-grid--drop-target-rejected');\n this.autoScroller?.stop();\n }\n }\n\n private onDrop(de: DragEvent): void {\n de.preventDefault();\n this.autoScroller?.stop();\n this.gridElement.classList.remove('tbw-grid--drop-target-active', 'tbw-grid--drop-target-rejected');\n this.clearDropTargetClasses();\n\n const dt = de.dataTransfer;\n if (!dt) return;\n\n // Resolve payload — prefer same-window session, fall back to dataTransfer JSON\n const session = getCurrentDragSession<T>();\n let payload: RowDragPayload<T> | null = session?.payload ?? null;\n let liveRows: T[] | null = null;\n\n if (payload) {\n const lookup = lookupDragSession<T>(payload.sessionId);\n if (lookup) liveRows = lookup;\n } else {\n const raw = dt.getData(TBW_ROW_DRAG_MIME);\n payload = decodePayload<T>(raw);\n if (payload) {\n const lookup = lookupDragSession<T>(payload.sessionId);\n if (lookup) liveRows = lookup;\n }\n }\n if (!payload) return;\n\n // Drop position (recompute in case dragover wasn't called for a few frames)\n const rowEl = (de.target as HTMLElement).closest('.data-grid-row') as HTMLElement | null;\n const rows = this.internalGrid._rows ?? [];\n const pos = computeDropPosition(rowEl, de.clientY, (el) => this.getRowIndex(el), rows.length);\n let targetIndex = this.dropRowIndex ?? pos.insertIndex;\n\n const isIntra = payload.sourceGridId === this.gridId;\n const dropZone = this.config.dropZone ?? '';\n\n if (isIntra) {\n // Intra-grid path — preserve `RowReorderPlugin` semantics: emit `row-move`.\n const fromIndex = payload.rowIndices[0];\n // Adjust toIndex when dropping after the dragged row (single-row only)\n if (payload.rowIndices.length === 1 && targetIndex > fromIndex) targetIndex--;\n if (fromIndex === targetIndex) return;\n const row = (liveRows ?? payload.rows)[0];\n if (!this.canMoveRow(fromIndex, targetIndex)) return;\n this.executeIntraGridMove(row, fromIndex, targetIndex, 'drag');\n return;\n }\n\n // Cross-grid path\n if (!dropZone || dropZone !== payload.dropZone) return;\n\n // canDrop final check\n if (this.config.canDrop && !this.config.canDrop(payload, targetIndex)) {\n this.gridElement.classList.add('tbw-grid--drop-target-rejected');\n setTimeout(() => this.gridElement.classList.remove('tbw-grid--drop-target-rejected'), 200);\n return;\n }\n\n // Resolve final row references — live (same-window) or deserialized JSON\n const deserialize = this.config.deserializeRow ?? ((r: unknown) => r as T);\n const incomingRows: T[] = liveRows ?? payload.rows.map((r) => deserialize(r as unknown));\n\n const dropDetail: RowDropDetail<T> = {\n payload,\n sourceGridId: payload.sourceGridId,\n targetIndex,\n operation: payload.operation,\n };\n if (this.emitCancelable('row-drop', dropDetail)) return;\n\n // Insert into target grid's _rows\n const targetRows = [...rows];\n targetRows.splice(targetIndex, 0, ...(incomingRows as unknown[]));\n this.grid.rows = targetRows;\n\n // Remove from source grid's _rows when operation === 'move'\n if (payload.operation === 'move') {\n const sourceGrid = document.getElementById(payload.sourceGridId) as\n | (HTMLElement & { rows?: unknown[]; _rows?: unknown[] })\n | null;\n if (sourceGrid) {\n const srcRows = (sourceGrid._rows ?? sourceGrid.rows ?? []).slice();\n // Remove from highest index down so earlier indices stay stable\n const sortedIndices = [...payload.rowIndices].sort((a, b) => b - a);\n for (const idx of sortedIndices) {\n if (idx >= 0 && idx < srcRows.length) srcRows.splice(idx, 1);\n }\n sourceGrid.rows = srcRows;\n }\n }\n\n // Mark accepted on the source plugin so dragend knows\n const sourcePlugin = this.findPeerOnGrid(payload.sourceGridId);\n if (sourcePlugin) sourcePlugin.dragAccepted = true;\n\n // Emit row-transfer on BOTH grids\n const transferDetail: RowTransferDetail<T> = {\n rows: incomingRows,\n fromGridId: payload.sourceGridId,\n toGridId: this.gridId,\n fromIndices: payload.rowIndices,\n toIndex: targetIndex,\n operation: payload.operation,\n };\n this.emit('row-transfer', transferDetail);\n sourcePlugin?.emitTransfer(transferDetail);\n }\n\n private onDragEnd(): void {\n if (this.dragSessionId) clearDragSession(this.dragSessionId);\n clearCurrentDragSession();\n this.autoScroller?.stop();\n this.gridElement.classList.remove('tbw-grid--drag-source');\n\n if (this.isDragging) {\n const endDetail: RowDragEndDetail<T> = {\n rows: this.draggedRows,\n indices: this.draggedIndices,\n accepted: this.dragAccepted,\n };\n this.emit('row-drag-end', endDetail);\n }\n this.clearDragClasses();\n this.resetDragState();\n }\n // #endregion\n\n // #region Helpers\n\n /** Public wrapper so a peer plugin can dispatch `row-transfer` on this grid. @internal */\n emitTransfer(detail: RowTransferDetail<T>): void {\n this.emit('row-transfer', detail);\n }\n\n /** Find the peer `RowDragDropPlugin` instance on another grid by id. */\n private findPeerOnGrid(gridId: string): RowDragDropPlugin<T> | null {\n const peerEl = document.getElementById(gridId) as\n | (HTMLElement & { getPluginByName?: (name: string) => RowDragDropPlugin<T> | undefined })\n | null;\n if (!peerEl?.getPluginByName) return null;\n return peerEl.getPluginByName('rowDragDrop') ?? null;\n }\n\n private resolveDraggedRows(originIndex: number): { rows: T[]; indices: number[] } {\n const rows = this.internalGrid._rows ?? this.sourceRows;\n const originRow = rows[originIndex] as T;\n\n // If a selection plugin is loaded and the dragged row is selected, drag the whole selection.\n const selection = this.grid?.getPluginByName?.('selection') as\n | { getSelectedRowIndices?: () => number[]; getSelectedRows?: <U>() => U[] }\n | undefined;\n if (selection?.getSelectedRowIndices) {\n const selectedIndices = selection.getSelectedRowIndices();\n if (selectedIndices.includes(originIndex) && selectedIndices.length > 1) {\n const sorted = [...selectedIndices].sort((a, b) => a - b);\n return {\n rows: sorted.map((i) => rows[i] as T),\n indices: sorted,\n };\n }\n }\n return { rows: [originRow], indices: [originIndex] };\n }\n\n private ensureAutoScroller(): void {\n if (this.autoScroller) return;\n const viewport = this.gridElement.querySelector<HTMLElement>('.rows-viewport');\n if (!viewport) return;\n const opts = typeof this.config.autoScroll === 'object' ? this.config.autoScroll : undefined;\n this.autoScroller = createAutoScroller(viewport, opts, (active) => {\n this.gridElement.classList.toggle('tbw-grid--auto-scrolling', active);\n });\n }\n\n private applyDropPositionClasses(rowEl: HTMLElement | null, isBefore: boolean): void {\n this.clearDropTargetClasses();\n if (!rowEl) return;\n rowEl.classList.add('drop-target');\n rowEl.classList.toggle('drop-before', isBefore);\n rowEl.classList.toggle('drop-after', !isBefore);\n }\n\n private clearDropTargetClasses(): void {\n this.gridElement?.querySelectorAll('.data-grid-row.drop-target').forEach((row) => {\n row.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n }\n\n private clearDragClasses(): void {\n this.gridElement?.querySelectorAll('.data-grid-row').forEach((row) => {\n row.classList.remove(GridClasses.DRAGGING, 'drop-target', 'drop-before', 'drop-after');\n });\n }\n\n private resetDragState(): void {\n this.isDragging = false;\n this.draggedRowIndex = null;\n this.draggedRows = [];\n this.draggedIndices = [];\n this.dragSessionId = null;\n this.dragAccepted = false;\n this.dropRowIndex = null;\n this.pendingMove = null;\n }\n\n private getRowIndex(rowEl: HTMLElement): number {\n const cell = rowEl.querySelector('.cell[data-row]');\n return cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n }\n\n private clearDebounceTimer(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n\n // #endregion\n\n // #region Intra-Grid Move (parity with RowReorderPlugin)\n\n private handleKeyboardMove(row: T, fromIndex: number, toIndex: number, focusCol: number): void {\n if (!this.pendingMove) {\n this.pendingMove = { originalIndex: fromIndex, currentIndex: toIndex, row };\n } else {\n this.pendingMove.currentIndex = toIndex;\n }\n this.lastFocusCol = focusCol;\n\n const grid = this.internalGrid;\n const rows = [...(grid._rows ?? this.sourceRows)];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n\n grid._rows = rows;\n grid._focusRow = toIndex;\n grid._focusCol = focusCol;\n grid.refreshVirtualWindow(true);\n ensureCellVisible(grid);\n\n this.clearDebounceTimer();\n this.debounceTimer = setTimeout(() => this.flushPendingMove(), this.config.debounceMs ?? 300);\n }\n\n private flushPendingMove(): void {\n this.clearDebounceTimer();\n if (!this.pendingMove) return;\n const { originalIndex, currentIndex, row: movedRow } = this.pendingMove;\n this.pendingMove = null;\n if (originalIndex === currentIndex) return;\n\n const grid = this.internalGrid;\n // `grid._rows` already reflects the post-move order (mutated incrementally\n // by `handleKeyboardMove`); report it as `detail.rows` so consumers see\n // the actual reordered array, not the original `sourceRows` snapshot.\n const postMoveRows = (grid._rows ?? this.sourceRows) as T[];\n const detail: RowMoveDetail<T> = {\n row: movedRow as T,\n fromIndex: originalIndex,\n toIndex: currentIndex,\n rows: [...postMoveRows],\n source: 'keyboard',\n };\n const cancelled = this.emitCancelable('row-move', detail);\n if (cancelled) {\n // Revert: restore the original snapshot. `sourceRows` was never mutated\n // during the pending move (only `grid._rows` was), so resetting from it\n // is the correct rollback regardless of how many incremental keyboard\n // moves accumulated.\n grid._rows = [...this.sourceRows];\n grid._focusRow = originalIndex;\n grid._focusCol = this.lastFocusCol;\n grid.refreshVirtualWindow(true);\n ensureCellVisible(grid);\n }\n }\n\n private executeIntraGridMove(row: unknown, fromIndex: number, toIndex: number, source: 'keyboard' | 'drag'): void {\n const rows = [...this.sourceRows];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n const detail: RowMoveDetail<T> = {\n row: row as T,\n fromIndex,\n toIndex,\n rows: rows as T[],\n source,\n };\n const cancelled = this.emitCancelable('row-move', detail);\n if (cancelled) return;\n if (this.animationType === 'flip' && this.gridElement) {\n const oldPositions = this.captureRowPositions();\n this.grid.rows = rows;\n requestAnimationFrame(() => {\n void this.gridElement.offsetHeight;\n this.animateFLIP(oldPositions, fromIndex, toIndex);\n });\n } else {\n this.grid.rows = rows;\n }\n }\n\n private captureRowPositions(): Map<number, number> {\n const positions = new Map<number, number>();\n this.gridElement?.querySelectorAll('.data-grid-row').forEach((row) => {\n const rowIndex = this.getRowIndex(row as HTMLElement);\n if (rowIndex >= 0) positions.set(rowIndex, row.getBoundingClientRect().top);\n });\n return positions;\n }\n\n private animateFLIP(oldPositions: Map<number, number>, fromIndex: number, toIndex: number): void {\n const gridEl = this.gridElement;\n if (!gridEl || oldPositions.size === 0) return;\n\n const minIndex = Math.min(fromIndex, toIndex);\n const maxIndex = Math.max(fromIndex, toIndex);\n const rowsToAnimate: { el: HTMLElement; deltaY: number }[] = [];\n\n gridEl.querySelectorAll('.data-grid-row').forEach((row) => {\n const rowEl = row as HTMLElement;\n const newRowIndex = this.getRowIndex(rowEl);\n if (newRowIndex < 0 || newRowIndex < minIndex || newRowIndex > maxIndex) return;\n let oldIndex: number;\n if (newRowIndex === toIndex) oldIndex = fromIndex;\n else if (fromIndex < toIndex) oldIndex = newRowIndex + 1;\n else oldIndex = newRowIndex - 1;\n const oldTop = oldPositions.get(oldIndex);\n if (oldTop === undefined) return;\n const newTop = rowEl.getBoundingClientRect().top;\n const deltaY = oldTop - newTop;\n if (Math.abs(deltaY) > 1) rowsToAnimate.push({ el: rowEl, deltaY });\n });\n\n if (rowsToAnimate.length === 0) return;\n\n rowsToAnimate.forEach(({ el, deltaY }) => {\n el.style.transform = `translateY(${deltaY}px)`;\n });\n void gridEl.offsetHeight;\n\n const duration = this.animationDuration;\n requestAnimationFrame(() => {\n rowsToAnimate.forEach(({ el }) => {\n el.classList.add('flip-animating');\n el.style.transform = '';\n });\n setTimeout(() => {\n rowsToAnimate.forEach(({ el }) => {\n el.style.transform = '';\n el.classList.remove('flip-animating');\n });\n }, duration + 50);\n });\n }\n\n // #endregion\n}\n"],"names":["currentSession","getCurrentDragSession","clearCurrentDragSession","TBW_ROW_DRAG_MIME","parseZoneFromMime","mime","match","decodeURIComponent","encodePayload","payload","JSON","stringify","computeDropPosition","rowEl","clientY","rowIndexResolver","totalRows","overIndex","insertIndex","isBefore","targetIndex","rect","getBoundingClientRect","top","height","RowDragDropPlugin","BaseGridPlugin","name","aliases","styles","static","events","type","description","cancelable","defaultConfig","enableKeyboard","showDragHandle","dragHandlePosition","dragHandleWidth","debounceMs","animation","operation","autoScroll","animationType","this","isAnimationEnabled","config","isDragging","draggedRowIndex","draggedRows","draggedIndices","dragSessionId","dragAccepted","dropRowIndex","pendingMove","debounceTimer","lastFocusCol","autoScroller","gridId","internalGrid","grid","attach","super","host","gridElement","id","newDragSessionId","slice","setupDelegatedDragListeners","detach","clearDebounceTimer","stop","clearDragSession","resetDragState","processColumns","columns","dragHandleColumn","field","header","width","resizable","sortable","filterable","lockPosition","utility","viewRenderer","container","document","createElement","className","setAttribute","draggable","setIcon","afterRender","onKeyDown","event","ctrlKey","key","focusRow","_focusRow","rows","_rows","sourceRows","length","toIndex","row","canMoveRow","handleKeyboardMove","_focusCol","preventDefault","stopPropagation","onCellClick","flushPendingMove","moveRow","fromIndex","executeIntraGridMove","queryResults","query","Array","isArray","includes","canDrag","canMove","direction","canDrop","sessionId","sourceGridId","dropZone","rowIndices","gridEl","signal","disconnectSignal","addEventListener","e","onDragStart","onDragEnd","onDragOver","onDragLeave","onDrop","de","handle","target","closest","rowIndex","getRowIndex","indices","resolveDraggedRows","startDetail","emitCancelable","serialize","serializeRow","r","map","dataTransfer","effectAllowed","setData","encodeURIComponent","dataColumns","filter","c","lines","cells","col","value","String","replace","push","join","formatRowsAsTSV","badge","textContent","body","appendChild","setDragImage","setTimeout","remove","registerDragSession","setCurrentDragSession","classList","add","GridClasses","DRAGGING","dt","types","from","t","startsWith","hasAnyRowDragMime","session","isIntra","matchingMime","zone","findMatchingZoneMime","dropEffect","pos","el","clearDropTargetClasses","accepted","toggle","applyDropPositionClasses","ensureAutoScroller","onPointerMove","currentTarget","contains","relatedTarget","liveRows","lookup","lookupDragSession","raw","parsed","parse","idx","Number","isInteger","decodePayload","getData","deserialize","deserializeRow","incomingRows","dropDetail","targetRows","splice","sourceGrid","getElementById","srcRows","sortedIndices","sort","a","b","sourcePlugin","findPeerOnGrid","transferDetail","fromGridId","toGridId","fromIndices","emit","emitTransfer","endDetail","clearDragClasses","detail","peerEl","getPluginByName","originIndex","originRow","selection","getSelectedRowIndices","selectedIndices","sorted","i","viewport","querySelector","opts","options","onScrollChange","edgeSize","baseSpeed","speed","maxSpeed","rafId","pointerY","active","setActive","next","tick","delta","ratio","Math","max","round","bottom","before","scrollTop","requestAnimationFrame","cancelAnimationFrame","isScrolling","createAutoScroller","querySelectorAll","forEach","cell","parseInt","getAttribute","clearTimeout","focusCol","currentIndex","originalIndex","movedRow","refreshVirtualWindow","ensureCellVisible","source","oldPositions","captureRowPositions","offsetHeight","animateFLIP","positions","Map","set","size","minIndex","min","maxIndex","rowsToAnimate","newRowIndex","oldIndex","oldTop","get","deltaY","abs","style","transform","duration","animationDuration"],"mappings":"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"}
|