@toolbox-web/grid 2.7.1 → 2.7.3

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.
@@ -1,2 +1,2 @@
1
- !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("../../core/internal/aria"),require("../../core/plugin/base-plugin"),require("../../core/internal/value-accessor")):"function"==typeof define&&define.amd?define(["exports","../../core/internal/aria","../../core/plugin/base-plugin","../../core/internal/value-accessor"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).TbwGridPlugin_multiSort={},t.TbwGrid,t.TbwGrid,t.TbwGrid)}(this,function(t,e,r,i){"use strict";function o(t,e){const r=!0===t?.__loading;return r===(!0===e?.__loading)?0:r?1:-1}function s(t,e){return null==t&&null==e?0:null==t?1:null==e?-1:"number"==typeof t&&"number"==typeof e?t-e:t instanceof Date&&e instanceof Date?t.getTime()-e.getTime():"boolean"==typeof t&&"boolean"==typeof e?t===e?0:t?-1:1:String(t).localeCompare(String(e))}function n(t,e){const r=t.findIndex(t=>t.field===e);return r>=0?r+1:void 0}function l(t,e){return t.find(t=>t.field===e)?.direction}class d extends r.BaseGridPlugin{static manifest={queries:[{type:"sort:get-model",description:"Returns the current multi-sort model as SortModel[]"},{type:"sort:set-model",description:"Sets the multi-sort model from context (SortModel[])"}]};name="multiSort";styles='@layer tbw-plugins{.header-cell[data-sort=asc]:after{content:"↑";margin-left:var(--tbw-spacing-xs, .25em);opacity:.8}.header-cell[data-sort=desc]:after{content:"↓";margin-left:var(--tbw-spacing-xs, .25em);opacity:.8}.sort-indicator{margin-left:var(--tbw-spacing-xs, .25em);opacity:.8}.sort-index{font-size:var(--tbw-font-size-2xs, .7em);background:var(--tbw-multi-sort-badge-bg, var(--tbw-color-panel-bg));color:var(--tbw-multi-sort-badge-color, var(--tbw-color-fg));border-radius:50%;width:var(--tbw-multi-sort-badge-size, 1em);height:var(--tbw-multi-sort-badge-size, 1em);display:inline-flex;align-items:center;justify-content:center;margin-left:var(--tbw-spacing-xs, .125em);font-weight:600}}';get defaultConfig(){return{maxSortColumns:3,showSortIndex:!0}}sortModel=[];cachedSortResult=null;#t=!1;#e=[];get#r(){return this.grid}clearCoreSortState(){this.#r._sortState=null}#i(t){const e=this.grid?.query?.("grouping:get-grouped-fields",null);if(!Array.isArray(e)||0===e.length)return t;const r=e[0];if(!Array.isArray(r)||0===r.length)return t;const i=new Set(r),o=t.filter(t=>!i.has(t.field));return o.length===t.length?t:o}detach(){this.sortModel=[],this.cachedSortResult=null}handleQuery(t){switch(t.type){case"sort:get-model":return[...this.sortModel];case"sort:set-model":{const e=t.context;return!!Array.isArray(e)&&(this.sortModel=[...e],this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[...this.sortModel]}),this.requestRender(),!0)}default:return}}processRows(t){if(0===this.sortModel.length)return this.cachedSortResult=null,[...t];const e=this.#r;if(!e._isGridEditMode&&"number"==typeof e._activeEditRows&&-1!==e._activeEditRows&&this.cachedSortResult&&this.cachedSortResult.length===t.length)return[...this.cachedSortResult];const r=this.#i(this.sortModel),n=t;return r.length>0&&function(t,e,r){if(!e.length)return;const n=e.map(t=>{const e=r.find(e=>e.field===t.field),i=e?.sortComparator??s;return{field:t.field,asc:"asc"===t.direction,comparator:i,pinPlaceholders:!e?.sortComparator,column:e}}),l=(t,e)=>e.column?.valueAccessor?i.resolveCellValue(t,e.column):t[e.field];if(1===n.length){const e=n[0];t.sort((t,r)=>{if(e.pinPlaceholders){const e=o(t,r);if(0!==e)return e}const i=e.comparator(l(t,e),l(r,e),t,r);return e.asc?i:-i})}else t.sort((t,e)=>{if(n.some(t=>t.pinPlaceholders)){const r=o(t,e);if(0!==r)return r}for(let r=0;r<n.length;r++){const i=n[r],o=i.comparator(l(t,i),l(e,i),t,e);if(0!==o)return i.asc?o:-o}return 0})}(n,r,this.columns),this.cachedSortResult=n,n}onHeaderClick(t){const r=this.columns.find(e=>e.field===t.field);if(!r?.sortable)return!1;const i=t.originalEvent.shiftKey,o=this.config.maxSortColumns??3;if(this.sortModel=function(t,e,r,i){const o=t.find(t=>t.field===e);return r?o?"asc"===o.direction?t.map(t=>t.field===e?{...t,direction:"desc"}:t):t.filter(t=>t.field!==e):t.length<i?[...t,{field:e,direction:"asc"}]:t:"asc"===o?.direction?[{field:e,direction:"desc"}]:"desc"===o?.direction?[]:[{field:e,direction:"asc"}]}(this.sortModel,t.field,i,o),this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[...this.sortModel]}),this.requestRender(),this.grid?.requestStateChange?.(),this.sortModel.length>0){const t=this.sortModel.map(t=>{const e=this.columns.find(e=>e.field===t.field);return`${e?.header??t.field} ${"asc"===t.direction?"ascending":"descending"}`});e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortApplied",t.join(", then "),""))}else e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortCleared"));return!0}afterRender(){const t=this.gridElement;if(!t)return;const e=!1!==this.config.showSortIndex;t.querySelectorAll(".header-row .cell[data-field]").forEach(t=>{const r=t.getAttribute("data-field");if(!r)return;const i=n(this.sortModel,r),o=l(this.sortModel,r);if(t.querySelector(".sort-index")?.remove(),o){const r=this.updateSortIndicator(t,o);if(e&&this.sortModel.length>1&&void 0!==i){const e=document.createElement("span");e.className="sort-index",e.textContent=String(i),r.nextSibling?t.insertBefore(e,r.nextSibling):t.appendChild(e)}}else t.classList.contains("sortable")&&this.updateSortIndicator(t,null)})}getSortModel(){return[...this.sortModel]}setSortModel(t){if(this.sortModel=[...t],this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[...t]}),this.requestRender(),this.grid?.requestStateChange?.(),t.length>0){const r=t.map(t=>{const e=this.columns.find(e=>e.field===t.field);return`${e?.header??t.field} ${"asc"===t.direction?"ascending":"descending"}`});e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortApplied",r.join(", then "),""))}}clearSort(){this.sortModel=[],this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[]}),this.requestRender(),this.grid?.requestStateChange?.(),e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortCleared"))}getSortIndex(t){return n(this.sortModel,t)}getSortDirection(t){return l(this.sortModel,t)}getColumnState(t){const e=this.sortModel.findIndex(e=>e.field===t);if(-1===e)return;return{sort:{direction:this.sortModel[e].direction,priority:e}}}applyColumnState(t,e){if(this.#t||(this.#e=[...this.sortModel]),e.sort){const r=this.sortModel.findIndex(e=>e.field===t),i={field:t,direction:e.sort.direction};-1!==r?this.sortModel[r]=i:this.sortModel.splice(e.sort.priority,0,i)}else this.sortModel=this.sortModel.filter(e=>e.field!==t);this.clearCoreSortState(),this.#t||(this.#t=!0,queueMicrotask(()=>{this.#t=!1;const t=this.#e;this.#e=[],function(t,e){if(t.length!==e.length)return!1;for(let r=0;r<t.length;r++)if(t[r].field!==e[r].field||t[r].direction!==e[r].direction)return!1;return!0}(t,this.sortModel)||(this.broadcast("sort-change",{sortModel:[...this.sortModel]}),this.requestRender())}))}}t.MultiSortPlugin=d,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})});
1
+ !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports,require("../../core/internal/aria"),require("../../core/plugin/base-plugin"),require("../../core/internal/value-accessor")):"function"==typeof define&&define.amd?define(["exports","../../core/internal/aria","../../core/plugin/base-plugin","../../core/internal/value-accessor"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).TbwGridPlugin_multiSort={},t.TbwGrid,t.TbwGrid,t.TbwGrid)}(this,function(t,e,r,i){"use strict";const o=new Intl.Collator(void 0,{sensitivity:"variant"});function n(t,e){return null==t&&null==e?0:null==t?1:null==e?-1:"number"==typeof t&&"number"==typeof e?t-e:t instanceof Date&&e instanceof Date?t.getTime()-e.getTime():"boolean"==typeof t&&"boolean"==typeof e?t===e?0:t?-1:1:o.compare("string"==typeof t?t:String(t),"string"==typeof e?e:String(e))}function s(t,e){const r=t.findIndex(t=>t.field===e);return r>=0?r+1:void 0}function l(t,e){return t.find(t=>t.field===e)?.direction}class d extends r.BaseGridPlugin{static manifest={queries:[{type:"sort:get-model",description:"Returns the current multi-sort model as SortModel[]"},{type:"sort:set-model",description:"Sets the multi-sort model from context (SortModel[])"}]};name="multiSort";styles='@layer tbw-plugins{.header-cell[data-sort=asc]:after{content:"↑";margin-left:var(--tbw-spacing-xs, .25em);opacity:.8}.header-cell[data-sort=desc]:after{content:"↓";margin-left:var(--tbw-spacing-xs, .25em);opacity:.8}.sort-indicator{margin-left:var(--tbw-spacing-xs, .25em);opacity:.8}.sort-index{font-size:var(--tbw-font-size-2xs, .7em);background:var(--tbw-multi-sort-badge-bg, var(--tbw-color-panel-bg));color:var(--tbw-multi-sort-badge-color, var(--tbw-color-fg));border-radius:50%;width:var(--tbw-multi-sort-badge-size, 1em);height:var(--tbw-multi-sort-badge-size, 1em);display:inline-flex;align-items:center;justify-content:center;margin-left:var(--tbw-spacing-xs, .125em);font-weight:600}}';get defaultConfig(){return{maxSortColumns:3,showSortIndex:!0}}sortModel=[];cachedSortResult=null;#t=!1;#e=[];get#r(){return this.grid}clearCoreSortState(){this.#r._sortState=null}#i(t){const e=this.grid?.query?.("grouping:get-grouped-fields",null);if(!Array.isArray(e)||0===e.length)return t;const r=e[0];if(!Array.isArray(r)||0===r.length)return t;const i=new Set(r),o=t.filter(t=>!i.has(t.field));return o.length===t.length?t:o}detach(){this.sortModel=[],this.cachedSortResult=null}handleQuery(t){switch(t.type){case"sort:get-model":return[...this.sortModel];case"sort:set-model":{const e=t.context;return!!Array.isArray(e)&&(this.sortModel=[...e],this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[...this.sortModel]}),this.requestRender(),!0)}default:return}}processRows(t){if(0===this.sortModel.length)return this.cachedSortResult=null,[...t];const e=this.#r;if(!e._isGridEditMode&&"number"==typeof e._activeEditRows&&-1!==e._activeEditRows&&this.cachedSortResult&&this.cachedSortResult.length===t.length)return[...this.cachedSortResult];const r=this.#i(this.sortModel),o=t;return r.length>0&&function(t,e,r){if(!e.length)return;const o=e.map(t=>{const e=r.find(e=>e.field===t.field),o=e?.sortComparator??n,s=t.field,l=e?.valueAccessor?t=>i.resolveCellValue(t,e):t=>t[s];return{field:s,asc:"asc"===t.direction,comparator:o,getValue:l,pinPlaceholders:!e?.sortComparator}}),s=o.some(t=>t.pinPlaceholders)&&function(t){for(let e=0;e<t.length;e++)if(!0===t[e]?.__loading)return!0;return!1}(t),l=t.length,d=o.length,a=new Array(l*d);for(let i=0;i<l;i++){const e=t[i],r=i*d;for(let t=0;t<d;t++)a[r+t]=o[t].getValue(e)}const c=l<=4294967295?new Uint32Array(l):new Array(l);for(let i=0;i<l;i++)c[i]=i;const h=(e,r)=>{if(s){const i=function(t,e){const r=!0===t?.__loading;return r===(!0===e?.__loading)?0:r?1:-1}(t[e],t[r]);if(0!==i)return i}const i=e*d,n=r*d;for(let s=0;s<d;s++){const l=o[s],d=l.comparator(a[i+s],a[n+s],t[e],t[r]);if(0!==d)return l.asc?d:-d}return 0};Uint32Array,c.sort(h);const u=t.slice();for(let i=0;i<l;i++)t[i]=u[c[i]]}(o,r,this.columns),this.cachedSortResult=o,o}onHeaderClick(t){const r=this.columns.find(e=>e.field===t.field);if(!r?.sortable)return!1;const i=t.originalEvent.shiftKey,o=this.config.maxSortColumns??3;if(this.sortModel=function(t,e,r,i){const o=t.find(t=>t.field===e);return r?o?"asc"===o.direction?t.map(t=>t.field===e?{...t,direction:"desc"}:t):t.filter(t=>t.field!==e):t.length<i?[...t,{field:e,direction:"asc"}]:t:"asc"===o?.direction?[{field:e,direction:"desc"}]:"desc"===o?.direction?[]:[{field:e,direction:"asc"}]}(this.sortModel,t.field,i,o),this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[...this.sortModel]}),this.requestRender(),this.grid?.requestStateChange?.(),this.sortModel.length>0){const t=this.sortModel.map(t=>{const e=this.columns.find(e=>e.field===t.field);return`${e?.header??t.field} ${"asc"===t.direction?"ascending":"descending"}`});e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortApplied",t.join(", then "),""))}else e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortCleared"));return!0}afterRender(){const t=this.gridElement;if(!t)return;const e=!1!==this.config.showSortIndex;t.querySelectorAll(".header-row .cell[data-field]").forEach(t=>{const r=t.getAttribute("data-field");if(!r)return;const i=s(this.sortModel,r),o=l(this.sortModel,r);if(t.querySelector(".sort-index")?.remove(),o){const r=this.updateSortIndicator(t,o);if(e&&this.sortModel.length>1&&void 0!==i){const e=document.createElement("span");e.className="sort-index",e.textContent=String(i),r.nextSibling?t.insertBefore(e,r.nextSibling):t.appendChild(e)}}else t.classList.contains("sortable")&&this.updateSortIndicator(t,null)})}getSortModel(){return[...this.sortModel]}setSortModel(t){if(this.sortModel=[...t],this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[...t]}),this.requestRender(),this.grid?.requestStateChange?.(),t.length>0){const r=t.map(t=>{const e=this.columns.find(e=>e.field===t.field);return`${e?.header??t.field} ${"asc"===t.direction?"ascending":"descending"}`});e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortApplied",r.join(", then "),""))}}clearSort(){this.sortModel=[],this.clearCoreSortState(),this.broadcast("sort-change",{sortModel:[]}),this.requestRender(),this.grid?.requestStateChange?.(),e.announce(this.gridElement,e.getA11yMessage(this.gridElement,"sortCleared"))}getSortIndex(t){return s(this.sortModel,t)}getSortDirection(t){return l(this.sortModel,t)}getColumnState(t){const e=this.sortModel.findIndex(e=>e.field===t);if(-1===e)return;return{sort:{direction:this.sortModel[e].direction,priority:e}}}applyColumnState(t,e){if(this.#t||(this.#e=[...this.sortModel]),e.sort){const r=this.sortModel.findIndex(e=>e.field===t),i={field:t,direction:e.sort.direction};-1!==r?this.sortModel[r]=i:this.sortModel.splice(e.sort.priority,0,i)}else this.sortModel=this.sortModel.filter(e=>e.field!==t);this.clearCoreSortState(),this.#t||(this.#t=!0,queueMicrotask(()=>{this.#t=!1;const t=this.#e;this.#e=[],function(t,e){if(t.length!==e.length)return!1;for(let r=0;r<t.length;r++)if(t[r].field!==e[r].field||t[r].direction!==e[r].direction)return!1;return!0}(t,this.sortModel)||(this.broadcast("sort-change",{sortModel:[...this.sortModel]}),this.requestRender())}))}}t.MultiSortPlugin=d,Object.defineProperty(t,Symbol.toStringTag,{value:"Module"})});
2
2
  //# sourceMappingURL=multi-sort.umd.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"multi-sort.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/multi-sort/multi-sort.ts","../../../../../libs/grid/src/lib/plugins/multi-sort/MultiSortPlugin.ts"],"sourcesContent":["/**\n * Multi-Sort Core Logic\n *\n * Pure functions for multi-column sorting operations.\n */\n\nimport { resolveCellValue } from '../../core/internal/value-accessor';\nimport type { ColumnConfig } from '../../core/types';\nimport type { SortModel } from './types';\n\n/**\n * Apply multiple sort columns to a row array.\n * Sorts are applied in order - first sort has highest priority.\n *\n * @param rows - Array of row objects to sort\n * @param sorts - Ordered array of sort configurations\n * @param columns - Column configurations (for custom comparators)\n * @returns New sorted array (does not mutate original)\n */\nexport function applySorts<TRow = unknown>(rows: TRow[], sorts: SortModel[], columns: ColumnConfig<TRow>[]): TRow[] {\n if (!sorts.length) return [...rows];\n\n const copy = [...rows];\n sortRowsInPlace(copy, sorts, columns);\n return copy;\n}\n\n/**\n * Sort an array in-place using multiple sort columns.\n * Pre-resolves column comparators to avoid O(n·log·n·m) column lookups\n * inside the comparator.\n * @internal\n */\nexport function sortRowsInPlace<TRow = unknown>(rows: TRow[], sorts: SortModel[], columns: ColumnConfig<TRow>[]): void {\n if (!sorts.length) return;\n\n // Pre-resolve comparator chain — avoids columns.find() on every pair comparison\n const chain = sorts.map((sort) => {\n const col = columns.find((c) => c.field === sort.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n return {\n field: sort.field,\n asc: sort.direction === 'asc',\n comparator,\n // Auto-pin `__loading` placeholder rows (e.g. ServerSidePlugin under `sortMode: 'local'`)\n // to the end ONLY when no custom comparator is configured. Custom comparators receive\n // the row pair as 3rd/4th args and own placeholder handling themselves.\n pinPlaceholders: !col?.sortComparator,\n column: col,\n };\n });\n\n // sortComparator (when present) takes precedence over valueAccessor; both still\n // receive the accessor-resolved value when no comparator is given. Documented\n // precedence: sortComparator → valueAccessor → field.\n const getValue = (row: any, link: (typeof chain)[number]) =>\n link.column?.valueAccessor ? resolveCellValue(row, link.column) : row[link.field];\n\n if (chain.length === 1) {\n // Single-sort fast path — avoid loop overhead\n const link = chain[0];\n rows.sort((a: any, b: any) => {\n if (link.pinPlaceholders) {\n const pinned = pinLoadingRows(a, b);\n if (pinned !== 0) return pinned;\n }\n const result = link.comparator(getValue(a, link), getValue(b, link), a, b);\n return link.asc ? result : -result;\n });\n } else {\n rows.sort((a: any, b: any) => {\n // Pin placeholders ahead of the chain — independent of every column's direction.\n // We only need to check once per pair, using the most permissive flag in the chain.\n const anyPin = chain.some((l) => l.pinPlaceholders);\n if (anyPin) {\n const pinned = pinLoadingRows(a, b);\n if (pinned !== 0) return pinned;\n }\n for (let i = 0; i < chain.length; i++) {\n const link = chain[i];\n const result = link.comparator(getValue(a, link), getValue(b, link), a, b);\n if (result !== 0) return link.asc ? result : -result;\n }\n return 0;\n });\n }\n}\n\n/**\n * Pin `__loading` placeholder rows (e.g. ServerSidePlugin under `sortMode: 'local'`)\n * to the end of the sorted array regardless of sort direction.\n *\n * Returns 0 when neither row is a placeholder, +1 when `a` is, -1 when `b` is.\n *\n * @internal\n */\nfunction pinLoadingRows(a: unknown, b: unknown): number {\n const aLoading = (a as { __loading?: unknown } | null)?.__loading === true;\n const bLoading = (b as { __loading?: unknown } | null)?.__loading === true;\n if (aLoading === bLoading) return 0;\n return aLoading ? 1 : -1;\n}\n\n/**\n * Default comparator for sorting values.\n * Handles nulls, numbers, dates, and strings.\n *\n * @param a - First value\n * @param b - Second value\n * @returns Comparison result (-1, 0, 1)\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n // Handle nulls/undefined - push to end\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n\n // Type-aware comparison\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n // Boolean comparison\n if (typeof a === 'boolean' && typeof b === 'boolean') {\n return a === b ? 0 : a ? -1 : 1;\n }\n\n // String comparison (fallback)\n return String(a).localeCompare(String(b));\n}\n\n/**\n * Toggle sort state for a field.\n * With shift key: adds/toggles in multi-sort list\n * Without shift key: replaces entire sort with single column\n *\n * @param current - Current sort model\n * @param field - Field to toggle\n * @param shiftKey - Whether shift key is held (multi-sort mode)\n * @param maxColumns - Maximum columns allowed in sort\n * @returns New sort model\n */\nexport function toggleSort(current: SortModel[], field: string, shiftKey: boolean, maxColumns: number): SortModel[] {\n const existing = current.find((s) => s.field === field);\n\n if (shiftKey) {\n // Multi-sort: add/toggle in list\n if (existing) {\n if (existing.direction === 'asc') {\n // Flip to descending\n return current.map((s) => (s.field === field ? { ...s, direction: 'desc' as const } : s));\n } else {\n // Remove from sort\n return current.filter((s) => s.field !== field);\n }\n } else if (current.length < maxColumns) {\n // Add new sort column\n return [...current, { field, direction: 'asc' as const }];\n }\n // Max columns reached, return unchanged\n return current;\n } else {\n // Single sort: replace all\n if (existing?.direction === 'asc') {\n return [{ field, direction: 'desc' }];\n } else if (existing?.direction === 'desc') {\n return [];\n }\n return [{ field, direction: 'asc' }];\n }\n}\n\n/**\n * Get the sort index (1-based) for a field in the sort model.\n * Returns undefined if the field is not in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns 1-based index or undefined\n */\nexport function getSortIndex(sortModel: SortModel[], field: string): number | undefined {\n const index = sortModel.findIndex((s) => s.field === field);\n return index >= 0 ? index + 1 : undefined;\n}\n\n/**\n * Get the sort direction for a field in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\nexport function getSortDirection(sortModel: SortModel[], field: string): 'asc' | 'desc' | undefined {\n return sortModel.find((s) => s.field === field)?.direction;\n}\n","/**\n * Multi-Sort Plugin (Class-based)\n *\n * Provides multi-column sorting capabilities for tbw-grid.\n * Supports shift+click for adding secondary sort columns.\n */\n\nimport { announce, getA11yMessage } from '../../core/internal/aria';\nimport { BaseGridPlugin, HeaderClickEvent, type PluginManifest, type PluginQuery } from '../../core/plugin/base-plugin';\nimport type { ColumnState, GridHost } from '../../core/types';\nimport { getSortDirection, getSortIndex, sortRowsInPlace, toggleSort } from './multi-sort';\nimport styles from './multi-sort.css?inline';\nimport type { MultiSortConfig, SortModel } from './types';\n\n/**\n * Multi-Sort Plugin for tbw-grid\n *\n * Enables sorting by multiple columns at once—hold Shift and click additional column\n * headers to build up a sort stack. Priority badges show the sort order, so users\n * always know which column takes precedence.\n *\n * ## Installation\n *\n * ```ts\n * import { MultiSortPlugin } from '@toolbox-web/grid/plugins/multi-sort';\n * ```\n *\n * ## Keyboard Shortcuts\n *\n * | Shortcut | Action |\n * |----------|--------|\n * | `Click header` | Sort by column (clears other sorts) |\n * | `Shift + Click` | Add column to multi-sort stack |\n * | `Ctrl + Click` | Toggle sort direction |\n *\n * ## Events\n *\n * | Event | Detail | Description |\n * |-------|--------|-------------|\n * | `sort-change` | `{ sortModel: SortModel[] }` | Fired when sort changes |\n *\n * @example Basic Multi-Column Sorting\n * ```ts\n * import { queryGrid } from '@toolbox-web/grid';\n * import { MultiSortPlugin } from '@toolbox-web/grid/plugins/multi-sort';\n *\n * const grid = queryGrid('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'name', header: 'Name', sortable: true },\n * { field: 'department', header: 'Department', sortable: true },\n * { field: 'salary', header: 'Salary', type: 'number', sortable: true },\n * ],\n * plugins: [new MultiSortPlugin({ maxSortColumns: 3, showSortIndex: true })],\n * };\n *\n * grid.on('sort-change', ({ sortModel }) => {\n * console.log('Active sorts:', sortModel);\n * });\n * ```\n *\n * @example Initial Sort Configuration\n * ```ts\n * new MultiSortPlugin({\n * initialSort: [\n * { field: 'department', direction: 'asc' },\n * { field: 'salary', direction: 'desc' },\n * ],\n * })\n * ```\n *\n * @see {@link MultiSortConfig} for all configuration options\n * @see {@link SortModel} for the sort model structure\n *\n * @internal Extends BaseGridPlugin\n */\nexport class MultiSortPlugin extends BaseGridPlugin<MultiSortConfig> {\n /**\n * Plugin manifest declaring query types this plugin responds to.\n * @internal\n */\n static override readonly manifest: PluginManifest = {\n queries: [\n { type: 'sort:get-model', description: 'Returns the current multi-sort model as SortModel[]' },\n { type: 'sort:set-model', description: 'Sets the multi-sort model from context (SortModel[])' },\n ],\n };\n\n /** @internal */\n readonly name = 'multiSort';\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n protected override get defaultConfig(): Partial<MultiSortConfig> {\n return {\n maxSortColumns: 3,\n showSortIndex: true,\n };\n }\n\n // #region Internal State\n private sortModel: SortModel[] = [];\n /** Cached sort result — returned as-is while a row edit is active to prevent\n * the edited row from jumping to a new sorted position mid-edit. Row data\n * mutations are still visible because the array holds shared object refs. */\n private cachedSortResult: unknown[] | null = null;\n /** Guards deferred sort-change broadcast during applyColumnState batch. */\n #pendingStateBroadcast = false;\n /** Snapshot of sortModel taken at the start of an applyColumnState batch,\n * used by the deferred broadcast to skip when the model didn't change. */\n #preBatchSortModel: SortModel[] = [];\n\n /** Typed internal grid accessor. */\n get #internalGrid(): GridHost {\n return this.grid as unknown as GridHost;\n }\n\n /**\n * Clear the core `_sortState` so that only this plugin's `processRows`\n * sorting applies. `ConfigManager.applyState()` always sets the core sort\n * state when restoring from storage, even when a plugin handles sorting.\n * Without this, the stale core state leaks into `collectState()` and\n * `reapplyCoreSort()` after the plugin clears its own model.\n */\n private clearCoreSortState(): void {\n this.#internalGrid._sortState = null;\n }\n\n /**\n * Remove sorts on fields that are owned by the grouping plugin.\n * GroupingRowsPlugin handles group header ordering independently, so\n * multi-sort should only sort by non-grouped data columns.\n */\n #filterGroupedFields(model: SortModel[]): SortModel[] {\n const results = this.grid?.query?.('grouping:get-grouped-fields', null);\n if (!Array.isArray(results) || results.length === 0) return model;\n\n const groupedFields = results[0] as string[];\n if (!Array.isArray(groupedFields) || groupedFields.length === 0) return model;\n\n const groupedSet = new Set(groupedFields);\n const filtered = model.filter((s) => !groupedSet.has(s.field));\n return filtered.length === model.length ? model : filtered;\n }\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\n override detach(): void {\n this.sortModel = [];\n this.cachedSortResult = null;\n }\n // #endregion\n\n // #region Query System\n\n /** @internal */\n override handleQuery(query: PluginQuery): unknown {\n switch (query.type) {\n case 'sort:get-model':\n return [...this.sortModel];\n case 'sort:set-model': {\n const model = query.context;\n if (!Array.isArray(model)) return false;\n this.sortModel = [...model] as SortModel[];\n this.clearCoreSortState();\n this.broadcast('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n return true;\n }\n default:\n return undefined;\n }\n }\n\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processRows(rows: readonly unknown[]): unknown[] {\n if (this.sortModel.length === 0) {\n this.cachedSortResult = null;\n return [...rows];\n }\n\n // Freeze sort order while a row is actively being edited (row mode only).\n // Re-sorting mid-edit would move the edited row to a new index while the\n // editors remain at the old position, causing data/UI mismatch.\n // In grid mode (_isGridEditMode) sorting is safe — afterCellRender\n // re-injects editors into the re-sorted cells.\n // We return the cached previous sort result (same object references, so\n // in-place value mutations are already visible) instead of unsorted input.\n const grid = this.#internalGrid;\n if (!grid._isGridEditMode && typeof grid._activeEditRows === 'number' && grid._activeEditRows !== -1) {\n if (this.cachedSortResult && this.cachedSortResult.length === rows.length) {\n return [...this.cachedSortResult];\n }\n }\n\n // Sort in-place — the input array is already a mutable copy from plugin-manager.\n // Pre-resolved comparator chain avoids column lookup on every pair comparison.\n // Exclude fields owned by the grouping plugin — group header order is handled\n // by GroupingRowsPlugin, so multi-sort should only affect within-group data order.\n const effectiveModel = this.#filterGroupedFields(this.sortModel);\n\n const mutableRows = rows as unknown[];\n if (effectiveModel.length > 0) {\n sortRowsInPlace(mutableRows, effectiveModel, this.columns);\n }\n this.cachedSortResult = mutableRows;\n return mutableRows;\n }\n\n /** @internal */\n override onHeaderClick(event: HeaderClickEvent): boolean {\n const column = this.columns.find((c) => c.field === event.field);\n if (!column?.sortable) return false;\n\n const shiftKey = event.originalEvent.shiftKey;\n const maxColumns = this.config.maxSortColumns ?? 3;\n\n this.sortModel = toggleSort(this.sortModel, event.field, shiftKey, maxColumns);\n this.clearCoreSortState();\n\n this.broadcast('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n this.grid?.requestStateChange?.();\n\n // Announce for screen readers\n if (this.sortModel.length > 0) {\n const labels = this.sortModel.map((s) => {\n const col = this.columns.find((c) => c.field === s.field);\n return `${col?.header ?? s.field} ${s.direction === 'asc' ? 'ascending' : 'descending'}`;\n });\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortApplied', labels.join(', then '), ''));\n } else {\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortCleared'));\n }\n\n return true;\n }\n\n /** @internal */\n override afterRender(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n const showIndex = this.config.showSortIndex !== false;\n\n const headerCells = gridEl.querySelectorAll('.header-row .cell[data-field]');\n headerCells.forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n\n const sortIndex = getSortIndex(this.sortModel, field);\n const sortDir = getSortDirection(this.sortModel, field);\n\n // Remove existing sort index badge (always clean up)\n cell.querySelector('.sort-index')?.remove();\n\n if (sortDir) {\n const indicator = this.updateSortIndicator(cell, sortDir);\n\n // Add sort index badge if multiple columns sorted and showSortIndex is enabled\n if (showIndex && this.sortModel.length > 1 && sortIndex !== undefined) {\n const badge = document.createElement('span');\n badge.className = 'sort-index';\n badge.textContent = String(sortIndex);\n if (indicator.nextSibling) {\n cell.insertBefore(badge, indicator.nextSibling);\n } else {\n cell.appendChild(badge);\n }\n }\n } else if (cell.classList.contains('sortable')) {\n this.updateSortIndicator(cell, null);\n }\n });\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Get the current sort model.\n * @returns Copy of the current sort model\n */\n getSortModel(): SortModel[] {\n return [...this.sortModel];\n }\n\n /**\n * Set the sort model programmatically.\n * @param model - New sort model to apply\n */\n setSortModel(model: SortModel[]): void {\n this.sortModel = [...model];\n this.clearCoreSortState();\n this.broadcast('sort-change', { sortModel: [...model] });\n this.requestRender();\n this.grid?.requestStateChange?.();\n if (model.length > 0) {\n const labels = model.map((s) => {\n const col = this.columns.find((c) => c.field === s.field);\n return `${col?.header ?? s.field} ${s.direction === 'asc' ? 'ascending' : 'descending'}`;\n });\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortApplied', labels.join(', then '), ''));\n }\n }\n\n /**\n * Clear all sorting.\n */\n clearSort(): void {\n this.sortModel = [];\n this.clearCoreSortState();\n this.broadcast('sort-change', { sortModel: [] });\n this.requestRender();\n this.grid?.requestStateChange?.();\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortCleared'));\n }\n\n /**\n * Get the sort index (1-based) for a specific field.\n * @param field - Field to check\n * @returns 1-based index or undefined if not sorted\n */\n getSortIndex(field: string): number | undefined {\n return getSortIndex(this.sortModel, field);\n }\n\n /**\n * Get the sort direction for a specific field.\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\n getSortDirection(field: string): 'asc' | 'desc' | undefined {\n return getSortDirection(this.sortModel, field);\n }\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Return sort state for a column if it's in the sort model.\n * @internal\n */\n override getColumnState(field: string): Partial<ColumnState> | undefined {\n const index = this.sortModel.findIndex((s) => s.field === field);\n if (index === -1) return undefined;\n\n const sortEntry = this.sortModel[index];\n return {\n sort: {\n direction: sortEntry.direction,\n priority: index,\n },\n };\n }\n\n /**\n * Apply sort state from column state.\n * Rebuilds the sort model from all column states.\n * @internal\n */\n override applyColumnState(field: string, state: ColumnState): void {\n // Snapshot model before mutation so the microtask can skip a no-op\n // broadcast/render when nothing actually changed.\n if (!this.#pendingStateBroadcast) {\n this.#preBatchSortModel = [...this.sortModel];\n }\n\n if (!state.sort) {\n // Remove this field from sortModel if it exists\n this.sortModel = this.sortModel.filter((s) => s.field !== field);\n } else {\n // Find existing entry or add new one\n const existingIndex = this.sortModel.findIndex((s) => s.field === field);\n const newEntry: SortModel = {\n field,\n direction: state.sort.direction,\n };\n\n if (existingIndex !== -1) {\n // Update existing entry\n this.sortModel[existingIndex] = newEntry;\n } else {\n // Add at the correct priority position\n this.sortModel.splice(state.sort.priority, 0, newEntry);\n }\n }\n\n // Clear core sort state — this plugin exclusively handles sorting via\n // processRows. The core _sortState is set by ConfigManager.applyState()\n // before plugins run; null it so reapplyCoreSort() is a no-op.\n this.clearCoreSortState();\n\n // Broadcast sort-change + force a row re-render after state restoration.\n // Both are deferred to a microtask so per-column calls coalesce into one\n // event and one render. The render handles the \"sort cleared\" case that\n // core's width-only fast path cannot detect (core only inspects the\n // *incoming* state, not what the plugin's model became). Skip entirely\n // when the model is unchanged so unrelated state restores stay cheap.\n if (!this.#pendingStateBroadcast) {\n this.#pendingStateBroadcast = true;\n queueMicrotask(() => {\n this.#pendingStateBroadcast = false;\n const prev = this.#preBatchSortModel;\n this.#preBatchSortModel = [];\n if (sortModelEquals(prev, this.sortModel)) return;\n this.broadcast('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n });\n }\n }\n // #endregion\n}\n\n/** Shallow equality for two sort models (order-sensitive). */\nfunction sortModelEquals(a: SortModel[], b: SortModel[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].field !== b[i].field || a[i].direction !== b[i].direction) return false;\n }\n return true;\n}\n"],"names":["pinLoadingRows","a","b","aLoading","__loading","defaultComparator","Date","getTime","String","localeCompare","getSortIndex","sortModel","field","index","findIndex","s","getSortDirection","find","direction","MultiSortPlugin","BaseGridPlugin","static","queries","type","description","name","styles","defaultConfig","maxSortColumns","showSortIndex","cachedSortResult","pendingStateBroadcast","preBatchSortModel","internalGrid","this","grid","clearCoreSortState","_sortState","filterGroupedFields","model","results","query","Array","isArray","length","groupedFields","groupedSet","Set","filtered","filter","has","detach","handleQuery","context","broadcast","requestRender","processRows","rows","_isGridEditMode","_activeEditRows","effectiveModel","mutableRows","sorts","columns","chain","map","sort","col","c","comparator","sortComparator","asc","pinPlaceholders","column","getValue","row","link","valueAccessor","resolveCellValue","pinned","result","some","l","i","sortRowsInPlace","onHeaderClick","event","sortable","shiftKey","originalEvent","maxColumns","config","current","existing","toggleSort","requestStateChange","labels","header","announce","gridElement","getA11yMessage","join","afterRender","gridEl","showIndex","querySelectorAll","forEach","cell","getAttribute","sortIndex","sortDir","querySelector","remove","indicator","updateSortIndicator","badge","document","createElement","className","textContent","nextSibling","insertBefore","appendChild","classList","contains","getSortModel","setSortModel","clearSort","getColumnState","priority","applyColumnState","state","existingIndex","newEntry","splice","queueMicrotask","prev","sortModelEquals"],"mappings":"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"}
1
+ {"version":3,"file":"multi-sort.umd.js","sources":["../../../../../libs/grid/src/lib/plugins/multi-sort/multi-sort.ts","../../../../../libs/grid/src/lib/plugins/multi-sort/MultiSortPlugin.ts"],"sourcesContent":["/**\n * Multi-Sort Core Logic\n *\n * Pure functions for multi-column sorting operations.\n */\n\nimport { resolveCellValue } from '../../core/internal/value-accessor';\nimport type { ColumnConfig } from '../../core/types';\nimport type { SortModel } from './types';\n\n// Module-level cached collator. `String.prototype.localeCompare(b)` (no args)\n// lazily allocates a fresh Intl.Collator on every call in V8 — measurable on\n// large datasets where string comparisons dominate the secondary sort key.\n// A single cached `Intl.Collator` reused via `.compare(a, b)` is ~3-5x faster.\nconst stringCollator = new Intl.Collator(undefined, { sensitivity: 'variant' });\n\n/**\n * Apply multiple sort columns to a row array.\n * Sorts are applied in order - first sort has highest priority.\n *\n * @param rows - Array of row objects to sort\n * @param sorts - Ordered array of sort configurations\n * @param columns - Column configurations (for custom comparators)\n * @returns New sorted array (does not mutate original)\n */\nexport function applySorts<TRow = unknown>(rows: TRow[], sorts: SortModel[], columns: ColumnConfig<TRow>[]): TRow[] {\n if (!sorts.length) return [...rows];\n\n const copy = [...rows];\n sortRowsInPlace(copy, sorts, columns);\n return copy;\n}\n\n/**\n * Sort an array in-place using multiple sort columns.\n * Pre-resolves column comparators to avoid O(n·log·n·m) column lookups\n * inside the comparator.\n * @internal\n */\nexport function sortRowsInPlace<TRow = unknown>(rows: TRow[], sorts: SortModel[], columns: ColumnConfig<TRow>[]): void {\n if (!sorts.length) return;\n\n // Pre-resolve comparator chain — avoids columns.find() on every pair comparison\n const chain = sorts.map((sort) => {\n const col = columns.find((c) => c.field === sort.field);\n const comparator = col?.sortComparator ?? defaultComparator;\n // Pre-bind the value getter per link so the hot comparator path doesn't\n // re-evaluate `column?.valueAccessor` on every pair comparison. For the\n // common case (no valueAccessor) this collapses to a single property read.\n // Documented precedence: sortComparator → valueAccessor → field.\n const field = sort.field;\n const getValue: (row: TRow) => unknown = col?.valueAccessor\n ? (row: TRow) => resolveCellValue(row, col)\n : (row: TRow) => (row as Record<string, unknown>)[field];\n return {\n field,\n asc: sort.direction === 'asc',\n comparator,\n getValue,\n // Auto-pin `__loading` placeholder rows (e.g. ServerSidePlugin under `sortMode: 'local'`)\n // to the end ONLY when no custom comparator is configured. Custom comparators receive\n // the row pair as 3rd/4th args and own placeholder handling themselves.\n pinPlaceholders: !col?.sortComparator,\n };\n });\n\n // Only enable per-pair pin checks if the dataset *actually* contains loading\n // placeholders — for the common case (no `__loading` rows) we skip ~n·log·n\n // property reads + branch on the hot path. Single O(n) scan up front.\n const chainNeedsPin = chain.some((l) => l.pinPlaceholders);\n const needsPinScan = chainNeedsPin && hasLoadingRow(rows);\n\n // Schwartzian transform: extract sort keys ONCE per row up front, then sort\n // indices by the cached keys. `Array.prototype.sort` calls the comparator\n // ~n·log·n times; without caching, each call would re-extract every key\n // (2 sides × k columns = 2·k property reads / accessor invocations per\n // compare). Caching collapses ~2·k·n·log·n extractions to k·n. For 10K\n // rows × 2 keys that's 20K extractions instead of ~260K, and the comparator\n // body becomes a pure key-vs-key comparison the JIT can optimize tightly.\n const n = rows.length;\n const k = chain.length;\n const keys: unknown[] = new Array(n * k);\n for (let i = 0; i < n; i++) {\n const row = rows[i];\n const base = i * k;\n for (let j = 0; j < k; j++) {\n keys[base + j] = chain[j].getValue(row);\n }\n }\n\n // Index array — sort this and then permute `rows` in one pass at the end.\n // Typed array when it fits; falls back to plain Array for very large datasets.\n const indices: Uint32Array | number[] = n <= 0xffffffff ? new Uint32Array(n) : new Array(n);\n for (let i = 0; i < n; i++) indices[i] = i;\n\n const compareIndices = (ia: number, ib: number): number => {\n if (needsPinScan) {\n const pinned = pinLoadingRows(rows[ia], rows[ib]);\n if (pinned !== 0) return pinned;\n }\n const baseA = ia * k;\n const baseB = ib * k;\n for (let j = 0; j < k; j++) {\n const link = chain[j];\n // Custom comparators receive the row pair as 3rd/4th args; defaultComparator\n // ignores them. Keys are already cached so no extraction happens here.\n const result = link.comparator(keys[baseA + j], keys[baseB + j], rows[ia], rows[ib]);\n if (result !== 0) return link.asc ? result : -result;\n }\n return 0;\n };\n\n if (indices instanceof Uint32Array) {\n indices.sort(compareIndices);\n } else {\n (indices as number[]).sort(compareIndices);\n }\n\n // Permute rows in-place using the sorted index array. Single allocation of\n // a snapshot; then write back. Done in two passes to avoid clobbering.\n const snapshot = rows.slice();\n for (let i = 0; i < n; i++) {\n rows[i] = snapshot[indices[i]];\n }\n}\n\n/**\n * O(n) scan checking whether any row has the `__loading` placeholder marker.\n * Cheap relative to the O(n·log·n) sort, and lets us elide per-pair pin checks\n * when the dataset has no placeholders (the overwhelmingly common case).\n * @internal\n */\nfunction hasLoadingRow(rows: readonly unknown[]): boolean {\n for (let i = 0; i < rows.length; i++) {\n if ((rows[i] as { __loading?: unknown } | null)?.__loading === true) return true;\n }\n return false;\n}\n\n/**\n * Pin `__loading` placeholder rows (e.g. ServerSidePlugin under `sortMode: 'local'`)\n * to the end of the sorted array regardless of sort direction.\n *\n * Returns 0 when neither row is a placeholder, +1 when `a` is, -1 when `b` is.\n *\n * @internal\n */\nfunction pinLoadingRows(a: unknown, b: unknown): number {\n const aLoading = (a as { __loading?: unknown } | null)?.__loading === true;\n const bLoading = (b as { __loading?: unknown } | null)?.__loading === true;\n if (aLoading === bLoading) return 0;\n return aLoading ? 1 : -1;\n}\n\n/**\n * Default comparator for sorting values.\n * Handles nulls, numbers, dates, and strings.\n *\n * @param a - First value\n * @param b - Second value\n * @returns Comparison result (-1, 0, 1)\n */\nexport function defaultComparator(a: unknown, b: unknown): number {\n // Handle nulls/undefined - push to end\n if (a == null && b == null) return 0;\n if (a == null) return 1;\n if (b == null) return -1;\n\n // Type-aware comparison\n if (typeof a === 'number' && typeof b === 'number') {\n return a - b;\n }\n\n if (a instanceof Date && b instanceof Date) {\n return a.getTime() - b.getTime();\n }\n\n // Boolean comparison\n if (typeof a === 'boolean' && typeof b === 'boolean') {\n return a === b ? 0 : a ? -1 : 1;\n }\n\n // String comparison (fallback). Uses a module-level cached Intl.Collator\n // — see `stringCollator` declaration for rationale.\n return stringCollator.compare(typeof a === 'string' ? a : String(a), typeof b === 'string' ? b : String(b));\n}\n\n/**\n * Toggle sort state for a field.\n * With shift key: adds/toggles in multi-sort list\n * Without shift key: replaces entire sort with single column\n *\n * @param current - Current sort model\n * @param field - Field to toggle\n * @param shiftKey - Whether shift key is held (multi-sort mode)\n * @param maxColumns - Maximum columns allowed in sort\n * @returns New sort model\n */\nexport function toggleSort(current: SortModel[], field: string, shiftKey: boolean, maxColumns: number): SortModel[] {\n const existing = current.find((s) => s.field === field);\n\n if (shiftKey) {\n // Multi-sort: add/toggle in list\n if (existing) {\n if (existing.direction === 'asc') {\n // Flip to descending\n return current.map((s) => (s.field === field ? { ...s, direction: 'desc' as const } : s));\n } else {\n // Remove from sort\n return current.filter((s) => s.field !== field);\n }\n } else if (current.length < maxColumns) {\n // Add new sort column\n return [...current, { field, direction: 'asc' as const }];\n }\n // Max columns reached, return unchanged\n return current;\n } else {\n // Single sort: replace all\n if (existing?.direction === 'asc') {\n return [{ field, direction: 'desc' }];\n } else if (existing?.direction === 'desc') {\n return [];\n }\n return [{ field, direction: 'asc' }];\n }\n}\n\n/**\n * Get the sort index (1-based) for a field in the sort model.\n * Returns undefined if the field is not in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns 1-based index or undefined\n */\nexport function getSortIndex(sortModel: SortModel[], field: string): number | undefined {\n const index = sortModel.findIndex((s) => s.field === field);\n return index >= 0 ? index + 1 : undefined;\n}\n\n/**\n * Get the sort direction for a field in the sort model.\n *\n * @param sortModel - Current sort model\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\nexport function getSortDirection(sortModel: SortModel[], field: string): 'asc' | 'desc' | undefined {\n return sortModel.find((s) => s.field === field)?.direction;\n}\n","/**\n * Multi-Sort Plugin (Class-based)\n *\n * Provides multi-column sorting capabilities for tbw-grid.\n * Supports shift+click for adding secondary sort columns.\n */\n\nimport { announce, getA11yMessage } from '../../core/internal/aria';\nimport { BaseGridPlugin, HeaderClickEvent, type PluginManifest, type PluginQuery } from '../../core/plugin/base-plugin';\nimport type { ColumnState, GridHost } from '../../core/types';\nimport { getSortDirection, getSortIndex, sortRowsInPlace, toggleSort } from './multi-sort';\nimport styles from './multi-sort.css?inline';\nimport type { MultiSortConfig, SortModel } from './types';\n\n/**\n * Multi-Sort Plugin for tbw-grid\n *\n * Enables sorting by multiple columns at once—hold Shift and click additional column\n * headers to build up a sort stack. Priority badges show the sort order, so users\n * always know which column takes precedence.\n *\n * ## Installation\n *\n * ```ts\n * import { MultiSortPlugin } from '@toolbox-web/grid/plugins/multi-sort';\n * ```\n *\n * ## Keyboard Shortcuts\n *\n * | Shortcut | Action |\n * |----------|--------|\n * | `Click header` | Sort by column (clears other sorts) |\n * | `Shift + Click` | Add column to multi-sort stack |\n * | `Ctrl + Click` | Toggle sort direction |\n *\n * ## Events\n *\n * | Event | Detail | Description |\n * |-------|--------|-------------|\n * | `sort-change` | `{ sortModel: SortModel[] }` | Fired when sort changes |\n *\n * @example Basic Multi-Column Sorting\n * ```ts\n * import { queryGrid } from '@toolbox-web/grid';\n * import { MultiSortPlugin } from '@toolbox-web/grid/plugins/multi-sort';\n *\n * const grid = queryGrid('tbw-grid');\n * grid.gridConfig = {\n * columns: [\n * { field: 'name', header: 'Name', sortable: true },\n * { field: 'department', header: 'Department', sortable: true },\n * { field: 'salary', header: 'Salary', type: 'number', sortable: true },\n * ],\n * plugins: [new MultiSortPlugin({ maxSortColumns: 3, showSortIndex: true })],\n * };\n *\n * grid.on('sort-change', ({ sortModel }) => {\n * console.log('Active sorts:', sortModel);\n * });\n * ```\n *\n * @example Initial Sort Configuration\n * ```ts\n * new MultiSortPlugin({\n * initialSort: [\n * { field: 'department', direction: 'asc' },\n * { field: 'salary', direction: 'desc' },\n * ],\n * })\n * ```\n *\n * @see {@link MultiSortConfig} for all configuration options\n * @see {@link SortModel} for the sort model structure\n *\n * @internal Extends BaseGridPlugin\n */\nexport class MultiSortPlugin extends BaseGridPlugin<MultiSortConfig> {\n /**\n * Plugin manifest declaring query types this plugin responds to.\n * @internal\n */\n static override readonly manifest: PluginManifest = {\n queries: [\n { type: 'sort:get-model', description: 'Returns the current multi-sort model as SortModel[]' },\n { type: 'sort:set-model', description: 'Sets the multi-sort model from context (SortModel[])' },\n ],\n };\n\n /** @internal */\n readonly name = 'multiSort';\n /** @internal */\n override readonly styles = styles;\n\n /** @internal */\n protected override get defaultConfig(): Partial<MultiSortConfig> {\n return {\n maxSortColumns: 3,\n showSortIndex: true,\n };\n }\n\n // #region Internal State\n private sortModel: SortModel[] = [];\n /** Cached sort result — returned as-is while a row edit is active to prevent\n * the edited row from jumping to a new sorted position mid-edit. Row data\n * mutations are still visible because the array holds shared object refs. */\n private cachedSortResult: unknown[] | null = null;\n /** Guards deferred sort-change broadcast during applyColumnState batch. */\n #pendingStateBroadcast = false;\n /** Snapshot of sortModel taken at the start of an applyColumnState batch,\n * used by the deferred broadcast to skip when the model didn't change. */\n #preBatchSortModel: SortModel[] = [];\n\n /** Typed internal grid accessor. */\n get #internalGrid(): GridHost {\n return this.grid as unknown as GridHost;\n }\n\n /**\n * Clear the core `_sortState` so that only this plugin's `processRows`\n * sorting applies. `ConfigManager.applyState()` always sets the core sort\n * state when restoring from storage, even when a plugin handles sorting.\n * Without this, the stale core state leaks into `collectState()` and\n * `reapplyCoreSort()` after the plugin clears its own model.\n */\n private clearCoreSortState(): void {\n this.#internalGrid._sortState = null;\n }\n\n /**\n * Remove sorts on fields that are owned by the grouping plugin.\n * GroupingRowsPlugin handles group header ordering independently, so\n * multi-sort should only sort by non-grouped data columns.\n */\n #filterGroupedFields(model: SortModel[]): SortModel[] {\n const results = this.grid?.query?.('grouping:get-grouped-fields', null);\n if (!Array.isArray(results) || results.length === 0) return model;\n\n const groupedFields = results[0] as string[];\n if (!Array.isArray(groupedFields) || groupedFields.length === 0) return model;\n\n const groupedSet = new Set(groupedFields);\n const filtered = model.filter((s) => !groupedSet.has(s.field));\n return filtered.length === model.length ? model : filtered;\n }\n // #endregion\n\n // #region Lifecycle\n\n /** @internal */\n override detach(): void {\n this.sortModel = [];\n this.cachedSortResult = null;\n }\n // #endregion\n\n // #region Query System\n\n /** @internal */\n override handleQuery(query: PluginQuery): unknown {\n switch (query.type) {\n case 'sort:get-model':\n return [...this.sortModel];\n case 'sort:set-model': {\n const model = query.context;\n if (!Array.isArray(model)) return false;\n this.sortModel = [...model] as SortModel[];\n this.clearCoreSortState();\n this.broadcast('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n return true;\n }\n default:\n return undefined;\n }\n }\n\n // #endregion\n\n // #region Hooks\n\n /** @internal */\n override processRows(rows: readonly unknown[]): unknown[] {\n if (this.sortModel.length === 0) {\n this.cachedSortResult = null;\n return [...rows];\n }\n\n // Freeze sort order while a row is actively being edited (row mode only).\n // Re-sorting mid-edit would move the edited row to a new index while the\n // editors remain at the old position, causing data/UI mismatch.\n // In grid mode (_isGridEditMode) sorting is safe — afterCellRender\n // re-injects editors into the re-sorted cells.\n // We return the cached previous sort result (same object references, so\n // in-place value mutations are already visible) instead of unsorted input.\n const grid = this.#internalGrid;\n if (!grid._isGridEditMode && typeof grid._activeEditRows === 'number' && grid._activeEditRows !== -1) {\n if (this.cachedSortResult && this.cachedSortResult.length === rows.length) {\n return [...this.cachedSortResult];\n }\n }\n\n // Sort in-place — the input array is already a mutable copy from plugin-manager.\n // Pre-resolved comparator chain avoids column lookup on every pair comparison.\n // Exclude fields owned by the grouping plugin — group header order is handled\n // by GroupingRowsPlugin, so multi-sort should only affect within-group data order.\n const effectiveModel = this.#filterGroupedFields(this.sortModel);\n\n const mutableRows = rows as unknown[];\n if (effectiveModel.length > 0) {\n sortRowsInPlace(mutableRows, effectiveModel, this.columns);\n }\n this.cachedSortResult = mutableRows;\n return mutableRows;\n }\n\n /** @internal */\n override onHeaderClick(event: HeaderClickEvent): boolean {\n const column = this.columns.find((c) => c.field === event.field);\n if (!column?.sortable) return false;\n\n const shiftKey = event.originalEvent.shiftKey;\n const maxColumns = this.config.maxSortColumns ?? 3;\n\n this.sortModel = toggleSort(this.sortModel, event.field, shiftKey, maxColumns);\n this.clearCoreSortState();\n\n this.broadcast('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n this.grid?.requestStateChange?.();\n\n // Announce for screen readers\n if (this.sortModel.length > 0) {\n const labels = this.sortModel.map((s) => {\n const col = this.columns.find((c) => c.field === s.field);\n return `${col?.header ?? s.field} ${s.direction === 'asc' ? 'ascending' : 'descending'}`;\n });\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortApplied', labels.join(', then '), ''));\n } else {\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortCleared'));\n }\n\n return true;\n }\n\n /** @internal */\n override afterRender(): void {\n const gridEl = this.gridElement;\n if (!gridEl) return;\n\n const showIndex = this.config.showSortIndex !== false;\n\n const headerCells = gridEl.querySelectorAll('.header-row .cell[data-field]');\n headerCells.forEach((cell) => {\n const field = cell.getAttribute('data-field');\n if (!field) return;\n\n const sortIndex = getSortIndex(this.sortModel, field);\n const sortDir = getSortDirection(this.sortModel, field);\n\n // Remove existing sort index badge (always clean up)\n cell.querySelector('.sort-index')?.remove();\n\n if (sortDir) {\n const indicator = this.updateSortIndicator(cell, sortDir);\n\n // Add sort index badge if multiple columns sorted and showSortIndex is enabled\n if (showIndex && this.sortModel.length > 1 && sortIndex !== undefined) {\n const badge = document.createElement('span');\n badge.className = 'sort-index';\n badge.textContent = String(sortIndex);\n if (indicator.nextSibling) {\n cell.insertBefore(badge, indicator.nextSibling);\n } else {\n cell.appendChild(badge);\n }\n }\n } else if (cell.classList.contains('sortable')) {\n this.updateSortIndicator(cell, null);\n }\n });\n }\n // #endregion\n\n // #region Public API\n\n /**\n * Get the current sort model.\n * @returns Copy of the current sort model\n */\n getSortModel(): SortModel[] {\n return [...this.sortModel];\n }\n\n /**\n * Set the sort model programmatically.\n * @param model - New sort model to apply\n */\n setSortModel(model: SortModel[]): void {\n this.sortModel = [...model];\n this.clearCoreSortState();\n this.broadcast('sort-change', { sortModel: [...model] });\n this.requestRender();\n this.grid?.requestStateChange?.();\n if (model.length > 0) {\n const labels = model.map((s) => {\n const col = this.columns.find((c) => c.field === s.field);\n return `${col?.header ?? s.field} ${s.direction === 'asc' ? 'ascending' : 'descending'}`;\n });\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortApplied', labels.join(', then '), ''));\n }\n }\n\n /**\n * Clear all sorting.\n */\n clearSort(): void {\n this.sortModel = [];\n this.clearCoreSortState();\n this.broadcast('sort-change', { sortModel: [] });\n this.requestRender();\n this.grid?.requestStateChange?.();\n announce(this.gridElement!, getA11yMessage(this.gridElement!, 'sortCleared'));\n }\n\n /**\n * Get the sort index (1-based) for a specific field.\n * @param field - Field to check\n * @returns 1-based index or undefined if not sorted\n */\n getSortIndex(field: string): number | undefined {\n return getSortIndex(this.sortModel, field);\n }\n\n /**\n * Get the sort direction for a specific field.\n * @param field - Field to check\n * @returns Sort direction or undefined if not sorted\n */\n getSortDirection(field: string): 'asc' | 'desc' | undefined {\n return getSortDirection(this.sortModel, field);\n }\n // #endregion\n\n // #region Column State Hooks\n\n /**\n * Return sort state for a column if it's in the sort model.\n * @internal\n */\n override getColumnState(field: string): Partial<ColumnState> | undefined {\n const index = this.sortModel.findIndex((s) => s.field === field);\n if (index === -1) return undefined;\n\n const sortEntry = this.sortModel[index];\n return {\n sort: {\n direction: sortEntry.direction,\n priority: index,\n },\n };\n }\n\n /**\n * Apply sort state from column state.\n * Rebuilds the sort model from all column states.\n * @internal\n */\n override applyColumnState(field: string, state: ColumnState): void {\n // Snapshot model before mutation so the microtask can skip a no-op\n // broadcast/render when nothing actually changed.\n if (!this.#pendingStateBroadcast) {\n this.#preBatchSortModel = [...this.sortModel];\n }\n\n if (!state.sort) {\n // Remove this field from sortModel if it exists\n this.sortModel = this.sortModel.filter((s) => s.field !== field);\n } else {\n // Find existing entry or add new one\n const existingIndex = this.sortModel.findIndex((s) => s.field === field);\n const newEntry: SortModel = {\n field,\n direction: state.sort.direction,\n };\n\n if (existingIndex !== -1) {\n // Update existing entry\n this.sortModel[existingIndex] = newEntry;\n } else {\n // Add at the correct priority position\n this.sortModel.splice(state.sort.priority, 0, newEntry);\n }\n }\n\n // Clear core sort state — this plugin exclusively handles sorting via\n // processRows. The core _sortState is set by ConfigManager.applyState()\n // before plugins run; null it so reapplyCoreSort() is a no-op.\n this.clearCoreSortState();\n\n // Broadcast sort-change + force a row re-render after state restoration.\n // Both are deferred to a microtask so per-column calls coalesce into one\n // event and one render. The render handles the \"sort cleared\" case that\n // core's width-only fast path cannot detect (core only inspects the\n // *incoming* state, not what the plugin's model became). Skip entirely\n // when the model is unchanged so unrelated state restores stay cheap.\n if (!this.#pendingStateBroadcast) {\n this.#pendingStateBroadcast = true;\n queueMicrotask(() => {\n this.#pendingStateBroadcast = false;\n const prev = this.#preBatchSortModel;\n this.#preBatchSortModel = [];\n if (sortModelEquals(prev, this.sortModel)) return;\n this.broadcast('sort-change', { sortModel: [...this.sortModel] });\n this.requestRender();\n });\n }\n }\n // #endregion\n}\n\n/** Shallow equality for two sort models (order-sensitive). */\nfunction sortModelEquals(a: SortModel[], b: SortModel[]): boolean {\n if (a.length !== b.length) return false;\n for (let i = 0; i < a.length; i++) {\n if (a[i].field !== b[i].field || a[i].direction !== b[i].direction) return false;\n }\n return true;\n}\n"],"names":["stringCollator","Intl","Collator","sensitivity","defaultComparator","a","b","Date","getTime","compare","String","getSortIndex","sortModel","field","index","findIndex","s","getSortDirection","find","direction","MultiSortPlugin","BaseGridPlugin","static","queries","type","description","name","styles","defaultConfig","maxSortColumns","showSortIndex","cachedSortResult","pendingStateBroadcast","preBatchSortModel","internalGrid","this","grid","clearCoreSortState","_sortState","filterGroupedFields","model","results","query","Array","isArray","length","groupedFields","groupedSet","Set","filtered","filter","has","detach","handleQuery","context","broadcast","requestRender","processRows","rows","_isGridEditMode","_activeEditRows","effectiveModel","mutableRows","sorts","columns","chain","map","sort","col","c","comparator","sortComparator","getValue","valueAccessor","row","resolveCellValue","asc","pinPlaceholders","needsPinScan","some","l","i","__loading","hasLoadingRow","n","k","keys","base","j","indices","Uint32Array","compareIndices","ia","ib","pinned","aLoading","pinLoadingRows","baseA","baseB","link","result","snapshot","slice","sortRowsInPlace","onHeaderClick","event","column","sortable","shiftKey","originalEvent","maxColumns","config","current","existing","toggleSort","requestStateChange","labels","header","announce","gridElement","getA11yMessage","join","afterRender","gridEl","showIndex","querySelectorAll","forEach","cell","getAttribute","sortIndex","sortDir","querySelector","remove","indicator","updateSortIndicator","badge","document","createElement","className","textContent","nextSibling","insertBefore","appendChild","classList","contains","getSortModel","setSortModel","clearSort","getColumnState","priority","applyColumnState","state","existingIndex","newEntry","splice","queueMicrotask","prev","sortModelEquals"],"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/utils"),require("../../core/plugin/base-plugin"),require("../../core/plugin/expander-column")):"function"==typeof define&&define.amd?define(["exports","../../core/constants","../../core/internal/aria","../../core/internal/utils","../../core/plugin/base-plugin","../../core/plugin/expander-column"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_selection={},e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid)}(this,function(e,t,s,i,r,l){"use strict";function o(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 n(e){const t=o(e);return{from:{row:t.startRow,col:t.startCol},to:{row:t.endRow,col:t.endCol}}}function c(e){return e.map(n)}function a(e,t,s){return s.some(s=>function(e,t,s){const i=o(s);return e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol}(e,t,s))}function d(e){const t=[],s=o(e);for(let i=s.startRow;i<=s.endRow;i++)for(let e=s.startCol;e<=s.endCol;e++)t.push({row:i,col:e});return t}function h(e,t){return{startRow:e.row,startCol:e.col,endRow:t.row,endCol:t.col}}function g(e,t){const s=o(e),i=o(t);return s.startRow===i.startRow&&s.startCol===i.startCol&&s.endRow===i.endRow&&s.endCol===i.endCol}const u="__tbw_checkbox";class w extends r.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)"}],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"===e.mode&&"dblclick"===e.triggerOn}]};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}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:"";position:absolute;inset:0;pointer-events:none;border-width:0;border-style:var(--tbw-selection-border-style, var(--tbw-border-style));border-color:var(--tbw-range-border-color, var(--tbw-color-accent));border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width));border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width));z-index:26}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:"";position:absolute;inset:0;pointer-events:none;border:0 var(--tbw-selection-border-style, var(--tbw-border-style)) var(--tbw-range-border-color);z-index:1}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg, rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm, .8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}tbw-grid .data-grid-row>.cell[data-field=__tbw_checkbox],tbw-grid .header-row>.cell[data-field=__tbw_checkbox]{text-align:center;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}tbw-grid .tbw-select-row-checkbox{pointer-events:none;margin:0;cursor:pointer}tbw-grid .tbw-checkbox-header{display:flex;justify-content:center;align-items:center;height:100%}tbw-grid .tbw-select-all-checkbox{margin:0;cursor:pointer}}';get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0,multiSelect:!0}}selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;pendingRowKeyUpdate=null;selectedCell=null;lastSyncedFocusRow=-1;lastSyncedFocusCol=-1;announceTimer=null;explicitSelection=!1;isSelectionEnabled(){return!1!==this.config.enabled&&!1!==this.grid.effectiveConfig?.selectable}checkSelectable(e,t){const{isSelectable:s}=this.config;if(!s)return!0;const i=this.rows[e];if(!i)return!1;return s(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.on("filter-change",()=>this.clearSelectionSilent()),this.on("group-toggle",()=>this.clearSelectionSilent()),this.on("tree-expand",()=>this.clearSelectionSilent()),this.on("sort-change",()=>this.clearSelectionSilent())}handleQuery(e){return"getSelection"===e.type?this.getSelection():"getSelectedRowIndices"===e.type?this.getSelectedRowIndices():"getSelectedRows"===e.type?this.getSelectedRows():"selectRows"===e.type?(this.selectRows(e.context),!0):void 0}detach(){const e=this.gridElement?.querySelector(".rows-body");e?.removeAttribute("aria-multiselectable"),this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.pendingKeyboardUpdate=null,this.pendingRowKeyUpdate=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1}clearSelectionSilent(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedCell=null,this.lastSelected=null,this.anchor=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,this.requestAfterRender()}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{rowIndex:t,colIndex:s,originalEvent:i}=e,{mode:r,triggerOn:o="click"}=this.config;if(i.type!==o)return!1;const n=e.column,c=n&&l.isUtilityColumn(n);if("cell"===r){if(c)return!1;if(!this.isCellSelectable(t,s))return!1;const e=this.selectedCell;return e&&e.row===t&&e.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()),this.requestAfterRender()),!1}if("row"===r){if(!this.isRowSelectable(t))return!1;const e=!1!==this.config.multiSelect,s=i.shiftKey&&e,r=(i.ctrlKey||i.metaKey)&&e,l=!0===n?.checkboxColumn;if(s&&null!==this.anchor){const e=Math.min(this.anchor,t),s=Math.max(this.anchor,t);r||this.selected.clear();for(let t=e;t<=s;t++)this.isRowSelectable(t)&&this.selected.add(t)}else if(r||l&&e)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.#e()),this.requestAfterRender(),!1}if("range"===r){if(c)return!1;if(!this.isCellSelectable(t,s))return!1;const e=i.shiftKey,r=(i.ctrlKey||i.metaKey)&&!1!==this.config.multiSelect;if(e&&this.cellAnchor){const e=h(this.cellAnchor,{row:t,col:s}),i=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(i&&g(i,e))return!1;r?this.ranges.length>0?this.ranges[this.ranges.length-1]=e:this.ranges.push(e):this.ranges=[e],this.activeRange=e}else if(r){const e={startRow:t,startCol:s,endRow:t,endCol:s};this.ranges.push(e),this.activeRange=e,this.cellAnchor={row:t,col:s}}else{const e={startRow:t,startCol:s,endRow:t,endCol:s};if(1===this.ranges.length&&g(this.ranges[0],e))return!1;this.ranges=[e],this.activeRange=e,this.cellAnchor={row:t,col:s}}return this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}return!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;const{mode:t}=this.config,s=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if("Escape"===e.key){return!this.grid.query("isEditing").some(Boolean)&&("cell"===t?this.selectedCell=null:"row"===t?(this.selected.clear(),this.anchor=null):"range"===t&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#e()),this.requestAfterRender(),!0)}if("cell"===t&&s)return queueMicrotask(()=>{const e=this.grid._focusRow,t=this.grid._focusCol;this.isCellSelectable(e,t)?this.selectedCell={row:e,col:t}:this.selectedCell=null,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if("row"===t){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 s=e.shiftKey&&t;return s&&null===this.anchor&&(this.anchor=this.grid._focusRow),this.explicitSelection=!0,this.pendingRowKeyUpdate={shiftKey:s},queueMicrotask(()=>this.requestAfterRender()),!1}if(t&&"a"===e.key&&(e.ctrlKey||e.metaKey)){return!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0)}}if("range"===t&&s){const t="Tab"===e.key,s=e.shiftKey&&!t;return s&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:s},queueMicrotask(()=>this.requestAfterRender()),!1}if("range"===t&&!1!==this.config.multiSelect&&"a"===e.key&&(e.ctrlKey||e.metaKey)){return!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0)}return!1}onCellMouseDown(e){if(!this.isSelectionEnabled())return;if("range"!==this.config.mode)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;if(e.column&&l.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,s=e.colIndex,i=(e.originalEvent.ctrlKey||e.originalEvent.metaKey)&&!1!==this.config.multiSelect,r={startRow:t,startCol:s,endRow:t,endCol:s};return!i&&1===this.ranges.length&&g(this.ranges[0],r)?(this.cellAnchor={row:t,col:s},!0):(this.cellAnchor={row:t,col:s},i||(this.ranges=[]),this.ranges.push(r),this.activeRange=r,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0)}onCellMouseMove(e){if(!this.isSelectionEnabled())return;if("range"!==this.config.mode)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 s=this.visibleColumns[t];if(s&&l.isUtilityColumn(s)){const e=this.visibleColumns.findIndex(e=>!l.isUtilityColumn(e));e>=0&&(t=e)}const i=h(this.cellAnchor,{row:e.rowIndex,col:t}),r=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return r&&g(r,i)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i),this.activeRange=i,this.emit("selection-change",this.#e()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&"range"===this.config.mode)return this.isDragging?(this.isDragging=!1,!0):void 0}processColumns(e){if(this.config.checkbox&&"row"===this.config.mode){if(e.some(e=>e.field===u))return e;const t=this.#t(),s=e.findIndex(l.isExpanderColumn),i=s>=0?s+1:0;return[...e.slice(0,i),t,...e.slice(i)]}return e}#t(){return{field:u,header:"",width:32,resizable:!1,sortable:!1,lockPosition:!0,utility:!0,checkboxColumn:!0,headerRenderer:()=>{const e=document.createElement("div");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.appendChild(t),e},renderer:e=>{const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-row-checkbox";const s=e.cellEl;if(s){const e=parseInt(s.getAttribute("data-row")??"-1",10);e>=0&&(t.checked=this.selected.has(e))}return t}}}#s(e){e.querySelectorAll(".tbw-select-row-checkbox").forEach(e=>{const t=e.closest(".cell"),s=t?i.getRowIndexFromCell(t):-1;s>=0&&(e.checked=this.selected.has(s))});const t=e.querySelector(".tbw-select-all-checkbox");if(t){const e=this.rows.length;let s=0;if(this.config.isSelectable)for(let t=0;t<e;t++)this.isRowSelectable(t)&&s++;else s=e;const i=s>0&&this.selected.size>=s,r=this.selected.size>0;t.checked=i,t.indeterminate=r&&!i}}#i(e){const t=this.grid._focusRow,s=this.grid._focusCol;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.#e()))))}if("cell"===e){if(this.explicitSelection)return this.explicitSelection=!1,this.lastSyncedFocusRow=t,void(this.lastSyncedFocusCol=s);if((t!==this.lastSyncedFocusRow||s!==this.lastSyncedFocusCol)&&(this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=s,this.isCellSelectable(t,s))){const e=this.selectedCell;e&&e.row===t&&e.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()))}}}#r(){const e=this.gridElement;if(!e)return;const{mode:s}=this.config,r=!!this.config.isSelectable,n=e.querySelector(".rows-body");if(n){const e=!1!==this.config.multiSelect;n.setAttribute("aria-multiselectable",e?"true":"false")}e.querySelectorAll(".cell").forEach(e=>{e.classList.remove(t.GridClasses.SELECTED,"top","bottom","first","last"),r&&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"),r&&e.removeAttribute("data-selectable")}),"row"===s&&(i.clearCellFocus(e),c.forEach(e=>{const s=e.querySelector(".cell[data-row]"),l=i.getRowIndexFromCell(s);l>=0&&(r&&!this.isRowSelectable(l)&&e.setAttribute("data-selectable","false"),this.selected.has(l)&&(e.classList.add(t.GridClasses.SELECTED,"row-focus"),e.setAttribute("aria-selected","true")))}),this.config.checkbox&&this.#s(e)),("cell"===s||"range"===s)&&r){e.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const t=parseInt(e.getAttribute("data-row")??"-1",10),s=parseInt(e.getAttribute("data-col")??"-1",10);t>=0&&s>=0&&(this.isCellSelectable(t,s)||e.setAttribute("data-selectable","false"))})}if("range"===s&&this.ranges.length>0){i.clearCellFocus(e);const s=this.ranges.map(o),r=(e,t)=>{for(const i of s)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 s=parseInt(e.getAttribute("data-row")??"-1",10),i=parseInt(e.getAttribute("data-col")??"-1",10);if(s>=0&&i>=0){const o=this.visibleColumns[i];if(o&&l.isUtilityColumn(o))return;r(s,i)&&(e.classList.add(t.GridClasses.SELECTED),e.setAttribute("aria-selected","true"),r(s-1,i)||e.classList.add("top"),r(s+1,i)||e.classList.add("bottom"),r(s,i-1)||e.classList.add("first"),r(s,i+1)||e.classList.add("last"))}})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.querySelector(".tbw-grid-root"),{mode:s}=this.config;if(this.pendingRowKeyUpdate&&"row"===s){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),s=Math.max(this.anchor,t);for(let t=e;t<=s;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.#e())}if(this.pendingKeyboardUpdate&&"range"===s){const{shiftKey:e}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const t=this.grid._focusRow,s=this.grid._focusCol;if(e&&this.cellAnchor){const e=h(this.cellAnchor,{row:t,col:s});this.ranges=[e],this.activeRange=e}else e||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:t,col:s});this.emit("selection-change",this.#e())}this.#i(s),this.gridElement.setAttribute("data-selection-mode",s),t&&t.classList.toggle("selecting",this.isDragging),this.#r()}onScrollRender(){this.isSelectionEnabled()&&this.#r()}getSelection(){return{mode:this.config.mode,ranges:this.#e().ranges,anchor:this.cellAnchor}}getSelectedCells(){return function(e){const t=new Map;for(const s of e)for(const e of d(s))t.set(`${e.row},${e.col}`,e);return[...t.values()]}(this.ranges)}isCellSelected(e,t){return a(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.#e()),this.requestAfterRender()}else if("range"===e){const e=this.rows.length,t=this.columns.length;if(e>0&&t>0){const s={startRow:0,startCol:0,endRow:e-1,endCol:t-1};this.ranges=[s],this.activeRange=s,this.emit("selection-change",this.#e()),this.requestAfterRender()}}}selectRows(e){if("row"!==this.config.mode)return;const t=!1===this.config.multiSelect&&e.length>1?[e[e.length-1]]:e;this.selected.clear();for(const s of t)s>=0&&s<this.rows.length&&this.isRowSelectable(s)&&this.selected.add(s);this.anchor=t.length>0?t[t.length-1]:null,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}getSelectedRowIndices(){return[...this.selected].sort((e,t)=>e-t)}getSelectedRows(){const{mode:e}=this.config,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 s of this.ranges){const i=Math.max(0,Math.min(s.startRow,s.endRow)),r=Math.min(t.length-1,Math.max(s.startRow,s.endRow));for(let t=i;t<=r;t++)e.add(t)}return[...e].sort((e,t)=>e-t).map(e=>t[e])}return[]}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(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",{mode:this.config.mode,ranges:c(this.ranges)}),this.requestAfterRender()}#e(){const e=function(e,t,s){if("cell"===e&&t.selectedCell)return{mode:e,ranges:[{from:{row:t.selectedCell.row,col:t.selectedCell.col},to:{row:t.selectedCell.row,col:t.selectedCell.col}}]};if("row"===e&&t.selected.size>0){const i=[...t.selected].sort((e,t)=>e-t),r=[];let l=i[0],o=l;for(let e=1;e<i.length;e++)i[e]===o+1?o=i[e]:(r.push({from:{row:l,col:0},to:{row:o,col:s-1}}),l=i[e],o=l);return r.push({from:{row:l,col:0},to:{row:o,col:s-1}}),{mode:e,ranges:r}}return"range"===e&&t.ranges.length>0?{mode:e,ranges:c(t.ranges)}:{mode:e,ranges:[]}}(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length);return this.announceTimer&&clearTimeout(this.announceTimer),this.announceTimer=setTimeout(()=>{const t="row"===e.mode?this.selected.size:e.ranges.length;t>0&&s.announce(this.gridElement,s.getA11yMessage(this.gridElement,"selectionChanged",t))},150),e}}e.SelectionPlugin=w,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
1
+ !function(e,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports,require("../../core/constants"),require("../../core/internal/aria"),require("../../core/internal/utils"),require("../../core/plugin/base-plugin"),require("../../core/plugin/expander-column")):"function"==typeof define&&define.amd?define(["exports","../../core/constants","../../core/internal/aria","../../core/internal/utils","../../core/plugin/base-plugin","../../core/plugin/expander-column"],t):t((e="undefined"!=typeof globalThis?globalThis:e||self).TbwGridPlugin_selection={},e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid,e.TbwGrid)}(this,function(e,t,s,i,l,r){"use strict";function o(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 n(e){const t=o(e);return{from:{row:t.startRow,col:t.startCol},to:{row:t.endRow,col:t.endCol}}}function c(e){return e.map(n)}function a(e,t,s){return s.some(s=>function(e,t,s){const i=o(s);return e>=i.startRow&&e<=i.endRow&&t>=i.startCol&&t<=i.endCol}(e,t,s))}function d(e){const t=[],s=o(e);for(let i=s.startRow;i<=s.endRow;i++)for(let e=s.startCol;e<=s.endCol;e++)t.push({row:i,col:e});return t}function h(e,t){return{startRow:e.row,startCol:e.col,endRow:t.row,endCol:t.col}}function g(e,t){const s=o(e),i=o(t);return s.startRow===i.startRow&&s.startCol===i.startCol&&s.endRow===i.endRow&&s.endCol===i.endCol}const u="__tbw_checkbox";class w 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)"}],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"===e.mode&&"dblclick"===e.triggerOn}]};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}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:"";position:absolute;inset:0;pointer-events:none;border-width:0;border-style:var(--tbw-selection-border-style, var(--tbw-border-style));border-color:var(--tbw-range-border-color, var(--tbw-color-accent));border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width));border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width));z-index:26}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:"";position:absolute;inset:0;pointer-events:none;border:0 var(--tbw-selection-border-style, var(--tbw-border-style)) var(--tbw-range-border-color);z-index:1}tbw-grid .data-grid-row>.cell.selected.top:after{border-top-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.bottom:after{border-bottom-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.first:after{border-left-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row>.cell.selected.last:after{border-right-width:var(--tbw-selection-border-width, var(--tbw-border-width))}tbw-grid .data-grid-row[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row[data-selectable=false].row-focus{background-color:var(--tbw-color-row-alt)}tbw-grid .data-grid-row>.cell[data-selectable=false]{cursor:not-allowed;opacity:.6}tbw-grid .data-grid-row>.cell[data-selectable=false].selected{background-color:var(--tbw-selection-warning-bg, rgba(from var(--tbw-color-error) r g b / 50%))}tbw-grid .tbw-selection-summary{font-size:var(--tbw-font-size-sm, .8125rem);color:var(--tbw-color-fg-muted);white-space:nowrap}tbw-grid .data-grid-row>.cell[data-field=__tbw_checkbox],tbw-grid .header-row>.cell[data-field=__tbw_checkbox]{text-align:center;cursor:pointer;padding:0;display:flex;align-items:center;justify-content:center}tbw-grid .tbw-select-row-checkbox{pointer-events:none;margin:0;cursor:pointer}tbw-grid .tbw-checkbox-header{display:flex;justify-content:center;align-items:center;height:100%}tbw-grid .tbw-select-all-checkbox{margin:0;cursor:pointer}}';get defaultConfig(){return{mode:"cell",triggerOn:"click",enabled:!0,multiSelect:!0}}selected=new Set;lastSelected=null;anchor=null;ranges=[];activeRange=null;cellAnchor=null;isDragging=!1;pendingKeyboardUpdate=null;pendingRowKeyUpdate=null;selectedCell=null;lastSyncedFocusRow=-1;lastSyncedFocusCol=-1;announceTimer=null;explicitSelection=!1;isSelectionEnabled(){return!1!==this.config.enabled&&!1!==this.grid.effectiveConfig?.selectable}checkSelectable(e,t){const{isSelectable:s}=this.config;if(!s)return!0;const i=this.rows[e];if(!i)return!1;return s(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.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.config.mode&&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.#e()),this.requestAfterRender())))});let t=-1;e.addEventListener("data-change",e=>{const{sourceRowCount:s}=e.detail,i=this.selected.size>0||this.ranges.length>0||null!==this.selectedCell;-1!==t&&s!==t&&i&&this.clearSelectionSilent(),t=s},{signal:this.disconnectSignal})}handleQuery(e){return"getSelection"===e.type?this.getSelection():"getSelectedRowIndices"===e.type?this.getSelectedRowIndices():"getSelectedRows"===e.type?this.getSelectedRows():"selectRows"===e.type?(this.selectRows(e.context),!0):void 0}detach(){const e=this.gridElement?.querySelector(".rows-body");e?.removeAttribute("aria-multiselectable"),this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.isDragging=!1,this.selectedCell=null,this.pendingKeyboardUpdate=null,this.pendingRowKeyUpdate=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1}clearSelectionSilent(){this.selected.clear(),this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.selectedCell=null,this.lastSelected=null,this.anchor=null,this.lastSyncedFocusRow=-1,this.lastSyncedFocusCol=-1,this.requestAfterRender()}onCellClick(e){if(!this.isSelectionEnabled())return!1;const{rowIndex:t,colIndex:s,originalEvent:i}=e,{mode:l,triggerOn:o="click"}=this.config;if(i.type!==o)return!1;const n=e.column,c=n&&r.isUtilityColumn(n);if("cell"===l){if(c)return!1;if(!this.isCellSelectable(t,s))return!1;const e=this.selectedCell;return e&&e.row===t&&e.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()),this.requestAfterRender()),!1}if("row"===l){if(!this.isRowSelectable(t))return!1;const e=!1!==this.config.multiSelect,s=i.shiftKey&&e,l=(i.ctrlKey||i.metaKey)&&e,r=!0===n?.checkboxColumn;if(s&&null!==this.anchor){const e=Math.min(this.anchor,t),s=Math.max(this.anchor,t);l||this.selected.clear();for(let t=e;t<=s;t++)this.isRowSelectable(t)&&this.selected.add(t)}else if(l||r&&e)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.#e()),this.requestAfterRender(),!1}if("range"===l){if(c)return!1;if(!this.isCellSelectable(t,s))return!1;const e=i.shiftKey,l=(i.ctrlKey||i.metaKey)&&!1!==this.config.multiSelect;if(e&&this.cellAnchor){const e=h(this.cellAnchor,{row:t,col:s}),i=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;if(i&&g(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 if(l){const e={startRow:t,startCol:s,endRow:t,endCol:s};this.ranges.push(e),this.activeRange=e,this.cellAnchor={row:t,col:s}}else{const e={startRow:t,startCol:s,endRow:t,endCol:s};if(1===this.ranges.length&&g(this.ranges[0],e))return!1;this.ranges=[e],this.activeRange=e,this.cellAnchor={row:t,col:s}}return this.emit("selection-change",this.#e()),this.requestAfterRender(),!1}return!1}onKeyDown(e){if(!this.isSelectionEnabled())return!1;const{mode:t}=this.config,s=["ArrowUp","ArrowDown","ArrowLeft","ArrowRight","Tab","Home","End","PageUp","PageDown"].includes(e.key);if("Escape"===e.key){return!this.grid.query("isEditing").some(Boolean)&&("cell"===t?this.selectedCell=null:"row"===t?(this.selected.clear(),this.anchor=null):"range"===t&&(this.ranges=[],this.activeRange=null,this.cellAnchor=null),this.emit("selection-change",this.#e()),this.requestAfterRender(),!0)}if("cell"===t&&s)return queueMicrotask(()=>{const e=this.grid._focusRow,t=this.grid._focusCol;this.isCellSelectable(e,t)?this.selectedCell={row:e,col:t}:this.selectedCell=null,this.emit("selection-change",this.#e()),this.requestAfterRender()}),!1;if("row"===t){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 s=e.shiftKey&&t;return s&&null===this.anchor&&(this.anchor=this.grid._focusRow),this.explicitSelection=!0,this.pendingRowKeyUpdate={shiftKey:s},queueMicrotask(()=>this.requestAfterRender()),!1}if(t&&"a"===e.key&&(e.ctrlKey||e.metaKey)){return!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0)}}if("range"===t&&s){const t="Tab"===e.key,s=e.shiftKey&&!t;return s&&!this.cellAnchor&&(this.cellAnchor={row:this.grid._focusRow,col:this.grid._focusCol}),this.pendingKeyboardUpdate={shiftKey:s},queueMicrotask(()=>this.requestAfterRender()),!1}if("range"===t&&!1!==this.config.multiSelect&&"a"===e.key&&(e.ctrlKey||e.metaKey)){return!this.grid.query("isEditing").some(Boolean)&&(e.preventDefault(),e.stopPropagation(),this.selectAll(),!0)}return!1}onCellMouseDown(e){if(!this.isSelectionEnabled())return;if("range"!==this.config.mode)return;if(void 0===e.rowIndex||void 0===e.colIndex)return;if(e.rowIndex<0)return;if(e.column&&r.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,s=e.colIndex,i=(e.originalEvent.ctrlKey||e.originalEvent.metaKey)&&!1!==this.config.multiSelect,l={startRow:t,startCol:s,endRow:t,endCol:s};return!i&&1===this.ranges.length&&g(this.ranges[0],l)?(this.cellAnchor={row:t,col:s},!0):(this.cellAnchor={row:t,col:s},i||(this.ranges=[]),this.ranges.push(l),this.activeRange=l,this.emit("selection-change",this.#e()),this.requestAfterRender(),!0)}onCellMouseMove(e){if(!this.isSelectionEnabled())return;if("range"!==this.config.mode)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 s=this.visibleColumns[t];if(s&&r.isUtilityColumn(s)){const e=this.visibleColumns.findIndex(e=>!r.isUtilityColumn(e));e>=0&&(t=e)}const i=h(this.cellAnchor,{row:e.rowIndex,col:t}),l=this.ranges.length>0?this.ranges[this.ranges.length-1]:null;return l&&g(l,i)||(this.ranges.length>0?this.ranges[this.ranges.length-1]=i:this.ranges.push(i),this.activeRange=i,this.emit("selection-change",this.#e()),this.requestAfterRender()),!0}onCellMouseUp(e){if(this.isSelectionEnabled()&&"range"===this.config.mode)return this.isDragging?(this.isDragging=!1,!0):void 0}processColumns(e){if(this.config.checkbox&&"row"===this.config.mode){if(e.some(e=>e.field===u))return e;const t=this.#t(),s=e.findIndex(r.isExpanderColumn),i=s>=0?s+1:0;return[...e.slice(0,i),t,...e.slice(i)]}return e}#t(){return{field:u,header:"",width:32,resizable:!1,sortable:!1,lockPosition:!0,utility:!0,checkboxColumn:!0,headerRenderer:()=>{const e=document.createElement("div");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.appendChild(t),e},renderer:e=>{const t=document.createElement("input");t.type="checkbox",t.className="tbw-select-row-checkbox";const s=e.cellEl;if(s){const e=parseInt(s.getAttribute("data-row")??"-1",10);e>=0&&(t.checked=this.selected.has(e))}return t}}}#s(e){e.querySelectorAll(".tbw-select-row-checkbox").forEach(e=>{const t=e.closest(".cell"),s=t?i.getRowIndexFromCell(t):-1;s>=0&&(e.checked=this.selected.has(s))});const t=e.querySelector(".tbw-select-all-checkbox");if(t){const e=this.rows.length;let s=0;if(this.config.isSelectable)for(let t=0;t<e;t++)this.isRowSelectable(t)&&s++;else s=e;const i=s>0&&this.selected.size>=s,l=this.selected.size>0;t.checked=i,t.indeterminate=l&&!i}}#i(e){const t=this.grid._focusRow,s=this.grid._focusCol;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.#e()))))}if("cell"===e){if(this.explicitSelection)return this.explicitSelection=!1,this.lastSyncedFocusRow=t,void(this.lastSyncedFocusCol=s);if((t!==this.lastSyncedFocusRow||s!==this.lastSyncedFocusCol)&&(this.lastSyncedFocusRow=t,this.lastSyncedFocusCol=s,this.isCellSelectable(t,s))){const e=this.selectedCell;e&&e.row===t&&e.col===s||(this.selectedCell={row:t,col:s},this.emit("selection-change",this.#e()))}}}#l(){const e=this.gridElement;if(!e)return;const{mode:s}=this.config,l=!!this.config.isSelectable,n=e.querySelector(".rows-body");if(n){const e=!1!==this.config.multiSelect;n.setAttribute("aria-multiselectable",e?"true":"false")}e.querySelectorAll(".cell").forEach(e=>{e.classList.remove(t.GridClasses.SELECTED,"top","bottom","first","last"),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")}),"row"===s&&(i.clearCellFocus(e),c.forEach(e=>{const s=e.querySelector(".cell[data-row]"),r=i.getRowIndexFromCell(s);r>=0&&(l&&!this.isRowSelectable(r)&&e.setAttribute("data-selectable","false"),this.selected.has(r)&&(e.classList.add(t.GridClasses.SELECTED,"row-focus"),e.setAttribute("aria-selected","true")))}),this.config.checkbox&&this.#s(e)),("cell"===s||"range"===s)&&l){e.querySelectorAll(".cell[data-row][data-col]").forEach(e=>{const t=parseInt(e.getAttribute("data-row")??"-1",10),s=parseInt(e.getAttribute("data-col")??"-1",10);t>=0&&s>=0&&(this.isCellSelectable(t,s)||e.setAttribute("data-selectable","false"))})}if("range"===s&&this.ranges.length>0){i.clearCellFocus(e);const s=this.ranges.map(o),l=(e,t)=>{for(const i of s)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 s=parseInt(e.getAttribute("data-row")??"-1",10),i=parseInt(e.getAttribute("data-col")??"-1",10);if(s>=0&&i>=0){const o=this.visibleColumns[i];if(o&&r.isUtilityColumn(o))return;l(s,i)&&(e.classList.add(t.GridClasses.SELECTED),e.setAttribute("aria-selected","true"),l(s-1,i)||e.classList.add("top"),l(s+1,i)||e.classList.add("bottom"),l(s,i-1)||e.classList.add("first"),l(s,i+1)||e.classList.add("last"))}})}}afterRender(){if(!this.isSelectionEnabled())return;const e=this.gridElement;if(!e)return;const t=e.querySelector(".tbw-grid-root"),{mode:s}=this.config;if(this.pendingRowKeyUpdate&&"row"===s){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),s=Math.max(this.anchor,t);for(let t=e;t<=s;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.#e())}if(this.pendingKeyboardUpdate&&"range"===s){const{shiftKey:e}=this.pendingKeyboardUpdate;this.pendingKeyboardUpdate=null;const t=this.grid._focusRow,s=this.grid._focusCol;if(e&&this.cellAnchor){const e=h(this.cellAnchor,{row:t,col:s});this.ranges=[e],this.activeRange=e}else e||(this.ranges=[],this.activeRange=null,this.cellAnchor={row:t,col:s});this.emit("selection-change",this.#e())}this.#i(s),this.gridElement.setAttribute("data-selection-mode",s),t&&t.classList.toggle("selecting",this.isDragging),this.#l()}onScrollRender(){this.isSelectionEnabled()&&this.#l()}getSelection(){return{mode:this.config.mode,ranges:this.#e().ranges,anchor:this.cellAnchor}}getSelectedCells(){return function(e){const t=new Map;for(const s of e)for(const e of d(s))t.set(`${e.row},${e.col}`,e);return[...t.values()]}(this.ranges)}isCellSelected(e,t){return a(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.#e()),this.requestAfterRender()}else if("range"===e){const e=this.rows.length,t=this.columns.length;if(e>0&&t>0){const s={startRow:0,startCol:0,endRow:e-1,endCol:t-1};this.ranges=[s],this.activeRange=s,this.emit("selection-change",this.#e()),this.requestAfterRender()}}}selectRows(e){if("row"!==this.config.mode)return;const t=!1===this.config.multiSelect&&e.length>1?[e[e.length-1]]:e;this.selected.clear();for(const s of t)s>=0&&s<this.rows.length&&this.isRowSelectable(s)&&this.selected.add(s);this.anchor=t.length>0?t[t.length-1]:null,this.explicitSelection=!0,this.emit("selection-change",this.#e()),this.requestAfterRender()}getSelectedRowIndices(){return[...this.selected].sort((e,t)=>e-t)}getSelectedRows(){const{mode:e}=this.config,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 s of this.ranges){const i=Math.max(0,Math.min(s.startRow,s.endRow)),l=Math.min(t.length-1,Math.max(s.startRow,s.endRow));for(let t=i;t<=l;t++)e.add(t)}return[...e].sort((e,t)=>e-t).map(e=>t[e])}return[]}clearSelection(){this.selectedCell=null,this.selected.clear(),this.anchor=null,this.ranges=[],this.activeRange=null,this.cellAnchor=null,this.emit("selection-change",{mode:this.config.mode,ranges:[]}),this.requestAfterRender()}setRanges(e){this.ranges=e.map(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",{mode:this.config.mode,ranges:c(this.ranges)}),this.requestAfterRender()}#e(){const e=function(e,t,s){if("cell"===e&&t.selectedCell)return{mode:e,ranges:[{from:{row:t.selectedCell.row,col:t.selectedCell.col},to:{row:t.selectedCell.row,col:t.selectedCell.col}}]};if("row"===e&&t.selected.size>0){const i=[...t.selected].sort((e,t)=>e-t),l=[];let r=i[0],o=r;for(let e=1;e<i.length;e++)i[e]===o+1?o=i[e]:(l.push({from:{row:r,col:0},to:{row:o,col:s-1}}),r=i[e],o=r);return l.push({from:{row:r,col:0},to:{row:o,col:s-1}}),{mode:e,ranges:l}}return"range"===e&&t.ranges.length>0?{mode:e,ranges:c(t.ranges)}:{mode:e,ranges:[]}}(this.config.mode,{selectedCell:this.selectedCell,selected:this.selected,ranges:this.ranges},this.columns.length);return this.announceTimer&&clearTimeout(this.announceTimer),this.announceTimer=setTimeout(()=>{const t="row"===e.mode?this.selected.size:e.ranges.length;t>0&&s.announce(this.gridElement,s.getA11yMessage(this.gridElement,"selectionChanged",t))},150),e}}e.SelectionPlugin=w,Object.defineProperty(e,Symbol.toStringTag,{value:"Module"})});
2
2
  //# sourceMappingURL=selection.umd.js.map