@toolbox-web/grid 1.6.0 → 1.6.2

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 (49) hide show
  1. package/all.js +561 -471
  2. package/all.js.map +1 -1
  3. package/index.js +3 -3
  4. package/index.js.map +1 -1
  5. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts +11 -0
  6. package/lib/plugins/context-menu/ContextMenuPlugin.d.ts.map +1 -1
  7. package/lib/plugins/context-menu/index.js +136 -77
  8. package/lib/plugins/context-menu/index.js.map +1 -1
  9. package/lib/plugins/filtering/FilteringPlugin.d.ts.map +1 -1
  10. package/lib/plugins/filtering/index.js +246 -250
  11. package/lib/plugins/filtering/index.js.map +1 -1
  12. package/lib/plugins/multi-sort/index.js +11 -11
  13. package/lib/plugins/pivot/PivotPlugin.d.ts +2 -0
  14. package/lib/plugins/pivot/PivotPlugin.d.ts.map +1 -1
  15. package/lib/plugins/pivot/index.js +29 -27
  16. package/lib/plugins/pivot/index.js.map +1 -1
  17. package/lib/plugins/print/index.js +1 -1
  18. package/lib/plugins/print/index.js.map +1 -1
  19. package/lib/plugins/row-reorder/RowReorderPlugin.d.ts +18 -0
  20. package/lib/plugins/row-reorder/RowReorderPlugin.d.ts.map +1 -1
  21. package/lib/plugins/row-reorder/index.js +132 -72
  22. package/lib/plugins/row-reorder/index.js.map +1 -1
  23. package/lib/plugins/selection/index.js +1 -1
  24. package/lib/plugins/visibility/index.js +7 -7
  25. package/package.json +1 -1
  26. package/themes/dg-theme-bootstrap.css +60 -33
  27. package/themes/dg-theme-material.css +83 -52
  28. package/themes/dg-theme-standard.css +80 -12
  29. package/themes/dg-theme-vibrant.css +78 -9
  30. package/umd/grid.all.umd.js +17 -17
  31. package/umd/grid.all.umd.js.map +1 -1
  32. package/umd/grid.umd.js +1 -1
  33. package/umd/grid.umd.js.map +1 -1
  34. package/umd/plugins/context-menu.umd.js +1 -1
  35. package/umd/plugins/context-menu.umd.js.map +1 -1
  36. package/umd/plugins/filtering.umd.js +1 -1
  37. package/umd/plugins/filtering.umd.js.map +1 -1
  38. package/umd/plugins/multi-sort.umd.js +1 -1
  39. package/umd/plugins/multi-sort.umd.js.map +1 -1
  40. package/umd/plugins/pivot.umd.js +1 -1
  41. package/umd/plugins/pivot.umd.js.map +1 -1
  42. package/umd/plugins/print.umd.js +1 -1
  43. package/umd/plugins/print.umd.js.map +1 -1
  44. package/umd/plugins/row-reorder.umd.js +1 -1
  45. package/umd/plugins/row-reorder.umd.js.map +1 -1
  46. package/umd/plugins/selection.umd.js +1 -1
  47. package/umd/plugins/selection.umd.js.map +1 -1
  48. package/umd/plugins/visibility.umd.js +1 -1
  49. package/umd/plugins/visibility.umd.js.map +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"print.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/print/print-isolated.ts","../../../../../libs/grid/src/lib/plugins/print/PrintPlugin.ts"],"sourcesContent":["/**\n * Utility for printing a grid in isolation by hiding all other page content.\n *\n * This approach keeps the grid in place (with virtualization disabled by PrintPlugin)\n * and uses CSS to hide everything else on the page during printing.\n */\n\nimport type { PrintOrientation } from './types';\n\nexport interface PrintIsolatedOptions {\n /** Page orientation hint */\n orientation?: PrintOrientation;\n}\n\n/** ID for the isolation stylesheet */\nconst ISOLATION_STYLE_ID = 'tbw-print-isolation-style';\n\n/**\n * Create a stylesheet that hides everything except the target grid.\n * Uses the grid's ID to target it specifically.\n */\nfunction createIsolationStylesheet(gridId: string, orientation: PrintOrientation): HTMLStyleElement {\n const style = document.createElement('style');\n style.id = ISOLATION_STYLE_ID;\n style.textContent = `\n /* Print isolation: hide everything except the target grid */\n @media print {\n /* Hide all body children by default */\n body > *:not(#${gridId}) {\n display: none !important;\n }\n\n /* But show the grid and ensure it's not hidden by ancestor rules */\n #${gridId} {\n display: block !important;\n position: static !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n width: 100% !important;\n max-height: none !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n }\n\n /* If grid is nested, we need to show its ancestors too */\n #${gridId},\n #${gridId} * {\n visibility: visible !important;\n }\n\n /* Walk up the DOM and show all ancestors of the grid */\n body *:has(> #${gridId}),\n body *:has(#${gridId}) {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n position: static !important;\n transform: none !important;\n background: transparent !important;\n border: none !important;\n padding: 0 !important;\n margin: 0 !important;\n }\n\n /* Hide siblings of ancestors (everything that's not in the path to the grid) */\n body *:has(#${gridId}) > *:not(:has(#${gridId})):not(#${gridId}) {\n display: none !important;\n }\n\n /* Page settings */\n @page {\n size: ${orientation};\n margin: 1cm;\n }\n\n /* Ensure proper print styling */\n body {\n margin: 0 !important;\n padding: 0 !important;\n background: white !important;\n color-scheme: light !important;\n }\n }\n\n /* Screen: also apply isolation for print preview */\n @media screen {\n /* When this stylesheet is active, we're about to print */\n /* No screen-specific rules needed - isolation only applies to print */\n }\n `;\n return style;\n}\n\n/**\n * Print a grid in isolation by hiding all other page content.\n *\n * This function adds a temporary stylesheet that uses CSS to hide everything\n * on the page except the target grid during printing. The grid stays in place\n * with all its data (virtualization should be disabled separately).\n *\n * @param gridElement - The tbw-grid element to print (must have an ID)\n * @param options - Optional configuration\n * @returns Promise that resolves when the print dialog closes\n *\n * @example\n * ```typescript\n * import { printGridIsolated } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = document.querySelector('tbw-grid');\n * await printGridIsolated(grid, { orientation: 'landscape' });\n * ```\n */\nexport async function printGridIsolated(gridElement: HTMLElement, options: PrintIsolatedOptions = {}): Promise<void> {\n const { orientation = 'landscape' } = options;\n\n const gridId = gridElement.id;\n\n // Warn if multiple elements share this ID (user-set IDs could collide)\n const elementsWithId = document.querySelectorAll(`#${CSS.escape(gridId)}`);\n if (elementsWithId.length > 1) {\n console.warn(\n `[tbw-grid:print] Multiple elements found with id=\"${gridId}\". ` +\n `Print isolation may not work correctly. Ensure each grid has a unique ID.`,\n );\n }\n\n // Remove any existing isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n\n // Add the isolation stylesheet\n const isolationStyle = createIsolationStylesheet(gridId, orientation);\n document.head.appendChild(isolationStyle);\n\n return new Promise((resolve) => {\n // Listen for afterprint event to cleanup\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n // Remove isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n window.removeEventListener('afterprint', onAfterPrint);\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n }, 5000);\n });\n}\n","/**\n * Print Plugin (Class-based)\n *\n * Provides print layout functionality for tbw-grid.\n * Temporarily disables virtualization to render all rows and uses\n * @media print CSS for print-optimized styling.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { InternalGrid, ToolbarContentDefinition } from '../../core/types';\nimport { printGridIsolated } from './print-isolated';\nimport styles from './print.css?inline';\nimport type { PrintCompleteDetail, PrintConfig, PrintParams, PrintStartDetail } from './types';\n\n/**\n * Extended grid interface for PrintPlugin internal access.\n * Includes registerToolbarContent which is available on the grid class\n * but not exposed in the standard plugin API.\n */\ninterface PrintGridRef extends InternalGrid {\n registerToolbarContent?(content: ToolbarContentDefinition): void;\n unregisterToolbarContent?(contentId: string): void;\n}\n\n/** Default configuration */\nconst DEFAULT_CONFIG: Required<PrintConfig> = {\n button: false,\n orientation: 'landscape',\n warnThreshold: 500,\n maxRows: 0,\n includeTitle: true,\n includeTimestamp: true,\n title: '',\n isolate: false,\n};\n\n/**\n * Print Plugin for tbw-grid\n *\n * Enables printing the full grid content by temporarily disabling virtualization\n * and applying print-optimized styles. Handles large datasets gracefully with\n * configurable row limits.\n *\n * ## Installation\n *\n * ```ts\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `button` | `boolean` | `false` | Show print button in toolbar |\n * | `orientation` | `'portrait' \\| 'landscape'` | `'landscape'` | Page orientation |\n * | `warnThreshold` | `number` | `500` | Show confirmation dialog when rows exceed this (0 = no warning) |\n * | `maxRows` | `number` | `0` | Hard limit on printed rows (0 = unlimited) |\n * | `includeTitle` | `boolean` | `true` | Include grid title in print |\n * | `includeTimestamp` | `boolean` | `true` | Include timestamp in footer |\n * | `title` | `string` | `''` | Custom print title |\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `print` | `(params?) => Promise<void>` | Trigger print dialog |\n * | `isPrinting` | `() => boolean` | Check if print is in progress |\n *\n * ## Events\n *\n * | Event | Detail | Description |\n * |-------|--------|-------------|\n * | `print-start` | `PrintStartDetail` | Fired when print begins |\n * | `print-complete` | `PrintCompleteDetail` | Fired when print completes |\n *\n * @example Basic Print\n * ```ts\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = document.querySelector('tbw-grid');\n * grid.gridConfig = {\n * plugins: [new PrintPlugin()],\n * };\n *\n * // Trigger print\n * const printPlugin = grid.getPlugin(PrintPlugin);\n * await printPlugin.print();\n * ```\n *\n * @example With Toolbar Button\n * ```ts\n * grid.gridConfig = {\n * plugins: [new PrintPlugin({ button: true, orientation: 'landscape' })],\n * };\n * ```\n *\n * @see {@link PrintConfig} for all configuration options\n */\nexport class PrintPlugin extends BaseGridPlugin<PrintConfig> {\n /** @internal */\n readonly name = 'print';\n\n /** @internal */\n override readonly version = '1.0.0';\n\n /** CSS styles for print mode */\n override readonly styles = styles;\n\n /** Current print state */\n #printing = false;\n\n /** Saved column visibility state */\n #savedHiddenColumns: Map<string, boolean> | null = null;\n\n /** Saved virtualization state */\n #savedVirtualization: { bypassThreshold: number } | null = null;\n\n /** Saved rows when maxRows limit is applied */\n #savedRows: unknown[] | null = null;\n\n /** Print header element */\n #printHeader: HTMLElement | null = null;\n\n /** Print footer element */\n #printFooter: HTMLElement | null = null;\n\n /** Applied scale factor (legacy, used for cleanup) */\n #appliedScale: number | null = null;\n\n /**\n * Get the grid typed as PrintGridRef for internal access.\n */\n get #internalGrid(): PrintGridRef {\n return this.grid as unknown as PrintGridRef;\n }\n\n /**\n * Check if print is currently in progress\n */\n isPrinting(): boolean {\n return this.#printing;\n }\n\n /**\n * Trigger the browser print dialog\n *\n * This method:\n * 1. Validates row count against maxRows limit\n * 2. Disables virtualization to render all rows\n * 3. Applies print-specific CSS classes\n * 4. Opens the browser print dialog (or isolated window if `isolate: true`)\n * 5. Restores normal state after printing\n *\n * @param params - Optional parameters to override config for this print\n * @param params.isolate - If true, prints in an isolated window containing only the grid\n * @returns Promise that resolves when print dialog closes\n */\n async print(params?: PrintParams): Promise<void> {\n if (this.#printing) {\n console.warn('[PrintPlugin] Print already in progress');\n return;\n }\n\n const grid = this.gridElement;\n if (!grid) {\n console.warn('[PrintPlugin] Grid not available');\n return;\n }\n\n const config = { ...DEFAULT_CONFIG, ...this.config, ...params };\n const rows = this.rows;\n const originalRowCount = rows.length;\n let rowCount = originalRowCount;\n let limitApplied = false;\n\n // Check if we should warn about large datasets\n if (config.warnThreshold > 0 && originalRowCount > config.warnThreshold) {\n const limitInfo =\n config.maxRows > 0 ? `\\n\\nNote: Output will be limited to ${config.maxRows.toLocaleString()} rows.` : '';\n const proceed = confirm(\n `This grid has ${originalRowCount.toLocaleString()} rows. ` +\n `Printing large datasets may cause performance issues or browser slowdowns.${limitInfo}\\n\\n` +\n `Click OK to continue, or Cancel to abort.`,\n );\n if (!proceed) {\n return;\n }\n }\n\n // Apply hard row limit if configured\n if (config.maxRows > 0 && originalRowCount > config.maxRows) {\n rowCount = config.maxRows;\n limitApplied = true;\n }\n\n this.#printing = true;\n\n // Track timing for duration reporting\n const startTime = performance.now();\n\n // Emit print-start event\n this.emit<PrintStartDetail>('print-start', {\n rowCount,\n limitApplied,\n originalRowCount,\n });\n\n try {\n // Save current virtualization state\n const internalGrid = this.#internalGrid;\n this.#savedVirtualization = {\n bypassThreshold: internalGrid._virtualization?.bypassThreshold ?? 24,\n };\n\n // Hide columns marked with printHidden\n this.#hidePrintColumns();\n\n // Apply row limit if configured\n if (limitApplied) {\n this.#savedRows = this.sourceRows;\n // Set limited rows on the grid\n (this.grid as unknown as { rows: unknown[] }).rows = this.sourceRows.slice(0, rowCount);\n // Wait for grid to process new rows\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Add print header if configured\n if (config.includeTitle || config.includeTimestamp) {\n this.#addPrintHeader(config);\n }\n\n // Disable virtualization to render all rows\n // This forces the grid to render all rows in the DOM\n await this.#disableVirtualization();\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Add orientation class for @page rules\n grid.classList.add(`print-${config.orientation}`);\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Trigger browser print dialog (isolated or inline)\n if (config.isolate) {\n await this.#printInIsolatedWindow(config);\n } else {\n await this.#triggerPrint();\n }\n\n // Emit print-complete event\n this.emit<PrintCompleteDetail>('print-complete', {\n success: true,\n rowCount,\n duration: Math.round(performance.now() - startTime),\n });\n } catch (error) {\n console.error('[PrintPlugin] Print failed:', error);\n this.emit<PrintCompleteDetail>('print-complete', {\n success: false,\n rowCount: 0,\n duration: Math.round(performance.now() - startTime),\n });\n } finally {\n // Restore normal state\n this.#cleanup();\n this.#printing = false;\n }\n }\n\n /**\n * Add print header with title and timestamp\n */\n #addPrintHeader(config: Required<PrintConfig>): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Create print header\n this.#printHeader = document.createElement('div');\n this.#printHeader.className = 'tbw-print-header';\n\n // Title\n if (config.includeTitle) {\n const title = config.title || this.grid.effectiveConfig?.shell?.header?.title || 'Grid Data';\n const titleEl = document.createElement('div');\n titleEl.className = 'tbw-print-header-title';\n titleEl.textContent = title;\n this.#printHeader.appendChild(titleEl);\n }\n\n // Timestamp\n if (config.includeTimestamp) {\n const timestampEl = document.createElement('div');\n timestampEl.className = 'tbw-print-header-timestamp';\n timestampEl.textContent = `Printed: ${new Date().toLocaleString()}`;\n this.#printHeader.appendChild(timestampEl);\n }\n\n // Insert at the beginning of the grid\n grid.insertBefore(this.#printHeader, grid.firstChild);\n\n // Create print footer\n this.#printFooter = document.createElement('div');\n this.#printFooter.className = 'tbw-print-footer';\n this.#printFooter.textContent = `Page generated from ${window.location.hostname}`;\n grid.appendChild(this.#printFooter);\n }\n\n /**\n * Disable virtualization to render all rows\n */\n async #disableVirtualization(): Promise<void> {\n const internalGrid = this.#internalGrid;\n if (!internalGrid._virtualization) return;\n\n // Set bypass threshold higher than total row count to disable virtualization\n // This makes the grid render all rows (up to maxRows) instead of just visible ones\n const totalRows = this.rows.length;\n internalGrid._virtualization.bypassThreshold = totalRows + 100;\n\n // Force a full refresh to re-render with virtualization disabled\n internalGrid.refreshVirtualWindow(true);\n\n // Wait for render to complete\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n /**\n * Trigger the browser print dialog\n */\n async #triggerPrint(): Promise<void> {\n return new Promise((resolve) => {\n // Listen for afterprint event\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n window.removeEventListener('afterprint', onAfterPrint);\n resolve();\n }, 1000);\n });\n }\n\n /**\n * Print in isolation by hiding all other page content.\n * This excludes navigation, sidebars, etc. while keeping the grid in place.\n */\n async #printInIsolatedWindow(config: Required<PrintConfig>): Promise<void> {\n const grid = this.gridElement;\n if (!grid) return;\n\n await printGridIsolated(grid, {\n orientation: config.orientation,\n });\n }\n\n /**\n * Hide columns marked with printHidden: true\n */\n #hidePrintColumns(): void {\n const columns = this.columns;\n if (!columns) return;\n\n // Save current hidden state and hide print columns\n this.#savedHiddenColumns = new Map();\n\n for (const col of columns) {\n if (col.printHidden && col.field) {\n // Save current visibility state (true = visible, false = hidden)\n this.#savedHiddenColumns.set(col.field, !col.hidden);\n // Hide the column for printing\n this.grid.setColumnVisible(col.field, false);\n }\n }\n }\n\n /**\n * Restore columns that were hidden for printing\n */\n #restorePrintColumns(): void {\n if (!this.#savedHiddenColumns) return;\n\n for (const [field, wasVisible] of this.#savedHiddenColumns) {\n // Restore original visibility\n this.grid.setColumnVisible(field, wasVisible);\n }\n\n this.#savedHiddenColumns = null;\n }\n\n /**\n * Cleanup after printing\n */\n #cleanup(): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Restore columns that were hidden for printing\n this.#restorePrintColumns();\n\n // Remove orientation classes (both original and possibly switched)\n grid.classList.remove('print-portrait', 'print-landscape');\n\n // Remove scaling transform if applied (legacy)\n if (this.#appliedScale !== null) {\n grid.style.transform = '';\n grid.style.transformOrigin = '';\n grid.style.width = '';\n this.#appliedScale = null;\n }\n\n // Remove print header/footer\n if (this.#printHeader) {\n this.#printHeader.remove();\n this.#printHeader = null;\n }\n if (this.#printFooter) {\n this.#printFooter.remove();\n this.#printFooter = null;\n }\n\n // Restore virtualization\n const internalGrid = this.#internalGrid;\n if (this.#savedVirtualization && internalGrid._virtualization) {\n internalGrid._virtualization.bypassThreshold = this.#savedVirtualization.bypassThreshold;\n internalGrid.refreshVirtualWindow(true);\n this.#savedVirtualization = null;\n }\n\n // Restore original rows if they were limited\n if (this.#savedRows !== null) {\n (this.grid as unknown as { rows: unknown[] }).rows = this.#savedRows;\n this.#savedRows = null;\n }\n }\n\n /**\n * Register toolbar button if configured\n * @internal\n */\n override afterRender(): void {\n // Register toolbar on first render when button is enabled\n if (this.config?.button && !this.#toolbarRegistered) {\n this.#registerToolbarButton();\n this.#toolbarRegistered = true;\n }\n }\n\n /** Track if toolbar button is registered */\n #toolbarRegistered = false;\n\n /**\n * Register print button in toolbar\n */\n #registerToolbarButton(): void {\n const grid = this.#internalGrid;\n\n // Register toolbar content\n grid.registerToolbarContent?.({\n id: 'print-button',\n order: 900, // High order to appear at the end\n render: (container: HTMLElement) => {\n const button = document.createElement('button');\n button.className = 'tbw-toolbar-btn tbw-print-btn';\n button.title = 'Print grid';\n button.type = 'button';\n\n // Use print icon\n const icon = this.resolveIcon('print') || '🖨️';\n this.setIcon(button, icon);\n\n button.addEventListener(\n 'click',\n () => {\n this.print();\n },\n { signal: this.disconnectSignal },\n );\n\n container.appendChild(button);\n },\n });\n }\n}\n"],"names":["ISOLATION_STYLE_ID","createIsolationStylesheet","gridId","orientation","style","printGridIsolated","gridElement","options","isolationStyle","resolve","onAfterPrint","DEFAULT_CONFIG","PrintPlugin","BaseGridPlugin","styles","#printing","#savedHiddenColumns","#savedVirtualization","#savedRows","#printHeader","#printFooter","#appliedScale","#internalGrid","params","grid","config","originalRowCount","rowCount","limitApplied","limitInfo","startTime","internalGrid","#hidePrintColumns","#addPrintHeader","#disableVirtualization","#printInIsolatedWindow","#triggerPrint","error","#cleanup","title","titleEl","timestampEl","totalRows","columns","col","#restorePrintColumns","field","wasVisible","#toolbarRegistered","#registerToolbarButton","container","button","icon"],"mappings":"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"}
1
+ {"version":3,"file":"print.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/print/print-isolated.ts","../../../../../libs/grid/src/lib/plugins/print/PrintPlugin.ts"],"sourcesContent":["/**\n * Utility for printing a grid in isolation by hiding all other page content.\n *\n * This approach keeps the grid in place (with virtualization disabled by PrintPlugin)\n * and uses CSS to hide everything else on the page during printing.\n */\n\nimport type { PrintOrientation } from './types';\n\nexport interface PrintIsolatedOptions {\n /** Page orientation hint */\n orientation?: PrintOrientation;\n}\n\n/** ID for the isolation stylesheet */\nconst ISOLATION_STYLE_ID = 'tbw-print-isolation-style';\n\n/**\n * Create a stylesheet that hides everything except the target grid.\n * Uses the grid's ID to target it specifically.\n */\nfunction createIsolationStylesheet(gridId: string, orientation: PrintOrientation): HTMLStyleElement {\n const style = document.createElement('style');\n style.id = ISOLATION_STYLE_ID;\n style.textContent = `\n /* Print isolation: hide everything except the target grid */\n @media print {\n /* Hide all body children by default */\n body > *:not(#${gridId}) {\n display: none !important;\n }\n\n /* But show the grid and ensure it's not hidden by ancestor rules */\n #${gridId} {\n display: block !important;\n position: static !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n width: 100% !important;\n max-height: none !important;\n margin: 0 !important;\n padding: 0 !important;\n transform: none !important;\n }\n\n /* If grid is nested, we need to show its ancestors too */\n #${gridId},\n #${gridId} * {\n visibility: visible !important;\n }\n\n /* Walk up the DOM and show all ancestors of the grid */\n body *:has(> #${gridId}),\n body *:has(#${gridId}) {\n display: block !important;\n visibility: visible !important;\n opacity: 1 !important;\n overflow: visible !important;\n height: auto !important;\n position: static !important;\n transform: none !important;\n background: transparent !important;\n border: none !important;\n padding: 0 !important;\n margin: 0 !important;\n }\n\n /* Hide siblings of ancestors (everything that's not in the path to the grid) */\n body *:has(#${gridId}) > *:not(:has(#${gridId})):not(#${gridId}) {\n display: none !important;\n }\n\n /* Page settings */\n @page {\n size: ${orientation};\n margin: 1cm;\n }\n\n /* Ensure proper print styling */\n body {\n margin: 0 !important;\n padding: 0 !important;\n background: white !important;\n color-scheme: light !important;\n }\n }\n\n /* Screen: also apply isolation for print preview */\n @media screen {\n /* When this stylesheet is active, we're about to print */\n /* No screen-specific rules needed - isolation only applies to print */\n }\n `;\n return style;\n}\n\n/**\n * Print a grid in isolation by hiding all other page content.\n *\n * This function adds a temporary stylesheet that uses CSS to hide everything\n * on the page except the target grid during printing. The grid stays in place\n * with all its data (virtualization should be disabled separately).\n *\n * @param gridElement - The tbw-grid element to print (must have an ID)\n * @param options - Optional configuration\n * @returns Promise that resolves when the print dialog closes\n *\n * @example\n * ```typescript\n * import { printGridIsolated } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = document.querySelector('tbw-grid');\n * await printGridIsolated(grid, { orientation: 'landscape' });\n * ```\n */\nexport async function printGridIsolated(gridElement: HTMLElement, options: PrintIsolatedOptions = {}): Promise<void> {\n const { orientation = 'landscape' } = options;\n\n const gridId = gridElement.id;\n\n // Warn if multiple elements share this ID (user-set IDs could collide)\n const elementsWithId = document.querySelectorAll(`#${CSS.escape(gridId)}`);\n if (elementsWithId.length > 1) {\n console.warn(\n `[tbw-grid:print] Multiple elements found with id=\"${gridId}\". ` +\n `Print isolation may not work correctly. Ensure each grid has a unique ID.`,\n );\n }\n\n // Remove any existing isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n\n // Add the isolation stylesheet\n const isolationStyle = createIsolationStylesheet(gridId, orientation);\n document.head.appendChild(isolationStyle);\n\n return new Promise((resolve) => {\n // Listen for afterprint event to cleanup\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n // Remove isolation stylesheet\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n window.removeEventListener('afterprint', onAfterPrint);\n document.getElementById(ISOLATION_STYLE_ID)?.remove();\n resolve();\n }, 5000);\n });\n}\n","/**\n * Print Plugin (Class-based)\n *\n * Provides print layout functionality for tbw-grid.\n * Temporarily disables virtualization to render all rows and uses\n * @media print CSS for print-optimized styling.\n */\n\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { InternalGrid, ToolbarContentDefinition } from '../../core/types';\nimport { printGridIsolated } from './print-isolated';\nimport styles from './print.css?inline';\nimport type { PrintCompleteDetail, PrintConfig, PrintParams, PrintStartDetail } from './types';\n\n/**\n * Extended grid interface for PrintPlugin internal access.\n * Includes registerToolbarContent which is available on the grid class\n * but not exposed in the standard plugin API.\n */\ninterface PrintGridRef extends InternalGrid {\n registerToolbarContent?(content: ToolbarContentDefinition): void;\n unregisterToolbarContent?(contentId: string): void;\n}\n\n/** Default configuration */\nconst DEFAULT_CONFIG: Required<PrintConfig> = {\n button: false,\n orientation: 'landscape',\n warnThreshold: 500,\n maxRows: 0,\n includeTitle: true,\n includeTimestamp: true,\n title: '',\n isolate: false,\n};\n\n/**\n * Print Plugin for tbw-grid\n *\n * Enables printing the full grid content by temporarily disabling virtualization\n * and applying print-optimized styles. Handles large datasets gracefully with\n * configurable row limits.\n *\n * ## Installation\n *\n * ```ts\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `button` | `boolean` | `false` | Show print button in toolbar |\n * | `orientation` | `'portrait' \\| 'landscape'` | `'landscape'` | Page orientation |\n * | `warnThreshold` | `number` | `500` | Show confirmation dialog when rows exceed this (0 = no warning) |\n * | `maxRows` | `number` | `0` | Hard limit on printed rows (0 = unlimited) |\n * | `includeTitle` | `boolean` | `true` | Include grid title in print |\n * | `includeTimestamp` | `boolean` | `true` | Include timestamp in footer |\n * | `title` | `string` | `''` | Custom print title |\n *\n * ## Programmatic API\n *\n * | Method | Signature | Description |\n * |--------|-----------|-------------|\n * | `print` | `(params?) => Promise<void>` | Trigger print dialog |\n * | `isPrinting` | `() => boolean` | Check if print is in progress |\n *\n * ## Events\n *\n * | Event | Detail | Description |\n * |-------|--------|-------------|\n * | `print-start` | `PrintStartDetail` | Fired when print begins |\n * | `print-complete` | `PrintCompleteDetail` | Fired when print completes |\n *\n * @example Basic Print\n * ```ts\n * import { PrintPlugin } from '@toolbox-web/grid/plugins/print';\n *\n * const grid = document.querySelector('tbw-grid');\n * grid.gridConfig = {\n * plugins: [new PrintPlugin()],\n * };\n *\n * // Trigger print\n * const printPlugin = grid.getPlugin(PrintPlugin);\n * await printPlugin.print();\n * ```\n *\n * @example With Toolbar Button\n * ```ts\n * grid.gridConfig = {\n * plugins: [new PrintPlugin({ button: true, orientation: 'landscape' })],\n * };\n * ```\n *\n * @see {@link PrintConfig} for all configuration options\n */\nexport class PrintPlugin extends BaseGridPlugin<PrintConfig> {\n /** @internal */\n readonly name = 'print';\n\n /** @internal */\n override readonly version = '1.0.0';\n\n /** CSS styles for print mode */\n override readonly styles = styles;\n\n /** Current print state */\n #printing = false;\n\n /** Saved column visibility state */\n #savedHiddenColumns: Map<string, boolean> | null = null;\n\n /** Saved virtualization state */\n #savedVirtualization: { bypassThreshold: number } | null = null;\n\n /** Saved rows when maxRows limit is applied */\n #savedRows: unknown[] | null = null;\n\n /** Print header element */\n #printHeader: HTMLElement | null = null;\n\n /** Print footer element */\n #printFooter: HTMLElement | null = null;\n\n /** Applied scale factor (legacy, used for cleanup) */\n #appliedScale: number | null = null;\n\n /**\n * Get the grid typed as PrintGridRef for internal access.\n */\n get #internalGrid(): PrintGridRef {\n return this.grid as unknown as PrintGridRef;\n }\n\n /**\n * Check if print is currently in progress\n */\n isPrinting(): boolean {\n return this.#printing;\n }\n\n /**\n * Trigger the browser print dialog\n *\n * This method:\n * 1. Validates row count against maxRows limit\n * 2. Disables virtualization to render all rows\n * 3. Applies print-specific CSS classes\n * 4. Opens the browser print dialog (or isolated window if `isolate: true`)\n * 5. Restores normal state after printing\n *\n * @param params - Optional parameters to override config for this print\n * @param params.isolate - If true, prints in an isolated window containing only the grid\n * @returns Promise that resolves when print dialog closes\n */\n async print(params?: PrintParams): Promise<void> {\n if (this.#printing) {\n console.warn('[PrintPlugin] Print already in progress');\n return;\n }\n\n const grid = this.gridElement;\n if (!grid) {\n console.warn('[PrintPlugin] Grid not available');\n return;\n }\n\n const config = { ...DEFAULT_CONFIG, ...this.config, ...params };\n const rows = this.rows;\n const originalRowCount = rows.length;\n let rowCount = originalRowCount;\n let limitApplied = false;\n\n // Check if we should warn about large datasets\n if (config.warnThreshold > 0 && originalRowCount > config.warnThreshold) {\n const limitInfo =\n config.maxRows > 0 ? `\\n\\nNote: Output will be limited to ${config.maxRows.toLocaleString()} rows.` : '';\n const proceed = confirm(\n `This grid has ${originalRowCount.toLocaleString()} rows. ` +\n `Printing large datasets may cause performance issues or browser slowdowns.${limitInfo}\\n\\n` +\n `Click OK to continue, or Cancel to abort.`,\n );\n if (!proceed) {\n return;\n }\n }\n\n // Apply hard row limit if configured\n if (config.maxRows > 0 && originalRowCount > config.maxRows) {\n rowCount = config.maxRows;\n limitApplied = true;\n }\n\n this.#printing = true;\n\n // Track timing for duration reporting\n const startTime = performance.now();\n\n // Emit print-start event\n this.emit<PrintStartDetail>('print-start', {\n rowCount,\n limitApplied,\n originalRowCount,\n });\n\n try {\n // Save current virtualization state\n const internalGrid = this.#internalGrid;\n this.#savedVirtualization = {\n bypassThreshold: internalGrid._virtualization?.bypassThreshold ?? 24,\n };\n\n // Hide columns marked with printHidden\n this.#hidePrintColumns();\n\n // Apply row limit if configured\n if (limitApplied) {\n this.#savedRows = this.sourceRows;\n // Set limited rows on the grid\n (this.grid as unknown as { rows: unknown[] }).rows = this.sourceRows.slice(0, rowCount);\n // Wait for grid to process new rows\n await new Promise((resolve) => setTimeout(resolve, 50));\n }\n\n // Add print header if configured\n if (config.includeTitle || config.includeTimestamp) {\n this.#addPrintHeader(config);\n }\n\n // Disable virtualization to render all rows\n // This forces the grid to render all rows in the DOM\n await this.#disableVirtualization();\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Add orientation class for @page rules\n grid.classList.add(`print-${config.orientation}`);\n\n // Wait for next frame to ensure DOM is updated\n await new Promise((resolve) => requestAnimationFrame(resolve));\n await new Promise((resolve) => requestAnimationFrame(resolve));\n\n // Trigger browser print dialog (isolated or inline)\n if (config.isolate) {\n await this.#printInIsolatedWindow(config);\n } else {\n await this.#triggerPrint();\n }\n\n // Emit print-complete event\n this.emit<PrintCompleteDetail>('print-complete', {\n success: true,\n rowCount,\n duration: Math.round(performance.now() - startTime),\n });\n } catch (error) {\n console.error('[PrintPlugin] Print failed:', error);\n this.emit<PrintCompleteDetail>('print-complete', {\n success: false,\n rowCount: 0,\n duration: Math.round(performance.now() - startTime),\n });\n } finally {\n // Restore normal state\n this.#cleanup();\n this.#printing = false;\n }\n }\n\n /**\n * Add print header with title and timestamp\n */\n #addPrintHeader(config: Required<PrintConfig>): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Create print header\n this.#printHeader = document.createElement('div');\n this.#printHeader.className = 'tbw-print-header';\n\n // Title\n if (config.includeTitle) {\n const title = config.title || this.grid.effectiveConfig?.shell?.header?.title || 'Grid Data';\n const titleEl = document.createElement('div');\n titleEl.className = 'tbw-print-header-title';\n titleEl.textContent = title;\n this.#printHeader.appendChild(titleEl);\n }\n\n // Timestamp\n if (config.includeTimestamp) {\n const timestampEl = document.createElement('div');\n timestampEl.className = 'tbw-print-header-timestamp';\n timestampEl.textContent = `Printed: ${new Date().toLocaleString()}`;\n this.#printHeader.appendChild(timestampEl);\n }\n\n // Insert at the beginning of the grid\n grid.insertBefore(this.#printHeader, grid.firstChild);\n\n // Create print footer\n this.#printFooter = document.createElement('div');\n this.#printFooter.className = 'tbw-print-footer';\n this.#printFooter.textContent = `Page generated from ${window.location.hostname}`;\n grid.appendChild(this.#printFooter);\n }\n\n /**\n * Disable virtualization to render all rows\n */\n async #disableVirtualization(): Promise<void> {\n const internalGrid = this.#internalGrid;\n if (!internalGrid._virtualization) return;\n\n // Set bypass threshold higher than total row count to disable virtualization\n // This makes the grid render all rows (up to maxRows) instead of just visible ones\n const totalRows = this.rows.length;\n internalGrid._virtualization.bypassThreshold = totalRows + 100;\n\n // Force a full refresh to re-render with virtualization disabled\n internalGrid.refreshVirtualWindow(true);\n\n // Wait for render to complete\n await new Promise((resolve) => setTimeout(resolve, 100));\n }\n\n /**\n * Trigger the browser print dialog\n */\n async #triggerPrint(): Promise<void> {\n return new Promise((resolve) => {\n // Listen for afterprint event\n const onAfterPrint = () => {\n window.removeEventListener('afterprint', onAfterPrint);\n resolve();\n };\n window.addEventListener('afterprint', onAfterPrint);\n\n // Trigger print\n window.print();\n\n // Fallback timeout in case afterprint doesn't fire (some browsers)\n setTimeout(() => {\n window.removeEventListener('afterprint', onAfterPrint);\n resolve();\n }, 1000);\n });\n }\n\n /**\n * Print in isolation by hiding all other page content.\n * This excludes navigation, sidebars, etc. while keeping the grid in place.\n */\n async #printInIsolatedWindow(config: Required<PrintConfig>): Promise<void> {\n const grid = this.gridElement;\n if (!grid) return;\n\n await printGridIsolated(grid, {\n orientation: config.orientation,\n });\n }\n\n /**\n * Hide columns marked with printHidden: true\n */\n #hidePrintColumns(): void {\n const columns = this.columns;\n if (!columns) return;\n\n // Save current hidden state and hide print columns\n this.#savedHiddenColumns = new Map();\n\n for (const col of columns) {\n if (col.printHidden && col.field) {\n // Save current visibility state (true = visible, false = hidden)\n this.#savedHiddenColumns.set(col.field, !col.hidden);\n // Hide the column for printing\n this.grid.setColumnVisible(col.field, false);\n }\n }\n }\n\n /**\n * Restore columns that were hidden for printing\n */\n #restorePrintColumns(): void {\n if (!this.#savedHiddenColumns) return;\n\n for (const [field, wasVisible] of this.#savedHiddenColumns) {\n // Restore original visibility\n this.grid.setColumnVisible(field, wasVisible);\n }\n\n this.#savedHiddenColumns = null;\n }\n\n /**\n * Cleanup after printing\n */\n #cleanup(): void {\n const grid = this.gridElement;\n if (!grid) return;\n\n // Restore columns that were hidden for printing\n this.#restorePrintColumns();\n\n // Remove orientation classes (both original and possibly switched)\n grid.classList.remove('print-portrait', 'print-landscape');\n\n // Remove scaling transform if applied (legacy)\n if (this.#appliedScale !== null) {\n grid.style.transform = '';\n grid.style.transformOrigin = '';\n grid.style.width = '';\n this.#appliedScale = null;\n }\n\n // Remove print header/footer\n if (this.#printHeader) {\n this.#printHeader.remove();\n this.#printHeader = null;\n }\n if (this.#printFooter) {\n this.#printFooter.remove();\n this.#printFooter = null;\n }\n\n // Restore virtualization\n const internalGrid = this.#internalGrid;\n if (this.#savedVirtualization && internalGrid._virtualization) {\n internalGrid._virtualization.bypassThreshold = this.#savedVirtualization.bypassThreshold;\n internalGrid.refreshVirtualWindow(true);\n this.#savedVirtualization = null;\n }\n\n // Restore original rows if they were limited\n if (this.#savedRows !== null) {\n (this.grid as unknown as { rows: unknown[] }).rows = this.#savedRows;\n this.#savedRows = null;\n }\n }\n\n /**\n * Register toolbar button if configured\n * @internal\n */\n override afterRender(): void {\n // Register toolbar on first render when button is enabled\n if (this.config?.button && !this.#toolbarRegistered) {\n this.#registerToolbarButton();\n this.#toolbarRegistered = true;\n }\n }\n\n /** Track if toolbar button is registered */\n #toolbarRegistered = false;\n\n /**\n * Register print button in toolbar\n */\n #registerToolbarButton(): void {\n const grid = this.#internalGrid;\n\n // Register toolbar content\n grid.registerToolbarContent?.({\n id: 'print-button',\n order: 900, // High order to appear at the end\n render: (container: HTMLElement) => {\n const button = document.createElement('button');\n button.className = 'tbw-toolbar-btn tbw-print-btn';\n button.title = 'Print grid';\n button.type = 'button';\n\n // Use print icon\n const icon = this.resolveIcon('print') || '🖨️';\n this.setIcon(button, icon);\n\n button.addEventListener(\n 'click',\n () => {\n this.print();\n },\n { signal: this.disconnectSignal },\n );\n\n container.appendChild(button);\n },\n });\n }\n}\n"],"names":["ISOLATION_STYLE_ID","createIsolationStylesheet","gridId","orientation","style","printGridIsolated","gridElement","options","isolationStyle","resolve","onAfterPrint","DEFAULT_CONFIG","PrintPlugin","BaseGridPlugin","styles","#printing","#savedHiddenColumns","#savedVirtualization","#savedRows","#printHeader","#printFooter","#appliedScale","#internalGrid","params","grid","config","originalRowCount","rowCount","limitApplied","limitInfo","startTime","internalGrid","#hidePrintColumns","#addPrintHeader","#disableVirtualization","#printInIsolatedWindow","#triggerPrint","error","#cleanup","title","titleEl","timestampEl","totalRows","columns","col","#restorePrintColumns","field","wasVisible","#toolbarRegistered","#registerToolbarButton","container","button","icon"],"mappings":"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"}
@@ -1,2 +1,2 @@
1
- (function(a,l){typeof exports=="object"&&typeof module<"u"?l(exports,require("../../core/internal/keyboard"),require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/keyboard","../../core/plugin/base-plugin"],l):(a=typeof globalThis<"u"?globalThis:a||self,l(a.TbwGridPlugin_rowReorder={},a.TbwGrid,a.TbwGrid))})(this,(function(a,l,g){"use strict";const u='.dg-row-drag-handle{display:flex;align-items:center;justify-content:center;cursor:grab;-webkit-user-select:none;user-select:none;color:var(--tbw-color-fg-muted, #999);transition:color .15s ease;font-size:14px;letter-spacing:-2px}.dg-row-drag-handle:hover{color:var(--tbw-color-fg, #333)}.dg-row-drag-handle:active{cursor:grabbing}.data-grid-row.dragging{opacity:.6}.data-grid-row.drop-target{position:relative}.data-grid-row.drop-target.drop-before:before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background-color:var(--tbw-color-accent, #1976d2);z-index:10}.data-grid-row.drop-target.drop-after:after{content:"";position:absolute;bottom:0;left:0;right:0;height:2px;background-color:var(--tbw-color-accent, #1976d2);z-index:10}.data-grid-row.keyboard-moving{background-color:var(--tbw-color-bg-selected, #e3f2fd);box-shadow:0 0 0 1px var(--tbw-color-accent, #1976d2) inset}.data-grid-row.animate-flip{transition:transform var(--tbw-animation-duration, .2s) ease-out}',c="__tbw_row_drag";class h extends g.BaseGridPlugin{name="rowReorder";styles=u;get defaultConfig(){return{enableKeyboard:!0,showDragHandle:!0,dragHandlePosition:"left",dragHandleWidth:40,debounceMs:150,animation:"flip"}}isDragging=!1;draggedRowIndex=null;dropRowIndex=null;pendingMove=null;debounceTimer=null;lastFocusCol=0;detach(){this.clearDebounceTimer(),this.isDragging=!1,this.draggedRowIndex=null,this.dropRowIndex=null,this.pendingMove=null}processColumns(e){if(!this.config.showDragHandle)return[...e];const r={field:c,header:"",width:this.config.dragHandleWidth??40,resizable:!1,sortable:!1,filterable:!1,meta:{lockPosition:!0,suppressMovable:!0,utility:!0},viewRenderer:()=>{const t=document.createElement("div");return t.className="dg-row-drag-handle",t.setAttribute("aria-label","Drag to reorder"),t.setAttribute("role","button"),t.setAttribute("tabindex","-1"),t.draggable=!0,this.setIcon(t,this.resolveIcon("dragHandle")),t}};return this.config.dragHandlePosition==="right"?[...e,r]:[r,...e]}afterRender(){if(!this.config.showDragHandle)return;const e=this.gridElement;if(!e)return;e.querySelectorAll(".dg-row-drag-handle").forEach(o=>{const i=o;if(i.getAttribute("data-drag-bound"))return;i.setAttribute("data-drag-bound","true");const n=i.closest(".data-grid-row");n&&this.setupHandleDragListeners(i,n)}),e.querySelectorAll(".data-grid-row").forEach(o=>{const i=o;i.getAttribute("data-drop-bound")||(i.setAttribute("data-drop-bound","true"),this.setupRowDropListeners(i))})}onKeyDown(e){if(!this.config.enableKeyboard||!e.ctrlKey||e.key!=="ArrowUp"&&e.key!=="ArrowDown")return;const r=this.grid,t=r._focusRow,o=r._rows??this.sourceRows;if(t<0||t>=o.length)return;const i=e.key==="ArrowUp"?"up":"down",n=i==="up"?t-1:t+1;if(n<0||n>=o.length)return;const s=o[t];if(!(this.config.canMove&&!this.config.canMove(s,t,n,i)))return this.handleKeyboardMove(s,t,n,i,r._focusCol),e.preventDefault(),e.stopPropagation(),!0}onCellClick(){this.flushPendingMove()}moveRow(e,r){const t=[...this.sourceRows];if(e<0||e>=t.length||r<0||r>=t.length||e===r)return;const o=r<e?"up":"down",i=t[e];this.config.canMove&&!this.config.canMove(i,e,r,o)||this.executeMove(i,e,r,"keyboard")}canMoveRow(e,r){const t=this.sourceRows;if(e<0||e>=t.length||r<0||r>=t.length||e===r)return!1;if(!this.config.canMove)return!0;const o=r<e?"up":"down";return this.config.canMove(t[e],e,r,o)}setupHandleDragListeners(e,r){e.addEventListener("dragstart",t=>{const o=this.getRowIndex(r);o<0||(this.isDragging=!0,this.draggedRowIndex=o,t.dataTransfer&&(t.dataTransfer.effectAllowed="move",t.dataTransfer.setData("text/plain",String(o))),r.classList.add("dragging"))}),e.addEventListener("dragend",()=>{this.isDragging=!1,this.draggedRowIndex=null,this.dropRowIndex=null,this.clearDragClasses()})}setupRowDropListeners(e){e.addEventListener("dragover",r=>{if(r.preventDefault(),!this.isDragging||this.draggedRowIndex===null)return;const t=this.getRowIndex(e);if(t<0||t===this.draggedRowIndex)return;const o=e.getBoundingClientRect(),i=o.top+o.height/2,n=r.clientY<i;this.dropRowIndex=n?t:t+1,e.classList.add("drop-target"),e.classList.toggle("drop-before",n),e.classList.toggle("drop-after",!n)}),e.addEventListener("dragleave",()=>{e.classList.remove("drop-target","drop-before","drop-after")}),e.addEventListener("drop",r=>{r.preventDefault();const t=this.draggedRowIndex;let o=this.dropRowIndex;if(!(!this.isDragging||t===null||o===null)&&(o>t&&o--,t!==o)){const n=this.sourceRows[t],s=o<t?"up":"down";(!this.config.canMove||this.config.canMove(n,t,o,s))&&this.executeMove(n,t,o,"drag")}})}handleKeyboardMove(e,r,t,o,i){this.pendingMove?this.pendingMove.currentIndex=t:this.pendingMove={originalIndex:r,currentIndex:t,row:e},this.lastFocusCol=i;const n=this.grid,s=[...n._rows??this.sourceRows],[d]=s.splice(r,1);s.splice(t,0,d),n._rows=s,n._focusRow=t,n._focusCol=i,n.refreshVirtualWindow(!0),l.ensureCellVisible(n),this.clearDebounceTimer(),this.debounceTimer=setTimeout(()=>{this.flushPendingMove()},this.config.debounceMs??300)}flushPendingMove(){if(this.clearDebounceTimer(),!this.pendingMove)return;const{originalIndex:e,currentIndex:r,row:t}=this.pendingMove;if(this.pendingMove=null,e===r)return;const o={row:t,fromIndex:e,toIndex:r,rows:[...this.sourceRows],source:"keyboard"};if(this.emitCancelable("row-move",o)){const n=[...this.sourceRows],[s]=n.splice(r,1);n.splice(e,0,s);const d=this.grid;d._rows=n,d._focusRow=e,d._focusCol=this.lastFocusCol,d.refreshVirtualWindow(!0),l.ensureCellVisible(d)}}executeMove(e,r,t,o){const i=[...this.sourceRows],[n]=i.splice(r,1);i.splice(t,0,n);const s={row:e,fromIndex:r,toIndex:t,rows:i,source:o};this.emitCancelable("row-move",s)||(this.grid.rows=i)}getRowIndex(e){const r=e.querySelector(".cell[data-row]");return r?parseInt(r.getAttribute("data-row")??"-1",10):-1}clearDragClasses(){this.gridElement?.querySelectorAll(".data-grid-row").forEach(e=>{e.classList.remove("dragging","drop-target","drop-before","drop-after")})}clearDebounceTimer(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null)}}a.ROW_DRAG_HANDLE_FIELD=c,a.RowReorderPlugin=h,Object.defineProperty(a,Symbol.toStringTag,{value:"Module"})}));
1
+ (function(l,c){typeof exports=="object"&&typeof module<"u"?c(exports,require("../../core/internal/keyboard"),require("../../core/plugin/base-plugin")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/keyboard","../../core/plugin/base-plugin"],c):(l=typeof globalThis<"u"?globalThis:l||self,c(l.TbwGridPlugin_rowReorder={},l.TbwGrid,l.TbwGrid))})(this,(function(l,c,b){"use strict";const v='@layer tbw-plugins{.dg-row-drag-handle{display:flex;align-items:center;justify-content:center;cursor:grab;-webkit-user-select:none;user-select:none;color:var(--tbw-row-reorder-handle-color, var(--tbw-color-fg-muted));transition:color var(--tbw-transition-duration, .12s) var(--tbw-transition-ease, ease);font-size:var(--tbw-font-size, 1em);letter-spacing:-2px}.dg-row-drag-handle:hover{color:var(--tbw-row-reorder-handle-hover, var(--tbw-color-fg))}.dg-row-drag-handle:active{cursor:grabbing}.data-grid-row.dragging{opacity:.6}.data-grid-row.drop-target{position:relative}.data-grid-row.drop-target.drop-before:before{content:"";position:absolute;top:0;left:0;right:0;height:2px;background-color:var(--tbw-row-reorder-indicator, var(--tbw-color-accent));z-index:10}.data-grid-row.drop-target.drop-after:after{content:"";position:absolute;bottom:0;left:0;right:0;height:2px;background-color:var(--tbw-row-reorder-indicator, var(--tbw-color-accent));z-index:10}.data-grid-row.keyboard-moving{background-color:var(--tbw-row-reorder-moving-bg, var(--tbw-focus-background));box-shadow:0 0 0 1px var(--tbw-row-reorder-moving-border, var(--tbw-color-accent)) inset}.data-grid-row.flip-animating{transition:transform var(--tbw-animation-duration, .2s) ease-out;will-change:transform;z-index:1}}',f="__tbw_row_drag";class m extends b.BaseGridPlugin{name="rowReorder";styles=v;get defaultConfig(){return{enableKeyboard:!0,showDragHandle:!0,dragHandlePosition:"left",dragHandleWidth:40,debounceMs:150,animation:"flip"}}get animationType(){return this.isAnimationEnabled?this.config.animation!==void 0?this.config.animation:"flip":!1}isDragging=!1;draggedRowIndex=null;dropRowIndex=null;pendingMove=null;debounceTimer=null;lastFocusCol=0;detach(){this.clearDebounceTimer(),this.isDragging=!1,this.draggedRowIndex=null,this.dropRowIndex=null,this.pendingMove=null}processColumns(e){if(!this.config.showDragHandle)return[...e];const r={field:f,header:"",width:this.config.dragHandleWidth??40,resizable:!1,sortable:!1,filterable:!1,meta:{lockPosition:!0,suppressMovable:!0,utility:!0},viewRenderer:()=>{const t=document.createElement("div");return t.className="dg-row-drag-handle",t.setAttribute("aria-label","Drag to reorder"),t.setAttribute("role","button"),t.setAttribute("tabindex","-1"),t.draggable=!0,this.setIcon(t,this.resolveIcon("dragHandle")),t}};return this.config.dragHandlePosition==="right"?[...e,r]:[r,...e]}afterRender(){if(!this.config.showDragHandle)return;const e=this.gridElement;if(!e)return;e.querySelectorAll(".dg-row-drag-handle").forEach(o=>{const i=o;if(i.getAttribute("data-drag-bound"))return;i.setAttribute("data-drag-bound","true");const n=i.closest(".data-grid-row");n&&this.setupHandleDragListeners(i,n)}),e.querySelectorAll(".data-grid-row").forEach(o=>{const i=o;i.getAttribute("data-drop-bound")||(i.setAttribute("data-drop-bound","true"),this.setupRowDropListeners(i))})}onKeyDown(e){if(!this.config.enableKeyboard||!e.ctrlKey||e.key!=="ArrowUp"&&e.key!=="ArrowDown")return;const r=this.grid,t=r._focusRow,o=r._rows??this.sourceRows;if(t<0||t>=o.length)return;const i=e.key==="ArrowUp"?"up":"down",n=i==="up"?t-1:t+1;if(n<0||n>=o.length)return;const s=o[t];if(!(this.config.canMove&&!this.config.canMove(s,t,n,i)))return this.handleKeyboardMove(s,t,n,i,r._focusCol),e.preventDefault(),e.stopPropagation(),!0}onCellClick(){this.flushPendingMove()}moveRow(e,r){const t=[...this.sourceRows];if(e<0||e>=t.length||r<0||r>=t.length||e===r)return;const o=r<e?"up":"down",i=t[e];this.config.canMove&&!this.config.canMove(i,e,r,o)||this.executeMove(i,e,r,"keyboard")}canMoveRow(e,r){const t=this.sourceRows;if(e<0||e>=t.length||r<0||r>=t.length||e===r)return!1;if(!this.config.canMove)return!0;const o=r<e?"up":"down";return this.config.canMove(t[e],e,r,o)}setupHandleDragListeners(e,r){e.addEventListener("dragstart",t=>{const o=this.getRowIndex(r);o<0||(this.isDragging=!0,this.draggedRowIndex=o,t.dataTransfer&&(t.dataTransfer.effectAllowed="move",t.dataTransfer.setData("text/plain",String(o))),r.classList.add("dragging"))}),e.addEventListener("dragend",()=>{this.isDragging=!1,this.draggedRowIndex=null,this.dropRowIndex=null,this.clearDragClasses()})}setupRowDropListeners(e){e.addEventListener("dragover",r=>{if(r.preventDefault(),!this.isDragging||this.draggedRowIndex===null)return;const t=this.getRowIndex(e);if(t<0||t===this.draggedRowIndex)return;const o=e.getBoundingClientRect(),i=o.top+o.height/2,n=r.clientY<i;this.dropRowIndex=n?t:t+1,e.classList.add("drop-target"),e.classList.toggle("drop-before",n),e.classList.toggle("drop-after",!n)}),e.addEventListener("dragleave",()=>{e.classList.remove("drop-target","drop-before","drop-after")}),e.addEventListener("drop",r=>{r.preventDefault();const t=this.draggedRowIndex;let o=this.dropRowIndex;if(!(!this.isDragging||t===null||o===null)&&(o>t&&o--,t!==o)){const n=this.sourceRows[t],s=o<t?"up":"down";(!this.config.canMove||this.config.canMove(n,t,o,s))&&this.executeMove(n,t,o,"drag")}})}handleKeyboardMove(e,r,t,o,i){this.pendingMove?this.pendingMove.currentIndex=t:this.pendingMove={originalIndex:r,currentIndex:t,row:e},this.lastFocusCol=i;const n=this.grid,s=[...n._rows??this.sourceRows],[d]=s.splice(r,1);s.splice(t,0,d),n._rows=s,n._focusRow=t,n._focusCol=i,n.refreshVirtualWindow(!0),c.ensureCellVisible(n),this.clearDebounceTimer(),this.debounceTimer=setTimeout(()=>{this.flushPendingMove()},this.config.debounceMs??300)}flushPendingMove(){if(this.clearDebounceTimer(),!this.pendingMove)return;const{originalIndex:e,currentIndex:r,row:t}=this.pendingMove;if(this.pendingMove=null,e===r)return;const o={row:t,fromIndex:e,toIndex:r,rows:[...this.sourceRows],source:"keyboard"};if(this.emitCancelable("row-move",o)){const n=[...this.sourceRows],[s]=n.splice(r,1);n.splice(e,0,s);const d=this.grid;d._rows=n,d._focusRow=e,d._focusCol=this.lastFocusCol,d.refreshVirtualWindow(!0),c.ensureCellVisible(d)}}executeMove(e,r,t,o){const i=[...this.sourceRows],[n]=i.splice(r,1);i.splice(t,0,n);const s={row:e,fromIndex:r,toIndex:t,rows:i,source:o};if(!this.emitCancelable("row-move",s))if(this.animationType==="flip"&&this.gridElement){const a=this.captureRowPositions();this.grid.rows=i,requestAnimationFrame(()=>{this.gridElement.offsetHeight,this.animateFLIP(a,r,t)})}else this.grid.rows=i}captureRowPositions(){const e=new Map;return this.gridElement?.querySelectorAll(".data-grid-row").forEach(r=>{const t=this.getRowIndex(r);t>=0&&e.set(t,r.getBoundingClientRect().top)}),e}animateFLIP(e,r,t){const o=this.gridElement;if(!o||e.size===0)return;const i=Math.min(r,t),n=Math.max(r,t),s=[];if(o.querySelectorAll(".data-grid-row").forEach(a=>{const u=a,g=this.getRowIndex(u);if(g<0||g<i||g>n)return;let h;g===t?h=r:r<t?h=g+1:h=g-1;const w=e.get(h);if(w===void 0)return;const R=u.getBoundingClientRect().top,p=w-R;Math.abs(p)>1&&s.push({el:u,deltaY:p})}),s.length===0)return;s.forEach(({el:a,deltaY:u})=>{a.style.transform=`translateY(${u}px)`}),o.offsetHeight;const d=this.animationDuration;requestAnimationFrame(()=>{s.forEach(({el:a})=>{a.classList.add("flip-animating"),a.style.transform=""}),setTimeout(()=>{s.forEach(({el:a})=>{a.style.transform="",a.classList.remove("flip-animating")})},d+50)})}getRowIndex(e){const r=e.querySelector(".cell[data-row]");return r?parseInt(r.getAttribute("data-row")??"-1",10):-1}clearDragClasses(){this.gridElement?.querySelectorAll(".data-grid-row").forEach(e=>{e.classList.remove("dragging","drop-target","drop-before","drop-after")})}clearDebounceTimer(){this.debounceTimer&&(clearTimeout(this.debounceTimer),this.debounceTimer=null)}}l.ROW_DRAG_HANDLE_FIELD=f,l.RowReorderPlugin=m,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})}));
2
2
  //# sourceMappingURL=row-reorder.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"row-reorder.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/row-reorder/RowReorderPlugin.ts"],"sourcesContent":["/**\n * Row Reordering Plugin\n *\n * Provides keyboard and drag-drop row reordering functionality for tbw-grid.\n * Supports Ctrl+Up/Down keyboard shortcuts and optional drag handle column.\n */\n\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, InternalGrid } from '../../core/types';\nimport styles from './row-reorder.css?inline';\nimport type { PendingMove, RowMoveDetail, RowReorderConfig } from './types';\n\n/** Field name for the drag handle column */\nexport const ROW_DRAG_HANDLE_FIELD = '__tbw_row_drag';\n\n/**\n * Row Reorder Plugin for tbw-grid\n *\n * Enables row reordering via keyboard shortcuts (Ctrl+Up/Down) and drag-drop.\n * Supports validation callbacks and debounced keyboard moves.\n *\n * ## Installation\n *\n * ```ts\n * import { RowReorderPlugin } from '@toolbox-web/grid/plugins/row-reorder';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `enableKeyboard` | `boolean` | `true` | Enable Ctrl+Up/Down shortcuts |\n * | `showDragHandle` | `boolean` | `true` | Show drag handle column |\n * | `dragHandlePosition` | `'left' \\| 'right'` | `'left'` | Drag handle column position |\n * | `dragHandleWidth` | `number` | `40` | Drag handle column width |\n * | `canMove` | `function` | - | Validation callback |\n * | `debounceMs` | `number` | `300` | Debounce time for keyboard moves |\n * | `animation` | `false \\| 'flip'` | `'flip'` | Animation for row moves |\n *\n * ## Keyboard Shortcuts\n *\n * | Key | Action |\n * |-----|--------|\n * | `Ctrl + ↑` | Move focused row up |\n * | `Ctrl + ↓` | Move focused row down |\n *\n * ## Events\n *\n * | Event | Detail | Cancelable | Description |\n * |-------|--------|------------|-------------|\n * | `row-move` | `RowMoveDetail` | Yes | Fired when a row move is attempted |\n *\n * @example Basic Row Reordering\n * ```ts\n * import '@toolbox-web/grid';\n * import { RowReorderPlugin } from '@toolbox-web/grid/plugins/row-reorder';\n *\n * const grid = document.querySelector('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'id', header: 'ID' },\n * { field: 'name', header: 'Name' },\n * ],\n * plugins: [new RowReorderPlugin()],\n * };\n *\n * grid.addEventListener('row-move', (e) => {\n * console.log('Row moved from', e.detail.fromIndex, 'to', e.detail.toIndex);\n * });\n * ```\n *\n * @example With Validation\n * ```ts\n * new RowReorderPlugin({\n * canMove: (row, fromIndex, toIndex, direction) => {\n * // Prevent moving locked rows\n * return !row.locked;\n * },\n * })\n * ```\n *\n * @see {@link RowReorderConfig} for all configuration options\n * @see {@link RowMoveDetail} for the event detail structure\n */\nexport class RowReorderPlugin extends BaseGridPlugin<RowReorderConfig> {\n /** @internal */\n readonly name = 'rowReorder';\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n protected override get defaultConfig(): Partial<RowReorderConfig> {\n return {\n enableKeyboard: true,\n showDragHandle: true,\n dragHandlePosition: 'left',\n dragHandleWidth: 40,\n debounceMs: 150,\n animation: 'flip',\n };\n }\n\n // #region Internal State\n private isDragging = false;\n private draggedRowIndex: number | null = null;\n private dropRowIndex: number | null = null;\n private pendingMove: PendingMove | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n /** Column index to use when flushing pending move */\n private lastFocusCol = 0;\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\n override detach(): void {\n this.clearDebounceTimer();\n this.isDragging = false;\n this.draggedRowIndex = null;\n this.dropRowIndex = null;\n this.pendingMove = null;\n }\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.showDragHandle) {\n return [...columns];\n }\n\n const dragHandleColumn: ColumnConfig = {\n field: ROW_DRAG_HANDLE_FIELD,\n header: '',\n width: this.config.dragHandleWidth ?? 40,\n resizable: false,\n sortable: false,\n filterable: false,\n meta: {\n lockPosition: true,\n suppressMovable: true,\n utility: true,\n },\n viewRenderer: () => {\n const container = document.createElement('div');\n container.className = 'dg-row-drag-handle';\n container.setAttribute('aria-label', 'Drag to reorder');\n container.setAttribute('role', 'button');\n container.setAttribute('tabindex', '-1');\n // Set draggable as property (not just attribute) for proper HTML5 drag-drop\n container.draggable = true;\n\n // Use the grid's configured dragHandle icon\n this.setIcon(container, this.resolveIcon('dragHandle'));\n\n return container;\n },\n };\n\n // Position the drag handle column\n if (this.config.dragHandlePosition === 'right') {\n return [...columns, dragHandleColumn];\n }\n return [dragHandleColumn, ...columns];\n }\n\n /** @internal */\n override afterRender(): void {\n if (!this.config.showDragHandle) return;\n\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n // Set up drag start/end listeners on drag handles\n const handles = gridEl.querySelectorAll('.dg-row-drag-handle');\n handles.forEach((handle) => {\n const handleEl = handle as HTMLElement;\n if (handleEl.getAttribute('data-drag-bound')) return;\n handleEl.setAttribute('data-drag-bound', 'true');\n\n const rowEl = handleEl.closest('.data-grid-row') as HTMLElement;\n if (!rowEl) return;\n\n // Set up dragstart/dragend on the handle\n this.setupHandleDragListeners(handleEl, rowEl);\n });\n\n // Set up drop target listeners on ALL rows (not just the handle's row)\n const rows = gridEl.querySelectorAll('.data-grid-row');\n rows.forEach((row) => {\n const rowEl = row as HTMLElement;\n if (rowEl.getAttribute('data-drop-bound')) return;\n rowEl.setAttribute('data-drop-bound', 'true');\n\n this.setupRowDropListeners(rowEl);\n });\n }\n\n /**\n * Handle Ctrl+Arrow keyboard shortcuts for row reordering.\n * @internal\n */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n if (!this.config.enableKeyboard) return;\n if (!event.ctrlKey || (event.key !== 'ArrowUp' && event.key !== 'ArrowDown')) {\n return;\n }\n\n const grid = this.grid as unknown as InternalGrid;\n const focusRow = grid._focusRow;\n // Use _rows (current visual state) for keyboard moves, not sourceRows\n // This ensures rapid moves work correctly since we update _rows directly\n // Fallback to sourceRows for compatibility with tests\n const rows = grid._rows ?? this.sourceRows;\n\n if (focusRow < 0 || focusRow >= rows.length) return;\n\n const direction = event.key === 'ArrowUp' ? 'up' : 'down';\n const toIndex = direction === 'up' ? focusRow - 1 : focusRow + 1;\n\n // Check bounds\n if (toIndex < 0 || toIndex >= rows.length) return;\n\n const row = rows[focusRow];\n\n // Validate move\n if (this.config.canMove && !this.config.canMove(row, focusRow, toIndex, direction)) {\n return;\n }\n\n // Debounce keyboard moves\n this.handleKeyboardMove(row, focusRow, toIndex, direction, grid._focusCol);\n\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n\n /**\n * Flush pending keyboard moves when user clicks a cell.\n * This commits the move immediately so focus works correctly.\n * @internal\n */\n override onCellClick(): void {\n // If there's a pending keyboard move, flush it immediately\n // so the user's click focus isn't overridden\n this.flushPendingMove();\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Move a row to a new position programmatically.\n * @param fromIndex - Current index of the row\n * @param toIndex - Target index\n */\n moveRow(fromIndex: number, toIndex: number): void {\n const rows = [...this.sourceRows];\n if (fromIndex < 0 || fromIndex >= rows.length) return;\n if (toIndex < 0 || toIndex >= rows.length) return;\n if (fromIndex === toIndex) return;\n\n const direction = toIndex < fromIndex ? 'up' : 'down';\n const row = rows[fromIndex];\n\n // Validate move\n if (this.config.canMove && !this.config.canMove(row, fromIndex, toIndex, direction)) {\n return;\n }\n\n this.executeMove(row, fromIndex, toIndex, 'keyboard');\n }\n\n /**\n * Check if a row can be moved to a position.\n * @param fromIndex - Current index of the row\n * @param toIndex - Target index\n */\n canMoveRow(fromIndex: number, toIndex: number): boolean {\n const rows = this.sourceRows;\n if (fromIndex < 0 || fromIndex >= rows.length) return false;\n if (toIndex < 0 || toIndex >= rows.length) return false;\n if (fromIndex === toIndex) return false;\n\n if (!this.config.canMove) return true;\n\n const direction = toIndex < fromIndex ? 'up' : 'down';\n return this.config.canMove(rows[fromIndex], fromIndex, toIndex, direction);\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Set up drag start/end listeners on the drag handle element.\n */\n private setupHandleDragListeners(handleEl: HTMLElement, rowEl: HTMLElement): void {\n handleEl.addEventListener('dragstart', (e: DragEvent) => {\n const rowIndex = this.getRowIndex(rowEl);\n if (rowIndex < 0) return;\n\n this.isDragging = true;\n this.draggedRowIndex = rowIndex;\n\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', String(rowIndex));\n }\n\n rowEl.classList.add('dragging');\n });\n\n handleEl.addEventListener('dragend', () => {\n this.isDragging = false;\n this.draggedRowIndex = null;\n this.dropRowIndex = null;\n this.clearDragClasses();\n });\n }\n\n /**\n * Set up drop target listeners on a row element.\n * All rows are valid drop targets during drag operations.\n */\n private setupRowDropListeners(rowEl: HTMLElement): void {\n rowEl.addEventListener('dragover', (e: DragEvent) => {\n e.preventDefault();\n if (!this.isDragging || this.draggedRowIndex === null) return;\n\n const targetIndex = this.getRowIndex(rowEl);\n if (targetIndex < 0 || targetIndex === this.draggedRowIndex) return;\n\n const rect = rowEl.getBoundingClientRect();\n const midY = rect.top + rect.height / 2;\n const isBefore = e.clientY < midY;\n\n this.dropRowIndex = isBefore ? targetIndex : targetIndex + 1;\n\n rowEl.classList.add('drop-target');\n rowEl.classList.toggle('drop-before', isBefore);\n rowEl.classList.toggle('drop-after', !isBefore);\n });\n\n rowEl.addEventListener('dragleave', () => {\n rowEl.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n rowEl.addEventListener('drop', (e: DragEvent) => {\n e.preventDefault();\n const fromIndex = this.draggedRowIndex;\n let toIndex = this.dropRowIndex;\n\n if (!this.isDragging || fromIndex === null || toIndex === null) {\n return;\n }\n\n // Adjust toIndex if dropping after the dragged row\n if (toIndex > fromIndex) {\n toIndex--;\n }\n\n if (fromIndex !== toIndex) {\n const rows = this.sourceRows;\n const row = rows[fromIndex];\n const direction = toIndex < fromIndex ? 'up' : 'down';\n\n // Validate move\n if (!this.config.canMove || this.config.canMove(row, fromIndex, toIndex, direction)) {\n this.executeMove(row, fromIndex, toIndex, 'drag');\n }\n }\n });\n }\n\n /**\n * Handle debounced keyboard moves.\n * Rows move immediately for visual feedback, but the event emission is debounced.\n */\n private handleKeyboardMove(\n row: unknown,\n fromIndex: number,\n toIndex: number,\n direction: 'up' | 'down',\n focusCol: number,\n ): void {\n // Track move for debounced event emission\n if (!this.pendingMove) {\n this.pendingMove = {\n originalIndex: fromIndex,\n currentIndex: toIndex,\n row,\n };\n } else {\n // Update the current index for rapid moves\n this.pendingMove.currentIndex = toIndex;\n }\n\n // Store focus column for flush\n this.lastFocusCol = focusCol;\n\n // Move rows immediately for visual feedback\n // Use _rows (current visual state) for rapid moves, not sourceRows\n // Fallback to sourceRows for compatibility with tests\n const grid = this.grid as unknown as InternalGrid;\n const rows = [...(grid._rows ?? this.sourceRows)];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n\n // Update grid rows immediately (without triggering change events)\n grid._rows = rows;\n\n // Update focus to follow the row\n grid._focusRow = toIndex;\n grid._focusCol = focusCol;\n\n // Refresh virtual window directly - this re-renders from _rows\n // without overwriting _rows from #rows (which requestRender does)\n grid.refreshVirtualWindow(true);\n\n // Ensure focus styling is applied after the row rebuild\n ensureCellVisible(grid);\n\n // Debounce the event emission only\n this.clearDebounceTimer();\n this.debounceTimer = setTimeout(() => {\n this.flushPendingMove();\n }, this.config.debounceMs ?? 300);\n }\n\n /**\n * Flush the pending move by emitting the event.\n * Called when debounce timer fires or user clicks elsewhere.\n */\n private flushPendingMove(): void {\n this.clearDebounceTimer();\n\n if (!this.pendingMove) return;\n\n const { originalIndex, currentIndex, row: movedRow } = this.pendingMove;\n this.pendingMove = null;\n\n if (originalIndex === currentIndex) return;\n\n // Emit cancelable event\n const detail: RowMoveDetail = {\n row: movedRow,\n fromIndex: originalIndex,\n toIndex: currentIndex,\n rows: [...this.sourceRows],\n source: 'keyboard',\n };\n\n const cancelled = this.emitCancelable('row-move', detail);\n if (cancelled) {\n // Revert to original position\n const rows = [...this.sourceRows];\n const [row] = rows.splice(currentIndex, 1);\n rows.splice(originalIndex, 0, row);\n\n const grid = this.grid as unknown as InternalGrid;\n grid._rows = rows;\n grid._focusRow = originalIndex;\n grid._focusCol = this.lastFocusCol;\n grid.refreshVirtualWindow(true);\n ensureCellVisible(grid);\n }\n }\n\n /**\n * Execute a row move and emit the event.\n */\n private executeMove(row: unknown, fromIndex: number, toIndex: number, source: 'keyboard' | 'drag'): void {\n const rows = [...this.sourceRows];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n\n const detail: RowMoveDetail = {\n row,\n fromIndex,\n toIndex,\n rows,\n source,\n };\n\n // Emit cancelable event\n const cancelled = this.emitCancelable('row-move', detail);\n if (!cancelled) {\n // Update the grid's rows\n this.grid.rows = rows;\n }\n }\n\n /**\n * Get the row index from a row element by checking data-row attribute on cells.\n * This is consistent with how other plugins retrieve row indices.\n */\n private getRowIndex(rowEl: HTMLElement): number {\n const cell = rowEl.querySelector('.cell[data-row]');\n return cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n }\n\n /**\n * Clear all drag-related classes from rows.\n */\n private clearDragClasses(): void {\n this.gridElement?.querySelectorAll('.data-grid-row').forEach((row) => {\n row.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\n });\n }\n\n /**\n * Clear the debounce timer.\n */\n private clearDebounceTimer(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n // #endregion\n}\n"],"names":["ROW_DRAG_HANDLE_FIELD","RowReorderPlugin","BaseGridPlugin","styles","columns","dragHandleColumn","container","gridEl","handle","handleEl","rowEl","row","event","grid","focusRow","rows","direction","toIndex","fromIndex","e","rowIndex","targetIndex","rect","midY","isBefore","focusCol","movedRow","ensureCellVisible","originalIndex","currentIndex","detail","source","cell"],"mappings":"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"}
1
+ {"version":3,"file":"row-reorder.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/row-reorder/RowReorderPlugin.ts"],"sourcesContent":["/**\n * Row Reordering Plugin\n *\n * Provides keyboard and drag-drop row reordering functionality for tbw-grid.\n * Supports Ctrl+Up/Down keyboard shortcuts and optional drag handle column.\n */\n\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { BaseGridPlugin } from '../../core/plugin/base-plugin';\nimport type { ColumnConfig, InternalGrid } from '../../core/types';\nimport styles from './row-reorder.css?inline';\nimport type { PendingMove, RowMoveDetail, RowReorderConfig } from './types';\n\n/** Field name for the drag handle column */\nexport const ROW_DRAG_HANDLE_FIELD = '__tbw_row_drag';\n\n/**\n * Row Reorder Plugin for tbw-grid\n *\n * Enables row reordering via keyboard shortcuts (Ctrl+Up/Down) and drag-drop.\n * Supports validation callbacks and debounced keyboard moves.\n *\n * ## Installation\n *\n * ```ts\n * import { RowReorderPlugin } from '@toolbox-web/grid/plugins/row-reorder';\n * ```\n *\n * ## Configuration Options\n *\n * | Option | Type | Default | Description |\n * |--------|------|---------|-------------|\n * | `enableKeyboard` | `boolean` | `true` | Enable Ctrl+Up/Down shortcuts |\n * | `showDragHandle` | `boolean` | `true` | Show drag handle column |\n * | `dragHandlePosition` | `'left' \\| 'right'` | `'left'` | Drag handle column position |\n * | `dragHandleWidth` | `number` | `40` | Drag handle column width |\n * | `canMove` | `function` | - | Validation callback |\n * | `debounceMs` | `number` | `300` | Debounce time for keyboard moves |\n * | `animation` | `false \\| 'flip'` | `'flip'` | Animation for row moves |\n *\n * ## Keyboard Shortcuts\n *\n * | Key | Action |\n * |-----|--------|\n * | `Ctrl + ↑` | Move focused row up |\n * | `Ctrl + ↓` | Move focused row down |\n *\n * ## Events\n *\n * | Event | Detail | Cancelable | Description |\n * |-------|--------|------------|-------------|\n * | `row-move` | `RowMoveDetail` | Yes | Fired when a row move is attempted |\n *\n * @example Basic Row Reordering\n * ```ts\n * import '@toolbox-web/grid';\n * import { RowReorderPlugin } from '@toolbox-web/grid/plugins/row-reorder';\n *\n * const grid = document.querySelector('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'id', header: 'ID' },\n * { field: 'name', header: 'Name' },\n * ],\n * plugins: [new RowReorderPlugin()],\n * };\n *\n * grid.addEventListener('row-move', (e) => {\n * console.log('Row moved from', e.detail.fromIndex, 'to', e.detail.toIndex);\n * });\n * ```\n *\n * @example With Validation\n * ```ts\n * new RowReorderPlugin({\n * canMove: (row, fromIndex, toIndex, direction) => {\n * // Prevent moving locked rows\n * return !row.locked;\n * },\n * })\n * ```\n *\n * @see {@link RowReorderConfig} for all configuration options\n * @see {@link RowMoveDetail} for the event detail structure\n */\nexport class RowReorderPlugin extends BaseGridPlugin<RowReorderConfig> {\n /** @internal */\n readonly name = 'rowReorder';\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n protected override get defaultConfig(): Partial<RowReorderConfig> {\n return {\n enableKeyboard: true,\n showDragHandle: true,\n dragHandlePosition: 'left',\n dragHandleWidth: 40,\n debounceMs: 150,\n animation: 'flip',\n };\n }\n\n /**\n * Resolve animation type from plugin config.\n * Uses base class isAnimationEnabled to respect grid-level settings.\n */\n private get animationType(): false | 'flip' {\n // Check if animations are globally disabled\n if (!this.isAnimationEnabled) return false;\n\n // Plugin config (with default from defaultConfig)\n if (this.config.animation !== undefined) return this.config.animation;\n\n return 'flip'; // Plugin default\n }\n\n // #region Internal State\n private isDragging = false;\n private draggedRowIndex: number | null = null;\n private dropRowIndex: number | null = null;\n private pendingMove: PendingMove | null = null;\n private debounceTimer: ReturnType<typeof setTimeout> | null = null;\n /** Column index to use when flushing pending move */\n private lastFocusCol = 0;\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\n override detach(): void {\n this.clearDebounceTimer();\n this.isDragging = false;\n this.draggedRowIndex = null;\n this.dropRowIndex = null;\n this.pendingMove = null;\n }\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processColumns(columns: readonly ColumnConfig[]): ColumnConfig[] {\n if (!this.config.showDragHandle) {\n return [...columns];\n }\n\n const dragHandleColumn: ColumnConfig = {\n field: ROW_DRAG_HANDLE_FIELD,\n header: '',\n width: this.config.dragHandleWidth ?? 40,\n resizable: false,\n sortable: false,\n filterable: false,\n meta: {\n lockPosition: true,\n suppressMovable: true,\n utility: true,\n },\n viewRenderer: () => {\n const container = document.createElement('div');\n container.className = 'dg-row-drag-handle';\n container.setAttribute('aria-label', 'Drag to reorder');\n container.setAttribute('role', 'button');\n container.setAttribute('tabindex', '-1');\n // Set draggable as property (not just attribute) for proper HTML5 drag-drop\n container.draggable = true;\n\n // Use the grid's configured dragHandle icon\n this.setIcon(container, this.resolveIcon('dragHandle'));\n\n return container;\n },\n };\n\n // Position the drag handle column\n if (this.config.dragHandlePosition === 'right') {\n return [...columns, dragHandleColumn];\n }\n return [dragHandleColumn, ...columns];\n }\n\n /** @internal */\n override afterRender(): void {\n if (!this.config.showDragHandle) return;\n\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n // Set up drag start/end listeners on drag handles\n const handles = gridEl.querySelectorAll('.dg-row-drag-handle');\n handles.forEach((handle) => {\n const handleEl = handle as HTMLElement;\n if (handleEl.getAttribute('data-drag-bound')) return;\n handleEl.setAttribute('data-drag-bound', 'true');\n\n const rowEl = handleEl.closest('.data-grid-row') as HTMLElement;\n if (!rowEl) return;\n\n // Set up dragstart/dragend on the handle\n this.setupHandleDragListeners(handleEl, rowEl);\n });\n\n // Set up drop target listeners on ALL rows (not just the handle's row)\n const rows = gridEl.querySelectorAll('.data-grid-row');\n rows.forEach((row) => {\n const rowEl = row as HTMLElement;\n if (rowEl.getAttribute('data-drop-bound')) return;\n rowEl.setAttribute('data-drop-bound', 'true');\n\n this.setupRowDropListeners(rowEl);\n });\n }\n\n /**\n * Handle Ctrl+Arrow keyboard shortcuts for row reordering.\n * @internal\n */\n override onKeyDown(event: KeyboardEvent): boolean | void {\n if (!this.config.enableKeyboard) return;\n if (!event.ctrlKey || (event.key !== 'ArrowUp' && event.key !== 'ArrowDown')) {\n return;\n }\n\n const grid = this.grid as unknown as InternalGrid;\n const focusRow = grid._focusRow;\n // Use _rows (current visual state) for keyboard moves, not sourceRows\n // This ensures rapid moves work correctly since we update _rows directly\n // Fallback to sourceRows for compatibility with tests\n const rows = grid._rows ?? this.sourceRows;\n\n if (focusRow < 0 || focusRow >= rows.length) return;\n\n const direction = event.key === 'ArrowUp' ? 'up' : 'down';\n const toIndex = direction === 'up' ? focusRow - 1 : focusRow + 1;\n\n // Check bounds\n if (toIndex < 0 || toIndex >= rows.length) return;\n\n const row = rows[focusRow];\n\n // Validate move\n if (this.config.canMove && !this.config.canMove(row, focusRow, toIndex, direction)) {\n return;\n }\n\n // Debounce keyboard moves\n this.handleKeyboardMove(row, focusRow, toIndex, direction, grid._focusCol);\n\n event.preventDefault();\n event.stopPropagation();\n return true;\n }\n\n /**\n * Flush pending keyboard moves when user clicks a cell.\n * This commits the move immediately so focus works correctly.\n * @internal\n */\n override onCellClick(): void {\n // If there's a pending keyboard move, flush it immediately\n // so the user's click focus isn't overridden\n this.flushPendingMove();\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Move a row to a new position programmatically.\n * @param fromIndex - Current index of the row\n * @param toIndex - Target index\n */\n moveRow(fromIndex: number, toIndex: number): void {\n const rows = [...this.sourceRows];\n if (fromIndex < 0 || fromIndex >= rows.length) return;\n if (toIndex < 0 || toIndex >= rows.length) return;\n if (fromIndex === toIndex) return;\n\n const direction = toIndex < fromIndex ? 'up' : 'down';\n const row = rows[fromIndex];\n\n // Validate move\n if (this.config.canMove && !this.config.canMove(row, fromIndex, toIndex, direction)) {\n return;\n }\n\n this.executeMove(row, fromIndex, toIndex, 'keyboard');\n }\n\n /**\n * Check if a row can be moved to a position.\n * @param fromIndex - Current index of the row\n * @param toIndex - Target index\n */\n canMoveRow(fromIndex: number, toIndex: number): boolean {\n const rows = this.sourceRows;\n if (fromIndex < 0 || fromIndex >= rows.length) return false;\n if (toIndex < 0 || toIndex >= rows.length) return false;\n if (fromIndex === toIndex) return false;\n\n if (!this.config.canMove) return true;\n\n const direction = toIndex < fromIndex ? 'up' : 'down';\n return this.config.canMove(rows[fromIndex], fromIndex, toIndex, direction);\n }\n // #endregion\n\n // #region Private Methods\n\n /**\n * Set up drag start/end listeners on the drag handle element.\n */\n private setupHandleDragListeners(handleEl: HTMLElement, rowEl: HTMLElement): void {\n handleEl.addEventListener('dragstart', (e: DragEvent) => {\n const rowIndex = this.getRowIndex(rowEl);\n if (rowIndex < 0) return;\n\n this.isDragging = true;\n this.draggedRowIndex = rowIndex;\n\n if (e.dataTransfer) {\n e.dataTransfer.effectAllowed = 'move';\n e.dataTransfer.setData('text/plain', String(rowIndex));\n }\n\n rowEl.classList.add('dragging');\n });\n\n handleEl.addEventListener('dragend', () => {\n this.isDragging = false;\n this.draggedRowIndex = null;\n this.dropRowIndex = null;\n this.clearDragClasses();\n });\n }\n\n /**\n * Set up drop target listeners on a row element.\n * All rows are valid drop targets during drag operations.\n */\n private setupRowDropListeners(rowEl: HTMLElement): void {\n rowEl.addEventListener('dragover', (e: DragEvent) => {\n e.preventDefault();\n if (!this.isDragging || this.draggedRowIndex === null) return;\n\n const targetIndex = this.getRowIndex(rowEl);\n if (targetIndex < 0 || targetIndex === this.draggedRowIndex) return;\n\n const rect = rowEl.getBoundingClientRect();\n const midY = rect.top + rect.height / 2;\n const isBefore = e.clientY < midY;\n\n this.dropRowIndex = isBefore ? targetIndex : targetIndex + 1;\n\n rowEl.classList.add('drop-target');\n rowEl.classList.toggle('drop-before', isBefore);\n rowEl.classList.toggle('drop-after', !isBefore);\n });\n\n rowEl.addEventListener('dragleave', () => {\n rowEl.classList.remove('drop-target', 'drop-before', 'drop-after');\n });\n\n rowEl.addEventListener('drop', (e: DragEvent) => {\n e.preventDefault();\n const fromIndex = this.draggedRowIndex;\n let toIndex = this.dropRowIndex;\n\n if (!this.isDragging || fromIndex === null || toIndex === null) {\n return;\n }\n\n // Adjust toIndex if dropping after the dragged row\n if (toIndex > fromIndex) {\n toIndex--;\n }\n\n if (fromIndex !== toIndex) {\n const rows = this.sourceRows;\n const row = rows[fromIndex];\n const direction = toIndex < fromIndex ? 'up' : 'down';\n\n // Validate move\n if (!this.config.canMove || this.config.canMove(row, fromIndex, toIndex, direction)) {\n this.executeMove(row, fromIndex, toIndex, 'drag');\n }\n }\n });\n }\n\n /**\n * Handle debounced keyboard moves.\n * Rows move immediately for visual feedback, but the event emission is debounced.\n */\n private handleKeyboardMove(\n row: unknown,\n fromIndex: number,\n toIndex: number,\n direction: 'up' | 'down',\n focusCol: number,\n ): void {\n // Track move for debounced event emission\n if (!this.pendingMove) {\n this.pendingMove = {\n originalIndex: fromIndex,\n currentIndex: toIndex,\n row,\n };\n } else {\n // Update the current index for rapid moves\n this.pendingMove.currentIndex = toIndex;\n }\n\n // Store focus column for flush\n this.lastFocusCol = focusCol;\n\n // Move rows immediately for visual feedback\n // Use _rows (current visual state) for rapid moves, not sourceRows\n // Fallback to sourceRows for compatibility with tests\n const grid = this.grid as unknown as InternalGrid;\n const rows = [...(grid._rows ?? this.sourceRows)];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n\n // Update grid rows immediately (without triggering change events)\n grid._rows = rows;\n\n // Update focus to follow the row\n grid._focusRow = toIndex;\n grid._focusCol = focusCol;\n\n // Refresh virtual window directly - this re-renders from _rows\n // without overwriting _rows from #rows (which requestRender does)\n grid.refreshVirtualWindow(true);\n\n // Ensure focus styling is applied after the row rebuild\n ensureCellVisible(grid);\n\n // Debounce the event emission only\n this.clearDebounceTimer();\n this.debounceTimer = setTimeout(() => {\n this.flushPendingMove();\n }, this.config.debounceMs ?? 300);\n }\n\n /**\n * Flush the pending move by emitting the event.\n * Called when debounce timer fires or user clicks elsewhere.\n */\n private flushPendingMove(): void {\n this.clearDebounceTimer();\n\n if (!this.pendingMove) return;\n\n const { originalIndex, currentIndex, row: movedRow } = this.pendingMove;\n this.pendingMove = null;\n\n if (originalIndex === currentIndex) return;\n\n // Emit cancelable event\n const detail: RowMoveDetail = {\n row: movedRow,\n fromIndex: originalIndex,\n toIndex: currentIndex,\n rows: [...this.sourceRows],\n source: 'keyboard',\n };\n\n const cancelled = this.emitCancelable('row-move', detail);\n if (cancelled) {\n // Revert to original position\n const rows = [...this.sourceRows];\n const [row] = rows.splice(currentIndex, 1);\n rows.splice(originalIndex, 0, row);\n\n const grid = this.grid as unknown as InternalGrid;\n grid._rows = rows;\n grid._focusRow = originalIndex;\n grid._focusCol = this.lastFocusCol;\n grid.refreshVirtualWindow(true);\n ensureCellVisible(grid);\n }\n }\n\n /**\n * Execute a row move and emit the event.\n */\n private executeMove(row: unknown, fromIndex: number, toIndex: number, source: 'keyboard' | 'drag'): void {\n const rows = [...this.sourceRows];\n const [movedRow] = rows.splice(fromIndex, 1);\n rows.splice(toIndex, 0, movedRow);\n\n const detail: RowMoveDetail = {\n row,\n fromIndex,\n toIndex,\n rows,\n source,\n };\n\n // Emit cancelable event\n const cancelled = this.emitCancelable('row-move', detail);\n if (!cancelled) {\n // Apply with animation if enabled\n if (this.animationType === 'flip' && this.gridElement) {\n const oldPositions = this.captureRowPositions();\n this.grid.rows = rows;\n // Wait for the scheduler to process the virtual window update (RAF)\n // before running FLIP animation on the new rows\n requestAnimationFrame(() => {\n void this.gridElement.offsetHeight;\n this.animateFLIP(oldPositions, fromIndex, toIndex);\n });\n } else {\n // No animation, just update rows\n this.grid.rows = rows;\n }\n }\n }\n\n /**\n * Capture row positions before reorder.\n * Maps visual row index to its top position.\n */\n private captureRowPositions(): Map<number, number> {\n const positions = new Map<number, number>();\n this.gridElement?.querySelectorAll('.data-grid-row').forEach((row) => {\n const rowIndex = this.getRowIndex(row as HTMLElement);\n if (rowIndex >= 0) {\n positions.set(rowIndex, row.getBoundingClientRect().top);\n }\n });\n return positions;\n }\n\n /**\n * Apply FLIP animation for row reorder.\n * Uses CSS transitions - JS sets initial transform and toggles class.\n * @param oldPositions - Row positions captured before DOM change\n * @param fromIndex - Original index of moved row\n * @param toIndex - New index of moved row\n */\n private animateFLIP(oldPositions: Map<number, number>, fromIndex: number, toIndex: number): void {\n const gridEl = this.gridElement;\n if (!gridEl || oldPositions.size === 0) return;\n\n // Calculate which row indices were affected and their new positions\n const minIndex = Math.min(fromIndex, toIndex);\n const maxIndex = Math.max(fromIndex, toIndex);\n\n // Build a map of new row index -> delta Y\n const rowsToAnimate: { el: HTMLElement; deltaY: number }[] = [];\n\n gridEl.querySelectorAll('.data-grid-row').forEach((row) => {\n const rowEl = row as HTMLElement;\n const newRowIndex = this.getRowIndex(rowEl);\n if (newRowIndex < 0 || newRowIndex < minIndex || newRowIndex > maxIndex) return;\n\n // Figure out what this row's old index was\n let oldIndex: number;\n if (newRowIndex === toIndex) {\n // This is the moved row\n oldIndex = fromIndex;\n } else if (fromIndex < toIndex) {\n // Row moved down: rows in between shifted up by 1\n oldIndex = newRowIndex + 1;\n } else {\n // Row moved up: rows in between shifted down by 1\n oldIndex = newRowIndex - 1;\n }\n\n const oldTop = oldPositions.get(oldIndex);\n if (oldTop === undefined) return;\n\n const newTop = rowEl.getBoundingClientRect().top;\n const deltaY = oldTop - newTop;\n\n if (Math.abs(deltaY) > 1) {\n rowsToAnimate.push({ el: rowEl, deltaY });\n }\n });\n\n if (rowsToAnimate.length === 0) return;\n\n // Set initial transform (First → Last position offset)\n rowsToAnimate.forEach(({ el, deltaY }) => {\n el.style.transform = `translateY(${deltaY}px)`;\n });\n\n // Force reflow then animate to final position via CSS transition\n void gridEl.offsetHeight;\n\n const duration = this.animationDuration;\n\n requestAnimationFrame(() => {\n rowsToAnimate.forEach(({ el }) => {\n el.classList.add('flip-animating');\n el.style.transform = '';\n });\n\n // Cleanup after animation\n setTimeout(() => {\n rowsToAnimate.forEach(({ el }) => {\n el.style.transform = '';\n el.classList.remove('flip-animating');\n });\n }, duration + 50);\n });\n }\n\n /**\n * Get the row index from a row element by checking data-row attribute on cells.\n * This is consistent with how other plugins retrieve row indices.\n */\n private getRowIndex(rowEl: HTMLElement): number {\n const cell = rowEl.querySelector('.cell[data-row]');\n return cell ? parseInt(cell.getAttribute('data-row') ?? '-1', 10) : -1;\n }\n\n /**\n * Clear all drag-related classes from rows.\n */\n private clearDragClasses(): void {\n this.gridElement?.querySelectorAll('.data-grid-row').forEach((row) => {\n row.classList.remove('dragging', 'drop-target', 'drop-before', 'drop-after');\n });\n }\n\n /**\n * Clear the debounce timer.\n */\n private clearDebounceTimer(): void {\n if (this.debounceTimer) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = null;\n }\n }\n // #endregion\n}\n"],"names":["ROW_DRAG_HANDLE_FIELD","RowReorderPlugin","BaseGridPlugin","styles","columns","dragHandleColumn","container","gridEl","handle","handleEl","rowEl","row","event","grid","focusRow","rows","direction","toIndex","fromIndex","e","rowIndex","targetIndex","rect","midY","isBefore","focusCol","movedRow","ensureCellVisible","originalIndex","currentIndex","detail","source","oldPositions","positions","minIndex","maxIndex","rowsToAnimate","newRowIndex","oldIndex","oldTop","newTop","deltaY","el","duration","cell"],"mappings":"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"}
@@ -1,4 +1,4 @@
1
- (function(h,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("../../core/internal/utils"),require("../../core/plugin/base-plugin"),require("../../core/plugin/expander-column")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/utils","../../core/plugin/base-plugin","../../core/plugin/expander-column"],g):(h=typeof globalThis<"u"?globalThis:h||self,g(h.TbwGridPlugin_selection={},h.TbwGrid,h.TbwGrid,h.TbwGrid))})(this,(function(h,g,y,f){"use strict";function w(r){return{startRow:Math.min(r.startRow,r.endRow),startCol:Math.min(r.startCol,r.endCol),endRow:Math.max(r.startRow,r.endRow),endCol:Math.max(r.startCol,r.endCol)}}function A(r){const e=w(r);return{from:{row:e.startRow,col:e.startCol},to:{row:e.endRow,col:e.endCol}}}function C(r){return r.map(A)}function S(r,e,t){const s=w(t);return r>=s.startRow&&r<=s.endRow&&e>=s.startCol&&e<=s.endCol}function p(r,e,t){return t.some(s=>S(r,e,s))}function v(r){const e=[],t=w(r);for(let s=t.startRow;s<=t.endRow;s++)for(let n=t.startCol;n<=t.endCol;n++)e.push({row:s,col:n});return e}function x(r){const e=new Map;for(const t of r)for(const s of v(t))e.set(`${s.row},${s.col}`,s);return[...e.values()]}function R(r,e){return{startRow:r.row,startCol:r.col,endRow:e.row,endCol:e.col}}function m(r,e){const t=w(r),s=w(e);return t.startRow===s.startRow&&t.startCol===s.startCol&&t.endRow===s.endRow&&t.endCol===s.endCol}const I="@layer tbw-plugins{tbw-grid.selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}tbw-grid[data-has-focus] .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%))}tbw-grid[data-selection-mode=row] .cell-focus{outline:none}tbw-grid .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg)}tbw-grid .data-grid-row>.cell.selected.top{border-top:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.bottom{border-bottom:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.first{border-left:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.last{border-right:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-color-warning-bg, rgba(255, 243, 205, .5))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm, .8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}}";function k(r,e,t){if(r==="cell"&&e.selectedCell)return{mode:r,ranges:[{from:{row:e.selectedCell.row,col:e.selectedCell.col},to:{row:e.selectedCell.row,col:e.selectedCell.col}}]};if(r==="row"&&e.selected.size>0){const s=[...e.selected].map(n=>({from:{row:n,col:0},to:{row:n,col:t-1}}));return{mode:r,ranges:s}}return r==="range"&&e.ranges.length>0?{mode:r,ranges:C(e.ranges)}:{mode:r,ranges:[]}}class E extends y.BaseGridPlugin{static manifest={configRules:[{id:"selection/range-dblclick",severity:"warn",message:`"triggerOn: 'dblclick'" has no effect when mode is "range".
1
+ (function(h,g){typeof exports=="object"&&typeof module<"u"?g(exports,require("../../core/internal/utils"),require("../../core/plugin/base-plugin"),require("../../core/plugin/expander-column")):typeof define=="function"&&define.amd?define(["exports","../../core/internal/utils","../../core/plugin/base-plugin","../../core/plugin/expander-column"],g):(h=typeof globalThis<"u"?globalThis:h||self,g(h.TbwGridPlugin_selection={},h.TbwGrid,h.TbwGrid,h.TbwGrid))})(this,(function(h,g,y,f){"use strict";function w(r){return{startRow:Math.min(r.startRow,r.endRow),startCol:Math.min(r.startCol,r.endCol),endRow:Math.max(r.startRow,r.endRow),endCol:Math.max(r.startCol,r.endCol)}}function A(r){const e=w(r);return{from:{row:e.startRow,col:e.startCol},to:{row:e.endRow,col:e.endCol}}}function C(r){return r.map(A)}function S(r,e,t){const s=w(t);return r>=s.startRow&&r<=s.endRow&&e>=s.startCol&&e<=s.endCol}function p(r,e,t){return t.some(s=>S(r,e,s))}function v(r){const e=[],t=w(r);for(let s=t.startRow;s<=t.endRow;s++)for(let n=t.startCol;n<=t.endCol;n++)e.push({row:s,col:n});return e}function x(r){const e=new Map;for(const t of r)for(const s of v(t))e.set(`${s.row},${s.col}`,s);return[...e.values()]}function R(r,e){return{startRow:r.row,startCol:r.col,endRow:e.row,endCol:e.col}}function m(r,e){const t=w(r),s=w(e);return t.startRow===s.startRow&&t.startCol===s.startCol&&t.endRow===s.endRow&&t.endCol===s.endCol}const I="@layer tbw-plugins{tbw-grid.selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}tbw-grid[data-has-focus] .data-grid-row.row-focus{background-color:var(--tbw-focus-background, rgba(from var(--tbw-color-accent) r g b / 12%))}tbw-grid[data-selection-mode=row] .cell-focus{outline:none}tbw-grid .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg)}tbw-grid .data-grid-row>.cell.selected.top{border-top:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.bottom{border-bottom:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.first{border-left:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row>.cell.selected.last{border-right:2px solid var(--tbw-range-border-color)}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg, rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm, .8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}}";function k(r,e,t){if(r==="cell"&&e.selectedCell)return{mode:r,ranges:[{from:{row:e.selectedCell.row,col:e.selectedCell.col},to:{row:e.selectedCell.row,col:e.selectedCell.col}}]};if(r==="row"&&e.selected.size>0){const s=[...e.selected].map(n=>({from:{row:n,col:0},to:{row:n,col:t-1}}));return{mode:r,ranges:s}}return r==="range"&&e.ranges.length>0?{mode:r,ranges:C(e.ranges)}:{mode:r,ranges:[]}}class E extends y.BaseGridPlugin{static manifest={configRules:[{id:"selection/range-dblclick",severity:"warn",message:`"triggerOn: 'dblclick'" has no effect when mode is "range".
2
2
  → Range selection uses drag interaction (mousedown → mousemove), not click events.
3
3
  → The "triggerOn" option only affects "cell" and "row" selection modes.`,check:e=>e.mode==="range"&&e.triggerOn==="dblclick"}]};name="selection";styles=I;get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0}}selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;selectedCell=null;isSelectionEnabled(){return this.config.enabled===!1?!1:this.grid.effectiveConfig?.selectable!==!1}checkSelectable(e,t){const{isSelectable:s}=this.config;if(!s)return!0;const n=this.rows[e];if(!n)return!1;const i=t!==void 0?this.columns[t]:void 0;return s(n,e,i,t)}isRowSelectable(e){return this.checkSelectable(e)}isCellSelectable(e,t){return this.checkSelectable(e,t)}detach(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.pendingKeyboardUpdate=null}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{rowIndex:t,colIndex:s,originalEvent:n}=e,{mode:i,triggerOn:l="click"}=this.config;if(n.type!==l)return!1;const o=this.columns[s],d=o&&f.isUtilityColumn(o);if(i==="cell"){if(d||!this.isCellSelectable(t,s))return!1;const c=this.selectedCell;return c&&c.row===t&&c.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()),this.requestAfterRender()),!1}if(i==="row")return!this.isRowSelectable(t)||this.selected.size===1&&this.selected.has(t)||(this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.emit("selection-change",this.#e()),this.requestAfterRender()),!1;if(i==="range"){if(d||!this.isCellSelectable(t,s))return!1;const c=n.shiftKey,u=n.ctrlKey||n.metaKey;if(c&&this.cellAnchor){const a=R(this.cellAnchor,{row:t,col:s}),b=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(b&&m(b,a))return!1;u?this.ranges.length>0?this.ranges[this.ranges.length-1]=a:this.ranges.push(a):this.ranges=[a],this.activeRange=a}else if(u){const a={startRow:t,startCol:s,endRow:t,endCol:s};this.ranges.push(a),this.activeRange=a,this.cellAnchor={row:t,col:s}}else{const a={startRow:t,startCol:s,endRow:t,endCol:s};if(this.ranges.length===1&&m(this.ranges[0],a))return!1;this.ranges=[a],this.activeRange=a,this.cellAnchor={row:t,col:s}}return this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}return!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;const{mode:t}=this.config,n=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if(e.key==="Escape")return t==="cell"?this.selectedCell=null:t==="row"?(this.selected.clear(),this.anchor=null):t==="range"&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#e()),this.requestAfterRender(),!0;if(t==="cell"&&n)return queueMicrotask(()=>{const i=this.grid._focusRow,l=this.grid._focusCol;this.isCellSelectable(i,l)?this.selectedCell={row:i,col:l}:this.selectedCell=null,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="row"&&(e.key==="ArrowUp"||e.key==="ArrowDown"))return queueMicrotask(()=>{const i=this.grid._focusRow;this.isRowSelectable(i)?(this.selected.clear(),this.selected.add(i),this.lastSelected=i):this.selected.clear(),this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if(t==="range"&&n){const i=e.key==="Tab",l=e.shiftKey&&!i;return l&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:l},queueMicrotask(()=>this.requestAfterRender()),!1}if(t==="range"&&e.key==="a"&&(e.ctrlKey||e.metaKey)){const i=this.rows.length,l=this.columns.length;if(i>0&&l>0){e.preventDefault(),e.stopPropagation();const o={startRow:0,startCol:0,endRow:i-1,endCol:l-1};return this.ranges=[o],this.activeRange=o,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0}}return!1}onCellMouseDown(e){if(!this.isSelectionEnabled()||this.config.mode!=="range"||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;const t=this.columns[e.colIndex];if(t&&f.isUtilityColumn(t)||!this.isCellSelectable(e.rowIndex,e.colIndex)||e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const s=e.rowIndex,n=e.colIndex,i=e.originalEvent.ctrlKey||e.originalEvent.metaKey,l={startRow:s,startCol:n,endRow:s,endCol:n};return!i&&this.ranges.length===1&&m(this.ranges[0],l)?(this.cellAnchor={row:s,col:n},!0):(this.cellAnchor={row:s,col:n},i||(this.ranges=[]),this.ranges.push(l),this.activeRange=l,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0)}onCellMouseMove(e){if(!this.isSelectionEnabled()||this.config.mode!=="range"||!this.isDragging||!this.cellAnchor||e.rowIndex===void 0||e.colIndex===void 0||e.rowIndex<0)return;let t=e.colIndex;const s=this.columns[t];if(s&&f.isUtilityColumn(s)){const l=this.columns.findIndex(o=>!f.isUtilityColumn(o));l>=0&&(t=l)}const n=R(this.cellAnchor,{row:e.rowIndex,col:t}),i=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return i&&m(i,n)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=n:this.ranges.push(n),this.activeRange=n,this.emit("selection-change",this.#e()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&this.config.mode==="range"&&this.isDragging)return this.isDragging=!1,!0}#t(){const e=this.gridElement;if(!e)return;const{mode:t}=this.config,s=!!this.config.isSelectable;e.querySelectorAll(".cell").forEach(l=>{l.classList.remove("selected","top","bottom","first","last"),s&&l.removeAttribute("data-selectable")});const i=e.querySelectorAll(".data-grid-row");if(i.forEach(l=>{l.classList.remove("selected","row-focus"),s&&l.removeAttribute("data-selectable")}),t==="row"&&(g.clearCellFocus(e),i.forEach(l=>{const o=l.querySelector(".cell[data-row]"),d=g.getRowIndexFromCell(o);d>=0&&(s&&!this.isRowSelectable(d)&&l.setAttribute("data-selectable","false"),this.selected.has(d)&&l.classList.add("selected","row-focus"))})),(t==="cell"||t==="range")&&s&&e.querySelectorAll(".cell[data-row][data-col]").forEach(o=>{const d=parseInt(o.getAttribute("data-row")??"-1",10),c=parseInt(o.getAttribute("data-col")??"-1",10);d>=0&&c>=0&&(this.isCellSelectable(d,c)||o.setAttribute("data-selectable","false"))}),t==="range"&&this.ranges.length>0){g.clearCellFocus(e);const l=this.activeRange?w(this.activeRange):null,o=this.columns.findIndex(c=>!f.isUtilityColumn(c));this.columns.length-1,e.querySelectorAll(".cell[data-row][data-col]").forEach(c=>{const u=parseInt(c.getAttribute("data-row")??"-1",10),a=parseInt(c.getAttribute("data-col")??"-1",10);if(u>=0&&a>=0){const b=this.columns[a];if(b&&f.isUtilityColumn(b))return;if(p(u,a,this.ranges)&&(c.classList.add("selected"),l)){u===l.startRow&&c.classList.add("top"),u===l.endRow&&c.classList.add("bottom");const K=Math.max(l.startCol,o);a===K&&c.classList.add("first"),a===l.endCol&&c.classList.add("last")}}})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.children[0],{mode:s}=this.config;if(this.pendingKeyboardUpdate&&s==="range"){const{shiftKey:n}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const i=this.grid._focusRow,l=this.grid._focusCol;if(n&&this.cellAnchor){const o=R(this.cellAnchor,{row:i,col:l});this.ranges=[o],this.activeRange=o}else n||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:i,col:l});this.emit("selection-change",this.#e())}this.grid.setAttribute("data-selection-mode",s),t&&t.classList.toggle("selecting",this.isDragging),this.#t()}onScrollRender(){this.isSelectionEnabled()&&this.#t()}getSelection(){return{mode:this.config.mode,ranges:this.#e().ranges,anchor:this.cellAnchor}}getSelectedCells(){return x(this.ranges)}isCellSelected(e,t){return p(e,t,this.ranges)}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(t=>({startRow:t.from.row,startCol:t.from.col,endRow:t.to.row,endCol:t.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",{mode:this.config.mode,ranges:C(this.ranges)}),this.requestAfterRender()}#e(){return k(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length)}}h.SelectionPlugin=E,Object.defineProperty(h,Symbol.toStringTag,{value:"Module"})}));
4
4
  //# sourceMappingURL=selection.umd.js.map