@toolbox-web/grid 3.6.0 → 3.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +1 -1
- package/all.js +1 -1
- package/all.js.map +1 -1
- package/index.js +1 -1
- package/index.js.map +1 -1
- package/lib/core/internal/empty.d.ts +14 -6
- package/lib/core/types.d.ts +4 -0
- package/lib/features/registry.js +1 -1
- package/lib/plugins/clipboard/index.js +1 -1
- package/lib/plugins/clipboard/index.js.map +1 -1
- package/lib/plugins/column-virtualization/index.js +1 -1
- package/lib/plugins/column-virtualization/index.js.map +1 -1
- package/lib/plugins/context-menu/index.js +1 -1
- package/lib/plugins/context-menu/index.js.map +1 -1
- package/lib/plugins/editing/index.js +1 -1
- package/lib/plugins/editing/index.js.map +1 -1
- package/lib/plugins/export/index.js +1 -1
- package/lib/plugins/export/index.js.map +1 -1
- package/lib/plugins/filtering/index.js +1 -1
- package/lib/plugins/filtering/index.js.map +1 -1
- package/lib/plugins/grouping-columns/index.js +1 -1
- package/lib/plugins/grouping-columns/index.js.map +1 -1
- package/lib/plugins/grouping-rows/index.js +1 -1
- package/lib/plugins/grouping-rows/index.js.map +1 -1
- package/lib/plugins/master-detail/index.js +1 -1
- package/lib/plugins/master-detail/index.js.map +1 -1
- package/lib/plugins/multi-sort/index.js +1 -1
- package/lib/plugins/multi-sort/index.js.map +1 -1
- package/lib/plugins/pinned-columns/index.js +1 -1
- package/lib/plugins/pinned-columns/index.js.map +1 -1
- package/lib/plugins/pinned-rows/index.js +1 -1
- package/lib/plugins/pinned-rows/index.js.map +1 -1
- package/lib/plugins/pivot/index.js +1 -1
- package/lib/plugins/pivot/index.js.map +1 -1
- package/lib/plugins/print/index.js +1 -1
- package/lib/plugins/print/index.js.map +1 -1
- package/lib/plugins/reorder-columns/index.js +1 -1
- package/lib/plugins/reorder-columns/index.js.map +1 -1
- package/lib/plugins/responsive/index.d.ts +1 -1
- package/lib/plugins/responsive/index.js +1 -1
- package/lib/plugins/responsive/index.js.map +1 -1
- package/lib/plugins/responsive/types.d.ts +36 -1
- package/lib/plugins/row-drag-drop/index.js +1 -1
- package/lib/plugins/row-drag-drop/index.js.map +1 -1
- package/lib/plugins/selection/index.js +1 -1
- package/lib/plugins/selection/index.js.map +1 -1
- package/lib/plugins/server-side/index.js +1 -1
- package/lib/plugins/server-side/index.js.map +1 -1
- package/lib/plugins/shell/index.js +1 -1
- package/lib/plugins/shell/index.js.map +1 -1
- package/lib/plugins/sticky-rows/index.js +1 -1
- package/lib/plugins/sticky-rows/index.js.map +1 -1
- package/lib/plugins/tooltip/index.js +1 -1
- package/lib/plugins/tooltip/index.js.map +1 -1
- package/lib/plugins/tree/index.js +1 -1
- package/lib/plugins/tree/index.js.map +1 -1
- package/lib/plugins/undo-redo/index.js +1 -1
- package/lib/plugins/undo-redo/index.js.map +1 -1
- package/lib/plugins/visibility/index.js +1 -1
- package/lib/plugins/visibility/index.js.map +1 -1
- package/package.json +3 -2
- package/themes/dg-theme-bootstrap.css +13 -4
- package/themes/dg-theme-contrast.css +3 -0
- package/themes/dg-theme-large.css +4 -0
- package/themes/dg-theme-material.css +3 -0
- package/themes/dg-theme-standard.css +12 -3
- package/themes/dg-theme-vibrant.css +10 -3
- package/umd/grid.all.umd.js +1 -1
- package/umd/grid.all.umd.js.map +1 -1
- package/umd/grid.umd.js +1 -1
- package/umd/grid.umd.js.map +1 -1
- package/umd/plugins/grouping-columns.umd.js +1 -1
- package/umd/plugins/grouping-columns.umd.js.map +1 -1
- package/umd/plugins/responsive.umd.js +1 -1
- package/umd/plugins/responsive.umd.js.map +1 -1
- package/umd/plugins/selection.umd.js +1 -1
- package/umd/plugins/selection.umd.js.map +1 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"responsive.umd.js","names":["#internalGrid","#isResponsive","#applyResponsiveState","#currentWidth","#checkBreakpoint","#activeBreakpoint","#parseLightDomCard","#buildHiddenColumnSets","#sortedBreakpoints","#resizeObserver","#debounceTimer","#hiddenColumnSet","#valueOnlyColumnSet","#measureCardHeightFromDOM","#checkMultiBreakpoint","#warnedAboutMissingBreakpoint","#originalRowHeight","#measuredCardHeight","#measuredGroupRowHeight","#lastCardRowCount","#getCardHeight","#getGroupRowHeight","#hasGroupRows","#countCardRows","#pendingRefresh"],"sources":["../../../../../libs/grid/src/lib/plugins/responsive/responsive.css?inline","../../../../../libs/grid/src/lib/plugins/responsive/responsive-plugin.ts"],"sourcesContent":["/**\n * Responsive Plugin CSS\n *\n * Card layout styles for narrow viewports.\n * Applied when [data-responsive] attribute is present on the grid.\n *\n * IMPORTANT: This CSS intentionally preserves the grid's virtualization system.\n * The row height observer will detect the new card height automatically and\n * recalculate the virtual window. Do NOT break virtualization by:\n * - Changing .rows-viewport overflow\n * - Changing .rows position\n * - Hiding .faux-vscroll\n */\n\n/* ============================================\n Animation / Transition Support\n ============================================ */\n\n/* Base transition setup for animated mode switching */\ntbw-grid[data-responsive-animate] {\n .data-grid-row,\n .data-grid-row > .cell {\n transition:\n opacity var(--tbw-responsive-duration, 200ms) ease-out,\n transform var(--tbw-responsive-duration, 200ms) ease-out;\n }\n}\n\n/* Fade-in when entering responsive mode */\ntbw-grid[data-responsive][data-responsive-animate] {\n .data-grid-row {\n animation: responsive-card-enter var(--tbw-responsive-duration, 200ms) ease-out;\n }\n}\n\n@keyframes responsive-card-enter {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* ============================================\n Responsive Card Layout\n ============================================ */\n\ntbw-grid[data-responsive] {\n /* Hide entire header section in card mode */\n .header {\n display: none !important;\n }\n\n /* Hide footer row in card mode (aggregations don't make sense in cards) */\n .footer-row {\n display: none;\n }\n\n /* Override horizontal scroll - single column flow */\n .tbw-scroll-area {\n overflow-x: hidden;\n min-width: 0 !important;\n }\n\n /* Override rows-body-wrapper to allow natural flow */\n .rows-body-wrapper {\n min-width: 0 !important;\n }\n\n /* Convert row from CSS Grid to block layout */\n .data-grid-row:not(.group-row) {\n display: block !important;\n grid-template-columns: none !important;\n padding: var(--tbw-cell-padding);\n padding-inline-start: var(--tbw-spacing-xl);\n border-bottom: 1px solid var(--tbw-color-border);\n min-height: auto !important;\n height: auto !important;\n /* Disable containment so row can grow to fit stacked cells */\n contain: none !important;\n content-visibility: visible !important;\n background: var(--tbw-color-bg);\n position: relative;\n\n &:nth-child(even) {\n background: var(--tbw-color-row-alt);\n }\n\n &:hover {\n background: var(--tbw-color-row-hover);\n }\n\n &[aria-selected='true'] {\n background: var(--tbw-color-selection);\n\n /* Selection indicator bar */\n &::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n top: 0;\n bottom: 0;\n width: 4px;\n background: var(--tbw-color-accent);\n }\n }\n }\n\n /* Stack cells vertically as label: value pairs */\n .data-grid-row:not(.group-row) > .cell {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n padding: var(--tbw-spacing-xs) var(--tbw-spacing-md);\n width: 100% !important;\n min-width: 0 !important;\n min-height: auto !important;\n height: auto !important;\n line-height: 1.5 !important;\n position: static !important;\n left: auto !important;\n right: auto !important;\n border: none !important;\n border-bottom: none !important;\n border-inline-end: none !important;\n background: transparent !important;\n white-space: normal !important;\n overflow: visible !important;\n\n /* Header label from data-header attribute */\n &::before {\n content: attr(data-header) ': ';\n font-weight: 600;\n color: var(--tbw-color-header-fg);\n flex-shrink: 0;\n margin-inline-end: var(--tbw-spacing-md);\n min-width: 100px;\n }\n\n /* Value takes remaining space */\n &::after {\n content: none;\n }\n }\n\n /* Hide utility columns (row expander, selection checkbox, etc.) */\n .cell[data-utility] {\n display: none !important;\n }\n}\n\n/* Hidden columns — specificity must beat `.data-grid-row:not(.group-row) > .cell` above */\ntbw-grid[data-responsive] .data-grid-row > .cell[data-responsive-hidden] {\n display: none !important;\n}\n\n/* Value-only columns in responsive mode (hides label, shows value full-width) */\ntbw-grid[data-responsive] .data-grid-row > .cell[data-responsive-value-only] {\n /* Override the header label ::before to hide it */\n &::before {\n display: none !important;\n }\n\n /* Value takes full width, no justify-content needed */\n justify-content: flex-start !important;\n font-weight: 500;\n}\n\n/* Opt-in: hide per-card field labels (the \"Name:\" prefix) when\n `hideHeader: true` is configured. Toggled by ResponsivePlugin. */\ntbw-grid[data-responsive][data-responsive-hide-header] .data-grid-row:not(.group-row) > .cell::before {\n display: none !important;\n}\n\n/* Non-responsive mode: progressive column hiding still works */\ntbw-grid:not([data-responsive]) .cell[data-responsive-hidden] {\n display: none !important;\n}\n\ntbw-grid:not([data-responsive]) .cell[data-responsive-value-only] {\n /* In table mode, value-only just hides the cell like normal */\n /* Actually no - in table mode headers are in header row, so this has no effect */\n}\n\n/* Hide pinned/aggregation rows in responsive mode - they don't make sense in card layout */\ntbw-grid[data-responsive] {\n .tbw-footer,\n .tbw-header-pinned,\n .tbw-pinned-rows,\n .tbw-aggregation-rows {\n display: none !important;\n }\n\n /* Reset min-widths that hold horizontal scroll width */\n .tbw-pinned-rows,\n .tbw-aggregation-rows,\n .tbw-aggregation-row {\n min-width: 0 !important;\n }\n}\n\n/* Custom card renderer styling */\ntbw-grid[data-responsive] .data-grid-row.responsive-card {\n /* Override default cell layout - custom content takes full width */\n display: block !important;\n padding: var(--tbw-cell-padding);\n border-bottom: 1px solid var(--tbw-color-border);\n\n /* Card content should not have cell-based constraints */\n > * {\n width: 100%;\n }\n\n /* Remove default cell ::before headers - custom renderer handles layout */\n .cell::before {\n display: none;\n }\n}\n","/**\n * Responsive Plugin\n *\n * Transforms the grid from tabular layout to a card/list layout when the grid\n * width falls below a configurable breakpoint. This enables grids to work in\n * narrow containers (split-pane UIs, mobile viewports, dashboard widgets).\n *\n * ## Installation\n *\n * ```ts\n * import { ResponsivePlugin } from '@toolbox-web/grid/plugins/responsive';\n *\n * const config: GridConfig = {\n * plugins: [new ResponsivePlugin({ breakpoint: 500 })],\n * };\n * ```\n *\n * ## How It Works\n *\n * 1. ResizeObserver monitors the grid element's width\n * 2. When `width < breakpoint`, adds `data-responsive` attribute to grid\n * 3. CSS transforms cells from horizontal to vertical layout\n * 4. Each cell displays \"Header: Value\" using CSS `::before` pseudo-element\n *\n * @see [Responsive Demo](?path=/story/grid-plugins-responsive--default)\n */\n\nimport { MISSING_BREAKPOINT } from '../../core/internal/diagnostics';\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { evalTemplateString, setSanitizedHTML } from '../../core/internal/sanitize';\nimport { BaseGridPlugin, type GridElement, type PluginManifest, type PluginQuery } from '../../core/plugin/base-plugin';\nimport type { GridHost } from '../../core/types';\nimport styles from './responsive.css?inline';\nimport type { BreakpointConfig, HiddenColumnConfig, ResponsiveChangeDetail, ResponsivePluginConfig } from './types';\n\n/**\n * Responsive Plugin for tbw-grid\n *\n * Adds automatic card layout mode when the grid width falls below a configurable\n * breakpoint. Perfect for responsive designs, split-pane UIs, and mobile viewports.\n *\n * @template T The row data type\n *\n * @example\n * ```ts\n * // Basic usage - switch to card layout below 500px\n * const config: GridConfig = {\n * plugins: [new ResponsivePlugin({ breakpoint: 500 })],\n * };\n * ```\n *\n * @example\n * ```ts\n * // Hide less important columns in card mode\n * const config: GridConfig = {\n * plugins: [\n * new ResponsivePlugin({\n * breakpoint: 600,\n * hiddenColumns: ['createdAt', 'updatedAt'],\n * }),\n * ],\n * };\n * ```\n *\n * @example\n * ```ts\n * // Custom card renderer for advanced layouts\n * const config: GridConfig = {\n * plugins: [\n * new ResponsivePlugin({\n * breakpoint: 400,\n * cardRenderer: (row) => {\n * const card = document.createElement('div');\n * card.className = 'custom-card';\n * card.innerHTML = `<strong>${row.name}</strong><br>${row.email}`;\n * return card;\n * },\n * }),\n * ],\n * };\n * ```\n * @since 1.1.0\n */\nexport class ResponsivePlugin<T = unknown> extends BaseGridPlugin<ResponsivePluginConfig<T>> {\n readonly name = 'responsive';\n override readonly version = '1.0.0';\n override readonly styles = styles;\n\n /**\n * Plugin manifest declaring queries this plugin handles.\n */\n static override readonly manifest: PluginManifest = {\n queries: [\n {\n type: 'isCardMode',\n description: 'Returns whether the grid is currently in responsive card mode',\n },\n ],\n };\n\n #resizeObserver?: ResizeObserver;\n #isResponsive = false;\n #debounceTimer?: ReturnType<typeof setTimeout>;\n #warnedAboutMissingBreakpoint = false;\n #currentWidth = 0;\n /** Set of column fields to completely hide */\n #hiddenColumnSet: Set<string> = new Set();\n /** Set of column fields to show value only (no header label) */\n #valueOnlyColumnSet: Set<string> = new Set();\n /** Currently active breakpoint, or null if none */\n #activeBreakpoint: BreakpointConfig | null = null;\n /** Sorted breakpoints from largest to smallest */\n #sortedBreakpoints: BreakpointConfig[] = [];\n\n /** Typed internal grid accessor — centralizes the single required cast. */\n get #internalGrid(): GridHost {\n return this.grid;\n }\n\n /**\n * Check if currently in responsive mode.\n * @returns `true` if the grid is in card layout mode\n */\n isResponsive(): boolean {\n return this.#isResponsive;\n }\n\n /**\n * Force responsive mode regardless of width.\n * Useful for testing or manual control.\n * @param enabled - Whether to enable responsive mode\n */\n setResponsive(enabled: boolean): void {\n if (enabled !== this.#isResponsive) {\n this.#isResponsive = enabled;\n this.#applyResponsiveState();\n this.emit('responsive-change', {\n isResponsive: enabled,\n width: this.#currentWidth,\n breakpoint: this.config.breakpoint ?? 0,\n } satisfies ResponsiveChangeDetail);\n this.requestRender();\n }\n }\n\n /**\n * Update breakpoint dynamically.\n * @param width - New breakpoint width in pixels\n */\n setBreakpoint(width: number): void {\n this.config.breakpoint = width;\n this.#checkBreakpoint(this.#currentWidth);\n }\n\n /**\n * Set a custom card renderer.\n * This allows framework adapters to provide template-based renderers at runtime.\n * @param renderer - The card renderer function, or undefined to use default\n */\n setCardRenderer(renderer: ResponsivePluginConfig<T>['cardRenderer']): void {\n this.config.cardRenderer = renderer;\n // If already in responsive mode, trigger a re-render to apply the new renderer\n if (this.#isResponsive) {\n this.requestRender();\n }\n }\n\n /**\n * Get current grid width.\n * @returns Width of the grid element in pixels\n */\n getWidth(): number {\n return this.#currentWidth;\n }\n\n /**\n * Get the currently active breakpoint config (multi-breakpoint mode only).\n * @returns The active BreakpointConfig, or null if no breakpoint is active\n */\n getActiveBreakpoint(): BreakpointConfig | null {\n return this.#activeBreakpoint;\n }\n\n override attach(grid: GridElement): void {\n super.attach(grid);\n\n // Parse light DOM configuration first (may update this.config)\n this.#parseLightDomCard();\n\n // Build hidden column sets from config\n this.#buildHiddenColumnSets(this.config.hiddenColumns);\n\n // Sort breakpoints from largest to smallest for evaluation\n if (this.config.breakpoints?.length) {\n this.#sortedBreakpoints = [...this.config.breakpoints].sort((a, b) => b.maxWidth - a.maxWidth);\n }\n\n // Observe the grid element itself (not internal viewport)\n // This captures the container width including when shell panels open/close\n this.#resizeObserver = new ResizeObserver((entries) => {\n const width = entries[0]?.contentRect.width ?? 0;\n this.#currentWidth = width;\n\n // Debounce to avoid thrashing during resize drag\n clearTimeout(this.#debounceTimer);\n this.#debounceTimer = setTimeout(() => {\n this.#checkBreakpoint(width);\n }, this.config.debounceMs ?? 100);\n });\n\n this.#resizeObserver.observe(this.gridElement);\n }\n\n // #region Light DOM Parsing\n\n /**\n * Parse `<tbw-grid-responsive-card>` elements from the grid's light DOM.\n *\n * Allows declarative configuration:\n * ```html\n * <tbw-grid [rows]=\"data\">\n * <tbw-grid-responsive-card breakpoint=\"500\" card-row-height=\"80\">\n * <div class=\"custom-card\">\n * <strong>{{ row.name }}</strong>\n * <span>{{ row.email }}</span>\n * </div>\n * </tbw-grid-responsive-card>\n * </tbw-grid>\n * ```\n *\n * Attributes:\n * - `breakpoint`: number - Width threshold for responsive mode\n * - `card-row-height`: number | 'auto' - Card height (default: 'auto')\n * - `hidden-columns`: string - Comma-separated fields to hide\n * - `hide-header`: 'true' | 'false' - Hide per-card field labels (default: 'false')\n * - `debounce-ms`: number - Resize debounce delay (default: 100)\n */\n #parseLightDomCard(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n const cardEl = gridEl.querySelector('tbw-grid-responsive-card');\n if (!cardEl) return;\n\n // Check if a framework adapter wants to handle this element\n // (e.g., React adapter intercepts for JSX rendering)\n const adapter = this.#internalGrid.__frameworkAdapter;\n if (adapter?.parseResponsiveCardElement) {\n const adapterRenderer = adapter.parseResponsiveCardElement(cardEl);\n if (adapterRenderer) {\n this.config = { ...this.config, cardRenderer: adapterRenderer };\n // Continue to parse attributes even if adapter provides renderer\n }\n }\n\n // Parse attributes for configuration\n const breakpointAttr = cardEl.getAttribute('breakpoint');\n const cardRowHeightAttr = cardEl.getAttribute('card-row-height');\n const hiddenColumnsAttr = cardEl.getAttribute('hidden-columns');\n const hideHeaderAttr = cardEl.getAttribute('hide-header');\n const debounceMsAttr = cardEl.getAttribute('debounce-ms');\n\n const configUpdates: Partial<ResponsivePluginConfig<T>> = {};\n\n if (breakpointAttr !== null) {\n const breakpoint = parseInt(breakpointAttr, 10);\n if (!isNaN(breakpoint)) {\n configUpdates.breakpoint = breakpoint;\n }\n }\n\n if (cardRowHeightAttr !== null) {\n configUpdates.cardRowHeight = cardRowHeightAttr === 'auto' ? 'auto' : parseInt(cardRowHeightAttr, 10);\n }\n\n if (hiddenColumnsAttr !== null) {\n // Parse comma-separated field names\n configUpdates.hiddenColumns = hiddenColumnsAttr\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n if (hideHeaderAttr !== null) {\n configUpdates.hideHeader = hideHeaderAttr !== 'false';\n }\n\n if (debounceMsAttr !== null) {\n const debounceMs = parseInt(debounceMsAttr, 10);\n if (!isNaN(debounceMs)) {\n configUpdates.debounceMs = debounceMs;\n }\n }\n\n // Get template content from innerHTML (only if no renderer already set)\n const templateHTML = cardEl.innerHTML.trim();\n if (templateHTML && !this.config.cardRenderer && !adapter?.parseResponsiveCardElement) {\n // Create a template-based renderer using the inner HTML\n configUpdates.cardRenderer = (row: T): HTMLElement => {\n // Evaluate template expressions like {{ row.field }}\n const evaluated = evalTemplateString(templateHTML, { value: row, row: row as Record<string, unknown> });\n const container = document.createElement('div');\n container.className = 'tbw-responsive-card-content';\n // Sanitize the result to prevent XSS\n setSanitizedHTML(container, evaluated);\n return container;\n };\n }\n\n // Merge updates into config (light DOM values override constructor config)\n if (Object.keys(configUpdates).length > 0) {\n this.config = { ...this.config, ...configUpdates };\n }\n }\n\n // #endregion\n\n /**\n * Build the hidden and value-only column sets from config.\n */\n #buildHiddenColumnSets(hiddenColumns?: HiddenColumnConfig[]): void {\n this.#hiddenColumnSet.clear();\n this.#valueOnlyColumnSet.clear();\n\n if (!hiddenColumns) return;\n\n for (const col of hiddenColumns) {\n if (typeof col === 'string') {\n this.#hiddenColumnSet.add(col);\n } else if (col.showValue) {\n this.#valueOnlyColumnSet.add(col.field);\n } else {\n this.#hiddenColumnSet.add(col.field);\n }\n }\n }\n\n override detach(): void {\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = undefined;\n clearTimeout(this.#debounceTimer);\n this.#debounceTimer = undefined;\n\n // Clean up attribute\n if (this.gridElement) {\n this.gridElement.removeAttribute('data-responsive');\n }\n\n super.detach();\n }\n\n /**\n * Handle plugin queries.\n * @internal\n */\n override handleQuery(query: PluginQuery): unknown {\n if (query.type === 'isCardMode') {\n return this.#isResponsive;\n }\n return undefined;\n }\n\n /**\n * Apply hidden and value-only columns.\n * In legacy mode (single breakpoint), only applies when in responsive mode.\n * In multi-breakpoint mode, applies whenever there's an active breakpoint.\n */\n override afterRender(): void {\n // Measure card height for virtualization calculations\n this.#measureCardHeightFromDOM();\n\n // In single breakpoint mode, only apply when responsive\n // In multi-breakpoint mode, apply when there's an active breakpoint\n const shouldApply = this.#sortedBreakpoints.length > 0 ? this.#activeBreakpoint !== null : this.#isResponsive;\n\n if (!shouldApply) {\n return;\n }\n\n const hasHiddenColumns = this.#hiddenColumnSet.size > 0;\n const hasValueOnlyColumns = this.#valueOnlyColumnSet.size > 0;\n\n if (!hasHiddenColumns && !hasValueOnlyColumns) {\n return;\n }\n\n // Mark cells for hidden columns and value-only columns\n const cells = this.gridElement.querySelectorAll('.cell[data-field]');\n for (const cell of cells) {\n const field = cell.getAttribute('data-field');\n if (!field) continue;\n\n // Apply hidden attribute\n if (this.#hiddenColumnSet.has(field)) {\n cell.setAttribute('data-responsive-hidden', '');\n cell.removeAttribute('data-responsive-value-only');\n }\n // Apply value-only attribute (shows value without header label)\n else if (this.#valueOnlyColumnSet.has(field)) {\n cell.setAttribute('data-responsive-value-only', '');\n cell.removeAttribute('data-responsive-hidden');\n }\n // Clear any previous responsive attributes\n else {\n cell.removeAttribute('data-responsive-hidden');\n cell.removeAttribute('data-responsive-value-only');\n }\n }\n }\n\n /**\n * Check if width has crossed any breakpoint threshold.\n * Handles both single breakpoint (legacy) and multi-breakpoint modes.\n */\n #checkBreakpoint(width: number): void {\n // Multi-breakpoint mode\n if (this.#sortedBreakpoints.length > 0) {\n this.#checkMultiBreakpoint(width);\n return;\n }\n\n // Legacy single breakpoint mode\n const breakpoint = this.config.breakpoint ?? 0;\n\n // Warn once if breakpoint not configured (0 means never responsive)\n if (breakpoint === 0 && !this.#warnedAboutMissingBreakpoint) {\n this.#warnedAboutMissingBreakpoint = true;\n this.warn(\n MISSING_BREAKPOINT,\n \"No breakpoint configured. Responsive mode is disabled. Set a breakpoint based on your grid's column count.\",\n );\n }\n\n const shouldBeResponsive = breakpoint > 0 && width < breakpoint;\n\n if (shouldBeResponsive !== this.#isResponsive) {\n this.#isResponsive = shouldBeResponsive;\n this.#applyResponsiveState();\n this.emit('responsive-change', {\n isResponsive: shouldBeResponsive,\n width,\n breakpoint,\n } satisfies ResponsiveChangeDetail);\n this.requestRender();\n }\n }\n\n /**\n * Check breakpoints in multi-breakpoint mode.\n * Evaluates breakpoints from largest to smallest, applying the first match.\n */\n #checkMultiBreakpoint(width: number): void {\n // Find the active breakpoint (first one where width <= maxWidth)\n // Since sorted largest to smallest, we find the largest matching breakpoint\n let newActiveBreakpoint: BreakpointConfig | null = null;\n\n for (const bp of this.#sortedBreakpoints) {\n if (width <= bp.maxWidth) {\n newActiveBreakpoint = bp;\n // Continue to find the most specific (smallest) matching breakpoint\n }\n }\n\n // Check if breakpoint changed\n const breakpointChanged = newActiveBreakpoint !== this.#activeBreakpoint;\n\n if (breakpointChanged) {\n this.#activeBreakpoint = newActiveBreakpoint;\n\n // Update hidden column sets from active breakpoint\n if (newActiveBreakpoint?.hiddenColumns) {\n this.#buildHiddenColumnSets(newActiveBreakpoint.hiddenColumns);\n } else {\n // Fall back to top-level hiddenColumns config\n this.#buildHiddenColumnSets(this.config.hiddenColumns);\n }\n\n // Determine if we should be in card layout\n const shouldBeResponsive = newActiveBreakpoint?.cardLayout === true;\n\n if (shouldBeResponsive !== this.#isResponsive) {\n this.#isResponsive = shouldBeResponsive;\n this.#applyResponsiveState();\n }\n\n // Emit event for any breakpoint change\n this.emit('responsive-change', {\n isResponsive: this.#isResponsive,\n width,\n breakpoint: newActiveBreakpoint?.maxWidth ?? 0,\n } satisfies ResponsiveChangeDetail);\n\n this.requestRender();\n }\n }\n\n /** Original row height before entering responsive mode, for restoration on exit */\n #originalRowHeight?: number;\n\n /**\n * Apply the responsive state to the grid element.\n * Handles scroll reset when entering responsive mode and row height restoration on exit.\n */\n #applyResponsiveState(): void {\n this.gridElement.toggleAttribute('data-responsive', this.#isResponsive);\n\n // Apply animation attribute if enabled (default: true)\n const animate = this.config.animate !== false;\n this.gridElement.toggleAttribute('data-responsive-animate', animate);\n\n // Toggle per-card label visibility based on hideHeader config.\n // Attribute only meaningful while in card mode; clear it otherwise so\n // the CSS rule cannot accidentally apply to non-responsive layouts.\n this.gridElement.toggleAttribute(\n 'data-responsive-hide-header',\n this.#isResponsive && this.config.hideHeader === true,\n );\n\n // Set custom animation duration if provided\n if (this.config.animationDuration) {\n this.gridElement.style.setProperty('--tbw-responsive-duration', `${this.config.animationDuration}ms`);\n }\n\n // Cast to internal type for virtualization access\n const internalGrid = this.#internalGrid;\n\n if (this.#isResponsive) {\n // Store original row height before responsive mode changes it\n if (internalGrid._virtualization) {\n this.#originalRowHeight = internalGrid._virtualization.rowHeight;\n }\n\n // Reset horizontal scroll position when entering responsive mode\n // The CSS hides overflow but doesn't reset the scroll position\n const scrollArea = this.gridElement.querySelector('.tbw-scroll-area') as HTMLElement | null;\n if (scrollArea) {\n scrollArea.scrollLeft = 0;\n }\n } else {\n // Exiting responsive mode - clean up inline styles set by renderRow\n // The rows are reused from the pool, so we need to remove the card-specific styles\n const rows = this.gridElement.querySelectorAll('.data-grid-row');\n for (const row of rows) {\n (row as HTMLElement).style.height = '';\n row.classList.remove('responsive-card');\n }\n\n // Restore original row height\n if (this.#originalRowHeight && this.#originalRowHeight > 0 && internalGrid._virtualization) {\n internalGrid._virtualization.rowHeight = this.#originalRowHeight;\n this.#originalRowHeight = undefined;\n }\n\n // Clear cached measurements so they're remeasured fresh when re-entering responsive mode\n // Without this, stale measurements cause incorrect height calculations after scrolling\n this.#measuredCardHeight = undefined;\n this.#measuredGroupRowHeight = undefined;\n this.#lastCardRowCount = undefined;\n }\n }\n\n /**\n * Custom row rendering when cardRenderer is provided and in responsive mode.\n *\n * When a cardRenderer is configured, this hook takes over row rendering to display\n * the custom card layout instead of the default cell structure.\n *\n * @param row - The row data object\n * @param rowEl - The row DOM element to render into\n * @param rowIndex - The index of the row in the data array\n * @returns `true` if rendered (prevents default), `void` for default rendering\n */\n override renderRow(row: unknown, rowEl: HTMLElement, rowIndex: number): boolean | void {\n // Only override when in responsive mode AND cardRenderer is provided\n if (!this.#isResponsive || !this.config.cardRenderer) {\n return; // Let default rendering proceed\n }\n\n // Skip group rows from GroupingRowsPlugin - they have special structure\n // and should use their own renderer\n if ((row as { __isGroupRow?: boolean }).__isGroupRow) {\n return; // Let GroupingRowsPlugin handle group row rendering\n }\n\n // Clear existing content\n rowEl.replaceChildren();\n\n // Call user's cardRenderer to get custom content\n const cardContent = this.config.cardRenderer(row as T, rowIndex);\n\n // Reset className - clears any stale classes from previous use (e.g., 'group-row' from recycled element)\n // This follows the same pattern as GroupingRowsPlugin which sets className explicitly\n rowEl.className = 'data-grid-row responsive-card';\n\n // Handle card row height — when a numeric height is configured, use the effective\n // height from #getCardHeight() which incorporates DOM measurement after first render.\n // This keeps inline height in sync with the position cache used for virtualization.\n const configuredHeight = this.config.cardRowHeight;\n if (configuredHeight === 'auto' || configuredHeight === undefined) {\n rowEl.style.height = 'auto';\n } else {\n rowEl.style.height = `${this.#getCardHeight()}px`;\n }\n\n // Append the custom card content\n rowEl.appendChild(cardContent);\n\n return true; // We handled rendering\n }\n\n /**\n * Handle keyboard navigation in responsive mode.\n *\n * In responsive mode, the visual layout is inverted:\n * - Cells are stacked vertically within each \"card\" (row)\n * - DOWN/UP visually moves within the card (between fields)\n * - Page Down/Page Up or Ctrl+Down/Up moves between cards\n *\n * For custom cardRenderers, keyboard navigation is disabled entirely\n * since the implementor controls the card content and should handle\n * navigation via their own event handlers.\n *\n * @returns `true` if the event was handled and default behavior should be prevented\n */\n override onKeyDown(e: KeyboardEvent): boolean {\n if (!this.#isResponsive) {\n return false;\n }\n\n // If custom cardRenderer is provided, disable grid's keyboard navigation\n // The implementor is responsible for their own navigation\n if (this.config.cardRenderer) {\n const navKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];\n if (navKeys.includes(e.key)) {\n // Let the event bubble - implementor can handle it\n return false;\n }\n }\n\n // Swap arrow key behavior for CSS-only responsive mode\n // In card layout, cells are stacked vertically:\n // Card 1: Card 2:\n // ID: 1 ID: 2\n // Name: Alice Name: Bob <- ArrowRight goes here\n // Dept: Eng Dept: Mkt\n // ↓ ArrowDown goes here\n //\n // ArrowDown/Up = move within card (change column/field)\n // ArrowRight/Left = move between cards (change row)\n const maxRow = this.rows.length - 1;\n const maxCol = this.visibleColumns.length - 1;\n\n switch (e.key) {\n case 'ArrowDown':\n // Move down WITHIN card (to next field/column)\n if (this.grid._focusCol < maxCol) {\n this.grid._focusCol += 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n // At bottom of card - optionally move to next card's first field\n if (this.grid._focusRow < maxRow) {\n this.grid._focusRow += 1;\n this.grid._focusCol = 0;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n\n case 'ArrowUp':\n // Move up WITHIN card (to previous field/column)\n if (this.grid._focusCol > 0) {\n this.grid._focusCol -= 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n // At top of card - optionally move to previous card's last field\n if (this.grid._focusRow > 0) {\n this.grid._focusRow -= 1;\n this.grid._focusCol = maxCol;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n\n case 'ArrowRight':\n // Move to NEXT card (same field)\n if (this.grid._focusRow < maxRow) {\n this.grid._focusRow += 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n\n case 'ArrowLeft':\n // Move to PREVIOUS card (same field)\n if (this.grid._focusRow > 0) {\n this.grid._focusRow -= 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n }\n\n return false;\n }\n\n // ============================================\n // Variable Height Support for Mixed Row Types\n // ============================================\n\n /** Measured card height from DOM for virtualization calculations */\n #measuredCardHeight?: number;\n\n /** Measured group row height from DOM for virtualization calculations */\n #measuredGroupRowHeight?: number;\n\n /** Last known card row count for detecting changes (e.g., group expand/collapse) */\n #lastCardRowCount?: number;\n\n /**\n * Get the effective card height for virtualization calculations.\n * Prioritizes DOM-measured height (actual rendered size) over config,\n * since content can overflow the configured height.\n */\n #getCardHeight(): number {\n // Prefer measured height - it reflects actual rendered size including overflow\n if (this.#measuredCardHeight && this.#measuredCardHeight > 0) {\n return this.#measuredCardHeight;\n }\n // Fall back to explicit config\n const configHeight = this.config.cardRowHeight;\n if (typeof configHeight === 'number' && configHeight > 0) {\n return configHeight;\n }\n // Default fallback\n return 80;\n }\n\n /**\n * Get the effective group row height for virtualization calculations.\n * Uses DOM-measured height, falling back to original row height.\n */\n #getGroupRowHeight(): number {\n if (this.#measuredGroupRowHeight && this.#measuredGroupRowHeight > 0) {\n return this.#measuredGroupRowHeight;\n }\n // Fall back to original row height (before responsive mode)\n return this.#originalRowHeight ?? 28;\n }\n\n /**\n * Check if there are any group rows in the current dataset.\n * Used to determine if we have mixed row heights.\n */\n #hasGroupRows(): boolean {\n for (const row of this.rows) {\n if ((row as { __isGroupRow?: boolean }).__isGroupRow) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Get the height of a specific row based on its type (group row vs card row).\n * Returns undefined if not in responsive mode.\n *\n * @param row - The row data\n * @param _index - The row index (unused, but part of the interface)\n * @returns The row height in pixels, or undefined if not in responsive mode\n */\n override getRowHeight(row: unknown, _index: number): number | undefined {\n // Only applies when in responsive mode with cardRenderer\n if (!this.#isResponsive || !this.config.cardRenderer) {\n return undefined;\n }\n\n // Check if this is a group row\n if ((row as { __isGroupRow?: boolean }).__isGroupRow) {\n return this.#getGroupRowHeight();\n }\n\n // Regular card row\n return this.#getCardHeight();\n }\n\n /**\n * Count the number of card rows (non-group rows) in the current dataset.\n */\n #countCardRows(): number {\n let count = 0;\n for (const row of this.rows) {\n if (!(row as { __isGroupRow?: boolean }).__isGroupRow) {\n count++;\n }\n }\n return count;\n }\n\n /** Pending refresh scheduled via microtask */\n #pendingRefresh = false;\n\n /**\n * Measure card height from DOM after render and detect row count changes.\n * Called in afterRender to ensure scroll calculations are accurate.\n *\n * This handles two scenarios:\n * 1. Card height changes (content overflow, dynamic sizing)\n * 2. Card row count changes (group expand/collapse)\n * 3. Group row height changes\n *\n * For uniform card layouts (no groups), we update the virtualization row height\n * directly to the card height. For mixed layouts (groups + cards), we use the\n * getExtraHeight mechanism to report height differences.\n *\n * The refresh is deferred via microtask to avoid nested render cycles.\n */\n #measureCardHeightFromDOM(): void {\n if (!this.#isResponsive) {\n return;\n }\n\n let needsRefresh = false;\n const internalGrid = this.#internalGrid;\n const hasGroups = this.#hasGroupRows();\n\n // Check if card row count changed (e.g., group expanded/collapsed)\n const currentCardRowCount = this.#countCardRows();\n if (currentCardRowCount !== this.#lastCardRowCount) {\n this.#lastCardRowCount = currentCardRowCount;\n needsRefresh = true;\n }\n\n // Measure actual group row height from DOM (for mixed layouts)\n if (hasGroups) {\n const groupRow = this.gridElement.querySelector('.data-grid-row.group-row') as HTMLElement | null;\n if (groupRow) {\n const height = groupRow.getBoundingClientRect().height;\n if (height > 0 && height !== this.#measuredGroupRowHeight) {\n this.#measuredGroupRowHeight = height;\n needsRefresh = true;\n }\n }\n }\n\n // Measure actual card height from DOM.\n // With cardRenderer, rows have the `.responsive-card` class.\n // Without cardRenderer (CSS-only card mode), rows are plain `.data-grid-row`\n // elements whose CSS height is `auto` — we need to measure their actual\n // rendered height so the faux scrollbar spacer is sized correctly.\n const cardSelector = this.config.cardRenderer ? '.data-grid-row.responsive-card' : '.data-grid-row:not(.group-row)';\n const cardRow = this.gridElement.querySelector(cardSelector) as HTMLElement | null;\n if (cardRow) {\n const height = cardRow.getBoundingClientRect().height;\n if (height > 0 && height !== this.#measuredCardHeight) {\n this.#measuredCardHeight = height;\n needsRefresh = true;\n\n // For uniform card layouts (no groups), update virtualization row height directly\n // This ensures proper row recycling and translateY calculations\n if (!hasGroups && internalGrid._virtualization) {\n internalGrid._virtualization.rowHeight = height;\n }\n }\n }\n\n // Defer virtualization refresh to avoid nested render cycles\n // This is called from afterRender, so we can't call refreshVirtualWindow synchronously\n // Use scheduler's VIRTUALIZATION phase to batch properly and avoid duplicate afterRender calls\n if (needsRefresh && !this.#pendingRefresh) {\n this.#pendingRefresh = true;\n queueMicrotask(() => {\n this.#pendingRefresh = false;\n // Only refresh if still attached and in responsive mode\n if (this.grid && this.#isResponsive) {\n // Request virtualization phase through grid's public API\n // This goes through the scheduler which batches and handles afterRender properly\n this.#internalGrid.refreshVirtualWindow?.(true, true);\n }\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"responsive.umd.js","names":["#morphInstance","#morphPrefix","#cellMorphPrefix","#internalGrid","#isResponsive","#commitLayoutSwitch","#currentWidth","#lastCheckedWidth","#checkBreakpoint","#activeBreakpoint","#parseLightDomCard","#buildHiddenColumnSets","#sortedBreakpoints","#syncMotionDuration","#syncLayoutAttributes","#measuredCardHeight","#measuredGroupRowHeight","#resizeObserver","#onResize","#lastSwitchAt","#debounceTimer","#hiddenColumnSet","#valueOnlyColumnSet","#queueHiddenColumnFades","#hiddenColumnsApply","#appliedHiddenFields","#markColumnFade","#openFadeWindow","#fadeInFields","#fadeOutFields","#motionDurationMs","#endFadeWindow","#fadeEndsAt","#fadeTimer","#templateOverridden","#clearMorphNames","#appliedValueOnlyFields","#measureCardHeightFromDOM","#applyColumnTemplate","#playColumnFade","#checkMultiBreakpoint","#warnedAboutMissingBreakpoint","#viewTransitionStarter","#applyResponsiveState","#resolveMorphGranularity","#transitionToken","#setMorphNames","#animation","#supportsViewTransition","#originalRowHeight","#lastCardRowCount","#getCardHeight","#getGroupRowHeight","#hasGroupRows","#countCardRows","#pendingRefresh"],"sources":["../../../../../libs/grid/src/lib/plugins/responsive/responsive.css?inline","../../../../../libs/grid/src/lib/plugins/responsive/responsive-plugin.ts"],"sourcesContent":["/**\n * Responsive Plugin CSS\n *\n * Card layout styles for narrow viewports.\n * Applied when [data-responsive] attribute is present on the grid.\n *\n * IMPORTANT: This CSS intentionally preserves the grid's virtualization system.\n * The row height observer will detect the new card height automatically and\n * recalculate the virtual window. Do NOT break virtualization by:\n * - Changing .rows-viewport overflow\n * - Changing .rows position\n * - Hiding .faux-vscroll\n */\n\n/* ============================================\n Animation / Transition Support\n ============================================ */\n\n/* View transition path: the compositor cross-fades the whole grid (or, with\n `animation: 'morph-rows' | 'morph-cells'`, each row or cell) between the table\n and card layouts. While this is active the plugin withholds\n `data-responsive-animate`, so the keyframe fade below never runs alongside it. */\ntbw-grid[data-responsive-transition]::view-transition-group(*) {\n animation-duration: var(--tbw-responsive-duration, 200ms);\n}\n\n/* `morph-cells` names every cell individually. One `view-transition-class` keeps\n that a single style rule instead of one rule per generated name — the class\n does NOT capture anything on its own, the plugin still authors the names.\n Cells travel a real distance between the two layouts, so they get an\n ease-in-out curve rather than the root's cross-fade. */\ntbw-grid[data-responsive-transition] .data-grid-row > .cell {\n view-transition-class: tbw-responsive-cell;\n}\n\ntbw-grid[data-responsive-transition]::view-transition-group(.tbw-responsive-cell) {\n animation-timing-function: ease-in-out;\n}\n\n/* A cell's box grows from one column to a full card row, and the UA stretches\n both snapshots to fill that box — so the text visibly zooms while it travels.\n Drawing them at natural size keeps the glyphs the size they actually are at\n both ends; the group still animates the box, so the cell still moves.\n Anchoring right lands the value on the card's right-aligned value slot rather\n than on the `Header:` label, and lets the label reveal as the box widens. */\ntbw-grid[data-responsive-transition]::view-transition-old(.tbw-responsive-cell),\ntbw-grid[data-responsive-transition]::view-transition-new(.tbw-responsive-cell) {\n width: 100%;\n height: 100%;\n object-fit: none;\n object-position: right center;\n}\n\n/* The old bitmap holds its value left-aligned in a column, the new one holds it\n right-aligned in a card — so the two can never line up exactly, and the gap\n varies with the text length. Front-load the cross-fade instead: the swap is\n over while the boxes are still visibly apart, and the rest of the travel runs\n on a single crisp copy. */\ntbw-grid[data-responsive-transition]::view-transition-old(.tbw-responsive-cell),\ntbw-grid[data-responsive-transition]::view-transition-new(.tbw-responsive-cell) {\n animation-duration: calc(var(--tbw-responsive-duration, 200ms) * 0.35);\n}\n\n/* Base transition setup for animated mode switching */\ntbw-grid[data-responsive-animate] {\n .data-grid-row,\n .data-grid-row > .cell {\n transition:\n opacity var(--tbw-responsive-duration, 200ms) ease-out,\n transform var(--tbw-responsive-duration, 200ms) ease-out;\n }\n}\n\n/* Fade-in when entering responsive mode */\ntbw-grid[data-responsive][data-responsive-animate] {\n .data-grid-row {\n animation: responsive-card-enter var(--tbw-responsive-duration, 200ms) ease-out;\n }\n}\n\n@keyframes responsive-card-enter {\n from {\n opacity: 0;\n transform: translateY(-10px);\n }\n to {\n opacity: 1;\n transform: translateY(0);\n }\n}\n\n/* ============================================\n Responsive Card Layout\n ============================================ */\n\ntbw-grid[data-responsive] {\n /* Hide entire header section in card mode */\n .header {\n display: none !important;\n }\n\n /* Hide footer row in card mode (aggregations don't make sense in cards) */\n .footer-row {\n display: none;\n }\n\n /* Override horizontal scroll - single column flow */\n .tbw-scroll-area {\n overflow-x: hidden;\n min-width: 0 !important;\n }\n\n /* Override rows-body-wrapper to allow natural flow */\n .rows-body-wrapper {\n min-width: 0 !important;\n }\n\n /* Convert row from CSS Grid to block layout */\n .data-grid-row:not(.group-row) {\n display: block !important;\n grid-template-columns: none !important;\n padding: var(--tbw-cell-padding);\n padding-inline-start: var(--tbw-spacing-xl);\n border-bottom: 1px solid var(--tbw-color-border);\n /* Disable containment so row can grow to fit stacked cells */\n contain: none !important;\n content-visibility: visible !important;\n background: var(--tbw-color-bg);\n position: relative;\n\n &:nth-child(even) {\n background: var(--tbw-color-row-alt);\n }\n\n &:hover {\n background: var(--tbw-color-row-hover);\n }\n\n &[aria-selected='true'] {\n background: var(--tbw-color-selection);\n\n /* Selection indicator bar */\n &::before {\n content: '';\n position: absolute;\n inset-inline-start: 0;\n top: 0;\n bottom: 0;\n width: 4px;\n background: var(--tbw-color-accent);\n }\n }\n }\n\n /* The default stacked-cell card grows to fit its cells. A custom `cardRenderer`\n row sets its own height from `cardRowHeight`, so it must not be forced open. */\n .data-grid-row:not(.group-row, .responsive-card) {\n min-height: auto !important;\n height: auto !important;\n }\n\n /* Stack cells vertically as label: value pairs */\n .data-grid-row:not(.group-row) > .cell {\n display: flex !important;\n justify-content: space-between;\n align-items: center;\n padding: var(--tbw-spacing-xs) var(--tbw-spacing-md);\n width: 100% !important;\n min-width: 0 !important;\n min-height: auto !important;\n height: auto !important;\n line-height: 1.5 !important;\n position: static !important;\n left: auto !important;\n right: auto !important;\n border: none !important;\n border-bottom: none !important;\n border-inline-end: none !important;\n background: transparent !important;\n white-space: normal !important;\n overflow: visible !important;\n\n /* Header label from data-header attribute */\n &::before {\n content: attr(data-header) ': ';\n font-weight: 600;\n color: var(--tbw-color-header-fg);\n flex-shrink: 0;\n margin-inline-end: var(--tbw-spacing-md);\n min-width: 100px;\n }\n\n /* Value takes remaining space */\n &::after {\n content: none;\n }\n }\n\n /* Hide utility columns (row expander, selection checkbox, etc.) */\n .cell[data-utility] {\n display: none !important;\n }\n}\n\n/* Hidden columns — specificity must beat `.data-grid-row:not(.group-row) > .cell` above */\ntbw-grid[data-responsive] .data-grid-row > .cell[data-responsive-hidden] {\n display: none !important;\n}\n\n/* Value-only columns in responsive mode (hides label, shows value full-width) */\ntbw-grid[data-responsive] .data-grid-row > .cell[data-responsive-value-only] {\n /* Override the header label ::before to hide it */\n &::before {\n display: none !important;\n }\n\n /* Value takes full width, no justify-content needed */\n justify-content: flex-start !important;\n font-weight: 500;\n}\n\n/* Opt-in: hide per-card field labels (the \"Name:\" prefix) when\n `hideHeader: true` is configured. Toggled by ResponsivePlugin. */\ntbw-grid[data-responsive][data-responsive-hide-header] .data-grid-row:not(.group-row) > .cell::before {\n display: none !important;\n}\n\n/* Non-responsive mode: a hidden column collapses its grid track to zero width\n rather than leaving layout. The cell must stay in flow — cells are auto-placed,\n so dropping one would pull every later cell into the wrong track. Padding and\n border are zeroed too, or the cell would floor at ~17px and the track could\n never reach zero. Opacity is deliberately not `!important`: the plugin fades\n the changed cells with WAAPI, and `!important` outranks animations. */\ntbw-grid:not([data-responsive]) .cell[data-responsive-hidden] {\n padding-inline: 0 !important;\n border-inline-width: 0 !important;\n overflow: hidden !important;\n pointer-events: none;\n opacity: 0;\n}\n\n/* `grid-template-columns` is animatable, and a row element survives a render\n (only its cells are rebuilt), so the collapse is a plain transition. Gated on\n the fade window so column resizing stays instant. A track list only\n interpolates pairwise, which holds here — collapsing never changes the number\n of tracks. */\ntbw-grid[data-responsive-column-fade] :is(.header-row, .data-grid-row) {\n transition: grid-template-columns var(--tbw-responsive-duration, 200ms) ease-out;\n}\n\n/* Hide pinned/aggregation rows in responsive mode - they don't make sense in card layout */\ntbw-grid[data-responsive] {\n .tbw-footer,\n .tbw-header-pinned,\n .tbw-pinned-rows,\n .tbw-aggregation-rows {\n display: none !important;\n }\n\n /* Reset min-widths that hold horizontal scroll width */\n .tbw-pinned-rows,\n .tbw-aggregation-rows,\n .tbw-aggregation-row {\n min-width: 0 !important;\n }\n}\n\n/* Custom card renderer styling */\ntbw-grid[data-responsive] .data-grid-row.responsive-card {\n /* Override default cell layout - custom content takes full width */\n display: block !important;\n padding: var(--tbw-cell-padding);\n border-bottom: 1px solid var(--tbw-color-border);\n\n /* Card content should not have cell-based constraints */\n > * {\n width: 100%;\n }\n\n /* Remove default cell ::before headers - custom renderer handles layout */\n .cell::before {\n display: none;\n }\n}\n","/**\n * Responsive Plugin\n *\n * Transforms the grid from tabular layout to a card/list layout when the grid\n * width falls below a configurable breakpoint. This enables grids to work in\n * narrow containers (split-pane UIs, mobile viewports, dashboard widgets).\n *\n * ## Installation\n *\n * ```ts\n * import { ResponsivePlugin } from '@toolbox-web/grid/plugins/responsive';\n *\n * const config: GridConfig = {\n * plugins: [new ResponsivePlugin({ breakpoint: 500 })],\n * };\n * ```\n *\n * ## How It Works\n *\n * 1. ResizeObserver monitors the grid element's width\n * 2. When `width < breakpoint`, adds `data-responsive` attribute to grid\n * 3. CSS transforms cells from horizontal to vertical layout\n * 4. Each cell displays \"Header: Value\" using CSS `::before` pseudo-element\n *\n * @see [Responsive Demo](?path=/story/grid-plugins-responsive--default)\n */\n\nimport { MISSING_BREAKPOINT } from '../../core/internal/diagnostics';\nimport { ensureCellVisible } from '../../core/internal/keyboard';\nimport { evalTemplateString, setSanitizedHTML } from '../../core/internal/sanitize';\nimport { BaseGridPlugin, type GridElement, type PluginManifest, type PluginQuery } from '../../core/plugin/base-plugin';\nimport type { GridHost } from '../../core/types';\nimport styles from './responsive.css?inline';\nimport type {\n BreakpointConfig,\n HiddenColumnConfig,\n ResponsiveAnimation,\n ResponsiveChangeDetail,\n ResponsivePluginConfig,\n} from './types';\n\n/**\n * The subset of the View Transition API this plugin depends on.\n *\n * `Element.startViewTransition()` is not yet in the TypeScript DOM lib, so the\n * shape is declared locally rather than widening the global `Element`.\n */\ntype ViewTransitionStarter = (update: () => Promise<void>) => {\n readonly finished: Promise<void>;\n /** Rejects when the UA skips the transition — routine while dragging a resize. */\n readonly ready?: Promise<void>;\n};\ntype ViewTransitionCapableElement = HTMLElement & { startViewTransition?: ViewTransitionStarter };\n\n/** How finely the layout switch is broken up for the compositor. */\ntype MorphGranularity = 'cells' | 'rows' | 'none';\n\n/** Disambiguates row morph names when several grids transition at once. */\nlet morphInstanceCounter = 0;\n\n/**\n * Above this, per-cell morphing costs more in compositor layers than it buys.\n * Each named cell is 3 animations and 2 snapshot textures, and rows are\n * virtualized — so this bounds the switch by viewport size, not by dataset size.\n */\nconst MAX_MORPH_CELLS = 150;\n\n/** Stands in for a column set that is not currently applied, without allocating. */\nconst EMPTY_FIELD_SET: ReadonlySet<string> = new Set<string>();\n\n/** Tags the column fade so a re-render can cancel its own leftovers and nothing else. */\nconst COLUMN_FADE_ID = 'tbw-responsive-column-fade';\n\n/** Present only while columns are collapsing, so column resizing stays instant. */\nconst COLUMN_FADE_ATTR = 'data-responsive-column-fade';\n\n/** Split a grid track list on top-level spaces, keeping `minmax(a, b)` intact. */\nfunction splitTrackList(template: string): string[] {\n const tracks: string[] = [];\n let depth = 0;\n let start = 0;\n for (let i = 0; i < template.length; i++) {\n const char = template[i];\n if (char === '(') depth++;\n else if (char === ')') depth--;\n else if (char === ' ' && depth === 0) {\n if (i > start) tracks.push(template.slice(start, i));\n start = i + 1;\n }\n }\n if (start < template.length) tracks.push(template.slice(start));\n return tracks;\n}\n\n/**\n * The zero-width form of `track`. A track list only interpolates pairwise, so the\n * collapsed track has to keep the same unit family — `1fr` has to become `0fr`,\n * not `0px`. Keywords (`auto`, `max-content`) have no interpolable zero and snap.\n */\nfunction collapsedTrack(track: string): string {\n if (track.startsWith('minmax(')) return track.includes('fr') ? 'minmax(0px, 0fr)' : 'minmax(0px, 0px)';\n if (track.endsWith('fr')) return '0fr';\n if (track.endsWith('%')) return '0%';\n return '0px';\n}\n\n/**\n * Responsive Plugin for tbw-grid\n *\n * Adds automatic card layout mode when the grid width falls below a configurable\n * breakpoint. Perfect for responsive designs, split-pane UIs, and mobile viewports.\n *\n * @template T The row data type\n *\n * @example\n * ```ts\n * // Basic usage - switch to card layout below 500px\n * const config: GridConfig = {\n * plugins: [new ResponsivePlugin({ breakpoint: 500 })],\n * };\n * ```\n *\n * @example\n * ```ts\n * // Hide less important columns in card mode\n * const config: GridConfig = {\n * plugins: [\n * new ResponsivePlugin({\n * breakpoint: 600,\n * hiddenColumns: ['createdAt', 'updatedAt'],\n * }),\n * ],\n * };\n * ```\n *\n * @example\n * ```ts\n * // Custom card renderer for advanced layouts\n * const config: GridConfig = {\n * plugins: [\n * new ResponsivePlugin({\n * breakpoint: 400,\n * cardRenderer: (row) => {\n * const card = document.createElement('div');\n * card.className = 'custom-card';\n * card.innerHTML = `<strong>${row.name}</strong><br>${row.email}`;\n * return card;\n * },\n * }),\n * ],\n * };\n * ```\n * @since 1.1.0\n */\nexport class ResponsivePlugin<T = unknown> extends BaseGridPlugin<ResponsivePluginConfig<T>> {\n readonly name = 'responsive';\n override readonly version = '1.0.0';\n override readonly styles = styles;\n\n /**\n * Plugin manifest declaring queries this plugin handles.\n */\n static override readonly manifest: PluginManifest = {\n queries: [\n {\n type: 'isCardMode',\n description: 'Returns whether the grid is currently in responsive card mode',\n },\n ],\n };\n\n #resizeObserver?: ResizeObserver;\n #isResponsive = false;\n #debounceTimer?: ReturnType<typeof setTimeout>;\n #warnedAboutMissingBreakpoint = false;\n #currentWidth = 0;\n /** Width the last `#checkBreakpoint()` ran against — guards height-only resizes. */\n #lastCheckedWidth?: number;\n /** Timestamp of the last applied layout switch, for the post-switch cooldown. */\n #lastSwitchAt = Number.NEGATIVE_INFINITY;\n /** Unique `view-transition-name` prefixes for this instance's rows and cells. */\n readonly #morphInstance = ++morphInstanceCounter;\n readonly #morphPrefix = `tbw-responsive-row-${this.#morphInstance}-`;\n readonly #cellMorphPrefix = `tbw-responsive-cell-${this.#morphInstance}-`;\n /** Identifies the newest view transition, so superseded ones skip their cleanup. */\n #transitionToken = 0;\n /** Set of column fields to completely hide */\n #hiddenColumnSet: Set<string> = new Set();\n /** The subset of the above that the last render actually hid. */\n #appliedHiddenFields: ReadonlySet<string> = EMPTY_FIELD_SET;\n /** Set of column fields to show value only (no header label) */\n #valueOnlyColumnSet: Set<string> = new Set();\n /** The subset of the above that the last render actually stripped labels from. */\n #appliedValueOnlyFields: ReadonlySet<string> = EMPTY_FIELD_SET;\n /** Fields currently fading out of the table, and the ones fading in. */\n #fadeOutFields: Set<string> = new Set();\n #fadeInFields: Set<string> = new Set();\n /** When the in-flight column fade ends, so a mid-fade re-render can resume it. */\n #fadeEndsAt = 0;\n #fadeTimer?: ReturnType<typeof setTimeout>;\n /** Whether the grid template currently carries this plugin's collapsed tracks. */\n #templateOverridden = false;\n /** Currently active breakpoint, or null if none */\n #activeBreakpoint: BreakpointConfig | null = null;\n /** Sorted breakpoints from largest to smallest */\n #sortedBreakpoints: BreakpointConfig[] = [];\n\n /** Typed internal grid accessor — centralizes the single required cast. */\n get #internalGrid(): GridHost {\n return this.grid;\n }\n\n /**\n * Check if currently in responsive mode.\n * @returns `true` if the grid is in card layout mode\n */\n isResponsive(): boolean {\n return this.#isResponsive;\n }\n\n /**\n * Force responsive mode regardless of width.\n * Useful for testing or manual control.\n * @param enabled - Whether to enable responsive mode\n */\n setResponsive(enabled: boolean): void {\n if (enabled !== this.#isResponsive) {\n this.#isResponsive = enabled;\n this.#commitLayoutSwitch();\n this.emit('responsive-change', {\n isResponsive: enabled,\n width: this.#currentWidth,\n breakpoint: this.config.breakpoint ?? 0,\n } satisfies ResponsiveChangeDetail);\n }\n }\n\n /**\n * Update breakpoint dynamically.\n * @param width - New breakpoint width in pixels\n */\n setBreakpoint(width: number): void {\n this.config.breakpoint = width;\n // The guard keys off width alone, so a config change must invalidate it.\n this.#lastCheckedWidth = undefined;\n this.#checkBreakpoint(this.#currentWidth);\n }\n\n /**\n * Set a custom card renderer.\n * This allows framework adapters to provide template-based renderers at runtime.\n * @param renderer - The card renderer function, or undefined to use default\n */\n setCardRenderer(renderer: ResponsivePluginConfig<T>['cardRenderer']): void {\n this.config.cardRenderer = renderer;\n // If already in responsive mode, trigger a re-render to apply the new renderer\n if (this.#isResponsive) {\n this.requestRender();\n }\n }\n\n /**\n * Get current grid width.\n * @returns Width of the grid element in pixels\n */\n getWidth(): number {\n return this.#currentWidth;\n }\n\n /**\n * Get the currently active breakpoint config (multi-breakpoint mode only).\n * @returns The active BreakpointConfig, or null if no breakpoint is active\n */\n getActiveBreakpoint(): BreakpointConfig | null {\n return this.#activeBreakpoint;\n }\n\n override attach(grid: GridElement): void {\n super.attach(grid);\n\n // Parse light DOM configuration first (may update this.config)\n this.#parseLightDomCard();\n\n // Build hidden column sets from config\n this.#buildHiddenColumnSets(this.config.hiddenColumns);\n\n // Sort breakpoints from largest to smallest for evaluation\n if (this.config.breakpoints?.length) {\n this.#sortedBreakpoints = [...this.config.breakpoints].sort((a, b) => b.maxWidth - a.maxWidth);\n }\n\n this.#syncMotionDuration();\n\n // A `gridConfig` rebuild detaches and re-attaches this same instance, which\n // strips the attributes but keeps the layout state — so re-assert them here or\n // the grid sits in card mode with none of the card CSS until the next resize.\n this.#syncLayoutAttributes();\n\n // Card height is measured from the DOM and outranks the config, so a rebuild\n // carrying a new `cardRowHeight` would keep serving the previous measurement.\n this.#measuredCardHeight = undefined;\n this.#measuredGroupRowHeight = undefined;\n\n // The cards on screen were rendered under the previous config.\n if (this.#isResponsive) this.requestRender();\n\n // Observe the grid element itself (not internal viewport)\n // This captures the container width including when shell panels open/close\n this.#resizeObserver = new ResizeObserver((entries) => {\n this.#onResize(entries[0]?.contentRect.width ?? 0);\n });\n\n this.#resizeObserver.observe(this.gridElement);\n }\n\n /**\n * React to an observed size change.\n *\n * ResizeObserver also fires for HEIGHT changes, and host apps commonly animate\n * a container's height (or load content into it) while the width is already\n * final. A trailing-only debounce is reset by every one of those, delaying the\n * layout switch long past the moment the final width was known. So:\n *\n * 1. Height-only notifications are dropped — they cannot change the outcome.\n * 2. A width change switches on the LEADING edge when no switch happened in\n * the last `debounceMs`, so a settled width applies within one frame.\n * 3. Further width changes inside that window are deferred to a single\n * trailing evaluation, which rate-limits thrash during a resize drag.\n */\n #onResize(width: number): void {\n this.#currentWidth = width;\n\n // Height-only resize — nothing the breakpoint logic reads has changed.\n if (width === this.#lastCheckedWidth) return;\n\n const debounceMs = this.config.debounceMs ?? 100;\n const sinceLastSwitch = performance.now() - this.#lastSwitchAt;\n\n clearTimeout(this.#debounceTimer);\n\n if (sinceLastSwitch >= debounceMs) {\n this.#lastCheckedWidth = width;\n this.#checkBreakpoint(width);\n return;\n }\n\n this.#debounceTimer = setTimeout(() => {\n this.#lastCheckedWidth = this.#currentWidth;\n this.#checkBreakpoint(this.#currentWidth);\n }, debounceMs - sinceLastSwitch);\n }\n\n // #region Light DOM Parsing\n\n /**\n * Parse `<tbw-grid-responsive-card>` elements from the grid's light DOM.\n *\n * Allows declarative configuration:\n * ```html\n * <tbw-grid [rows]=\"data\">\n * <tbw-grid-responsive-card breakpoint=\"500\" card-row-height=\"80\">\n * <div class=\"custom-card\">\n * <strong>{{ row.name }}</strong>\n * <span>{{ row.email }}</span>\n * </div>\n * </tbw-grid-responsive-card>\n * </tbw-grid>\n * ```\n *\n * Attributes:\n * - `breakpoint`: number - Width threshold for responsive mode\n * - `card-row-height`: number | 'auto' - Card height (default: 'auto')\n * - `hidden-columns`: string - Comma-separated fields to hide\n * - `hide-header`: 'true' | 'false' - Hide per-card field labels (default: 'false')\n * - `debounce-ms`: number - Resize debounce delay (default: 100)\n */\n #parseLightDomCard(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n const cardEl = gridEl.querySelector('tbw-grid-responsive-card');\n if (!cardEl) return;\n\n // Check if a framework adapter wants to handle this element\n // (e.g., React adapter intercepts for JSX rendering)\n const adapter = this.#internalGrid.__frameworkAdapter;\n if (adapter?.parseResponsiveCardElement) {\n const adapterRenderer = adapter.parseResponsiveCardElement(cardEl);\n if (adapterRenderer) {\n this.config = { ...this.config, cardRenderer: adapterRenderer };\n // Continue to parse attributes even if adapter provides renderer\n }\n }\n\n // Parse attributes for configuration\n const breakpointAttr = cardEl.getAttribute('breakpoint');\n const cardRowHeightAttr = cardEl.getAttribute('card-row-height');\n const hiddenColumnsAttr = cardEl.getAttribute('hidden-columns');\n const hideHeaderAttr = cardEl.getAttribute('hide-header');\n const debounceMsAttr = cardEl.getAttribute('debounce-ms');\n\n const configUpdates: Partial<ResponsivePluginConfig<T>> = {};\n\n if (breakpointAttr !== null) {\n const breakpoint = parseInt(breakpointAttr, 10);\n if (!isNaN(breakpoint)) {\n configUpdates.breakpoint = breakpoint;\n }\n }\n\n if (cardRowHeightAttr !== null) {\n configUpdates.cardRowHeight = cardRowHeightAttr === 'auto' ? 'auto' : parseInt(cardRowHeightAttr, 10);\n }\n\n if (hiddenColumnsAttr !== null) {\n // Parse comma-separated field names\n configUpdates.hiddenColumns = hiddenColumnsAttr\n .split(',')\n .map((s) => s.trim())\n .filter((s) => s.length > 0);\n }\n\n if (hideHeaderAttr !== null) {\n configUpdates.hideHeader = hideHeaderAttr !== 'false';\n }\n\n if (debounceMsAttr !== null) {\n const debounceMs = parseInt(debounceMsAttr, 10);\n if (!isNaN(debounceMs)) {\n configUpdates.debounceMs = debounceMs;\n }\n }\n\n // Get template content from innerHTML (only if no renderer already set)\n const templateHTML = cardEl.innerHTML.trim();\n if (templateHTML && !this.config.cardRenderer && !adapter?.parseResponsiveCardElement) {\n // Create a template-based renderer using the inner HTML\n configUpdates.cardRenderer = (row: T): HTMLElement => {\n // Evaluate template expressions like {{ row.field }}\n const evaluated = evalTemplateString(templateHTML, { value: row, row: row as Record<string, unknown> });\n const container = document.createElement('div');\n container.className = 'tbw-responsive-card-content';\n // Sanitize the result to prevent XSS\n setSanitizedHTML(container, evaluated);\n return container;\n };\n }\n\n // Merge updates into config (light DOM values override constructor config)\n if (Object.keys(configUpdates).length > 0) {\n this.config = { ...this.config, ...configUpdates };\n }\n }\n\n // #endregion\n\n /**\n * Build the hidden and value-only column sets from config.\n */\n #buildHiddenColumnSets(hiddenColumns?: HiddenColumnConfig[]): void {\n this.#hiddenColumnSet = new Set();\n this.#valueOnlyColumnSet.clear();\n\n for (const col of hiddenColumns ?? []) {\n if (typeof col === 'string') {\n this.#hiddenColumnSet.add(col);\n } else if (col.showValue) {\n this.#valueOnlyColumnSet.add(col.field);\n } else {\n this.#hiddenColumnSet.add(col.field);\n }\n }\n\n this.#queueHiddenColumnFades();\n }\n\n /**\n * Whether `hiddenColumns` is in effect right now: card mode drops the column\n * from the card, and in progressive degradation a matched breakpoint drops it\n * from the table. A plain table with no matching breakpoint shows everything.\n */\n get #hiddenColumnsApply(): boolean {\n return this.#sortedBreakpoints.length > 0 ? this.#activeBreakpoint !== null : this.#isResponsive;\n }\n\n /**\n * Queue a fade for every column whose visibility actually changed. The diff is\n * against what the last render hid rather than against the previous config,\n * because `hiddenColumns` is inert in a plain table — fading a column out there\n * would strand it invisible in a track that is never collapsed.\n */\n #queueHiddenColumnFades(): void {\n const applied = this.#hiddenColumnsApply ? this.#hiddenColumnSet : EMPTY_FIELD_SET;\n\n for (const field of this.#appliedHiddenFields) {\n if (!applied.has(field)) this.#markColumnFade(field, false);\n }\n for (const field of applied) {\n if (!this.#appliedHiddenFields.has(field)) this.#markColumnFade(field, true);\n }\n this.#openFadeWindow();\n }\n\n /** Queue `field` for the next fade, dropping any opposite direction still pending for it. */\n #markColumnFade(field: string, out: boolean): void {\n (out ? this.#fadeInFields : this.#fadeOutFields).delete(field);\n (out ? this.#fadeOutFields : this.#fadeInFields).add(field);\n }\n\n /**\n * Start the window during which queued columns animate. The track collapse is a\n * CSS transition on the row, which survives a render, but the opacity fade is\n * not: cells are rebuilt on every render, so a hidden column's cell would be\n * re-inserted visible and transition to hidden all over again — exactly the\n * flash of every hidden column reappearing. Cells are animated explicitly\n * instead, and only the fields that actually changed are queued.\n */\n #openFadeWindow(): void {\n const duration = this.#motionDurationMs;\n if (duration <= 0 || (this.#fadeOutFields.size === 0 && this.#fadeInFields.size === 0)) {\n this.#endFadeWindow();\n return;\n }\n\n this.#fadeEndsAt = performance.now() + duration;\n this.gridElement?.setAttribute(COLUMN_FADE_ATTR, '');\n clearTimeout(this.#fadeTimer);\n this.#fadeTimer = setTimeout(() => this.#endFadeWindow(), duration);\n }\n\n #endFadeWindow(): void {\n clearTimeout(this.#fadeTimer);\n this.#fadeTimer = undefined;\n this.#fadeEndsAt = 0;\n this.#fadeOutFields.clear();\n this.#fadeInFields.clear();\n this.gridElement?.removeAttribute(COLUMN_FADE_ATTR);\n\n // The fade-out fills forwards to hold the cell at zero until the render that\n // hides it lands. Past the window CSS owns visibility, so releasing the fill\n // is what stops an abandoned fade from stranding a visible column invisible.\n for (const cell of this.gridElement?.querySelectorAll<HTMLElement>('.cell[data-field]') ?? []) {\n for (const animation of cell.getAnimations?.() ?? []) {\n if (animation.id === COLUMN_FADE_ID) animation.cancel();\n }\n }\n }\n\n /** Animate one cell, seeking to `elapsed` so a cell created mid-fade joins in progress. */\n #playColumnFade(cell: HTMLElement, out: boolean, duration: number, elapsed: number): void {\n for (const animation of cell.getAnimations?.() ?? []) {\n if (animation.id === COLUMN_FADE_ID) animation.cancel();\n }\n const frames = out ? [{ opacity: 1 }, { opacity: 0 }] : [{ opacity: 0 }, { opacity: 1 }];\n // A fade-out has to hold at zero until the closing render hides the cell for real.\n const animation = cell.animate?.(frames, { duration, easing: 'ease-out', fill: out ? 'forwards' : 'none' });\n if (!animation) return;\n animation.id = COLUMN_FADE_ID;\n animation.currentTime = elapsed;\n }\n\n /**\n * Collapse the tracks of hidden columns to zero width. Cells are auto-placed, so\n * a hidden cell has to stay in layout — dropping it would pull every later cell\n * into the wrong track. Collapsing the track instead is what lets the row, and\n * therefore the horizontal scroll width, shrink to the visible columns.\n *\n * Core rewrites `--tbw-column-template` from the full column list on every\n * template update, so this override is re-applied after each render.\n */\n #applyColumnTemplate(hidden: ReadonlySet<string>): void {\n const base = this.#internalGrid?._gridTemplate ?? '';\n if (!base) return;\n\n const columns = this.visibleColumns;\n const tracks = splitTrackList(base);\n let collapsed = false;\n\n if (hidden.size > 0 && tracks.length === columns.length) {\n for (let i = 0; i < columns.length; i++) {\n const field = columns[i]?.field;\n if (field && hidden.has(field)) {\n tracks[i] = collapsedTrack(tracks[i]);\n collapsed = true;\n }\n }\n }\n\n if (!collapsed && !this.#templateOverridden) return;\n this.gridElement.style.setProperty('--tbw-column-template', collapsed ? tracks.join(' ') : base);\n this.#templateOverridden = collapsed;\n }\n\n override detach(): void {\n this.#resizeObserver?.disconnect();\n this.#resizeObserver = undefined;\n this.#endFadeWindow();\n clearTimeout(this.#debounceTimer);\n this.#debounceTimer = undefined;\n this.#lastCheckedWidth = undefined;\n this.#lastSwitchAt = Number.NEGATIVE_INFINITY;\n\n // Every attribute `#syncLayoutAttributes()` writes styles the grid on its own\n // — `[data-responsive-animate]` transitions rows even in table mode — so a\n // detached plugin that leaves one behind keeps styling a grid it no longer owns.\n if (this.gridElement) {\n this.gridElement.removeAttribute('data-responsive');\n this.gridElement.removeAttribute('data-responsive-animate');\n this.gridElement.removeAttribute('data-responsive-hide-header');\n this.gridElement.removeAttribute('data-responsive-transition');\n for (const cell of this.gridElement.querySelectorAll(\n '.cell[data-responsive-hidden], .cell[data-responsive-value-only]',\n )) {\n cell.removeAttribute('data-responsive-hidden');\n cell.removeAttribute('data-responsive-value-only');\n cell.removeAttribute('aria-hidden');\n }\n this.#clearMorphNames();\n }\n this.#appliedHiddenFields = EMPTY_FIELD_SET;\n this.#appliedValueOnlyFields = EMPTY_FIELD_SET;\n\n super.detach();\n }\n\n /**\n * Handle plugin queries.\n * @internal\n */\n override handleQuery(query: PluginQuery): unknown {\n if (query.type === 'isCardMode') {\n return this.#isResponsive;\n }\n return undefined;\n }\n\n /**\n * Apply hidden and value-only columns.\n * In legacy mode (single breakpoint), only applies when in responsive mode.\n * In multi-breakpoint mode, applies whenever there's an active breakpoint.\n */\n override afterRender(): void {\n // Measure card height for virtualization calculations\n this.#measureCardHeightFromDOM();\n\n // In single breakpoint mode, only apply when responsive\n // In multi-breakpoint mode, apply when there's an active breakpoint\n const shouldApply = this.#sortedBreakpoints.length > 0 ? this.#activeBreakpoint !== null : this.#isResponsive;\n\n const hidden = shouldApply ? this.#hiddenColumnSet : EMPTY_FIELD_SET;\n const valueOnly = shouldApply ? this.#valueOnlyColumnSet : EMPTY_FIELD_SET;\n const fading = this.#fadeOutFields.size > 0 || this.#fadeInFields.size > 0;\n\n // Cells live in a recycled row pool, so an attribute written in card mode\n // outlives the switch back to table — one clearing pass is still owed.\n const stale = this.#appliedHiddenFields.size > 0 || this.#appliedValueOnlyFields.size > 0;\n\n // A layout switch changes which columns are hidden without going through the\n // config, and the switch animation already covers it — so record the new\n // truth here rather than letting the next config change diff against a stale set.\n this.#appliedHiddenFields = hidden;\n this.#appliedValueOnlyFields = valueOnly;\n\n // Card mode stacks cells vertically, so the track list is meaningless there.\n this.#applyColumnTemplate(this.#isResponsive ? EMPTY_FIELD_SET : hidden);\n\n if (hidden.size === 0 && valueOnly.size === 0 && !fading && !stale) {\n return;\n }\n\n const duration = this.#motionDurationMs;\n const elapsed = fading ? Math.min(duration, Math.max(0, duration - (this.#fadeEndsAt - performance.now()))) : 0;\n\n // Mark cells for hidden columns and value-only columns\n const cells = this.gridElement.querySelectorAll<HTMLElement>('.cell[data-field]');\n for (const cell of cells) {\n const field = cell.getAttribute('data-field');\n if (!field) continue;\n\n const fadingOut = this.#fadeOutFields.has(field);\n if (fadingOut || this.#fadeInFields.has(field)) this.#playColumnFade(cell, fadingOut, duration, elapsed);\n\n // The cell keeps its box and collapses with its track, so it stays out of\n // the accessibility tree the way `display: none` used to keep it.\n if (hidden.has(field)) {\n cell.setAttribute('data-responsive-hidden', '');\n cell.setAttribute('aria-hidden', 'true');\n cell.removeAttribute('data-responsive-value-only');\n }\n // Apply value-only attribute (shows value without header label)\n else if (valueOnly.has(field)) {\n cell.setAttribute('data-responsive-value-only', '');\n cell.removeAttribute('data-responsive-hidden');\n cell.removeAttribute('aria-hidden');\n }\n // Clear any previous responsive attributes\n else {\n cell.removeAttribute('data-responsive-hidden');\n cell.removeAttribute('data-responsive-value-only');\n cell.removeAttribute('aria-hidden');\n }\n }\n }\n\n /**\n * Check if width has crossed any breakpoint threshold.\n * Handles both single breakpoint (legacy) and multi-breakpoint modes.\n */\n #checkBreakpoint(width: number): void {\n // Multi-breakpoint mode\n if (this.#sortedBreakpoints.length > 0) {\n this.#checkMultiBreakpoint(width);\n return;\n }\n\n // Legacy single breakpoint mode\n const breakpoint = this.config.breakpoint ?? 0;\n\n // Warn once if breakpoint not configured (0 means never responsive)\n if (breakpoint === 0 && !this.#warnedAboutMissingBreakpoint) {\n this.#warnedAboutMissingBreakpoint = true;\n this.warn(\n MISSING_BREAKPOINT,\n \"No breakpoint configured. Responsive mode is disabled. Set a breakpoint based on your grid's column count.\",\n );\n }\n\n const shouldBeResponsive = breakpoint > 0 && width < breakpoint;\n\n if (shouldBeResponsive !== this.#isResponsive) {\n this.#isResponsive = shouldBeResponsive;\n this.#lastSwitchAt = performance.now();\n this.#commitLayoutSwitch();\n this.emit('responsive-change', {\n isResponsive: shouldBeResponsive,\n width,\n breakpoint,\n } satisfies ResponsiveChangeDetail);\n }\n }\n\n /**\n * Check breakpoints in multi-breakpoint mode.\n * Evaluates breakpoints from largest to smallest, applying the first match.\n */\n #checkMultiBreakpoint(width: number): void {\n // Find the active breakpoint (first one where width <= maxWidth)\n // Since sorted largest to smallest, we find the largest matching breakpoint\n let newActiveBreakpoint: BreakpointConfig | null = null;\n\n for (const bp of this.#sortedBreakpoints) {\n if (width <= bp.maxWidth) {\n newActiveBreakpoint = bp;\n // Continue to find the most specific (smallest) matching breakpoint\n }\n }\n\n // Check if breakpoint changed\n const breakpointChanged = newActiveBreakpoint !== this.#activeBreakpoint;\n\n if (breakpointChanged) {\n this.#activeBreakpoint = newActiveBreakpoint;\n this.#lastSwitchAt = performance.now();\n\n // Update hidden column sets from active breakpoint\n if (newActiveBreakpoint?.hiddenColumns) {\n this.#buildHiddenColumnSets(newActiveBreakpoint.hiddenColumns);\n } else {\n // Fall back to top-level hiddenColumns config\n this.#buildHiddenColumnSets(this.config.hiddenColumns);\n }\n\n // Determine if we should be in card layout\n const shouldBeResponsive = newActiveBreakpoint?.cardLayout === true;\n const layoutChanged = shouldBeResponsive !== this.#isResponsive;\n\n if (layoutChanged) {\n this.#isResponsive = shouldBeResponsive;\n }\n\n // A hidden-column change still needs a re-render even when the card\n // layout itself did not flip, so always commit through the same path.\n this.#commitLayoutSwitch(layoutChanged);\n\n // Emit event for any breakpoint change\n this.emit('responsive-change', {\n isResponsive: this.#isResponsive,\n width,\n breakpoint: newActiveBreakpoint?.maxWidth ?? 0,\n } satisfies ResponsiveChangeDetail);\n }\n }\n\n /**\n * Commit a layout switch, driving it through a view transition when the\n * browser supports element-scoped transitions.\n *\n * The switch is a wholesale layout change (table rows become cards), so the\n * compositor cross-fade is both cheaper and more legible than animating the\n * DOM. Where `Element.startViewTransition()` is unavailable the grid falls\n * back to the CSS keyframe fade driven by `data-responsive-animate`.\n *\n * A hidden-column change is deliberately excluded from the transition. Only the\n * affected column moves in that case — its grid track collapses to zero width\n * while every other track keeps its size — so the leaving cell can fade and\n * shrink in place. Snapshotting the whole grid for it makes all columns shimmer,\n * and because the change is driven by a resize the frozen snapshot is scaled\n * against a container that is still being dragged.\n * @param layoutChanged - Whether the table/card layout itself flipped. `false`\n * when only the hidden-column set changed.\n */\n #commitLayoutSwitch(layoutChanged = true): void {\n this.#syncMotionDuration();\n\n // The view transition already cross-fades everything, so per-column fades\n // would only fight it.\n if (layoutChanged) this.#endFadeWindow();\n\n const startViewTransition = layoutChanged ? this.#viewTransitionStarter() : null;\n if (!startViewTransition) {\n if (layoutChanged) this.#applyResponsiveState();\n this.requestRender();\n return;\n }\n\n const host = this.gridElement;\n const morph = this.#resolveMorphGranularity();\n const token = ++this.#transitionToken;\n\n // Names must exist on the outgoing rows before the snapshot is taken.\n if (morph !== 'none') this.#setMorphNames(morph);\n host.setAttribute('data-responsive-transition', '');\n\n const transition = startViewTransition(async () => {\n this.#applyResponsiveState();\n // The render scheduler batches into rAF, which still runs inside the\n // update callback — so awaiting here captures the finished new layout.\n await this.#internalGrid.forceLayout?.();\n if (morph !== 'none') this.#setMorphNames(morph);\n });\n\n const cleanup = (): void => {\n // A superseded transition still settles, and tearing down the names and\n // attribute its successor is mid-flight on would abort that successor.\n if (token !== this.#transitionToken) return;\n host.removeAttribute('data-responsive-transition');\n if (morph !== 'none') this.#clearMorphNames();\n };\n transition.finished.then(cleanup, cleanup);\n // A drag across the breakpoint supersedes the transition in flight; the UA\n // skips it and rejects `ready`, which is expected rather than an error.\n transition.ready?.catch(() => undefined);\n }\n\n /** Animation style in effect, resolving the deprecated `animate` flag. */\n get #animation(): ResponsiveAnimation {\n const { animation, animate } = this.config;\n if (animation !== undefined) return animation;\n return animate === false ? false : 'fade';\n }\n\n /** Duration every responsive animation runs at; `0` when motion is suppressed. */\n get #motionDurationMs(): number {\n const enabled = this.#animation !== false && this.isAnimationEnabled;\n return enabled ? (this.config.animationDuration ?? 200) : 0;\n }\n\n /**\n * Publish the single duration every responsive animation reads. Collapsing it\n * to `0ms` is how `animation: false` and `prefers-reduced-motion` silence the\n * CSS-driven paths (the column fade, the card-enter keyframe, and the view\n * transition groups), none of which can see the plugin config.\n */\n #syncMotionDuration(): void {\n this.gridElement.style.setProperty('--tbw-responsive-duration', `${this.#motionDurationMs}ms`);\n }\n\n /**\n * Resolve the element-scoped view transition entry point, or `null` when\n * animation is disabled or the browser does not support it.\n */\n #viewTransitionStarter(): ViewTransitionStarter | null {\n if (this.#animation === false || !this.isAnimationEnabled) return null;\n\n const host: ViewTransitionCapableElement = this.gridElement;\n const start = host.startViewTransition;\n return typeof start === 'function' ? start.bind(host) : null;\n }\n\n /** Whether the browser can drive the layout switch on the compositor. */\n get #supportsViewTransition(): boolean {\n const host: ViewTransitionCapableElement | undefined = this.gridElement;\n return typeof host?.startViewTransition === 'function';\n }\n\n /**\n * Give every rendered row a stable `view-transition-name` so the compositor\n * animates each row from its table position to its card position.\n */\n #setMorphNames(granularity: MorphGranularity): void {\n const rows = this.gridElement.querySelectorAll<HTMLElement>('.data-grid-row[aria-rowindex]');\n for (const row of rows) {\n const rowIndex = row.getAttribute('aria-rowindex');\n if (granularity === 'rows') {\n row.style.setProperty('view-transition-name', `${this.#morphPrefix}${rowIndex}`);\n continue;\n }\n // Only the leaves are named — naming the row too would lift its cells out\n // of the row's own snapshot and animate both against each other.\n for (const cell of row.querySelectorAll<HTMLElement>(':scope > .cell[aria-colindex]')) {\n const name = `${this.#cellMorphPrefix}${rowIndex}-${cell.getAttribute('aria-colindex')}`;\n cell.style.setProperty('view-transition-name', name);\n }\n }\n }\n\n /**\n * Pick the morph granularity for this switch.\n *\n * Cell identity is NOT stable across a layout switch (the row renderer\n * rebuilds cells whenever the visible column set changes), so names are\n * authored rather than left to `view-transition-name: match-element`.\n */\n #resolveMorphGranularity(): MorphGranularity {\n const animation = this.#animation;\n if (animation === 'morph-cells') {\n const cells = this.gridElement.querySelectorAll('.data-grid-row > .cell[aria-colindex]').length;\n if (cells > 0 && cells <= MAX_MORPH_CELLS) return 'cells';\n return 'rows';\n }\n return animation === 'morph-rows' ? 'rows' : 'none';\n }\n\n /** Remove morph names so recycled rows and cells do not carry stale identities. */\n #clearMorphNames(): void {\n const named = this.gridElement.querySelectorAll<HTMLElement>('.data-grid-row, .data-grid-row > .cell');\n for (const el of named) {\n el.style.removeProperty('view-transition-name');\n }\n }\n\n /** Original row height before entering responsive mode, for restoration on exit */\n #originalRowHeight?: number;\n\n /** The host attributes every responsive CSS rule keys off. */\n #syncLayoutAttributes(): void {\n this.gridElement.toggleAttribute('data-responsive', this.#isResponsive);\n\n // The keyframe fade is the no-view-transition path only. Leaving it on\n // would replay the card-enter animation the moment the transition ends.\n this.gridElement.toggleAttribute(\n 'data-responsive-animate',\n this.#animation !== false && !this.#supportsViewTransition,\n );\n\n // Attribute only meaningful while in card mode; clear it otherwise so\n // the CSS rule cannot accidentally apply to non-responsive layouts.\n this.gridElement.toggleAttribute(\n 'data-responsive-hide-header',\n this.#isResponsive && this.config.hideHeader === true,\n );\n }\n\n /**\n * Apply the responsive state to the grid element.\n * Handles scroll reset when entering responsive mode and row height restoration on exit.\n */\n #applyResponsiveState(): void {\n this.#syncLayoutAttributes();\n\n // Cast to internal type for virtualization access\n const internalGrid = this.#internalGrid;\n\n if (this.#isResponsive) {\n // Store original row height before responsive mode changes it\n if (internalGrid._virtualization) {\n this.#originalRowHeight = internalGrid._virtualization.rowHeight;\n }\n\n // Reset horizontal scroll position when entering responsive mode\n // The CSS hides overflow but doesn't reset the scroll position\n const scrollArea = this.gridElement.querySelector('.tbw-scroll-area') as HTMLElement | null;\n if (scrollArea) {\n scrollArea.scrollLeft = 0;\n }\n } else {\n // Exiting responsive mode - clean up inline styles set by renderRow\n // The rows are reused from the pool, so we need to remove the card-specific styles\n const rows = this.gridElement.querySelectorAll('.data-grid-row');\n for (const row of rows) {\n (row as HTMLElement).style.height = '';\n row.classList.remove('responsive-card');\n }\n\n // Restore original row height\n if (this.#originalRowHeight && this.#originalRowHeight > 0 && internalGrid._virtualization) {\n internalGrid._virtualization.rowHeight = this.#originalRowHeight;\n this.#originalRowHeight = undefined;\n }\n\n // Clear cached measurements so they're remeasured fresh when re-entering responsive mode\n // Without this, stale measurements cause incorrect height calculations after scrolling\n this.#measuredCardHeight = undefined;\n this.#measuredGroupRowHeight = undefined;\n this.#lastCardRowCount = undefined;\n }\n }\n\n /**\n * Custom row rendering when cardRenderer is provided and in responsive mode.\n *\n * When a cardRenderer is configured, this hook takes over row rendering to display\n * the custom card layout instead of the default cell structure.\n *\n * @param row - The row data object\n * @param rowEl - The row DOM element to render into\n * @param rowIndex - The index of the row in the data array\n * @returns `true` if rendered (prevents default), `void` for default rendering\n */\n override renderRow(row: unknown, rowEl: HTMLElement, rowIndex: number): boolean | void {\n // Only override when in responsive mode AND cardRenderer is provided\n if (!this.#isResponsive || !this.config.cardRenderer) {\n return; // Let default rendering proceed\n }\n\n // Skip group rows from GroupingRowsPlugin - they have special structure\n // and should use their own renderer\n if ((row as { __isGroupRow?: boolean }).__isGroupRow) {\n return; // Let GroupingRowsPlugin handle group row rendering\n }\n\n // Clear existing content\n rowEl.replaceChildren();\n\n // Call user's cardRenderer to get custom content\n const cardContent = this.config.cardRenderer(row as T, rowIndex);\n\n // Reset className - clears any stale classes from previous use (e.g., 'group-row' from recycled element)\n // This follows the same pattern as GroupingRowsPlugin which sets className explicitly\n rowEl.className = 'data-grid-row responsive-card';\n\n // Handle card row height — when a numeric height is configured, use the effective\n // height from #getCardHeight() which incorporates DOM measurement after first render.\n // This keeps inline height in sync with the position cache used for virtualization.\n const configuredHeight = this.config.cardRowHeight;\n if (configuredHeight === 'auto' || configuredHeight === undefined) {\n rowEl.style.height = 'auto';\n } else {\n rowEl.style.height = `${this.#getCardHeight()}px`;\n }\n\n // Append the custom card content\n // The row keeps `role=\"row\"`, which may only own cell-ish children (WCAG\n // 1.3.1 / axe `aria-required-children`). A card collapses the whole record\n // into one box, so it is exposed as a single spanning gridcell — unless the\n // renderer already assigned its own role.\n if (!cardContent.hasAttribute('role')) {\n cardContent.setAttribute('role', 'gridcell');\n cardContent.setAttribute('aria-colindex', '1');\n }\n rowEl.appendChild(cardContent);\n\n return true; // We handled rendering\n }\n\n /**\n * Handle keyboard navigation in responsive mode.\n *\n * In responsive mode, the visual layout is inverted:\n * - Cells are stacked vertically within each \"card\" (row)\n * - DOWN/UP visually moves within the card (between fields)\n * - Page Down/Page Up or Ctrl+Down/Up moves between cards\n *\n * For custom cardRenderers, keyboard navigation is disabled entirely\n * since the implementor controls the card content and should handle\n * navigation via their own event handlers.\n *\n * @returns `true` if the event was handled and default behavior should be prevented\n */\n override onKeyDown(e: KeyboardEvent): boolean {\n if (!this.#isResponsive) {\n return false;\n }\n\n // If custom cardRenderer is provided, disable grid's keyboard navigation\n // The implementor is responsible for their own navigation\n if (this.config.cardRenderer) {\n const navKeys = ['ArrowUp', 'ArrowDown', 'ArrowLeft', 'ArrowRight'];\n if (navKeys.includes(e.key)) {\n // Let the event bubble - implementor can handle it\n return false;\n }\n }\n\n // Swap arrow key behavior for CSS-only responsive mode\n // In card layout, cells are stacked vertically:\n // Card 1: Card 2:\n // ID: 1 ID: 2\n // Name: Alice Name: Bob <- ArrowRight goes here\n // Dept: Eng Dept: Mkt\n // ↓ ArrowDown goes here\n //\n // ArrowDown/Up = move within card (change column/field)\n // ArrowRight/Left = move between cards (change row)\n const maxRow = this.rows.length - 1;\n const maxCol = this.visibleColumns.length - 1;\n\n switch (e.key) {\n case 'ArrowDown':\n // Move down WITHIN card (to next field/column)\n if (this.grid._focusCol < maxCol) {\n this.grid._focusCol += 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n // At bottom of card - optionally move to next card's first field\n if (this.grid._focusRow < maxRow) {\n this.grid._focusRow += 1;\n this.grid._focusCol = 0;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n\n case 'ArrowUp':\n // Move up WITHIN card (to previous field/column)\n if (this.grid._focusCol > 0) {\n this.grid._focusCol -= 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n // At top of card - optionally move to previous card's last field\n if (this.grid._focusRow > 0) {\n this.grid._focusRow -= 1;\n this.grid._focusCol = maxCol;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n\n case 'ArrowRight':\n // Move to NEXT card (same field)\n if (this.grid._focusRow < maxRow) {\n this.grid._focusRow += 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n\n case 'ArrowLeft':\n // Move to PREVIOUS card (same field)\n if (this.grid._focusRow > 0) {\n this.grid._focusRow -= 1;\n e.preventDefault();\n ensureCellVisible(this.#internalGrid);\n return true;\n }\n break;\n }\n\n return false;\n }\n\n // ============================================\n // Variable Height Support for Mixed Row Types\n // ============================================\n\n /** Measured card height from DOM for virtualization calculations */\n #measuredCardHeight?: number;\n\n /** Measured group row height from DOM for virtualization calculations */\n #measuredGroupRowHeight?: number;\n\n /** Last known card row count for detecting changes (e.g., group expand/collapse) */\n #lastCardRowCount?: number;\n\n /**\n * Get the effective card height for virtualization calculations.\n */\n #getCardHeight(): number {\n const configHeight = this.config.cardRowHeight;\n const measured = this.#measuredCardHeight;\n\n // A fixed height is the answer unless the content overflows it; only `auto`\n // hands the decision to the measurement outright.\n if (typeof configHeight === 'number' && configHeight > 0) {\n return measured && measured > configHeight ? measured : configHeight;\n }\n if (measured && measured > 0) {\n return measured;\n }\n return 80;\n }\n\n /**\n * Get the effective group row height for virtualization calculations.\n * Uses DOM-measured height, falling back to original row height.\n */\n #getGroupRowHeight(): number {\n if (this.#measuredGroupRowHeight && this.#measuredGroupRowHeight > 0) {\n return this.#measuredGroupRowHeight;\n }\n // Fall back to original row height (before responsive mode)\n return this.#originalRowHeight ?? 28;\n }\n\n /**\n * Check if there are any group rows in the current dataset.\n * Used to determine if we have mixed row heights.\n */\n #hasGroupRows(): boolean {\n for (const row of this.rows) {\n if ((row as { __isGroupRow?: boolean }).__isGroupRow) {\n return true;\n }\n }\n return false;\n }\n\n /**\n * Get the height of a specific row based on its type (group row vs card row).\n * Returns undefined if not in responsive mode.\n *\n * @param row - The row data\n * @param _index - The row index (unused, but part of the interface)\n * @returns The row height in pixels, or undefined if not in responsive mode\n */\n override getRowHeight(row: unknown, _index: number): number | undefined {\n // Only applies when in responsive mode with cardRenderer\n if (!this.#isResponsive || !this.config.cardRenderer) {\n return undefined;\n }\n\n // Check if this is a group row\n if ((row as { __isGroupRow?: boolean }).__isGroupRow) {\n return this.#getGroupRowHeight();\n }\n\n // Regular card row\n return this.#getCardHeight();\n }\n\n /**\n * Count the number of card rows (non-group rows) in the current dataset.\n */\n #countCardRows(): number {\n let count = 0;\n for (const row of this.rows) {\n if (!(row as { __isGroupRow?: boolean }).__isGroupRow) {\n count++;\n }\n }\n return count;\n }\n\n /** Pending refresh scheduled via microtask */\n #pendingRefresh = false;\n\n /**\n * Measure card height from DOM after render and detect row count changes.\n * Called in afterRender to ensure scroll calculations are accurate.\n *\n * This handles two scenarios:\n * 1. Card height changes (content overflow, dynamic sizing)\n * 2. Card row count changes (group expand/collapse)\n * 3. Group row height changes\n *\n * For uniform card layouts (no groups), we update the virtualization row height\n * directly to the card height. For mixed layouts (groups + cards), we use the\n * getExtraHeight mechanism to report height differences.\n *\n * The refresh is deferred via microtask to avoid nested render cycles.\n */\n #measureCardHeightFromDOM(): void {\n if (!this.#isResponsive) {\n return;\n }\n\n let needsRefresh = false;\n const internalGrid = this.#internalGrid;\n const hasGroups = this.#hasGroupRows();\n\n // Check if card row count changed (e.g., group expanded/collapsed)\n const currentCardRowCount = this.#countCardRows();\n if (currentCardRowCount !== this.#lastCardRowCount) {\n this.#lastCardRowCount = currentCardRowCount;\n needsRefresh = true;\n }\n\n // Measure actual group row height from DOM (for mixed layouts)\n if (hasGroups) {\n const groupRow = this.gridElement.querySelector('.data-grid-row.group-row') as HTMLElement | null;\n if (groupRow) {\n const height = groupRow.getBoundingClientRect().height;\n if (height > 0 && height !== this.#measuredGroupRowHeight) {\n this.#measuredGroupRowHeight = height;\n needsRefresh = true;\n }\n }\n }\n\n // Measure actual card height from DOM.\n // With cardRenderer, rows have the `.responsive-card` class.\n // Without cardRenderer (CSS-only card mode), rows are plain `.data-grid-row`\n // elements whose CSS height is `auto` — we need to measure their actual\n // rendered height so the faux scrollbar spacer is sized correctly.\n const cardSelector = this.config.cardRenderer ? '.data-grid-row.responsive-card' : '.data-grid-row:not(.group-row)';\n const cardRow = this.gridElement.querySelector(cardSelector) as HTMLElement | null;\n if (cardRow) {\n const height = cardRow.getBoundingClientRect().height;\n if (height > 0 && height !== this.#measuredCardHeight) {\n this.#measuredCardHeight = height;\n needsRefresh = true;\n\n // For uniform card layouts (no groups), update virtualization row height directly\n // This ensures proper row recycling and translateY calculations\n if (!hasGroups && internalGrid._virtualization) {\n internalGrid._virtualization.rowHeight = height;\n }\n }\n }\n\n // Defer virtualization refresh to avoid nested render cycles\n // This is called from afterRender, so we can't call refreshVirtualWindow synchronously\n // Use scheduler's VIRTUALIZATION phase to batch properly and avoid duplicate afterRender calls\n if (needsRefresh && !this.#pendingRefresh) {\n this.#pendingRefresh = true;\n queueMicrotask(() => {\n this.#pendingRefresh = false;\n // Only refresh if still attached and in responsive mode\n if (this.grid && this.#isResponsive) {\n // Request virtualization phase through grid's public API\n // This goes through the scheduler which batches and handles afterRender properly\n this.#internalGrid.refreshVirtualWindow?.(true, true);\n }\n });\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("../../core/constants"),require("../../core/internal/aria"),require("../../core/internal/pointer-modality"),require("../../core/internal/utils"),require("../../core/plugin/base-plugin"),require("../../core/plugin/expander-column"),require("../../core/internal/pointer-drag")):"function"==typeof define&&define.amd?define(["exports","../../core/constants","../../core/internal/aria","../../core/internal/pointer-modality","../../core/internal/utils","../../core/plugin/base-plugin","../../core/plugin/expander-column","../../core/internal/pointer-drag"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_selection={},e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid)}(this,function(e,t,n,i,s,l,o,r){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function c(){return"function"==typeof HTMLElement.prototype?.showPopover}function a(e,t){let n=null,i=!1,s=!1;const l=new AbortController;function o(){i&&n&&(i=!1,c()&&n.hidePopover(),n.hidden=!0)}return document.addEventListener("pointerdown",e=>{i&&n&&!e.composedPath().includes(n)&&o()},{capture:!0,signal:l.signal}),document.addEventListener("keydown",e=>{"Escape"===e.key&&o()},{capture:!0,signal:l.signal}),{get element(){return n},open(r,a,d){if(s||0===d.length)return;n||(n=function(){const n=document.createElement("div");return n.id=e,n.className=t,n.setAttribute("popover","manual"),n.setAttribute("role","group"),n.hidden=!0,n.addEventListener("focusout",e=>{const t=e.relatedTarget;t instanceof Node&&!n.contains(t)&&o()},{signal:l.signal}),n}());const h=r.closest("tbw-grid, [data-tbw-grid]")??document.body;n.parentNode!==h&&h.appendChild(n),n.setAttribute("aria-label",a),n.replaceChildren();for(const e of d){const i=document.createElement("button");i.type="button",i.className=`${t}-item`,i.textContent=e.label,i.disabled=!0===e.disabled,i.addEventListener("click",()=>{o(),e.run()}),n.appendChild(i)}n.hidden=!1,!i&&c()&&n.showPopover(),i=!0,function(e){if(!n)return;const t=e.getBoundingClientRect(),i=n.getBoundingClientRect(),s=Math.max(4,Math.min(t.left,window.innerWidth-i.width-4)),l=t.bottom+6,o=l+i.height<=window.innerHeight?l:Math.max(4,t.top-6-i.height);n.style.left=`${Math.round(s)}px`,n.style.top=`${Math.round(o)}px`}(r),n.querySelector("button:not([disabled])")?.focus()},close:o,dispose(){s=!0,o(),l.abort(),n?.remove(),n=null}}}function d(e){const t=new Set(["cell","row","column","range"]);if("string"==typeof e){if(!t.has(e))throw new Error(`[SelectionPlugin] Invalid selection mode: "${e}". Expected one of: 'cell' | 'row' | 'column' | 'range'.`);return{primary:e,columnEnabled:"column"===e,bothAxes:!1}}if(!Array.isArray(e))throw new Error(`[SelectionPlugin] Invalid selection mode: expected string or array, got ${typeof e}.`);if(0===e.length)throw new Error("[SelectionPlugin] Invalid selection mode: array must contain at least one mode.");for(const i of e)if(!t.has(i))throw new Error(`[SelectionPlugin] Invalid selection mode in array: "${i}". Expected one of: 'cell' | 'row' | 'column' | 'range'.`);if(new Set(e).size!==e.length)throw new Error(`[SelectionPlugin] Invalid selection mode: array contains duplicate entries (${e.join(", ")}).`);if(1===e.length)return d(e[0]);if(e.length>2)throw new Error(`[SelectionPlugin] Invalid selection mode: arrays of more than 2 modes are not supported. Got [${e.join(", ")}].`);if(!e.includes("column"))throw new Error(`[SelectionPlugin] Invalid selection mode: [${e.join(", ")}]. Two-mode arrays must include 'column' (the only valid combinations are ['row','column'], ['cell','column'], ['range','column']). Other in-row modes don't compose meaningfully.`);const n=e.find(e=>"column"!==e);if("column"===n)throw new Error(`[SelectionPlugin] Invalid selection mode: [${e.join(", ")}].`);return{primary:n,columnEnabled:!0,bothAxes:!0}}function h(e){const t=[];for(const n of e)!0!==n.utility&&"string"==typeof n.field&&n.field.length>0&&t.push(n.field);return t}function u(e,t){return t.indexOf(e)}function g(e){return{startRow:Math.min(e.startRow,e.endRow),startCol:Math.min(e.startCol,e.endCol),endRow:Math.max(e.startRow,e.endRow),endCol:Math.max(e.startCol,e.endCol)}}function m(e){const t=g(e);return{from:{row:t.startRow,col:t.startCol},to:{row:t.endRow,col:t.endCol}}}function w(e,t,n){return n.some(n=>function(e,t,n){const i=g(n);return e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol}(e,t,n))}function b(e){const t=[],n=g(e);for(let i=n.startRow;i<=n.endRow;i++)for(let e=n.startCol;e<=n.endCol;e++)t.push({row:i,col:e});return t}function f(e,t){return{startRow:e.row,startCol:e.col,endRow:t.row,endCol:t.col}}function p(e,t){const n=g(e),i=g(t);return n.startRow===i.startRow&&n.startCol===i.startCol&&n.endRow===i.endRow&&n.endCol===i.endCol}var C="tbw-selection-toolbar",v=class{#e=null;#t=null;#n=null;#i=null;get mounted(){return null!==this.#e}get element(){return this.#e}show(e,t,n,i){this.#i=i,this.#e||this.#s(e),this.#t&&(this.#t.textContent=`${t} selected`),this.#e?.setAttribute("data-selected-count",String(t)),this.#n&&(this.#n.hidden=!n)}destroy(){this.#e?.remove(),this.#e=null,this.#t=null,this.#n=null,this.#i=null}#s(e){const t=e.ownerDocument.createElement("div");t.className=C,t.setAttribute("role","toolbar"),t.setAttribute("aria-label","Selection");const n=e.ownerDocument.createElement("span");n.className=`${C}-count`,n.setAttribute("aria-live","polite"),t.appendChild(n);const i=e.ownerDocument.createElement("div");i.className=`${C}-actions`,t.appendChild(i),i.appendChild(this.#l(e,"select-all","Select all",()=>this.#i?.selectAll())),i.appendChild(this.#l(e,"clear","Clear",()=>this.#i?.clear()));const s=this.#l(e,"more","More…",e=>this.#i?.more(e));s.setAttribute("aria-haspopup","menu"),i.appendChild(s),i.appendChild(this.#l(e,"done","Done",()=>this.#i?.done())),e.prepend(t),this.#e=t,this.#t=n,this.#n=s}#l(e,t,n,i){const s=e.ownerDocument.createElement("button");return s.type="button",s.className=`${C}-btn`,s.dataset.action=t,s.textContent=n,s.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),i(s)}),s}},y="tbw-range-handle",S=class{#o=null;#r=null;#i=null;#c=null;render(e,t,n,i){this.#i=i,t?(this.#o??=this.#a(e,"start"),this.#r??=this.#a(e,"end"),this.#d(e,this.#o,n(t.startRow,t.startCol),"start"),this.#d(e,this.#r,n(t.endRow,t.endCol),"end"),this.setArmed(this.#c)):this.#h()}setArmed(e){this.#c=e;for(const[t,n]of[[this.#o,"start"],[this.#r,"end"]]){if(!t)continue;const i=e===n;t.classList.toggle("armed",i),t.setAttribute("aria-pressed",String(i))}}destroy(){this.#o?.remove(),this.#r?.remove(),this.#o=null,this.#r=null,this.#i=null,this.#c=null}#h(){this.#o&&(this.#o.hidden=!0),this.#r&&(this.#r.hidden=!0)}#d(e,t,n,i){if(!t)return;if(!n)return void(t.hidden=!0);t.hidden=!1;const s=n.getBoundingClientRect?.(),l=e.getBoundingClientRect?.();if(!s||!l)return;const o="start"===i?s.left-l.left:s.right-l.left,r="start"===i?s.top-l.top:s.bottom-l.top;t.style.left=`${o}px`,t.style.top=`${r}px`}#a(e,t){const n=e.ownerDocument.createElement("div");return n.className=`${y} ${y}-${t}`,n.dataset.corner=t,n.setAttribute("role","button"),n.setAttribute("tabindex","-1"),n.setAttribute("aria-pressed","false"),n.setAttribute("aria-label","start"===t?"Selection start corner — drag, or activate to place it with a tap":"Selection end corner — drag, or activate to place it with a tap"),n.addEventListener("pointerdown",e=>this.#u(e,n,t)),e.appendChild(n),n}#u(e,t,n){if(0!==e.button&&"mouse"===e.pointerType)return;e.preventDefault(),e.stopPropagation();const i=this.#i;if(!i)return;t.classList.add("dragging");let s=!1;(0,r.startPointerDrag)(e,t,{onMove:e=>{s=!0;const t=i.cellAt(e.clientX,e.clientY);t&&i.resize(n,t.row,t.col)},onEnd:()=>{t.classList.remove("dragging"),s?i.commit():i.arm(n)},onCancel:()=>{t.classList.remove("dragging"),i.commit()}})}};function x(e){if("string"==typeof e)return e;if(Array.isArray(e)){const t=e.find(e=>"column"!==e);if(t)return t;if(e.includes("column"))return"column"}return"cell"}var A="__tbw_checkbox",E=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"];var R=class extends l.BaseGridPlugin{static manifest={queries:[{type:"getSelection",description:"Get the current selection state"},{type:"selectRows",description:"Select specific rows by index (row mode only)"},{type:"getSelectedRowIndices",description:"Get sorted array of selected row indices"},{type:"getSelectedRows",description:"Get actual row objects for the current selection (works in all modes)"},{type:"getSelectedColumns",description:"Get field names of selected columns (column mode only)"},{type:"selectColumns",description:"Select specific columns by field name (column mode only)"},{type:"getContextMenuItems",description:"Contribute the non-dragging range-extension action (WCAG 2.2 SC 2.5.7)"}],configRules:[{id:"selection/range-dblclick",severity:"warn",message:'"triggerOn: \'dblclick\'" has no effect when mode is "range".\n → Range selection uses drag interaction (mousedown → mousemove), not click events.\n → The "triggerOn" option only affects "cell" and "row" selection modes.',check:e=>"range"===x(e.mode)&&"dblclick"===e.triggerOn},{id:"selection/column-checkbox",severity:"warn",message:'"checkbox: true" only renders in row mode.\n → Column selection has no checkbox UI; activate columns via Ctrl+Click on a header or Ctrl+Space on a focused cell.',check:e=>!!e.checkbox&&"row"!==x(e.mode)}]};name="selection";styles='@layer tbw-plugins{tbw-grid.selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}tbw-grid:has(.selection){-webkit-user-select:none;user-select:none}:is(tbw-grid:has(.selection)[data-selection-mode=row] .cell-focus,tbw-grid:has(.selection)[data-selection-mode=row] .row-focus,tbw-grid:has(.selection)[data-selection-mode=range] .cell-focus){outline:none}tbw-grid .data-grid-row.row-focus{background-color:var(--tbw-focus-background,rgba(from var(--tbw-color-accent) r g b / 12%));outline:none;position:relative}tbw-grid .data-grid-row.row-focus:after{content:"";pointer-events:none;border-width:0;border-style:var(--tbw-selection-border-style,var(--tbw-border-style));border-color:var(--tbw-range-border-color,var(--tbw-color-accent));border-top-width:var(--tbw-selection-border-width,var(--tbw-border-width));border-bottom-width:var(--tbw-selection-border-width,var(--tbw-border-width));z-index:26;position:absolute;inset:0}tbw-grid .data-grid-row.row-focus+.data-grid-row.row-focus:after{border-top-width:0}tbw-grid .data-grid-row.row-focus:has(+.data-grid-row.row-focus):after{border-bottom-width:0}tbw-grid .data-grid-row.row-focus>.cell.sticky-left,tbw-grid .data-grid-row.row-focus>.cell.sticky-right{background:linear-gradient(var(--tbw-focus-background,rgba(from var(--tbw-color-accent) r g b / 12%)) 0 0), var(--tbw-color-panel-bg)}tbw-grid .data-grid-row.row-focus[data-selectable=false]>.cell.sticky-left,tbw-grid .data-grid-row.row-focus[data-selectable=false]>.cell.sticky-right{background:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg);position:relative}tbw-grid .data-grid-row>.cell.selected:after{content:"";pointer-events:none;border:0 var(--tbw-selection-border-style,var(--tbw-border-style)) var(--tbw-range-border-color);z-index:1;position:absolute;inset:0}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg,rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm,.8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}tbw-grid .data-grid-row>.cell[data-field=__tbw_checkbox],tbw-grid .header-row>.cell[data-field=__tbw_checkbox]{text-align:center;cursor:pointer;justify-content:center;align-items:center;padding:0;display:flex}tbw-grid .tbw-select-row-checkbox{pointer-events:none;cursor:pointer;margin:0}tbw-grid .tbw-checkbox-header{cursor:pointer;justify-content:center;align-items:center;height:100%;display:flex}tbw-grid .tbw-select-all-checkbox{cursor:pointer;margin:0}@media (pointer:coarse){tbw-grid .tbw-select-row-checkbox,tbw-grid .tbw-select-all-checkbox{min-width:var(--tbw-touch-target-min,24px);min-height:var(--tbw-touch-target-min,24px)}}tbw-grid .header-row>.cell.column-selected,tbw-grid .data-grid-row>.cell.column-selected,tbw-grid .data-grid-row>.cell.column-selected.sticky-left,tbw-grid .data-grid-row>.cell.column-selected.sticky-right,tbw-grid .header-row>.cell.column-selected.sticky-left,tbw-grid .header-row>.cell.column-selected.sticky-right{background:linear-gradient(var(--tbw-column-selection-bg,rgba(from var(--tbw-color-accent) r g b / 12%)) 0 0), var(--tbw-color-panel-bg)}tbw-grid .tbw-selection-toolbar{z-index:30;background:var(--tbw-color-panel-bg);border-bottom:1px solid var(--tbw-color-border);justify-content:space-between;align-items:center;gap:8px;padding:4px 8px;font-size:13px;display:flex;position:sticky;top:0}tbw-grid .tbw-selection-toolbar-count{white-space:nowrap;font-weight:600}tbw-grid .tbw-selection-toolbar-actions{align-items:center;gap:4px;display:flex}tbw-grid .tbw-selection-toolbar-btn{min-height:var(--tbw-touch-target-min,24px);border-radius:min(var(--tbw-border-radius,4px), 6px);color:inherit;font:inherit;cursor:pointer;background:0 0;border:0;padding:0 10px}tbw-grid .tbw-selection-toolbar-btn:hover,tbw-grid .tbw-selection-toolbar-btn:focus-visible{background:var(--tbw-color-row-hover,light-dark(#f0f0f0,#2a2a2a))}tbw-grid .tbw-selection-toolbar-btn[data-action=done]{color:var(--tbw-color-accent);font-weight:600}tbw-grid .tbw-range-handle{z-index:25;width:var(--tbw-touch-target-min,24px);height:var(--tbw-touch-target-min,24px);background:radial-gradient(circle, var(--tbw-color-accent) 0 5px, transparent 5px);cursor:crosshair;touch-action:none;position:absolute;translate:-50% -50%}tbw-grid .tbw-range-handle[hidden]{display:none}tbw-grid .tbw-range-handle.dragging{scale:1.3}tbw-grid .tbw-range-handle.armed{outline:2px solid var(--tbw-color-accent);outline-offset:-4px;border-radius:50%;scale:1.3}}';get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0,multiSelect:!0,touchMode:"transient"}}get touchSelectionActive(){return this.#g}#g=!1;#m=!1;#w=new v;#b=new S;#f=null;#p=null;#C=null;selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;pendingRowKeyUpdate=null;selectedCell=null;selectedColumns=new Set;columnAnchor=null;columnHead=null;activeAxis="none";#v={primary:"cell",columnEnabled:!1,bothAxes:!1};lastSyncedFocusRow=-1;lastSyncedFocusCol=-1;announceTimer=null;explicitSelection=!1;isSelectionEnabled(){return!1!==this.config.enabled&&!1!==this.grid.effectiveConfig?.selectable}checkSelectable(e,t){const{isSelectable:n}=this.config;if(!n)return!0;const i=this.rows[e];return!!i&&n(i,e,void 0!==t?this.visibleColumns[t]:void 0,t)}isRowSelectable(e){return this.checkSelectable(e)}isCellSelectable(e,t){return this.checkSelectable(e,t)}attach(e){super.attach(e),this.#v=d(this.config.mode),this.on("filter-change",()=>this.clearSelectionSilent()),this.on("group-toggle",()=>this.clearSelectionSilent()),this.on("tree-expand",()=>this.clearSelectionSilent()),this.on("sort-change",()=>this.clearSelectionSilent()),this.on("edit-open",({rowIndex:e,row:t})=>{this.isSelectionEnabled()&&(null==t||e<0||"row"===this.#v.primary&&this.isRowSelectable(e)&&(this.selected.has(e)||(!1===this.config.multiSelect&&this.selected.clear(),this.selected.add(e),this.lastSelected=e,this.anchor=e,this.explicitSelection=!0,this.emit("selection-change",this.#y()),this.requestAfterRender())))});let t=-1;e.addEventListener("data-change",e=>{const{sourceRowCount:n}=e.detail,i=this.selected.size>0||this.ranges.length>0||null!==this.selectedCell;-1!==t&&n!==t&&i&&this.clearSelectionSilent(),t=n},{signal:this.disconnectSignal}),e.addEventListener("contextmenu",e=>{if(this.#S())return;const t=e.target?.closest?.(".cell[data-row][data-col]");if(!t)return;const n=parseInt(t.getAttribute("data-row")??"-1",10),i=parseInt(t.getAttribute("data-col")??"-1",10),s=this.#x(n,i);s&&(e.preventDefault(),this.#A(t,s))},{signal:this.disconnectSignal})}handleQuery(e){if("getSelection"===e.type)return this.getSelection();if("getSelectedRowIndices"===e.type)return this.getSelectedRowIndices();if("getSelectedRows"===e.type)return this.getSelectedRows();if("selectRows"===e.type)return this.selectRows(e.context),!0;if("getSelectedColumns"===e.type)return this.getSelectedColumns();if("selectColumns"===e.type){const t=e.context;return this.#E(t),!0}if("getContextMenuItems"===e.type){const t=e.context;if(!t||t.isHeader)return;const n=this.#x(t.rowIndex,t.columnIndex);if(!n)return;return n.map((e,t)=>({id:`selection-extend-${t}`,label:e.label,disabled:e.disabled,action:()=>e.run(),order:60+t}))}}detach(){(this.gridElement?.querySelector(".rows-body"))?.removeAttribute("aria-multiselectable"),this.#w.destroy(),this.#b.destroy(),this.#C?.dispose(),this.#C=null,this.#f=null,this.#p=null,this.#g=!1,this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.activeAxis="none",this.pendingKeyboardUpdate=null,this.pendingRowKeyUpdate=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,this.announceTimer&&(clearTimeout(this.announceTimer),this.announceTimer=null)}clearSelectionSilent(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedCell=null,this.lastSelected=null,this.anchor=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,"column"!==this.activeAxis&&(this.activeAxis="none"),this.requestAfterRender()}#R(e){const t=e.pointerType;return"touch"===t||"pen"===t||"mouse"!==t&&"coarse"===(0,i.getPrimaryPointer)()}#k(e){return!!this.isRowSelectable(e)&&(this.#g=!0,this.selected.clear(),this.selected.add(e),this.anchor=e,this.lastSelected=e,this.explicitSelection=!0,this.emit("selection-change",this.#y()),this.requestAfterRender(),!0)}exitTouchSelection(){this.#g&&(this.#g=!1,"transient"===(this.config.touchMode??"transient")&&(this.selected.clear(),this.anchor=null,this.lastSelected=null,this.emit("selection-change",this.#y())),this.requestAfterRender())}#M(e){const t=this.anchor??e,n=Math.min(t,e),i=Math.max(t,e);for(let s=n;s<=i;s++)this.isRowSelectable(s)&&this.selected.add(s);this.lastSelected=e,this.emit("selection-change",this.#y()),this.requestAfterRender()}#S(){const e=this.grid?.getPluginByName?.("contextMenu")??this.grid?.getPluginByName?.("context-menu");return"function"==typeof e?.showMenu?e:void 0}#I(e,t){return this.gridElement?.querySelector(`.cell[data-row="${e}"][data-col="${t}"]`)??null}#q(e,t){const n=this.gridElement,i=((n?.ownerDocument)?.elementFromPoint?.(e,t))?.closest?.(".cell[data-row][data-col]");if(!i||!n?.contains?.(i))return null;const s=parseInt(i.getAttribute("data-row")??"-1",10),l=parseInt(i.getAttribute("data-col")??"-1",10);return s>=0&&l>=0?{row:s,col:l}:null}#P(e,t,n){const i=this.ranges[this.ranges.length-1];if(!i)return;const s=g(i),l="start"===e?{row:s.endRow,col:s.endCol}:{row:s.startRow,col:s.startCol},o=f(l,{row:t,col:n});p(i,o)||(this.ranges[this.ranges.length-1]=o,this.activeRange=o,this.cellAnchor=l,this.emit("selection-change",this.#y()),this.requestAfterRender())}#x(e,t){if("range"!==this.#v.primary)return null;if(!this.isSelectionEnabled())return null;if(e<0||t<0)return null;if(!this.isCellSelectable(e,t))return null;const n=this.#p;return n?n.row===e&&n.col===t?null:[{label:"Extend selection to here",run:()=>this.#T(e,t)}]:null}#T(e,t){const n=this.#p;if(!n)return;const i=f(n,{row:e,col:t});this.ranges=[i],this.activeRange=i,this.cellAnchor=n,this.emit("selection-change",this.#y()),this.requestAfterRender()}#A(e,t){this.#C??=a("tbw-range-extend-menu","tbw-range-extend-menu"),this.#C.open(e,"Selection",t)}#K(e){this.#f=this.#f===e?null:e,this.#b.setArmed(this.#f),this.gridElement&&(0,n.announce)(this.gridElement,this.#f?"Select a cell to place the selection corner":"Selection corner placement cancelled")}#L(){return!!this.#f&&(this.#f=null,this.#b.setArmed(null),!0)}#D(e){if(!e)return;this.#g?this.#w.show(e,this.selected.size,void 0!==this.#S(),{selectAll:()=>this.selectAll(),clear:()=>this.clearSelection(),done:()=>this.exitTouchSelection(),more:e=>this.#z(e)}):this.#w.destroy();const t=this.#m&&"range"===this.#v.primary&&this.ranges.length>0?this.ranges[this.ranges.length-1]:null,n=t?g(t):null;n||this.#L(),this.#b.render(e,n,(e,t)=>this.#I(e,t),{cellAt:(e,t)=>this.#q(e,t),resize:(e,t,n)=>this.#P(e,t,n),commit:()=>this.requestAfterRender(),arm:e=>this.#K(e)})}#z(e){const t=this.#S();if(!t)return;const n=e.getBoundingClientRect?.(),i=this.lastSelected??-1;t.showMenu(n?.left??0,n?.bottom??0,{row:i>=0?this.rows[i]:null,rowIndex:i,selectedRows:this.getSelectedRowIndices()})}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{triggerOn:t="click"}=this.config;if(e.originalEvent.type!==t)return!1;switch(this.#v.primary){case"cell":return this.#F(e);case"row":return this.#H(e);case"range":return this.#U(e);default:return!1}}#F(e){const{rowIndex:t,colIndex:n}=e;if(e.column&&(0,o.isUtilityColumn)(e.column))return!1;if(!this.isCellSelectable(t,n))return!1;const i=this.selectedCell;return i&&i.row===t&&i.col===n||(this.selectedCell={row:t,col:n},this.emit("selection-change",this.#y()),this.requestAfterRender()),!1}#H(e){const{rowIndex:t,originalEvent:n,column:i}=e;if(!this.isRowSelectable(t))return!1;const s=!1!==this.config.multiSelect,l=n.shiftKey&&s,o=(n.ctrlKey||n.metaKey||this.#g)&&s,r=!0===i?.checkboxColumn;if(l&&null!==this.anchor){const e=Math.min(this.anchor,t),n=Math.max(this.anchor,t);o||this.selected.clear();for(let t=e;t<=n;t++)this.isRowSelectable(t)&&this.selected.add(t)}else if(o||r&&s)this.selected.has(t)?this.selected.delete(t):this.selected.add(t),this.anchor=t;else{if(1===this.selected.size&&this.selected.has(t))return!1;this.selected.clear(),this.selected.add(t),this.anchor=t}return this.lastSelected=t,this.explicitSelection=!0,this.emit("selection-change",this.#y()),this.requestAfterRender(),!1}#U(e){const{rowIndex:t,colIndex:n,originalEvent:i}=e;if(e.column&&(0,o.isUtilityColumn)(e.column))return!1;if(!this.isCellSelectable(t,n))return!1;if(this.#f){const e=this.#f;return this.#L(),this.#P(e,t,n),!0}this.#m=this.#R(i);const s=i.shiftKey,l=(i.ctrlKey||i.metaKey)&&!1!==this.config.multiSelect;if(s&&this.cellAnchor){const e=f(this.cellAnchor,{row:t,col:n}),i=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(i&&p(i,e))return!1;l?this.ranges.length>0?this.ranges[this.ranges.length-1]=e:this.ranges.push(e):this.ranges=[e],this.activeRange=e}else{const e={startRow:t,startCol:n,endRow:t,endCol:n};if(l)this.ranges.push(e);else{if(1===this.ranges.length&&p(this.ranges[0],e))return!1;this.ranges=[e]}this.activeRange=e,this.cellAnchor={row:t,col:n},this.#p={row:t,col:n}}return this.emit("selection-change",this.#y()),this.requestAfterRender(),!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;if(this.#v.columnEnabled&&this.#$(e))return!0;const t=this.#v.primary;if("Escape"===e.key&&this.#L())return!0;if("Escape"===e.key&&this.#g)return this.exitTouchSelection(),!0;if("Escape"===e.key)return this.#_(t);const n=E.includes(e.key);switch(t){case"cell":return!!n&&this.#G();case"row":return this.#N(e);case"range":return this.#j(e,n);default:return!1}}#$(e){if(!e.ctrlKey&&!e.metaKey)return!1;if(" "===e.key||"Spacebar"===e.key){const t=this.visibleColumns[this.grid._focusCol];return!(!t||(0,o.isUtilityColumn)(t)||"string"!=typeof t.field)&&(e.preventDefault(),e.stopPropagation(),this.selectColumn(t.field,{toggle:!0}),!0)}if(e.shiftKey&&"column"===this.activeAxis&&!1!==this.config.multiSelect&&("ArrowLeft"===e.key||"ArrowRight"===e.key)){const t=h(this.visibleColumns),n="ArrowLeft"===e.key?"left":"right",i=function(e,t,n){if(0===t.length)return null;if(null===e)return null;const i=u(e,t);if(i<0)return null;const s="left"===n?Math.max(0,i-1):Math.min(t.length-1,i+1);return s===i?null:t[s]}(this.columnHead,t,n);return null!==i&&null!==this.columnAnchor&&(e.preventDefault(),e.stopPropagation(),this.selectColumn(i,{range:!0}),!0)}return!1}#_(e){return!this.grid.query("isEditing").some(Boolean)&&("column"===this.activeAxis?(this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.activeAxis="none"):"cell"===e?this.selectedCell=null:"row"===e?(this.selected.clear(),this.anchor=null):"range"===e&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#y()),this.requestAfterRender(),!0)}#G(){return queueMicrotask(()=>{const e=this.grid._focusRow,t=this.grid._focusCol;this.selectedCell=this.isCellSelectable(e,t)?{row:e,col:t}:null,this.emit("selection-change",this.#y()),this.requestAfterRender()}),!1}#N(e){const t=!1!==this.config.multiSelect;if("ArrowUp"===e.key||"ArrowDown"===e.key||"PageUp"===e.key||"PageDown"===e.key||(e.ctrlKey||e.metaKey)&&("Home"===e.key||"End"===e.key)){const n=e.shiftKey&&t;return n&&null===this.anchor&&(this.anchor=this.grid._focusRow),this.explicitSelection=!0,this.pendingRowKeyUpdate={shiftKey:n},queueMicrotask(()=>this.requestAfterRender()),!1}return this.#B(e,t)}#j(e,t){if(!t)return this.#B(e,!1!==this.config.multiSelect);const n=e.shiftKey&&"Tab"!==e.key;return n&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:n},queueMicrotask(()=>this.requestAfterRender()),!1}#B(e,t){return!(!t||"a"!==e.key||!e.ctrlKey&&!e.metaKey)&&(!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0))}onCellMouseDown(e){if(!this.isSelectionEnabled())return;if("row"===this.#v.primary){if(void 0===e.rowIndex||e.rowIndex<0)return;if(!1===this.config.multiSelect)return;if(!this.#R(e.originalEvent))return;if(e.column&&(0,o.isUtilityColumn)(e.column))return;return this.#g?(this.#M(e.rowIndex),!0):this.#k(e.rowIndex)||void 0}if("range"!==this.#v.primary)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;if(this.#f)return!0;if(this.#m=this.#R(e.originalEvent),2===e.originalEvent.button&&w(e.rowIndex,e.colIndex,this.ranges))return;if(e.column&&(0,o.isUtilityColumn)(e.column))return;if(!this.isCellSelectable(e.rowIndex,e.colIndex))return;if(e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const t=e.rowIndex,n=e.colIndex,i=(e.originalEvent.ctrlKey||e.originalEvent.metaKey)&&!1!==this.config.multiSelect,s={startRow:t,startCol:n,endRow:t,endCol:n};return!i&&1===this.ranges.length&&p(this.ranges[0],s)?(this.cellAnchor={row:t,col:n},this.#O(e,t,n),!0):(this.cellAnchor={row:t,col:n},this.#O(e,t,n),i||(this.ranges=[]),this.ranges.push(s),this.activeRange=s,this.emit("selection-change",this.#y()),this.requestAfterRender(),!0)}#O(e,t,n){2!==e.originalEvent.button&&(this.#p={row:t,col:n})}onCellMouseMove(e){if(!this.isSelectionEnabled())return;if("range"!==this.#v.primary)return;if(!this.isDragging||!this.cellAnchor)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;let t=e.colIndex;const n=this.visibleColumns[t];if(n&&(0,o.isUtilityColumn)(n)){const e=this.visibleColumns.findIndex(e=>!(0,o.isUtilityColumn)(e));e>=0&&(t=e)}const i=f(this.cellAnchor,{row:e.rowIndex,col:t}),s=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return s&&p(s,i)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i),this.activeRange=i,this.emit("selection-change",this.#y()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&"range"===this.#v.primary)return this.isDragging?(this.isDragging=!1,!0):void 0}onHeaderClick(e){if(!this.isSelectionEnabled())return!1;if(!this.#v.columnEnabled)return!1;if(e.column&&(0,o.isUtilityColumn)(e.column))return!1;const t=e.originalEvent;if(!t.ctrlKey&&!t.metaKey)return!1;const n=e.field;if(!n)return!1;t.preventDefault(),"function"==typeof t.stopPropagation&&t.stopPropagation();const i=!0===t.shiftKey&&!1!==this.config.multiSelect&&null!==this.columnAnchor;return this.selectColumn(n,i?{range:!0}:{toggle:!0}),!0}processColumns(e){if(this.config.checkbox&&"row"===this.#v.primary){if(e.some(e=>e.field===A))return e;const t=this.#W(),n=e.findIndex(o.isExpanderColumn),i=n>=0?n+1:0;return[...e.slice(0,i),t,...e.slice(i)]}return e}#W(){return{field:A,header:"",width:32,resizable:!1,sortable:!1,lockPosition:!0,utility:!0,checkboxColumn:!0,headerRenderer:()=>{const e=document.createElement("label");if(e.className="tbw-checkbox-header",!1===this.config.multiSelect)return e;const t=document.createElement("input");return t.type="checkbox",t.className="tbw-select-all-checkbox",t.addEventListener("click",e=>{e.stopPropagation(),e.target.checked?this.selectAll():this.clearSelection()}),e.addEventListener("click",e=>e.stopPropagation()),e.appendChild(t),e},renderer:e=>{const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-row-checkbox";const n=e.cellEl;if(n){const e=parseInt(n.getAttribute("data-row")??"-1",10);e>=0&&(t.checked=this.selected.has(e))}return t}}}#Q(e){e.querySelectorAll(".tbw-select-row-checkbox").forEach(e=>{const t=e.closest(".cell"),n=t?(0,s.getRowIndexFromCell)(t):-1;n>=0&&(e.checked=this.selected.has(n))});const t=e.querySelector(".tbw-select-all-checkbox");if(t){const e=this.rows.length;let n=0;if(this.config.isSelectable)for(let t=0;t<e;t++)this.isRowSelectable(t)&&n++;else n=e;const i=n>0&&this.selected.size>=n,s=this.selected.size>0;t.checked=i,t.indeterminate=s&&!i}}#X(){return this.gridElement?.hasAttribute("data-has-focus")??!1}#Y(e){const t=this.grid._focusRow,n=this.grid._focusCol;if(!this.#X())return this.lastSyncedFocusRow=t,void(this.lastSyncedFocusCol=n);if("row"===e){if(this.explicitSelection)return this.explicitSelection=!1,void(this.lastSyncedFocusRow=t);t!==this.lastSyncedFocusRow&&(this.lastSyncedFocusRow=t,this.isRowSelectable(t)&&(this.selected.has(t)&&1===this.selected.size||(this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.anchor=t,this.emit("selection-change",this.#y()))))}if("cell"===e){if(this.explicitSelection)return this.explicitSelection=!1,this.lastSyncedFocusRow=t,void(this.lastSyncedFocusCol=n);if((t!==this.lastSyncedFocusRow||n!==this.lastSyncedFocusCol)&&(this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=n,this.isCellSelectable(t,n))){const e=this.selectedCell;e&&e.row===t&&e.col===n||(this.selectedCell={row:t,col:n},this.emit("selection-change",this.#y()))}}}#J(){const e=this.gridElement;if(!e)return;const n=this.#v.primary,i=this.#v.columnEnabled,l=!!this.config.isSelectable,r=e.querySelector(".rows-body");if(r){const e=!1!==this.config.multiSelect;r.setAttribute("aria-multiselectable",e?"true":"false")}e.querySelectorAll(".cell").forEach(e=>{const n=e.classList.contains(t.GridClasses.SELECTED)||e.classList.contains("column-selected");e.classList.remove(t.GridClasses.SELECTED,"top","bottom","first","last","column-selected"),n&&e.removeAttribute("aria-selected"),l&&e.removeAttribute("data-selectable")});const c=e.querySelectorAll(".data-grid-row");if(c.forEach(e=>{e.classList.remove(t.GridClasses.SELECTED,"row-focus"),e.setAttribute("aria-selected","false"),l&&e.removeAttribute("data-selectable")}),i&&e.querySelectorAll(".header-row > .cell").forEach(e=>{e.classList.remove("column-selected"),e.removeAttribute("aria-selected")}),"row"===n&&((0,s.clearCellFocus)(e),c.forEach(e=>{const n=(0,s.getRowIndexFromCell)(e.querySelector(".cell[data-row]"));n>=0&&(l&&!this.isRowSelectable(n)&&e.setAttribute("data-selectable","false"),this.selected.has(n)&&(e.classList.add(t.GridClasses.SELECTED,"row-focus"),e.setAttribute("aria-selected","true")))}),this.config.checkbox&&this.#Q(e)),"cell"!==n&&"range"!==n||!l||e.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const t=parseInt(e.getAttribute("data-row")??"-1",10),n=parseInt(e.getAttribute("data-col")??"-1",10);t>=0&&n>=0&&(this.isCellSelectable(t,n)||e.setAttribute("data-selectable","false"))}),"range"===n&&this.ranges.length>0){(0,s.clearCellFocus)(e);const n=this.ranges.map(g),i=(e,t)=>{for(const i of n)if(e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol)return!0;return!1};e.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const n=parseInt(e.getAttribute("data-row")??"-1",10),s=parseInt(e.getAttribute("data-col")??"-1",10);if(n>=0&&s>=0){const l=this.visibleColumns[s];if(l&&(0,o.isUtilityColumn)(l))return;i(n,s)&&(e.classList.add(t.GridClasses.SELECTED),e.setAttribute("aria-selected","true"),i(n-1,s)||e.classList.add("top"),i(n+1,s)||e.classList.add("bottom"),i(n,s-1)||e.classList.add("first"),i(n,s+1)||e.classList.add("last"))}})}if(i&&this.selectedColumns.size>0){const t=this.visibleColumns.map(e=>"string"==typeof e.field?e.field:void 0);e.querySelectorAll(".header-row > .cell[data-col]").forEach(e=>{const n=parseInt(e.getAttribute("data-col")??"-1",10),i=n>=0?t[n]:void 0;i&&this.selectedColumns.has(i)&&(e.classList.add("column-selected"),e.setAttribute("aria-selected","true"))}),e.querySelectorAll(".cell[data-col]:not(.header-row .cell)").forEach(e=>{if(e.closest(".header-row"))return;const n=parseInt(e.getAttribute("data-col")??"-1",10);if(n<0)return;const i=this.visibleColumns[n];if(!i||(0,o.isUtilityColumn)(i))return;const s=t[n];s&&this.selectedColumns.has(s)&&(e.classList.add("column-selected"),e.setAttribute("aria-selected","true"))})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.querySelector(".tbw-grid-root"),n=this.#v.primary;if(this.pendingRowKeyUpdate&&"row"===n){const{shiftKey:e}=this.pendingRowKeyUpdate;this.pendingRowKeyUpdate=null;const t=this.grid._focusRow;if(e&&null!==this.anchor){this.selected.clear();const e=Math.min(this.anchor,t),n=Math.max(this.anchor,t);for(let t=e;t<=n;t++)this.isRowSelectable(t)&&this.selected.add(t)}else this.isRowSelectable(t)?(this.selected.clear(),this.selected.add(t),this.anchor=t):this.selected.clear();this.lastSelected=t,this.emit("selection-change",this.#y())}if(this.pendingKeyboardUpdate&&"range"===n){const{shiftKey:e}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const t=this.grid._focusRow,n=this.grid._focusCol;if(e&&this.cellAnchor){const e=f(this.cellAnchor,{row:t,col:n});this.ranges=[e],this.activeRange=e}else e||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:t,col:n});this.emit("selection-change",this.#y())}this.#Y(n),this.gridElement.setAttribute("data-selection-mode",n),t&&t.classList.toggle("selecting",this.isDragging),this.#J(),this.#D(t)}onScrollRender(){this.isSelectionEnabled()&&this.#J()}getSelection(){const e=this.#y();return{mode:this.config.mode,activeAxis:e.activeAxis,ranges:e.ranges,selectedColumns:e.selectedColumns,anchor:this.cellAnchor}}getSelectedCells(){return function(e){const t=new Map;for(const n of e)for(const e of b(n))t.set(`${e.row},${e.col}`,e);return[...t.values()]}(this.ranges)}isCellSelected(e,t){return w(e,t,this.ranges)}selectAll(){const{mode:e,multiSelect:t}=this.config;if(!1!==t)if("row"===e){this.selected.clear();for(let e=0;e<this.rows.length;e++)this.isRowSelectable(e)&&this.selected.add(e);this.explicitSelection=!0,this.emit("selection-change",this.#y()),this.requestAfterRender()}else if("range"===e){const e=this.rows.length,t=this.columns.length;if(e>0&&t>0){const n={startRow:0,startCol:0,endRow:e-1,endCol:t-1};this.ranges=[n],this.activeRange=n,this.emit("selection-change",this.#y()),this.requestAfterRender()}}}selectRows(e){if("row"!==this.#v.primary)return;const t=!1===this.config.multiSelect&&e.length>1?[e[e.length-1]]:e;this.selected.clear();for(const n of t)n>=0&&n<this.rows.length&&this.isRowSelectable(n)&&this.selected.add(n);this.anchor=t.length>0?t[t.length-1]:null,this.explicitSelection=!0,this.emit("selection-change",this.#y()),this.requestAfterRender()}getSelectedRowIndices(){return[...this.selected].sort((e,t)=>e-t)}getSelectedRows(){const e=this.#v.primary,t=this.rows;if("row"===e)return this.getSelectedRowIndices().filter(e=>e>=0&&e<t.length).map(e=>t[e]);if("cell"===e&&this.selectedCell){const{row:e}=this.selectedCell;return e>=0&&e<t.length?[t[e]]:[]}if("range"===e&&this.ranges.length>0){const e=new Set;for(const n of this.ranges){const i=Math.max(0,Math.min(n.startRow,n.endRow)),s=Math.min(t.length-1,Math.max(n.startRow,n.endRow));for(let t=i;t<=s;t++)e.add(t)}return[...e].sort((e,t)=>e-t).map(e=>t[e])}return[]}#E(e){if(!this.#v.columnEnabled)return;const t=h(this.visibleColumns),n=new Set(t),i=[],s=new Set;for(const o of e)n.has(o)&&!s.has(o)&&(s.add(o),i.push(o));this.#V("column"),this.selectedColumns.clear();for(const o of i)this.selectedColumns.add(o);const l=i.length>0?i[i.length-1]:null;this.columnAnchor=l,this.columnHead=l,this.activeAxis=this.selectedColumns.size>0?"column":"none",this.emit("selection-change",this.#y()),this.requestAfterRender()}selectColumn(e,t={}){if(!this.#v.columnEnabled)return;const n=h(this.visibleColumns);if(n.includes(e)){if(this.#V("column"),!1===this.config.multiSelect)this.selectedColumns.clear(),this.selectedColumns.add(e),this.columnAnchor=e,this.columnHead=e;else if(t.range&&this.columnAnchor){const t=function(e,t,n){const i=u(e,n),s=u(t,n);if(-1===i||-1===s)return[];const l=Math.min(i,s),o=Math.max(i,s);return n.slice(l,o+1)}(this.columnAnchor,e,n);this.selectedColumns.clear();for(const e of t)this.selectedColumns.add(e);this.columnHead=e}else t.toggle?(this.selectedColumns.has(e)?this.selectedColumns.delete(e):this.selectedColumns.add(e),this.columnAnchor=e,this.columnHead=e):(this.selectedColumns.clear(),this.selectedColumns.add(e),this.columnAnchor=e,this.columnHead=e);this.activeAxis=this.selectedColumns.size>0?"column":"none",this.emit("selection-change",this.#y()),this.requestAfterRender()}}deselectColumn(e){this.#v.columnEnabled&&this.selectedColumns.delete(e)&&(0===this.selectedColumns.size&&(this.columnAnchor=null,this.columnHead=null,"column"===this.activeAxis&&(this.activeAxis="none")),this.emit("selection-change",this.#y()),this.requestAfterRender())}selectAllColumns(){if(!this.#v.columnEnabled)return;if(!1===this.config.multiSelect)return;const e=h(this.visibleColumns);if(0!==e.length){this.#V("column"),this.selectedColumns.clear();for(const t of e)this.selectedColumns.add(t);this.columnAnchor=e[0],this.columnHead=e[e.length-1],this.activeAxis="column",this.emit("selection-change",this.#y()),this.requestAfterRender()}}clearColumnSelection(){0!==this.selectedColumns.size&&(this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,"column"===this.activeAxis&&(this.activeAxis="none"),this.emit("selection-change",this.#y()),this.requestAfterRender())}getSelectedColumns(){return 0===this.selectedColumns.size?[]:h(this.visibleColumns).filter(e=>this.selectedColumns.has(e))}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.activeAxis="none",this.emit("selection-change",this.#y()),this.requestAfterRender()}setRanges(e){this.ranges=e.map(e=>({startRow:e.from.row,startCol:e.from.col,endRow:e.to.row,endCol:e.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",this.#y()),this.requestAfterRender()}#y(){const e=this.#v.primary,t="row"===e&&this.selected.size>0||"cell"===e&&null!==this.selectedCell||"range"===e&&this.ranges.length>0;let i;this.#v.bothAxes&&t&&this.selectedColumns.size>0&&(this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.gridElement&&(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"selectionAxisChanged","row"))),i=t?"row"===e?"row":"cell"===e?"cell":"range":this.selectedColumns.size>0?"column":"none",this.activeAxis=i;const s=function(e,t,n,i,s,l){if("column"===t&&i.selectedColumns.size>0)return{mode:e,activeAxis:"column",ranges:[],selectedColumns:l.filter(e=>i.selectedColumns.has(e))};if("cell"===n&&i.selectedCell)return{mode:e,activeAxis:"cell",ranges:[{from:{row:i.selectedCell.row,col:i.selectedCell.col},to:{row:i.selectedCell.row,col:i.selectedCell.col}}],selectedColumns:[]};if("row"===n&&i.selected.size>0){const t=[...i.selected].sort((e,t)=>e-t),n=[];let l=t[0],o=l;for(let e=1;e<t.length;e++)t[e]===o+1?o=t[e]:(n.push({from:{row:l,col:0},to:{row:o,col:s-1}}),l=t[e],o=l);return n.push({from:{row:l,col:0},to:{row:o,col:s-1}}),{mode:e,activeAxis:"row",ranges:n,selectedColumns:[]}}return"range"===n&&i.ranges.length>0?{mode:e,activeAxis:"range",ranges:(o=i.ranges,o.map(m)),selectedColumns:[]}:{mode:e,activeAxis:"none",ranges:[],selectedColumns:[]};var o}(this.config.mode,i,e,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges,selectedColumns:this.selectedColumns},this.columns.length,h(this.visibleColumns));return this.announceTimer&&clearTimeout(this.announceTimer),this.announceTimer=setTimeout(()=>{if("column"===s.activeAxis){const e=s.selectedColumns;if(1===e.length){const t=e[0],i=this.columns.find(e=>e.field===t),s="string"==typeof i?.header&&i.header||t;(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"columnSelected",s))}else e.length>1?(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"columnSelectionChanged",e.length)):(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"columnSelectionCleared"))}else{const e="row"===s.activeAxis?this.selected.size:s.ranges.length;e>0&&(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"selectionChanged",e))}},150),s}#V(e){if(this.#v.bothAxes&&"column"===e){if(!(this.selected.size>0||null!==this.selectedCell||this.ranges.length>0))return;this.selected.clear(),this.lastSelected=null,this.anchor=null,this.selectedCell=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.gridElement&&(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"selectionAxisChanged","column"))}}};e.SelectionPlugin=R});
|
|
1
|
+
!function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("../../core/constants"),require("../../core/internal/aria"),require("../../core/internal/pointer-modality"),require("../../core/internal/utils"),require("../../core/plugin/base-plugin"),require("../../core/plugin/expander-column"),require("../../core/internal/pointer-drag")):"function"==typeof define&&define.amd?define(["exports","../../core/constants","../../core/internal/aria","../../core/internal/pointer-modality","../../core/internal/utils","../../core/plugin/base-plugin","../../core/plugin/expander-column","../../core/internal/pointer-drag"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_selection={},e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid)}(this,function(e,t,n,i,s,l,o,r){Object.defineProperty(e,Symbol.toStringTag,{value:"Module"});function c(){return"function"==typeof HTMLElement.prototype?.showPopover}function a(e,t){let n=null,i=!1,s=!1;const l=new AbortController;function o(){i&&n&&(i=!1,c()&&n.hidePopover(),n.hidden=!0)}return document.addEventListener("pointerdown",e=>{i&&n&&!e.composedPath().includes(n)&&o()},{capture:!0,signal:l.signal}),document.addEventListener("keydown",e=>{"Escape"===e.key&&o()},{capture:!0,signal:l.signal}),{get element(){return n},open(r,a,d){if(s||0===d.length)return;n||(n=function(){const n=document.createElement("div");return n.id=e,n.className=t,n.setAttribute("popover","manual"),n.setAttribute("role","group"),n.hidden=!0,n.addEventListener("focusout",e=>{const t=e.relatedTarget;t instanceof Node&&!n.contains(t)&&o()},{signal:l.signal}),n}());const h=r.closest("tbw-grid, [data-tbw-grid]")??document.body;n.parentNode!==h&&h.appendChild(n),n.setAttribute("aria-label",a),n.replaceChildren();for(const e of d){const i=document.createElement("button");i.type="button",i.className=`${t}-item`,i.textContent=e.label,i.disabled=!0===e.disabled,i.addEventListener("click",()=>{o(),e.run()}),n.appendChild(i)}n.hidden=!1,!i&&c()&&n.showPopover(),i=!0,function(e){if(!n)return;const t=e.getBoundingClientRect(),i=n.getBoundingClientRect(),s=Math.max(4,Math.min(t.left,window.innerWidth-i.width-4)),l=t.bottom+6,o=l+i.height<=window.innerHeight?l:Math.max(4,t.top-6-i.height);n.style.left=`${Math.round(s)}px`,n.style.top=`${Math.round(o)}px`}(r),n.querySelector("button:not([disabled])")?.focus()},close:o,dispose(){s=!0,o(),l.abort(),n?.remove(),n=null}}}function d(e){const t=new Set(["cell","row","column","range"]);if("string"==typeof e){if(!t.has(e))throw new Error(`[SelectionPlugin] Invalid selection mode: "${e}". Expected one of: 'cell' | 'row' | 'column' | 'range'.`);return{primary:e,columnEnabled:"column"===e,bothAxes:!1}}if(!Array.isArray(e))throw new Error(`[SelectionPlugin] Invalid selection mode: expected string or array, got ${typeof e}.`);if(0===e.length)throw new Error("[SelectionPlugin] Invalid selection mode: array must contain at least one mode.");for(const i of e)if(!t.has(i))throw new Error(`[SelectionPlugin] Invalid selection mode in array: "${i}". Expected one of: 'cell' | 'row' | 'column' | 'range'.`);if(new Set(e).size!==e.length)throw new Error(`[SelectionPlugin] Invalid selection mode: array contains duplicate entries (${e.join(", ")}).`);if(1===e.length)return d(e[0]);if(e.length>2)throw new Error(`[SelectionPlugin] Invalid selection mode: arrays of more than 2 modes are not supported. Got [${e.join(", ")}].`);if(!e.includes("column"))throw new Error(`[SelectionPlugin] Invalid selection mode: [${e.join(", ")}]. Two-mode arrays must include 'column' (the only valid combinations are ['row','column'], ['cell','column'], ['range','column']). Other in-row modes don't compose meaningfully.`);const n=e.find(e=>"column"!==e);if("column"===n)throw new Error(`[SelectionPlugin] Invalid selection mode: [${e.join(", ")}].`);return{primary:n,columnEnabled:!0,bothAxes:!0}}function h(e){const t=[];for(const n of e)!0!==n.utility&&"string"==typeof n.field&&n.field.length>0&&t.push(n.field);return t}function u(e,t){return t.indexOf(e)}function g(e){return{startRow:Math.min(e.startRow,e.endRow),startCol:Math.min(e.startCol,e.endCol),endRow:Math.max(e.startRow,e.endRow),endCol:Math.max(e.startCol,e.endCol)}}function m(e){const t=g(e);return{from:{row:t.startRow,col:t.startCol},to:{row:t.endRow,col:t.endCol}}}function w(e,t,n){return n.some(n=>function(e,t,n){const i=g(n);return e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol}(e,t,n))}function b(e){const t=[],n=g(e);for(let i=n.startRow;i<=n.endRow;i++)for(let e=n.startCol;e<=n.endCol;e++)t.push({row:i,col:e});return t}function f(e,t){return{startRow:e.row,startCol:e.col,endRow:t.row,endCol:t.col}}function p(e,t){const n=g(e),i=g(t);return n.startRow===i.startRow&&n.startCol===i.startCol&&n.endRow===i.endRow&&n.endCol===i.endCol}var C="tbw-selection-toolbar",y=class{#e=null;#t=null;#n=null;#i=null;get mounted(){return null!==this.#e}get element(){return this.#e}show(e,t,n,i){this.#i=i,this.#e||this.#s(e),this.#t&&(this.#t.textContent=`${t} selected`),this.#e?.setAttribute("data-selected-count",String(t)),this.#n&&(this.#n.hidden=!n)}destroy(){this.#e?.remove(),this.#e=null,this.#t=null,this.#n=null,this.#i=null}#s(e){const t=e.ownerDocument.createElement("div");t.className=C,t.setAttribute("role","toolbar"),t.setAttribute("aria-label","Selection");const n=e.ownerDocument.createElement("span");n.className=`${C}-count`,n.setAttribute("aria-live","polite"),t.appendChild(n);const i=e.ownerDocument.createElement("div");i.className=`${C}-actions`,t.appendChild(i),i.appendChild(this.#l(e,"select-all","Select all",()=>this.#i?.selectAll())),i.appendChild(this.#l(e,"clear","Clear",()=>this.#i?.clear()));const s=this.#l(e,"more","More…",e=>this.#i?.more(e));s.setAttribute("aria-haspopup","menu"),i.appendChild(s),i.appendChild(this.#l(e,"done","Done",()=>this.#i?.done())),e.prepend(t),this.#e=t,this.#t=n,this.#n=s}#l(e,t,n,i){const s=e.ownerDocument.createElement("button");return s.type="button",s.className=`${C}-btn`,s.dataset.action=t,s.textContent=n,s.addEventListener("click",e=>{e.preventDefault(),e.stopPropagation(),i(s)}),s}},v="tbw-range-handle",S=class{#o=null;#r=null;#i=null;#c=null;render(e,t,n,i){this.#i=i,t?(this.#o??=this.#a(e,"start"),this.#r??=this.#a(e,"end"),this.#d(e,this.#o,n(t.startRow,t.startCol),"start"),this.#d(e,this.#r,n(t.endRow,t.endCol),"end"),this.setArmed(this.#c)):this.#h()}setArmed(e){this.#c=e;for(const[t,n]of[[this.#o,"start"],[this.#r,"end"]]){if(!t)continue;const i=e===n;t.classList.toggle("armed",i),t.setAttribute("aria-pressed",String(i))}}destroy(){this.#o?.remove(),this.#r?.remove(),this.#o=null,this.#r=null,this.#i=null,this.#c=null}#h(){this.#o&&(this.#o.hidden=!0),this.#r&&(this.#r.hidden=!0)}#d(e,t,n,i){if(!t)return;if(!n)return void(t.hidden=!0);t.hidden=!1;const s=n.getBoundingClientRect?.(),l=e.getBoundingClientRect?.();if(!s||!l)return;const o="start"===i?s.left-l.left:s.right-l.left,r="start"===i?s.top-l.top:s.bottom-l.top;t.style.left=`${o}px`,t.style.top=`${r}px`}#a(e,t){const n=e.ownerDocument.createElement("div");return n.className=`${v} ${v}-${t}`,n.dataset.corner=t,n.setAttribute("role","button"),n.setAttribute("tabindex","-1"),n.setAttribute("aria-pressed","false"),n.setAttribute("aria-label","start"===t?"Selection start corner — drag, or activate to place it with a tap":"Selection end corner — drag, or activate to place it with a tap"),n.addEventListener("pointerdown",e=>this.#u(e,n,t)),e.appendChild(n),n}#u(e,t,n){if(0!==e.button&&"mouse"===e.pointerType)return;e.preventDefault(),e.stopPropagation();const i=this.#i;if(!i)return;t.classList.add("dragging");let s=!1;(0,r.startPointerDrag)(e,t,{onMove:e=>{s=!0;const t=i.cellAt(e.clientX,e.clientY);t&&i.resize(n,t.row,t.col)},onEnd:()=>{t.classList.remove("dragging"),s?i.commit():i.arm(n)},onCancel:()=>{t.classList.remove("dragging"),i.commit()}})}};function x(e){if("string"==typeof e)return e;if(Array.isArray(e)){const t=e.find(e=>"column"!==e);if(t)return t;if(e.includes("column"))return"column"}return"cell"}var A="__tbw_checkbox",E=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"];var R=class extends l.BaseGridPlugin{static manifest={queries:[{type:"getSelection",description:"Get the current selection state"},{type:"selectRows",description:"Select specific rows by index (row mode only)"},{type:"getSelectedRowIndices",description:"Get sorted array of selected row indices"},{type:"getSelectedRows",description:"Get actual row objects for the current selection (works in all modes)"},{type:"getSelectedColumns",description:"Get field names of selected columns (column mode only)"},{type:"selectColumns",description:"Select specific columns by field name (column mode only)"},{type:"getContextMenuItems",description:"Contribute the non-dragging range-extension action (WCAG 2.2 SC 2.5.7)"}],configRules:[{id:"selection/range-dblclick",severity:"warn",message:'"triggerOn: \'dblclick\'" has no effect when mode is "range".\n → Range selection uses drag interaction (mousedown → mousemove), not click events.\n → The "triggerOn" option only affects "cell" and "row" selection modes.',check:e=>"range"===x(e.mode)&&"dblclick"===e.triggerOn},{id:"selection/column-checkbox",severity:"warn",message:'"checkbox: true" only renders in row mode.\n → Column selection has no checkbox UI; activate columns via Ctrl+Click on a header or Ctrl+Space on a focused cell.',check:e=>!!e.checkbox&&"row"!==x(e.mode)}]};name="selection";styles='@layer tbw-plugins{tbw-grid.selecting .data-grid-row>.cell{-webkit-user-select:none;user-select:none}tbw-grid:has(.selection){-webkit-user-select:none;user-select:none}:is(tbw-grid:has(.selection)[data-selection-mode=row] .cell-focus,tbw-grid:has(.selection)[data-selection-mode=row] .row-focus,tbw-grid:has(.selection)[data-selection-mode=range] .cell-focus){outline:none}tbw-grid .data-grid-row.row-focus{background-color:var(--tbw-focus-background,rgba(from var(--tbw-color-accent) r g b / 12%));outline:none;position:relative}tbw-grid .data-grid-row.row-focus:after{content:"";pointer-events:none;border-width:0;border-style:var(--tbw-selection-border-style,var(--tbw-border-style));border-color:var(--tbw-range-border-color,var(--tbw-color-accent));border-top-width:var(--tbw-selection-border-width,var(--tbw-border-width));border-bottom-width:var(--tbw-selection-border-width,var(--tbw-border-width));z-index:26;position:absolute;inset:0}tbw-grid .data-grid-row.row-focus+.data-grid-row.row-focus:after{border-top-width:0}tbw-grid .data-grid-row.row-focus:has(+.data-grid-row.row-focus):after{border-bottom-width:0}tbw-grid .data-grid-row.row-focus>.cell.sticky-left,tbw-grid .data-grid-row.row-focus>.cell.sticky-right{background:linear-gradient(var(--tbw-focus-background,rgba(from var(--tbw-color-accent) r g b / 12%)) 0 0), var(--tbw-color-panel-bg)}tbw-grid .data-grid-row.row-focus[data-selectable=false]>.cell.sticky-left,tbw-grid .data-grid-row.row-focus[data-selectable=false]>.cell.sticky-right{background:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell.selected{background-color:var(--tbw-range-selection-bg);position:relative}tbw-grid .data-grid-row>.cell.selected:after{content:"";pointer-events:none;border:0 var(--tbw-selection-border-style,var(--tbw-border-style)) var(--tbw-range-border-color);z-index:1;position:absolute;inset:0}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:var(--tbw-selection-border-width,var(--tbw-border-width))}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:var(--tbw-selection-disabled-opacity,.65)}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:var(--tbw-selection-disabled-opacity,.65)}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg,rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm,.8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}tbw-grid .data-grid-row>.cell[data-field=__tbw_checkbox],tbw-grid .header-row>.cell[data-field=__tbw_checkbox]{text-align:center;cursor:pointer;justify-content:center;align-items:center;padding:0;display:flex}tbw-grid .tbw-select-row-checkbox{pointer-events:none;cursor:pointer;margin:0}tbw-grid .tbw-checkbox-header{cursor:pointer;justify-content:center;align-items:center;height:100%;display:flex}tbw-grid .tbw-select-all-checkbox{cursor:pointer;margin:0}@media (pointer:coarse){tbw-grid .tbw-select-row-checkbox,tbw-grid .tbw-select-all-checkbox{min-width:var(--tbw-touch-target-min,24px);min-height:var(--tbw-touch-target-min,24px)}}tbw-grid .header-row>.cell.column-selected,tbw-grid .data-grid-row>.cell.column-selected,tbw-grid .data-grid-row>.cell.column-selected.sticky-left,tbw-grid .data-grid-row>.cell.column-selected.sticky-right,tbw-grid .header-row>.cell.column-selected.sticky-left,tbw-grid .header-row>.cell.column-selected.sticky-right{background:linear-gradient(var(--tbw-column-selection-bg,rgba(from var(--tbw-color-accent) r g b / 12%)) 0 0), var(--tbw-color-panel-bg)}tbw-grid .tbw-selection-toolbar{z-index:30;background:var(--tbw-color-panel-bg);border-bottom:1px solid var(--tbw-color-border);justify-content:space-between;align-items:center;gap:8px;padding:4px 8px;font-size:13px;display:flex;position:sticky;top:0}tbw-grid .tbw-selection-toolbar-count{white-space:nowrap;font-weight:600}tbw-grid .tbw-selection-toolbar-actions{align-items:center;gap:4px;display:flex}tbw-grid .tbw-selection-toolbar-btn{min-height:var(--tbw-touch-target-min,24px);border-radius:min(var(--tbw-border-radius,4px), 6px);color:inherit;font:inherit;cursor:pointer;background:0 0;border:0;padding:0 10px}tbw-grid .tbw-selection-toolbar-btn:hover,tbw-grid .tbw-selection-toolbar-btn:focus-visible{background:var(--tbw-color-row-hover,light-dark(#f0f0f0,#2a2a2a))}tbw-grid .tbw-selection-toolbar-btn[data-action=done]{color:var(--tbw-color-accent-text,var(--tbw-color-accent));font-weight:600}tbw-grid .tbw-range-handle{z-index:25;width:var(--tbw-touch-target-min,24px);height:var(--tbw-touch-target-min,24px);background:radial-gradient(circle, var(--tbw-color-accent) 0 5px, transparent 5px);cursor:crosshair;touch-action:none;position:absolute;translate:-50% -50%}tbw-grid .tbw-range-handle[hidden]{display:none}tbw-grid .tbw-range-handle.dragging{scale:1.3}tbw-grid .tbw-range-handle.armed{outline:2px solid var(--tbw-color-accent);outline-offset:-4px;border-radius:50%;scale:1.3}}';get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0,multiSelect:!0,touchMode:"transient"}}get touchSelectionActive(){return this.#g}#g=!1;#m=!1;#w=new y;#b=new S;#f=null;#p=null;#C=null;selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;pendingRowKeyUpdate=null;selectedCell=null;selectedColumns=new Set;columnAnchor=null;columnHead=null;activeAxis="none";#y={primary:"cell",columnEnabled:!1,bothAxes:!1};lastSyncedFocusRow=-1;lastSyncedFocusCol=-1;announceTimer=null;explicitSelection=!1;isSelectionEnabled(){return!1!==this.config.enabled&&!1!==this.grid.effectiveConfig?.selectable}checkSelectable(e,t){const{isSelectable:n}=this.config;if(!n)return!0;const i=this.rows[e];return!!i&&n(i,e,void 0!==t?this.visibleColumns[t]:void 0,t)}isRowSelectable(e){return this.checkSelectable(e)}isCellSelectable(e,t){return this.checkSelectable(e,t)}attach(e){super.attach(e),this.#y=d(this.config.mode),this.on("filter-change",()=>this.clearSelectionSilent()),this.on("group-toggle",()=>this.clearSelectionSilent()),this.on("tree-expand",()=>this.clearSelectionSilent()),this.on("sort-change",()=>this.clearSelectionSilent()),this.on("edit-open",({rowIndex:e,row:t})=>{this.isSelectionEnabled()&&(null==t||e<0||"row"===this.#y.primary&&this.isRowSelectable(e)&&(this.selected.has(e)||(!1===this.config.multiSelect&&this.selected.clear(),this.selected.add(e),this.lastSelected=e,this.anchor=e,this.explicitSelection=!0,this.emit("selection-change",this.#v()),this.requestAfterRender())))});let t=-1;e.addEventListener("data-change",e=>{const{sourceRowCount:n}=e.detail,i=this.selected.size>0||this.ranges.length>0||null!==this.selectedCell;-1!==t&&n!==t&&i&&this.clearSelectionSilent(),t=n},{signal:this.disconnectSignal}),e.addEventListener("contextmenu",e=>{if(this.#S())return;const t=e.target?.closest?.(".cell[data-row][data-col]");if(!t)return;const n=parseInt(t.getAttribute("data-row")??"-1",10),i=parseInt(t.getAttribute("data-col")??"-1",10),s=this.#x(n,i);s&&(e.preventDefault(),this.#A(t,s))},{signal:this.disconnectSignal})}handleQuery(e){if("getSelection"===e.type)return this.getSelection();if("getSelectedRowIndices"===e.type)return this.getSelectedRowIndices();if("getSelectedRows"===e.type)return this.getSelectedRows();if("selectRows"===e.type)return this.selectRows(e.context),!0;if("getSelectedColumns"===e.type)return this.getSelectedColumns();if("selectColumns"===e.type){const t=e.context;return this.#E(t),!0}if("getContextMenuItems"===e.type){const t=e.context;if(!t||t.isHeader)return;const n=this.#x(t.rowIndex,t.columnIndex);if(!n)return;return n.map((e,t)=>({id:`selection-extend-${t}`,label:e.label,disabled:e.disabled,action:()=>e.run(),order:60+t}))}}detach(){(this.gridElement?.querySelector(".rows-body"))?.removeAttribute("aria-multiselectable"),this.#w.destroy(),this.#b.destroy(),this.#C?.dispose(),this.#C=null,this.#f=null,this.#p=null,this.#g=!1,this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.activeAxis="none",this.pendingKeyboardUpdate=null,this.pendingRowKeyUpdate=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,this.announceTimer&&(clearTimeout(this.announceTimer),this.announceTimer=null)}clearSelectionSilent(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedCell=null,this.lastSelected=null,this.anchor=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,"column"!==this.activeAxis&&(this.activeAxis="none"),this.requestAfterRender()}#R(e){const t=e.pointerType;return"touch"===t||"pen"===t||"mouse"!==t&&"coarse"===(0,i.getPrimaryPointer)()}#k(e){return!!this.isRowSelectable(e)&&(this.#g=!0,this.selected.clear(),this.selected.add(e),this.anchor=e,this.lastSelected=e,this.explicitSelection=!0,this.emit("selection-change",this.#v()),this.requestAfterRender(),!0)}exitTouchSelection(){this.#g&&(this.#g=!1,"transient"===(this.config.touchMode??"transient")&&(this.selected.clear(),this.anchor=null,this.lastSelected=null,this.emit("selection-change",this.#v())),this.requestAfterRender())}#M(e){const t=this.anchor??e,n=Math.min(t,e),i=Math.max(t,e);for(let s=n;s<=i;s++)this.isRowSelectable(s)&&this.selected.add(s);this.lastSelected=e,this.emit("selection-change",this.#v()),this.requestAfterRender()}#S(){const e=this.grid?.getPluginByName?.("contextMenu")??this.grid?.getPluginByName?.("context-menu");return"function"==typeof e?.showMenu?e:void 0}#I(e,t){return this.gridElement?.querySelector(`.cell[data-row="${e}"][data-col="${t}"]`)??null}#q(e,t){const n=this.gridElement,i=((n?.ownerDocument)?.elementFromPoint?.(e,t))?.closest?.(".cell[data-row][data-col]");if(!i||!n?.contains?.(i))return null;const s=parseInt(i.getAttribute("data-row")??"-1",10),l=parseInt(i.getAttribute("data-col")??"-1",10);return s>=0&&l>=0?{row:s,col:l}:null}#P(e,t,n){const i=this.ranges[this.ranges.length-1];if(!i)return;const s=g(i),l="start"===e?{row:s.endRow,col:s.endCol}:{row:s.startRow,col:s.startCol},o=f(l,{row:t,col:n});p(i,o)||(this.ranges[this.ranges.length-1]=o,this.activeRange=o,this.cellAnchor=l,this.emit("selection-change",this.#v()),this.requestAfterRender())}#x(e,t){if("range"!==this.#y.primary)return null;if(!this.isSelectionEnabled())return null;if(e<0||t<0)return null;if(!this.isCellSelectable(e,t))return null;const n=this.#p;return n?n.row===e&&n.col===t?null:[{label:"Extend selection to here",run:()=>this.#T(e,t)}]:null}#T(e,t){const n=this.#p;if(!n)return;const i=f(n,{row:e,col:t});this.ranges=[i],this.activeRange=i,this.cellAnchor=n,this.emit("selection-change",this.#v()),this.requestAfterRender()}#A(e,t){this.#C??=a("tbw-range-extend-menu","tbw-range-extend-menu"),this.#C.open(e,"Selection",t)}#K(e){this.#f=this.#f===e?null:e,this.#b.setArmed(this.#f),this.gridElement&&(0,n.announce)(this.gridElement,this.#f?"Select a cell to place the selection corner":"Selection corner placement cancelled")}#L(){return!!this.#f&&(this.#f=null,this.#b.setArmed(null),!0)}#D(e){if(!e)return;this.#g?this.#w.show(e,this.selected.size,void 0!==this.#S(),{selectAll:()=>this.selectAll(),clear:()=>this.clearSelection(),done:()=>this.exitTouchSelection(),more:e=>this.#z(e)}):this.#w.destroy();const t=this.#m&&"range"===this.#y.primary&&this.ranges.length>0?this.ranges[this.ranges.length-1]:null,n=t?g(t):null;n||this.#L(),this.#b.render(e,n,(e,t)=>this.#I(e,t),{cellAt:(e,t)=>this.#q(e,t),resize:(e,t,n)=>this.#P(e,t,n),commit:()=>this.requestAfterRender(),arm:e=>this.#K(e)})}#z(e){const t=this.#S();if(!t)return;const n=e.getBoundingClientRect?.(),i=this.lastSelected??-1;t.showMenu(n?.left??0,n?.bottom??0,{row:i>=0?this.rows[i]:null,rowIndex:i,selectedRows:this.getSelectedRowIndices()})}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{triggerOn:t="click"}=this.config;if(e.originalEvent.type!==t)return!1;switch(this.#y.primary){case"cell":return this.#F(e);case"row":return this.#H(e);case"range":return this.#U(e);default:return!1}}#F(e){const{rowIndex:t,colIndex:n}=e;if(e.column&&(0,o.isUtilityColumn)(e.column))return!1;if(!this.isCellSelectable(t,n))return!1;const i=this.selectedCell;return i&&i.row===t&&i.col===n||(this.selectedCell={row:t,col:n},this.emit("selection-change",this.#v()),this.requestAfterRender()),!1}#H(e){const{rowIndex:t,originalEvent:n,column:i}=e;if(!this.isRowSelectable(t))return!1;const s=!1!==this.config.multiSelect,l=n.shiftKey&&s,o=(n.ctrlKey||n.metaKey||this.#g)&&s,r=!0===i?.checkboxColumn;if(l&&null!==this.anchor){const e=Math.min(this.anchor,t),n=Math.max(this.anchor,t);o||this.selected.clear();for(let t=e;t<=n;t++)this.isRowSelectable(t)&&this.selected.add(t)}else if(o||r&&s)this.selected.has(t)?this.selected.delete(t):this.selected.add(t),this.anchor=t;else{if(1===this.selected.size&&this.selected.has(t))return!1;this.selected.clear(),this.selected.add(t),this.anchor=t}return this.lastSelected=t,this.explicitSelection=!0,this.emit("selection-change",this.#v()),this.requestAfterRender(),!1}#U(e){const{rowIndex:t,colIndex:n,originalEvent:i}=e;if(e.column&&(0,o.isUtilityColumn)(e.column))return!1;if(!this.isCellSelectable(t,n))return!1;if(this.#f){const e=this.#f;return this.#L(),this.#P(e,t,n),!0}this.#m=this.#R(i);const s=i.shiftKey,l=(i.ctrlKey||i.metaKey)&&!1!==this.config.multiSelect;if(s&&this.cellAnchor){const e=f(this.cellAnchor,{row:t,col:n}),i=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(i&&p(i,e))return!1;l?this.ranges.length>0?this.ranges[this.ranges.length-1]=e:this.ranges.push(e):this.ranges=[e],this.activeRange=e}else{const e={startRow:t,startCol:n,endRow:t,endCol:n};if(l)this.ranges.push(e);else{if(1===this.ranges.length&&p(this.ranges[0],e))return!1;this.ranges=[e]}this.activeRange=e,this.cellAnchor={row:t,col:n},this.#p={row:t,col:n}}return this.emit("selection-change",this.#v()),this.requestAfterRender(),!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;if(this.#y.columnEnabled&&this.#$(e))return!0;const t=this.#y.primary;if("Escape"===e.key&&this.#L())return!0;if("Escape"===e.key&&this.#g)return this.exitTouchSelection(),!0;if("Escape"===e.key)return this.#_(t);const n=E.includes(e.key);switch(t){case"cell":return!!n&&this.#G();case"row":return this.#N(e);case"range":return this.#j(e,n);default:return!1}}#$(e){if(!e.ctrlKey&&!e.metaKey)return!1;if(" "===e.key||"Spacebar"===e.key){const t=this.visibleColumns[this.grid._focusCol];return!(!t||(0,o.isUtilityColumn)(t)||"string"!=typeof t.field)&&(e.preventDefault(),e.stopPropagation(),this.selectColumn(t.field,{toggle:!0}),!0)}if(e.shiftKey&&"column"===this.activeAxis&&!1!==this.config.multiSelect&&("ArrowLeft"===e.key||"ArrowRight"===e.key)){const t=h(this.visibleColumns),n="ArrowLeft"===e.key?"left":"right",i=function(e,t,n){if(0===t.length)return null;if(null===e)return null;const i=u(e,t);if(i<0)return null;const s="left"===n?Math.max(0,i-1):Math.min(t.length-1,i+1);return s===i?null:t[s]}(this.columnHead,t,n);return null!==i&&null!==this.columnAnchor&&(e.preventDefault(),e.stopPropagation(),this.selectColumn(i,{range:!0}),!0)}return!1}#_(e){return!this.grid.query("isEditing").some(Boolean)&&("column"===this.activeAxis?(this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.activeAxis="none"):"cell"===e?this.selectedCell=null:"row"===e?(this.selected.clear(),this.anchor=null):"range"===e&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#v()),this.requestAfterRender(),!0)}#G(){return queueMicrotask(()=>{const e=this.grid._focusRow,t=this.grid._focusCol;this.selectedCell=this.isCellSelectable(e,t)?{row:e,col:t}:null,this.emit("selection-change",this.#v()),this.requestAfterRender()}),!1}#N(e){const t=!1!==this.config.multiSelect;if("ArrowUp"===e.key||"ArrowDown"===e.key||"PageUp"===e.key||"PageDown"===e.key||(e.ctrlKey||e.metaKey)&&("Home"===e.key||"End"===e.key)){const n=e.shiftKey&&t;return n&&null===this.anchor&&(this.anchor=this.grid._focusRow),this.explicitSelection=!0,this.pendingRowKeyUpdate={shiftKey:n},queueMicrotask(()=>this.requestAfterRender()),!1}return this.#B(e,t)}#j(e,t){if(!t)return this.#B(e,!1!==this.config.multiSelect);const n=e.shiftKey&&"Tab"!==e.key;return n&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:n},queueMicrotask(()=>this.requestAfterRender()),!1}#B(e,t){return!(!t||"a"!==e.key||!e.ctrlKey&&!e.metaKey)&&(!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0))}onCellMouseDown(e){if(!this.isSelectionEnabled())return;if("row"===this.#y.primary){if(void 0===e.rowIndex||e.rowIndex<0)return;if(!1===this.config.multiSelect)return;if(!this.#R(e.originalEvent))return;if(e.column&&(0,o.isUtilityColumn)(e.column))return;return this.#g?(this.#M(e.rowIndex),!0):this.#k(e.rowIndex)||void 0}if("range"!==this.#y.primary)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;if(this.#f)return!0;if(this.#m=this.#R(e.originalEvent),2===e.originalEvent.button&&w(e.rowIndex,e.colIndex,this.ranges))return;if(e.column&&(0,o.isUtilityColumn)(e.column))return;if(!this.isCellSelectable(e.rowIndex,e.colIndex))return;if(e.originalEvent.shiftKey&&this.cellAnchor)return;this.isDragging=!0;const t=e.rowIndex,n=e.colIndex,i=(e.originalEvent.ctrlKey||e.originalEvent.metaKey)&&!1!==this.config.multiSelect,s={startRow:t,startCol:n,endRow:t,endCol:n};return!i&&1===this.ranges.length&&p(this.ranges[0],s)?(this.cellAnchor={row:t,col:n},this.#O(e,t,n),!0):(this.cellAnchor={row:t,col:n},this.#O(e,t,n),i||(this.ranges=[]),this.ranges.push(s),this.activeRange=s,this.emit("selection-change",this.#v()),this.requestAfterRender(),!0)}#O(e,t,n){2!==e.originalEvent.button&&(this.#p={row:t,col:n})}onCellMouseMove(e){if(!this.isSelectionEnabled())return;if("range"!==this.#y.primary)return;if(!this.isDragging||!this.cellAnchor)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;let t=e.colIndex;const n=this.visibleColumns[t];if(n&&(0,o.isUtilityColumn)(n)){const e=this.visibleColumns.findIndex(e=>!(0,o.isUtilityColumn)(e));e>=0&&(t=e)}const i=f(this.cellAnchor,{row:e.rowIndex,col:t}),s=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return s&&p(s,i)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i),this.activeRange=i,this.emit("selection-change",this.#v()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&"range"===this.#y.primary)return this.isDragging?(this.isDragging=!1,!0):void 0}onHeaderClick(e){if(!this.isSelectionEnabled())return!1;if(!this.#y.columnEnabled)return!1;if(e.column&&(0,o.isUtilityColumn)(e.column))return!1;const t=e.originalEvent;if(!t.ctrlKey&&!t.metaKey)return!1;const n=e.field;if(!n)return!1;t.preventDefault(),"function"==typeof t.stopPropagation&&t.stopPropagation();const i=!0===t.shiftKey&&!1!==this.config.multiSelect&&null!==this.columnAnchor;return this.selectColumn(n,i?{range:!0}:{toggle:!0}),!0}processColumns(e){if(this.config.checkbox&&"row"===this.#y.primary){if(e.some(e=>e.field===A))return e;const t=this.#W(),n=e.findIndex(o.isExpanderColumn),i=n>=0?n+1:0;return[...e.slice(0,i),t,...e.slice(i)]}return e}#W(){return{field:A,header:"",width:32,resizable:!1,sortable:!1,lockPosition:!0,utility:!0,checkboxColumn:!0,headerRenderer:()=>{const e=document.createElement("label");if(e.className="tbw-checkbox-header",!1===this.config.multiSelect)return e;const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-all-checkbox";const i=document.createElement("span");return i.className="tbw-sr-only",i.textContent=(0,n.getA11yMessage)(this.gridElement,"selectAllRows"),t.addEventListener("click",e=>{e.stopPropagation(),e.target.checked?this.selectAll():this.clearSelection()}),e.addEventListener("click",e=>e.stopPropagation()),e.append(t,i),e},renderer:e=>{const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-row-checkbox";const i=e.cellEl,s=i?parseInt(i.getAttribute("data-row")??"-1",10):-1;return s>=0&&(t.checked=this.selected.has(s)),t.setAttribute("aria-label",(0,n.getA11yMessage)(this.gridElement,"selectRow",Math.max(s,0))),t}}}#Q(e){e.querySelectorAll(".tbw-select-row-checkbox").forEach(e=>{const t=e.closest(".cell"),n=t?(0,s.getRowIndexFromCell)(t):-1;n>=0&&(e.checked=this.selected.has(n))});const t=e.querySelector(".tbw-select-all-checkbox");if(t){const e=this.rows.length;let n=0;if(this.config.isSelectable)for(let t=0;t<e;t++)this.isRowSelectable(t)&&n++;else n=e;const i=n>0&&this.selected.size>=n,s=this.selected.size>0;t.checked=i,t.indeterminate=s&&!i}}#X(){return this.gridElement?.hasAttribute("data-has-focus")??!1}#Y(e){const t=this.grid._focusRow,n=this.grid._focusCol;if(!this.#X())return this.lastSyncedFocusRow=t,void(this.lastSyncedFocusCol=n);if("row"===e){if(this.explicitSelection)return this.explicitSelection=!1,void(this.lastSyncedFocusRow=t);t!==this.lastSyncedFocusRow&&(this.lastSyncedFocusRow=t,this.isRowSelectable(t)&&(this.selected.has(t)&&1===this.selected.size||(this.selected.clear(),this.selected.add(t),this.lastSelected=t,this.anchor=t,this.emit("selection-change",this.#v()))))}if("cell"===e){if(this.explicitSelection)return this.explicitSelection=!1,this.lastSyncedFocusRow=t,void(this.lastSyncedFocusCol=n);if((t!==this.lastSyncedFocusRow||n!==this.lastSyncedFocusCol)&&(this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=n,this.isCellSelectable(t,n))){const e=this.selectedCell;e&&e.row===t&&e.col===n||(this.selectedCell={row:t,col:n},this.emit("selection-change",this.#v()))}}}#J(){const e=this.gridElement;if(!e)return;const n=this.#y.primary,i=this.#y.columnEnabled,l=!!this.config.isSelectable,r=e.querySelector(".rows-body");if(r){const e=!1!==this.config.multiSelect;r.setAttribute("aria-multiselectable",e?"true":"false")}e.querySelectorAll(".cell").forEach(e=>{const n=e.classList.contains(t.GridClasses.SELECTED)||e.classList.contains("column-selected");e.classList.remove(t.GridClasses.SELECTED,"top","bottom","first","last","column-selected"),n&&e.removeAttribute("aria-selected"),l&&e.removeAttribute("data-selectable")});const c=e.querySelectorAll(".data-grid-row");if(c.forEach(e=>{e.classList.remove(t.GridClasses.SELECTED,"row-focus"),e.setAttribute("aria-selected","false"),l&&e.removeAttribute("data-selectable")}),i&&e.querySelectorAll(".header-row > .cell").forEach(e=>{e.classList.remove("column-selected"),e.removeAttribute("aria-selected")}),"row"===n&&((0,s.clearCellFocus)(e),c.forEach(e=>{const n=(0,s.getRowIndexFromCell)(e.querySelector(".cell[data-row]"));n>=0&&(l&&!this.isRowSelectable(n)&&e.setAttribute("data-selectable","false"),this.selected.has(n)&&(e.classList.add(t.GridClasses.SELECTED,"row-focus"),e.setAttribute("aria-selected","true")))}),this.config.checkbox&&this.#Q(e)),"cell"!==n&&"range"!==n||!l||e.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const t=parseInt(e.getAttribute("data-row")??"-1",10),n=parseInt(e.getAttribute("data-col")??"-1",10);t>=0&&n>=0&&(this.isCellSelectable(t,n)||e.setAttribute("data-selectable","false"))}),"range"===n&&this.ranges.length>0){(0,s.clearCellFocus)(e);const n=this.ranges.map(g),i=(e,t)=>{for(const i of n)if(e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol)return!0;return!1};e.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const n=parseInt(e.getAttribute("data-row")??"-1",10),s=parseInt(e.getAttribute("data-col")??"-1",10);if(n>=0&&s>=0){const l=this.visibleColumns[s];if(l&&(0,o.isUtilityColumn)(l))return;i(n,s)&&(e.classList.add(t.GridClasses.SELECTED),e.setAttribute("aria-selected","true"),i(n-1,s)||e.classList.add("top"),i(n+1,s)||e.classList.add("bottom"),i(n,s-1)||e.classList.add("first"),i(n,s+1)||e.classList.add("last"))}})}if(i&&this.selectedColumns.size>0){const t=this.visibleColumns.map(e=>"string"==typeof e.field?e.field:void 0);e.querySelectorAll(".header-row > .cell[data-col]").forEach(e=>{const n=parseInt(e.getAttribute("data-col")??"-1",10),i=n>=0?t[n]:void 0;i&&this.selectedColumns.has(i)&&(e.classList.add("column-selected"),e.setAttribute("aria-selected","true"))}),e.querySelectorAll(".cell[data-col]:not(.header-row .cell)").forEach(e=>{if(e.closest(".header-row"))return;const n=parseInt(e.getAttribute("data-col")??"-1",10);if(n<0)return;const i=this.visibleColumns[n];if(!i||(0,o.isUtilityColumn)(i))return;const s=t[n];s&&this.selectedColumns.has(s)&&(e.classList.add("column-selected"),e.setAttribute("aria-selected","true"))})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.querySelector(".tbw-grid-root"),n=this.#y.primary;if(this.pendingRowKeyUpdate&&"row"===n){const{shiftKey:e}=this.pendingRowKeyUpdate;this.pendingRowKeyUpdate=null;const t=this.grid._focusRow;if(e&&null!==this.anchor){this.selected.clear();const e=Math.min(this.anchor,t),n=Math.max(this.anchor,t);for(let t=e;t<=n;t++)this.isRowSelectable(t)&&this.selected.add(t)}else this.isRowSelectable(t)?(this.selected.clear(),this.selected.add(t),this.anchor=t):this.selected.clear();this.lastSelected=t,this.emit("selection-change",this.#v())}if(this.pendingKeyboardUpdate&&"range"===n){const{shiftKey:e}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const t=this.grid._focusRow,n=this.grid._focusCol;if(e&&this.cellAnchor){const e=f(this.cellAnchor,{row:t,col:n});this.ranges=[e],this.activeRange=e}else e||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:t,col:n});this.emit("selection-change",this.#v())}this.#Y(n),this.gridElement.setAttribute("data-selection-mode",n),t&&t.classList.toggle("selecting",this.isDragging),this.#J(),this.#D(t)}onScrollRender(){this.isSelectionEnabled()&&this.#J()}getSelection(){const e=this.#v();return{mode:this.config.mode,activeAxis:e.activeAxis,ranges:e.ranges,selectedColumns:e.selectedColumns,anchor:this.cellAnchor}}getSelectedCells(){return function(e){const t=new Map;for(const n of e)for(const e of b(n))t.set(`${e.row},${e.col}`,e);return[...t.values()]}(this.ranges)}isCellSelected(e,t){return w(e,t,this.ranges)}selectAll(){const{mode:e,multiSelect:t}=this.config;if(!1!==t)if("row"===e){this.selected.clear();for(let e=0;e<this.rows.length;e++)this.isRowSelectable(e)&&this.selected.add(e);this.explicitSelection=!0,this.emit("selection-change",this.#v()),this.requestAfterRender()}else if("range"===e){const e=this.rows.length,t=this.columns.length;if(e>0&&t>0){const n={startRow:0,startCol:0,endRow:e-1,endCol:t-1};this.ranges=[n],this.activeRange=n,this.emit("selection-change",this.#v()),this.requestAfterRender()}}}selectRows(e){if("row"!==this.#y.primary)return;const t=!1===this.config.multiSelect&&e.length>1?[e[e.length-1]]:e;this.selected.clear();for(const n of t)n>=0&&n<this.rows.length&&this.isRowSelectable(n)&&this.selected.add(n);this.anchor=t.length>0?t[t.length-1]:null,this.explicitSelection=!0,this.emit("selection-change",this.#v()),this.requestAfterRender()}getSelectedRowIndices(){return[...this.selected].sort((e,t)=>e-t)}getSelectedRows(){const e=this.#y.primary,t=this.rows;if("row"===e)return this.getSelectedRowIndices().filter(e=>e>=0&&e<t.length).map(e=>t[e]);if("cell"===e&&this.selectedCell){const{row:e}=this.selectedCell;return e>=0&&e<t.length?[t[e]]:[]}if("range"===e&&this.ranges.length>0){const e=new Set;for(const n of this.ranges){const i=Math.max(0,Math.min(n.startRow,n.endRow)),s=Math.min(t.length-1,Math.max(n.startRow,n.endRow));for(let t=i;t<=s;t++)e.add(t)}return[...e].sort((e,t)=>e-t).map(e=>t[e])}return[]}#E(e){if(!this.#y.columnEnabled)return;const t=h(this.visibleColumns),n=new Set(t),i=[],s=new Set;for(const o of e)n.has(o)&&!s.has(o)&&(s.add(o),i.push(o));this.#V("column"),this.selectedColumns.clear();for(const o of i)this.selectedColumns.add(o);const l=i.length>0?i[i.length-1]:null;this.columnAnchor=l,this.columnHead=l,this.activeAxis=this.selectedColumns.size>0?"column":"none",this.emit("selection-change",this.#v()),this.requestAfterRender()}selectColumn(e,t={}){if(!this.#y.columnEnabled)return;const n=h(this.visibleColumns);if(n.includes(e)){if(this.#V("column"),!1===this.config.multiSelect)this.selectedColumns.clear(),this.selectedColumns.add(e),this.columnAnchor=e,this.columnHead=e;else if(t.range&&this.columnAnchor){const t=function(e,t,n){const i=u(e,n),s=u(t,n);if(-1===i||-1===s)return[];const l=Math.min(i,s),o=Math.max(i,s);return n.slice(l,o+1)}(this.columnAnchor,e,n);this.selectedColumns.clear();for(const e of t)this.selectedColumns.add(e);this.columnHead=e}else t.toggle?(this.selectedColumns.has(e)?this.selectedColumns.delete(e):this.selectedColumns.add(e),this.columnAnchor=e,this.columnHead=e):(this.selectedColumns.clear(),this.selectedColumns.add(e),this.columnAnchor=e,this.columnHead=e);this.activeAxis=this.selectedColumns.size>0?"column":"none",this.emit("selection-change",this.#v()),this.requestAfterRender()}}deselectColumn(e){this.#y.columnEnabled&&this.selectedColumns.delete(e)&&(0===this.selectedColumns.size&&(this.columnAnchor=null,this.columnHead=null,"column"===this.activeAxis&&(this.activeAxis="none")),this.emit("selection-change",this.#v()),this.requestAfterRender())}selectAllColumns(){if(!this.#y.columnEnabled)return;if(!1===this.config.multiSelect)return;const e=h(this.visibleColumns);if(0!==e.length){this.#V("column"),this.selectedColumns.clear();for(const t of e)this.selectedColumns.add(t);this.columnAnchor=e[0],this.columnHead=e[e.length-1],this.activeAxis="column",this.emit("selection-change",this.#v()),this.requestAfterRender()}}clearColumnSelection(){0!==this.selectedColumns.size&&(this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,"column"===this.activeAxis&&(this.activeAxis="none"),this.emit("selection-change",this.#v()),this.requestAfterRender())}getSelectedColumns(){return 0===this.selectedColumns.size?[]:h(this.visibleColumns).filter(e=>this.selectedColumns.has(e))}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.activeAxis="none",this.emit("selection-change",this.#v()),this.requestAfterRender()}setRanges(e){this.ranges=e.map(e=>({startRow:e.from.row,startCol:e.from.col,endRow:e.to.row,endCol:e.to.col})),this.activeRange=this.ranges.length>0?this.ranges[this.ranges.length-1]:null,this.emit("selection-change",this.#v()),this.requestAfterRender()}#v(){const e=this.#y.primary,t="row"===e&&this.selected.size>0||"cell"===e&&null!==this.selectedCell||"range"===e&&this.ranges.length>0;let i;this.#y.bothAxes&&t&&this.selectedColumns.size>0&&(this.selectedColumns.clear(),this.columnAnchor=null,this.columnHead=null,this.gridElement&&(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"selectionAxisChanged","row"))),i=t?"row"===e?"row":"cell"===e?"cell":"range":this.selectedColumns.size>0?"column":"none",this.activeAxis=i;const s=function(e,t,n,i,s,l){if("column"===t&&i.selectedColumns.size>0)return{mode:e,activeAxis:"column",ranges:[],selectedColumns:l.filter(e=>i.selectedColumns.has(e))};if("cell"===n&&i.selectedCell)return{mode:e,activeAxis:"cell",ranges:[{from:{row:i.selectedCell.row,col:i.selectedCell.col},to:{row:i.selectedCell.row,col:i.selectedCell.col}}],selectedColumns:[]};if("row"===n&&i.selected.size>0){const t=[...i.selected].sort((e,t)=>e-t),n=[];let l=t[0],o=l;for(let e=1;e<t.length;e++)t[e]===o+1?o=t[e]:(n.push({from:{row:l,col:0},to:{row:o,col:s-1}}),l=t[e],o=l);return n.push({from:{row:l,col:0},to:{row:o,col:s-1}}),{mode:e,activeAxis:"row",ranges:n,selectedColumns:[]}}return"range"===n&&i.ranges.length>0?{mode:e,activeAxis:"range",ranges:(o=i.ranges,o.map(m)),selectedColumns:[]}:{mode:e,activeAxis:"none",ranges:[],selectedColumns:[]};var o}(this.config.mode,i,e,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges,selectedColumns:this.selectedColumns},this.columns.length,h(this.visibleColumns));return this.announceTimer&&clearTimeout(this.announceTimer),this.announceTimer=setTimeout(()=>{if("column"===s.activeAxis){const e=s.selectedColumns;if(1===e.length){const t=e[0],i=this.columns.find(e=>e.field===t),s="string"==typeof i?.header&&i.header||t;(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"columnSelected",s))}else e.length>1?(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"columnSelectionChanged",e.length)):(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"columnSelectionCleared"))}else{const e="row"===s.activeAxis?this.selected.size:s.ranges.length;e>0&&(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"selectionChanged",e))}},150),s}#V(e){if(this.#y.bothAxes&&"column"===e){if(!(this.selected.size>0||null!==this.selectedCell||this.ranges.length>0))return;this.selected.clear(),this.lastSelected=null,this.anchor=null,this.selectedCell=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.gridElement&&(0,n.announce)(this.gridElement,(0,n.getA11yMessage)(this.gridElement,"selectionAxisChanged","column"))}}};e.SelectionPlugin=R});
|
|
2
2
|
//# sourceMappingURL=selection.umd.js.map
|