@toolbox-web/grid 0.3.3 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/all.d.ts +19 -19
  2. package/all.d.ts.map +1 -1
  3. package/all.js +1798 -1201
  4. package/all.js.map +1 -1
  5. package/index.js +2215 -2026
  6. package/index.js.map +1 -1
  7. package/lib/core/grid.d.ts +22 -12
  8. package/lib/core/grid.d.ts.map +1 -1
  9. package/lib/core/internal/columns.d.ts +0 -9
  10. package/lib/core/internal/columns.d.ts.map +1 -1
  11. package/lib/core/internal/config-manager.d.ts +236 -0
  12. package/lib/core/internal/config-manager.d.ts.map +1 -0
  13. package/lib/core/internal/editing.d.ts +3 -3
  14. package/lib/core/internal/editing.d.ts.map +1 -1
  15. package/lib/core/internal/event-delegation.d.ts.map +1 -1
  16. package/lib/core/internal/header.d.ts.map +1 -1
  17. package/lib/core/internal/keyboard.d.ts.map +1 -1
  18. package/lib/core/internal/render-scheduler.d.ts +123 -0
  19. package/lib/core/internal/render-scheduler.d.ts.map +1 -0
  20. package/lib/core/internal/rows.d.ts +8 -3
  21. package/lib/core/internal/rows.d.ts.map +1 -1
  22. package/lib/core/internal/sanitize.d.ts +2 -2
  23. package/lib/core/internal/sanitize.d.ts.map +1 -1
  24. package/lib/core/internal/shell.d.ts +40 -2
  25. package/lib/core/internal/shell.d.ts.map +1 -1
  26. package/lib/core/internal/validate-config.d.ts +25 -0
  27. package/lib/core/internal/validate-config.d.ts.map +1 -0
  28. package/lib/core/plugin/base-plugin.d.ts +118 -0
  29. package/lib/core/plugin/base-plugin.d.ts.map +1 -1
  30. package/lib/core/plugin/index.d.ts +1 -0
  31. package/lib/core/plugin/index.d.ts.map +1 -1
  32. package/lib/core/plugin/plugin-manager.d.ts +13 -2
  33. package/lib/core/plugin/plugin-manager.d.ts.map +1 -1
  34. package/lib/core/plugin/types.d.ts +17 -3
  35. package/lib/core/plugin/types.d.ts.map +1 -1
  36. package/lib/core/types.d.ts +112 -12
  37. package/lib/core/types.d.ts.map +1 -1
  38. package/lib/plugins/clipboard/ClipboardPlugin.d.ts +7 -1
  39. package/lib/plugins/clipboard/ClipboardPlugin.d.ts.map +1 -1
  40. package/lib/plugins/clipboard/index.js +78 -22
  41. package/lib/plugins/clipboard/index.js.map +1 -1
  42. package/lib/plugins/column-virtualization/index.js +72 -21
  43. package/lib/plugins/column-virtualization/index.js.map +1 -1
  44. package/lib/plugins/context-menu/index.js +53 -2
  45. package/lib/plugins/context-menu/index.js.map +1 -1
  46. package/lib/plugins/editing/EditingPlugin.d.ts +117 -0
  47. package/lib/plugins/editing/EditingPlugin.d.ts.map +1 -0
  48. package/lib/{core/internal → plugins/editing}/editors.d.ts +1 -1
  49. package/lib/plugins/editing/editors.d.ts.map +1 -0
  50. package/lib/plugins/editing/index.d.ts +8 -0
  51. package/lib/plugins/editing/index.d.ts.map +1 -0
  52. package/lib/plugins/editing/index.js +721 -0
  53. package/lib/plugins/editing/index.js.map +1 -0
  54. package/lib/plugins/editing/types.d.ts +45 -0
  55. package/lib/plugins/editing/types.d.ts.map +1 -0
  56. package/lib/plugins/export/ExportPlugin.d.ts.map +1 -1
  57. package/lib/plugins/export/index.js +68 -17
  58. package/lib/plugins/export/index.js.map +1 -1
  59. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  60. package/lib/plugins/filtering/index.js +103 -50
  61. package/lib/plugins/filtering/index.js.map +1 -1
  62. package/lib/plugins/grouping-columns/grouping-columns.d.ts +4 -4
  63. package/lib/plugins/grouping-columns/grouping-columns.d.ts.map +1 -1
  64. package/lib/plugins/grouping-columns/index.js +80 -29
  65. package/lib/plugins/grouping-columns/index.js.map +1 -1
  66. package/lib/plugins/grouping-rows/GroupingRowsPlugin.d.ts.map +1 -1
  67. package/lib/plugins/grouping-rows/grouping-rows.d.ts.map +1 -1
  68. package/lib/plugins/grouping-rows/index.js +62 -11
  69. package/lib/plugins/grouping-rows/index.js.map +1 -1
  70. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts +2 -2
  71. package/lib/plugins/master-detail/MasterDetailPlugin.d.ts.map +1 -1
  72. package/lib/plugins/master-detail/index.js +156 -102
  73. package/lib/plugins/master-detail/index.js.map +1 -1
  74. package/lib/plugins/master-detail/types.d.ts +12 -2
  75. package/lib/plugins/master-detail/types.d.ts.map +1 -1
  76. package/lib/plugins/multi-sort/MultiSortPlugin.d.ts.map +1 -1
  77. package/lib/plugins/multi-sort/index.js +75 -22
  78. package/lib/plugins/multi-sort/index.js.map +1 -1
  79. package/lib/plugins/pinned-columns/index.js +57 -6
  80. package/lib/plugins/pinned-columns/index.js.map +1 -1
  81. package/lib/plugins/pinned-rows/PinnedRowsPlugin.d.ts.map +1 -1
  82. package/lib/plugins/pinned-rows/index.js +61 -9
  83. package/lib/plugins/pinned-rows/index.js.map +1 -1
  84. package/lib/plugins/pivot/index.js +58 -7
  85. package/lib/plugins/pivot/index.js.map +1 -1
  86. package/lib/plugins/reorder/ReorderPlugin.d.ts.map +1 -1
  87. package/lib/plugins/reorder/index.js +75 -19
  88. package/lib/plugins/reorder/index.js.map +1 -1
  89. package/lib/plugins/selection/index.js +57 -6
  90. package/lib/plugins/selection/index.js.map +1 -1
  91. package/lib/plugins/server-side/index.js +60 -9
  92. package/lib/plugins/server-side/index.js.map +1 -1
  93. package/lib/plugins/tree/TreePlugin.d.ts +3 -3
  94. package/lib/plugins/tree/TreePlugin.d.ts.map +1 -1
  95. package/lib/plugins/tree/index.js +181 -126
  96. package/lib/plugins/tree/index.js.map +1 -1
  97. package/lib/plugins/tree/tree-data.d.ts +6 -6
  98. package/lib/plugins/tree/tree-data.d.ts.map +1 -1
  99. package/lib/plugins/tree/tree-detect.d.ts +5 -9
  100. package/lib/plugins/tree/tree-detect.d.ts.map +1 -1
  101. package/lib/plugins/tree/types.d.ts +16 -4
  102. package/lib/plugins/tree/types.d.ts.map +1 -1
  103. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts +7 -1
  104. package/lib/plugins/undo-redo/UndoRedoPlugin.d.ts.map +1 -1
  105. package/lib/plugins/undo-redo/index.js +65 -6
  106. package/lib/plugins/undo-redo/index.js.map +1 -1
  107. package/lib/plugins/visibility/VisibilityPlugin.d.ts +7 -1
  108. package/lib/plugins/visibility/VisibilityPlugin.d.ts.map +1 -1
  109. package/lib/plugins/visibility/index.js +61 -2
  110. package/lib/plugins/visibility/index.js.map +1 -1
  111. package/package.json +1 -1
  112. package/public.d.ts +104 -13
  113. package/public.d.ts.map +1 -1
  114. package/umd/grid.all.umd.js +37 -19
  115. package/umd/grid.all.umd.js.map +1 -1
  116. package/umd/grid.umd.js +25 -7
  117. package/umd/grid.umd.js.map +1 -1
  118. package/umd/plugins/clipboard.umd.js +2 -2
  119. package/umd/plugins/clipboard.umd.js.map +1 -1
  120. package/umd/plugins/editing.umd.js +2 -0
  121. package/umd/plugins/editing.umd.js.map +1 -0
  122. package/umd/plugins/export.umd.js +2 -2
  123. package/umd/plugins/export.umd.js.map +1 -1
  124. package/umd/plugins/filtering.umd.js +1 -1
  125. package/umd/plugins/filtering.umd.js.map +1 -1
  126. package/umd/plugins/grouping-columns.umd.js +1 -1
  127. package/umd/plugins/grouping-columns.umd.js.map +1 -1
  128. package/umd/plugins/grouping-rows.umd.js +1 -1
  129. package/umd/plugins/grouping-rows.umd.js.map +1 -1
  130. package/umd/plugins/master-detail.umd.js +1 -1
  131. package/umd/plugins/master-detail.umd.js.map +1 -1
  132. package/umd/plugins/multi-sort.umd.js +1 -1
  133. package/umd/plugins/multi-sort.umd.js.map +1 -1
  134. package/umd/plugins/pinned-rows.umd.js +1 -1
  135. package/umd/plugins/pinned-rows.umd.js.map +1 -1
  136. package/umd/plugins/reorder.umd.js +1 -1
  137. package/umd/plugins/reorder.umd.js.map +1 -1
  138. package/umd/plugins/tree.umd.js +1 -1
  139. package/umd/plugins/tree.umd.js.map +1 -1
  140. package/umd/plugins/undo-redo.umd.js +1 -1
  141. package/umd/plugins/undo-redo.umd.js.map +1 -1
  142. package/umd/plugins/visibility.umd.js +1 -1
  143. package/umd/plugins/visibility.umd.js.map +1 -1
  144. package/lib/core/internal/column-state.d.ts +0 -124
  145. package/lib/core/internal/column-state.d.ts.map +0 -1
  146. package/lib/core/internal/editors.d.ts.map +0 -1
  147. package/lib/core/internal/grid-internals.d.ts +0 -83
  148. package/lib/core/internal/grid-internals.d.ts.map +0 -1
@@ -3,7 +3,7 @@
3
3
  `,l=t.filter(i=>!i.hidden&&!i.field.startsWith("__")),c=[];if(s.includeHeaders){const i=l.map(d=>{const h=d.header||d.field;return h.includes(r)||h.includes(o)||h.includes('"')?`"${h.replace(/"/g,'""')}"`:h});c.push(i.join(r))}const u=[...n instanceof Set?[...n]:n].sort((i,d)=>i-d);for(const i of u){const d=e[i];if(!d)continue;const h=l.map(m=>R(d[m.field],m.field,d,s));c.push(h.join(r))}return c.join(o)}async function x(f){try{return await navigator.clipboard.writeText(f),!0}catch{const e=document.createElement("textarea");e.value=f,e.style.position="fixed",e.style.opacity="0",e.style.pointerEvents="none",document.body.appendChild(e),e.select();const t=document.execCommand("copy");return document.body.removeChild(e),t}}function S(f,e){const t=e.delimiter??" ",n=e.newline??`
4
4
  `,s=f.replace(/\r\n/g,`
5
5
  `).replace(/\r/g,`
6
- `),r=[];let o=[],l="",c=!1;for(let a=0;a<s.length;a++){const u=s[a];u==='"'&&!c?c=!0:u==='"'&&c?s[a+1]==='"'?(l+='"',a++):c=!1:u===t&&!c?(o.push(l),l=""):u===n&&!c?(o.push(l),l="",(o.length>1||o.some(i=>i.trim()!==""))&&r.push(o),o=[]):l+=u}return o.push(l),(o.length>1||o.some(a=>a.trim()!==""))&&r.push(o),r}async function b(){try{return await navigator.clipboard.readText()}catch{return""}}class T extends g.BaseGridPlugin{name="clipboard";version="1.0.0";get defaultConfig(){return{includeHeaders:!1,delimiter:" ",newline:`
7
- `,quoteStrings:!1}}lastCopied=null;detach(){this.lastCopied=null}onKeyDown(e){const t=(e.ctrlKey||e.metaKey)&&e.key==="c",n=(e.ctrlKey||e.metaKey)&&e.key==="v";return t?(this.#t(e.target),!0):n?(this.#n(),!0):!1}#t(e){const t=this.#e(),n=t?.getSelectedRows()??[],s=n.length>0,r=t?.getRanges()??[],o=r.length>0,l=t?.getSelectedCell()!=null;let c,a,u;if(s&&t)c=y({rows:this.rows,columns:[...this.columns],selectedIndices:n,config:this.config}),a=n.length,u=this.columns.filter(i=>!i.hidden&&!i.field.startsWith("__")).length;else if(o&&t){const i=r[r.length-1],d=this.#s({startRow:i.from.row,startCol:i.from.col,endRow:i.to.row,endCol:i.to.col});c=d.text,a=d.rowCount,u=d.columnCount}else if(l&&t){const i=t.getSelectedCell(),d=this.#i(i.row,i.col);if(!d)return;c=d.text,a=1,u=1}else{const i=this.#o(e);if(!i)return;c=i.text,a=1,u=1}x(c).then(()=>{this.lastCopied={text:c,timestamp:Date.now()},this.emit("copy",{text:c,rowCount:a,columnCount:u})})}#n(){b().then(e=>{if(!e)return;const t=S(e,this.config);this.emit("paste",{rows:t,text:e})})}#e(){try{const e=this.grid;if(e?._plugins){for(const t of e._plugins)if(t.name==="selection")return t}}catch{}}#i(e,t){const n=this.rows[e];if(!n)return null;const s=this.columns[t];if(!s)return null;const r=n[s.field],o=s.header||s.field;let l;if(this.config.includeHeaders){const c=r==null?"":String(r);l=`${o}: ${c}`}else l=r==null?"":String(r);return{text:l}}#s(e){const{startRow:t,startCol:n,endRow:s,endCol:r}=e,o=Math.min(t,s),l=Math.max(t,s),c=Math.min(n,r),a=Math.max(n,r),u=this.config.delimiter??" ",i=this.config.newline??`
6
+ `),r=[];let o=[],l="",c=!1;for(let a=0;a<s.length;a++){const u=s[a];u==='"'&&!c?c=!0:u==='"'&&c?s[a+1]==='"'?(l+='"',a++):c=!1:u===t&&!c?(o.push(l),l=""):u===n&&!c?(o.push(l),l="",(o.length>1||o.some(i=>i.trim()!==""))&&r.push(o),o=[]):l+=u}return o.push(l),(o.length>1||o.some(a=>a.trim()!==""))&&r.push(o),r}async function b(){try{return await navigator.clipboard.readText()}catch{return""}}class T extends g.BaseGridPlugin{static dependencies=[{name:"selection",required:!0,reason:"ClipboardPlugin needs selection to determine what cells to copy"}];name="clipboard";version="1.0.0";get defaultConfig(){return{includeHeaders:!1,delimiter:" ",newline:`
7
+ `,quoteStrings:!1}}lastCopied=null;detach(){this.lastCopied=null}onKeyDown(e){const t=(e.ctrlKey||e.metaKey)&&e.key==="c",n=(e.ctrlKey||e.metaKey)&&e.key==="v";return t?(this.#t(e.target),!0):n?(this.#n(),!0):!1}#t(e){const t=this.#e(),n=t?.getSelectedRows()??[],s=n.length>0,r=t?.getRanges()??[],o=r.length>0,l=t?.getSelectedCell()!=null;let c,a,u;if(s&&t)c=y({rows:this.rows,columns:[...this.columns],selectedIndices:n,config:this.config}),a=n.length,u=this.columns.filter(i=>!i.hidden&&!i.field.startsWith("__")).length;else if(o&&t){const i=r[r.length-1],d=this.#s({startRow:i.from.row,startCol:i.from.col,endRow:i.to.row,endCol:i.to.col});c=d.text,a=d.rowCount,u=d.columnCount}else if(l&&t){const i=t.getSelectedCell(),d=this.#i(i.row,i.col);if(!d)return;c=d.text,a=1,u=1}else{const i=this.#o(e);if(!i)return;c=i.text,a=1,u=1}x(c).then(()=>{this.lastCopied={text:c,timestamp:Date.now()},this.emit("copy",{text:c,rowCount:a,columnCount:u})})}#n(){b().then(e=>{if(!e)return;const t=S(e,this.config);this.emit("paste",{rows:t,text:e})})}#e(){try{const e=this.grid?.getPluginByName("selection");if(e)return e}catch{}}#i(e,t){const n=this.rows[e];if(!n)return null;const s=this.columns[t];if(!s)return null;const r=n[s.field],o=s.header||s.field;let l;if(this.config.includeHeaders){const c=r==null?"":String(r);l=`${o}: ${c}`}else l=r==null?"":String(r);return{text:l}}#s(e){const{startRow:t,startCol:n,endRow:s,endCol:r}=e,o=Math.min(t,s),l=Math.max(t,s),c=Math.min(n,r),a=Math.max(n,r),u=this.config.delimiter??" ",i=this.config.newline??`
8
8
  `,d=[],h=this.columns.slice(c,a+1);if(this.config.includeHeaders){const m=h.map(w=>w.header||w.field);d.push(m.join(u))}for(let m=o;m<=l;m++){const w=this.rows[m];if(!w)continue;const P=h.map(I=>{const C=w[I.field];return C==null?"":C instanceof Date?C.toISOString():String(C)});d.push(P.join(u))}return{text:d.join(i),rowCount:l-o+1,columnCount:a-c+1}}#o(e){const t=e.closest("[data-field-cache]");if(!t)return null;const n=t.dataset.fieldCache;if(!n)return null;const s=t.dataset.row;if(!s)return null;const r=parseInt(s,10);if(isNaN(r))return null;const o=this.rows[r];if(!o)return null;const l=o[n],a=this.columns.find(i=>i.field===n)?.header||n;let u;if(this.config.includeHeaders){const i=l==null?"":String(l);u=`${a}: ${i}`}else u=l==null?"":String(l);return{text:u,field:n,value:l}}async copy(){const t=this.#e()?.getSelectedRows()??[],n=y({rows:this.rows,columns:[...this.columns],selectedIndices:t,config:this.config});return await x(n),this.lastCopied={text:n,timestamp:Date.now()},n}async copyRows(e){const t=y({rows:this.rows,columns:[...this.columns],selectedIndices:e,config:this.config});return await x(t),this.lastCopied={text:t,timestamp:Date.now()},t}async paste(){const e=await b();return e?S(e,this.config):null}getLastCopied(){return this.lastCopied}}p.ClipboardPlugin=T,Object.defineProperty(p,Symbol.toStringTag,{value:"Module"})}));
9
9
  //# sourceMappingURL=clipboard.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"clipboard.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/clipboard/copy.ts","../../../../../libs/grid/src/lib/plugins/clipboard/paste.ts","../../../../../libs/grid/src/lib/plugins/clipboard/ClipboardPlugin.ts"],"sourcesContent":["/**\n * Clipboard Copy Logic\n *\n * Pure functions for copying grid data to clipboard.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ClipboardConfig } from './types';\n\n/** Parameters for building clipboard text */\nexport interface CopyParams {\n /** All grid rows */\n rows: unknown[];\n /** Column configurations */\n columns: ColumnConfig[];\n /** Selected row indices */\n selectedIndices: Set<number> | number[];\n /** Clipboard configuration */\n config: ClipboardConfig;\n}\n\n/**\n * Format a cell value for clipboard output.\n *\n * Uses custom processCell if provided, otherwise applies default formatting:\n * - null/undefined → empty string\n * - Date → ISO string\n * - Object → JSON string\n * - Other → String conversion with optional quoting\n *\n * @param value - The cell value to format\n * @param field - The field name\n * @param row - The full row object\n * @param config - Clipboard configuration\n * @returns Formatted string value\n */\nexport function formatCellValue(value: unknown, field: string, row: unknown, config: ClipboardConfig): string {\n if (config.processCell) {\n return config.processCell(value, field, row);\n }\n\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Quote if contains delimiter, newline, or quotes (or if quoteStrings is enabled)\n if (config.quoteStrings || str.includes(delimiter) || str.includes(newline) || str.includes('\"')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build clipboard text from selected rows and columns.\n *\n * @param params - Copy parameters including rows, columns, selection, and config\n * @returns Tab-separated (or custom delimiter) text ready for clipboard\n */\nexport function buildClipboardText(params: CopyParams): string {\n const { rows, columns, selectedIndices, config } = params;\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Filter to visible columns (not hidden, not internal __ prefixed)\n const visibleColumns = columns.filter((c) => !c.hidden && !c.field.startsWith('__'));\n\n const lines: string[] = [];\n\n // Add header row if configured\n if (config.includeHeaders) {\n const headerCells = visibleColumns.map((c) => {\n const header = c.header || c.field;\n // Quote headers if they contain special characters\n if (header.includes(delimiter) || header.includes(newline) || header.includes('\"')) {\n return `\"${header.replace(/\"/g, '\"\"')}\"`;\n }\n return header;\n });\n lines.push(headerCells.join(delimiter));\n }\n\n // Convert indices to sorted array\n const indices = selectedIndices instanceof Set ? [...selectedIndices] : selectedIndices;\n const sortedIndices = [...indices].sort((a, b) => a - b);\n\n // Build data rows\n for (const idx of sortedIndices) {\n const row = rows[idx];\n if (!row) continue;\n\n const cells = visibleColumns.map((col) =>\n formatCellValue((row as Record<string, unknown>)[col.field], col.field, row, config)\n );\n lines.push(cells.join(delimiter));\n }\n\n return lines.join(newline);\n}\n\n/**\n * Copy text to the system clipboard.\n *\n * Uses the modern Clipboard API when available, with fallback\n * to execCommand for older browsers.\n *\n * @param text - The text to copy\n * @returns Promise resolving to true if successful, false otherwise\n */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n // Fallback for older browsers or when Clipboard API is not available\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.position = 'fixed';\n textarea.style.opacity = '0';\n textarea.style.pointerEvents = 'none';\n document.body.appendChild(textarea);\n textarea.select();\n const success = document.execCommand('copy');\n document.body.removeChild(textarea);\n return success;\n }\n}\n","/**\n * Clipboard Paste Logic\n *\n * Pure functions for reading and parsing clipboard data.\n */\n\nimport type { ClipboardConfig } from './types';\n\n/**\n * Parse clipboard text into a 2D array of cell values.\n *\n * Handles:\n * - Quoted values (with escaped double quotes \"\")\n * - Multi-line quoted values (newlines within quotes are preserved)\n * - Custom delimiters and newlines\n * - Empty lines (filtered out only if they contain no data)\n *\n * @param text - Raw clipboard text\n * @param config - Clipboard configuration\n * @returns 2D array where each sub-array is a row of cell values\n */\nexport function parseClipboardText(text: string, config: ClipboardConfig): string[][] {\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Handle Windows CRLF line endings\n const normalizedText = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n\n // Parse the entire text handling quoted fields that may span multiple lines\n const rows: string[][] = [];\n let currentRow: string[] = [];\n let currentCell = '';\n let inQuotes = false;\n\n for (let i = 0; i < normalizedText.length; i++) {\n const char = normalizedText[i];\n\n if (char === '\"' && !inQuotes) {\n // Start of quoted field\n inQuotes = true;\n } else if (char === '\"' && inQuotes) {\n // Check for escaped quote (\"\")\n if (normalizedText[i + 1] === '\"') {\n currentCell += '\"';\n i++; // Skip the second quote\n } else {\n // End of quoted field\n inQuotes = false;\n }\n } else if (char === delimiter && !inQuotes) {\n // Field separator\n currentRow.push(currentCell);\n currentCell = '';\n } else if (char === newline && !inQuotes) {\n // Row separator (only if not inside quotes)\n currentRow.push(currentCell);\n currentCell = '';\n // Only add non-empty rows (at least one non-empty cell or multiple cells)\n if (currentRow.length > 1 || currentRow.some((c) => c.trim() !== '')) {\n rows.push(currentRow);\n }\n currentRow = [];\n } else {\n currentCell += char;\n }\n }\n\n // Handle the last cell and row\n currentRow.push(currentCell);\n if (currentRow.length > 1 || currentRow.some((c) => c.trim() !== '')) {\n rows.push(currentRow);\n }\n\n return rows;\n}\n\n/**\n * Read text from the system clipboard.\n *\n * Uses the modern Clipboard API. Returns empty string if\n * the API is not available or permission is denied.\n *\n * @returns Promise resolving to clipboard text or empty string\n */\nexport async function readFromClipboard(): Promise<string> {\n try {\n return await navigator.clipboard.readText();\n } catch {\n // Permission denied or API not available\n return '';\n }\n}\n","/**\n * Clipboard Plugin (Class-based)\n *\n * Provides copy/paste functionality for tbw-grid.\n * Supports Ctrl+C/Cmd+C for copying and Ctrl+V/Cmd+V for pasting.\n *\n * **With Selection plugin:** Copy selected rows as a range\n * - includeHeaders: true → adds header row at top\n * - includeHeaders: false → data rows only\n *\n * **Without Selection plugin:** Copy focused cell only\n * - includeHeaders: true → \"Header: value\" format\n * - includeHeaders: false → value only\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport { buildClipboardText, copyToClipboard } from './copy';\nimport { parseClipboardText, readFromClipboard } from './paste';\nimport type { ClipboardConfig, CopyDetail, PasteDetail } from './types';\n\n/**\n * Clipboard Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ClipboardPlugin({ includeHeaders: true })\n * ```\n */\nexport class ClipboardPlugin extends BaseGridPlugin<ClipboardConfig> {\n readonly name = 'clipboard';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ClipboardConfig> {\n return {\n includeHeaders: false,\n delimiter: '\\t',\n newline: '\\n',\n quoteStrings: false,\n };\n }\n\n // #region Internal State\n /** The last copied text (for reference/debugging) */\n private lastCopied: { text: string; timestamp: number } | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.lastCopied = null;\n }\n // #endregion\n\n // #region Event Handlers\n\n override onKeyDown(event: KeyboardEvent): boolean {\n const isCopy = (event.ctrlKey || event.metaKey) && event.key === 'c';\n const isPaste = (event.ctrlKey || event.metaKey) && event.key === 'v';\n\n if (isCopy) {\n this.#handleCopy(event.target as HTMLElement);\n return true; // Prevent default browser behavior\n }\n\n if (isPaste) {\n this.#handlePaste();\n return true; // Prevent default browser behavior\n }\n\n return false;\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Handle copy operation\n */\n #handleCopy(target: HTMLElement): void {\n // Try to get selection from selection plugin (if available)\n // Use dynamic import to avoid circular dependency issues\n const selectionPlugin = this.#getSelectionPlugin();\n\n // Check for different selection types\n const selectedRows = selectionPlugin?.getSelectedRows() ?? [];\n const hasRowSelection = selectedRows.length > 0;\n const ranges = selectionPlugin?.getRanges() ?? [];\n const hasRangeSelection = ranges.length > 0;\n const hasCellSelection = selectionPlugin?.getSelectedCell() != null;\n\n let text: string;\n let rowCount: number;\n let columnCount: number;\n\n if (hasRowSelection && selectionPlugin) {\n // Row selection mode - copy full rows\n text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: selectedRows,\n config: this.config,\n });\n rowCount = selectedRows.length;\n columnCount = this.columns.filter((c) => !c.hidden && !c.field.startsWith('__')).length;\n } else if (hasRangeSelection && selectionPlugin) {\n // Range selection mode - copy rectangular range (use last range as active)\n const range = ranges[ranges.length - 1];\n const result = this.#buildRangeText({\n startRow: range.from.row,\n startCol: range.from.col,\n endRow: range.to.row,\n endCol: range.to.col,\n });\n text = result.text;\n rowCount = result.rowCount;\n columnCount = result.columnCount;\n } else if (hasCellSelection && selectionPlugin) {\n // Cell selection mode - copy single cell\n const cell = selectionPlugin.getSelectedCell()!;\n const result = this.#buildCellText(cell.row, cell.col);\n if (!result) return;\n text = result.text;\n rowCount = 1;\n columnCount = 1;\n } else {\n // Fallback: try to find focused cell from DOM\n const result = this.#buildSingleCellText(target);\n if (!result) return;\n text = result.text;\n rowCount = 1;\n columnCount = 1;\n }\n\n copyToClipboard(text).then(() => {\n this.lastCopied = { text, timestamp: Date.now() };\n this.emit<CopyDetail>('copy', { text, rowCount, columnCount });\n });\n }\n\n /**\n * Handle paste operation\n */\n #handlePaste(): void {\n readFromClipboard().then((text) => {\n if (!text) return;\n const parsed = parseClipboardText(text, this.config);\n this.emit<PasteDetail>('paste', { rows: parsed, text });\n });\n }\n\n /**\n * Get the selection plugin instance if available.\n */\n #getSelectionPlugin(): SelectionPluginInterface | undefined {\n // Dynamically get the SelectionPlugin class to avoid import order issues\n try {\n // Use getPlugin with the class - this requires the class to be imported\n // For now, we'll use a duck-typing approach via the grid's plugin registry\n const grid = this.grid as any;\n if (grid?._plugins) {\n for (const plugin of grid._plugins) {\n if (plugin.name === 'selection') {\n return plugin as SelectionPluginInterface;\n }\n }\n }\n } catch {\n // Selection plugin not available\n }\n return undefined;\n }\n\n /**\n * Build text for a single cell by row/col index.\n */\n #buildCellText(rowIndex: number, colIndex: number): { text: string } | null {\n const rowData = this.rows[rowIndex] as Record<string, unknown> | undefined;\n if (!rowData) return null;\n\n const column = this.columns[colIndex];\n if (!column) return null;\n\n const value = rowData[column.field];\n const header = column.header || column.field;\n\n let text: string;\n if (this.config.includeHeaders) {\n const formattedValue = value == null ? '' : String(value);\n text = `${header}: ${formattedValue}`;\n } else {\n text = value == null ? '' : String(value);\n }\n\n return { text };\n }\n\n /**\n * Build text for a rectangular range of cells.\n */\n #buildRangeText(range: { startRow: number; startCol: number; endRow: number; endCol: number }): {\n text: string;\n rowCount: number;\n columnCount: number;\n } {\n const { startRow, startCol, endRow, endCol } = range;\n const minRow = Math.min(startRow, endRow);\n const maxRow = Math.max(startRow, endRow);\n const minCol = Math.min(startCol, endCol);\n const maxCol = Math.max(startCol, endCol);\n\n const delimiter = this.config.delimiter ?? '\\t';\n const newline = this.config.newline ?? '\\n';\n const lines: string[] = [];\n\n // Get columns in the range\n const rangeColumns = this.columns.slice(minCol, maxCol + 1);\n\n // Add header row if configured\n if (this.config.includeHeaders) {\n const headerCells = rangeColumns.map((c) => c.header || c.field);\n lines.push(headerCells.join(delimiter));\n }\n\n // Add data rows\n for (let r = minRow; r <= maxRow; r++) {\n const rowData = this.rows[r] as Record<string, unknown> | undefined;\n if (!rowData) continue;\n\n const cells = rangeColumns.map((col) => {\n const value = rowData[col.field];\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n return String(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return {\n text: lines.join(newline),\n rowCount: maxRow - minRow + 1,\n columnCount: maxCol - minCol + 1,\n };\n }\n\n /**\n * Build text for a single focused cell from DOM.\n * Used when selection plugin is not available or no rows are selected.\n */\n #buildSingleCellText(target: HTMLElement): { text: string; field: string; value: unknown } | null {\n // Find the cell element - cells use data-field-cache for the field name\n const cell = target.closest('[data-field-cache]') as HTMLElement | null;\n if (!cell) return null;\n\n const field = cell.dataset.fieldCache;\n if (!field) return null;\n\n // Get row index from data-row attribute on the cell\n const rowIndexStr = cell.dataset.row;\n if (!rowIndexStr) return null;\n\n const rowIndex = parseInt(rowIndexStr, 10);\n if (isNaN(rowIndex)) return null;\n\n const rowData = this.rows[rowIndex] as Record<string, unknown> | undefined;\n if (!rowData) return null;\n\n const value = rowData[field];\n const column = this.columns.find((c) => c.field === field);\n const header = column?.header || field;\n\n // Format the text based on includeHeaders config\n let text: string;\n if (this.config.includeHeaders) {\n // Format: \"{header}: {value}\"\n const formattedValue = value == null ? '' : String(value);\n text = `${header}: ${formattedValue}`;\n } else {\n // Just the value\n text = value == null ? '' : String(value);\n }\n\n return { text, field, value };\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Copy currently selected rows to clipboard.\n * @returns The copied text\n */\n async copy(): Promise<string> {\n const selectionPlugin = this.#getSelectionPlugin();\n const indices = selectionPlugin?.getSelectedRows() ?? [];\n\n const text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: indices,\n config: this.config,\n });\n\n await copyToClipboard(text);\n this.lastCopied = { text, timestamp: Date.now() };\n return text;\n }\n\n /**\n * Copy specific rows by index to clipboard.\n * @param indices - Array of row indices to copy\n * @returns The copied text\n */\n async copyRows(indices: number[]): Promise<string> {\n const text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: indices,\n config: this.config,\n });\n\n await copyToClipboard(text);\n this.lastCopied = { text, timestamp: Date.now() };\n return text;\n }\n\n /**\n * Read and parse clipboard content.\n * @returns Parsed 2D array of cell values, or null if clipboard is empty\n */\n async paste(): Promise<string[][] | null> {\n const text = await readFromClipboard();\n if (!text) return null;\n return parseClipboardText(text, this.config);\n }\n\n /**\n * Get the last copied text and timestamp.\n * @returns The last copied info or null\n */\n getLastCopied(): { text: string; timestamp: number } | null {\n return this.lastCopied;\n }\n // #endregion\n}\n\n// #region Internal Types\n\n/**\n * Interface for SelectionPlugin methods we need.\n * This avoids circular imports while providing type safety.\n */\ninterface SelectionPluginInterface {\n name: string;\n /** Get selected row indices (row mode) */\n getSelectedRows(): number[];\n /** Get all selected cell ranges (range mode) */\n getRanges(): Array<{ from: { row: number; col: number }; to: { row: number; col: number } }>;\n /** Get selected cell (cell mode) */\n getSelectedCell(): { row: number; col: number } | null;\n}\n// #endregion\n\n// Re-export types\nexport type { ClipboardConfig, CopyDetail, PasteDetail } from './types';\n"],"names":["formatCellValue","value","field","row","config","str","delimiter","newline","buildClipboardText","params","rows","columns","selectedIndices","visibleColumns","c","lines","headerCells","header","sortedIndices","a","b","idx","cells","col","copyToClipboard","text","textarea","success","parseClipboardText","normalizedText","currentRow","currentCell","inQuotes","i","char","readFromClipboard","ClipboardPlugin","BaseGridPlugin","event","isCopy","isPaste","#handleCopy","#handlePaste","target","selectionPlugin","#getSelectionPlugin","selectedRows","hasRowSelection","ranges","hasRangeSelection","hasCellSelection","rowCount","columnCount","range","result","#buildRangeText","cell","#buildCellText","#buildSingleCellText","parsed","grid","plugin","rowIndex","colIndex","rowData","column","formattedValue","startRow","startCol","endRow","endCol","minRow","maxRow","minCol","maxCol","rangeColumns","r","rowIndexStr","indices"],"mappings":"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"}
1
+ {"version":3,"file":"clipboard.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/clipboard/copy.ts","../../../../../libs/grid/src/lib/plugins/clipboard/paste.ts","../../../../../libs/grid/src/lib/plugins/clipboard/ClipboardPlugin.ts"],"sourcesContent":["/**\n * Clipboard Copy Logic\n *\n * Pure functions for copying grid data to clipboard.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ClipboardConfig } from './types';\n\n/** Parameters for building clipboard text */\nexport interface CopyParams {\n /** All grid rows */\n rows: unknown[];\n /** Column configurations */\n columns: ColumnConfig[];\n /** Selected row indices */\n selectedIndices: Set<number> | number[];\n /** Clipboard configuration */\n config: ClipboardConfig;\n}\n\n/**\n * Format a cell value for clipboard output.\n *\n * Uses custom processCell if provided, otherwise applies default formatting:\n * - null/undefined → empty string\n * - Date → ISO string\n * - Object → JSON string\n * - Other → String conversion with optional quoting\n *\n * @param value - The cell value to format\n * @param field - The field name\n * @param row - The full row object\n * @param config - Clipboard configuration\n * @returns Formatted string value\n */\nexport function formatCellValue(value: unknown, field: string, row: unknown, config: ClipboardConfig): string {\n if (config.processCell) {\n return config.processCell(value, field, row);\n }\n\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Quote if contains delimiter, newline, or quotes (or if quoteStrings is enabled)\n if (config.quoteStrings || str.includes(delimiter) || str.includes(newline) || str.includes('\"')) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build clipboard text from selected rows and columns.\n *\n * @param params - Copy parameters including rows, columns, selection, and config\n * @returns Tab-separated (or custom delimiter) text ready for clipboard\n */\nexport function buildClipboardText(params: CopyParams): string {\n const { rows, columns, selectedIndices, config } = params;\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Filter to visible columns (not hidden, not internal __ prefixed)\n const visibleColumns = columns.filter((c) => !c.hidden && !c.field.startsWith('__'));\n\n const lines: string[] = [];\n\n // Add header row if configured\n if (config.includeHeaders) {\n const headerCells = visibleColumns.map((c) => {\n const header = c.header || c.field;\n // Quote headers if they contain special characters\n if (header.includes(delimiter) || header.includes(newline) || header.includes('\"')) {\n return `\"${header.replace(/\"/g, '\"\"')}\"`;\n }\n return header;\n });\n lines.push(headerCells.join(delimiter));\n }\n\n // Convert indices to sorted array\n const indices = selectedIndices instanceof Set ? [...selectedIndices] : selectedIndices;\n const sortedIndices = [...indices].sort((a, b) => a - b);\n\n // Build data rows\n for (const idx of sortedIndices) {\n const row = rows[idx];\n if (!row) continue;\n\n const cells = visibleColumns.map((col) =>\n formatCellValue((row as Record<string, unknown>)[col.field], col.field, row, config)\n );\n lines.push(cells.join(delimiter));\n }\n\n return lines.join(newline);\n}\n\n/**\n * Copy text to the system clipboard.\n *\n * Uses the modern Clipboard API when available, with fallback\n * to execCommand for older browsers.\n *\n * @param text - The text to copy\n * @returns Promise resolving to true if successful, false otherwise\n */\nexport async function copyToClipboard(text: string): Promise<boolean> {\n try {\n await navigator.clipboard.writeText(text);\n return true;\n } catch {\n // Fallback for older browsers or when Clipboard API is not available\n const textarea = document.createElement('textarea');\n textarea.value = text;\n textarea.style.position = 'fixed';\n textarea.style.opacity = '0';\n textarea.style.pointerEvents = 'none';\n document.body.appendChild(textarea);\n textarea.select();\n const success = document.execCommand('copy');\n document.body.removeChild(textarea);\n return success;\n }\n}\n","/**\n * Clipboard Paste Logic\n *\n * Pure functions for reading and parsing clipboard data.\n */\n\nimport type { ClipboardConfig } from './types';\n\n/**\n * Parse clipboard text into a 2D array of cell values.\n *\n * Handles:\n * - Quoted values (with escaped double quotes \"\")\n * - Multi-line quoted values (newlines within quotes are preserved)\n * - Custom delimiters and newlines\n * - Empty lines (filtered out only if they contain no data)\n *\n * @param text - Raw clipboard text\n * @param config - Clipboard configuration\n * @returns 2D array where each sub-array is a row of cell values\n */\nexport function parseClipboardText(text: string, config: ClipboardConfig): string[][] {\n const delimiter = config.delimiter ?? '\\t';\n const newline = config.newline ?? '\\n';\n\n // Handle Windows CRLF line endings\n const normalizedText = text.replace(/\\r\\n/g, '\\n').replace(/\\r/g, '\\n');\n\n // Parse the entire text handling quoted fields that may span multiple lines\n const rows: string[][] = [];\n let currentRow: string[] = [];\n let currentCell = '';\n let inQuotes = false;\n\n for (let i = 0; i < normalizedText.length; i++) {\n const char = normalizedText[i];\n\n if (char === '\"' && !inQuotes) {\n // Start of quoted field\n inQuotes = true;\n } else if (char === '\"' && inQuotes) {\n // Check for escaped quote (\"\")\n if (normalizedText[i + 1] === '\"') {\n currentCell += '\"';\n i++; // Skip the second quote\n } else {\n // End of quoted field\n inQuotes = false;\n }\n } else if (char === delimiter && !inQuotes) {\n // Field separator\n currentRow.push(currentCell);\n currentCell = '';\n } else if (char === newline && !inQuotes) {\n // Row separator (only if not inside quotes)\n currentRow.push(currentCell);\n currentCell = '';\n // Only add non-empty rows (at least one non-empty cell or multiple cells)\n if (currentRow.length > 1 || currentRow.some((c) => c.trim() !== '')) {\n rows.push(currentRow);\n }\n currentRow = [];\n } else {\n currentCell += char;\n }\n }\n\n // Handle the last cell and row\n currentRow.push(currentCell);\n if (currentRow.length > 1 || currentRow.some((c) => c.trim() !== '')) {\n rows.push(currentRow);\n }\n\n return rows;\n}\n\n/**\n * Read text from the system clipboard.\n *\n * Uses the modern Clipboard API. Returns empty string if\n * the API is not available or permission is denied.\n *\n * @returns Promise resolving to clipboard text or empty string\n */\nexport async function readFromClipboard(): Promise<string> {\n try {\n return await navigator.clipboard.readText();\n } catch {\n // Permission denied or API not available\n return '';\n }\n}\n","/**\n * Clipboard Plugin (Class-based)\n *\n * Provides copy/paste functionality for tbw-grid.\n * Supports Ctrl+C/Cmd+C for copying and Ctrl+V/Cmd+V for pasting.\n *\n * **With Selection plugin:** Copy selected rows as a range\n * - includeHeaders: true → adds header row at top\n * - includeHeaders: false → data rows only\n *\n * **Without Selection plugin:** Copy focused cell only\n * - includeHeaders: true → \"Header: value\" format\n * - includeHeaders: false → value only\n */\n\nimport { BaseGridPlugin, type PluginDependency } from '../../core/plugin/base-plugin';\nimport { buildClipboardText, copyToClipboard } from './copy';\nimport { parseClipboardText, readFromClipboard } from './paste';\nimport type { ClipboardConfig, CopyDetail, PasteDetail } from './types';\n\n/**\n * Clipboard Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ClipboardPlugin({ includeHeaders: true })\n * ```\n */\nexport class ClipboardPlugin extends BaseGridPlugin<ClipboardConfig> {\n /**\n * Plugin dependencies - ClipboardPlugin requires SelectionPlugin to know what to copy.\n *\n * The SelectionPlugin must be loaded BEFORE this plugin in the plugins array.\n */\n static override readonly dependencies: PluginDependency[] = [\n { name: 'selection', required: true, reason: 'ClipboardPlugin needs selection to determine what cells to copy' },\n ];\n\n readonly name = 'clipboard';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ClipboardConfig> {\n return {\n includeHeaders: false,\n delimiter: '\\t',\n newline: '\\n',\n quoteStrings: false,\n };\n }\n\n // #region Internal State\n /** The last copied text (for reference/debugging) */\n private lastCopied: { text: string; timestamp: number } | null = null;\n // #endregion\n\n // #region Lifecycle\n\n override detach(): void {\n this.lastCopied = null;\n }\n // #endregion\n\n // #region Event Handlers\n\n override onKeyDown(event: KeyboardEvent): boolean {\n const isCopy = (event.ctrlKey || event.metaKey) && event.key === 'c';\n const isPaste = (event.ctrlKey || event.metaKey) && event.key === 'v';\n\n if (isCopy) {\n this.#handleCopy(event.target as HTMLElement);\n return true; // Prevent default browser behavior\n }\n\n if (isPaste) {\n this.#handlePaste();\n return true; // Prevent default browser behavior\n }\n\n return false;\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Handle copy operation\n */\n #handleCopy(target: HTMLElement): void {\n // Try to get selection from selection plugin (if available)\n // Use dynamic import to avoid circular dependency issues\n const selectionPlugin = this.#getSelectionPlugin();\n\n // Check for different selection types\n const selectedRows = selectionPlugin?.getSelectedRows() ?? [];\n const hasRowSelection = selectedRows.length > 0;\n const ranges = selectionPlugin?.getRanges() ?? [];\n const hasRangeSelection = ranges.length > 0;\n const hasCellSelection = selectionPlugin?.getSelectedCell() != null;\n\n let text: string;\n let rowCount: number;\n let columnCount: number;\n\n if (hasRowSelection && selectionPlugin) {\n // Row selection mode - copy full rows\n text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: selectedRows,\n config: this.config,\n });\n rowCount = selectedRows.length;\n columnCount = this.columns.filter((c) => !c.hidden && !c.field.startsWith('__')).length;\n } else if (hasRangeSelection && selectionPlugin) {\n // Range selection mode - copy rectangular range (use last range as active)\n const range = ranges[ranges.length - 1];\n const result = this.#buildRangeText({\n startRow: range.from.row,\n startCol: range.from.col,\n endRow: range.to.row,\n endCol: range.to.col,\n });\n text = result.text;\n rowCount = result.rowCount;\n columnCount = result.columnCount;\n } else if (hasCellSelection && selectionPlugin) {\n // Cell selection mode - copy single cell\n const cell = selectionPlugin.getSelectedCell()!;\n const result = this.#buildCellText(cell.row, cell.col);\n if (!result) return;\n text = result.text;\n rowCount = 1;\n columnCount = 1;\n } else {\n // Fallback: try to find focused cell from DOM\n const result = this.#buildSingleCellText(target);\n if (!result) return;\n text = result.text;\n rowCount = 1;\n columnCount = 1;\n }\n\n copyToClipboard(text).then(() => {\n this.lastCopied = { text, timestamp: Date.now() };\n this.emit<CopyDetail>('copy', { text, rowCount, columnCount });\n });\n }\n\n /**\n * Handle paste operation\n */\n #handlePaste(): void {\n readFromClipboard().then((text) => {\n if (!text) return;\n const parsed = parseClipboardText(text, this.config);\n this.emit<PasteDetail>('paste', { rows: parsed, text });\n });\n }\n\n /**\n * Get the selection plugin instance if available.\n */\n #getSelectionPlugin(): SelectionPluginInterface | undefined {\n // Use getPluginByName for duck-typing approach to avoid import order issues\n try {\n const plugin = this.grid?.getPluginByName('selection');\n if (plugin) {\n return plugin as unknown as SelectionPluginInterface;\n }\n } catch {\n // Selection plugin not available\n }\n return undefined;\n }\n\n /**\n * Build text for a single cell by row/col index.\n */\n #buildCellText(rowIndex: number, colIndex: number): { text: string } | null {\n const rowData = this.rows[rowIndex] as Record<string, unknown> | undefined;\n if (!rowData) return null;\n\n const column = this.columns[colIndex];\n if (!column) return null;\n\n const value = rowData[column.field];\n const header = column.header || column.field;\n\n let text: string;\n if (this.config.includeHeaders) {\n const formattedValue = value == null ? '' : String(value);\n text = `${header}: ${formattedValue}`;\n } else {\n text = value == null ? '' : String(value);\n }\n\n return { text };\n }\n\n /**\n * Build text for a rectangular range of cells.\n */\n #buildRangeText(range: { startRow: number; startCol: number; endRow: number; endCol: number }): {\n text: string;\n rowCount: number;\n columnCount: number;\n } {\n const { startRow, startCol, endRow, endCol } = range;\n const minRow = Math.min(startRow, endRow);\n const maxRow = Math.max(startRow, endRow);\n const minCol = Math.min(startCol, endCol);\n const maxCol = Math.max(startCol, endCol);\n\n const delimiter = this.config.delimiter ?? '\\t';\n const newline = this.config.newline ?? '\\n';\n const lines: string[] = [];\n\n // Get columns in the range\n const rangeColumns = this.columns.slice(minCol, maxCol + 1);\n\n // Add header row if configured\n if (this.config.includeHeaders) {\n const headerCells = rangeColumns.map((c) => c.header || c.field);\n lines.push(headerCells.join(delimiter));\n }\n\n // Add data rows\n for (let r = minRow; r <= maxRow; r++) {\n const rowData = this.rows[r] as Record<string, unknown> | undefined;\n if (!rowData) continue;\n\n const cells = rangeColumns.map((col) => {\n const value = rowData[col.field];\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n return String(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return {\n text: lines.join(newline),\n rowCount: maxRow - minRow + 1,\n columnCount: maxCol - minCol + 1,\n };\n }\n\n /**\n * Build text for a single focused cell from DOM.\n * Used when selection plugin is not available or no rows are selected.\n */\n #buildSingleCellText(target: HTMLElement): { text: string; field: string; value: unknown } | null {\n // Find the cell element - cells use data-field-cache for the field name\n const cell = target.closest('[data-field-cache]') as HTMLElement | null;\n if (!cell) return null;\n\n const field = cell.dataset.fieldCache;\n if (!field) return null;\n\n // Get row index from data-row attribute on the cell\n const rowIndexStr = cell.dataset.row;\n if (!rowIndexStr) return null;\n\n const rowIndex = parseInt(rowIndexStr, 10);\n if (isNaN(rowIndex)) return null;\n\n const rowData = this.rows[rowIndex] as Record<string, unknown> | undefined;\n if (!rowData) return null;\n\n const value = rowData[field];\n const column = this.columns.find((c) => c.field === field);\n const header = column?.header || field;\n\n // Format the text based on includeHeaders config\n let text: string;\n if (this.config.includeHeaders) {\n // Format: \"{header}: {value}\"\n const formattedValue = value == null ? '' : String(value);\n text = `${header}: ${formattedValue}`;\n } else {\n // Just the value\n text = value == null ? '' : String(value);\n }\n\n return { text, field, value };\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Copy currently selected rows to clipboard.\n * @returns The copied text\n */\n async copy(): Promise<string> {\n const selectionPlugin = this.#getSelectionPlugin();\n const indices = selectionPlugin?.getSelectedRows() ?? [];\n\n const text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: indices,\n config: this.config,\n });\n\n await copyToClipboard(text);\n this.lastCopied = { text, timestamp: Date.now() };\n return text;\n }\n\n /**\n * Copy specific rows by index to clipboard.\n * @param indices - Array of row indices to copy\n * @returns The copied text\n */\n async copyRows(indices: number[]): Promise<string> {\n const text = buildClipboardText({\n rows: this.rows as unknown[],\n columns: [...this.columns],\n selectedIndices: indices,\n config: this.config,\n });\n\n await copyToClipboard(text);\n this.lastCopied = { text, timestamp: Date.now() };\n return text;\n }\n\n /**\n * Read and parse clipboard content.\n * @returns Parsed 2D array of cell values, or null if clipboard is empty\n */\n async paste(): Promise<string[][] | null> {\n const text = await readFromClipboard();\n if (!text) return null;\n return parseClipboardText(text, this.config);\n }\n\n /**\n * Get the last copied text and timestamp.\n * @returns The last copied info or null\n */\n getLastCopied(): { text: string; timestamp: number } | null {\n return this.lastCopied;\n }\n // #endregion\n}\n\n// #region Internal Types\n\n/**\n * Interface for SelectionPlugin methods we need.\n * This avoids circular imports while providing type safety.\n */\ninterface SelectionPluginInterface {\n name: string;\n /** Get selected row indices (row mode) */\n getSelectedRows(): number[];\n /** Get all selected cell ranges (range mode) */\n getRanges(): Array<{ from: { row: number; col: number }; to: { row: number; col: number } }>;\n /** Get selected cell (cell mode) */\n getSelectedCell(): { row: number; col: number } | null;\n}\n// #endregion\n\n// Re-export types\nexport type { ClipboardConfig, CopyDetail, PasteDetail } from './types';\n"],"names":["formatCellValue","value","field","row","config","str","delimiter","newline","buildClipboardText","params","rows","columns","selectedIndices","visibleColumns","c","lines","headerCells","header","sortedIndices","a","b","idx","cells","col","copyToClipboard","text","textarea","success","parseClipboardText","normalizedText","currentRow","currentCell","inQuotes","i","char","readFromClipboard","ClipboardPlugin","BaseGridPlugin","event","isCopy","isPaste","#handleCopy","#handlePaste","target","selectionPlugin","#getSelectionPlugin","selectedRows","hasRowSelection","ranges","hasRangeSelection","hasCellSelection","rowCount","columnCount","range","result","#buildRangeText","cell","#buildCellText","#buildSingleCellText","parsed","plugin","rowIndex","colIndex","rowData","column","formattedValue","startRow","startCol","endRow","endCol","minRow","maxRow","minCol","maxCol","rangeColumns","r","rowIndexStr","indices"],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ (function(E,y){typeof exports=="object"&&typeof module<"u"?y(exports,require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/plugin/base-plugin"],y):(E=typeof globalThis<"u"?globalThis:E||self,y(E.TbwGridPlugin_editing={},E.TbwGrid))})(this,(function(E,y){"use strict";function _(f){switch(f.type){case"number":return e=>{const t=document.createElement("input");return t.type="number",t.value=e.value!=null?String(e.value):"",t.addEventListener("blur",()=>e.commit(t.value===""?null:Number(t.value))),t.addEventListener("keydown",i=>{i.key==="Enter"&&e.commit(t.value===""?null:Number(t.value)),i.key==="Escape"&&e.cancel()}),t};case"boolean":return e=>{const t=document.createElement("input");return t.type="checkbox",t.checked=!!e.value,t.addEventListener("change",()=>e.commit(t.checked)),t};case"date":return e=>{const t=document.createElement("input");return t.type="date",e.value instanceof Date&&(t.valueAsDate=e.value),t.addEventListener("change",()=>e.commit(t.valueAsDate)),t.addEventListener("keydown",i=>{i.key==="Escape"&&e.cancel()}),t};case"select":case"typeahead":return e=>{const t=document.createElement("select"),i=e.column;i.multi&&(t.multiple=!0);const s=i.options;(typeof s=="function"?s():s||[]).forEach(r=>{const c=document.createElement("option");c.value=String(r.value),c.textContent=r.label,(i.multi&&Array.isArray(e.value)&&e.value.includes(r.value)||!i.multi&&e.value===r.value)&&(c.selected=!0),t.appendChild(c)});const l=()=>{if(i.multi){const r=[];Array.from(t.selectedOptions).forEach(c=>{r.push(c.value)}),e.commit(r)}else e.commit(t.value)};return t.addEventListener("change",l),t.addEventListener("blur",l),t.addEventListener("keydown",r=>{r.key==="Escape"&&e.cancel()}),t};default:return e=>{const t=document.createElement("input");return t.type="text",t.value=e.value!=null?String(e.value):"",t.addEventListener("blur",()=>e.commit(t.value)),t.addEventListener("keydown",i=>{i.key==="Enter"&&e.commit(t.value),i.key==="Escape"&&e.cancel()}),t}}}const C='input,select,textarea,[contenteditable="true"],[contenteditable=""],[tabindex]:not([tabindex="-1"])';function w(f){return!(typeof f!="string"||f==="__proto__"||f==="constructor"||f==="prototype")}function S(f){return(f.__editingCellCount??0)>0}function L(f){const e=(f.__editingCellCount??0)+1;f.__editingCellCount=e,f.setAttribute("data-has-editing","")}function k(f){f.__editingCellCount=0,f.removeAttribute("data-has-editing")}function T(f,e,t){const i=f.querySelector("input,textarea,select");if(!i)return;const s=()=>i instanceof HTMLInputElement?i.type==="checkbox"?i.checked:i.type==="number"?i.value===""?null:Number(i.value):i.type==="date"?i.valueAsDate:i.value:e.type==="number"&&i.value!==""?Number(i.value):i.value;i.addEventListener("blur",()=>{t(s())}),i instanceof HTMLInputElement&&i.type==="checkbox"?i.addEventListener("change",()=>t(i.checked)):i instanceof HTMLSelectElement&&i.addEventListener("change",()=>t(s()))}class A extends y.BaseGridPlugin{name="editing";version="1.0.0";get defaultConfig(){return{editOn:"click"}}#e=-1;#r=-1;#s=new Map;#t=new Set;#n=new Set;#o=!1;attach(e){super.attach(e);const t=this.disconnectSignal,i=e;i._activeEditRows=-1,i._rowEditSnapshots=new Map,i._changedRowIndices=new Set,Object.defineProperty(e,"changedRows",{get:()=>this.changedRows,configurable:!0}),Object.defineProperty(e,"changedRowIndices",{get:()=>this.changedRowIndices,configurable:!0}),e.resetChangedRows=s=>this.resetChangedRows(s),e.beginBulkEdit=(s,n)=>{n&&this.beginCellEdit(s,n)},document.addEventListener("keydown",s=>{s.key==="Escape"&&this.#e!==-1&&this.#i(this.#e,!0)},{capture:!0,signal:t}),document.addEventListener("mousedown",s=>{if(this.#e===-1)return;const n=i.findRenderedRowElement?.(this.#e);!n||(s.composedPath&&s.composedPath()||[]).includes(n)||this.#i(this.#e,!1)},{signal:t})}detach(){this.#e=-1,this.#r=-1,this.#s.clear(),this.#t.clear(),this.#n.clear(),super.detach()}processColumns(e){return e}onCellClick(e){const t=this.grid,i=this.config.editOn??t.effectiveConfig?.editOn;if(i===!1||i==="manual"||i!=="click"&&i!=="dblclick")return!1;const s=e.originalEvent.type==="dblclick";if(i==="click"&&s||i==="dblclick"&&!s)return!1;const{rowIndex:n}=e;return t._columns?.some(r=>r.editable)?(e.originalEvent.stopPropagation(),this.beginBulkEdit(n),!0):!1}onKeyDown(e){const t=this.grid;if(e.key==="Escape"&&this.#e!==-1)return this.#i(this.#e,!0),!0;if(e.key===" "||e.key==="Spacebar"){const i=t._focusRow,s=t._focusCol;if(i>=0&&s>=0){const n=t._visibleColumns[s],l=t._rows[i];if(n?.editable&&n.type==="boolean"&&l){const r=n.field;if(w(r)){const u=!l[r];return this.#c(i,n,u,l),e.preventDefault(),this.requestRender(),!0}}}return!1}if(e.key==="Enter"&&!e.shiftKey){if(this.#e!==-1)return!1;const i=this.config.editOn??t.effectiveConfig?.editOn;if(i===!1||i==="manual")return!1;const s=t._focusRow;return s>=0&&t._columns?.some(l=>l.editable)?(this.beginBulkEdit(s),!0):!1}return!1}afterRender(){const e=this.grid;if(this.#o&&(this.#o=!1,this.#u(e)),this.#n.size!==0)for(const t of this.#n){const[i,s]=t.split(":"),n=parseInt(i,10),l=parseInt(s,10),r=e.findRenderedRowElement?.(n);if(!r)continue;const c=r.querySelector(`.cell[data-col="${l}"]`);if(!c||c.classList.contains("editing"))continue;const u=e._rows[n],a=e._visibleColumns[l];u&&a&&this.#a(u,n,a,l,c,!0)}}onScrollRender(){this.afterRender()}get changedRows(){const e=this.grid;return Array.from(this.#t).map(t=>e._rows[t])}get changedRowIndices(){return Array.from(this.#t)}get activeEditRow(){return this.#e}get activeEditCol(){return this.#r}isRowEditing(e){return this.#e===e}isCellEditing(e,t){return this.#n.has(`${e}:${t}`)}isRowChanged(e){return this.#t.has(e)}resetChangedRows(e){const t=this.changedRows,i=this.changedRowIndices;this.#t.clear(),this.#l(),e||this.emit("changed-rows-reset",{rows:t,indices:i}),this.grid._rowPool?.forEach(n=>n.classList.remove("changed"))}beginCellEdit(e,t){const i=this.grid,s=i._visibleColumns.findIndex(c=>c.field===t);if(s===-1||!i._visibleColumns[s]?.editable)return;const r=i.findRenderedRowElement?.(e)?.querySelector(`.cell[data-col="${s}"]`);r&&this.#f(e,s,r)}beginBulkEdit(e){const t=this.grid;if((this.config.editOn??t.effectiveConfig?.editOn)===!1||!t._columns?.some(r=>r.editable))return;const n=t.findRenderedRowElement?.(e);if(!n)return;const l=t._rows[e];this.#d(e,l),Array.from(n.children).forEach((r,c)=>{const u=t._visibleColumns[c];if(u?.editable){const a=r;a.classList.contains("editing")||this.#a(l,e,u,c,a,!0)}}),setTimeout(()=>{let r=n.querySelector(`.cell[data-col="${t._focusCol}"]`);if(r?.classList.contains("editing")||(r=n.querySelector(".cell.editing")),r?.classList.contains("editing")){const c=r.querySelector(C);try{c?.focus({preventScroll:!0})}catch{}}},0)}commitActiveRowEdit(){this.#e!==-1&&this.#i(this.#e,!1)}cancelActiveRowEdit(){this.#e!==-1&&this.#i(this.#e,!0)}#f(e,t,i){const s=this.grid,n=s._rows[e],l=s._visibleColumns[t];!n||!l?.editable||i.classList.contains("editing")||(this.#e!==e&&this.#d(e,n),this.#r=t,this.#a(n,e,l,t,i,!1))}#l(){const e=this.grid;e._activeEditRows=this.#e,e._rowEditSnapshots=this.#s,e._changedRowIndices=this.#t}#d(e,t){this.#e!==e&&(this.#s.set(e,{...t}),this.#e=e,this.#l())}#i(e,t){if(this.#e!==e)return;const i=this.grid,s=this.#s.get(e),n=i._rows[e],l=i.findRenderedRowElement?.(e);if(!t&&l&&n&&l.querySelectorAll(".cell.editing").forEach(c=>{const u=Number(c.getAttribute("data-col"));if(isNaN(u))return;const a=i._visibleColumns[u];if(!a)return;const p=c.querySelector("input,textarea,select");if(p){let o;p instanceof HTMLInputElement&&p.type==="checkbox"?o=p.checked:(o=p.value,a.type==="number"&&o!==""&&(o=Number(o))),n[a.field]!==o&&this.#c(e,a,o,n)}}),t&&s&&n)Object.keys(s).forEach(r=>{n[r]=s[r]}),this.#t.delete(e);else if(!t){const r=this.#t.has(e);this.emit("row-commit",{rowIndex:e,row:n,changed:r,changedRows:this.changedRows,changedRowIndices:this.changedRowIndices})}this.#s.delete(e),this.#e=-1,this.#r=-1,this.#l();for(const r of this.#n)r.startsWith(`${e}:`)&&this.#n.delete(r);l&&(l.querySelectorAll(".cell.editing").forEach(r=>{r.classList.remove("editing"),k(r.parentElement)}),this.requestRender()),this.#o=!0,l||(this.#u(i),this.#o=!1)}#c(e,t,i,s){const n=t.field;if(!w(n)||s[n]===i)return;s[n]=i;const r=!this.#t.has(e);this.#t.add(e),this.#l();const u=this.grid.findRenderedRowElement?.(e);u&&u.classList.add("changed"),this.emit("cell-commit",{row:s,field:n,value:i,rowIndex:e,changedRows:this.changedRows,changedRowIndices:this.changedRowIndices,firstTimeForRow:r})}#a(e,t,i,s,n,l){if(!i.editable||n.classList.contains("editing"))return;const r=w(i.field)?e[i.field]:void 0;n.classList.add("editing"),this.#n.add(`${t}:${s}`);const c=n.parentElement;c&&L(c);let u=!1;const a=h=>{u||this.#e===-1||this.#c(t,i,h,e)},p=()=>{u=!0,w(i.field)&&(e[i.field]=r)},o=document.createElement("div");o.className="tbw-editor-host",n.innerHTML="",n.appendChild(o),o.addEventListener("keydown",h=>{h.key==="Enter"&&(h.stopPropagation(),h.preventDefault(),u=!0,this.#i(t,!1)),h.key==="Escape"&&(h.stopPropagation(),h.preventDefault(),p(),this.#i(t,!0))});const g=i,b=g.__editorTemplate,d=g.editor||(b?"template":_(i)),v=r;if(d==="template"&&b)this.#h(o,g,e,r,a,p,l,t);else if(typeof d=="string"){const h=document.createElement(d);h.value=v,h.addEventListener("change",()=>a(h.value)),o.appendChild(h),l||queueMicrotask(()=>{o.querySelector(C)?.focus({preventScroll:!0})})}else if(typeof d=="function"){const h={row:e,value:v,field:i.field,column:i,commit:a,cancel:p},m=d(h);typeof m=="string"?(o.innerHTML=m,T(o,i,a)):m instanceof Node&&o.appendChild(m),l||queueMicrotask(()=>{o.querySelector(C)?.focus({preventScroll:!0})})}else if(d&&typeof d=="object"){const h=this.grid,m=document.createElement("div");m.setAttribute("data-external-editor",""),m.setAttribute("data-field",i.field),o.appendChild(m);const R={row:e,value:v,field:i.field,column:i,commit:a,cancel:p};if(d.mount)try{d.mount({placeholder:m,context:R,spec:d})}catch(O){console.warn(`[tbw-grid] External editor mount error for column '${i.field}':`,O)}else h.dispatchEvent(new CustomEvent("mount-external-editor",{detail:{placeholder:m,spec:d,context:R}}))}}#h(e,t,i,s,n,l,r,c){const u=t.__editorTemplate;if(!u)return;const a=u.cloneNode(!0),p=t.__compiledEditor;p?a.innerHTML=p({row:i,value:s,field:t.field,column:t,commit:n,cancel:l}):a.querySelectorAll("*").forEach(g=>{g.childNodes.length===1&&g.firstChild?.nodeType===Node.TEXT_NODE&&(g.textContent=g.textContent?.replace(/{{\s*value\s*}}/g,s==null?"":String(s)).replace(/{{\s*row\.([a-zA-Z0-9_]+)\s*}}/g,(b,d)=>{if(!w(d))return"";const v=i[d];return v==null?"":String(v)})||"")});const o=a.querySelector("input,textarea,select");if(o){o instanceof HTMLInputElement&&o.type==="checkbox"?o.checked=!!s:o.value=String(s??"");let g=!1;o.addEventListener("blur",()=>{if(g)return;const b=o instanceof HTMLInputElement&&o.type==="checkbox"?o.checked:o.value;n(b)}),o.addEventListener("keydown",b=>{const d=b;if(d.key==="Enter"){d.stopPropagation(),d.preventDefault(),g=!0;const v=o instanceof HTMLInputElement&&o.type==="checkbox"?o.checked:o.value;n(v),this.#i(c,!1)}d.key==="Escape"&&(d.stopPropagation(),d.preventDefault(),l(),this.#i(c,!0))}),o instanceof HTMLInputElement&&o.type==="checkbox"&&o.addEventListener("change",()=>n(o.checked)),r||setTimeout(()=>o.focus({preventScroll:!0}),0)}e.appendChild(a)}#u(e){queueMicrotask(()=>{try{const t=e._focusRow,i=e._focusCol,s=e.findRenderedRowElement?.(t);if(s){Array.from(e._bodyEl.querySelectorAll(".cell-focus")).forEach(l=>l.classList.remove("cell-focus"));const n=s.querySelector(`.cell[data-row="${t}"][data-col="${i}"]`);n&&(n.classList.add("cell-focus"),n.setAttribute("aria-selected","true"),n.hasAttribute("tabindex")||n.setAttribute("tabindex","-1"),n.focus({preventScroll:!0}))}}catch{}})}}E.EditingPlugin=A,E.FOCUSABLE_EDITOR_SELECTOR=C,E.clearEditingState=k,E.defaultEditorFor=_,E.hasEditingCells=S,Object.defineProperty(E,Symbol.toStringTag,{value:"Module"})}));
2
+ //# sourceMappingURL=editing.umd.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"editing.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/editing/editors.ts","../../../../../libs/grid/src/lib/plugins/editing/EditingPlugin.ts"],"sourcesContent":["/**\n * Default Editors Module\n *\n * Provides built-in editor factories for different column types.\n *\n * IMPORTANT: Editor factories should NOT call focus() on elements - they are called\n * before the element is appended to the DOM. The calling code (beginBulkEdit,\n * inlineEnterEdit) is responsible for focusing the correct editor after insertion.\n */\n\nimport type { ColumnConfig, EditorContext } from '../../core/types';\n\n/**\n * Returns a default editor factory function for the given column type.\n * Each editor handles commit on blur/Enter, and cancel on Escape.\n * Note: Focus is NOT called here - the calling code handles focusing after DOM insertion.\n */\nexport function defaultEditorFor(column: ColumnConfig<any>): (ctx: EditorContext) => HTMLElement | string {\n switch (column.type) {\n case 'number':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'number';\n input.value = ctx.value != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value === '' ? null : Number(input.value)));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value === '' ? null : Number(input.value));\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n case 'boolean':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'checkbox';\n input.checked = !!ctx.value;\n input.addEventListener('change', () => ctx.commit(input.checked));\n return input;\n };\n case 'date':\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'date';\n if (ctx.value instanceof Date) input.valueAsDate = ctx.value;\n input.addEventListener('change', () => ctx.commit(input.valueAsDate));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n case 'select':\n case 'typeahead':\n return (ctx: EditorContext) => {\n const select = document.createElement('select');\n const col = ctx.column;\n if (col.multi) select.multiple = true;\n const rawOptions = col.options;\n const options = typeof rawOptions === 'function' ? rawOptions() : rawOptions || [];\n options.forEach((opt) => {\n const o = document.createElement('option');\n o.value = String(opt.value);\n o.textContent = opt.label;\n if (col.multi && Array.isArray(ctx.value) && ctx.value.includes(opt.value)) o.selected = true;\n else if (!col.multi && ctx.value === opt.value) o.selected = true;\n select.appendChild(o);\n });\n const commitValue = () => {\n if (col.multi) {\n const values: unknown[] = [];\n Array.from(select.selectedOptions).forEach((o) => {\n values.push(o.value);\n });\n ctx.commit(values);\n } else {\n ctx.commit(select.value);\n }\n };\n select.addEventListener('change', commitValue);\n select.addEventListener('blur', commitValue);\n select.addEventListener('keydown', (e) => {\n if (e.key === 'Escape') ctx.cancel();\n });\n return select;\n };\n default:\n return (ctx: EditorContext) => {\n const input = document.createElement('input');\n input.type = 'text';\n input.value = ctx.value != null ? String(ctx.value) : '';\n input.addEventListener('blur', () => ctx.commit(input.value));\n input.addEventListener('keydown', (e) => {\n if (e.key === 'Enter') ctx.commit(input.value);\n if (e.key === 'Escape') ctx.cancel();\n });\n return input;\n };\n }\n}\n","/**\n * Editing Plugin\n *\n * Provides complete editing functionality for tbw-grid.\n * This plugin is FULLY SELF-CONTAINED - the grid has ZERO editing knowledge.\n *\n * The plugin:\n * - Owns all editing state (active cell, snapshots, changed rows)\n * - Uses event distribution (onCellClick, onKeyDown) to handle edit lifecycle\n * - Uses afterRender() hook to inject editors into cells\n * - Uses processColumns() to augment columns with editing metadata\n * - Emits its own events (cell-commit, row-commit, changed-rows-reset)\n *\n * Without this plugin, the grid cannot edit. With this plugin, editing\n * is fully functional without any core changes.\n */\n\nimport { BaseGridPlugin, type CellClickEvent, type GridElement } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, ColumnInternal, InternalGrid, RowElementInternal } from '../../core/types';\nimport { defaultEditorFor } from './editors';\nimport type { CellCommitDetail, ChangedRowsResetDetail, EditingConfig, EditorContext, RowCommitDetail } from './types';\n\n// ============================================================================\n// Constants\n// ============================================================================\n\n/**\n * CSS selector for focusable editor elements within a cell.\n */\nexport const FOCUSABLE_EDITOR_SELECTOR =\n 'input,select,textarea,[contenteditable=\"true\"],[contenteditable=\"\"],[tabindex]:not([tabindex=\"-1\"])';\n\n// ============================================================================\n// Helper Functions\n// ============================================================================\n\n/**\n * Returns true if the given property key is safe to use on a plain object.\n */\nfunction isSafePropertyKey(key: unknown): key is string {\n if (typeof key !== 'string') return false;\n if (key === '__proto__' || key === 'constructor' || key === 'prototype') return false;\n return true;\n}\n\n/**\n * Check if a row element has any cells in editing mode.\n */\nexport function hasEditingCells(rowEl: RowElementInternal): boolean {\n return (rowEl.__editingCellCount ?? 0) > 0;\n}\n\n/**\n * Increment the editing cell count on a row element.\n */\nfunction incrementEditingCount(rowEl: RowElementInternal): void {\n const count = (rowEl.__editingCellCount ?? 0) + 1;\n rowEl.__editingCellCount = count;\n rowEl.setAttribute('data-has-editing', '');\n}\n\n/**\n * Clear all editing state from a row element.\n */\nexport function clearEditingState(rowEl: RowElementInternal): void {\n rowEl.__editingCellCount = 0;\n rowEl.removeAttribute('data-has-editing');\n}\n\n/**\n * Auto-wire commit/cancel lifecycle for input elements in string-returned editors.\n */\nfunction wireEditorInputs(\n editorHost: HTMLElement,\n column: ColumnConfig<unknown>,\n commit: (value: unknown) => void,\n): void {\n const input = editorHost.querySelector('input,textarea,select') as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement\n | null;\n if (!input) return;\n\n const getInputValue = (): unknown => {\n if (input instanceof HTMLInputElement) {\n if (input.type === 'checkbox') return input.checked;\n if (input.type === 'number') return input.value === '' ? null : Number(input.value);\n if (input.type === 'date') return input.valueAsDate;\n return input.value;\n }\n if (column.type === 'number' && input.value !== '') {\n return Number(input.value);\n }\n return input.value;\n };\n\n input.addEventListener('blur', () => {\n commit(getInputValue());\n });\n\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n input.addEventListener('change', () => commit(input.checked));\n } else if (input instanceof HTMLSelectElement) {\n input.addEventListener('change', () => commit(getInputValue()));\n }\n}\n\n// ============================================================================\n// EditingPlugin\n// ============================================================================\n\n/**\n * Editing Plugin for tbw-grid\n *\n * Provides complete cell/row editing functionality. Without this plugin,\n * the grid has no editing capability.\n *\n * @example\n * ```ts\n * import { EditingPlugin } from '@toolbox-web/grid/plugins/editing';\n *\n * const grid = document.createElement('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'name', editable: true },\n * { field: 'age', editable: true, type: 'number' }\n * ],\n * plugins: [new EditingPlugin({ editOn: 'dblclick' })]\n * };\n * ```\n */\nexport class EditingPlugin<T = unknown> extends BaseGridPlugin<EditingConfig> {\n readonly name = 'editing';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<EditingConfig> {\n return {\n editOn: 'click',\n };\n }\n\n // #region Editing State (fully owned by plugin)\n\n /** Currently active edit row index, or -1 if not editing */\n #activeEditRow = -1;\n\n /** Currently active edit column index, or -1 if not editing */\n #activeEditCol = -1;\n\n /** Snapshots of row data before editing started */\n #rowEditSnapshots = new Map<number, T>();\n\n /** Set of row indices that have been modified */\n #changedRowIndices = new Set<number>();\n\n /** Set of cells currently in edit mode: \"rowIndex:colIndex\" */\n #editingCells = new Set<string>();\n\n /** Flag to restore focus after next render (used when exiting edit mode) */\n #pendingFocusRestore = false;\n\n // #endregion\n\n // #region Lifecycle\n\n override attach(grid: GridElement): void {\n super.attach(grid);\n\n const signal = this.disconnectSignal;\n const internalGrid = grid as unknown as InternalGrid<T>;\n\n // Inject editing state and methods onto grid for backward compatibility\n internalGrid._activeEditRows = -1;\n internalGrid._rowEditSnapshots = new Map();\n internalGrid._changedRowIndices = new Set();\n\n // Inject changedRows getter\n Object.defineProperty(grid, 'changedRows', {\n get: () => this.changedRows,\n configurable: true,\n });\n\n // Inject changedRowIndices getter\n Object.defineProperty(grid, 'changedRowIndices', {\n get: () => this.changedRowIndices,\n configurable: true,\n });\n\n // Inject resetChangedRows method\n (grid as any).resetChangedRows = (silent?: boolean) => this.resetChangedRows(silent);\n\n // Inject beginBulkEdit method (for backward compatibility)\n (grid as any).beginBulkEdit = (rowIndex: number, field?: string) => {\n if (field) {\n this.beginCellEdit(rowIndex, field);\n }\n // If no field specified, we can't start editing without a specific cell\n };\n\n // Document-level Escape to cancel editing\n document.addEventListener(\n 'keydown',\n (e: KeyboardEvent) => {\n if (e.key === 'Escape' && this.#activeEditRow !== -1) {\n this.#exitRowEdit(this.#activeEditRow, true);\n }\n },\n { capture: true, signal },\n );\n\n // Click outside to commit editing\n document.addEventListener(\n 'mousedown',\n (e: MouseEvent) => {\n if (this.#activeEditRow === -1) return;\n const rowEl = internalGrid.findRenderedRowElement?.(this.#activeEditRow);\n if (!rowEl) return;\n const path = (e.composedPath && e.composedPath()) || [];\n if (path.includes(rowEl)) return;\n this.#exitRowEdit(this.#activeEditRow, false);\n },\n { signal },\n );\n }\n\n override detach(): void {\n this.#activeEditRow = -1;\n this.#activeEditCol = -1;\n this.#rowEditSnapshots.clear();\n this.#changedRowIndices.clear();\n this.#editingCells.clear();\n super.detach();\n }\n\n // #endregion\n\n // #region Config Augmentation (processColumns hook)\n\n /**\n * Augment columns with editing metadata.\n * This enables the grid to recognize editable columns without core knowledge.\n */\n override processColumns?(columns: readonly ColumnConfig<T>[]): ColumnConfig<T>[] {\n // For now, just pass through - the column config already has `editable` and `editor`\n // This hook could be used to inject default editors based on column type\n return columns as ColumnConfig<T>[];\n }\n\n // #endregion\n\n // #region Event Handlers (event distribution)\n\n /**\n * Handle cell clicks - start editing if configured for click mode.\n * Both click and dblclick events come through this handler.\n * Starts row-based editing (all editable cells in the row get editors).\n */\n override onCellClick(event: CellClickEvent): boolean | void {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n const editOn = this.config.editOn ?? internalGrid.effectiveConfig?.editOn;\n\n // Check if editing is disabled\n if (editOn === false || editOn === 'manual') return false;\n\n // Check if this is click or dblclick mode\n if (editOn !== 'click' && editOn !== 'dblclick') return false;\n\n // Check if the event type matches the edit mode\n const isDoubleClick = event.originalEvent.type === 'dblclick';\n if (editOn === 'click' && isDoubleClick) return false; // In click mode, only handle single clicks\n if (editOn === 'dblclick' && !isDoubleClick) return false; // In dblclick mode, only handle double clicks\n\n const { rowIndex } = event;\n\n // Check if any column in the row is editable\n const hasEditableColumn = internalGrid._columns?.some((col) => col.editable);\n if (!hasEditableColumn) return false;\n\n // Start row-based editing (all editable cells get editors)\n event.originalEvent.stopPropagation();\n this.beginBulkEdit(rowIndex);\n return true; // Handled\n }\n\n /**\n * Handle keyboard events for edit lifecycle.\n */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n\n // Escape: cancel current edit\n if (event.key === 'Escape' && this.#activeEditRow !== -1) {\n this.#exitRowEdit(this.#activeEditRow, true);\n return true;\n }\n\n // Space: toggle boolean cells\n if (event.key === ' ' || event.key === 'Spacebar') {\n const focusRow = internalGrid._focusRow;\n const focusCol = internalGrid._focusCol;\n if (focusRow >= 0 && focusCol >= 0) {\n const column = internalGrid._visibleColumns[focusCol];\n const rowData = internalGrid._rows[focusRow];\n if (column?.editable && column.type === 'boolean' && rowData) {\n const field = column.field;\n if (isSafePropertyKey(field)) {\n const currentValue = (rowData as Record<string, unknown>)[field];\n const newValue = !currentValue;\n this.#commitCellValue(focusRow, column, newValue, rowData);\n event.preventDefault();\n // Re-render to update the UI\n this.requestRender();\n return true;\n }\n }\n }\n // Space on non-boolean cell - don't block keyboard navigation\n return false;\n }\n\n // Enter: start row edit or commit\n if (event.key === 'Enter' && !event.shiftKey) {\n if (this.#activeEditRow !== -1) {\n // Already editing - let cell handlers deal with it\n return false;\n }\n\n // Start row-based editing (not just the focused cell)\n const editOn = this.config.editOn ?? internalGrid.effectiveConfig?.editOn;\n if (editOn === false || editOn === 'manual') return false;\n\n const focusRow = internalGrid._focusRow;\n if (focusRow >= 0) {\n // Check if ANY column in the row is editable\n const hasEditableColumn = internalGrid._columns?.some((col) => col.editable);\n if (hasEditableColumn) {\n this.beginBulkEdit(focusRow);\n return true;\n }\n }\n // No editable columns - don't block keyboard navigation\n return false;\n }\n\n // Don't block other keyboard events\n return false;\n }\n\n // #endregion\n\n // #region Render Hooks\n\n /**\n * After render, reapply editors to cells in edit mode.\n * This handles virtualization - when a row scrolls back into view,\n * we need to re-inject the editor.\n */\n override afterRender(): void {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n\n // Restore focus after exiting edit mode\n if (this.#pendingFocusRestore) {\n this.#pendingFocusRestore = false;\n this.#restoreCellFocus(internalGrid);\n }\n\n if (this.#editingCells.size === 0) return;\n\n // Re-inject editors for any editing cells that are visible\n for (const cellKey of this.#editingCells) {\n const [rowStr, colStr] = cellKey.split(':');\n const rowIndex = parseInt(rowStr, 10);\n const colIndex = parseInt(colStr, 10);\n\n const rowEl = internalGrid.findRenderedRowElement?.(rowIndex);\n if (!rowEl) continue;\n\n const cellEl = rowEl.querySelector(`.cell[data-col=\"${colIndex}\"]`) as HTMLElement | null;\n if (!cellEl || cellEl.classList.contains('editing')) continue;\n\n // Cell is visible but not in editing mode - reinject editor\n const rowData = internalGrid._rows[rowIndex];\n const column = internalGrid._visibleColumns[colIndex];\n if (rowData && column) {\n this.#injectEditor(rowData, rowIndex, column, colIndex, cellEl, true);\n }\n }\n }\n\n /**\n * On scroll render, reapply editors to recycled cells.\n */\n override onScrollRender(): void {\n this.afterRender();\n }\n\n // #endregion\n\n // #region Public API\n\n /**\n * Get all rows that have been modified.\n */\n get changedRows(): T[] {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n return Array.from(this.#changedRowIndices).map((i) => internalGrid._rows[i]);\n }\n\n /**\n * Get indices of all modified rows.\n */\n get changedRowIndices(): number[] {\n return Array.from(this.#changedRowIndices);\n }\n\n /**\n * Get the currently active edit row index, or -1 if not editing.\n */\n get activeEditRow(): number {\n return this.#activeEditRow;\n }\n\n /**\n * Get the currently active edit column index, or -1 if not editing.\n */\n get activeEditCol(): number {\n return this.#activeEditCol;\n }\n\n /**\n * Check if a specific row is currently being edited.\n */\n isRowEditing(rowIndex: number): boolean {\n return this.#activeEditRow === rowIndex;\n }\n\n /**\n * Check if a specific cell is currently being edited.\n */\n isCellEditing(rowIndex: number, colIndex: number): boolean {\n return this.#editingCells.has(`${rowIndex}:${colIndex}`);\n }\n\n /**\n * Check if a specific row has been modified.\n */\n isRowChanged(rowIndex: number): boolean {\n return this.#changedRowIndices.has(rowIndex);\n }\n\n /**\n * Reset all change tracking.\n */\n resetChangedRows(silent?: boolean): void {\n const rows = this.changedRows;\n const indices = this.changedRowIndices;\n this.#changedRowIndices.clear();\n this.#syncGridEditState();\n\n if (!silent) {\n this.emit<ChangedRowsResetDetail<T>>('changed-rows-reset', { rows, indices });\n }\n\n // Clear visual indicators\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n internalGrid._rowPool?.forEach((r) => r.classList.remove('changed'));\n }\n\n /**\n * Programmatically begin editing a cell.\n */\n beginCellEdit(rowIndex: number, field: string): void {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n const colIndex = internalGrid._visibleColumns.findIndex((c) => c.field === field);\n if (colIndex === -1) return;\n\n const column = internalGrid._visibleColumns[colIndex];\n if (!column?.editable) return;\n\n const rowEl = internalGrid.findRenderedRowElement?.(rowIndex);\n const cellEl = rowEl?.querySelector(`.cell[data-col=\"${colIndex}\"]`) as HTMLElement | null;\n if (!cellEl) return;\n\n this.#beginCellEdit(rowIndex, colIndex, cellEl);\n }\n\n /**\n * Programmatically begin editing all editable cells in a row.\n */\n beginBulkEdit(rowIndex: number): void {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n const editOn = this.config.editOn ?? internalGrid.effectiveConfig?.editOn;\n if (editOn === false) return;\n\n const hasEditableColumn = internalGrid._columns?.some((col) => col.editable);\n if (!hasEditableColumn) return;\n\n const rowEl = internalGrid.findRenderedRowElement?.(rowIndex);\n if (!rowEl) return;\n\n // Start row edit\n const rowData = internalGrid._rows[rowIndex];\n this.#startRowEdit(rowIndex, rowData);\n\n // Enter edit mode on all editable cells\n Array.from(rowEl.children).forEach((cell, i) => {\n const col = internalGrid._visibleColumns[i];\n if (col?.editable) {\n const cellEl = cell as HTMLElement;\n if (!cellEl.classList.contains('editing')) {\n this.#injectEditor(rowData, rowIndex, col, i, cellEl, true);\n }\n }\n });\n\n // Focus the first editable cell\n setTimeout(() => {\n let targetCell = rowEl.querySelector(`.cell[data-col=\"${internalGrid._focusCol}\"]`);\n if (!targetCell?.classList.contains('editing')) {\n targetCell = rowEl.querySelector('.cell.editing');\n }\n if (targetCell?.classList.contains('editing')) {\n const editor = (targetCell as HTMLElement).querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n try {\n editor?.focus({ preventScroll: true });\n } catch {\n /* empty */\n }\n }\n }, 0);\n }\n\n /**\n * Commit the currently active row edit.\n */\n commitActiveRowEdit(): void {\n if (this.#activeEditRow !== -1) {\n this.#exitRowEdit(this.#activeEditRow, false);\n }\n }\n\n /**\n * Cancel the currently active row edit.\n */\n cancelActiveRowEdit(): void {\n if (this.#activeEditRow !== -1) {\n this.#exitRowEdit(this.#activeEditRow, true);\n }\n }\n\n // #endregion\n\n // #region Internal Methods\n\n /**\n * Begin editing a single cell.\n */\n #beginCellEdit(rowIndex: number, colIndex: number, cellEl: HTMLElement): void {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n const rowData = internalGrid._rows[rowIndex];\n const column = internalGrid._visibleColumns[colIndex];\n\n if (!rowData || !column?.editable) return;\n if (cellEl.classList.contains('editing')) return;\n\n // Start row edit if not already\n if (this.#activeEditRow !== rowIndex) {\n this.#startRowEdit(rowIndex, rowData);\n }\n\n this.#activeEditCol = colIndex;\n this.#injectEditor(rowData, rowIndex, column, colIndex, cellEl, false);\n }\n\n /**\n * Sync the internal grid state with the plugin's editing state.\n */\n #syncGridEditState(): void {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n internalGrid._activeEditRows = this.#activeEditRow;\n internalGrid._rowEditSnapshots = this.#rowEditSnapshots;\n internalGrid._changedRowIndices = this.#changedRowIndices;\n }\n\n /**\n * Snapshot original row data and mark as editing.\n */\n #startRowEdit(rowIndex: number, rowData: T): void {\n if (this.#activeEditRow !== rowIndex) {\n this.#rowEditSnapshots.set(rowIndex, { ...rowData });\n this.#activeEditRow = rowIndex;\n this.#syncGridEditState();\n }\n }\n\n /**\n * Exit editing for a row.\n */\n #exitRowEdit(rowIndex: number, revert: boolean): void {\n if (this.#activeEditRow !== rowIndex) return;\n\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n const snapshot = this.#rowEditSnapshots.get(rowIndex);\n const current = internalGrid._rows[rowIndex];\n const rowEl = internalGrid.findRenderedRowElement?.(rowIndex);\n\n // Collect and commit values from active editors before re-rendering\n if (!revert && rowEl && current) {\n const editingCells = rowEl.querySelectorAll('.cell.editing');\n editingCells.forEach((cell) => {\n const colIndex = Number((cell as HTMLElement).getAttribute('data-col'));\n if (isNaN(colIndex)) return;\n const col = internalGrid._visibleColumns[colIndex];\n if (!col) return;\n const input = cell.querySelector('input,textarea,select') as\n | HTMLInputElement\n | HTMLTextAreaElement\n | HTMLSelectElement\n | null;\n if (input) {\n let val: unknown;\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n val = input.checked;\n } else {\n val = input.value;\n if (col.type === 'number' && val !== '') {\n val = Number(val);\n }\n }\n if (current[col.field as keyof T] !== val) {\n this.#commitCellValue(rowIndex, col, val, current);\n }\n }\n });\n }\n\n // Revert if requested\n if (revert && snapshot && current) {\n Object.keys(snapshot as object).forEach((k) => {\n (current as Record<string, unknown>)[k] = (snapshot as Record<string, unknown>)[k];\n });\n this.#changedRowIndices.delete(rowIndex);\n } else if (!revert) {\n const changed = this.#changedRowIndices.has(rowIndex);\n this.emit<RowCommitDetail<T>>('row-commit', {\n rowIndex,\n row: current,\n changed,\n changedRows: this.changedRows,\n changedRowIndices: this.changedRowIndices,\n });\n }\n\n // Clear editing state\n this.#rowEditSnapshots.delete(rowIndex);\n this.#activeEditRow = -1;\n this.#activeEditCol = -1;\n this.#syncGridEditState();\n\n // Remove all editing cells for this row\n for (const cellKey of this.#editingCells) {\n if (cellKey.startsWith(`${rowIndex}:`)) {\n this.#editingCells.delete(cellKey);\n }\n }\n\n // Re-render the row to remove editors\n if (rowEl) {\n // Remove editing class and re-render cells\n rowEl.querySelectorAll('.cell.editing').forEach((cell) => {\n cell.classList.remove('editing');\n clearEditingState(cell.parentElement as RowElementInternal);\n });\n\n // Request grid re-render to restore cell content\n this.requestRender();\n }\n\n // Mark that focus should be restored after render completes\n this.#pendingFocusRestore = true;\n\n // If no render was scheduled (row not visible), restore focus immediately\n if (!rowEl) {\n this.#restoreCellFocus(internalGrid);\n this.#pendingFocusRestore = false;\n }\n }\n\n /**\n * Commit a single cell value change.\n */\n #commitCellValue(rowIndex: number, column: ColumnConfig<T>, newValue: unknown, rowData: T): void {\n const field = column.field;\n if (!isSafePropertyKey(field)) return;\n const oldValue = (rowData as Record<string, unknown>)[field];\n if (oldValue === newValue) return;\n\n (rowData as Record<string, unknown>)[field] = newValue;\n const firstTime = !this.#changedRowIndices.has(rowIndex);\n this.#changedRowIndices.add(rowIndex);\n this.#syncGridEditState();\n\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n const rowEl = internalGrid.findRenderedRowElement?.(rowIndex);\n if (rowEl) rowEl.classList.add('changed');\n\n this.emit<CellCommitDetail<T>>('cell-commit', {\n row: rowData,\n field,\n value: newValue,\n rowIndex,\n changedRows: this.changedRows,\n changedRowIndices: this.changedRowIndices,\n firstTimeForRow: firstTime,\n });\n }\n\n /**\n * Inject an editor into a cell.\n */\n #injectEditor(\n rowData: T,\n rowIndex: number,\n column: ColumnConfig<T>,\n colIndex: number,\n cell: HTMLElement,\n skipFocus: boolean,\n ): void {\n if (!column.editable) return;\n if (cell.classList.contains('editing')) return;\n\n const originalValue = isSafePropertyKey(column.field)\n ? (rowData as Record<string, unknown>)[column.field]\n : undefined;\n\n cell.classList.add('editing');\n this.#editingCells.add(`${rowIndex}:${colIndex}`);\n\n const rowEl = cell.parentElement as RowElementInternal | null;\n if (rowEl) incrementEditingCount(rowEl);\n\n let editFinalized = false;\n const commit = (newValue: unknown) => {\n if (editFinalized || this.#activeEditRow === -1) return;\n this.#commitCellValue(rowIndex, column, newValue, rowData);\n };\n const cancel = () => {\n editFinalized = true;\n if (isSafePropertyKey(column.field)) {\n (rowData as Record<string, unknown>)[column.field] = originalValue;\n }\n };\n\n const editorHost = document.createElement('div');\n editorHost.className = 'tbw-editor-host';\n cell.innerHTML = '';\n cell.appendChild(editorHost);\n\n // Keydown handler for Enter/Escape\n editorHost.addEventListener('keydown', (e: KeyboardEvent) => {\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true;\n this.#exitRowEdit(rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel();\n this.#exitRowEdit(rowIndex, true);\n }\n });\n\n const colInternal = column as ColumnInternal<T>;\n const tplHolder = colInternal.__editorTemplate;\n const editorSpec = colInternal.editor || (tplHolder ? 'template' : defaultEditorFor(column));\n const value = originalValue;\n\n if (editorSpec === 'template' && tplHolder) {\n this.#renderTemplateEditor(editorHost, colInternal, rowData, originalValue, commit, cancel, skipFocus, rowIndex);\n } else if (typeof editorSpec === 'string') {\n const el = document.createElement(editorSpec) as HTMLElement & { value?: unknown };\n el.value = value;\n el.addEventListener('change', () => commit(el.value));\n editorHost.appendChild(el);\n if (!skipFocus) {\n queueMicrotask(() => {\n const focusable = editorHost.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n focusable?.focus({ preventScroll: true });\n });\n }\n } else if (typeof editorSpec === 'function') {\n const ctx: EditorContext<T> = { row: rowData, value, field: column.field, column, commit, cancel };\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n const produced = (editorSpec as any)(ctx);\n if (typeof produced === 'string') {\n editorHost.innerHTML = produced;\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n wireEditorInputs(editorHost, column as any, commit);\n } else if (produced instanceof Node) {\n editorHost.appendChild(produced);\n }\n if (!skipFocus) {\n queueMicrotask(() => {\n const focusable = editorHost.querySelector(FOCUSABLE_EDITOR_SELECTOR) as HTMLElement | null;\n focusable?.focus({ preventScroll: true });\n });\n }\n } else if (editorSpec && typeof editorSpec === 'object') {\n const internalGrid = this.grid as unknown as InternalGrid<T>;\n const placeholder = document.createElement('div');\n placeholder.setAttribute('data-external-editor', '');\n placeholder.setAttribute('data-field', column.field);\n editorHost.appendChild(placeholder);\n const context: EditorContext<T> = { row: rowData, value, field: column.field, column, commit, cancel };\n if (editorSpec.mount) {\n try {\n // eslint-disable-next-line @typescript-eslint/no-explicit-any\n editorSpec.mount({ placeholder, context: context as any, spec: editorSpec });\n } catch (e) {\n console.warn(`[tbw-grid] External editor mount error for column '${column.field}':`, e);\n }\n } else {\n (internalGrid as unknown as HTMLElement).dispatchEvent(\n new CustomEvent('mount-external-editor', { detail: { placeholder, spec: editorSpec, context } }),\n );\n }\n }\n }\n\n /**\n * Render a template-based editor.\n */\n #renderTemplateEditor(\n editorHost: HTMLElement,\n column: ColumnInternal<T>,\n rowData: T,\n originalValue: unknown,\n commit: (value: unknown) => void,\n cancel: () => void,\n skipFocus: boolean,\n rowIndex: number,\n ): void {\n const tplHolder = column.__editorTemplate;\n if (!tplHolder) return;\n\n const clone = tplHolder.cloneNode(true) as HTMLElement;\n const compiledEditor = column.__compiledEditor;\n\n if (compiledEditor) {\n clone.innerHTML = compiledEditor({\n row: rowData,\n value: originalValue,\n field: column.field,\n column,\n commit,\n cancel,\n });\n } else {\n clone.querySelectorAll<HTMLElement>('*').forEach((node) => {\n if (node.childNodes.length === 1 && node.firstChild?.nodeType === Node.TEXT_NODE) {\n node.textContent =\n node.textContent\n ?.replace(/{{\\s*value\\s*}}/g, originalValue == null ? '' : String(originalValue))\n .replace(/{{\\s*row\\.([a-zA-Z0-9_]+)\\s*}}/g, (_m, g: string) => {\n if (!isSafePropertyKey(g)) return '';\n const v = (rowData as Record<string, unknown>)[g];\n return v == null ? '' : String(v);\n }) || '';\n }\n });\n }\n\n const input = clone.querySelector<HTMLInputElement | HTMLTextAreaElement | HTMLSelectElement>(\n 'input,textarea,select',\n );\n if (input) {\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n input.checked = !!originalValue;\n } else {\n input.value = String(originalValue ?? '');\n }\n\n let editFinalized = false;\n input.addEventListener('blur', () => {\n if (editFinalized) return;\n const val = input instanceof HTMLInputElement && input.type === 'checkbox' ? input.checked : input.value;\n commit(val);\n });\n input.addEventListener('keydown', (evt) => {\n const e = evt as KeyboardEvent;\n if (e.key === 'Enter') {\n e.stopPropagation();\n e.preventDefault();\n editFinalized = true;\n const val = input instanceof HTMLInputElement && input.type === 'checkbox' ? input.checked : input.value;\n commit(val);\n this.#exitRowEdit(rowIndex, false);\n }\n if (e.key === 'Escape') {\n e.stopPropagation();\n e.preventDefault();\n cancel();\n this.#exitRowEdit(rowIndex, true);\n }\n });\n if (input instanceof HTMLInputElement && input.type === 'checkbox') {\n input.addEventListener('change', () => commit(input.checked));\n }\n if (!skipFocus) {\n setTimeout(() => input.focus({ preventScroll: true }), 0);\n }\n }\n editorHost.appendChild(clone);\n }\n\n /**\n * Restore focus to cell after exiting edit mode.\n */\n #restoreCellFocus(internalGrid: InternalGrid<T>): void {\n queueMicrotask(() => {\n try {\n const rowIdx = internalGrid._focusRow;\n const colIdx = internalGrid._focusCol;\n const rowEl = internalGrid.findRenderedRowElement?.(rowIdx);\n if (rowEl) {\n Array.from(internalGrid._bodyEl.querySelectorAll('.cell-focus')).forEach((el) =>\n el.classList.remove('cell-focus'),\n );\n const cell = rowEl.querySelector(`.cell[data-row=\"${rowIdx}\"][data-col=\"${colIdx}\"]`) as HTMLElement | null;\n if (cell) {\n cell.classList.add('cell-focus');\n cell.setAttribute('aria-selected', 'true');\n if (!cell.hasAttribute('tabindex')) cell.setAttribute('tabindex', '-1');\n cell.focus({ preventScroll: true });\n }\n }\n } catch {\n /* empty */\n }\n });\n }\n\n // #endregion\n}\n"],"names":["defaultEditorFor","column","ctx","input","e","select","col","rawOptions","opt","o","commitValue","values","FOCUSABLE_EDITOR_SELECTOR","isSafePropertyKey","key","hasEditingCells","rowEl","incrementEditingCount","count","clearEditingState","wireEditorInputs","editorHost","commit","getInputValue","EditingPlugin","BaseGridPlugin","#activeEditRow","#activeEditCol","#rowEditSnapshots","#changedRowIndices","#editingCells","#pendingFocusRestore","grid","signal","internalGrid","silent","rowIndex","field","#exitRowEdit","columns","event","editOn","isDoubleClick","focusRow","focusCol","rowData","newValue","#commitCellValue","#restoreCellFocus","cellKey","rowStr","colStr","colIndex","cellEl","#injectEditor","i","rows","indices","#syncGridEditState","r","#beginCellEdit","#startRowEdit","cell","targetCell","editor","revert","snapshot","current","val","k","changed","firstTime","skipFocus","originalValue","editFinalized","cancel","colInternal","tplHolder","editorSpec","value","#renderTemplateEditor","el","produced","placeholder","context","clone","compiledEditor","node","_m","g","evt","rowIdx","colIdx"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  (function(a,p){typeof exports=="object"&&typeof module<"u"?p(exports,require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/plugin/base-plugin"],p):(a=typeof globalThis<"u"?globalThis:a||self,p(a.TbwGridPlugin_export={},a.TbwGrid))})(this,(function(a,p){"use strict";function b(i,t=!0){if(i==null)return"";if(i instanceof Date)return i.toISOString();if(typeof i=="object")return JSON.stringify(i);const e=String(i);return t&&(e.includes(",")||e.includes('"')||e.includes(`
2
2
  `)||e.includes("\r"))?`"${e.replace(/"/g,'""')}"`:e}function y(i,t,e,o={}){const c=o.delimiter??",",r=o.newline??`
3
- `,s=[],l=o.bom?"\uFEFF":"";if(e.includeHeaders!==!1){const n=t.map(f=>{const d=f.header||f.field,u=e.processHeader?e.processHeader(d,f.field):d;return b(u)});s.push(n.join(c))}for(const n of i){const f=t.map(d=>{let u=n[d.field];return e.processCell&&(u=e.processCell(u,d.field,n)),b(u)});s.push(f.join(c))}return l+s.join(r)}function g(i,t){const e=URL.createObjectURL(i),o=document.createElement("a");o.href=e,o.download=t,o.style.display="none",document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(e)}function S(i,t){const e=new Blob([i],{type:"text/csv;charset=utf-8;"});g(e,t)}function m(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function C(i,t,e){let o=`<?xml version="1.0" encoding="UTF-8"?>
3
+ `,s=[],l=o.bom?"\uFEFF":"";if(e.includeHeaders!==!1){const n=t.map(f=>{const d=f.header||f.field,u=e.processHeader?e.processHeader(d,f.field):d;return b(u)});s.push(n.join(c))}for(const n of i){const f=t.map(d=>{let u=n[d.field];return e.processCell&&(u=e.processCell(u,d.field,n)),b(u)});s.push(f.join(c))}return l+s.join(r)}function h(i,t){const e=URL.createObjectURL(i),o=document.createElement("a");o.href=e,o.download=t,o.style.display="none",document.body.appendChild(o),o.click(),document.body.removeChild(o),URL.revokeObjectURL(e)}function S(i,t){const e=new Blob([i],{type:"text/csv;charset=utf-8;"});h(e,t)}function m(i){return i.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&apos;")}function C(i,t,e){let o=`<?xml version="1.0" encoding="UTF-8"?>
4
4
  <?mso-application progid="Excel.Sheet"?>
5
5
  <Workbook xmlns="urn:schemas-microsoft-com:office:spreadsheet"
6
6
  xmlns:ss="urn:schemas-microsoft-com:office:spreadsheet">
@@ -10,5 +10,5 @@
10
10
  <Row>`;for(const r of t){let s=c[r.field];e.processCell&&(s=e.processCell(s,r.field,c));let l="String",n="";s==null?n="":typeof s=="number"&&!isNaN(s)?(l="Number",n=String(s)):s instanceof Date?(l="DateTime",n=s.toISOString()):n=m(String(s)),o+=`<Cell><Data ss:Type="${l}">${n}</Data></Cell>`}o+="</Row>"}return o+=`
11
11
  </Table>
12
12
  </Worksheet>
13
- </Workbook>`,o}function E(i,t){const e=t.endsWith(".xls")?t:`${t}.xls`,o=new Blob([i],{type:"application/vnd.ms-excel;charset=utf-8;"});g(o,e)}class j extends p.BaseGridPlugin{name="export";version="1.0.0";get defaultConfig(){return{fileName:"export",includeHeaders:!0,onlyVisible:!0,onlySelected:!1}}isExportingFlag=!1;lastExportInfo=null;performExport(t,e){const o=this.config,c={format:t,fileName:e?.fileName??o.fileName??"export",includeHeaders:e?.includeHeaders??o.includeHeaders,processCell:e?.processCell,processHeader:e?.processHeader,columns:e?.columns,rowIndices:e?.rowIndices};let r=[...this.columns];if(o.onlyVisible&&(r=r.filter(n=>!n.hidden&&!n.field.startsWith("__"))),e?.columns){const n=new Set(e.columns);r=r.filter(f=>n.has(f.field))}let s=[...this.rows];if(o.onlySelected){const n=this.getSelectionState();n?.selected?.size&&(s=[...n.selected].sort((d,u)=>d-u).map(d=>this.rows[d]).filter(Boolean))}e?.rowIndices&&(s=e.rowIndices.map(n=>this.rows[n]).filter(Boolean)),this.isExportingFlag=!0;let l=c.fileName;try{switch(t){case"csv":{const n=y(s,r,c,{bom:!0});l=l.endsWith(".csv")?l:`${l}.csv`,S(n,l);break}case"excel":{const n=C(s,r,c);l=l.endsWith(".xls")?l:`${l}.xls`,E(n,l);break}case"json":{const n=s.map(u=>{const w={};for(const h of r){let x=u[h.field];c.processCell&&(x=c.processCell(x,h.field,u)),w[h.field]=x}return w}),f=JSON.stringify(n,null,2);l=l.endsWith(".json")?l:`${l}.json`;const d=new Blob([f],{type:"application/json"});g(d,l);break}}this.lastExportInfo={format:t,timestamp:new Date},this.emit("export-complete",{format:t,fileName:l,rowCount:s.length,columnCount:r.length})}finally{this.isExportingFlag=!1}}getSelectionState(){try{return this.grid?.getPluginState?.("selection")??null}catch{return null}}exportCsv(t){this.performExport("csv",t)}exportExcel(t){this.performExport("excel",t)}exportJson(t){this.performExport("json",t)}isExporting(){return this.isExportingFlag}getLastExport(){return this.lastExportInfo}}a.ExportPlugin=j,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
13
+ </Workbook>`,o}function E(i,t){const e=t.endsWith(".xls")?t:`${t}.xls`,o=new Blob([i],{type:"application/vnd.ms-excel;charset=utf-8;"});h(o,e)}class j extends p.BaseGridPlugin{name="export";version="1.0.0";get defaultConfig(){return{fileName:"export",includeHeaders:!0,onlyVisible:!0,onlySelected:!1}}isExportingFlag=!1;lastExportInfo=null;performExport(t,e){const o=this.config,c={format:t,fileName:e?.fileName??o.fileName??"export",includeHeaders:e?.includeHeaders??o.includeHeaders,processCell:e?.processCell,processHeader:e?.processHeader,columns:e?.columns,rowIndices:e?.rowIndices};let r=[...this.columns];if(o.onlyVisible&&(r=r.filter(n=>!n.hidden&&!n.field.startsWith("__"))),e?.columns){const n=new Set(e.columns);r=r.filter(f=>n.has(f.field))}let s=[...this.rows];if(o.onlySelected){const n=this.getSelectionState();n?.selected?.size&&(s=[...n.selected].sort((d,u)=>d-u).map(d=>this.rows[d]).filter(Boolean))}e?.rowIndices&&(s=e.rowIndices.map(n=>this.rows[n]).filter(Boolean)),this.isExportingFlag=!0;let l=c.fileName;try{switch(t){case"csv":{const n=y(s,r,c,{bom:!0});l=l.endsWith(".csv")?l:`${l}.csv`,S(n,l);break}case"excel":{const n=C(s,r,c);l=l.endsWith(".xls")?l:`${l}.xls`,E(n,l);break}case"json":{const n=s.map(u=>{const w={};for(const g of r){let x=u[g.field];c.processCell&&(x=c.processCell(x,g.field,u)),w[g.field]=x}return w}),f=JSON.stringify(n,null,2);l=l.endsWith(".json")?l:`${l}.json`;const d=new Blob([f],{type:"application/json"});h(d,l);break}}this.lastExportInfo={format:t,timestamp:new Date},this.emit("export-complete",{format:t,fileName:l,rowCount:s.length,columnCount:r.length})}finally{this.isExportingFlag=!1}}getSelectionState(){try{return this.grid?.getPluginState?.("selection")??null}catch{return null}}exportCsv(t){this.performExport("csv",t)}exportExcel(t){this.performExport("excel",t)}exportJson(t){this.performExport("json",t)}isExporting(){return this.isExportingFlag}getLastExport(){return this.lastExportInfo}}a.ExportPlugin=j,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
14
14
  //# sourceMappingURL=export.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"export.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/export/csv.ts","../../../../../libs/grid/src/lib/plugins/export/excel.ts","../../../../../libs/grid/src/lib/plugins/export/ExportPlugin.ts"],"sourcesContent":["/**\n * CSV Export Utilities\n *\n * Functions for building and downloading CSV content.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\n\n/** CSV export options */\nexport interface CsvOptions {\n /** Field delimiter (default: ',') */\n delimiter?: string;\n /** Line separator (default: '\\n') */\n newline?: string;\n /** Whether to quote strings containing special characters (default: true) */\n quoteStrings?: boolean;\n /** Add UTF-8 BOM for Excel compatibility (default: false) */\n bom?: boolean;\n}\n\n/**\n * Format a value for CSV output.\n * Handles null, Date, objects, and strings with special characters.\n */\nexport function formatCsvValue(value: any, quote = true): string {\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n\n // Quote if contains special characters (comma, quote, newline)\n if (quote && (str.includes(',') || str.includes('\"') || str.includes('\\n') || str.includes('\\r'))) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build CSV content from rows and columns.\n */\nexport function buildCsv(rows: any[], columns: ColumnConfig[], params: ExportParams, options: CsvOptions = {}): string {\n const delimiter = options.delimiter ?? ',';\n const newline = options.newline ?? '\\n';\n const lines: string[] = [];\n\n // UTF-8 BOM for Excel compatibility\n const bom = options.bom ? '\\uFEFF' : '';\n\n // Build header row\n if (params.includeHeaders !== false) {\n const headerRow = columns.map((col) => {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n return formatCsvValue(processed);\n });\n lines.push(headerRow.join(delimiter));\n }\n\n // Build data rows\n for (const row of rows) {\n const cells = columns.map((col) => {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n return formatCsvValue(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return bom + lines.join(newline);\n}\n\n/**\n * Download a Blob as a file.\n */\nexport function downloadBlob(blob: Blob, fileName: string): void {\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n link.style.display = 'none';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Download CSV content as a file.\n */\nexport function downloadCsv(content: string, fileName: string): void {\n const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });\n downloadBlob(blob, fileName);\n}\n","/**\n * Excel Export Utilities\n *\n * Simple Excel XML format export (no external dependencies).\n * Produces XML Spreadsheet 2003 format which opens in Excel.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\nimport { downloadBlob } from './csv';\n\n/**\n * Escape XML special characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n\n/**\n * Build Excel XML content from rows and columns.\n * Uses XML Spreadsheet 2003 format for broad compatibility.\n */\nexport function buildExcelXml(rows: any[], columns: ColumnConfig[], params: ExportParams): string {\n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?mso-application progid=\"Excel.Sheet\"?>\n<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"\n xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\">\n<Worksheet ss:Name=\"Sheet1\">\n<Table>`;\n\n // Build header row\n if (params.includeHeaders !== false) {\n xml += '\\n<Row>';\n for (const col of columns) {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n xml += `<Cell><Data ss:Type=\"String\">${escapeXml(processed)}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n // Build data rows\n for (const row of rows) {\n xml += '\\n<Row>';\n for (const col of columns) {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n\n // Determine cell type based on value\n let type: 'Number' | 'String' | 'DateTime' = 'String';\n let displayValue = '';\n\n if (value == null) {\n displayValue = '';\n } else if (typeof value === 'number' && !isNaN(value)) {\n type = 'Number';\n displayValue = String(value);\n } else if (value instanceof Date) {\n type = 'DateTime';\n displayValue = value.toISOString();\n } else {\n displayValue = escapeXml(String(value));\n }\n\n xml += `<Cell><Data ss:Type=\"${type}\">${displayValue}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n xml += '\\n</Table>\\n</Worksheet>\\n</Workbook>';\n return xml;\n}\n\n/**\n * Download Excel XML content as a file.\n */\nexport function downloadExcel(content: string, fileName: string): void {\n const finalName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n const blob = new Blob([content], {\n type: 'application/vnd.ms-excel;charset=utf-8;',\n });\n downloadBlob(blob, finalName);\n}\n","/**\n * Export Plugin (Class-based)\n *\n * Provides data export functionality for tbw-grid.\n * Supports CSV, Excel (XML), and JSON formats.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildCsv, downloadBlob, downloadCsv } from './csv';\nimport { buildExcelXml, downloadExcel } from './excel';\nimport type { ExportCompleteDetail, ExportConfig, ExportFormat, ExportParams } from './types';\n\n/** Selection plugin state interface for type safety */\ninterface SelectionPluginState {\n selected: Set<number>;\n}\n\n/**\n * Export Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ExportPlugin({\n * enabled: true,\n * fileName: 'my-data',\n * includeHeaders: true,\n * onlyVisible: true,\n * })\n * ```\n */\nexport class ExportPlugin extends BaseGridPlugin<ExportConfig> {\n readonly name = 'export';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ExportConfig> {\n return {\n fileName: 'export',\n includeHeaders: true,\n onlyVisible: true,\n onlySelected: false,\n };\n }\n\n // #region Internal State\n private isExportingFlag = false;\n private lastExportInfo: { format: ExportFormat; timestamp: Date } | null = null;\n // #endregion\n\n // #region Private Methods\n\n private performExport(format: ExportFormat, params?: Partial<ExportParams>): void {\n const config = this.config;\n\n // Build full params with defaults\n const fullParams: ExportParams = {\n format,\n fileName: params?.fileName ?? config.fileName ?? 'export',\n includeHeaders: params?.includeHeaders ?? config.includeHeaders,\n processCell: params?.processCell,\n processHeader: params?.processHeader,\n columns: params?.columns,\n rowIndices: params?.rowIndices,\n };\n\n // Get columns to export\n let columns = [...this.columns] as ColumnConfig[];\n if (config.onlyVisible) {\n columns = columns.filter((c) => !c.hidden && !c.field.startsWith('__'));\n }\n if (params?.columns) {\n const colSet = new Set(params.columns);\n columns = columns.filter((c) => colSet.has(c.field));\n }\n\n // Get rows to export\n let rows = [...this.rows] as any[];\n if (config.onlySelected) {\n const selectionState = this.getSelectionState();\n if (selectionState?.selected?.size) {\n const sortedIndices = [...selectionState.selected].sort((a, b) => a - b);\n rows = sortedIndices.map((i) => this.rows[i]).filter(Boolean);\n }\n }\n if (params?.rowIndices) {\n rows = params.rowIndices.map((i) => this.rows[i]).filter(Boolean);\n }\n\n this.isExportingFlag = true;\n let fileName = fullParams.fileName!;\n\n try {\n switch (format) {\n case 'csv': {\n const content = buildCsv(rows, columns, fullParams, { bom: true });\n fileName = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`;\n downloadCsv(content, fileName);\n break;\n }\n\n case 'excel': {\n const content = buildExcelXml(rows, columns, fullParams);\n fileName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n downloadExcel(content, fileName);\n break;\n }\n\n case 'json': {\n const jsonData = rows.map((row) => {\n const obj: Record<string, any> = {};\n for (const col of columns) {\n let value = row[col.field];\n if (fullParams.processCell) {\n value = fullParams.processCell(value, col.field, row);\n }\n obj[col.field] = value;\n }\n return obj;\n });\n const content = JSON.stringify(jsonData, null, 2);\n fileName = fileName.endsWith('.json') ? fileName : `${fileName}.json`;\n const blob = new Blob([content], { type: 'application/json' });\n downloadBlob(blob, fileName);\n break;\n }\n }\n\n this.lastExportInfo = { format, timestamp: new Date() };\n\n this.emit<ExportCompleteDetail>('export-complete', {\n format,\n fileName,\n rowCount: rows.length,\n columnCount: columns.length,\n });\n } finally {\n this.isExportingFlag = false;\n }\n }\n\n private getSelectionState(): SelectionPluginState | null {\n try {\n const grid = this.grid as any;\n return grid?.getPluginState?.('selection') ?? null;\n } catch {\n return null;\n }\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Export data to CSV format.\n * @param params - Optional export parameters\n */\n exportCsv(params?: Partial<ExportParams>): void {\n this.performExport('csv', params);\n }\n\n /**\n * Export data to Excel format (XML Spreadsheet).\n * @param params - Optional export parameters\n */\n exportExcel(params?: Partial<ExportParams>): void {\n this.performExport('excel', params);\n }\n\n /**\n * Export data to JSON format.\n * @param params - Optional export parameters\n */\n exportJson(params?: Partial<ExportParams>): void {\n this.performExport('json', params);\n }\n\n /**\n * Check if an export is currently in progress.\n * @returns Whether export is in progress\n */\n isExporting(): boolean {\n return this.isExportingFlag;\n }\n\n /**\n * Get information about the last export.\n * @returns Export info or null if no export has occurred\n */\n getLastExport(): { format: ExportFormat; timestamp: Date } | null {\n return this.lastExportInfo;\n }\n // #endregion\n}\n"],"names":["formatCsvValue","value","quote","str","buildCsv","rows","columns","params","options","delimiter","newline","lines","bom","headerRow","col","header","processed","row","cells","downloadBlob","blob","fileName","url","link","downloadCsv","content","escapeXml","buildExcelXml","xml","type","displayValue","downloadExcel","finalName","ExportPlugin","BaseGridPlugin","format","config","fullParams","c","colSet","selectionState","a","b","i","jsonData","obj"],"mappings":"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"}
1
+ {"version":3,"file":"export.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/export/csv.ts","../../../../../libs/grid/src/lib/plugins/export/excel.ts","../../../../../libs/grid/src/lib/plugins/export/ExportPlugin.ts"],"sourcesContent":["/**\n * CSV Export Utilities\n *\n * Functions for building and downloading CSV content.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\n\n/** CSV export options */\nexport interface CsvOptions {\n /** Field delimiter (default: ',') */\n delimiter?: string;\n /** Line separator (default: '\\n') */\n newline?: string;\n /** Whether to quote strings containing special characters (default: true) */\n quoteStrings?: boolean;\n /** Add UTF-8 BOM for Excel compatibility (default: false) */\n bom?: boolean;\n}\n\n/**\n * Format a value for CSV output.\n * Handles null, Date, objects, and strings with special characters.\n */\nexport function formatCsvValue(value: any, quote = true): string {\n if (value == null) return '';\n if (value instanceof Date) return value.toISOString();\n if (typeof value === 'object') return JSON.stringify(value);\n\n const str = String(value);\n\n // Quote if contains special characters (comma, quote, newline)\n if (quote && (str.includes(',') || str.includes('\"') || str.includes('\\n') || str.includes('\\r'))) {\n return `\"${str.replace(/\"/g, '\"\"')}\"`;\n }\n\n return str;\n}\n\n/**\n * Build CSV content from rows and columns.\n */\nexport function buildCsv(rows: any[], columns: ColumnConfig[], params: ExportParams, options: CsvOptions = {}): string {\n const delimiter = options.delimiter ?? ',';\n const newline = options.newline ?? '\\n';\n const lines: string[] = [];\n\n // UTF-8 BOM for Excel compatibility\n const bom = options.bom ? '\\uFEFF' : '';\n\n // Build header row\n if (params.includeHeaders !== false) {\n const headerRow = columns.map((col) => {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n return formatCsvValue(processed);\n });\n lines.push(headerRow.join(delimiter));\n }\n\n // Build data rows\n for (const row of rows) {\n const cells = columns.map((col) => {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n return formatCsvValue(value);\n });\n lines.push(cells.join(delimiter));\n }\n\n return bom + lines.join(newline);\n}\n\n/**\n * Download a Blob as a file.\n */\nexport function downloadBlob(blob: Blob, fileName: string): void {\n const url = URL.createObjectURL(blob);\n const link = document.createElement('a');\n link.href = url;\n link.download = fileName;\n link.style.display = 'none';\n document.body.appendChild(link);\n link.click();\n document.body.removeChild(link);\n URL.revokeObjectURL(url);\n}\n\n/**\n * Download CSV content as a file.\n */\nexport function downloadCsv(content: string, fileName: string): void {\n const blob = new Blob([content], { type: 'text/csv;charset=utf-8;' });\n downloadBlob(blob, fileName);\n}\n","/**\n * Excel Export Utilities\n *\n * Simple Excel XML format export (no external dependencies).\n * Produces XML Spreadsheet 2003 format which opens in Excel.\n */\n\nimport type { ColumnConfig } from '../../core/types';\nimport type { ExportParams } from './types';\nimport { downloadBlob } from './csv';\n\n/**\n * Escape XML special characters.\n */\nfunction escapeXml(str: string): string {\n return str\n .replace(/&/g, '&amp;')\n .replace(/</g, '&lt;')\n .replace(/>/g, '&gt;')\n .replace(/\"/g, '&quot;')\n .replace(/'/g, '&apos;');\n}\n\n/**\n * Build Excel XML content from rows and columns.\n * Uses XML Spreadsheet 2003 format for broad compatibility.\n */\nexport function buildExcelXml(rows: any[], columns: ColumnConfig[], params: ExportParams): string {\n let xml = `<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<?mso-application progid=\"Excel.Sheet\"?>\n<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\"\n xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\">\n<Worksheet ss:Name=\"Sheet1\">\n<Table>`;\n\n // Build header row\n if (params.includeHeaders !== false) {\n xml += '\\n<Row>';\n for (const col of columns) {\n const header = col.header || col.field;\n const processed = params.processHeader ? params.processHeader(header, col.field) : header;\n xml += `<Cell><Data ss:Type=\"String\">${escapeXml(processed)}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n // Build data rows\n for (const row of rows) {\n xml += '\\n<Row>';\n for (const col of columns) {\n let value = row[col.field];\n if (params.processCell) {\n value = params.processCell(value, col.field, row);\n }\n\n // Determine cell type based on value\n let type: 'Number' | 'String' | 'DateTime' = 'String';\n let displayValue = '';\n\n if (value == null) {\n displayValue = '';\n } else if (typeof value === 'number' && !isNaN(value)) {\n type = 'Number';\n displayValue = String(value);\n } else if (value instanceof Date) {\n type = 'DateTime';\n displayValue = value.toISOString();\n } else {\n displayValue = escapeXml(String(value));\n }\n\n xml += `<Cell><Data ss:Type=\"${type}\">${displayValue}</Data></Cell>`;\n }\n xml += '</Row>';\n }\n\n xml += '\\n</Table>\\n</Worksheet>\\n</Workbook>';\n return xml;\n}\n\n/**\n * Download Excel XML content as a file.\n */\nexport function downloadExcel(content: string, fileName: string): void {\n const finalName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n const blob = new Blob([content], {\n type: 'application/vnd.ms-excel;charset=utf-8;',\n });\n downloadBlob(blob, finalName);\n}\n","/**\n * Export Plugin (Class-based)\n *\n * Provides data export functionality for tbw-grid.\n * Supports CSV, Excel (XML), and JSON formats.\n */\n\n/* eslint-disable @typescript-eslint/no-explicit-any */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig } from '../../core/types';\nimport { buildCsv, downloadBlob, downloadCsv } from './csv';\nimport { buildExcelXml, downloadExcel } from './excel';\nimport type { ExportCompleteDetail, ExportConfig, ExportFormat, ExportParams } from './types';\n\n/** Selection plugin state interface for type safety */\ninterface SelectionPluginState {\n selected: Set<number>;\n}\n\n/**\n * Export Plugin for tbw-grid\n *\n * @example\n * ```ts\n * new ExportPlugin({\n * enabled: true,\n * fileName: 'my-data',\n * includeHeaders: true,\n * onlyVisible: true,\n * })\n * ```\n */\nexport class ExportPlugin extends BaseGridPlugin<ExportConfig> {\n readonly name = 'export';\n override readonly version = '1.0.0';\n\n protected override get defaultConfig(): Partial<ExportConfig> {\n return {\n fileName: 'export',\n includeHeaders: true,\n onlyVisible: true,\n onlySelected: false,\n };\n }\n\n // #region Internal State\n private isExportingFlag = false;\n private lastExportInfo: { format: ExportFormat; timestamp: Date } | null = null;\n // #endregion\n\n // #region Private Methods\n\n private performExport(format: ExportFormat, params?: Partial<ExportParams>): void {\n const config = this.config;\n\n // Build full params with defaults\n const fullParams: ExportParams = {\n format,\n fileName: params?.fileName ?? config.fileName ?? 'export',\n includeHeaders: params?.includeHeaders ?? config.includeHeaders,\n processCell: params?.processCell,\n processHeader: params?.processHeader,\n columns: params?.columns,\n rowIndices: params?.rowIndices,\n };\n\n // Get columns to export\n let columns = [...this.columns] as ColumnConfig[];\n if (config.onlyVisible) {\n columns = columns.filter((c) => !c.hidden && !c.field.startsWith('__'));\n }\n if (params?.columns) {\n const colSet = new Set(params.columns);\n columns = columns.filter((c) => colSet.has(c.field));\n }\n\n // Get rows to export\n let rows = [...this.rows] as Record<string, unknown>[];\n if (config.onlySelected) {\n const selectionState = this.getSelectionState();\n if (selectionState?.selected?.size) {\n const sortedIndices = [...selectionState.selected].sort((a, b) => a - b);\n rows = sortedIndices.map((i) => this.rows[i]).filter(Boolean) as Record<string, unknown>[];\n }\n }\n if (params?.rowIndices) {\n rows = params.rowIndices.map((i) => this.rows[i]).filter(Boolean) as Record<string, unknown>[];\n }\n\n this.isExportingFlag = true;\n let fileName = fullParams.fileName!;\n\n try {\n switch (format) {\n case 'csv': {\n const content = buildCsv(rows, columns, fullParams, { bom: true });\n fileName = fileName.endsWith('.csv') ? fileName : `${fileName}.csv`;\n downloadCsv(content, fileName);\n break;\n }\n\n case 'excel': {\n const content = buildExcelXml(rows, columns, fullParams);\n fileName = fileName.endsWith('.xls') ? fileName : `${fileName}.xls`;\n downloadExcel(content, fileName);\n break;\n }\n\n case 'json': {\n const jsonData = rows.map((row) => {\n const obj: Record<string, any> = {};\n for (const col of columns) {\n let value = row[col.field];\n if (fullParams.processCell) {\n value = fullParams.processCell(value, col.field, row);\n }\n obj[col.field] = value;\n }\n return obj;\n });\n const content = JSON.stringify(jsonData, null, 2);\n fileName = fileName.endsWith('.json') ? fileName : `${fileName}.json`;\n const blob = new Blob([content], { type: 'application/json' });\n downloadBlob(blob, fileName);\n break;\n }\n }\n\n this.lastExportInfo = { format, timestamp: new Date() };\n\n this.emit<ExportCompleteDetail>('export-complete', {\n format,\n fileName,\n rowCount: rows.length,\n columnCount: columns.length,\n });\n } finally {\n this.isExportingFlag = false;\n }\n }\n\n private getSelectionState(): SelectionPluginState | null {\n try {\n return (this.grid?.getPluginState?.('selection') as SelectionPluginState | null) ?? null;\n } catch {\n return null;\n }\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Export data to CSV format.\n * @param params - Optional export parameters\n */\n exportCsv(params?: Partial<ExportParams>): void {\n this.performExport('csv', params);\n }\n\n /**\n * Export data to Excel format (XML Spreadsheet).\n * @param params - Optional export parameters\n */\n exportExcel(params?: Partial<ExportParams>): void {\n this.performExport('excel', params);\n }\n\n /**\n * Export data to JSON format.\n * @param params - Optional export parameters\n */\n exportJson(params?: Partial<ExportParams>): void {\n this.performExport('json', params);\n }\n\n /**\n * Check if an export is currently in progress.\n * @returns Whether export is in progress\n */\n isExporting(): boolean {\n return this.isExportingFlag;\n }\n\n /**\n * Get information about the last export.\n * @returns Export info or null if no export has occurred\n */\n getLastExport(): { format: ExportFormat; timestamp: Date } | null {\n return this.lastExportInfo;\n }\n // #endregion\n}\n"],"names":["formatCsvValue","value","quote","str","buildCsv","rows","columns","params","options","delimiter","newline","lines","bom","headerRow","col","header","processed","row","cells","downloadBlob","blob","fileName","url","link","downloadCsv","content","escapeXml","buildExcelXml","xml","type","displayValue","downloadExcel","finalName","ExportPlugin","BaseGridPlugin","format","config","fullParams","c","colSet","selectionState","a","b","i","jsonData","obj"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(w,C){typeof exports=="object"&&typeof module<"u"?C(exports,require("../../core/internal/virtualization"),require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/virtualization","../../core/plugin/base-plugin"],C):(w=typeof globalThis<"u"?globalThis:w||self,C(w.TbwGridPlugin_filtering={},w.TbwGrid,w.TbwGrid))})(this,(function(w,C,M){"use strict";function _(m,e,t=!1){const r=m[e.field];if(e.operator==="blank")return r==null||r==="";if(e.operator==="notBlank")return r!=null&&r!=="";if(r==null)return!1;const l=String(r),n=t?l:l.toLowerCase(),a=t?String(e.value):String(e.value).toLowerCase();switch(e.operator){case"contains":return n.includes(a);case"notContains":return!n.includes(a);case"equals":return n===a;case"notEquals":return n!==a;case"startsWith":return n.startsWith(a);case"endsWith":return n.endsWith(a);case"lessThan":return Number(r)<Number(e.value);case"lessThanOrEqual":return Number(r)<=Number(e.value);case"greaterThan":return Number(r)>Number(e.value);case"greaterThanOrEqual":return Number(r)>=Number(e.value);case"between":return Number(r)>=Number(e.value)&&Number(r)<=Number(e.valueTo);case"in":return Array.isArray(e.value)&&e.value.includes(r);case"notIn":return Array.isArray(e.value)&&!e.value.includes(r);default:return!0}}function q(m,e,t=!1){return e.length?m.filter(r=>e.every(l=>_(r,l,t))):m}function z(m){return JSON.stringify(m.map(e=>({field:e.field,operator:e.operator,value:e.value,valueTo:e.valueTo})))}function A(m,e){const t=new Set;for(const r of m){const l=r[e];l!=null&&t.add(l)}return[...t].sort((r,l)=>typeof r=="number"&&typeof l=="number"?r-l:String(r).localeCompare(String(l)))}const B=':host .tbw-quick-filter-input{flex:1;max-width:300px;height:28px;padding:0 8px;border:1px solid var(--tbw-color-border);border-radius:var(--tbw-border-radius);background:var(--tbw-color-bg);color:var(--tbw-color-fg);font-size:13px}:host .tbw-quick-filter-input:focus{outline:none;border-color:var(--tbw-color-accent)}.header-cell.filtered:before{content:"";position:absolute;top:4px;right:4px;width:6px;height:6px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}.tbw-filter-btn{display:inline-flex;align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:4px;opacity:.4;transition:opacity .15s;color:inherit;vertical-align:middle}.tbw-filter-btn:hover,.tbw-filter-btn.active{opacity:1}.tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}',G=".tbw-filter-panel{position:fixed;background:var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));color:var(--tbw-filter-panel-fg, var(--tbw-color-fg, light-dark(#222222, #eeeeee)));border:1px solid var(--tbw-filter-panel-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-panel-radius, var(--tbw-border-radius, 4px));box-shadow:0 4px 16px var(--tbw-filter-panel-shadow, var(--tbw-color-shadow, light-dark(rgba(0, 0, 0, .1), rgba(0, 0, 0, .3))));padding:12px;z-index:10000;min-width:200px;max-width:280px;max-height:350px;display:flex;flex-direction:column;font-family:var(--tbw-font-family, system-ui, sans-serif);font-size:var(--tbw-font-size, 13px)}.tbw-filter-search{margin-bottom:8px}.tbw-filter-search-input{width:100%;padding:6px 10px;background:var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));color:inherit;border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-input-radius, 4px);font-size:inherit;box-sizing:border-box}.tbw-filter-search-input:focus{outline:none;border-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));box-shadow:0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%)}.tbw-filter-actions{display:flex;padding:4px 2px;margin-bottom:8px;border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-action-btn{background:transparent;border:none;color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));cursor:pointer;font-size:12px;padding:2px 0}.tbw-filter-action-btn:hover{text-decoration:underline}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:8px;max-height:180px;position:relative}.tbw-filter-values-spacer{width:1px}.tbw-filter-values-content{position:absolute;top:0;left:0;right:0}.tbw-filter-value-item{display:flex;align-items:center;gap:8px;padding:4px 2px;cursor:pointer;border-radius:3px}.tbw-filter-value-item:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-checkbox{margin:0;cursor:pointer;accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}.tbw-filter-no-match{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding:8px 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:6px 12px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));color:var(--tbw-filter-accent-fg, var(--tbw-color-accent-fg, light-dark(#ffffff, #000000)));border:none;border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:6px 12px;background:transparent;color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}";class v extends M.BaseGridPlugin{name="filtering";version="1.0.0";get defaultConfig(){return{debounceMs:300,caseSensitive:!1,trimInput:!0,useWorker:!0}}filters=new Map;cachedResult=null;cacheKey=null;openPanelField=null;panelElement=null;searchText=new Map;excludedValues=new Map;panelAbortController=null;globalStylesInjected=!1;static LIST_ITEM_HEIGHT=28;static LIST_OVERSCAN=3;static LIST_BYPASS_THRESHOLD=50;attach(e){super.attach(e),this.injectGlobalStyles()}detach(){this.filters.clear(),this.cachedResult=null,this.cacheKey=null,this.openPanelField=null,this.panelElement&&(this.panelElement.remove(),this.panelElement=null),this.searchText.clear(),this.excludedValues.clear(),this.panelAbortController?.abort(),this.panelAbortController=null}processRows(e){const t=[...this.filters.values()];if(!t.length)return[...e];if(this.config.filterHandler)return this.cachedResult?this.cachedResult:[...e];const r=z(t);if(this.cacheKey===r&&this.cachedResult)return this.cachedResult;const l=q([...e],t,this.config.caseSensitive);return this.cachedResult=l,this.cacheKey=r,l}afterRender(){const e=this.shadowRoot;if(!e)return;e.querySelectorAll('[part~="header-cell"]').forEach(r=>{const l=r.getAttribute("data-col");if(l===null)return;const n=this.visibleColumns[parseInt(l,10)];if(!n||n.filterable===!1)return;const a=n.field;if(!a)return;const d=this.filters.has(a);let o=r.querySelector(".tbw-filter-btn");if(o){o.classList.toggle("active",d),r.classList.toggle("filtered",d);return}o=document.createElement("button"),o.className="tbw-filter-btn",o.setAttribute("aria-label",`Filter ${n.header??a}`),o.innerHTML='<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>',d&&(o.classList.add("active"),r.classList.add("filtered")),o.addEventListener("click",p=>{p.stopPropagation(),this.toggleFilterPanel(a,n,o)}),r.appendChild(o)})}setFilter(e,t){t===null?(this.filters.delete(e),this.excludedValues.delete(e)):(this.filters.set(e,{...t,field:e}),t.type==="set"&&t.operator==="notIn"&&Array.isArray(t.value)?this.excludedValues.set(e,new Set(t.value)):t.type==="set"&&this.excludedValues.delete(e)),this.cachedResult=null,this.cacheKey=null,this.emit("filter-change",{filters:[...this.filters.values()],filteredRowCount:0}),this.requestRender()}getFilter(e){return this.filters.get(e)}getFilters(){return[...this.filters.values()]}getFilterModel(){return this.getFilters()}setFilterModel(e){this.filters.clear(),this.excludedValues.clear();for(const t of e)this.filters.set(t.field,t),t.type==="set"&&t.operator==="notIn"&&Array.isArray(t.value)&&this.excludedValues.set(t.field,new Set(t.value));this.cachedResult=null,this.cacheKey=null,this.emit("filter-change",{filters:[...this.filters.values()],filteredRowCount:0}),this.requestRender()}clearAllFilters(){this.filters.clear(),this.excludedValues.clear(),this.searchText.clear(),this.applyFiltersInternal()}clearFieldFilter(e){this.filters.delete(e),this.excludedValues.delete(e),this.searchText.delete(e),this.applyFiltersInternal()}isFieldFiltered(e){return this.filters.has(e)}getFilteredRowCount(){return this.cachedResult?.length??this.rows.length}getActiveFilters(){return this.getFilters()}getUniqueValues(e){return A(this.sourceRows,e)}injectGlobalStyles(){if(this.globalStylesInjected)return;if(document.getElementById("tbw-filter-panel-styles")){this.globalStylesInjected=!0;return}const e=document.createElement("style");e.id="tbw-filter-panel-styles",e.textContent=G,document.head.appendChild(e),this.globalStylesInjected=!0}toggleFilterPanel(e,t,r){if(this.openPanelField===e){this.closeFilterPanel();return}this.closeFilterPanel();const l=document.createElement("div");if(l.className="tbw-filter-panel",this.panelElement=l,this.openPanelField=e,this.config.valuesHandler){l.innerHTML='<div class="tbw-filter-loading">Loading...</div>',document.body.appendChild(l),this.positionPanel(l,r),this.setupPanelCloseHandler(l,r),this.config.valuesHandler(e,t).then(a=>{this.openPanelField!==e||!this.panelElement||(l.innerHTML="",this.renderPanelContent(e,t,l,a))});return}const n=A(this.sourceRows,e);this.renderPanelContent(e,t,l,n),document.body.appendChild(l),this.positionPanel(l,r),this.setupPanelCloseHandler(l,r)}renderPanelContent(e,t,r,l){let n=this.excludedValues.get(e);n||(n=new Set,this.excludedValues.set(e,n));const a=this.searchText.get(e)??"",d={field:e,column:t,uniqueValues:l,excludedValues:n,searchText:a,applySetFilter:p=>{this.applySetFilter(e,p),this.closeFilterPanel()},applyTextFilter:(p,b,S)=>{this.applyTextFilter(e,p,b,S),this.closeFilterPanel()},clearFilter:()=>{this.clearFieldFilter(e),this.closeFilterPanel()},closePanel:()=>this.closeFilterPanel()};let o=!1;this.config.filterPanelRenderer&&(this.config.filterPanelRenderer(r,d),o=r.children.length>0),o||this.renderDefaultFilterPanel(r,d,l,n)}setupPanelCloseHandler(e,t){this.panelAbortController=new AbortController,setTimeout(()=>{document.addEventListener("click",r=>{!e.contains(r.target)&&r.target!==t&&this.closeFilterPanel()},{signal:this.panelAbortController?.signal})},0)}closeFilterPanel(){this.panelElement&&(this.panelElement.remove(),this.panelElement=null),this.openPanelField=null,this.panelAbortController?.abort(),this.panelAbortController=null}positionPanel(e,t){const r=t.getBoundingClientRect();e.style.position="fixed",e.style.top=`${r.bottom+4}px`,e.style.left=`${r.left}px`,requestAnimationFrame(()=>{const l=e.getBoundingClientRect();l.right>window.innerWidth-8&&(e.style.left=`${window.innerWidth-l.width-8}px`),l.bottom>window.innerHeight-8&&(e.style.top=`${r.top-l.height-4}px`)})}renderDefaultFilterPanel(e,t,r,l){const{field:n}=t,a=document.createElement("div");a.className="tbw-filter-search";const d=document.createElement("input");d.type="text",d.placeholder="Search...",d.className="tbw-filter-search-input",d.value=this.searchText.get(n)??"",a.appendChild(d),e.appendChild(a);const o=document.createElement("div");o.className="tbw-filter-actions";const p=document.createElement("label");p.className="tbw-filter-value-item",p.style.padding="0",p.style.margin="0";const b=document.createElement("input");b.type="checkbox",b.className="tbw-filter-checkbox";const S=document.createElement("span");S.textContent="Select All",p.appendChild(b),p.appendChild(S),o.appendChild(p);const I=()=>{const i=[...y.values()],u=i.every(c=>c),h=i.every(c=>!c);b.checked=u,b.indeterminate=!u&&!h};b.addEventListener("change",()=>{const i=b.checked;for(const u of y.keys())y.set(u,i);I(),L()}),e.appendChild(o);const x=document.createElement("div");x.className="tbw-filter-values";const E=document.createElement("div");E.className="tbw-filter-values-spacer",x.appendChild(E);const f=document.createElement("div");f.className="tbw-filter-values-content",x.appendChild(f);const y=new Map;r.forEach(i=>{const u=i==null?"__null__":String(i);y.set(u,!l.has(i))}),I();let T=[];const P=(i,u)=>{const h=i==null?"(Blank)":String(i),c=i==null?"__null__":String(i),s=document.createElement("label");s.className="tbw-filter-value-item",s.style.position="absolute",s.style.top=`${u*v.LIST_ITEM_HEIGHT}px`,s.style.left="0",s.style.right="0",s.style.height=`${v.LIST_ITEM_HEIGHT}px`,s.style.boxSizing="border-box";const g=document.createElement("input");g.type="checkbox",g.className="tbw-filter-checkbox",g.checked=y.get(c)??!0,g.dataset.value=c,g.addEventListener("change",()=>{y.set(c,g.checked),I()});const V=document.createElement("span");return V.textContent=h,s.appendChild(g),s.appendChild(V),s},L=()=>{const i=T.length,u=x.clientHeight,h=x.scrollTop;if(E.style.height=`${i*v.LIST_ITEM_HEIGHT}px`,C.shouldBypassVirtualization(i,v.LIST_BYPASS_THRESHOLD/3)){f.innerHTML="",f.style.transform="translateY(0px)",T.forEach((s,g)=>{f.appendChild(P(s,g))});return}const c=C.computeVirtualWindow({totalRows:i,viewportHeight:u,scrollTop:h,rowHeight:v.LIST_ITEM_HEIGHT,overscan:v.LIST_OVERSCAN});f.style.transform=`translateY(${c.offsetY}px)`,f.innerHTML="";for(let s=c.start;s<c.end;s++)f.appendChild(P(T[s],s-c.start))},H=i=>{const u=i.toLowerCase();if(T=r.filter(h=>{const c=h==null?"(Blank)":String(h);return!i||c.toLowerCase().includes(u)}),T.length===0){E.style.height="0px",f.innerHTML="";const h=document.createElement("div");h.className="tbw-filter-no-match",h.textContent="No matching values",f.appendChild(h);return}L()};x.addEventListener("scroll",()=>{T.length>0&&L()},{passive:!0}),H(d.value),e.appendChild(x);let N;d.addEventListener("input",()=>{clearTimeout(N),N=setTimeout(()=>{this.searchText.set(n,d.value),H(d.value)},this.config.debounceMs??150)});const k=document.createElement("div");k.className="tbw-filter-buttons";const F=document.createElement("button");F.className="tbw-filter-apply-btn",F.textContent="Apply",F.addEventListener("click",()=>{const i=[];for(const[u,h]of y)if(!h)if(u==="__null__")i.push(null);else{const c=r.find(s=>String(s)===u);i.push(c!==void 0?c:u)}t.applySetFilter(i)}),k.appendChild(F);const R=document.createElement("button");R.className="tbw-filter-clear-btn",R.textContent="Clear Filter",R.addEventListener("click",()=>{t.clearFilter()}),k.appendChild(R),e.appendChild(k)}applySetFilter(e,t){this.excludedValues.set(e,new Set(t)),t.length===0?this.filters.delete(e):this.filters.set(e,{field:e,type:"set",operator:"notIn",value:t}),this.applyFiltersInternal()}applyTextFilter(e,t,r,l){this.filters.set(e,{field:e,type:"text",operator:t,value:r,valueTo:l}),this.applyFiltersInternal()}applyFiltersInternal(){this.cachedResult=null,this.cacheKey=null;const e=[...this.filters.values()];if(this.config.filterHandler){const t=this.grid;t.setAttribute("aria-busy","true");const r=this.config.filterHandler(e,this.sourceRows),l=n=>{t.removeAttribute("aria-busy"),this.cachedResult=n,this.grid.rows=n,this.emit("filter-change",{filters:e,filteredRowCount:n.length}),this.requestRender()};r&&typeof r.then=="function"?r.then(l):l(r);return}this.emit("filter-change",{filters:e,filteredRowCount:0}),this.requestRender()}getColumnState(e){const t=this.filters.get(e);if(t)return{filter:{type:t.type,operator:t.operator,value:t.value,valueTo:t.valueTo}}}applyColumnState(e,t){if(!t.filter){this.filters.delete(e);return}const r={field:e,type:t.filter.type,operator:t.filter.operator,value:t.filter.value,valueTo:t.filter.valueTo};this.filters.set(e,r),this.cachedResult=null,this.cacheKey=null}styles=B}w.FilteringPlugin=v,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(w,C){typeof exports=="object"&&typeof module<"u"?C(exports,require("../../core/internal/virtualization"),require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/virtualization","../../core/plugin/base-plugin"],C):(w=typeof globalThis<"u"?globalThis:w||self,C(w.TbwGridPlugin_filtering={},w.TbwGrid,w.TbwGrid))})(this,(function(w,C,M){"use strict";function _(m,e,t=!1){const r=m[e.field];if(e.operator==="blank")return r==null||r==="";if(e.operator==="notBlank")return r!=null&&r!=="";if(r==null)return!1;const l=String(r),n=t?l:l.toLowerCase(),a=t?String(e.value):String(e.value).toLowerCase();switch(e.operator){case"contains":return n.includes(a);case"notContains":return!n.includes(a);case"equals":return n===a;case"notEquals":return n!==a;case"startsWith":return n.startsWith(a);case"endsWith":return n.endsWith(a);case"lessThan":return Number(r)<Number(e.value);case"lessThanOrEqual":return Number(r)<=Number(e.value);case"greaterThan":return Number(r)>Number(e.value);case"greaterThanOrEqual":return Number(r)>=Number(e.value);case"between":return Number(r)>=Number(e.value)&&Number(r)<=Number(e.valueTo);case"in":return Array.isArray(e.value)&&e.value.includes(r);case"notIn":return Array.isArray(e.value)&&!e.value.includes(r);default:return!0}}function z(m,e,t=!1){return e.length?m.filter(r=>e.every(l=>_(r,l,t))):m}function q(m){return JSON.stringify(m.map(e=>({field:e.field,operator:e.operator,value:e.value,valueTo:e.valueTo})))}function A(m,e){const t=new Set;for(const r of m){const l=r[e];l!=null&&t.add(l)}return[...t].sort((r,l)=>typeof r=="number"&&typeof l=="number"?r-l:String(r).localeCompare(String(l)))}const B=':host .tbw-quick-filter-input{flex:1;max-width:300px;height:28px;padding:0 8px;border:1px solid var(--tbw-color-border);border-radius:var(--tbw-border-radius);background:var(--tbw-color-bg);color:var(--tbw-color-fg);font-size:13px}:host .tbw-quick-filter-input:focus{outline:none;border-color:var(--tbw-color-accent)}:host .header-cell.filtered:before{content:"";position:absolute;top:4px;right:4px;width:6px;height:6px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));border-radius:50%}:host .tbw-filter-btn{display:inline-flex;align-items:center;justify-content:center;background:transparent;border:none;cursor:pointer;padding:2px;margin-left:4px;opacity:.4;transition:opacity .15s;color:inherit;vertical-align:middle}:host .tbw-filter-btn:hover,:host .tbw-filter-btn.active{opacity:1}:host .tbw-filter-btn.active{color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}',G=".tbw-filter-panel{position:fixed;background:var(--tbw-filter-panel-bg, var(--tbw-color-panel-bg, light-dark(#eeeeee, #222222)));color:var(--tbw-filter-panel-fg, var(--tbw-color-fg, light-dark(#222222, #eeeeee)));border:1px solid var(--tbw-filter-panel-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-panel-radius, var(--tbw-border-radius, 4px));box-shadow:0 4px 16px var(--tbw-filter-panel-shadow, var(--tbw-color-shadow, light-dark(rgba(0, 0, 0, .1), rgba(0, 0, 0, .3))));padding:12px;z-index:10000;min-width:200px;max-width:280px;max-height:350px;display:flex;flex-direction:column;font-family:var(--tbw-font-family, system-ui, sans-serif);font-size:var(--tbw-font-size, 13px)}.tbw-filter-search{margin-bottom:8px}.tbw-filter-search-input{width:100%;padding:6px 10px;background:var(--tbw-filter-input-bg, var(--tbw-color-bg, transparent));color:inherit;border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:var(--tbw-filter-input-radius, 4px);font-size:inherit;box-sizing:border-box}.tbw-filter-search-input:focus{outline:none;border-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));box-shadow:0 0 0 2px rgba(from var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6)) r g b / 15%)}.tbw-filter-actions{display:flex;padding:4px 2px;margin-bottom:8px;border-bottom:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-action-btn{background:transparent;border:none;color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));cursor:pointer;font-size:12px;padding:2px 0}.tbw-filter-action-btn:hover{text-decoration:underline}.tbw-filter-values{flex:1;overflow-y:auto;margin-bottom:8px;max-height:180px;position:relative}.tbw-filter-values-spacer{width:1px}.tbw-filter-values-content{position:absolute;top:0;left:0;right:0}.tbw-filter-value-item{display:flex;align-items:center;gap:8px;padding:4px 2px;cursor:pointer;border-radius:3px}.tbw-filter-value-item:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}.tbw-filter-checkbox{margin:0;cursor:pointer;accent-color:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6))}.tbw-filter-no-match{color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));padding:8px 0;text-align:center;font-style:italic}.tbw-filter-buttons{display:flex;gap:8px;padding-top:8px;border-top:1px solid var(--tbw-filter-divider, var(--tbw-color-border, light-dark(#d0d0d4, #454545)))}.tbw-filter-apply-btn{flex:1;padding:6px 12px;background:var(--tbw-filter-accent, var(--tbw-color-accent, #3b82f6));color:var(--tbw-filter-accent-fg, var(--tbw-color-accent-fg, light-dark(#ffffff, #000000)));border:none;border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-apply-btn:hover{filter:brightness(.9)}.tbw-filter-clear-btn{flex:1;padding:6px 12px;background:transparent;color:var(--tbw-filter-muted, var(--tbw-color-fg-muted, light-dark(#555555, #aaaaaa)));border:1px solid var(--tbw-filter-input-border, var(--tbw-color-border, light-dark(#d0d0d4, #454545)));border-radius:4px;cursor:pointer;font-size:13px}.tbw-filter-clear-btn:hover{background:var(--tbw-filter-hover, var(--tbw-color-row-hover, light-dark(#f0f6ff, #1c1c1c)))}";class v extends M.BaseGridPlugin{name="filtering";version="1.0.0";get defaultConfig(){return{debounceMs:300,caseSensitive:!1,trimInput:!0,useWorker:!0}}filters=new Map;cachedResult=null;cacheKey=null;openPanelField=null;panelElement=null;searchText=new Map;excludedValues=new Map;panelAbortController=null;globalStylesInjected=!1;static LIST_ITEM_HEIGHT=28;static LIST_OVERSCAN=3;static LIST_BYPASS_THRESHOLD=50;attach(e){super.attach(e),this.injectGlobalStyles()}detach(){this.filters.clear(),this.cachedResult=null,this.cacheKey=null,this.openPanelField=null,this.panelElement&&(this.panelElement.remove(),this.panelElement=null),this.searchText.clear(),this.excludedValues.clear(),this.panelAbortController?.abort(),this.panelAbortController=null}processRows(e){const t=[...this.filters.values()];if(!t.length)return[...e];if(this.config.filterHandler)return this.cachedResult?this.cachedResult:[...e];const r=q(t);if(this.cacheKey===r&&this.cachedResult)return this.cachedResult;const l=z([...e],t,this.config.caseSensitive);return this.cachedResult=l,this.cacheKey=r,l}afterRender(){const e=this.shadowRoot;if(!e)return;e.querySelectorAll('[part~="header-cell"]').forEach(r=>{const l=r.getAttribute("data-col");if(l===null)return;const n=this.visibleColumns[parseInt(l,10)];if(!n||n.filterable===!1)return;const a=n.field;if(!a)return;const d=this.filters.has(a);let o=r.querySelector(".tbw-filter-btn");if(o){o.classList.toggle("active",d),r.classList.toggle("filtered",d);return}o=document.createElement("button"),o.className="tbw-filter-btn",o.setAttribute("aria-label",`Filter ${n.header??a}`),o.innerHTML='<svg viewBox="0 0 16 16" width="12" height="12"><path fill="currentColor" d="M6 10.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h7a.5.5 0 0 1 0 1h-7a.5.5 0 0 1-.5-.5zm-2-3a.5.5 0 0 1 .5-.5h11a.5.5 0 0 1 0 1h-11a.5.5 0 0 1-.5-.5z"/></svg>',d&&(o.classList.add("active"),r.classList.add("filtered")),o.addEventListener("click",f=>{f.stopPropagation(),this.toggleFilterPanel(a,n,o)});const p=r.querySelector(".resize-handle");p?r.insertBefore(o,p):r.appendChild(o)})}setFilter(e,t){t===null?(this.filters.delete(e),this.excludedValues.delete(e)):(this.filters.set(e,{...t,field:e}),t.type==="set"&&t.operator==="notIn"&&Array.isArray(t.value)?this.excludedValues.set(e,new Set(t.value)):t.type==="set"&&this.excludedValues.delete(e)),this.cachedResult=null,this.cacheKey=null,this.emit("filter-change",{filters:[...this.filters.values()],filteredRowCount:0}),this.requestRender()}getFilter(e){return this.filters.get(e)}getFilters(){return[...this.filters.values()]}getFilterModel(){return this.getFilters()}setFilterModel(e){this.filters.clear(),this.excludedValues.clear();for(const t of e)this.filters.set(t.field,t),t.type==="set"&&t.operator==="notIn"&&Array.isArray(t.value)&&this.excludedValues.set(t.field,new Set(t.value));this.cachedResult=null,this.cacheKey=null,this.emit("filter-change",{filters:[...this.filters.values()],filteredRowCount:0}),this.requestRender()}clearAllFilters(){this.filters.clear(),this.excludedValues.clear(),this.searchText.clear(),this.applyFiltersInternal()}clearFieldFilter(e){this.filters.delete(e),this.excludedValues.delete(e),this.searchText.delete(e),this.applyFiltersInternal()}isFieldFiltered(e){return this.filters.has(e)}getFilteredRowCount(){return this.cachedResult?.length??this.rows.length}getActiveFilters(){return this.getFilters()}getUniqueValues(e){return A(this.sourceRows,e)}injectGlobalStyles(){if(this.globalStylesInjected)return;if(document.getElementById("tbw-filter-panel-styles")){this.globalStylesInjected=!0;return}const e=document.createElement("style");e.id="tbw-filter-panel-styles",e.textContent=G,document.head.appendChild(e),this.globalStylesInjected=!0}toggleFilterPanel(e,t,r){if(this.openPanelField===e){this.closeFilterPanel();return}this.closeFilterPanel();const l=document.createElement("div");if(l.className="tbw-filter-panel",this.panelElement=l,this.openPanelField=e,this.config.valuesHandler){l.innerHTML='<div class="tbw-filter-loading">Loading...</div>',document.body.appendChild(l),this.positionPanel(l,r),this.setupPanelCloseHandler(l,r),this.config.valuesHandler(e,t).then(a=>{this.openPanelField!==e||!this.panelElement||(l.innerHTML="",this.renderPanelContent(e,t,l,a))});return}const n=A(this.sourceRows,e);this.renderPanelContent(e,t,l,n),document.body.appendChild(l),this.positionPanel(l,r),this.setupPanelCloseHandler(l,r)}renderPanelContent(e,t,r,l){let n=this.excludedValues.get(e);n||(n=new Set,this.excludedValues.set(e,n));const a=this.searchText.get(e)??"",d={field:e,column:t,uniqueValues:l,excludedValues:n,searchText:a,applySetFilter:p=>{this.applySetFilter(e,p),this.closeFilterPanel()},applyTextFilter:(p,f,S)=>{this.applyTextFilter(e,p,f,S),this.closeFilterPanel()},clearFilter:()=>{this.clearFieldFilter(e),this.closeFilterPanel()},closePanel:()=>this.closeFilterPanel()};let o=!1;this.config.filterPanelRenderer&&(this.config.filterPanelRenderer(r,d),o=r.children.length>0),o||this.renderDefaultFilterPanel(r,d,l,n)}setupPanelCloseHandler(e,t){this.panelAbortController=new AbortController,setTimeout(()=>{document.addEventListener("click",r=>{!e.contains(r.target)&&r.target!==t&&this.closeFilterPanel()},{signal:this.panelAbortController?.signal})},0)}closeFilterPanel(){this.panelElement&&(this.panelElement.remove(),this.panelElement=null),this.openPanelField=null,this.panelAbortController?.abort(),this.panelAbortController=null}positionPanel(e,t){const r=t.getBoundingClientRect();e.style.position="fixed",e.style.top=`${r.bottom+4}px`,e.style.left=`${r.left}px`,requestAnimationFrame(()=>{const l=e.getBoundingClientRect();l.right>window.innerWidth-8&&(e.style.left=`${window.innerWidth-l.width-8}px`),l.bottom>window.innerHeight-8&&(e.style.top=`${r.top-l.height-4}px`)})}renderDefaultFilterPanel(e,t,r,l){const{field:n}=t,a=document.createElement("div");a.className="tbw-filter-search";const d=document.createElement("input");d.type="text",d.placeholder="Search...",d.className="tbw-filter-search-input",d.value=this.searchText.get(n)??"",a.appendChild(d),e.appendChild(a);const o=document.createElement("div");o.className="tbw-filter-actions";const p=document.createElement("label");p.className="tbw-filter-value-item",p.style.padding="0",p.style.margin="0";const f=document.createElement("input");f.type="checkbox",f.className="tbw-filter-checkbox";const S=document.createElement("span");S.textContent="Select All",p.appendChild(f),p.appendChild(S),o.appendChild(p);const I=()=>{const i=[...y.values()],u=i.every(c=>c),h=i.every(c=>!c);f.checked=u,f.indeterminate=!u&&!h};f.addEventListener("change",()=>{const i=f.checked;for(const u of y.keys())y.set(u,i);I(),L()}),e.appendChild(o);const x=document.createElement("div");x.className="tbw-filter-values";const E=document.createElement("div");E.className="tbw-filter-values-spacer",x.appendChild(E);const b=document.createElement("div");b.className="tbw-filter-values-content",x.appendChild(b);const y=new Map;r.forEach(i=>{const u=i==null?"__null__":String(i);y.set(u,!l.has(i))}),I();let T=[];const H=(i,u)=>{const h=i==null?"(Blank)":String(i),c=i==null?"__null__":String(i),s=document.createElement("label");s.className="tbw-filter-value-item",s.style.position="absolute",s.style.top=`${u*v.LIST_ITEM_HEIGHT}px`,s.style.left="0",s.style.right="0",s.style.height=`${v.LIST_ITEM_HEIGHT}px`,s.style.boxSizing="border-box";const g=document.createElement("input");g.type="checkbox",g.className="tbw-filter-checkbox",g.checked=y.get(c)??!0,g.dataset.value=c,g.addEventListener("change",()=>{y.set(c,g.checked),I()});const V=document.createElement("span");return V.textContent=h,s.appendChild(g),s.appendChild(V),s},L=()=>{const i=T.length,u=x.clientHeight,h=x.scrollTop;if(E.style.height=`${i*v.LIST_ITEM_HEIGHT}px`,C.shouldBypassVirtualization(i,v.LIST_BYPASS_THRESHOLD/3)){b.innerHTML="",b.style.transform="translateY(0px)",T.forEach((s,g)=>{b.appendChild(H(s,g))});return}const c=C.computeVirtualWindow({totalRows:i,viewportHeight:u,scrollTop:h,rowHeight:v.LIST_ITEM_HEIGHT,overscan:v.LIST_OVERSCAN});b.style.transform=`translateY(${c.offsetY}px)`,b.innerHTML="";for(let s=c.start;s<c.end;s++)b.appendChild(H(T[s],s-c.start))},P=i=>{const u=i.toLowerCase();if(T=r.filter(h=>{const c=h==null?"(Blank)":String(h);return!i||c.toLowerCase().includes(u)}),T.length===0){E.style.height="0px",b.innerHTML="";const h=document.createElement("div");h.className="tbw-filter-no-match",h.textContent="No matching values",b.appendChild(h);return}L()};x.addEventListener("scroll",()=>{T.length>0&&L()},{passive:!0}),P(d.value),e.appendChild(x);let N;d.addEventListener("input",()=>{clearTimeout(N),N=setTimeout(()=>{this.searchText.set(n,d.value),P(d.value)},this.config.debounceMs??150)});const k=document.createElement("div");k.className="tbw-filter-buttons";const F=document.createElement("button");F.className="tbw-filter-apply-btn",F.textContent="Apply",F.addEventListener("click",()=>{const i=[];for(const[u,h]of y)if(!h)if(u==="__null__")i.push(null);else{const c=r.find(s=>String(s)===u);i.push(c!==void 0?c:u)}t.applySetFilter(i)}),k.appendChild(F);const R=document.createElement("button");R.className="tbw-filter-clear-btn",R.textContent="Clear Filter",R.addEventListener("click",()=>{t.clearFilter()}),k.appendChild(R),e.appendChild(k)}applySetFilter(e,t){this.excludedValues.set(e,new Set(t)),t.length===0?this.filters.delete(e):this.filters.set(e,{field:e,type:"set",operator:"notIn",value:t}),this.applyFiltersInternal()}applyTextFilter(e,t,r,l){this.filters.set(e,{field:e,type:"text",operator:t,value:r,valueTo:l}),this.applyFiltersInternal()}applyFiltersInternal(){this.cachedResult=null,this.cacheKey=null;const e=[...this.filters.values()];if(this.config.filterHandler){const t=this.grid;t.setAttribute("aria-busy","true");const r=this.config.filterHandler(e,this.sourceRows),l=n=>{t.removeAttribute("aria-busy"),this.cachedResult=n,this.grid.rows=n,this.emit("filter-change",{filters:e,filteredRowCount:n.length}),this.requestRender()};r&&typeof r.then=="function"?r.then(l):l(r);return}this.emit("filter-change",{filters:e,filteredRowCount:0}),this.requestRender()}getColumnState(e){const t=this.filters.get(e);if(t)return{filter:{type:t.type,operator:t.operator,value:t.value,valueTo:t.valueTo}}}applyColumnState(e,t){if(!t.filter){this.filters.delete(e);return}const r={field:e,type:t.filter.type,operator:t.filter.operator,value:t.filter.value,valueTo:t.filter.valueTo};this.filters.set(e,r),this.cachedResult=null,this.cacheKey=null}styles=B}w.FilteringPlugin=v,Object.defineProperty(w,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=filtering.umd.js.map