diff --git a/dist/browser/plugins/slick.cellrangedecorator.js b/dist/browser/plugins/slick.cellrangedecorator.js index f16a6bd6..3bbb95c7 100644 --- a/dist/browser/plugins/slick.cellrangedecorator.js +++ b/dist/browser/plugins/slick.cellrangedecorator.js @@ -15,6 +15,7 @@ // protected props __publicField(this, "_options"); __publicField(this, "_elem"); + __publicField(this, "_selectionCss"); __publicField(this, "_defaults", { selectionCssClass: "slick-range-decorator", selectionCss: { @@ -23,11 +24,17 @@ }, offset: { top: -1, left: -1, height: -2, width: -2 } }); - this._options = Utils.extend(!0, {}, this._defaults, options); + this._options = Utils.extend(!0, {}, this._defaults, options), this._selectionCss = (options == null ? void 0 : options.selectionCss) || {}; } destroy() { this.hide(); } + getSelectionCss() { + return this._selectionCss; + } + setSelectionCss(cssProps) { + this._selectionCss = cssProps; + } init() { } hide() { @@ -37,8 +44,8 @@ show(range) { var _a; if (!this._elem) { - this._elem = document.createElement("div"), this._elem.className = this._options.selectionCssClass, Object.keys(this._options.selectionCss).forEach((cssStyleKey) => { - this._elem.style[cssStyleKey] = this._options.selectionCss[cssStyleKey]; + this._elem = document.createElement("div"), this._elem.className = this._options.selectionCssClass, Object.keys(this._selectionCss).forEach((cssStyleKey) => { + this._elem.style[cssStyleKey] = this._selectionCss[cssStyleKey]; }), this._elem.style.position = "absolute"; let canvasNode = this.grid.getActiveCanvasNode(); canvasNode && canvasNode.appendChild(this._elem); diff --git a/dist/browser/plugins/slick.cellrangedecorator.js.map b/dist/browser/plugins/slick.cellrangedecorator.js.map index 7da7f458..7a9eef7d 100644 --- a/dist/browser/plugins/slick.cellrangedecorator.js.map +++ b/dist/browser/plugins/slick.cellrangedecorator.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.cellrangedecorator.ts"], - "sourcesContent": ["import type { CSSStyleDeclarationWritable, CellRangeDecoratorOption, SlickPlugin } from '../models/index.js';\r\nimport { Utils as Utils_, type SlickRange } from '../slick.core.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/***\r\n * Displays an overlay on top of a given cell range.\r\n *\r\n * TODO:\r\n * Currently, it blocks mouse events to DOM nodes behind it.\r\n * Use FF and WebKit-specific \"pointer-events\" CSS style, or some kind of event forwarding.\r\n * Could also construct the borders separately using 4 individual DIVs.\r\n *\r\n * @param {Grid} grid\r\n * @param {Object} options\r\n */\r\nexport class SlickCellRangeDecorator implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellRangeDecorator' as const;\r\n\r\n // --\r\n // protected props\r\n protected _options: CellRangeDecoratorOption;\r\n protected _elem?: HTMLDivElement | null;\r\n protected _defaults = {\r\n selectionCssClass: 'slick-range-decorator',\r\n selectionCss: {\r\n zIndex: '9999',\r\n border: '2px dashed red'\r\n },\r\n offset: { top: -1, left: -1, height: -2, width: -2 }\r\n } as CellRangeDecoratorOption;\r\n\r\n constructor(protected readonly grid: SlickGrid, options?: Partial) {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n }\r\n\r\n destroy() {\r\n this.hide();\r\n }\r\n\r\n init() { }\r\n\r\n hide() {\r\n this._elem?.remove();\r\n this._elem = null;\r\n }\r\n\r\n show(range: SlickRange) {\r\n if (!this._elem) {\r\n this._elem = document.createElement('div');\r\n this._elem.className = this._options.selectionCssClass;\r\n Object.keys(this._options.selectionCss as CSSStyleDeclaration).forEach((cssStyleKey) => {\r\n this._elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this._options.selectionCss[cssStyleKey as CSSStyleDeclarationWritable];\r\n });\r\n this._elem.style.position = 'absolute';\r\n const canvasNode = this.grid.getActiveCanvasNode();\r\n if (canvasNode) {\r\n canvasNode.appendChild(this._elem);\r\n }\r\n }\r\n\r\n const from = this.grid.getCellNodeBox(range.fromRow, range.fromCell);\r\n const to = this.grid.getCellNodeBox(range.toRow, range.toCell);\r\n\r\n if (from && to && this._options?.offset) {\r\n this._elem.style.top = `${from.top + this._options.offset.top}px`;\r\n this._elem.style.left = `${from.left + this._options.offset.left}px`;\r\n this._elem.style.height = `${to.bottom - from.top + this._options.offset.height}px`;\r\n this._elem.style.width = `${to.right - from.left + this._options.offset.width}px`;\r\n }\r\n\r\n return this._elem;\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(true, window, {\r\n Slick: {\r\n CellRangeDecorator: SlickCellRangeDecorator\r\n }\r\n });\r\n}\r\n"], - "mappings": ";;;;;;;AAKA,MAAM,QAAoB,MAAM,OAanB,0BAAN,MAAqD;AAAA,IAkB1D,YAA+B,MAAiB,SAA6C;AAA9D;AAf/B;AAAA;AAAA,wCAAa;AAIb;AAAA;AAAA,0BAAU;AACV,0BAAU;AACV,0BAAU,aAAY;AAAA,QACpB,mBAAmB;AAAA,QACnB,cAAc;AAAA,UACZ,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV;AAAA,QACA,QAAQ,EAAE,KAAK,IAAI,MAAM,IAAI,QAAQ,IAAI,OAAO,GAAG;AAAA,MACrD;AAGE,WAAK,WAAW,MAAM,OAAO,IAAM,CAAC,GAAG,KAAK,WAAW,OAAO;AAAA,IAChE;AAAA,IAEA,UAAU;AACR,WAAK,KAAK;AAAA,IACZ;AAAA,IAEA,OAAO;AAAA,IAAE;AAAA,IAET,OAAO;AA9CT;AA+CI,iBAAK,UAAL,WAAY,UACZ,KAAK,QAAQ;AAAA,IACf;AAAA,IAEA,KAAK,OAAmB;AAnD1B;AAoDI,UAAI,CAAC,KAAK,OAAO;AACf,aAAK,QAAQ,SAAS,cAAc,KAAK,GACzC,KAAK,MAAM,YAAY,KAAK,SAAS,mBACrC,OAAO,KAAK,KAAK,SAAS,YAAmC,EAAE,QAAQ,CAAC,gBAAgB;AACtF,eAAK,MAAO,MAAM,WAA0C,IAAI,KAAK,SAAS,aAAa,WAA0C;AAAA,QACvI,CAAC,GACD,KAAK,MAAM,MAAM,WAAW;AAC5B,YAAM,aAAa,KAAK,KAAK,oBAAoB;AACjD,QAAI,cACF,WAAW,YAAY,KAAK,KAAK;AAAA,MAErC;AAEA,UAAM,OAAO,KAAK,KAAK,eAAe,MAAM,SAAS,MAAM,QAAQ,GAC7D,KAAK,KAAK,KAAK,eAAe,MAAM,OAAO,MAAM,MAAM;AAE7D,aAAI,QAAQ,QAAM,UAAK,aAAL,WAAe,YAC/B,KAAK,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,KAAK,SAAS,OAAO,GAAG,MAC7D,KAAK,MAAM,MAAM,OAAO,GAAG,KAAK,OAAO,KAAK,SAAS,OAAO,IAAI,MAChE,KAAK,MAAM,MAAM,SAAS,GAAG,GAAG,SAAS,KAAK,MAAM,KAAK,SAAS,OAAO,MAAM,MAC/E,KAAK,MAAM,MAAM,QAAQ,GAAG,GAAG,QAAQ,KAAK,OAAO,KAAK,SAAS,OAAO,KAAK,OAGxE,KAAK;AAAA,IACd;AAAA,EACF;AAGA,EAAiB,OAAO,SACtB,MAAM,OAAO,IAAM,QAAQ;AAAA,IACzB,OAAO;AAAA,MACL,oBAAoB;AAAA,IACtB;AAAA,EACF,CAAC;", + "sourcesContent": ["import type { CSSStyleDeclarationWritable, CellRangeDecoratorOption, SlickPlugin } from '../models/index.js';\r\nimport { Utils as Utils_, type SlickRange } from '../slick.core.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\n/***\r\n * Displays an overlay on top of a given cell range.\r\n *\r\n * TODO:\r\n * Currently, it blocks mouse events to DOM nodes behind it.\r\n * Use FF and WebKit-specific \"pointer-events\" CSS style, or some kind of event forwarding.\r\n * Could also construct the borders separately using 4 individual DIVs.\r\n *\r\n * @param {Grid} grid\r\n * @param {Object} options\r\n */\r\nexport class SlickCellRangeDecorator implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellRangeDecorator' as const;\r\n\r\n // --\r\n // protected props\r\n protected _options: CellRangeDecoratorOption;\r\n protected _elem?: HTMLDivElement | null;\r\n protected _selectionCss: CSSStyleDeclaration;\r\n protected _defaults = {\r\n selectionCssClass: 'slick-range-decorator',\r\n selectionCss: {\r\n zIndex: '9999',\r\n border: '2px dashed red'\r\n },\r\n offset: { top: -1, left: -1, height: -2, width: -2 }\r\n } as CellRangeDecoratorOption;\r\n\r\n constructor(protected readonly grid: SlickGrid, options?: Partial) {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n this._selectionCss = options?.selectionCss || {} as CSSStyleDeclaration;\r\n }\r\n\r\n destroy() {\r\n this.hide();\r\n }\r\n\r\n getSelectionCss() {\r\n return this._selectionCss;\r\n }\r\n\r\n setSelectionCss(cssProps: CSSStyleDeclaration) {\r\n this._selectionCss = cssProps;\r\n }\r\n\r\n init() { }\r\n\r\n hide() {\r\n this._elem?.remove();\r\n this._elem = null;\r\n }\r\n\r\n show(range: SlickRange) {\r\n if (!this._elem) {\r\n this._elem = document.createElement('div');\r\n this._elem.className = this._options.selectionCssClass;\r\n Object.keys(this._selectionCss as CSSStyleDeclaration).forEach((cssStyleKey) => {\r\n this._elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this._selectionCss[cssStyleKey as CSSStyleDeclarationWritable];\r\n });\r\n this._elem.style.position = 'absolute';\r\n const canvasNode = this.grid.getActiveCanvasNode();\r\n if (canvasNode) {\r\n canvasNode.appendChild(this._elem);\r\n }\r\n }\r\n\r\n const from = this.grid.getCellNodeBox(range.fromRow, range.fromCell);\r\n const to = this.grid.getCellNodeBox(range.toRow, range.toCell);\r\n\r\n if (from && to && this._options?.offset) {\r\n this._elem.style.top = `${from.top + this._options.offset.top}px`;\r\n this._elem.style.left = `${from.left + this._options.offset.left}px`;\r\n this._elem.style.height = `${to.bottom - from.top + this._options.offset.height}px`;\r\n this._elem.style.width = `${to.right - from.left + this._options.offset.width}px`;\r\n }\r\n\r\n return this._elem;\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(true, window, {\r\n Slick: {\r\n CellRangeDecorator: SlickCellRangeDecorator\r\n }\r\n });\r\n}\r\n"], + "mappings": ";;;;;;;AAKA,MAAM,QAAoB,MAAM,OAanB,0BAAN,MAAqD;AAAA,IAmB1D,YAA+B,MAAiB,SAA6C;AAA9D;AAhB/B;AAAA;AAAA,wCAAa;AAIb;AAAA;AAAA,0BAAU;AACV,0BAAU;AACV,0BAAU;AACV,0BAAU,aAAY;AAAA,QACpB,mBAAmB;AAAA,QACnB,cAAc;AAAA,UACZ,QAAQ;AAAA,UACR,QAAQ;AAAA,QACV;AAAA,QACA,QAAQ,EAAE,KAAK,IAAI,MAAM,IAAI,QAAQ,IAAI,OAAO,GAAG;AAAA,MACrD;AAGE,WAAK,WAAW,MAAM,OAAO,IAAM,CAAC,GAAG,KAAK,WAAW,OAAO,GAC9D,KAAK,iBAAgB,mCAAS,iBAAgB,CAAC;AAAA,IACjD;AAAA,IAEA,UAAU;AACR,WAAK,KAAK;AAAA,IACZ;AAAA,IAEA,kBAAkB;AAChB,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,gBAAgB,UAA+B;AAC7C,WAAK,gBAAgB;AAAA,IACvB;AAAA,IAEA,OAAO;AAAA,IAAE;AAAA,IAET,OAAO;AAxDT;AAyDI,iBAAK,UAAL,WAAY,UACZ,KAAK,QAAQ;AAAA,IACf;AAAA,IAEA,KAAK,OAAmB;AA7D1B;AA8DI,UAAI,CAAC,KAAK,OAAO;AACf,aAAK,QAAQ,SAAS,cAAc,KAAK,GACzC,KAAK,MAAM,YAAY,KAAK,SAAS,mBACrC,OAAO,KAAK,KAAK,aAAoC,EAAE,QAAQ,CAAC,gBAAgB;AAC9E,eAAK,MAAO,MAAM,WAA0C,IAAI,KAAK,cAAc,WAA0C;AAAA,QAC/H,CAAC,GACD,KAAK,MAAM,MAAM,WAAW;AAC5B,YAAM,aAAa,KAAK,KAAK,oBAAoB;AACjD,QAAI,cACF,WAAW,YAAY,KAAK,KAAK;AAAA,MAErC;AAEA,UAAM,OAAO,KAAK,KAAK,eAAe,MAAM,SAAS,MAAM,QAAQ,GAC7D,KAAK,KAAK,KAAK,eAAe,MAAM,OAAO,MAAM,MAAM;AAE7D,aAAI,QAAQ,QAAM,UAAK,aAAL,WAAe,YAC/B,KAAK,MAAM,MAAM,MAAM,GAAG,KAAK,MAAM,KAAK,SAAS,OAAO,GAAG,MAC7D,KAAK,MAAM,MAAM,OAAO,GAAG,KAAK,OAAO,KAAK,SAAS,OAAO,IAAI,MAChE,KAAK,MAAM,MAAM,SAAS,GAAG,GAAG,SAAS,KAAK,MAAM,KAAK,SAAS,OAAO,MAAM,MAC/E,KAAK,MAAM,MAAM,QAAQ,GAAG,GAAG,QAAQ,KAAK,OAAO,KAAK,SAAS,OAAO,KAAK,OAGxE,KAAK;AAAA,IACd;AAAA,EACF;AAGA,EAAiB,OAAO,SACtB,MAAM,OAAO,IAAM,QAAQ;AAAA,IACzB,OAAO;AAAA,MACL,oBAAoB;AAAA,IACtB;AAAA,EACF,CAAC;", "names": [] } diff --git a/dist/browser/plugins/slick.cellrangeselector.js b/dist/browser/plugins/slick.cellrangeselector.js index b01caaf2..f481a822 100644 --- a/dist/browser/plugins/slick.cellrangeselector.js +++ b/dist/browser/plugins/slick.cellrangeselector.js @@ -5,7 +5,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value); // src/plugins/slick.cellrangeselector.ts - var SlickEvent = Slick.Event, SlickEventHandler = Slick.EventHandler, SlickRange = Slick.Range, Draggable = Slick.Draggable, SlickCellRangeDecorator = Slick.CellRangeDecorator, Utils = Slick.Utils, SlickCellRangeSelector = class { + var SlickEvent = Slick.Event, SlickEventHandler = Slick.EventHandler, SlickRange = Slick.Range, Draggable = Slick.Draggable, SlickCellRangeDecorator = Slick.CellRangeDecorator, Utils = Slick.Utils, CellSelectionMode = Slick.CellSelectionMode, SlickCellRangeSelector = class { constructor(options) { // -- // public API @@ -24,6 +24,7 @@ __publicField(this, "_dragging", !1); __publicField(this, "_handler", new SlickEventHandler()); __publicField(this, "_options"); + __publicField(this, "_selectionMode", CellSelectionMode.Select); __publicField(this, "_defaults", { autoScroll: !0, minIntervalToShowNextCell: 30, @@ -67,6 +68,12 @@ getCellDecorator() { return this._decorator; } + getSelectionMode() { + return this._selectionMode; + } + setSelectionMode(mode) { + this._selectionMode = mode; + } handleScroll(_e, args) { this._scrollTop = args.scrollTop, this._scrollLeft = args.scrollLeft; } @@ -84,10 +91,11 @@ let canvasLeftElm = document.querySelector(`.${this._grid.getUID()} .grid-canvas-left`); canvasLeftElm && (this._columnOffset = canvasLeftElm.clientWidth || 0); } - e.stopImmediatePropagation(), e.preventDefault(); + console.log("CellRangeSelector.handleDragInit() _activeViewport is " + (this._activeViewport ? "defined" : "undefined")), e.stopImmediatePropagation(), e.preventDefault(); } handleDragStart(e, dd) { - var _a, _b; + var _a, _b, _c, _d; + console.log("CellRangeSelector.handleDragStart() _activeViewport is " + (this._activeViewport ? "defined" : "undefined")), this._activeViewport; let cell = this._grid.getCellFromEvent(e); if (cell && this.onBeforeCellRangeSelected.notify(cell).getReturnValue() !== !1 && this._grid.canCellBeSelected(cell.row, cell.cell) && (this._dragging = !0, e.stopImmediatePropagation()), !this._dragging) return; @@ -96,8 +104,8 @@ this._gridOptions.frozenColumn >= 0 && this._isRightCanvas && (startX += this._scrollLeft); let startY = dd.startY - ((_b = canvasOffset == null ? void 0 : canvasOffset.top) != null ? _b : 0); this._gridOptions.frozenRow >= 0 && this._isBottomCanvas && (startY += this._scrollTop); - let start = this._grid.getCellFromPoint(startX, startY); - return dd.range = { start, end: {} }, this._currentlySelectedRange = dd.range, this._decorator.show(new SlickRange(start.row, start.cell)); + let start; + return this._selectionMode = CellSelectionMode.Select, dd.matchClassTag !== "dragReplaceHandle" ? start = this._grid.getCellFromPoint(startX, startY) : (start = this._grid.getActiveCell() || { row: void 0, cell: void 0 }, this._selectionMode = CellSelectionMode.Replace), dd.range = { start, end: {} }, this._currentlySelectedRange = dd.range, this._decorator.show(new SlickRange((_c = start.row) != null ? _c : 0, (_d = start.cell) != null ? _d : 0)); } handleDrag(evt, dd) { if (!this._dragging && !this._isRowMoveRegistered) @@ -110,6 +118,7 @@ } getMouseOffsetViewport(e, dd) { var _a, _b, _c, _d; + console.log("Drag.getMouseOffsetViewport() _activeViewport is " + (this._activeViewport ? "defined" : "undefined")), this._activeViewport; let targetEvent = (_b = (_a = e == null ? void 0 : e.touches) == null ? void 0 : _a[0]) != null ? _b : e, viewportLeft = this._activeViewport.scrollLeft, viewportTop = this._activeViewport.scrollTop, viewportRight = viewportLeft + this._viewportWidth, viewportBottom = viewportTop + this._viewportHeight, viewportOffset = Utils.offset(this._activeViewport), viewportOffsetLeft = (_c = viewportOffset == null ? void 0 : viewportOffset.left) != null ? _c : 0, viewportOffsetTop = (_d = viewportOffset == null ? void 0 : viewportOffset.top) != null ? _d : 0, viewportOffsetRight = viewportOffsetLeft + this._viewportWidth, viewportOffsetBottom = viewportOffsetTop + this._viewportHeight, result = { e, dd, @@ -185,8 +194,9 @@ (_b = dd.range.start.cell) != null ? _b : 0, dd.range.end.row, dd.range.end.cell - ) - })); + ), + selectionMode: this._selectionMode + }), console.log("handleDragEnd")); } getCurrentRange() { return this._currentlySelectedRange; diff --git a/dist/browser/plugins/slick.cellrangeselector.js.map b/dist/browser/plugins/slick.cellrangeselector.js.map index 683575af..3e2dfd78 100644 --- a/dist/browser/plugins/slick.cellrangeselector.js.map +++ b/dist/browser/plugins/slick.cellrangeselector.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.cellrangeselector.ts"], - "sourcesContent": ["import { SlickEvent as SlickEvent_, type SlickEventData, SlickEventHandler as SlickEventHandler_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js';\r\nimport { Draggable as Draggable_ } from '../slick.interactions.js';\r\nimport { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js';\r\nimport type { CellRangeSelectorOption, DragPosition, DragRange, DragRowMove, GridOption, MouseOffsetViewport, OnScrollEventArgs, SlickPlugin } from '../models/index.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_;\r\nconst SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport class SlickCellRangeSelector implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellRangeSelector' as const;\r\n onBeforeCellRangeSelected = new SlickEvent<{ row: number; cell: number; }>('onBeforeCellRangeSelected');\r\n onCellRangeSelected = new SlickEvent<{ range: SlickRange_; }>('onCellRangeSelected');\r\n onCellRangeSelecting = new SlickEvent<{ range: SlickRange_; }>('onCellRangeSelecting');\r\n\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _currentlySelectedRange: DragRange | null = null;\r\n protected _canvas: HTMLElement | null = null;\r\n protected _decorator!: SlickCellRangeDecorator_;\r\n protected _gridOptions!: GridOption;\r\n protected _activeCanvas!: HTMLElement;\r\n protected _dragging = false;\r\n protected _handler = new SlickEventHandler();\r\n protected _options: CellRangeSelectorOption;\r\n protected _defaults = {\r\n autoScroll: true,\r\n minIntervalToShowNextCell: 30,\r\n maxIntervalToShowNextCell: 600, // better to a multiple of minIntervalToShowNextCell\r\n accelerateInterval: 5, // increase 5ms when cursor 1px outside the viewport.\r\n selectionCss: {\r\n border: '2px dashed blue'\r\n }\r\n } as CellRangeSelectorOption;\r\n\r\n // Frozen row & column variables\r\n protected _rowOffset = 0;\r\n protected _columnOffset = 0;\r\n protected _isRightCanvas = false;\r\n protected _isBottomCanvas = false;\r\n\r\n // autoScroll related constiables\r\n protected _activeViewport!: HTMLElement;\r\n protected _autoScrollTimerId?: number;\r\n protected _draggingMouseOffset!: MouseOffsetViewport;\r\n protected _moveDistanceForOneCell!: { x: number; y: number; };\r\n protected _xDelayForNextCell = 0;\r\n protected _yDelayForNextCell = 0;\r\n protected _viewportHeight = 0;\r\n protected _viewportWidth = 0;\r\n protected _isRowMoveRegistered = false;\r\n\r\n // Scrollings\r\n protected _scrollLeft = 0;\r\n protected _scrollTop = 0;\r\n\r\n constructor(options?: Partial) {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n if (Draggable === undefined) {\r\n throw new Error('Slick.Draggable is undefined, make sure to import \"slick.interactions.js\"');\r\n }\r\n\r\n this._decorator = this._options.cellDecorator || new SlickCellRangeDecorator(grid, this._options);\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this._canvas = this._grid.getCanvasNode();\r\n this._gridOptions = this._grid.getOptions();\r\n this._handler\r\n .subscribe(this._grid.onScroll, this.handleScroll.bind(this))\r\n .subscribe(this._grid.onDragInit, this.handleDragInit.bind(this))\r\n .subscribe(this._grid.onDragStart, this.handleDragStart.bind(this))\r\n .subscribe(this._grid.onDrag, this.handleDrag.bind(this))\r\n .subscribe(this._grid.onDragEnd, this.handleDragEnd.bind(this));\r\n }\r\n\r\n destroy() {\r\n this._handler.unsubscribeAll();\r\n this._activeCanvas = null as any;\r\n this._activeViewport = null as any;\r\n this._canvas = null;\r\n this._decorator?.destroy();\r\n }\r\n\r\n getCellDecorator() {\r\n return this._decorator;\r\n }\r\n\r\n protected handleScroll(_e: SlickEventData, args: OnScrollEventArgs) {\r\n this._scrollTop = args.scrollTop;\r\n this._scrollLeft = args.scrollLeft;\r\n }\r\n\r\n protected handleDragInit(e: SlickEventData) {\r\n // Set the active canvas node because the decorator needs to append its\r\n // box to the correct canvas\r\n this._activeCanvas = this._grid.getActiveCanvasNode(e);\r\n this._activeViewport = this._grid.getActiveViewportNode(e);\r\n\r\n const scrollbarDimensions = this._grid.getDisplayedScrollbarDimensions();\r\n this._viewportWidth = this._activeViewport.offsetWidth - scrollbarDimensions.width;\r\n this._viewportHeight = this._activeViewport.offsetHeight - scrollbarDimensions.height;\r\n\r\n this._moveDistanceForOneCell = {\r\n x: this._grid.getAbsoluteColumnMinWidth() / 2,\r\n y: this._grid.getOptions().rowHeight! / 2\r\n };\r\n this._isRowMoveRegistered = this.hasRowMoveManager();\r\n\r\n this._rowOffset = 0;\r\n this._columnOffset = 0;\r\n this._isBottomCanvas = this._activeCanvas.classList.contains('grid-canvas-bottom');\r\n\r\n if (this._gridOptions.frozenRow! > -1 && this._isBottomCanvas) {\r\n const canvasSelector = `.${this._grid.getUID()} .grid-canvas-${this._gridOptions.frozenBottom ? 'bottom' : 'top'}`;\r\n const canvasElm = document.querySelector(canvasSelector);\r\n if (canvasElm) {\r\n this._rowOffset = canvasElm.clientHeight || 0;\r\n }\r\n }\r\n\r\n this._isRightCanvas = this._activeCanvas.classList.contains('grid-canvas-right');\r\n\r\n if (this._gridOptions.frozenColumn! > -1 && this._isRightCanvas) {\r\n const canvasLeftElm = document.querySelector(`.${this._grid.getUID()} .grid-canvas-left`);\r\n if (canvasLeftElm) {\r\n this._columnOffset = canvasLeftElm.clientWidth || 0;\r\n }\r\n }\r\n\r\n // prevent the grid from cancelling drag'n'drop by default\r\n e.stopImmediatePropagation();\r\n e.preventDefault();\r\n }\r\n\r\n protected handleDragStart(e: SlickEventData, dd: DragRowMove) {\r\n const cell = this._grid.getCellFromEvent(e);\r\n if (cell && this.onBeforeCellRangeSelected.notify(cell).getReturnValue() !== false && this._grid.canCellBeSelected(cell.row, cell.cell)) {\r\n this._dragging = true;\r\n e.stopImmediatePropagation();\r\n }\r\n if (!this._dragging) {\r\n return;\r\n }\r\n\r\n this._grid.focus();\r\n\r\n const canvasOffset = Utils.offset(this._canvas);\r\n\r\n let startX = dd.startX - (canvasOffset?.left ?? 0);\r\n if (this._gridOptions.frozenColumn! >= 0 && this._isRightCanvas) {\r\n startX += this._scrollLeft;\r\n }\r\n\r\n let startY = dd.startY - (canvasOffset?.top ?? 0);\r\n if (this._gridOptions.frozenRow! >= 0 && this._isBottomCanvas) {\r\n startY += this._scrollTop;\r\n }\r\n\r\n const start = this._grid.getCellFromPoint(startX, startY);\r\n\r\n dd.range = { start, end: {} };\r\n this._currentlySelectedRange = dd.range;\r\n return this._decorator.show(new SlickRange(start.row, start.cell));\r\n }\r\n\r\n protected handleDrag(evt: SlickEventData, dd: DragRowMove) {\r\n if (!this._dragging && !this._isRowMoveRegistered) {\r\n return;\r\n }\r\n if (!this._isRowMoveRegistered) {\r\n evt.stopImmediatePropagation();\r\n }\r\n\r\n const e = evt.getNativeEvent();\r\n if (this._options.autoScroll) {\r\n this._draggingMouseOffset = this.getMouseOffsetViewport(e, dd);\r\n if (this._draggingMouseOffset.isOutsideViewport) {\r\n return this.handleDragOutsideViewport();\r\n }\r\n }\r\n this.stopIntervalTimer();\r\n this.handleDragTo(e, dd);\r\n }\r\n\r\n protected getMouseOffsetViewport(e: MouseEvent | TouchEvent, dd: DragRowMove): MouseOffsetViewport {\r\n const targetEvent: MouseEvent | Touch = (e as TouchEvent)?.touches?.[0] ?? e;\r\n const viewportLeft = this._activeViewport.scrollLeft;\r\n const viewportTop = this._activeViewport.scrollTop;\r\n const viewportRight = viewportLeft + this._viewportWidth;\r\n const viewportBottom = viewportTop + this._viewportHeight;\r\n\r\n const viewportOffset = Utils.offset(this._activeViewport);\r\n const viewportOffsetLeft = viewportOffset?.left ?? 0;\r\n const viewportOffsetTop = viewportOffset?.top ?? 0;\r\n const viewportOffsetRight = viewportOffsetLeft + this._viewportWidth;\r\n const viewportOffsetBottom = viewportOffsetTop + this._viewportHeight;\r\n\r\n const result = {\r\n e,\r\n dd,\r\n viewport: {\r\n left: viewportLeft,\r\n top: viewportTop,\r\n right: viewportRight,\r\n bottom: viewportBottom,\r\n offset: {\r\n left: viewportOffsetLeft,\r\n top: viewportOffsetTop,\r\n right: viewportOffsetRight,\r\n bottom: viewportOffsetBottom\r\n }\r\n },\r\n // Consider the viewport as the origin, the `offset` is based on the coordinate system:\r\n // the cursor is on the viewport's left/bottom when it is less than 0, and on the right/top when greater than 0.\r\n offset: {\r\n x: 0,\r\n y: 0\r\n },\r\n isOutsideViewport: false\r\n };\r\n // ... horizontal\r\n if (targetEvent.pageX < viewportOffsetLeft) {\r\n result.offset.x = targetEvent.pageX - viewportOffsetLeft;\r\n } else if (targetEvent.pageX > viewportOffsetRight) {\r\n result.offset.x = targetEvent.pageX - viewportOffsetRight;\r\n }\r\n // ... vertical\r\n if (targetEvent.pageY < viewportOffsetTop) {\r\n result.offset.y = viewportOffsetTop - targetEvent.pageY;\r\n } else if (targetEvent.pageY > viewportOffsetBottom) {\r\n result.offset.y = viewportOffsetBottom - targetEvent.pageY;\r\n }\r\n result.isOutsideViewport = !!result.offset.x || !!result.offset.y;\r\n return result;\r\n }\r\n\r\n protected handleDragOutsideViewport() {\r\n this._xDelayForNextCell = this._options.maxIntervalToShowNextCell - Math.abs(this._draggingMouseOffset.offset.x) * this._options.accelerateInterval;\r\n this._yDelayForNextCell = this._options.maxIntervalToShowNextCell - Math.abs(this._draggingMouseOffset.offset.y) * this._options.accelerateInterval;\r\n // only one timer is created to handle the case that cursor outside the viewport\r\n if (!this._autoScrollTimerId) {\r\n let xTotalDelay = 0;\r\n let yTotalDelay = 0;\r\n this._autoScrollTimerId = window.setInterval(() => {\r\n let xNeedUpdate = false;\r\n let yNeedUpdate = false;\r\n // ... horizontal\r\n if (this._draggingMouseOffset.offset.x) {\r\n xTotalDelay += this._options.minIntervalToShowNextCell;\r\n xNeedUpdate = xTotalDelay >= this._xDelayForNextCell;\r\n } else {\r\n xTotalDelay = 0;\r\n }\r\n // ... vertical\r\n if (this._draggingMouseOffset.offset.y) {\r\n yTotalDelay += this._options.minIntervalToShowNextCell;\r\n yNeedUpdate = yTotalDelay >= this._yDelayForNextCell;\r\n } else {\r\n yTotalDelay = 0;\r\n }\r\n if (xNeedUpdate || yNeedUpdate) {\r\n if (xNeedUpdate) {\r\n xTotalDelay = 0;\r\n }\r\n if (yNeedUpdate) {\r\n yTotalDelay = 0;\r\n }\r\n this.handleDragToNewPosition(xNeedUpdate, yNeedUpdate);\r\n }\r\n }, this._options.minIntervalToShowNextCell);\r\n }\r\n }\r\n\r\n protected handleDragToNewPosition(xNeedUpdate: boolean, yNeedUpdate: boolean) {\r\n let pageX = this._draggingMouseOffset.e.pageX;\r\n let pageY = this._draggingMouseOffset.e.pageY;\r\n const mouseOffsetX = this._draggingMouseOffset.offset.x;\r\n const mouseOffsetY = this._draggingMouseOffset.offset.y;\r\n const viewportOffset = this._draggingMouseOffset.viewport.offset;\r\n // ... horizontal\r\n if (xNeedUpdate && mouseOffsetX) {\r\n if (mouseOffsetX > 0) {\r\n pageX = viewportOffset.right + this._moveDistanceForOneCell.x;\r\n } else {\r\n pageX = viewportOffset.left - this._moveDistanceForOneCell.x;\r\n }\r\n }\r\n // ... vertical\r\n if (yNeedUpdate && mouseOffsetY) {\r\n if (mouseOffsetY > 0) {\r\n pageY = viewportOffset.top - this._moveDistanceForOneCell.y;\r\n } else {\r\n pageY = viewportOffset.bottom + this._moveDistanceForOneCell.y;\r\n }\r\n }\r\n this.handleDragTo({ pageX, pageY }, this._draggingMouseOffset.dd);\r\n }\r\n\r\n protected stopIntervalTimer() {\r\n if (this._autoScrollTimerId) {\r\n window.clearInterval(this._autoScrollTimerId);\r\n this._autoScrollTimerId = undefined;\r\n }\r\n }\r\n\r\n protected handleDragTo(e: { pageX: number; pageY: number; }, dd: DragPosition) {\r\n const targetEvent: MouseEvent | Touch = (e as unknown as TouchEvent)?.touches?.[0] ?? e;\r\n const canvasOffset = Utils.offset(this._activeCanvas);\r\n const end = this._grid.getCellFromPoint(\r\n targetEvent.pageX - (canvasOffset?.left ?? 0) + this._columnOffset,\r\n targetEvent.pageY - (canvasOffset?.top ?? 0) + this._rowOffset\r\n );\r\n\r\n // ... frozen column(s),\r\n if (this._gridOptions.frozenColumn! >= 0 && (!this._isRightCanvas && (end.cell > this._gridOptions.frozenColumn!)) || (this._isRightCanvas && (end.cell <= this._gridOptions.frozenColumn!))) {\r\n return;\r\n }\r\n\r\n // ... or frozen row(s)\r\n if (this._gridOptions.frozenRow! >= 0 && (!this._isBottomCanvas && (end.row >= this._gridOptions.frozenRow!)) || (this._isBottomCanvas && (end.row < this._gridOptions.frozenRow!))) {\r\n return;\r\n }\r\n\r\n // scrolling the viewport to display the target `end` cell if it is not fully displayed\r\n if (this._options.autoScroll && this._draggingMouseOffset) {\r\n const endCellBox = this._grid.getCellNodeBox(end.row, end.cell);\r\n if (!endCellBox) {\r\n return;\r\n }\r\n const viewport = this._draggingMouseOffset.viewport;\r\n if (endCellBox.left < viewport.left || endCellBox.right > viewport.right\r\n || endCellBox.top < viewport.top || endCellBox.bottom > viewport.bottom) {\r\n this._grid.scrollCellIntoView(end.row, end.cell);\r\n }\r\n }\r\n\r\n // ... or regular grid (without any frozen options)\r\n if (!this._grid.canCellBeSelected(end.row, end.cell)) {\r\n return;\r\n }\r\n\r\n if (dd?.range) {\r\n dd.range.end = end;\r\n\r\n const range = new SlickRange(dd.range.start.row ?? 0, dd.range.start.cell ?? 0, end.row, end.cell);\r\n this._decorator.show(range);\r\n this.onCellRangeSelecting.notify({\r\n range\r\n });\r\n }\r\n }\r\n\r\n protected hasRowMoveManager() {\r\n return !!(this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager'));\r\n }\r\n\r\n protected handleDragEnd(e: SlickEventData, dd: DragPosition) {\r\n this._decorator.hide();\r\n if (!this._dragging) {\r\n return;\r\n }\r\n\r\n this._dragging = false;\r\n e.stopImmediatePropagation();\r\n\r\n this.stopIntervalTimer();\r\n this.onCellRangeSelected.notify({\r\n range: new SlickRange(\r\n dd.range.start.row ?? 0,\r\n dd.range.start.cell ?? 0,\r\n dd.range.end.row,\r\n dd.range.end.cell\r\n )\r\n });\r\n }\r\n\r\n getCurrentRange() {\r\n return this._currentlySelectedRange;\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(Slick, {\r\n CellRangeSelector: SlickCellRangeSelector\r\n });\r\n}\r\n"], - "mappings": "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", + "sourcesContent": ["import { CellSelectionMode as CellSelectionMode_, SlickEvent as SlickEvent_, type SlickEventData, SlickEventHandler as SlickEventHandler_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js';\r\nimport { Draggable as Draggable_ } from '../slick.interactions.js';\r\nimport { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js';\r\nimport type { CellRangeSelectorOption, DragPosition, DragRange, DragRowMove, GridOption, MouseOffsetViewport, OnScrollEventArgs, SlickPlugin } from '../models/index.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_;\r\nconst SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\nconst CellSelectionMode = IIFE_ONLY ? Slick.CellSelectionMode : CellSelectionMode_;\r\n\r\nexport class SlickCellRangeSelector implements SlickPlugin {\r\n // --\r\n // public API\r\n pluginName = 'CellRangeSelector' as const;\r\n onBeforeCellRangeSelected = new SlickEvent<{ row: number; cell: number; }>('onBeforeCellRangeSelected');\r\n onCellRangeSelected = new SlickEvent<{ range: SlickRange_; selectionMode?: string; }>('onCellRangeSelected');\r\n onCellRangeSelecting = new SlickEvent<{ range: SlickRange_; selectionMode?: string; }>('onCellRangeSelecting');\r\n\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _currentlySelectedRange: DragRange | null = null;\r\n protected _canvas: HTMLElement | null = null;\r\n protected _decorator!: SlickCellRangeDecorator_;\r\n protected _gridOptions!: GridOption;\r\n protected _activeCanvas!: HTMLElement;\r\n protected _dragging = false;\r\n protected _handler = new SlickEventHandler();\r\n protected _options: CellRangeSelectorOption;\r\n protected _selectionMode: string = CellSelectionMode.Select;\r\n protected _defaults = {\r\n autoScroll: true,\r\n minIntervalToShowNextCell: 30,\r\n maxIntervalToShowNextCell: 600, // better to a multiple of minIntervalToShowNextCell\r\n accelerateInterval: 5, // increase 5ms when cursor 1px outside the viewport.\r\n selectionCss: {\r\n border: '2px dashed blue'\r\n }\r\n } as CellRangeSelectorOption;\r\n\r\n // Frozen row & column variables\r\n protected _rowOffset = 0;\r\n protected _columnOffset = 0;\r\n protected _isRightCanvas = false;\r\n protected _isBottomCanvas = false;\r\n\r\n // autoScroll related constiables\r\n protected _activeViewport!: HTMLElement;\r\n protected _autoScrollTimerId?: number;\r\n protected _draggingMouseOffset!: MouseOffsetViewport;\r\n protected _moveDistanceForOneCell!: { x: number; y: number; };\r\n protected _xDelayForNextCell = 0;\r\n protected _yDelayForNextCell = 0;\r\n protected _viewportHeight = 0;\r\n protected _viewportWidth = 0;\r\n protected _isRowMoveRegistered = false;\r\n\r\n // Scrollings\r\n protected _scrollLeft = 0;\r\n protected _scrollTop = 0;\r\n\r\n constructor(options?: Partial) {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n if (Draggable === undefined) {\r\n throw new Error('Slick.Draggable is undefined, make sure to import \"slick.interactions.js\"');\r\n }\r\n\r\n this._decorator = this._options.cellDecorator || new SlickCellRangeDecorator(grid, this._options);\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n this._canvas = this._grid.getCanvasNode();\r\n this._gridOptions = this._grid.getOptions();\r\n this._handler\r\n .subscribe(this._grid.onScroll, this.handleScroll.bind(this))\r\n .subscribe(this._grid.onDragInit, this.handleDragInit.bind(this))\r\n .subscribe(this._grid.onDragStart, this.handleDragStart.bind(this))\r\n .subscribe(this._grid.onDrag, this.handleDrag.bind(this))\r\n .subscribe(this._grid.onDragEnd, this.handleDragEnd.bind(this));\r\n }\r\n\r\n destroy() {\r\n this._handler.unsubscribeAll();\r\n this._activeCanvas = null as any;\r\n this._activeViewport = null as any;\r\n this._canvas = null;\r\n this._decorator?.destroy();\r\n }\r\n\r\n getCellDecorator() {\r\n return this._decorator;\r\n }\r\n\r\n getSelectionMode() {\r\n return this._selectionMode;\r\n }\r\n\r\n setSelectionMode(mode: string) {\r\n this._selectionMode = mode;\r\n }\r\n\r\n protected handleScroll(_e: SlickEventData, args: OnScrollEventArgs) {\r\n this._scrollTop = args.scrollTop;\r\n this._scrollLeft = args.scrollLeft;\r\n }\r\n\r\n protected handleDragInit(e: SlickEventData) {\r\n // Set the active canvas node because the decorator needs to append its\r\n // box to the correct canvas\r\n this._activeCanvas = this._grid.getActiveCanvasNode(e);\r\n this._activeViewport = this._grid.getActiveViewportNode(e);\r\n\r\n const scrollbarDimensions = this._grid.getDisplayedScrollbarDimensions();\r\n this._viewportWidth = this._activeViewport.offsetWidth - scrollbarDimensions.width;\r\n this._viewportHeight = this._activeViewport.offsetHeight - scrollbarDimensions.height;\r\n\r\n this._moveDistanceForOneCell = {\r\n x: this._grid.getAbsoluteColumnMinWidth() / 2,\r\n y: this._grid.getOptions().rowHeight! / 2\r\n };\r\n this._isRowMoveRegistered = this.hasRowMoveManager();\r\n\r\n this._rowOffset = 0;\r\n this._columnOffset = 0;\r\n this._isBottomCanvas = this._activeCanvas.classList.contains('grid-canvas-bottom');\r\n\r\n if (this._gridOptions.frozenRow! > -1 && this._isBottomCanvas) {\r\n const canvasSelector = `.${this._grid.getUID()} .grid-canvas-${this._gridOptions.frozenBottom ? 'bottom' : 'top'}`;\r\n const canvasElm = document.querySelector(canvasSelector);\r\n if (canvasElm) {\r\n this._rowOffset = canvasElm.clientHeight || 0;\r\n }\r\n }\r\n\r\n this._isRightCanvas = this._activeCanvas.classList.contains('grid-canvas-right');\r\n\r\n if (this._gridOptions.frozenColumn! > -1 && this._isRightCanvas) {\r\n const canvasLeftElm = document.querySelector(`.${this._grid.getUID()} .grid-canvas-left`);\r\n if (canvasLeftElm) {\r\n this._columnOffset = canvasLeftElm.clientWidth || 0;\r\n }\r\n }\r\n\r\n console.log('CellRangeSelector.handleDragInit() _activeViewport is ' + (this._activeViewport ? 'defined' : 'undefined'));\r\n\r\n // prevent the grid from cancelling drag'n'drop by default\r\n e.stopImmediatePropagation();\r\n e.preventDefault();\r\n }\r\n\r\n protected handleDragStart(e: SlickEventData, dd: DragRowMove) {\r\n console.log('CellRangeSelector.handleDragStart() _activeViewport is ' + (this._activeViewport ? 'defined' : 'undefined'));\r\n if (!this._activeViewport) {\r\n // const x = 1;\r\n }\r\n\r\n const cell = this._grid.getCellFromEvent(e);\r\n if (cell && this.onBeforeCellRangeSelected.notify(cell).getReturnValue() !== false && this._grid.canCellBeSelected(cell.row, cell.cell)) {\r\n this._dragging = true;\r\n e.stopImmediatePropagation();\r\n }\r\n if (!this._dragging) {\r\n return;\r\n }\r\n\r\n this._grid.focus();\r\n\r\n const canvasOffset = Utils.offset(this._canvas);\r\n\r\n let startX = dd.startX - (canvasOffset?.left ?? 0);\r\n if (this._gridOptions.frozenColumn! >= 0 && this._isRightCanvas) {\r\n startX += this._scrollLeft;\r\n }\r\n\r\n let startY = dd.startY - (canvasOffset?.top ?? 0);\r\n if (this._gridOptions.frozenRow! >= 0 && this._isBottomCanvas) {\r\n startY += this._scrollTop;\r\n }\r\n\r\n let start: { row: number | undefined, cell: number | undefined; } | null;\r\n this._selectionMode = CellSelectionMode.Select;\r\n if (dd.matchClassTag !== 'dragReplaceHandle') {\r\n start = this._grid.getCellFromPoint(startX, startY);\r\n } else {\r\n start = this._grid.getActiveCell() || { row: undefined, cell: undefined };\r\n this._selectionMode = CellSelectionMode.Replace;\r\n }\r\n\r\n dd.range = { start, end: {} };\r\n this._currentlySelectedRange = dd.range;\r\n return this._decorator.show(new SlickRange(start.row ?? 0, start.cell ?? 0));\r\n }\r\n\r\n protected handleDrag(evt: SlickEventData, dd: DragRowMove) {\r\n if (!this._dragging && !this._isRowMoveRegistered) {\r\n return;\r\n }\r\n if (!this._isRowMoveRegistered) {\r\n evt.stopImmediatePropagation();\r\n }\r\n\r\n const e = evt.getNativeEvent();\r\n if (this._options.autoScroll) {\r\n this._draggingMouseOffset = this.getMouseOffsetViewport(e, dd);\r\n if (this._draggingMouseOffset.isOutsideViewport) {\r\n return this.handleDragOutsideViewport();\r\n }\r\n }\r\n this.stopIntervalTimer();\r\n this.handleDragTo(e, dd);\r\n }\r\n\r\n protected getMouseOffsetViewport(e: MouseEvent | TouchEvent, dd: DragRowMove): MouseOffsetViewport {\r\n console.log('Drag.getMouseOffsetViewport() _activeViewport is ' + (this._activeViewport ? 'defined' : 'undefined'));\r\n if (!this._activeViewport) {\r\n // const x = 1;\r\n }\r\n\r\n const targetEvent: MouseEvent | Touch = (e as TouchEvent)?.touches?.[0] ?? e;\r\n const viewportLeft = this._activeViewport.scrollLeft;\r\n const viewportTop = this._activeViewport.scrollTop;\r\n const viewportRight = viewportLeft + this._viewportWidth;\r\n const viewportBottom = viewportTop + this._viewportHeight;\r\n\r\n const viewportOffset = Utils.offset(this._activeViewport);\r\n const viewportOffsetLeft = viewportOffset?.left ?? 0;\r\n const viewportOffsetTop = viewportOffset?.top ?? 0;\r\n const viewportOffsetRight = viewportOffsetLeft + this._viewportWidth;\r\n const viewportOffsetBottom = viewportOffsetTop + this._viewportHeight;\r\n\r\n const result = {\r\n e,\r\n dd,\r\n viewport: {\r\n left: viewportLeft,\r\n top: viewportTop,\r\n right: viewportRight,\r\n bottom: viewportBottom,\r\n offset: {\r\n left: viewportOffsetLeft,\r\n top: viewportOffsetTop,\r\n right: viewportOffsetRight,\r\n bottom: viewportOffsetBottom\r\n }\r\n },\r\n // Consider the viewport as the origin, the `offset` is based on the coordinate system:\r\n // the cursor is on the viewport's left/bottom when it is less than 0, and on the right/top when greater than 0.\r\n offset: {\r\n x: 0,\r\n y: 0\r\n },\r\n isOutsideViewport: false\r\n };\r\n // ... horizontal\r\n if (targetEvent.pageX < viewportOffsetLeft) {\r\n result.offset.x = targetEvent.pageX - viewportOffsetLeft;\r\n } else if (targetEvent.pageX > viewportOffsetRight) {\r\n result.offset.x = targetEvent.pageX - viewportOffsetRight;\r\n }\r\n // ... vertical\r\n if (targetEvent.pageY < viewportOffsetTop) {\r\n result.offset.y = viewportOffsetTop - targetEvent.pageY;\r\n } else if (targetEvent.pageY > viewportOffsetBottom) {\r\n result.offset.y = viewportOffsetBottom - targetEvent.pageY;\r\n }\r\n result.isOutsideViewport = !!result.offset.x || !!result.offset.y;\r\n return result;\r\n }\r\n\r\n protected handleDragOutsideViewport() {\r\n this._xDelayForNextCell = this._options.maxIntervalToShowNextCell - Math.abs(this._draggingMouseOffset.offset.x) * this._options.accelerateInterval;\r\n this._yDelayForNextCell = this._options.maxIntervalToShowNextCell - Math.abs(this._draggingMouseOffset.offset.y) * this._options.accelerateInterval;\r\n // only one timer is created to handle the case that cursor outside the viewport\r\n if (!this._autoScrollTimerId) {\r\n let xTotalDelay = 0;\r\n let yTotalDelay = 0;\r\n this._autoScrollTimerId = window.setInterval(() => {\r\n let xNeedUpdate = false;\r\n let yNeedUpdate = false;\r\n // ... horizontal\r\n if (this._draggingMouseOffset.offset.x) {\r\n xTotalDelay += this._options.minIntervalToShowNextCell;\r\n xNeedUpdate = xTotalDelay >= this._xDelayForNextCell;\r\n } else {\r\n xTotalDelay = 0;\r\n }\r\n // ... vertical\r\n if (this._draggingMouseOffset.offset.y) {\r\n yTotalDelay += this._options.minIntervalToShowNextCell;\r\n yNeedUpdate = yTotalDelay >= this._yDelayForNextCell;\r\n } else {\r\n yTotalDelay = 0;\r\n }\r\n if (xNeedUpdate || yNeedUpdate) {\r\n if (xNeedUpdate) {\r\n xTotalDelay = 0;\r\n }\r\n if (yNeedUpdate) {\r\n yTotalDelay = 0;\r\n }\r\n this.handleDragToNewPosition(xNeedUpdate, yNeedUpdate);\r\n }\r\n }, this._options.minIntervalToShowNextCell);\r\n }\r\n }\r\n\r\n protected handleDragToNewPosition(xNeedUpdate: boolean, yNeedUpdate: boolean) {\r\n let pageX = this._draggingMouseOffset.e.pageX;\r\n let pageY = this._draggingMouseOffset.e.pageY;\r\n const mouseOffsetX = this._draggingMouseOffset.offset.x;\r\n const mouseOffsetY = this._draggingMouseOffset.offset.y;\r\n const viewportOffset = this._draggingMouseOffset.viewport.offset;\r\n // ... horizontal\r\n if (xNeedUpdate && mouseOffsetX) {\r\n if (mouseOffsetX > 0) {\r\n pageX = viewportOffset.right + this._moveDistanceForOneCell.x;\r\n } else {\r\n pageX = viewportOffset.left - this._moveDistanceForOneCell.x;\r\n }\r\n }\r\n // ... vertical\r\n if (yNeedUpdate && mouseOffsetY) {\r\n if (mouseOffsetY > 0) {\r\n pageY = viewportOffset.top - this._moveDistanceForOneCell.y;\r\n } else {\r\n pageY = viewportOffset.bottom + this._moveDistanceForOneCell.y;\r\n }\r\n }\r\n this.handleDragTo({ pageX, pageY }, this._draggingMouseOffset.dd);\r\n }\r\n\r\n protected stopIntervalTimer() {\r\n if (this._autoScrollTimerId) {\r\n window.clearInterval(this._autoScrollTimerId);\r\n this._autoScrollTimerId = undefined;\r\n }\r\n }\r\n\r\n protected handleDragTo(e: { pageX: number; pageY: number; }, dd: DragPosition) {\r\n const targetEvent: MouseEvent | Touch = (e as unknown as TouchEvent)?.touches?.[0] ?? e;\r\n const canvasOffset = Utils.offset(this._activeCanvas);\r\n const end = this._grid.getCellFromPoint(\r\n targetEvent.pageX - (canvasOffset?.left ?? 0) + this._columnOffset,\r\n targetEvent.pageY - (canvasOffset?.top ?? 0) + this._rowOffset\r\n );\r\n\r\n // ... frozen column(s),\r\n if (this._gridOptions.frozenColumn! >= 0 && (!this._isRightCanvas && (end.cell > this._gridOptions.frozenColumn!)) || (this._isRightCanvas && (end.cell <= this._gridOptions.frozenColumn!))) {\r\n return;\r\n }\r\n\r\n // ... or frozen row(s)\r\n if (this._gridOptions.frozenRow! >= 0 && (!this._isBottomCanvas && (end.row >= this._gridOptions.frozenRow!)) || (this._isBottomCanvas && (end.row < this._gridOptions.frozenRow!))) {\r\n return;\r\n }\r\n\r\n // scrolling the viewport to display the target `end` cell if it is not fully displayed\r\n if (this._options.autoScroll && this._draggingMouseOffset) {\r\n const endCellBox = this._grid.getCellNodeBox(end.row, end.cell);\r\n if (!endCellBox) {\r\n return;\r\n }\r\n const viewport = this._draggingMouseOffset.viewport;\r\n if (endCellBox.left < viewport.left || endCellBox.right > viewport.right\r\n || endCellBox.top < viewport.top || endCellBox.bottom > viewport.bottom) {\r\n this._grid.scrollCellIntoView(end.row, end.cell);\r\n }\r\n }\r\n\r\n // ... or regular grid (without any frozen options)\r\n if (!this._grid.canCellBeSelected(end.row, end.cell)) {\r\n return;\r\n }\r\n\r\n if (dd?.range) {\r\n dd.range.end = end;\r\n\r\n const range = new SlickRange(dd.range.start.row ?? 0, dd.range.start.cell ?? 0, end.row, end.cell);\r\n this._decorator.show(range);\r\n this.onCellRangeSelecting.notify({\r\n range,\r\n });\r\n }\r\n }\r\n\r\n protected hasRowMoveManager() {\r\n return !!(this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager'));\r\n }\r\n\r\n protected handleDragEnd(e: SlickEventData, dd: DragPosition) {\r\n this._decorator.hide();\r\n if (!this._dragging) {\r\n return;\r\n }\r\n\r\n this._dragging = false;\r\n e.stopImmediatePropagation();\r\n\r\n this.stopIntervalTimer();\r\n this.onCellRangeSelected.notify({\r\n range: new SlickRange(\r\n dd.range.start.row ?? 0,\r\n dd.range.start.cell ?? 0,\r\n dd.range.end.row,\r\n dd.range.end.cell\r\n ),\r\n selectionMode: this._selectionMode\r\n });\r\n console.log('handleDragEnd');\r\n }\r\n\r\n getCurrentRange() {\r\n return this._currentlySelectedRange;\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(Slick, {\r\n CellRangeSelector: SlickCellRangeSelector\r\n });\r\n}\r\n"], + "mappings": "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", "names": [] } diff --git a/dist/browser/plugins/slick.cellselectionmodel.js b/dist/browser/plugins/slick.cellselectionmodel.js index d1384277..e8c92f10 100644 --- a/dist/browser/plugins/slick.cellselectionmodel.js +++ b/dist/browser/plugins/slick.cellselectionmodel.js @@ -56,12 +56,12 @@ resetPageRowCount() { this._cachedPageRowCount = 0; } - setSelectedRanges(ranges, caller = "SlickCellSelectionModel.setSelectedRanges") { + setSelectedRanges(ranges, caller = "SlickCellSelectionModel.setSelectedRanges", selectionMode) { if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) return; let rangeHasChanged = !this.rangesAreEqual(this._ranges, ranges); if (this._ranges = this.removeInvalidRanges(ranges), rangeHasChanged) { - let eventData = new SlickEventData(new CustomEvent("click", { detail: { caller } }), this._ranges); + let eventData = new SlickEventData(new CustomEvent("click", { detail: { caller, selectionMode } }), this._ranges); this.onSelectedRangesChanged.notify(this._ranges, eventData); } } @@ -69,20 +69,20 @@ return this._ranges; } refreshSelections() { - this.setSelectedRanges(this.getSelectedRanges()); + this.setSelectedRanges(this.getSelectedRanges(), void 0, ""); } handleBeforeCellRangeSelected(e) { if (this._grid.getEditorLock().isActive()) return e.stopPropagation(), !1; } handleCellRangeSelected(_e, args) { - this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, !1, !1, !0), this.setSelectedRanges([args.range]); + this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, !1, !1, !0), this.setSelectedRanges([args.range], void 0, args.selectionMode); } handleActiveCellChange(_e, args) { var _a, _b; this._prevSelectedRow = void 0; let isCellDefined = Utils.isDefined(args.cell), isRowDefined = Utils.isDefined(args.row); - (_a = this._options) != null && _a.selectActiveCell && isRowDefined && isCellDefined ? this.setSelectedRanges([new SlickRange(args.row, args.cell)]) : (!((_b = this._options) != null && _b.selectActiveCell) || !isRowDefined && !isCellDefined) && this.setSelectedRanges([]); + (_a = this._options) != null && _a.selectActiveCell && isRowDefined && isCellDefined ? this.setSelectedRanges([new SlickRange(args.row, args.cell)], void 0, "") : (!((_b = this._options) != null && _b.selectActiveCell) || !isRowDefined && !isCellDefined) && this.setSelectedRanges([], void 0, ""); } isKeyAllowed(key, isShiftKeyPressed) { return [ @@ -113,7 +113,7 @@ isSingleKeyMove ? (this._grid.scrollRowIntoView(viewRow), this._grid.scrollCellIntoView(viewRow, viewCell)) : (this._grid.scrollRowIntoView(toRow), this._grid.scrollCellIntoView(toRow, viewCell)); } else ranges.push(last); - this.setSelectedRanges(ranges), e.preventDefault(), e.stopPropagation(), this._prevKeyDown = e.key; + this.setSelectedRanges(ranges, void 0, ""), e.preventDefault(), e.stopPropagation(), this._prevKeyDown = e.key; } } }; diff --git a/dist/browser/plugins/slick.cellselectionmodel.js.map b/dist/browser/plugins/slick.cellselectionmodel.js.map index ca95970a..41cac9e9 100644 --- a/dist/browser/plugins/slick.cellselectionmodel.js.map +++ b/dist/browser/plugins/slick.cellselectionmodel.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.cellselectionmodel.ts"], - "sourcesContent": ["import { SlickEvent as SlickEvent_, SlickEventData as SlickEventData_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js';\r\nimport { SlickCellRangeSelector as SlickCellRangeSelector_ } from './slick.cellrangeselector.js';\r\nimport type { CustomDataView, OnActiveCellChangedEventArgs } from '../models/index.js';\r\nimport type { SlickDataView } from '../slick.dataview.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst SlickCellRangeSelector = IIFE_ONLY ? Slick.CellRangeSelector : SlickCellRangeSelector_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport interface CellSelectionModelOption {\r\n selectActiveCell: boolean;\r\n cellRangeSelector?: SlickCellRangeSelector_;\r\n}\r\n\r\nexport class SlickCellSelectionModel {\r\n // --\r\n // public API\r\n pluginName = 'CellSelectionModel' as const;\r\n onSelectedRangesChanged = new SlickEvent('onSelectedRangesChanged');\r\n\r\n // --\r\n // protected props\r\n protected _cachedPageRowCount = 0;\r\n protected _dataView?: CustomDataView | SlickDataView;\r\n protected _grid!: SlickGrid;\r\n protected _prevSelectedRow?: number;\r\n protected _prevKeyDown = '';\r\n protected _ranges: SlickRange_[] = [];\r\n protected _selector: SlickCellRangeSelector_;\r\n protected _options?: CellSelectionModelOption;\r\n protected _defaults: CellSelectionModelOption = {\r\n selectActiveCell: true\r\n };\r\n\r\n constructor(options?: { selectActiveCell: boolean; cellRangeSelector: SlickCellRangeSelector_; }) {\r\n if (options === undefined || options.cellRangeSelector === undefined) {\r\n this._selector = new SlickCellRangeSelector({ selectionCss: { border: '2px solid black' } as CSSStyleDeclaration });\r\n } else {\r\n this._selector = options.cellRangeSelector;\r\n }\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n this._options = Utils.extend(true, {}, this._defaults, this._options);\r\n this._grid = grid;\r\n if (grid.hasDataView()) {\r\n this._dataView = grid.getData();\r\n }\r\n this._grid.onActiveCellChanged.subscribe(this.handleActiveCellChange.bind(this));\r\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\r\n grid.registerPlugin(this._selector);\r\n this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n }\r\n\r\n destroy() {\r\n this._grid.onActiveCellChanged.unsubscribe(this.handleActiveCellChange.bind(this));\r\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\r\n this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n this._grid.unregisterPlugin(this._selector);\r\n this._selector?.destroy();\r\n }\r\n\r\n protected removeInvalidRanges(ranges: SlickRange_[]) {\r\n const result: SlickRange_[] = [];\r\n\r\n for (let i = 0; i < ranges.length; i++) {\r\n const r = ranges[i];\r\n if (this._grid.canCellBeSelected(r.fromRow, r.fromCell) && this._grid.canCellBeSelected(r.toRow, r.toCell)) {\r\n result.push(r);\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n protected rangesAreEqual(range1: SlickRange_[], range2: SlickRange_[]) {\r\n let areDifferent = (range1.length !== range2.length);\r\n if (!areDifferent) {\r\n for (let i = 0; i < range1.length; i++) {\r\n if (\r\n range1[i].fromCell !== range2[i].fromCell\r\n || range1[i].fromRow !== range2[i].fromRow\r\n || range1[i].toCell !== range2[i].toCell\r\n || range1[i].toRow !== range2[i].toRow\r\n ) {\r\n areDifferent = true;\r\n break;\r\n }\r\n }\r\n }\r\n return !areDifferent;\r\n }\r\n\r\n /** Provide a way to force a recalculation of page row count (for example on grid resize) */\r\n resetPageRowCount() {\r\n this._cachedPageRowCount = 0;\r\n }\r\n\r\n setSelectedRanges(ranges: SlickRange_[], caller = 'SlickCellSelectionModel.setSelectedRanges') {\r\n // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged\r\n if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }\r\n\r\n // if range has not changed, don't fire onSelectedRangesChanged\r\n const rangeHasChanged = !this.rangesAreEqual(this._ranges, ranges);\r\n\r\n this._ranges = this.removeInvalidRanges(ranges);\r\n if (rangeHasChanged) {\r\n // provide extra \"caller\" argument through SlickEventData event to avoid breaking the previous pubsub event structure\r\n // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`\r\n const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller } }), this._ranges);\r\n this.onSelectedRangesChanged.notify(this._ranges, eventData);\r\n }\r\n }\r\n\r\n getSelectedRanges() {\r\n return this._ranges;\r\n }\r\n\r\n refreshSelections() {\r\n this.setSelectedRanges(this.getSelectedRanges());\r\n }\r\n\r\n protected handleBeforeCellRangeSelected(e: SlickEventData_): boolean | void {\r\n if (this._grid.getEditorLock().isActive()) {\r\n e.stopPropagation();\r\n return false;\r\n }\r\n }\r\n\r\n protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; }) {\r\n this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);\r\n this.setSelectedRanges([args.range]);\r\n }\r\n\r\n protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) {\r\n this._prevSelectedRow = undefined;\r\n const isCellDefined = Utils.isDefined(args.cell);\r\n const isRowDefined = Utils.isDefined(args.row);\r\n\r\n if (this._options?.selectActiveCell && isRowDefined && isCellDefined) {\r\n this.setSelectedRanges([new SlickRange(args.row, args.cell)]);\r\n } else if (!this._options?.selectActiveCell || (!isRowDefined && !isCellDefined)) {\r\n // clear the previous selection once the cell changes\r\n this.setSelectedRanges([]);\r\n }\r\n }\r\n\r\n protected isKeyAllowed(key: string, isShiftKeyPressed?: boolean): boolean {\r\n return [\r\n 'ArrowLeft',\r\n 'ArrowRight',\r\n 'ArrowUp',\r\n 'ArrowDown',\r\n 'PageDown',\r\n 'PageUp',\r\n 'Home',\r\n 'End',\r\n ...(!isShiftKeyPressed ? ['a', 'A'] : []),\r\n ].some((k) => k === key);\r\n }\r\n\r\n protected handleKeyDown(e: SlickEventData_) {\r\n let ranges: SlickRange_[], last: SlickRange_;\r\n const colLn = this._grid.getColumns().length;\r\n const active = this._grid.getActiveCell();\r\n let dataLn = 0;\r\n if (this._dataView && 'getPagingInfo' in this._dataView) {\r\n dataLn = this._dataView?.getPagingInfo().pageSize || this._dataView.getLength();\r\n } else {\r\n dataLn = this._grid.getDataLength();\r\n }\r\n\r\n if (active && (e.shiftKey || e.ctrlKey) && !e.altKey && this.isKeyAllowed(e.key as string, e.shiftKey)) {\r\n ranges = this.getSelectedRanges().slice();\r\n if (!ranges.length) {\r\n ranges.push(new SlickRange(active.row, active.cell));\r\n }\r\n // keyboard can work with last range only\r\n last = ranges.pop() as SlickRange_;\r\n\r\n // can't handle selection out of active cell\r\n if (!last.contains(active.row, active.cell)) {\r\n last = new SlickRange(active.row, active.cell);\r\n }\r\n\r\n let dRow = last.toRow - last.fromRow;\r\n let dCell = last.toCell - last.fromCell;\r\n let toCell: undefined | number;\r\n let toRow = 0;\r\n\r\n // when using Ctrl+{a, A} we will change our position to cell 0,0 and select all grid cells\r\n if (e.ctrlKey && e.key?.toLowerCase() === 'a') {\r\n this._grid.setActiveCell(0, 0, false, false, true);\r\n active.row = 0;\r\n active.cell = 0;\r\n toCell = colLn - 1;\r\n toRow = dataLn - 1;\r\n }\r\n\r\n // walking direction\r\n const dirRow = active.row === last.fromRow ? 1 : -1;\r\n const dirCell = active.cell === last.fromCell ? 1 : -1;\r\n const isSingleKeyMove = e.key!.startsWith('Arrow');\r\n\r\n if (isSingleKeyMove && !e.ctrlKey) {\r\n // single cell move: (Arrow{Up/ArrowDown/ArrowLeft/ArrowRight})\r\n if (e.key === 'ArrowLeft') {\r\n dCell -= dirCell;\r\n } else if (e.key === 'ArrowRight') {\r\n dCell += dirCell;\r\n } else if (e.key === 'ArrowUp') {\r\n dRow -= dirRow;\r\n } else if (e.key === 'ArrowDown') {\r\n dRow += dirRow;\r\n }\r\n toRow = active.row + dirRow * dRow;\r\n } else {\r\n // multiple cell moves: (Home, End, Page{Up/Down})\r\n if (this._cachedPageRowCount < 1) {\r\n this._cachedPageRowCount = this._grid.getViewportRowCount();\r\n }\r\n if (this._prevSelectedRow === undefined) {\r\n this._prevSelectedRow = active.row;\r\n }\r\n\r\n if ((!e.ctrlKey && e.shiftKey && e.key === 'Home') || (e.ctrlKey && e.shiftKey && e.key === 'ArrowLeft')) {\r\n toCell = 0;\r\n toRow = active.row;\r\n } else if ((!e.ctrlKey && e.shiftKey && e.key === 'End') || (e.ctrlKey && e.shiftKey && e.key === 'ArrowRight')) {\r\n toCell = colLn - 1;\r\n toRow = active.row;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'ArrowUp') {\r\n toRow = 0;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'ArrowDown') {\r\n toRow = dataLn - 1;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'Home') {\r\n toCell = 0;\r\n toRow = 0;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'End') {\r\n toCell = colLn - 1;\r\n toRow = dataLn - 1;\r\n } else if (e.key === 'PageUp') {\r\n if (this._prevSelectedRow >= 0) {\r\n toRow = this._prevSelectedRow - this._cachedPageRowCount;\r\n }\r\n if (toRow < 0) {\r\n toRow = 0;\r\n }\r\n } else if (e.key === 'PageDown') {\r\n if (this._prevSelectedRow <= dataLn - 1) {\r\n toRow = this._prevSelectedRow + this._cachedPageRowCount;\r\n }\r\n if (toRow > dataLn - 1) {\r\n toRow = dataLn - 1;\r\n }\r\n }\r\n this._prevSelectedRow = toRow;\r\n }\r\n\r\n // define new selection range\r\n toCell ??= active.cell + dirCell * dCell;\r\n const new_last = new SlickRange(active.row, active.cell, toRow, toCell);\r\n if (this.removeInvalidRanges([new_last]).length) {\r\n ranges.push(new_last);\r\n const viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;\r\n const viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;\r\n\r\n if (isSingleKeyMove) {\r\n this._grid.scrollRowIntoView(viewRow);\r\n this._grid.scrollCellIntoView(viewRow, viewCell);\r\n } else {\r\n this._grid.scrollRowIntoView(toRow);\r\n this._grid.scrollCellIntoView(toRow, viewCell);\r\n }\r\n } else {\r\n ranges.push(last);\r\n }\r\n\r\n this.setSelectedRanges(ranges);\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this._prevKeyDown = e.key as string;\r\n }\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(true, window, {\r\n Slick: {\r\n CellSelectionModel: SlickCellSelectionModel\r\n }\r\n });\r\n}\r\n"], - "mappings": ";;;;;;;AAOA,MAAM,aAAyB,MAAM,OAC/B,iBAA6B,MAAM,WACnC,aAAyB,MAAM,OAC/B,yBAAqC,MAAM,mBAC3C,QAAoB,MAAM,OAOnB,0BAAN,MAA8B;AAAA,IAoBnC,YAAY,SAAsF;AAjBlG;AAAA;AAAA,wCAAa;AACb,qDAA0B,IAAI,WAA0B,yBAAyB;AAIjF;AAAA;AAAA,0BAAU,uBAAsB;AAChC,0BAAU;AACV,0BAAU;AACV,0BAAU;AACV,0BAAU,gBAAe;AACzB,0BAAU,WAAyB,CAAC;AACpC,0BAAU;AACV,0BAAU;AACV,0BAAU,aAAsC;AAAA,QAC9C,kBAAkB;AAAA,MACpB;AAGE,MAAI,YAAY,UAAa,QAAQ,sBAAsB,SACzD,KAAK,YAAY,IAAI,uBAAuB,EAAE,cAAc,EAAE,QAAQ,kBAAkB,EAAyB,CAAC,IAElH,KAAK,YAAY,QAAQ;AAAA,IAE7B;AAAA,IAEA,KAAK,MAAiB;AACpB,WAAK,WAAW,MAAM,OAAO,IAAM,CAAC,GAAG,KAAK,WAAW,KAAK,QAAQ,GACpE,KAAK,QAAQ,MACT,KAAK,YAAY,MACnB,KAAK,YAAY,KAAK,QAAwC,IAEhE,KAAK,MAAM,oBAAoB,UAAU,KAAK,uBAAuB,KAAK,IAAI,CAAC,GAC/E,KAAK,MAAM,UAAU,UAAU,KAAK,cAAc,KAAK,IAAI,CAAC,GAC5D,KAAK,eAAe,KAAK,SAAS,GAClC,KAAK,UAAU,oBAAoB,UAAU,KAAK,wBAAwB,KAAK,IAAI,CAAC,GACpF,KAAK,UAAU,0BAA0B,UAAU,KAAK,8BAA8B,KAAK,IAAI,CAAC;AAAA,IAClG;AAAA,IAEA,UAAU;AA3DZ;AA4DI,WAAK,MAAM,oBAAoB,YAAY,KAAK,uBAAuB,KAAK,IAAI,CAAC,GACjF,KAAK,MAAM,UAAU,YAAY,KAAK,cAAc,KAAK,IAAI,CAAC,GAC9D,KAAK,UAAU,oBAAoB,YAAY,KAAK,wBAAwB,KAAK,IAAI,CAAC,GACtF,KAAK,UAAU,0BAA0B,YAAY,KAAK,8BAA8B,KAAK,IAAI,CAAC,GAClG,KAAK,MAAM,iBAAiB,KAAK,SAAS,IAC1C,UAAK,cAAL,WAAgB;AAAA,IAClB;AAAA,IAEU,oBAAoB,QAAuB;AACnD,UAAM,SAAwB,CAAC;AAE/B,eAAS,IAAI,GAAG,IAAI,OAAO,QAAQ,KAAK;AACtC,YAAM,IAAI,OAAO,CAAC;AAClB,QAAI,KAAK,MAAM,kBAAkB,EAAE,SAAS,EAAE,QAAQ,KAAK,KAAK,MAAM,kBAAkB,EAAE,OAAO,EAAE,MAAM,KACvG,OAAO,KAAK,CAAC;AAAA,MAEjB;AAEA,aAAO;AAAA,IACT;AAAA,IAEU,eAAe,QAAuB,QAAuB;AACrE,UAAI,eAAgB,OAAO,WAAW,OAAO;AAC7C,UAAI,CAAC;AACH,iBAAS,IAAI,GAAG,IAAI,OAAO,QAAQ;AACjC,cACE,OAAO,CAAC,EAAE,aAAa,OAAO,CAAC,EAAE,YAC9B,OAAO,CAAC,EAAE,YAAY,OAAO,CAAC,EAAE,WAChC,OAAO,CAAC,EAAE,WAAW,OAAO,CAAC,EAAE,UAC/B,OAAO,CAAC,EAAE,UAAU,OAAO,CAAC,EAAE,OACjC;AACA,2BAAe;AACf;AAAA,UACF;AAAA;AAGJ,aAAO,CAAC;AAAA,IACV;AAAA;AAAA,IAGA,oBAAoB;AAClB,WAAK,sBAAsB;AAAA,IAC7B;AAAA,IAEA,kBAAkB,QAAuB,SAAS,6CAA6C;AAE7F,WAAK,CAAC,KAAK,WAAW,KAAK,QAAQ,WAAW,OAAO,CAAC,UAAU,OAAO,WAAW;AAAM;AAGxF,UAAM,kBAAkB,CAAC,KAAK,eAAe,KAAK,SAAS,MAAM;AAGjE,UADA,KAAK,UAAU,KAAK,oBAAoB,MAAM,GAC1C,iBAAiB;AAGnB,YAAM,YAAY,IAAI,eAAe,IAAI,YAAY,SAAS,EAAE,QAAQ,EAAE,OAAO,EAAE,CAAC,GAAG,KAAK,OAAO;AACnG,aAAK,wBAAwB,OAAO,KAAK,SAAS,SAAS;AAAA,MAC7D;AAAA,IACF;AAAA,IAEA,oBAAoB;AAClB,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,oBAAoB;AAClB,WAAK,kBAAkB,KAAK,kBAAkB,CAAC;AAAA,IACjD;AAAA,IAEU,8BAA8B,GAAoC;AAC1E,UAAI,KAAK,MAAM,cAAc,EAAE,SAAS;AACtC,iBAAE,gBAAgB,GACX;AAAA,IAEX;AAAA,IAEU,wBAAwB,IAAqB,MAA+B;AACpF,WAAK,MAAM,cAAc,KAAK,MAAM,SAAS,KAAK,MAAM,UAAU,IAAO,IAAO,EAAI,GACpF,KAAK,kBAAkB,CAAC,KAAK,KAAK,CAAC;AAAA,IACrC;AAAA,IAEU,uBAAuB,IAAqB,MAAoC;AA5I5F;AA6II,WAAK,mBAAmB;AACxB,UAAM,gBAAgB,MAAM,UAAU,KAAK,IAAI,GACzC,eAAe,MAAM,UAAU,KAAK,GAAG;AAE7C,OAAI,UAAK,aAAL,WAAe,oBAAoB,gBAAgB,gBACrD,KAAK,kBAAkB,CAAC,IAAI,WAAW,KAAK,KAAK,KAAK,IAAI,CAAC,CAAC,KACnD,GAAC,UAAK,aAAL,WAAe,qBAAqB,CAAC,gBAAgB,CAAC,kBAEhE,KAAK,kBAAkB,CAAC,CAAC;AAAA,IAE7B;AAAA,IAEU,aAAa,KAAa,mBAAsC;AACxE,aAAO;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA,GAAK,oBAAiC,CAAC,IAAd,CAAC,KAAK,GAAG;AAAA,MACpC,EAAE,KAAK,CAAC,MAAM,MAAM,GAAG;AAAA,IACzB;AAAA,IAEU,cAAc,GAAoB;AAvK9C;AAwKI,UAAI,QAAuB,MACrB,QAAQ,KAAK,MAAM,WAAW,EAAE,QAChC,SAAS,KAAK,MAAM,cAAc,GACpC,SAAS;AAOb,UANI,KAAK,aAAa,mBAAmB,KAAK,YAC5C,WAAS,UAAK,cAAL,mBAAgB,gBAAgB,aAAY,KAAK,UAAU,UAAU,IAE9E,SAAS,KAAK,MAAM,cAAc,GAGhC,WAAW,EAAE,YAAY,EAAE,YAAY,CAAC,EAAE,UAAU,KAAK,aAAa,EAAE,KAAe,EAAE,QAAQ,GAAG;AACtG,iBAAS,KAAK,kBAAkB,EAAE,MAAM,GACnC,OAAO,UACV,OAAO,KAAK,IAAI,WAAW,OAAO,KAAK,OAAO,IAAI,CAAC,GAGrD,OAAO,OAAO,IAAI,GAGb,KAAK,SAAS,OAAO,KAAK,OAAO,IAAI,MACxC,OAAO,IAAI,WAAW,OAAO,KAAK,OAAO,IAAI;AAG/C,YAAI,OAAO,KAAK,QAAQ,KAAK,SACzB,QAAQ,KAAK,SAAS,KAAK,UAC3B,QACA,QAAQ;AAGZ,QAAI,EAAE,aAAW,OAAE,QAAF,mBAAO,mBAAkB,QACxC,KAAK,MAAM,cAAc,GAAG,GAAG,IAAO,IAAO,EAAI,GACjD,OAAO,MAAM,GACb,OAAO,OAAO,GACd,SAAS,QAAQ,GACjB,QAAQ,SAAS;AAInB,YAAM,SAAS,OAAO,QAAQ,KAAK,UAAU,IAAI,IAC3C,UAAU,OAAO,SAAS,KAAK,WAAW,IAAI,IAC9C,kBAAkB,EAAE,IAAK,WAAW,OAAO;AAEjD,QAAI,mBAAmB,CAAC,EAAE,WAEpB,EAAE,QAAQ,cACZ,SAAS,UACA,EAAE,QAAQ,eACnB,SAAS,UACA,EAAE,QAAQ,YACnB,QAAQ,SACC,EAAE,QAAQ,gBACnB,QAAQ,SAEV,QAAQ,OAAO,MAAM,SAAS,SAG1B,KAAK,sBAAsB,MAC7B,KAAK,sBAAsB,KAAK,MAAM,oBAAoB,IAExD,KAAK,qBAAqB,WAC5B,KAAK,mBAAmB,OAAO,MAG5B,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,UAAY,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,eAC1F,SAAS,GACT,QAAQ,OAAO,OACL,CAAC,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAAW,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,gBAChG,SAAS,QAAQ,GACjB,QAAQ,OAAO,OACN,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,YAC9C,QAAQ,IACC,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,cAC9C,QAAQ,SAAS,IACR,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,UAC9C,SAAS,GACT,QAAQ,KACC,EAAE,WAAW,EAAE,YAAY,EAAE,QAAQ,SAC9C,SAAS,QAAQ,GACjB,QAAQ,SAAS,KACR,EAAE,QAAQ,YACf,KAAK,oBAAoB,MAC3B,QAAQ,KAAK,mBAAmB,KAAK,sBAEnC,QAAQ,MACV,QAAQ,MAED,EAAE,QAAQ,eACf,KAAK,oBAAoB,SAAS,MACpC,QAAQ,KAAK,mBAAmB,KAAK,sBAEnC,QAAQ,SAAS,MACnB,QAAQ,SAAS,KAGrB,KAAK,mBAAmB,QAI1B,4BAAW,OAAO,OAAO,UAAU;AACnC,YAAM,WAAW,IAAI,WAAW,OAAO,KAAK,OAAO,MAAM,OAAO,MAAM;AACtE,YAAI,KAAK,oBAAoB,CAAC,QAAQ,CAAC,EAAE,QAAQ;AAC/C,iBAAO,KAAK,QAAQ;AACpB,cAAM,UAAU,SAAS,IAAI,SAAS,QAAQ,SAAS,SACjD,WAAW,UAAU,IAAI,SAAS,SAAS,SAAS;AAE1D,UAAI,mBACF,KAAK,MAAM,kBAAkB,OAAO,GACpC,KAAK,MAAM,mBAAmB,SAAS,QAAQ,MAE/C,KAAK,MAAM,kBAAkB,KAAK,GAClC,KAAK,MAAM,mBAAmB,OAAO,QAAQ;AAAA,QAEjD;AACE,iBAAO,KAAK,IAAI;AAGlB,aAAK,kBAAkB,MAAM,GAE7B,EAAE,eAAe,GACjB,EAAE,gBAAgB,GAClB,KAAK,eAAe,EAAE;AAAA,MACxB;AAAA,IACF;AAAA,EACF;AAGA,EAAiB,OAAO,SACtB,MAAM,OAAO,IAAM,QAAQ;AAAA,IACzB,OAAO;AAAA,MACL,oBAAoB;AAAA,IACtB;AAAA,EACF,CAAC;", + "sourcesContent": ["import { SlickEvent as SlickEvent_, SlickEventData as SlickEventData_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js';\r\nimport { SlickCellRangeSelector as SlickCellRangeSelector_ } from './slick.cellrangeselector.js';\r\nimport type { CustomDataView, OnActiveCellChangedEventArgs } from '../models/index.js';\r\nimport type { SlickDataView } from '../slick.dataview.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst SlickCellRangeSelector = IIFE_ONLY ? Slick.CellRangeSelector : SlickCellRangeSelector_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport interface CellSelectionModelOption {\r\n selectActiveCell: boolean;\r\n cellRangeSelector?: SlickCellRangeSelector_;\r\n}\r\n\r\nexport class SlickCellSelectionModel {\r\n // --\r\n // public API\r\n pluginName = 'CellSelectionModel' as const;\r\n onSelectedRangesChanged = new SlickEvent('onSelectedRangesChanged');\r\n\r\n // --\r\n // protected props\r\n protected _cachedPageRowCount = 0;\r\n protected _dataView?: CustomDataView | SlickDataView;\r\n protected _grid!: SlickGrid;\r\n protected _prevSelectedRow?: number;\r\n protected _prevKeyDown = '';\r\n protected _ranges: SlickRange_[] = [];\r\n protected _selector: SlickCellRangeSelector_;\r\n protected _options?: CellSelectionModelOption;\r\n protected _defaults: CellSelectionModelOption = {\r\n selectActiveCell: true\r\n };\r\n\r\n constructor(options?: { selectActiveCell: boolean; cellRangeSelector: SlickCellRangeSelector_; }) {\r\n if (options === undefined || options.cellRangeSelector === undefined) {\r\n this._selector = new SlickCellRangeSelector({ selectionCss: { border: '2px solid black' } as CSSStyleDeclaration });\r\n } else {\r\n this._selector = options.cellRangeSelector;\r\n }\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n this._options = Utils.extend(true, {}, this._defaults, this._options);\r\n this._grid = grid;\r\n if (grid.hasDataView()) {\r\n this._dataView = grid.getData();\r\n }\r\n this._grid.onActiveCellChanged.subscribe(this.handleActiveCellChange.bind(this));\r\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\r\n grid.registerPlugin(this._selector);\r\n this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n }\r\n\r\n destroy() {\r\n this._grid.onActiveCellChanged.unsubscribe(this.handleActiveCellChange.bind(this));\r\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\r\n this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n this._grid.unregisterPlugin(this._selector);\r\n this._selector?.destroy();\r\n }\r\n\r\n protected removeInvalidRanges(ranges: SlickRange_[]) {\r\n const result: SlickRange_[] = [];\r\n\r\n for (let i = 0; i < ranges.length; i++) {\r\n const r = ranges[i];\r\n if (this._grid.canCellBeSelected(r.fromRow, r.fromCell) && this._grid.canCellBeSelected(r.toRow, r.toCell)) {\r\n result.push(r);\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n protected rangesAreEqual(range1: SlickRange_[], range2: SlickRange_[]) {\r\n let areDifferent = (range1.length !== range2.length);\r\n if (!areDifferent) {\r\n for (let i = 0; i < range1.length; i++) {\r\n if (\r\n range1[i].fromCell !== range2[i].fromCell\r\n || range1[i].fromRow !== range2[i].fromRow\r\n || range1[i].toCell !== range2[i].toCell\r\n || range1[i].toRow !== range2[i].toRow\r\n ) {\r\n areDifferent = true;\r\n break;\r\n }\r\n }\r\n }\r\n return !areDifferent;\r\n }\r\n\r\n /** Provide a way to force a recalculation of page row count (for example on grid resize) */\r\n resetPageRowCount() {\r\n this._cachedPageRowCount = 0;\r\n }\r\n\r\n setSelectedRanges(ranges: SlickRange_[], caller = 'SlickCellSelectionModel.setSelectedRanges', selectionMode: string) {\r\n // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged\r\n if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }\r\n\r\n // if range has not changed, don't fire onSelectedRangesChanged\r\n const rangeHasChanged = !this.rangesAreEqual(this._ranges, ranges);\r\n\r\n this._ranges = this.removeInvalidRanges(ranges);\r\n if (rangeHasChanged) {\r\n // provide extra \"caller\" argument through SlickEventData event to avoid breaking the previous pubsub event structure\r\n // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`\r\n const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges);\r\n this.onSelectedRangesChanged.notify(this._ranges, eventData);\r\n }\r\n }\r\n\r\n getSelectedRanges() {\r\n return this._ranges;\r\n }\r\n\r\n refreshSelections() {\r\n this.setSelectedRanges(this.getSelectedRanges(), undefined, '');\r\n }\r\n\r\n protected handleBeforeCellRangeSelected(e: SlickEventData_): boolean | void {\r\n if (this._grid.getEditorLock().isActive()) {\r\n e.stopPropagation();\r\n return false;\r\n }\r\n }\r\n\r\n protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; selectionMode: string; }) {\r\n this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);\r\n this.setSelectedRanges([args.range], undefined, args.selectionMode);\r\n }\r\n\r\n protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) {\r\n this._prevSelectedRow = undefined;\r\n const isCellDefined = Utils.isDefined(args.cell);\r\n const isRowDefined = Utils.isDefined(args.row);\r\n\r\n if (this._options?.selectActiveCell && isRowDefined && isCellDefined) {\r\n this.setSelectedRanges([new SlickRange(args.row, args.cell)], undefined, '');\r\n } else if (!this._options?.selectActiveCell || (!isRowDefined && !isCellDefined)) {\r\n // clear the previous selection once the cell changes\r\n this.setSelectedRanges([], undefined, '');\r\n }\r\n }\r\n\r\n protected isKeyAllowed(key: string, isShiftKeyPressed?: boolean): boolean {\r\n return [\r\n 'ArrowLeft',\r\n 'ArrowRight',\r\n 'ArrowUp',\r\n 'ArrowDown',\r\n 'PageDown',\r\n 'PageUp',\r\n 'Home',\r\n 'End',\r\n ...(!isShiftKeyPressed ? ['a', 'A'] : []),\r\n ].some((k) => k === key);\r\n }\r\n\r\n protected handleKeyDown(e: SlickEventData_) {\r\n let ranges: SlickRange_[], last: SlickRange_;\r\n const colLn = this._grid.getColumns().length;\r\n const active = this._grid.getActiveCell();\r\n let dataLn = 0;\r\n if (this._dataView && 'getPagingInfo' in this._dataView) {\r\n dataLn = this._dataView?.getPagingInfo().pageSize || this._dataView.getLength();\r\n } else {\r\n dataLn = this._grid.getDataLength();\r\n }\r\n\r\n if (active && (e.shiftKey || e.ctrlKey) && !e.altKey && this.isKeyAllowed(e.key as string, e.shiftKey)) {\r\n ranges = this.getSelectedRanges().slice();\r\n if (!ranges.length) {\r\n ranges.push(new SlickRange(active.row, active.cell));\r\n }\r\n // keyboard can work with last range only\r\n last = ranges.pop() as SlickRange_;\r\n\r\n // can't handle selection out of active cell\r\n if (!last.contains(active.row, active.cell)) {\r\n last = new SlickRange(active.row, active.cell);\r\n }\r\n\r\n let dRow = last.toRow - last.fromRow;\r\n let dCell = last.toCell - last.fromCell;\r\n let toCell: undefined | number;\r\n let toRow = 0;\r\n\r\n // when using Ctrl+{a, A} we will change our position to cell 0,0 and select all grid cells\r\n if (e.ctrlKey && e.key?.toLowerCase() === 'a') {\r\n this._grid.setActiveCell(0, 0, false, false, true);\r\n active.row = 0;\r\n active.cell = 0;\r\n toCell = colLn - 1;\r\n toRow = dataLn - 1;\r\n }\r\n\r\n // walking direction\r\n const dirRow = active.row === last.fromRow ? 1 : -1;\r\n const dirCell = active.cell === last.fromCell ? 1 : -1;\r\n const isSingleKeyMove = e.key!.startsWith('Arrow');\r\n\r\n if (isSingleKeyMove && !e.ctrlKey) {\r\n // single cell move: (Arrow{Up/ArrowDown/ArrowLeft/ArrowRight})\r\n if (e.key === 'ArrowLeft') {\r\n dCell -= dirCell;\r\n } else if (e.key === 'ArrowRight') {\r\n dCell += dirCell;\r\n } else if (e.key === 'ArrowUp') {\r\n dRow -= dirRow;\r\n } else if (e.key === 'ArrowDown') {\r\n dRow += dirRow;\r\n }\r\n toRow = active.row + dirRow * dRow;\r\n } else {\r\n // multiple cell moves: (Home, End, Page{Up/Down})\r\n if (this._cachedPageRowCount < 1) {\r\n this._cachedPageRowCount = this._grid.getViewportRowCount();\r\n }\r\n if (this._prevSelectedRow === undefined) {\r\n this._prevSelectedRow = active.row;\r\n }\r\n\r\n if ((!e.ctrlKey && e.shiftKey && e.key === 'Home') || (e.ctrlKey && e.shiftKey && e.key === 'ArrowLeft')) {\r\n toCell = 0;\r\n toRow = active.row;\r\n } else if ((!e.ctrlKey && e.shiftKey && e.key === 'End') || (e.ctrlKey && e.shiftKey && e.key === 'ArrowRight')) {\r\n toCell = colLn - 1;\r\n toRow = active.row;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'ArrowUp') {\r\n toRow = 0;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'ArrowDown') {\r\n toRow = dataLn - 1;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'Home') {\r\n toCell = 0;\r\n toRow = 0;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'End') {\r\n toCell = colLn - 1;\r\n toRow = dataLn - 1;\r\n } else if (e.key === 'PageUp') {\r\n if (this._prevSelectedRow >= 0) {\r\n toRow = this._prevSelectedRow - this._cachedPageRowCount;\r\n }\r\n if (toRow < 0) {\r\n toRow = 0;\r\n }\r\n } else if (e.key === 'PageDown') {\r\n if (this._prevSelectedRow <= dataLn - 1) {\r\n toRow = this._prevSelectedRow + this._cachedPageRowCount;\r\n }\r\n if (toRow > dataLn - 1) {\r\n toRow = dataLn - 1;\r\n }\r\n }\r\n this._prevSelectedRow = toRow;\r\n }\r\n\r\n // define new selection range\r\n toCell ??= active.cell + dirCell * dCell;\r\n const new_last = new SlickRange(active.row, active.cell, toRow, toCell);\r\n if (this.removeInvalidRanges([new_last]).length) {\r\n ranges.push(new_last);\r\n const viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;\r\n const viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;\r\n\r\n if (isSingleKeyMove) {\r\n this._grid.scrollRowIntoView(viewRow);\r\n this._grid.scrollCellIntoView(viewRow, viewCell);\r\n } else {\r\n this._grid.scrollRowIntoView(toRow);\r\n this._grid.scrollCellIntoView(toRow, viewCell);\r\n }\r\n } else {\r\n ranges.push(last);\r\n }\r\n\r\n this.setSelectedRanges(ranges, undefined, '');\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this._prevKeyDown = e.key as string;\r\n }\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(true, window, {\r\n Slick: {\r\n CellSelectionModel: SlickCellSelectionModel\r\n }\r\n });\r\n}\r\n"], + "mappings": "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", "names": [] } diff --git a/dist/browser/plugins/slick.hybridselectionmodel.js b/dist/browser/plugins/slick.hybridselectionmodel.js new file mode 100644 index 00000000..761e0f38 --- /dev/null +++ b/dist/browser/plugins/slick.hybridselectionmodel.js @@ -0,0 +1,246 @@ +"use strict"; +(() => { + var __defProp = Object.defineProperty; + var __defNormalProp = (obj, key, value) => key in obj ? __defProp(obj, key, { enumerable: !0, configurable: !0, writable: !0, value }) : obj[key] = value; + var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value); + + // src/plugins/slick.hybridselectionmodel.ts + var Draggable = Slick.Draggable, keyCode = Slick.keyCode, SlickEvent = Slick.Event, SlickEventData = Slick.EventData, SlickRange = Slick.Range, SlickCellRangeDecorator = Slick.CellRangeDecorator, SlickCellRangeSelector = Slick.CellRangeSelector, Utils = Slick.Utils, SlickHybridSelectionModel = class { + constructor(options) { + // hybrid selection model is CellSelectionModel except when selecting + // specific columns, which behave as RowSelectionModel + // -- + // public API + __publicField(this, "pluginName", "HybridSelectionModel"); + __publicField(this, "onSelectedRangesChanged", new SlickEvent("onSelectedRangesChanged")); + // -- + // protected props + __publicField(this, "_cachedPageRowCount", 0); + __publicField(this, "_dataView"); + __publicField(this, "_grid"); + __publicField(this, "_prevSelectedRow"); + __publicField(this, "_prevKeyDown", ""); + __publicField(this, "_ranges", []); + __publicField(this, "_selector"); + __publicField(this, "_isRowMoveManagerHandler"); + __publicField(this, "_activeSelectionIsRow", !1); + __publicField(this, "_options"); + __publicField(this, "_defaults", { + selectActiveCell: !0, + selectActiveRow: !0, + dragToSelect: !1, + autoScrollWhenDrag: !0, + handleRowMoveManagerColumn: !0, + // Row Selection on RowMoveManage column + rowSelectColumnObjectArr: [], + // Row Selection on these columns + rowSelectOverride: void 0, + // function to toggle Row Selection Models + cellRangeSelector: void 0 + }); + options === void 0 || options.cellRangeSelector === void 0 ? this._selector = new SlickCellRangeSelector({ selectionCss: { border: "2px solid black" } }) : this._selector = options.cellRangeSelector; + } + // Region: Setup + // ----------------------------------------------------------------------------- + init(grid) { + if (Draggable === void 0) + throw new Error('Slick.Draggable is undefined, make sure to import "slick.interactions.js"'); + if (this._options = Utils.extend(!0, {}, this._defaults, this._options), this._grid = grid, Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this), !this._selector && this._options.dragToSelect) { + if (!SlickCellRangeDecorator) + throw new Error("Slick.CellRangeDecorator is required when option dragToSelect set to true"); + this._selector = new SlickCellRangeSelector({ + selectionCss: { border: "none" }, + autoScroll: this._options.autoScrollWhenDrag + }); + } + grid.hasDataView() && (this._dataView = grid.getData()), this._grid.onActiveCellChanged.subscribe(this.handleActiveCellChange.bind(this)), this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this)), this._grid.onClick.subscribe(this.handleClick.bind(this)), this._selector && (grid.registerPlugin(this._selector), this._selector.onCellRangeSelecting.subscribe(this.handleCellRangeSelected.bind(this)), this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this)), this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this))); + } + destroy() { + var _a; + this._grid.onActiveCellChanged.unsubscribe(this.handleActiveCellChange.bind(this)), this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this)), this._grid.onClick.unsubscribe(this.handleClick.bind(this)), this._selector.onCellRangeSelecting.unsubscribe(this.handleCellRangeSelected.bind(this)), this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this)), this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this)), this._grid.unregisterPlugin(this._selector), (_a = this._selector) == null || _a.destroy(); + } + // Region: CellSelectionModel Members + // ----------------------------------------------------------------------------- + removeInvalidRanges(ranges) { + let result = []; + for (let i = 0; i < ranges.length; i++) { + let r = ranges[i]; + this._grid.canCellBeSelected(r.fromRow, r.fromCell) && this._grid.canCellBeSelected(r.toRow, r.toCell) && result.push(r); + } + return result; + } + rangesAreEqual(range1, range2) { + let areDifferent = range1.length !== range2.length; + if (!areDifferent) { + for (let i = 0; i < range1.length; i++) + if (range1[i].fromCell !== range2[i].fromCell || range1[i].fromRow !== range2[i].fromRow || range1[i].toCell !== range2[i].toCell || range1[i].toRow !== range2[i].toRow) { + areDifferent = !0; + break; + } + } + return !areDifferent; + } + // Region: RowSelectionModel Members + // ----------------------------------------------------------------------------- + rangesToRows(ranges) { + let rows = []; + for (let i = 0; i < ranges.length; i++) + for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) + rows.push(j); + return rows; + } + rowsToRanges(rows) { + let ranges = [], lastCell = this._grid.getColumns().length - 1; + return rows.forEach((row) => ranges.push(new SlickRange(row, 0, row, lastCell))), ranges; + } + getRowsRange(from, to) { + let i, rows = []; + for (i = from; i <= to; i++) + rows.push(i); + for (i = to; i < from; i++) + rows.push(i); + return rows; + } + getSelectedRows() { + return this.rangesToRows(this._ranges); + } + setSelectedRows(rows) { + this.setSelectedRanges(this.rowsToRanges(rows), "SlickRowSelectionModel.setSelectedRows", ""); + } + // Region: Shared Members + // ----------------------------------------------------------------------------- + /** Provide a way to force a recalculation of page row count (for example on grid resize) */ + resetPageRowCount() { + this._cachedPageRowCount = 0; + } + setSelectedRanges(ranges, caller = "SlickHybridSelectionModel.setSelectedRanges", selectionMode) { + if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) + return; + let rangeHasChanged = !this.rangesAreEqual(this._ranges, ranges); + if (this._activeSelectionIsRow) { + this._ranges = ranges; + let eventData = new SlickEventData(new CustomEvent("click", { detail: { caller, selectionMode } }), this._ranges); + this.onSelectedRangesChanged.notify(this._ranges, eventData); + } else if (this._ranges = this.removeInvalidRanges(ranges), rangeHasChanged) { + let eventData = new SlickEventData(new CustomEvent("click", { detail: { caller, selectionMode } }), this._ranges); + this.onSelectedRangesChanged.notify(this._ranges, eventData); + } + } + currentSelectionModeIsRow() { + return this._activeSelectionIsRow; + } + getSelectedRanges() { + return this._ranges; + } + refreshSelections() { + this._activeSelectionIsRow ? this.setSelectedRows(this.getSelectedRows()) : this.setSelectedRanges(this.getSelectedRanges(), void 0, ""); + } + getRowMoveManagerPlugin() { + return this._grid.getPluginByName("RowMoveManager") || this._grid.getPluginByName("CrossGridRowMoveManager"); + } + rowSelectionModelIsActive(data) { + var _a, _b, _c, _d; + if ((_a = this._options) != null && _a.rowSelectOverride) + return (_b = this._options) == null ? void 0 : _b.rowSelectOverride(data, this, this._grid); + if ((_c = this._options) != null && _c.handleRowMoveManagerColumn) { + let rowMoveManager = this.getRowMoveManagerPlugin(); + if (rowMoveManager != null && rowMoveManager.isHandlerColumn(data.cell)) + return !0; + } + let targetColumn = this._grid.getVisibleColumns()[data.cell]; + return ((_d = this._options) == null ? void 0 : _d.rowSelectColumnObjectArr.includes(targetColumn)) || !1; + } + handleActiveCellChange(_e, args) { + var _a, _b, _c; + this._prevSelectedRow = void 0; + let isCellDefined = Utils.isDefined(args.cell), isRowDefined = Utils.isDefined(args.row); + this._activeSelectionIsRow = this.rowSelectionModelIsActive(args), this._activeSelectionIsRow ? (_a = this._options) != null && _a.selectActiveRow && args.row !== null && this.setSelectedRanges([new Slick.Range(args.row, 0, args.row, this._grid.getColumns().length - 1)], void 0, "") : (_b = this._options) != null && _b.selectActiveCell && isRowDefined && isCellDefined ? this.setSelectedRanges([new SlickRange(args.row, args.cell)], void 0, "") : (!((_c = this._options) != null && _c.selectActiveCell) || !isRowDefined && !isCellDefined) && this.setSelectedRanges([], void 0, ""); + } + isKeyAllowed(key) { + return ["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "PageDown", "PageUp", "Home", "End"].some((k) => k === key); + } + handleKeyDown(e) { + var _a; + if (this._activeSelectionIsRow) { + let activeRow = this._grid.getActiveCell(); + if (this._grid.getOptions().multiSelect && activeRow && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey && (e.which === keyCode.UP || e.which === keyCode.DOWN)) { + let selectedRows = this.getSelectedRows(); + selectedRows.sort(function(x, y) { + return x - y; + }), selectedRows.length || (selectedRows = [activeRow.row]); + let top = selectedRows[0], bottom = selectedRows[selectedRows.length - 1], active; + if (e.which === keyCode.DOWN ? active = activeRow.row < bottom || top === bottom ? ++bottom : ++top : active = activeRow.row < bottom ? --bottom : --top, active >= 0 && active < this._grid.getDataLength()) { + this._grid.scrollRowIntoView(active); + let tempRanges = this.rowsToRanges(this.getRowsRange(top, bottom)); + this.setSelectedRanges(tempRanges, void 0, ""); + } + e.preventDefault(), e.stopPropagation(); + } + } else { + let ranges, last, colLn = this._grid.getColumns().length, active = this._grid.getActiveCell(), dataLn = 0; + if (this._dataView && "getPagingInfo" in this._dataView ? dataLn = ((_a = this._dataView) == null ? void 0 : _a.getPagingInfo().pageSize) || this._dataView.getLength() : dataLn = this._grid.getDataLength(), active && (e.shiftKey || e.ctrlKey) && !e.altKey && this.isKeyAllowed(e.key)) { + ranges = this.getSelectedRanges().slice(), ranges.length || ranges.push(new SlickRange(active.row, active.cell)), last = ranges.pop(), last.contains(active.row, active.cell) || (last = new SlickRange(active.row, active.cell)); + let dRow = last.toRow - last.fromRow, dCell = last.toCell - last.fromCell, dirRow = active.row === last.fromRow ? 1 : -1, dirCell = active.cell === last.fromCell ? 1 : -1, isSingleKeyMove = e.key.startsWith("Arrow"), toCell, toRow = 0; + isSingleKeyMove && !e.ctrlKey ? (e.key === "ArrowLeft" ? dCell -= dirCell : e.key === "ArrowRight" ? dCell += dirCell : e.key === "ArrowUp" ? dRow -= dirRow : e.key === "ArrowDown" && (dRow += dirRow), toRow = active.row + dirRow * dRow) : (this._cachedPageRowCount < 1 && (this._cachedPageRowCount = this._grid.getViewportRowCount()), this._prevSelectedRow === void 0 && (this._prevSelectedRow = active.row), e.shiftKey && !e.ctrlKey && e.key === "Home" ? (toCell = 0, toRow = active.row) : e.shiftKey && !e.ctrlKey && e.key === "End" ? (toCell = colLn - 1, toRow = active.row) : e.ctrlKey && e.shiftKey && e.key === "Home" ? (toCell = 0, toRow = 0) : e.ctrlKey && e.shiftKey && e.key === "End" ? (toCell = colLn - 1, toRow = dataLn - 1) : e.key === "PageUp" ? (this._prevSelectedRow >= 0 && (toRow = this._prevSelectedRow - this._cachedPageRowCount), toRow < 0 && (toRow = 0)) : e.key === "PageDown" && (this._prevSelectedRow <= dataLn - 1 && (toRow = this._prevSelectedRow + this._cachedPageRowCount), toRow > dataLn - 1 && (toRow = dataLn - 1)), this._prevSelectedRow = toRow), toCell != null || (toCell = active.cell + dirCell * dCell); + let new_last = new SlickRange(active.row, active.cell, toRow, toCell); + if (this.removeInvalidRanges([new_last]).length) { + ranges.push(new_last); + let viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow, viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell; + isSingleKeyMove ? (this._grid.scrollRowIntoView(viewRow), this._grid.scrollCellIntoView(viewRow, viewCell)) : (this._grid.scrollRowIntoView(toRow), this._grid.scrollCellIntoView(toRow, viewCell)); + } else + ranges.push(last); + this.setSelectedRanges(ranges, void 0, ""), e.preventDefault(), e.stopPropagation(), this._prevKeyDown = e.key; + } + } + } + handleClick(e) { + if (!this._activeSelectionIsRow) + return; + let cell = this._grid.getCellFromEvent(e); + if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell) || !this._grid.getOptions().multiSelect || !e.ctrlKey && !e.shiftKey && !e.metaKey) + return !1; + let selection = this.rangesToRows(this._ranges), idx = selection.indexOf(cell.row); + if (idx === -1 && (e.ctrlKey || e.metaKey)) + selection.push(cell.row), this._grid.setActiveCell(cell.row, cell.cell); + else if (idx !== -1 && (e.ctrlKey || e.metaKey)) + selection = selection.filter((o) => o !== cell.row), this._grid.setActiveCell(cell.row, cell.cell); + else if (selection.length && e.shiftKey) { + let last = selection.pop(), from = Math.min(cell.row, last), to = Math.max(cell.row, last); + selection = []; + for (let i = from; i <= to; i++) + i !== last && selection.push(i); + selection.push(last), this._grid.setActiveCell(cell.row, cell.cell); + } + let tempRanges = this.rowsToRanges(selection); + return this.setSelectedRanges(tempRanges, void 0, ""), e.stopImmediatePropagation(), !0; + } + handleBeforeCellRangeSelected(e, cell) { + if (this._activeSelectionIsRow) { + if (!this._isRowMoveManagerHandler) { + let rowMoveManager = this._grid.getPluginByName("RowMoveManager") || this._grid.getPluginByName("CrossGridRowMoveManager"); + this._isRowMoveManagerHandler = rowMoveManager ? rowMoveManager.isHandlerColumn : Utils.noop; + } + if (this._grid.getEditorLock().isActive() || this._isRowMoveManagerHandler(cell.cell)) + return e.stopPropagation(), !1; + this._grid.setActiveCell(cell.row, cell.cell); + } else if (this._grid.getEditorLock().isActive()) + return e.stopPropagation(), !1; + } + handleCellRangeSelected(_e, args) { + var _a; + if (this._activeSelectionIsRow) { + if (!this._grid.getOptions().multiSelect || !((_a = this._options) != null && _a.selectActiveRow)) + return !1; + this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)], void 0, args.selectionMode); + } else + this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, !1, !1, !0), this.setSelectedRanges([args.range], void 0, args.selectionMode); + return !0; + } + }; + window.Slick && Utils.extend(!0, window, { + Slick: { + HybridSelectionModel: SlickHybridSelectionModel + } + }); +})(); +//# sourceMappingURL=slick.hybridselectionmodel.js.map diff --git a/dist/browser/plugins/slick.hybridselectionmodel.js.map b/dist/browser/plugins/slick.hybridselectionmodel.js.map new file mode 100644 index 00000000..74a4bf11 --- /dev/null +++ b/dist/browser/plugins/slick.hybridselectionmodel.js.map @@ -0,0 +1,7 @@ +{ + "version": 3, + "sources": ["../../../src/plugins/slick.hybridselectionmodel.ts"], + "sourcesContent": ["import { keyCode as keyCode_, SlickEvent as SlickEvent_, SlickEventData as SlickEventData_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js';\r\nimport { Draggable as Draggable_ } from '../slick.interactions.js';\r\nimport { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js';\r\nimport { SlickCellRangeSelector as SlickCellRangeSelector_ } from './slick.cellrangeselector.js';\r\nimport type { Column, CustomDataView, OnActiveCellChangedEventArgs } from '../models/index.js';\r\nimport type { SlickDataView } from '../slick.dataview.js';\r\nimport type { SlickCrossGridRowMoveManager as SlickCrossGridRowMoveManager_ } from './slick.crossgridrowmovemanager.js';\r\nimport type { SlickRowMoveManager as SlickRowMoveManager_ } from './slick.rowmovemanager.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_;\r\nconst keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_;\r\nconst SlickCellRangeSelector = IIFE_ONLY ? Slick.CellRangeSelector : SlickCellRangeSelector_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport declare type RowSelectOverride = (data: OnActiveCellChangedEventArgs, selectionModel: SlickHybridSelectionModel, grid: SlickGrid) => boolean;\r\n\r\nexport interface HybridSelectionModelOption {\r\n selectActiveCell: boolean;\r\n selectActiveRow: boolean;\r\n cellRangeSelector?: SlickCellRangeSelector_;\r\n dragToSelect: boolean;\r\n autoScrollWhenDrag: boolean;\r\n handleRowMoveManagerColumn: boolean; // Row Selection on RowMoveManage column\r\n rowSelectColumnObjectArr: Column[]; // Row Selection on these columns\r\n rowSelectOverride: RowSelectOverride | undefined; // function to toggle Row Selection Models\r\n}\r\n\r\nexport class SlickHybridSelectionModel {\r\n // hybrid selection model is CellSelectionModel except when selecting\r\n // specific columns, which behave as RowSelectionModel\r\n\r\n // --\r\n // public API\r\n pluginName = 'HybridSelectionModel' as const;\r\n onSelectedRangesChanged = new SlickEvent('onSelectedRangesChanged');\r\n\r\n // --\r\n // protected props\r\n protected _cachedPageRowCount = 0;\r\n protected _dataView?: CustomDataView | SlickDataView;\r\n protected _grid!: SlickGrid;\r\n protected _prevSelectedRow?: number;\r\n protected _prevKeyDown = '';\r\n protected _ranges: SlickRange_[] = [];\r\n protected _selector: SlickCellRangeSelector_;\r\n protected _isRowMoveManagerHandler: any;\r\n protected _activeSelectionIsRow = false;\r\n protected _options?: HybridSelectionModelOption;\r\n protected _defaults: HybridSelectionModelOption = {\r\n selectActiveCell: true,\r\n selectActiveRow: true,\r\n dragToSelect: false,\r\n autoScrollWhenDrag: true,\r\n handleRowMoveManagerColumn: true, // Row Selection on RowMoveManage column\r\n rowSelectColumnObjectArr: [], // Row Selection on these columns\r\n rowSelectOverride: undefined, // function to toggle Row Selection Models\r\n cellRangeSelector: undefined\r\n\r\n };\r\n\r\n constructor(options?: { selectActiveCell: boolean; cellRangeSelector: SlickCellRangeSelector_; }) {\r\n if (options === undefined || options.cellRangeSelector === undefined) {\r\n this._selector = new SlickCellRangeSelector({ selectionCss: { border: '2px solid black' } as CSSStyleDeclaration });\r\n } else {\r\n this._selector = options.cellRangeSelector;\r\n }\r\n }\r\n\r\n // Region: Setup\r\n // -----------------------------------------------------------------------------\r\n\r\n init(grid: SlickGrid) {\r\n if (Draggable === undefined) {\r\n throw new Error('Slick.Draggable is undefined, make sure to import \"slick.interactions.js\"');\r\n }\r\n\r\n this._options = Utils.extend(true, {}, this._defaults, this._options);\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n\r\n if (!this._selector && this._options.dragToSelect) {\r\n if (!SlickCellRangeDecorator) {\r\n throw new Error('Slick.CellRangeDecorator is required when option dragToSelect set to true');\r\n }\r\n this._selector = new SlickCellRangeSelector({\r\n selectionCss: { border: 'none' } as CSSStyleDeclaration,\r\n autoScroll: this._options.autoScrollWhenDrag\r\n });\r\n }\r\n\r\n if (grid.hasDataView()) {\r\n this._dataView = grid.getData();\r\n }\r\n this._grid.onActiveCellChanged.subscribe(this.handleActiveCellChange.bind(this));\r\n this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this));\r\n this._grid.onClick.subscribe(this.handleClick.bind(this));\r\n if (this._selector) {\r\n grid.registerPlugin(this._selector);\r\n this._selector.onCellRangeSelecting.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n }\r\n }\r\n\r\n destroy() {\r\n this._grid.onActiveCellChanged.unsubscribe(this.handleActiveCellChange.bind(this));\r\n this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this));\r\n this._grid.onClick.unsubscribe(this.handleClick.bind(this));\r\n this._selector.onCellRangeSelecting.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n this._grid.unregisterPlugin(this._selector);\r\n this._selector?.destroy();\r\n }\r\n\r\n // Region: CellSelectionModel Members\r\n // -----------------------------------------------------------------------------\r\n\r\n protected removeInvalidRanges(ranges: SlickRange_[]) {\r\n const result: SlickRange_[] = [];\r\n\r\n for (let i = 0; i < ranges.length; i++) {\r\n const r = ranges[i];\r\n if (this._grid.canCellBeSelected(r.fromRow, r.fromCell) && this._grid.canCellBeSelected(r.toRow, r.toCell)) {\r\n result.push(r);\r\n }\r\n }\r\n\r\n return result;\r\n }\r\n\r\n protected rangesAreEqual(range1: SlickRange_[], range2: SlickRange_[]) {\r\n let areDifferent = (range1.length !== range2.length);\r\n if (!areDifferent) {\r\n for (let i = 0; i < range1.length; i++) {\r\n if (\r\n range1[i].fromCell !== range2[i].fromCell\r\n || range1[i].fromRow !== range2[i].fromRow\r\n || range1[i].toCell !== range2[i].toCell\r\n || range1[i].toRow !== range2[i].toRow\r\n ) {\r\n areDifferent = true;\r\n break;\r\n }\r\n }\r\n }\r\n return !areDifferent;\r\n }\r\n\r\n // Region: RowSelectionModel Members\r\n // -----------------------------------------------------------------------------\r\n\r\n protected rangesToRows(ranges: SlickRange_[]): number[] {\r\n const rows: number[] = [];\r\n for (let i = 0; i < ranges.length; i++) {\r\n for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {\r\n rows.push(j);\r\n }\r\n }\r\n return rows;\r\n }\r\n\r\n protected rowsToRanges(rows: number[]) {\r\n const ranges: SlickRange_[] = [];\r\n const lastCell = this._grid.getColumns().length - 1;\r\n rows.forEach(row => ranges.push(new SlickRange(row, 0, row, lastCell)));\r\n return ranges;\r\n }\r\n\r\n protected getRowsRange(from: number, to: number) {\r\n let i;\r\n const rows: number[] = [];\r\n for (i = from; i <= to; i++) {\r\n rows.push(i);\r\n }\r\n for (i = to; i < from; i++) {\r\n rows.push(i);\r\n }\r\n return rows;\r\n }\r\n\r\n getSelectedRows() {\r\n return this.rangesToRows(this._ranges);\r\n }\r\n\r\n setSelectedRows(rows: number[]) {\r\n this.setSelectedRanges(this.rowsToRanges(rows), 'SlickRowSelectionModel.setSelectedRows', '');\r\n }\r\n\r\n // Region: Shared Members\r\n // -----------------------------------------------------------------------------\r\n\r\n /** Provide a way to force a recalculation of page row count (for example on grid resize) */\r\n resetPageRowCount() {\r\n this._cachedPageRowCount = 0;\r\n }\r\n\r\n setSelectedRanges(ranges: SlickRange_[], caller = 'SlickHybridSelectionModel.setSelectedRanges', selectionMode: string) {\r\n // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged\r\n if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; }\r\n\r\n // if range has not changed, don't fire onSelectedRangesChanged\r\n const rangeHasChanged = !this.rangesAreEqual(this._ranges, ranges);\r\n\r\n if (this._activeSelectionIsRow) {\r\n this._ranges = ranges;\r\n\r\n // provide extra \"caller\" argument through SlickEventData event to avoid breaking the previous pubsub event structure\r\n // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`\r\n const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges);\r\n this.onSelectedRangesChanged.notify(this._ranges, eventData);\r\n } else {\r\n this._ranges = this.removeInvalidRanges(ranges);\r\n if (rangeHasChanged) {\r\n // provide extra \"caller\" argument through SlickEventData event to avoid breaking the previous pubsub event structure\r\n // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`\r\n const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges);\r\n this.onSelectedRangesChanged.notify(this._ranges, eventData);\r\n }\r\n }\r\n }\r\n\r\n currentSelectionModeIsRow() {\r\n return this._activeSelectionIsRow;\r\n }\r\n\r\n getSelectedRanges() {\r\n return this._ranges;\r\n }\r\n\r\n refreshSelections() {\r\n if (this._activeSelectionIsRow) {\r\n this.setSelectedRows(this.getSelectedRows());\r\n } else {\r\n this.setSelectedRanges(this.getSelectedRanges(), undefined, '');\r\n }\r\n }\r\n\r\n getRowMoveManagerPlugin(): SlickRowMoveManager_ | SlickCrossGridRowMoveManager_ | undefined {\r\n return this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager');\r\n }\r\n\r\n rowSelectionModelIsActive(data: OnActiveCellChangedEventArgs): boolean {\r\n // work out required selection mode\r\n if (this._options?.rowSelectOverride) {\r\n return this._options?.rowSelectOverride(data, this, this._grid);\r\n }\r\n\r\n if (this._options?.handleRowMoveManagerColumn) {\r\n const rowMoveManager = this.getRowMoveManagerPlugin();\r\n if (rowMoveManager?.isHandlerColumn(data.cell)) { return true; }\r\n }\r\n\r\n const targetColumn = this._grid.getVisibleColumns()[data.cell];\r\n return this._options?.rowSelectColumnObjectArr.includes(targetColumn) || false;\r\n }\r\n\r\n protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) {\r\n this._prevSelectedRow = undefined;\r\n const isCellDefined = Utils.isDefined(args.cell);\r\n const isRowDefined = Utils.isDefined(args.row);\r\n this._activeSelectionIsRow = this.rowSelectionModelIsActive(args);\r\n\r\n if (this._activeSelectionIsRow) {\r\n if (this._options?.selectActiveRow && args.row !== null) {\r\n this.setSelectedRanges([new Slick.Range(args.row, 0, args.row, this._grid.getColumns().length - 1)], undefined, '');\r\n }\r\n } else {\r\n if (this._options?.selectActiveCell && isRowDefined && isCellDefined) {\r\n this.setSelectedRanges([new SlickRange(args.row, args.cell)], undefined, '');\r\n } else if (!this._options?.selectActiveCell || (!isRowDefined && !isCellDefined)) {\r\n // clear the previous selection once the cell changes\r\n this.setSelectedRanges([], undefined, '');\r\n }\r\n }\r\n }\r\n\r\n protected isKeyAllowed(key: string) {\r\n return ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'PageDown', 'PageUp', 'Home', 'End'].some(k => k === key);\r\n }\r\n\r\n protected handleKeyDown(e: SlickEventData_) {\r\n if (!this._activeSelectionIsRow) {\r\n let ranges: SlickRange_[], last: SlickRange_;\r\n const colLn = this._grid.getColumns().length;\r\n const active = this._grid.getActiveCell();\r\n let dataLn = 0;\r\n if (this._dataView && 'getPagingInfo' in this._dataView) {\r\n dataLn = this._dataView?.getPagingInfo().pageSize || this._dataView.getLength();\r\n } else {\r\n dataLn = this._grid.getDataLength();\r\n }\r\n\r\n if (active && (e.shiftKey || e.ctrlKey) && !e.altKey && this.isKeyAllowed(e.key as string)) {\r\n ranges = this.getSelectedRanges().slice();\r\n if (!ranges.length) {\r\n ranges.push(new SlickRange(active.row, active.cell));\r\n }\r\n // keyboard can work with last range only\r\n last = ranges.pop() as SlickRange_;\r\n\r\n // can't handle selection out of active cell\r\n if (!last.contains(active.row, active.cell)) {\r\n last = new SlickRange(active.row, active.cell);\r\n }\r\n\r\n let dRow = last.toRow - last.fromRow;\r\n let dCell = last.toCell - last.fromCell;\r\n\r\n // walking direction\r\n const dirRow = active.row === last.fromRow ? 1 : -1;\r\n const dirCell = active.cell === last.fromCell ? 1 : -1;\r\n const isSingleKeyMove = e.key!.startsWith('Arrow');\r\n let toCell: undefined | number = undefined;\r\n let toRow = 0;\r\n\r\n if (isSingleKeyMove && !e.ctrlKey) {\r\n // single cell move: (Arrow{Up/ArrowDown/ArrowLeft/ArrowRight})\r\n if (e.key === 'ArrowLeft') {\r\n dCell -= dirCell;\r\n } else if (e.key === 'ArrowRight') {\r\n dCell += dirCell;\r\n } else if (e.key === 'ArrowUp') {\r\n dRow -= dirRow;\r\n } else if (e.key === 'ArrowDown') {\r\n dRow += dirRow;\r\n }\r\n toRow = active.row + dirRow * dRow;\r\n } else {\r\n // multiple cell moves: (Home, End, Page{Up/Down})\r\n if (this._cachedPageRowCount < 1) {\r\n this._cachedPageRowCount = this._grid.getViewportRowCount();\r\n }\r\n if (this._prevSelectedRow === undefined) {\r\n this._prevSelectedRow = active.row;\r\n }\r\n\r\n if (e.shiftKey && !e.ctrlKey && e.key === 'Home') {\r\n toCell = 0;\r\n toRow = active.row;\r\n } else if (e.shiftKey && !e.ctrlKey && e.key === 'End') {\r\n toCell = colLn - 1;\r\n toRow = active.row;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'Home') {\r\n toCell = 0;\r\n toRow = 0;\r\n } else if (e.ctrlKey && e.shiftKey && e.key === 'End') {\r\n toCell = colLn - 1;\r\n toRow = dataLn - 1;\r\n } else if (e.key === 'PageUp') {\r\n if (this._prevSelectedRow >= 0) {\r\n toRow = this._prevSelectedRow - this._cachedPageRowCount;\r\n }\r\n if (toRow < 0) {\r\n toRow = 0;\r\n }\r\n } else if (e.key === 'PageDown') {\r\n if (this._prevSelectedRow <= dataLn - 1) {\r\n toRow = this._prevSelectedRow + this._cachedPageRowCount;\r\n }\r\n if (toRow > dataLn - 1) {\r\n toRow = dataLn - 1;\r\n }\r\n }\r\n this._prevSelectedRow = toRow;\r\n }\r\n\r\n // define new selection range\r\n toCell ??= active.cell + dirCell * dCell;\r\n const new_last = new SlickRange(active.row, active.cell, toRow, toCell);\r\n if (this.removeInvalidRanges([new_last]).length) {\r\n ranges.push(new_last);\r\n const viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow;\r\n const viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell;\r\n\r\n if (isSingleKeyMove) {\r\n this._grid.scrollRowIntoView(viewRow);\r\n this._grid.scrollCellIntoView(viewRow, viewCell);\r\n } else {\r\n this._grid.scrollRowIntoView(toRow);\r\n this._grid.scrollCellIntoView(toRow, viewCell);\r\n }\r\n } else {\r\n ranges.push(last);\r\n }\r\n\r\n this.setSelectedRanges(ranges, undefined, '');\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n this._prevKeyDown = e.key as string;\r\n }\r\n } else {\r\n const activeRow = this._grid.getActiveCell();\r\n if (this._grid.getOptions().multiSelect && activeRow\r\n && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey\r\n && (e.which === keyCode.UP || e.which === keyCode.DOWN)) {\r\n let selectedRows = this.getSelectedRows();\r\n selectedRows.sort(function (x, y) {\r\n return x - y;\r\n });\r\n\r\n if (!selectedRows.length) {\r\n selectedRows = [activeRow.row];\r\n }\r\n\r\n let top = selectedRows[0];\r\n let bottom = selectedRows[selectedRows.length - 1];\r\n let active: number;\r\n\r\n if (e.which === keyCode.DOWN) {\r\n active = activeRow.row < bottom || top === bottom ? ++bottom : ++top;\r\n } else {\r\n active = activeRow.row < bottom ? --bottom : --top;\r\n }\r\n\r\n if (active >= 0 && active < this._grid.getDataLength()) {\r\n this._grid.scrollRowIntoView(active);\r\n const tempRanges = this.rowsToRanges(this.getRowsRange(top, bottom));\r\n this.setSelectedRanges(tempRanges, undefined, '');\r\n }\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n }\r\n }\r\n }\r\n\r\n protected handleClick(e: SlickEventData_): boolean | void {\r\n if (!this._activeSelectionIsRow) { return; }\r\n\r\n const cell = this._grid.getCellFromEvent(e);\r\n if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {\r\n return false;\r\n }\r\n\r\n if (!this._grid.getOptions().multiSelect || (\r\n !e.ctrlKey && !e.shiftKey && !e.metaKey)) {\r\n return false;\r\n }\r\n\r\n let selection = this.rangesToRows(this._ranges);\r\n const idx = selection.indexOf(cell.row);\r\n\r\n if (idx === -1 && (e.ctrlKey || e.metaKey)) {\r\n selection.push(cell.row);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) {\r\n selection = selection.filter((o) => o !== cell.row);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n } else if (selection.length && e.shiftKey) {\r\n const last = selection.pop() as number;\r\n const from = Math.min(cell.row, last);\r\n const to = Math.max(cell.row, last);\r\n selection = [];\r\n for (let i = from; i <= to; i++) {\r\n if (i !== last) {\r\n selection.push(i);\r\n }\r\n }\r\n selection.push(last);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n }\r\n\r\n const tempRanges = this.rowsToRanges(selection);\r\n this.setSelectedRanges(tempRanges, undefined, '');\r\n e.stopImmediatePropagation();\r\n\r\n return true;\r\n }\r\n\r\n protected handleBeforeCellRangeSelected(e: SlickEventData_, cell: { row: number; cell: number; }): boolean | void {\r\n if (this._activeSelectionIsRow) {\r\n if (!this._isRowMoveManagerHandler) {\r\n const rowMoveManager = this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager');\r\n this._isRowMoveManagerHandler = rowMoveManager ? rowMoveManager.isHandlerColumn : Utils.noop;\r\n }\r\n if (this._grid.getEditorLock().isActive() || this._isRowMoveManagerHandler(cell.cell)) {\r\n e.stopPropagation();\r\n return false;\r\n }\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n } else {\r\n if (this._grid.getEditorLock().isActive()) {\r\n e.stopPropagation();\r\n return false;\r\n }\r\n }\r\n }\r\n\r\n protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; selectionMode: string; }) {\r\n if (this._activeSelectionIsRow) {\r\n if (!this._grid.getOptions().multiSelect || !this._options?.selectActiveRow) {\r\n return false;\r\n }\r\n this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)], undefined, args.selectionMode);\r\n } else {\r\n this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true);\r\n this.setSelectedRanges([args.range], undefined, args.selectionMode);\r\n }\r\n return true;\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(true, window, {\r\n Slick: {\r\n HybridSelectionModel: SlickHybridSelectionModel\r\n }\r\n });\r\n}\r\n"], + "mappings": "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", + "names": [] +} diff --git a/dist/browser/plugins/slick.rowselectionmodel.js b/dist/browser/plugins/slick.rowselectionmodel.js index fce54f7f..42a3302b 100644 --- a/dist/browser/plugins/slick.rowselectionmodel.js +++ b/dist/browser/plugins/slick.rowselectionmodel.js @@ -75,11 +75,11 @@ setSelectedRows(rows) { this.setSelectedRanges(this.rowsToRanges(rows), "SlickRowSelectionModel.setSelectedRows"); } - setSelectedRanges(ranges, caller = "SlickRowSelectionModel.setSelectedRanges") { + setSelectedRanges(ranges, caller = "SlickRowSelectionModel.setSelectedRanges", selectionMode) { if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) return; this._ranges = ranges; - let eventData = new SlickEventData(new CustomEvent("click", { detail: { caller } }), this._ranges); + let eventData = new SlickEventData(new CustomEvent("click", { detail: { caller, selectionMode } }), this._ranges); this.onSelectedRangesChanged.notify(this._ranges, eventData); } getSelectedRanges() { @@ -138,7 +138,7 @@ handleCellRangeSelected(_e, args) { if (!this._grid.getOptions().multiSelect || !this._options.selectActiveRow) return !1; - this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)]); + this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)], void 0, args.selectionMode); } }; window.Slick && Utils.extend(!0, window, { diff --git a/dist/browser/plugins/slick.rowselectionmodel.js.map b/dist/browser/plugins/slick.rowselectionmodel.js.map index c1d7eb5e..1b33982c 100644 --- a/dist/browser/plugins/slick.rowselectionmodel.js.map +++ b/dist/browser/plugins/slick.rowselectionmodel.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../../src/plugins/slick.rowselectionmodel.ts"], - "sourcesContent": ["import {\r\n keyCode as keyCode_,\r\n SlickEvent as SlickEvent_,\r\n SlickEventData as SlickEventData_,\r\n SlickEventHandler as SlickEventHandler_,\r\n SlickRange as SlickRange_,\r\n Utils as Utils_\r\n} from '../slick.core.js';\r\nimport { Draggable as Draggable_ } from '../slick.interactions.js';\r\nimport { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js';\r\nimport { SlickCellRangeSelector as SlickCellRangeSelector_ } from './slick.cellrangeselector.js';\r\nimport type { SlickCrossGridRowMoveManager as SlickCrossGridRowMoveManager_ } from './slick.crossgridrowmovemanager.js';\r\nimport type { SlickRowMoveManager as SlickRowMoveManager_ } from './slick.rowmovemanager.js';\r\nimport type { OnActiveCellChangedEventArgs, RowSelectionModelOption } from '../models/index.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_;\r\nconst keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_;\r\nconst SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_;\r\nconst SlickCellRangeSelector = IIFE_ONLY ? Slick.CellRangeSelector : SlickCellRangeSelector_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\r\nconst SlickEventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport class SlickRowSelectionModel {\r\n // --\r\n // public API\r\n pluginName = 'RowSelectionModel' as const;\r\n onSelectedRangesChanged = new SlickEvent('onSelectedRangesChanged');\r\n // _handler, _inHandler, _isRowMoveManagerHandler, _options, wrapHandler\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _ranges: SlickRange_[] = [];\r\n protected _eventHandler = new SlickEventHandler();\r\n protected _inHandler = false;\r\n protected _selector?: SlickCellRangeSelector_;\r\n protected _isRowMoveManagerHandler: any;\r\n protected _options: RowSelectionModelOption;\r\n protected _defaults: RowSelectionModelOption = {\r\n selectActiveRow: true,\r\n dragToSelect: false,\r\n autoScrollWhenDrag: true,\r\n cellRangeSelector: undefined\r\n };\r\n\r\n constructor(options?: Partial) {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n if (Draggable === undefined) {\r\n throw new Error('Slick.Draggable is undefined, make sure to import \"slick.interactions.js\"');\r\n }\r\n\r\n this._selector = this._options.cellRangeSelector;\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n\r\n if (!this._selector && this._options.dragToSelect) {\r\n if (!SlickCellRangeDecorator) {\r\n throw new Error('Slick.CellRangeDecorator is required when option dragToSelect set to true');\r\n }\r\n this._selector = new SlickCellRangeSelector({\r\n selectionCss: { border: 'none' } as CSSStyleDeclaration,\r\n autoScroll: this._options.autoScrollWhenDrag\r\n });\r\n }\r\n\r\n this._eventHandler.subscribe(this._grid.onActiveCellChanged, this.wrapHandler(this.handleActiveCellChange).bind(this));\r\n this._eventHandler.subscribe(this._grid.onKeyDown, this.wrapHandler(this.handleKeyDown).bind(this));\r\n this._eventHandler.subscribe(this._grid.onClick, this.wrapHandler(this.handleClick).bind(this));\r\n if (this._selector) {\r\n grid.registerPlugin(this._selector);\r\n this._selector.onCellRangeSelecting.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n }\r\n }\r\n\r\n destroy() {\r\n this._eventHandler.unsubscribeAll();\r\n if (this._selector) {\r\n this._selector.onCellRangeSelecting.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n this._grid.unregisterPlugin(this._selector);\r\n if (this._selector.destroy) {\r\n this._selector.destroy();\r\n }\r\n }\r\n }\r\n\r\n protected wrapHandler(handler: (...args: any) => void) {\r\n return (...args: any) => {\r\n if (!this._inHandler) {\r\n this._inHandler = true;\r\n handler.apply(this, args);\r\n this._inHandler = false;\r\n }\r\n };\r\n }\r\n\r\n protected rangesToRows(ranges: SlickRange_[]): number[] {\r\n const rows: number[] = [];\r\n for (let i = 0; i < ranges.length; i++) {\r\n for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {\r\n rows.push(j);\r\n }\r\n }\r\n return rows;\r\n }\r\n\r\n protected rowsToRanges(rows: number[]) {\r\n const ranges: SlickRange_[] = [];\r\n const lastCell = this._grid.getColumns().length - 1;\r\n rows.forEach(row => ranges.push(new SlickRange(row, 0, row, lastCell)));\r\n return ranges;\r\n }\r\n\r\n protected getRowsRange(from: number, to: number) {\r\n let i;\r\n const rows: number[] = [];\r\n for (i = from; i <= to; i++) {\r\n rows.push(i);\r\n }\r\n for (i = to; i < from; i++) {\r\n rows.push(i);\r\n }\r\n return rows;\r\n }\r\n\r\n getSelectedRows() {\r\n return this.rangesToRows(this._ranges);\r\n }\r\n\r\n setSelectedRows(rows: number[]) {\r\n this.setSelectedRanges(this.rowsToRanges(rows), 'SlickRowSelectionModel.setSelectedRows');\r\n }\r\n\r\n setSelectedRanges(ranges: SlickRange_[], caller = 'SlickRowSelectionModel.setSelectedRanges') {\r\n // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged\r\n if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) {\r\n return;\r\n }\r\n this._ranges = ranges;\r\n\r\n // provide extra \"caller\" argument through SlickEventData event to avoid breaking the previous pubsub event structure\r\n // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`\r\n const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller } }), this._ranges);\r\n this.onSelectedRangesChanged.notify(this._ranges, eventData);\r\n }\r\n\r\n getSelectedRanges() {\r\n return this._ranges;\r\n }\r\n\r\n refreshSelections() {\r\n this.setSelectedRows(this.getSelectedRows());\r\n }\r\n\r\n protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) {\r\n if (this._options.selectActiveRow && Utils.isDefined(args.row)) {\r\n this.setSelectedRanges([new SlickRange(args.row, 0, args.row, this._grid.getColumns().length - 1)]);\r\n }\r\n }\r\n\r\n protected handleKeyDown(e: KeyboardEvent) {\r\n const activeRow = this._grid.getActiveCell();\r\n if (this._grid.getOptions().multiSelect && activeRow\r\n && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey\r\n && (e.which === keyCode.UP || e.which === keyCode.DOWN)) {\r\n let selectedRows = this.getSelectedRows();\r\n selectedRows.sort(function (x, y) {\r\n return x - y;\r\n });\r\n\r\n if (!selectedRows.length) {\r\n selectedRows = [activeRow.row];\r\n }\r\n\r\n let top = selectedRows[0];\r\n let bottom = selectedRows[selectedRows.length - 1];\r\n let active: number;\r\n\r\n if (e.which === keyCode.DOWN) {\r\n active = activeRow.row < bottom || top === bottom ? ++bottom : ++top;\r\n } else {\r\n active = activeRow.row < bottom ? --bottom : --top;\r\n }\r\n\r\n if (active >= 0 && active < this._grid.getDataLength()) {\r\n this._grid.scrollRowIntoView(active);\r\n const tempRanges = this.rowsToRanges(this.getRowsRange(top, bottom));\r\n this.setSelectedRanges(tempRanges);\r\n }\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n }\r\n }\r\n\r\n protected handleClick(e: MouseEvent): boolean | void {\r\n const cell = this._grid.getCellFromEvent(e);\r\n if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {\r\n return false;\r\n }\r\n\r\n if (!this._grid.getOptions().multiSelect || (\r\n !e.ctrlKey && !e.shiftKey && !e.metaKey)) {\r\n return false;\r\n }\r\n\r\n let selection = this.rangesToRows(this._ranges);\r\n const idx = selection.indexOf(cell.row);\r\n\r\n if (idx === -1 && (e.ctrlKey || e.metaKey)) {\r\n selection.push(cell.row);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) {\r\n selection = selection.filter((o) => o !== cell.row);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n } else if (selection.length && e.shiftKey) {\r\n const last = selection.pop() as number;\r\n const from = Math.min(cell.row, last);\r\n const to = Math.max(cell.row, last);\r\n selection = [];\r\n for (let i = from; i <= to; i++) {\r\n if (i !== last) {\r\n selection.push(i);\r\n }\r\n }\r\n selection.push(last);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n }\r\n\r\n const tempRanges = this.rowsToRanges(selection);\r\n this.setSelectedRanges(tempRanges);\r\n e.stopImmediatePropagation();\r\n\r\n return true;\r\n }\r\n\r\n protected handleBeforeCellRangeSelected(e: SlickEventData_, cell: { row: number; cell: number; }): boolean | void {\r\n if (!this._isRowMoveManagerHandler) {\r\n const rowMoveManager = this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager');\r\n this._isRowMoveManagerHandler = rowMoveManager ? rowMoveManager.isHandlerColumn : Utils.noop;\r\n }\r\n if (this._grid.getEditorLock().isActive() || this._isRowMoveManagerHandler(cell.cell)) {\r\n e.stopPropagation();\r\n return false;\r\n }\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n }\r\n\r\n protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; }): boolean | void {\r\n if (!this._grid.getOptions().multiSelect || !this._options.selectActiveRow) {\r\n return false;\r\n }\r\n this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)]);\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(true, window, {\r\n Slick: {\r\n RowSelectionModel: SlickRowSelectionModel\r\n }\r\n });\r\n}\r\n\r\n"], - "mappings": "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", + "sourcesContent": ["import {\r\n // CellSelectionMode as CellSelectionMode_,\r\n keyCode as keyCode_,\r\n SlickEvent as SlickEvent_,\r\n SlickEventData as SlickEventData_,\r\n SlickEventHandler as SlickEventHandler_,\r\n SlickRange as SlickRange_,\r\n Utils as Utils_\r\n} from '../slick.core.js';\r\nimport { Draggable as Draggable_ } from '../slick.interactions.js';\r\nimport { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js';\r\nimport { SlickCellRangeSelector as SlickCellRangeSelector_ } from './slick.cellrangeselector.js';\r\nimport type { SlickCrossGridRowMoveManager as SlickCrossGridRowMoveManager_ } from './slick.crossgridrowmovemanager.js';\r\nimport type { SlickRowMoveManager as SlickRowMoveManager_ } from './slick.rowmovemanager.js';\r\nimport type { OnActiveCellChangedEventArgs, RowSelectionModelOption } from '../models/index.js';\r\nimport type { SlickGrid } from '../slick.grid.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_;\r\nconst keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_;\r\nconst SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_;\r\nconst SlickCellRangeSelector = IIFE_ONLY ? Slick.CellRangeSelector : SlickCellRangeSelector_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\r\nconst SlickEventHandler = IIFE_ONLY ? Slick.EventHandler : SlickEventHandler_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\n\r\nexport class SlickRowSelectionModel {\r\n // --\r\n // public API\r\n pluginName = 'RowSelectionModel' as const;\r\n onSelectedRangesChanged = new SlickEvent('onSelectedRangesChanged');\r\n // _handler, _inHandler, _isRowMoveManagerHandler, _options, wrapHandler\r\n // --\r\n // protected props\r\n protected _grid!: SlickGrid;\r\n protected _ranges: SlickRange_[] = [];\r\n protected _eventHandler = new SlickEventHandler();\r\n protected _inHandler = false;\r\n protected _selector?: SlickCellRangeSelector_;\r\n protected _isRowMoveManagerHandler: any;\r\n protected _options: RowSelectionModelOption;\r\n protected _defaults: RowSelectionModelOption = {\r\n selectActiveRow: true,\r\n dragToSelect: false,\r\n autoScrollWhenDrag: true,\r\n cellRangeSelector: undefined\r\n };\r\n\r\n constructor(options?: Partial) {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n }\r\n\r\n init(grid: SlickGrid) {\r\n if (Draggable === undefined) {\r\n throw new Error('Slick.Draggable is undefined, make sure to import \"slick.interactions.js\"');\r\n }\r\n\r\n this._selector = this._options.cellRangeSelector;\r\n this._grid = grid;\r\n Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this);\r\n\r\n if (!this._selector && this._options.dragToSelect) {\r\n if (!SlickCellRangeDecorator) {\r\n throw new Error('Slick.CellRangeDecorator is required when option dragToSelect set to true');\r\n }\r\n this._selector = new SlickCellRangeSelector({\r\n selectionCss: { border: 'none' } as CSSStyleDeclaration,\r\n autoScroll: this._options.autoScrollWhenDrag\r\n });\r\n }\r\n\r\n this._eventHandler.subscribe(this._grid.onActiveCellChanged, this.wrapHandler(this.handleActiveCellChange).bind(this));\r\n this._eventHandler.subscribe(this._grid.onKeyDown, this.wrapHandler(this.handleKeyDown).bind(this));\r\n this._eventHandler.subscribe(this._grid.onClick, this.wrapHandler(this.handleClick).bind(this));\r\n if (this._selector) {\r\n grid.registerPlugin(this._selector);\r\n this._selector.onCellRangeSelecting.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n }\r\n }\r\n\r\n destroy() {\r\n this._eventHandler.unsubscribeAll();\r\n if (this._selector) {\r\n this._selector.onCellRangeSelecting.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this));\r\n this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this));\r\n this._grid.unregisterPlugin(this._selector);\r\n if (this._selector.destroy) {\r\n this._selector.destroy();\r\n }\r\n }\r\n }\r\n\r\n protected wrapHandler(handler: (...args: any) => void) {\r\n return (...args: any) => {\r\n if (!this._inHandler) {\r\n this._inHandler = true;\r\n handler.apply(this, args);\r\n this._inHandler = false;\r\n }\r\n };\r\n }\r\n\r\n protected rangesToRows(ranges: SlickRange_[]): number[] {\r\n const rows: number[] = [];\r\n for (let i = 0; i < ranges.length; i++) {\r\n for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {\r\n rows.push(j);\r\n }\r\n }\r\n return rows;\r\n }\r\n\r\n protected rowsToRanges(rows: number[]) {\r\n const ranges: SlickRange_[] = [];\r\n const lastCell = this._grid.getColumns().length - 1;\r\n rows.forEach(row => ranges.push(new SlickRange(row, 0, row, lastCell)));\r\n return ranges;\r\n }\r\n\r\n protected getRowsRange(from: number, to: number) {\r\n let i;\r\n const rows: number[] = [];\r\n for (i = from; i <= to; i++) {\r\n rows.push(i);\r\n }\r\n for (i = to; i < from; i++) {\r\n rows.push(i);\r\n }\r\n return rows;\r\n }\r\n\r\n getSelectedRows() {\r\n return this.rangesToRows(this._ranges);\r\n }\r\n\r\n setSelectedRows(rows: number[]) {\r\n this.setSelectedRanges(this.rowsToRanges(rows), 'SlickRowSelectionModel.setSelectedRows');\r\n }\r\n\r\n setSelectedRanges(ranges: SlickRange_[], caller = 'SlickRowSelectionModel.setSelectedRanges', selectionMode?: string) {\r\n // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged\r\n if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) {\r\n return;\r\n }\r\n this._ranges = ranges;\r\n\r\n // provide extra \"caller\" argument through SlickEventData event to avoid breaking the previous pubsub event structure\r\n // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged`\r\n const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges);\r\n this.onSelectedRangesChanged.notify(this._ranges, eventData);\r\n }\r\n\r\n getSelectedRanges() {\r\n return this._ranges;\r\n }\r\n\r\n refreshSelections() {\r\n this.setSelectedRows(this.getSelectedRows());\r\n }\r\n\r\n protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) {\r\n if (this._options.selectActiveRow && Utils.isDefined(args.row)) {\r\n this.setSelectedRanges([new SlickRange(args.row, 0, args.row, this._grid.getColumns().length - 1)]);\r\n }\r\n }\r\n\r\n protected handleKeyDown(e: KeyboardEvent) {\r\n const activeRow = this._grid.getActiveCell();\r\n if (this._grid.getOptions().multiSelect && activeRow\r\n && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey\r\n && (e.which === keyCode.UP || e.which === keyCode.DOWN)) {\r\n let selectedRows = this.getSelectedRows();\r\n selectedRows.sort(function (x, y) {\r\n return x - y;\r\n });\r\n\r\n if (!selectedRows.length) {\r\n selectedRows = [activeRow.row];\r\n }\r\n\r\n let top = selectedRows[0];\r\n let bottom = selectedRows[selectedRows.length - 1];\r\n let active: number;\r\n\r\n if (e.which === keyCode.DOWN) {\r\n active = activeRow.row < bottom || top === bottom ? ++bottom : ++top;\r\n } else {\r\n active = activeRow.row < bottom ? --bottom : --top;\r\n }\r\n\r\n if (active >= 0 && active < this._grid.getDataLength()) {\r\n this._grid.scrollRowIntoView(active);\r\n const tempRanges = this.rowsToRanges(this.getRowsRange(top, bottom));\r\n this.setSelectedRanges(tempRanges);\r\n }\r\n\r\n e.preventDefault();\r\n e.stopPropagation();\r\n }\r\n }\r\n\r\n protected handleClick(e: MouseEvent): boolean | void {\r\n const cell = this._grid.getCellFromEvent(e);\r\n if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) {\r\n return false;\r\n }\r\n\r\n if (!this._grid.getOptions().multiSelect || (\r\n !e.ctrlKey && !e.shiftKey && !e.metaKey)) {\r\n return false;\r\n }\r\n\r\n let selection = this.rangesToRows(this._ranges);\r\n const idx = selection.indexOf(cell.row);\r\n\r\n if (idx === -1 && (e.ctrlKey || e.metaKey)) {\r\n selection.push(cell.row);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) {\r\n selection = selection.filter((o) => o !== cell.row);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n } else if (selection.length && e.shiftKey) {\r\n const last = selection.pop() as number;\r\n const from = Math.min(cell.row, last);\r\n const to = Math.max(cell.row, last);\r\n selection = [];\r\n for (let i = from; i <= to; i++) {\r\n if (i !== last) {\r\n selection.push(i);\r\n }\r\n }\r\n selection.push(last);\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n }\r\n\r\n const tempRanges = this.rowsToRanges(selection);\r\n this.setSelectedRanges(tempRanges);\r\n e.stopImmediatePropagation();\r\n\r\n return true;\r\n }\r\n\r\n protected handleBeforeCellRangeSelected(e: SlickEventData_, cell: { row: number; cell: number; }): boolean | void {\r\n if (!this._isRowMoveManagerHandler) {\r\n const rowMoveManager = this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager');\r\n this._isRowMoveManagerHandler = rowMoveManager ? rowMoveManager.isHandlerColumn : Utils.noop;\r\n }\r\n if (this._grid.getEditorLock().isActive() || this._isRowMoveManagerHandler(cell.cell)) {\r\n e.stopPropagation();\r\n return false;\r\n }\r\n this._grid.setActiveCell(cell.row, cell.cell);\r\n }\r\n\r\n protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; selectionMode: string; }): boolean | void {\r\n if (!this._grid.getOptions().multiSelect || !this._options.selectActiveRow) {\r\n return false;\r\n }\r\n this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)], undefined, args.selectionMode);\r\n }\r\n}\r\n\r\n// extend Slick namespace on window object when building as iife\r\nif (IIFE_ONLY && window.Slick) {\r\n Utils.extend(true, window, {\r\n Slick: {\r\n RowSelectionModel: SlickRowSelectionModel\r\n }\r\n });\r\n}\r\n\r\n"], + "mappings": "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", "names": [] } diff --git a/dist/browser/slick.core.js b/dist/browser/slick.core.js index d86798d2..598a48d6 100644 --- a/dist/browser/slick.core.js +++ b/dist/browser/slick.core.js @@ -21,7 +21,9 @@ var Slick = (() => { var slick_core_exports = {}; __export(slick_core_exports, { BindingEventService: () => BindingEventService, + CellSelectionMode: () => CellSelectionMode, ColAutosizeMode: () => ColAutosizeMode, + DragExtendHandle: () => DragExtendHandle, EditorLock: () => EditorLock, Event: () => Event, EventData: () => EventData, @@ -34,6 +36,7 @@ var Slick = (() => { Range: () => Range, RegexSanitizer: () => RegexSanitizer, RowSelectionMode: () => RowSelectionMode, + SlickDragExtendHandle: () => SlickDragExtendHandle, SlickEditorLock: () => SlickEditorLock, SlickEvent: () => SlickEvent, SlickEventData: () => SlickEventData, @@ -279,6 +282,23 @@ var Slick = (() => { toString() { return this.isSingleCell() ? `(${this.fromRow}:${this.fromCell})` : `(${this.fromRow}:${this.fromCell} - ${this.toRow}:${this.toCell})`; } + }, SlickDragExtendHandle = class { + constructor(gridUid) { + __publicField(this, "id"); + __publicField(this, "cssClass", "slick-drag-replace-handle"); + this.id = gridUid + "_drag_replace_handle"; + } + getHandleHtml() { + return '
'; + } + removeEl() { + let dragReplaceEl = document.getElementById(this.id); + dragReplaceEl && dragReplaceEl.remove(); + } + createEl(activeCellNode) { + let dragReplaceEl = document.createElement("div"); + dragReplaceEl.classList.add("slick-drag-replace-handle"), dragReplaceEl.setAttribute("id", this.id), activeCellNode.appendChild(dragReplaceEl), console.log("DragReplaceEl.createEl"); + } }, SlickNonDataItem = class { constructor() { __publicField(this, "__nonDataRow", !0); @@ -724,6 +744,7 @@ var Slick = (() => { EventData: SlickEventData, EventHandler: SlickEventHandler, Range: SlickRange, + DragExtendHandle: SlickDragExtendHandle, NonDataRow: SlickNonDataItem, Group: SlickGroup, GroupTotals: SlickGroupTotals, @@ -776,6 +797,10 @@ var Slick = (() => { AllRows: "ALL", LastRow: "LS1" }, + CellSelectionMode: { + Select: "SEL", + Replace: "REP" + }, ValueFilterMode: { None: "NONE", DeDuplicate: "DEDP", @@ -797,6 +822,7 @@ var Slick = (() => { GroupTotals, NonDataRow, Range, + DragExtendHandle, RegexSanitizer, GlobalEditorLock, keyCode, @@ -804,6 +830,7 @@ var Slick = (() => { GridAutosizeColsMode, ColAutosizeMode, RowSelectionMode, + CellSelectionMode, ValueFilterMode, WidthEvalMode } = SlickCore; diff --git a/dist/browser/slick.core.js.map b/dist/browser/slick.core.js.map index e6e4c2e4..c8c4a9dc 100644 --- a/dist/browser/slick.core.js.map +++ b/dist/browser/slick.core.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../src/slick.core.ts"], - "sourcesContent": ["/**\r\n * Contains core SlickGrid classes.\r\n * @module Core\r\n * @namespace Slick\r\n */\r\n\r\nimport type {\r\n AnyFunction,\r\n CSSStyleDeclarationWritable,\r\n EditController,\r\n ElementEventListener,\r\n Handler,\r\n InferDOMType,\r\n MergeTypes\r\n} from './models/index.js';\r\n\r\nexport interface BasePubSub {\r\n publish(_eventName: string | any, _data?: ArgType): any;\r\n subscribe(_eventName: string | Function, _callback: (data: ArgType) => void): any;\r\n}\r\n\r\n/**\r\n * An event object for passing data to event handlers and letting them control propagation.\r\n *

This is pretty much identical to how W3C and jQuery implement events.

\r\n * @class EventData\r\n * @constructor\r\n */\r\nexport class SlickEventData {\r\n protected _isPropagationStopped = false;\r\n protected _isImmediatePropagationStopped = false;\r\n protected _isDefaultPrevented = false;\r\n protected returnValues: string[] = [];\r\n protected returnValue: any = undefined;\r\n protected _eventTarget?: EventTarget | null;\r\n protected nativeEvent?: Event | null;\r\n protected arguments_?: ArgType;\r\n\r\n // public props that can be optionally pulled from the provided Event in constructor\r\n // they are all optional props because it really depends on the type of Event provided (KeyboardEvent, MouseEvent, ...)\r\n readonly altKey?: boolean;\r\n readonly ctrlKey?: boolean;\r\n readonly metaKey?: boolean;\r\n readonly shiftKey?: boolean;\r\n readonly key?: string;\r\n readonly keyCode?: number;\r\n readonly clientX?: number;\r\n readonly clientY?: number;\r\n readonly offsetX?: number;\r\n readonly offsetY?: number;\r\n readonly pageX?: number;\r\n readonly pageY?: number;\r\n readonly bubbles?: boolean;\r\n readonly target?: HTMLElement;\r\n readonly type?: string;\r\n readonly which?: number;\r\n readonly x?: number;\r\n readonly y?: number;\r\n\r\n get defaultPrevented() {\r\n return this._isDefaultPrevented;\r\n }\r\n\r\n constructor(protected event?: Event | null, protected args?: ArgType) {\r\n this.nativeEvent = event;\r\n this.arguments_ = args;\r\n\r\n // when we already have an event, we want to keep some of the event properties\r\n // looping through some props is the only way to keep and sync these properties to the returned EventData\r\n if (event) {\r\n [\r\n 'altKey', 'ctrlKey', 'metaKey', 'shiftKey', 'key', 'keyCode',\r\n 'clientX', 'clientY', 'offsetX', 'offsetY', 'pageX', 'pageY',\r\n 'bubbles', 'target', 'type', 'which', 'x', 'y'\r\n ].forEach(key => (this as any)[key] = event[key as keyof Event]);\r\n }\r\n this._eventTarget = this.nativeEvent ? this.nativeEvent.target : undefined;\r\n }\r\n\r\n /**\r\n * Stops event from propagating up the DOM tree.\r\n * @method stopPropagation\r\n */\r\n stopPropagation() {\r\n this._isPropagationStopped = true;\r\n this.nativeEvent?.stopPropagation();\r\n }\r\n\r\n /**\r\n * Returns whether stopPropagation was called on this event object.\r\n * @method isPropagationStopped\r\n * @return {Boolean}\r\n */\r\n isPropagationStopped() {\r\n return this._isPropagationStopped;\r\n }\r\n\r\n /**\r\n * Prevents the rest of the handlers from being executed.\r\n * @method stopImmediatePropagation\r\n */\r\n stopImmediatePropagation() {\r\n this._isImmediatePropagationStopped = true;\r\n if (this.nativeEvent) {\r\n this.nativeEvent.stopImmediatePropagation();\r\n }\r\n };\r\n\r\n /**\r\n * Returns whether stopImmediatePropagation was called on this event object.\\\r\n * @method isImmediatePropagationStopped\r\n * @return {Boolean}\r\n */\r\n isImmediatePropagationStopped() {\r\n return this._isImmediatePropagationStopped;\r\n };\r\n\r\n getNativeEvent() {\r\n return this.nativeEvent as E;\r\n }\r\n\r\n preventDefault() {\r\n if (this.nativeEvent) {\r\n this.nativeEvent.preventDefault();\r\n }\r\n this._isDefaultPrevented = true;\r\n }\r\n\r\n isDefaultPrevented() {\r\n if (this.nativeEvent) {\r\n return this.nativeEvent.defaultPrevented;\r\n }\r\n return this._isDefaultPrevented;\r\n }\r\n\r\n addReturnValue(value: any) {\r\n this.returnValues.push(value);\r\n if (this.returnValue === undefined && value !== undefined) {\r\n this.returnValue = value;\r\n }\r\n }\r\n\r\n getReturnValue() {\r\n return this.returnValue;\r\n }\r\n\r\n getArguments() {\r\n return this.arguments_;\r\n }\r\n}\r\n\r\n/**\r\n * A simple publisher-subscriber implementation.\r\n * @class Event\r\n * @constructor\r\n */\r\nexport class SlickEvent {\r\n protected _handlers: Handler[] = [];\r\n protected _pubSubService?: BasePubSub;\r\n\r\n get subscriberCount() {\r\n return this._handlers.length;\r\n }\r\n\r\n /**\r\n * Constructor\r\n * @param {String} [eventName] - event name that could be used for dispatching CustomEvent (when enabled)\r\n * @param {BasePubSub} [pubSubService] - event name that could be used for dispatching CustomEvent (when enabled)\r\n */\r\n constructor(protected readonly eventName?: string, protected readonly pubSub?: BasePubSub) {\r\n this._pubSubService = pubSub;\r\n }\r\n\r\n /**\r\n * Adds an event handler to be called when the event is fired.\r\n *

Event handler will receive two arguments - an EventData and the data\r\n * object the event was fired with.

\r\n * @method subscribe\r\n * @param {Function} fn - Event handler.\r\n */\r\n subscribe(fn: Handler) {\r\n this._handlers.push(fn);\r\n }\r\n\r\n /**\r\n * Removes an event handler added with subscribe(fn).\r\n * @method unsubscribe\r\n * @param {Function} [fn] - Event handler to be removed.\r\n */\r\n unsubscribe(fn?: Handler) {\r\n for (let i = this._handlers.length - 1; i >= 0; i--) {\r\n if (this._handlers[i] === fn) {\r\n this._handlers.splice(i, 1);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Fires an event notifying all subscribers.\r\n * @method notify\r\n * @param {Object} args Additional data object to be passed to all handlers.\r\n * @param {EventData} [event] - An EventData object to be passed to all handlers.\r\n * For DOM events, an existing W3C event object can be passed in.\r\n * @param {Object} [scope] - The scope (\"this\") within which the handler will be executed.\r\n * If not specified, the scope will be set to the Event instance.\r\n */\r\n notify(args: ArgType, evt?: SlickEventData | Event | MergeTypes, Event> | null, scope?: any) {\r\n const sed: SlickEventData = evt instanceof SlickEventData\r\n ? evt\r\n : new SlickEventData(evt, args);\r\n scope = scope || this;\r\n\r\n for (let i = 0; i < this._handlers.length && !(sed.isPropagationStopped() || sed.isImmediatePropagationStopped()); i++) {\r\n const returnValue = this._handlers[i].call(scope, sed, args);\r\n sed.addReturnValue(returnValue);\r\n }\r\n\r\n // user can optionally add a global PubSub Service which makes it easy to publish/subscribe to events\r\n if (typeof this._pubSubService?.publish === 'function' && this.eventName) {\r\n const ret = this._pubSubService.publish<{ args: ArgType; eventData?: SlickEventData; nativeEvent?: Event; }>(this.eventName, { args, eventData: sed });\r\n sed.addReturnValue(ret);\r\n }\r\n return sed;\r\n }\r\n\r\n setPubSubService(pubSub: BasePubSub) {\r\n this._pubSubService = pubSub;\r\n }\r\n}\r\n\r\nexport class SlickEventHandler {\r\n protected handlers: Array<{ event: SlickEvent; handler: Handler; }> = [];\r\n\r\n subscribe(event: SlickEvent, handler: Handler) {\r\n this.handlers.push({ event, handler });\r\n event.subscribe(handler);\r\n\r\n return this as SlickEventHandler; // allow chaining\r\n }\r\n\r\n unsubscribe(event: SlickEvent, handler: Handler) {\r\n let i = this.handlers.length;\r\n while (i--) {\r\n if (this.handlers[i].event === event &&\r\n this.handlers[i].handler === handler) {\r\n this.handlers.splice(i, 1);\r\n event.unsubscribe(handler);\r\n return;\r\n }\r\n }\r\n\r\n return this as SlickEventHandler; // allow chaining\r\n }\r\n\r\n unsubscribeAll() {\r\n let i = this.handlers.length;\r\n while (i--) {\r\n this.handlers[i].event.unsubscribe(this.handlers[i].handler);\r\n }\r\n this.handlers = [];\r\n\r\n return this as SlickEventHandler; // allow chaining\r\n }\r\n}\r\n\r\n/**\r\n * A structure containing a range of cells.\r\n * @class Range\r\n * @constructor\r\n * @param fromRow {Integer} Starting row.\r\n * @param fromCell {Integer} Starting cell.\r\n * @param toRow {Integer} Optional. Ending row. Defaults to fromRow.\r\n * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell.\r\n */\r\nexport class SlickRange {\r\n fromRow: number;\r\n fromCell: number;\r\n toCell: number;\r\n toRow: number;\r\n\r\n constructor(fromRow: number, fromCell: number, toRow?: number, toCell?: number) {\r\n if (toRow === undefined && toCell === undefined) {\r\n toRow = fromRow;\r\n toCell = fromCell;\r\n }\r\n\r\n /**\r\n * @property fromRow\r\n * @type {Integer}\r\n */\r\n this.fromRow = Math.min(fromRow, toRow as number);\r\n\r\n /**\r\n * @property fromCell\r\n * @type {Integer}\r\n */\r\n this.fromCell = Math.min(fromCell, toCell as number);\r\n\r\n /**\r\n * @property toCell\r\n * @type {Integer}\r\n */\r\n this.toCell = Math.max(fromCell, toCell as number);\r\n\r\n /**\r\n * @property toRow\r\n * @type {Integer}\r\n */\r\n this.toRow = Math.max(fromRow, toRow as number);\r\n }\r\n\r\n\r\n /**\r\n * Returns whether a range represents a single row.\r\n * @method isSingleRow\r\n * @return {Boolean}\r\n */\r\n isSingleRow() {\r\n return this.fromRow === this.toRow;\r\n }\r\n\r\n /**\r\n * Returns whether a range represents a single cell.\r\n * @method isSingleCell\r\n * @return {Boolean}\r\n */\r\n isSingleCell() {\r\n return this.fromRow === this.toRow && this.fromCell === this.toCell;\r\n }\r\n\r\n /**\r\n * Returns whether a range contains a given cell.\r\n * @method contains\r\n * @param row {Integer}\r\n * @param cell {Integer}\r\n * @return {Boolean}\r\n */\r\n contains(row: number, cell: number) {\r\n return row >= this.fromRow && row <= this.toRow &&\r\n cell >= this.fromCell && cell <= this.toCell;\r\n }\r\n\r\n /**\r\n * Returns a readable representation of a range.\r\n * @method toString\r\n * @return {String}\r\n */\r\n toString() {\r\n if (this.isSingleCell()) {\r\n return `(${this.fromRow}:${this.fromCell})`;\r\n }\r\n else {\r\n return `(${this.fromRow}:${this.fromCell} - ${this.toRow}:${this.toCell})`;\r\n }\r\n };\r\n}\r\n\r\n\r\n/**\r\n * A base class that all special / non-data rows (like Group and GroupTotals) derive from.\r\n * @class NonDataItem\r\n * @constructor\r\n */\r\nexport class SlickNonDataItem {\r\n __nonDataRow = true;\r\n}\r\n\r\n\r\n/**\r\n * Information about a group of rows.\r\n * @class Group\r\n * @extends Slick.NonDataItem\r\n * @constructor\r\n */\r\nexport class SlickGroup extends SlickNonDataItem {\r\n __group = true;\r\n\r\n /**\r\n * Grouping level, starting with 0.\r\n * @property level\r\n * @type {Number}\r\n */\r\n level = 0;\r\n\r\n /**\r\n * Number of rows in the group.\r\n * @property count\r\n * @type {Integer}\r\n */\r\n count = 0;\r\n\r\n /**\r\n * Grouping value.\r\n * @property value\r\n * @type {Object}\r\n */\r\n value = null;\r\n\r\n /**\r\n * Formatted display value of the group.\r\n * @property title\r\n * @type {String}\r\n */\r\n title: string | null = null;\r\n\r\n /**\r\n * Whether a group is collapsed.\r\n * @property collapsed\r\n * @type {Boolean}\r\n */\r\n collapsed: boolean | number = false;\r\n\r\n /**\r\n * Whether a group selection checkbox is checked.\r\n * @property selectChecked\r\n * @type {Boolean}\r\n */\r\n selectChecked = false;\r\n\r\n /**\r\n * GroupTotals, if any.\r\n * @property totals\r\n * @type {GroupTotals}\r\n */\r\n totals: SlickGroupTotals = null as any;\r\n\r\n /**\r\n * Rows that are part of the group.\r\n * @property rows\r\n * @type {Array}\r\n */\r\n rows: number[] = [];\r\n\r\n /**\r\n * Sub-groups that are part of the group.\r\n * @property groups\r\n * @type {Array}\r\n */\r\n groups: any[] = null as any;\r\n\r\n /**\r\n * A unique key used to identify the group. This key can be used in calls to DataView\r\n * collapseGroup() or expandGroup().\r\n * @property groupingKey\r\n * @type {Object}\r\n */\r\n groupingKey: any = null;\r\n\r\n constructor() {\r\n super();\r\n }\r\n /**\r\n * Compares two Group instances.\r\n * @method equals\r\n * @return {Boolean}\r\n * @param group {Group} Group instance to compare to.\r\n */\r\n equals(group: SlickGroup): boolean {\r\n return this.value === group.value &&\r\n this.count === group.count &&\r\n this.collapsed === group.collapsed &&\r\n this.title === group.title;\r\n };\r\n}\r\n\r\n/**\r\n * Information about group totals.\r\n * An instance of GroupTotals will be created for each totals row and passed to the aggregators\r\n * so that they can store arbitrary data in it. That data can later be accessed by group totals\r\n * formatters during the display.\r\n * @class GroupTotals\r\n * @extends Slick.NonDataItem\r\n * @constructor\r\n */\r\nexport class SlickGroupTotals extends SlickNonDataItem {\r\n __groupTotals = true;\r\n\r\n /**\r\n * Parent Group.\r\n * @param group\r\n * @type {Group}\r\n */\r\n group: SlickGroup = null as any;\r\n\r\n /**\r\n * Whether the totals have been fully initialized / calculated.\r\n * Will be set to false for lazy-calculated group totals.\r\n * @param initialized\r\n * @type {Boolean}\r\n */\r\n initialized = false;\r\n\r\n constructor() {\r\n super();\r\n }\r\n}\r\n\r\n/**\r\n * A locking helper to track the active edit controller and ensure that only a single controller\r\n * can be active at a time. This prevents a whole class of state and validation synchronization\r\n * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress\r\n * and attempt a commit or cancel before proceeding.\r\n * @class EditorLock\r\n * @constructor\r\n */\r\nexport class SlickEditorLock {\r\n activeEditController: any = null;\r\n\r\n /**\r\n * Returns true if a specified edit controller is active (has the edit lock).\r\n * If the parameter is not specified, returns true if any edit controller is active.\r\n * @method isActive\r\n * @param editController {EditController}\r\n * @return {Boolean}\r\n */\r\n isActive(editController?: EditController): boolean {\r\n return (editController ? this.activeEditController === editController : this.activeEditController !== null);\r\n };\r\n\r\n /**\r\n * Sets the specified edit controller as the active edit controller (acquire edit lock).\r\n * If another edit controller is already active, and exception will be throw new Error(.\r\n * @method activate\r\n * @param editController {EditController} edit controller acquiring the lock\r\n */\r\n activate(editController: EditController) {\r\n if (editController === this.activeEditController) { // already activated?\r\n return;\r\n }\r\n if (this.activeEditController !== null) {\r\n throw new Error(`Slick.EditorLock.activate: an editController is still active, can't activate another editController`);\r\n }\r\n if (!editController.commitCurrentEdit) {\r\n throw new Error('Slick.EditorLock.activate: editController must implement .commitCurrentEdit()');\r\n }\r\n if (!editController.cancelCurrentEdit) {\r\n throw new Error('Slick.EditorLock.activate: editController must implement .cancelCurrentEdit()');\r\n }\r\n this.activeEditController = editController;\r\n };\r\n\r\n /**\r\n * Unsets the specified edit controller as the active edit controller (release edit lock).\r\n * If the specified edit controller is not the active one, an exception will be throw new Error(.\r\n * @method deactivate\r\n * @param editController {EditController} edit controller releasing the lock\r\n */\r\n deactivate(editController: EditController) {\r\n if (!this.activeEditController) {\r\n return;\r\n }\r\n if (this.activeEditController !== editController) {\r\n throw new Error('Slick.EditorLock.deactivate: specified editController is not the currently active one');\r\n }\r\n this.activeEditController = null;\r\n };\r\n\r\n /**\r\n * Attempts to commit the current edit by calling \"commitCurrentEdit\" method on the active edit\r\n * controller and returns whether the commit attempt was successful (commit may fail due to validation\r\n * errors, etc.). Edit controller's \"commitCurrentEdit\" must return true if the commit has succeeded\r\n * and false otherwise. If no edit controller is active, returns true.\r\n * @method commitCurrentEdit\r\n * @return {Boolean}\r\n */\r\n commitCurrentEdit(): boolean {\r\n return (this.activeEditController ? this.activeEditController.commitCurrentEdit() : true);\r\n };\r\n\r\n /**\r\n * Attempts to cancel the current edit by calling \"cancelCurrentEdit\" method on the active edit\r\n * controller and returns whether the edit was successfully cancelled. If no edit controller is\r\n * active, returns true.\r\n * @method cancelCurrentEdit\r\n * @return {Boolean}\r\n */\r\n cancelCurrentEdit(): boolean {\r\n return (this.activeEditController ? this.activeEditController.cancelCurrentEdit() : true);\r\n };\r\n}\r\n\r\nfunction regexSanitizer(dirtyHtml: string) {\r\n return dirtyHtml.replace(/(\\b)(on[a-z]+)(\\s*)=|javascript:([^>]*)[^>]*|(<\\s*)(\\/*)script([<>]*).*(<\\s*)(\\/*)script(>*)|(<)(\\/*)(script|script defer)(.*)(>|>\">)/gi, '');\r\n}\r\n\r\n/**\r\n * A simple binding event service to keep track of all JavaScript events with callback listeners,\r\n * it allows us to unbind event(s) and their listener(s) by calling a simple unbind method call.\r\n * Unbinding is a necessary step to make sure that all event listeners are removed to avoid memory leaks when destroing the grid\r\n */\r\nexport class BindingEventService {\r\n protected _boundedEvents: ElementEventListener[] = [];\r\n\r\n getBoundedEvents() {\r\n return this._boundedEvents;\r\n }\r\n\r\n destroy() {\r\n this.unbindAll();\r\n }\r\n\r\n /** Bind an event listener to any element */\r\n bind(element: Element | Window, eventName: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions, groupName = '') {\r\n if (element) {\r\n element.addEventListener(eventName, listener, options);\r\n this._boundedEvents.push({ element, eventName, listener, groupName });\r\n }\r\n }\r\n\r\n /** Unbind all will remove every every event handlers that were bounded earlier */\r\n unbind(element: Element | Window, eventName: string, listener: EventListenerOrEventListenerObject) {\r\n if (element?.removeEventListener) {\r\n element.removeEventListener(eventName, listener);\r\n }\r\n }\r\n\r\n unbindByEventName(element: Element | Window, eventName: string) {\r\n const boundedEvent = this._boundedEvents.find(e => e.element === element && e.eventName === eventName);\r\n if (boundedEvent) {\r\n this.unbind(boundedEvent.element, boundedEvent.eventName, boundedEvent.listener);\r\n }\r\n }\r\n\r\n /**\r\n * Unbind all event listeners that were bounded, optionally provide a group name to unbind all listeners assigned to that specific group only.\r\n */\r\n unbindAll(groupName?: string | string[]) {\r\n if (groupName) {\r\n const groupNames = Array.isArray(groupName) ? groupName : [groupName];\r\n\r\n // unbind only the bounded event with a specific group\r\n // Note: we need to loop in reverse order to avoid array reindexing (causing index offset) after a splice is called\r\n for (let i = this._boundedEvents.length - 1; i >= 0; --i) {\r\n const boundedEvent = this._boundedEvents[i];\r\n if (groupNames.some(g => g === boundedEvent.groupName)) {\r\n const { element, eventName, listener } = boundedEvent;\r\n this.unbind(element, eventName, listener);\r\n this._boundedEvents.splice(i, 1);\r\n }\r\n }\r\n } else {\r\n // unbind everything\r\n while (this._boundedEvents.length > 0) {\r\n const boundedEvent = this._boundedEvents.pop() as ElementEventListener;\r\n const { element, eventName, listener } = boundedEvent;\r\n this.unbind(element, eventName, listener);\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport class Utils {\r\n // jQuery's extend\r\n private static getProto = Object.getPrototypeOf;\r\n private static class2type: any = {};\r\n private static toString = Utils.class2type.toString;\r\n private static hasOwn = Utils.class2type.hasOwnProperty;\r\n private static fnToString = Utils.hasOwn.toString;\r\n private static ObjectFunctionString = Utils.fnToString.call(Object);\r\n public static storage = {\r\n // https://stackoverflow.com/questions/29222027/vanilla-alternative-to-jquery-data-function-any-native-javascript-alternati\r\n _storage: new WeakMap(),\r\n // eslint-disable-next-line object-shorthand\r\n put: function (element: any, key: string, obj: any) {\r\n if (!this._storage.has(element)) {\r\n this._storage.set(element, new Map());\r\n }\r\n this._storage.get(element).set(key, obj);\r\n },\r\n // eslint-disable-next-line object-shorthand\r\n get: function (element: any, key: string) {\r\n const el = this._storage.get(element);\r\n if (el) {\r\n return el.get(key);\r\n }\r\n return null;\r\n },\r\n // eslint-disable-next-line object-shorthand\r\n remove: function (element: any, key: string) {\r\n const ret = this._storage.get(element).delete(key);\r\n if (!(this._storage.get(element).size === 0)) {\r\n this._storage.delete(element);\r\n }\r\n return ret;\r\n }\r\n };\r\n\r\n public static isFunction(obj: any) {\r\n return typeof obj === 'function' && typeof obj.nodeType !== 'number' && typeof obj.item !== 'function';\r\n }\r\n\r\n public static isPlainObject(obj: any) {\r\n if (!obj || Utils.toString.call(obj) !== '[object Object]') {\r\n return false;\r\n }\r\n\r\n const proto = Utils.getProto(obj);\r\n if (!proto) {\r\n return true;\r\n }\r\n const Ctor = Utils.hasOwn.call(proto, 'constructor') && proto.constructor;\r\n return typeof Ctor === 'function' && Utils.fnToString.call(Ctor) === Utils.ObjectFunctionString;\r\n }\r\n\r\n public static calculateAvailableSpace(element: HTMLElement) {\r\n let bottom = 0, top = 0, left = 0, right = 0;\r\n\r\n const windowHeight = window.innerHeight || 0;\r\n const windowWidth = window.innerWidth || 0;\r\n const scrollPosition = Utils.windowScrollPosition();\r\n const pageScrollTop = scrollPosition.top;\r\n const pageScrollLeft = scrollPosition.left;\r\n const elmOffset = Utils.offset(element);\r\n\r\n if (elmOffset) {\r\n const elementOffsetTop = elmOffset.top || 0;\r\n const elementOffsetLeft = elmOffset.left || 0;\r\n top = elementOffsetTop - pageScrollTop;\r\n bottom = windowHeight - (elementOffsetTop - pageScrollTop);\r\n left = elementOffsetLeft - pageScrollLeft;\r\n right = windowWidth - (elementOffsetLeft - pageScrollLeft);\r\n }\r\n\r\n return { top, bottom, left, right };\r\n }\r\n\r\n public static extend(...args: any[]): T {\r\n let options, name, src, copy, copyIsArray, clone,\r\n target = args[0],\r\n i = 1,\r\n deep = false;\r\n const length = args.length;\r\n\r\n if (typeof target === 'boolean') {\r\n deep = target;\r\n target = args[i] || {};\r\n i++;\r\n } else {\r\n target = target || {};\r\n }\r\n if (typeof target !== 'object' && !Utils.isFunction(target)) {\r\n target = {};\r\n }\r\n if (i === length) {\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n target = this;\r\n i--;\r\n }\r\n for (; i < length; i++) {\r\n if (Utils.isDefined(options = args[i])) {\r\n for (name in options) {\r\n copy = options[name];\r\n if (name === '__proto__' || target === copy) {\r\n continue;\r\n }\r\n if (deep && copy && (Utils.isPlainObject(copy) ||\r\n (copyIsArray = Array.isArray(copy)))) {\r\n src = target[name];\r\n if (copyIsArray && !Array.isArray(src)) {\r\n clone = [];\r\n } else if (!copyIsArray && !Utils.isPlainObject(src)) {\r\n clone = {};\r\n } else {\r\n clone = src;\r\n }\r\n copyIsArray = false;\r\n target[name] = Utils.extend(deep, clone, copy);\r\n } else if (copy !== undefined) {\r\n target[name] = copy;\r\n }\r\n }\r\n }\r\n }\r\n return target as T;\r\n }\r\n\r\n /**\r\n * Create a DOM Element with any optional attributes or properties.\r\n * It will only accept valid DOM element properties that `createElement` would accept.\r\n * For example: `createDomElement('div', { className: 'my-css-class' })`,\r\n * for style or dataset you need to use nested object `{ style: { display: 'none' }}\r\n * The last argument is to optionally append the created element to a parent container element.\r\n * @param {String} tagName - html tag\r\n * @param {Object} options - element properties\r\n * @param {[HTMLElement]} appendToParent - parent element to append to\r\n */\r\n public static createDomElement(\r\n tagName: T,\r\n elementOptions?: null | { [P in K]: InferDOMType },\r\n appendToParent?: Element\r\n ): HTMLElementTagNameMap[T] {\r\n const elm = document.createElement(tagName);\r\n\r\n if (elementOptions) {\r\n Object.keys(elementOptions).forEach((elmOptionKey) => {\r\n if (elmOptionKey === 'innerHTML') {\r\n console.warn(`[SlickGrid] For better CSP (Content Security Policy) support, do not use \"innerHTML\" directly in \"createDomElement('${tagName}', { innerHTML: 'some html'})\"` +\r\n `, it is better as separate assignment: \"const elm = createDomElement('span'); elm.innerHTML = 'some html';\"`);\r\n }\r\n\r\n const elmValue = elementOptions[elmOptionKey as keyof typeof elementOptions];\r\n if (typeof elmValue === 'object') {\r\n Object.assign(elm[elmOptionKey as K] as object, elmValue);\r\n } else {\r\n elm[elmOptionKey as K] = (elementOptions as any)[elmOptionKey as keyof typeof elementOptions];\r\n }\r\n });\r\n }\r\n if (appendToParent?.appendChild) {\r\n appendToParent.appendChild(elm);\r\n }\r\n return elm;\r\n }\r\n\r\n /**\r\n * From any input provided, return the HTML string (when a string is provided, it will be returned \"as is\" but when it's a number it will be converted to string)\r\n * When detecting HTMLElement/DocumentFragment, we can also specify which HTML type to retrieve innerHTML or outerHTML.\r\n * We can get the HTML by looping through all fragment `childNodes`\r\n * @param {DocumentFragment | HTMLElement | string | number} input\r\n * @param {'innerHTML' | 'outerHTML'} [type] - when the input is a DocumentFragment or HTMLElement, which type of HTML do you want to return? 'innerHTML' or 'outerHTML'\r\n * @returns {String}\r\n */\r\n public static getHtmlStringOutput(input: DocumentFragment | HTMLElement | string | number, type: 'innerHTML' | 'outerHTML' = 'innerHTML'): string {\r\n if (input instanceof DocumentFragment) {\r\n // a DocumentFragment doesn't have innerHTML/outerHTML, but we can loop through all children and concatenate them all to an HTML string\r\n return [].map.call(input.childNodes, (x: HTMLElement) => x[type]).join('') || input.textContent || '';\r\n } else if (input instanceof HTMLElement) {\r\n return input[type];\r\n }\r\n return String(input); // reaching this line means it's already a string (or number) so just return it as string\r\n }\r\n\r\n public static emptyElement(element?: T | null): T | undefined | null {\r\n while (element?.firstChild) {\r\n element.removeChild(element.firstChild);\r\n }\r\n return element;\r\n }\r\n\r\n /**\r\n * Accepts string containing the class or space-separated list of classes, and\r\n * returns list of individual classes.\r\n * Method properly takes into account extra whitespaces in the `className`\r\n * e.g.: \" class1 class2 \" => will result in `['class1', 'class2']`.\r\n * @param {String} className - space separated list of class names\r\n */\r\n public static classNameToList(className = ''): string[] {\r\n return className.split(' ').filter(cls => cls);\r\n }\r\n\r\n public static innerSize(elm: HTMLElement, type: 'height' | 'width') {\r\n let size = 0;\r\n\r\n if (elm) {\r\n const clientSize = type === 'height' ? 'clientHeight' : 'clientWidth';\r\n const sides = type === 'height' ? ['top', 'bottom'] : ['left', 'right'];\r\n size = elm[clientSize];\r\n for (const side of sides) {\r\n const sideSize = (parseFloat(Utils.getElementProp(elm, `padding-${side}`) || '') || 0);\r\n size -= sideSize;\r\n }\r\n }\r\n return size;\r\n }\r\n\r\n public static isDefined(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null && value !== '';\r\n }\r\n\r\n public static getElementProp(elm: HTMLElement & { getComputedStyle?: () => CSSStyleDeclaration }, property: string) {\r\n if (elm?.getComputedStyle) {\r\n return window.getComputedStyle(elm, null).getPropertyValue(property);\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * Get the function details (param & body) of a function.\r\n * It supports regular function and also ES6 arrow functions\r\n * @param {Function} fn - function to analyze\r\n * @param {Boolean} [addReturn] - when using ES6 function as single liner, we could add the missing `return ...`\r\n * @returns\r\n */\r\n public static getFunctionDetails(fn: AnyFunction, addReturn = true) {\r\n let isAsyncFn = false;\r\n\r\n const getFunctionBody = (func: AnyFunction) => {\r\n const fnStr = func.toString();\r\n isAsyncFn = fnStr.includes('async ');\r\n\r\n // when fn is one liner arrow fn returning an object in brackets e.g. `() => ({ hello: 'world' })`\r\n if ((fnStr.replaceAll(' ', '').includes('=>({'))) {\r\n const matches = fnStr.match(/(({.*}))/g) || [];\r\n return matches.length >= 1 ? `return ${matches[0]!.trimStart()}` : fnStr;\r\n }\r\n const isOneLinerArrowFn = (!fnStr.includes('{') && fnStr.includes('=>'));\r\n const body = fnStr.substring(\r\n (fnStr.indexOf('{') + 1) || (fnStr.indexOf('=>') + 2),\r\n fnStr.includes('}') ? fnStr.lastIndexOf('}') : fnStr.length\r\n );\r\n if (addReturn && isOneLinerArrowFn && !body.startsWith('return')) {\r\n return 'return ' + body.trimStart(); // add the `return ...` to the body for ES6 arrow fn\r\n }\r\n return body;\r\n };\r\n\r\n const getFunctionParams = (func: AnyFunction): string[] => {\r\n const STRIP_COMMENTS = /(\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/)|(\\s*=[^,)]*(('(?:\\\\'|[^'\\r\\n])*')|(\"(?:\\\\\"|[^\"\\r\\n])*\"))|(\\s*=[^,)]*))/mg;\r\n const ARG_NAMES = /([^\\s,]+)/g;\r\n const fnStr = func.toString().replace(STRIP_COMMENTS, '');\r\n return fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARG_NAMES) ?? [];\r\n };\r\n\r\n return {\r\n params: getFunctionParams(fn),\r\n body: getFunctionBody(fn),\r\n isAsync: isAsyncFn,\r\n };\r\n }\r\n\r\n public static insertAfterElement(referenceNode: HTMLElement, newNode: HTMLElement) {\r\n referenceNode.parentNode?.insertBefore(newNode, referenceNode.nextSibling);\r\n }\r\n\r\n public static isEmptyObject(obj: any) {\r\n if (obj === null || obj === undefined) {\r\n return true;\r\n }\r\n return Object.entries(obj).length === 0;\r\n }\r\n\r\n public static noop() { }\r\n\r\n public static offset(el: HTMLElement | null) {\r\n if (!el || !el.getBoundingClientRect) {\r\n return undefined;\r\n }\r\n const box = el.getBoundingClientRect();\r\n const docElem = document.documentElement;\r\n\r\n return {\r\n top: box.top + window.pageYOffset - docElem.clientTop,\r\n left: box.left + window.pageXOffset - docElem.clientLeft\r\n };\r\n }\r\n\r\n public static windowScrollPosition() {\r\n return {\r\n left: window.pageXOffset || document.documentElement.scrollLeft || 0,\r\n top: window.pageYOffset || document.documentElement.scrollTop || 0,\r\n };\r\n }\r\n\r\n public static width(el: HTMLElement, value?: number | string): number | void {\r\n if (!el || !el.getBoundingClientRect) { return; }\r\n if (value === undefined) {\r\n return el.getBoundingClientRect().width;\r\n }\r\n Utils.setStyleSize(el, 'width', value);\r\n }\r\n\r\n public static height(el: HTMLElement, value?: number | string): number | void {\r\n if (!el) { return; }\r\n if (value === undefined) {\r\n return el.getBoundingClientRect().height;\r\n }\r\n Utils.setStyleSize(el, 'height', value);\r\n }\r\n\r\n public static setStyleSize(el: HTMLElement, style: string, val?: number | string | Function) {\r\n if (typeof val === 'function') {\r\n val = val();\r\n } else if (typeof val === 'string') {\r\n el.style[style as CSSStyleDeclarationWritable] = val;\r\n } else {\r\n el.style[style as CSSStyleDeclarationWritable] = val + 'px';\r\n }\r\n }\r\n\r\n public static contains(parent: HTMLElement, child: HTMLElement) {\r\n if (!parent || !child) {\r\n return false;\r\n }\r\n\r\n const parentList = Utils.parents(child);\r\n return !parentList.every((p) => {\r\n if (parent === p) {\r\n return false;\r\n }\r\n return true;\r\n });\r\n }\r\n\r\n public static isHidden(el: HTMLElement) {\r\n return el.offsetWidth === 0 && el.offsetHeight === 0;\r\n }\r\n\r\n public static parents(el: HTMLElement | ParentNode, selector?: string) {\r\n const parents: Array = [];\r\n const visible = selector === ':visible';\r\n const hidden = selector === ':hidden';\r\n\r\n while ((el = el.parentNode as ParentNode) && el !== document) {\r\n if (!el || !el.parentNode) {\r\n break;\r\n }\r\n if (hidden) {\r\n if (Utils.isHidden(el as HTMLElement)) {\r\n parents.push(el);\r\n }\r\n } else if (visible) {\r\n if (!Utils.isHidden(el as HTMLElement)) {\r\n parents.push(el);\r\n }\r\n } else if (!selector || (el as any).matches(selector)) {\r\n parents.push(el);\r\n }\r\n }\r\n return parents;\r\n }\r\n\r\n public static toFloat(value: string | number) {\r\n const x = parseFloat(value as string);\r\n if (isNaN(x)) {\r\n return 0;\r\n }\r\n return x;\r\n }\r\n\r\n public static show(el: HTMLElement | HTMLElement[], type = '') {\r\n if (Array.isArray(el)) {\r\n el.forEach((e) => e.style.display = type);\r\n } else {\r\n el.style.display = type;\r\n }\r\n }\r\n\r\n public static hide(el: HTMLElement | HTMLElement[]) {\r\n if (Array.isArray(el)) {\r\n el.forEach((e) => e.style.display = 'none');\r\n } else {\r\n el.style.display = 'none';\r\n }\r\n }\r\n\r\n public static slideUp(el: HTMLElement | HTMLElement[], callback: Function) {\r\n return Utils.slideAnimation(el, 'slideUp', callback);\r\n }\r\n\r\n public static slideDown(el: HTMLElement | HTMLElement[], callback: Function) {\r\n return Utils.slideAnimation(el, 'slideDown', callback);\r\n }\r\n\r\n public static slideAnimation(el: HTMLElement | HTMLElement[], slideDirection: 'slideDown' | 'slideUp', callback: Function) {\r\n if ((window as any).jQuery !== undefined) {\r\n (window as any).jQuery(el)[slideDirection]('fast', callback);\r\n return;\r\n }\r\n (slideDirection === 'slideUp') ? Utils.hide(el) : Utils.show(el);\r\n callback();\r\n }\r\n\r\n public static applyDefaults(targetObj: any, srcObj: any) {\r\n if (typeof srcObj === 'object') {\r\n Object.keys(srcObj).forEach(key => {\r\n if (srcObj.hasOwnProperty(key) && !targetObj.hasOwnProperty(key)) {\r\n targetObj[key] = srcObj[key];\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * User could optionally add PubSub Service to SlickEvent\r\n * When it is defined then a SlickEvent `notify()` call will also dispatch it by using the PubSub publish() method\r\n * @param {BasePubSub} [pubSubService]\r\n * @param {*} scope\r\n */\r\n public static addSlickEventPubSubWhenDefined(pubSub?: BasePubSub, scope?: T) {\r\n if (pubSub) {\r\n for (const prop in scope) {\r\n if (scope[prop] instanceof SlickEvent && typeof (scope[prop] as SlickEvent).setPubSubService === 'function') {\r\n (scope[prop] as SlickEvent).setPubSubService(pubSub);\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport const SlickGlobalEditorLock = new SlickEditorLock();\r\n\r\n// export Slick namespace on both global & window objects\r\nconst SlickCore = {\r\n Event: SlickEvent,\r\n EventData: SlickEventData,\r\n EventHandler: SlickEventHandler,\r\n Range: SlickRange,\r\n NonDataRow: SlickNonDataItem,\r\n Group: SlickGroup,\r\n GroupTotals: SlickGroupTotals,\r\n EditorLock: SlickEditorLock,\r\n RegexSanitizer: regexSanitizer,\r\n\r\n /**\r\n * A global singleton editor lock.\r\n * @class GlobalEditorLock\r\n * @static\r\n * @constructor\r\n */\r\n GlobalEditorLock: SlickGlobalEditorLock,\r\n\r\n keyCode: {\r\n SPACE: 8,\r\n BACKSPACE: 8,\r\n DELETE: 46,\r\n DOWN: 40,\r\n END: 35,\r\n ENTER: 13,\r\n ESCAPE: 27,\r\n HOME: 36,\r\n INSERT: 45,\r\n LEFT: 37,\r\n PAGE_DOWN: 34,\r\n PAGE_UP: 33,\r\n RIGHT: 39,\r\n TAB: 9,\r\n UP: 38,\r\n A: 65\r\n },\r\n preClickClassName: 'slick-edit-preclick',\r\n\r\n GridAutosizeColsMode: {\r\n None: 'NOA',\r\n LegacyOff: 'LOF',\r\n LegacyForceFit: 'LFF',\r\n IgnoreViewport: 'IGV',\r\n FitColsToViewport: 'FCV',\r\n FitViewportToCols: 'FVC'\r\n },\r\n\r\n 'ColAutosizeMode': {\r\n Locked: 'LCK',\r\n Guide: 'GUI',\r\n Content: 'CON',\r\n ContentExpandOnly: 'CXO',\r\n ContentIntelligent: 'CTI'\r\n },\r\n\r\n 'RowSelectionMode': {\r\n FirstRow: 'FS1',\r\n FirstNRows: 'FSN',\r\n AllRows: 'ALL',\r\n LastRow: 'LS1'\r\n },\r\n\r\n 'ValueFilterMode': {\r\n None: 'NONE',\r\n DeDuplicate: 'DEDP',\r\n GetGreatestAndSub: 'GR8T',\r\n GetLongestTextAndSub: 'LNSB',\r\n GetLongestText: 'LNSC'\r\n },\r\n\r\n WidthEvalMode: {\r\n Auto: 'AUTO',\r\n TextOnly: 'CANV',\r\n HTML: 'HTML'\r\n }\r\n};\r\n\r\nexport const {\r\n EditorLock, Event, EventData, EventHandler, Group, GroupTotals, NonDataRow, Range,\r\n RegexSanitizer, GlobalEditorLock, keyCode, preClickClassName, GridAutosizeColsMode, ColAutosizeMode,\r\n RowSelectionMode, ValueFilterMode, WidthEvalMode\r\n} = SlickCore;\r\n\r\n// also add to global object when exist\r\nif (IIFE_ONLY && typeof global !== 'undefined' && window.Slick) {\r\n global.Slick = window.Slick;\r\n}\r\n"], - "mappings": ";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AA2BO,MAAM,iBAAN,MAAoC;AAAA,IAmCzC,YAAsB,OAAgC,MAAgB;AAAhD;AAAgC;AAlCtD,0BAAU,yBAAwB;AAClC,0BAAU,kCAAiC;AAC3C,0BAAU,uBAAsB;AAChC,0BAAU,gBAAyB,CAAC;AACpC,0BAAU;AACV,0BAAU;AACV,0BAAU;AACV,0BAAU;AAIV;AAAA;AAAA,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AACT,0BAAS;AAOP,WAAK,cAAc,OACnB,KAAK,aAAa,MAId,SACF;AAAA,QACE;AAAA,QAAU;AAAA,QAAW;AAAA,QAAW;AAAA,QAAY;AAAA,QAAO;AAAA,QACnD;AAAA,QAAW;AAAA,QAAW;AAAA,QAAW;AAAA,QAAW;AAAA,QAAS;AAAA,QACrD;AAAA,QAAW;AAAA,QAAU;AAAA,QAAQ;AAAA,QAAS;AAAA,QAAK;AAAA,MAC7C,EAAE,QAAQ,SAAQ,KAAa,GAAG,IAAI,MAAM,GAAkB,CAAC,GAEjE,KAAK,eAAe,KAAK,cAAc,KAAK,YAAY,SAAS;AAAA,IACnE;AAAA,IAlBA,IAAI,mBAAmB;AACrB,aAAO,KAAK;AAAA,IACd;AAAA;AAAA;AAAA;AAAA;AAAA,IAsBA,kBAAkB;AAlFpB;AAmFI,WAAK,wBAAwB,KAC7B,UAAK,gBAAL,WAAkB;AAAA,IACpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,uBAAuB;AACrB,aAAO,KAAK;AAAA,IACd;AAAA;AAAA;AAAA;AAAA;AAAA,IAMA,2BAA2B;AACzB,WAAK,iCAAiC,IAClC,KAAK,eACP,KAAK,YAAY,yBAAyB;AAAA,IAE9C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,gCAAgC;AAC9B,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,iBAAkC;AAChC,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,iBAAiB;AACf,MAAI,KAAK,eACP,KAAK,YAAY,eAAe,GAElC,KAAK,sBAAsB;AAAA,IAC7B;AAAA,IAEA,qBAAqB;AACnB,aAAI,KAAK,cACA,KAAK,YAAY,mBAEnB,KAAK;AAAA,IACd;AAAA,IAEA,eAAe,OAAY;AACzB,WAAK,aAAa,KAAK,KAAK,GACxB,KAAK,gBAAgB,UAAa,UAAU,WAC9C,KAAK,cAAc;AAAA,IAEvB;AAAA,IAEA,iBAAiB;AACf,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,eAAe;AACb,aAAO,KAAK;AAAA,IACd;AAAA,EACF,GAOa,aAAN,MAAgC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAarC,YAA+B,WAAuC,QAAqB;AAA5D;AAAuC;AAZtE,0BAAU,aAAgC,CAAC;AAC3C,0BAAU;AAYR,WAAK,iBAAiB;AAAA,IACxB;AAAA,IAXA,IAAI,kBAAkB;AACpB,aAAO,KAAK,UAAU;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAkBA,UAAU,IAAsB;AAC9B,WAAK,UAAU,KAAK,EAAE;AAAA,IACxB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,YAAY,IAAuB;AACjC,eAAS,IAAI,KAAK,UAAU,SAAS,GAAG,KAAK,GAAG;AAC9C,QAAI,KAAK,UAAU,CAAC,MAAM,MACxB,KAAK,UAAU,OAAO,GAAG,CAAC;AAAA,IAGhC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAWA,OAAO,MAAe,KAA2F,OAAa;AA7MhI;AA8MI,UAAM,MAAsB,eAAe,iBACvC,MACA,IAAI,eAAe,KAAK,IAAI;AAChC,cAAQ,SAAS;AAEjB,eAAS,IAAI,GAAG,IAAI,KAAK,UAAU,UAAU,EAAE,IAAI,qBAAqB,KAAK,IAAI,8BAA8B,IAAI,KAAK;AACtH,YAAM,cAAc,KAAK,UAAU,CAAC,EAAE,KAAK,OAAO,KAAK,IAAI;AAC3D,YAAI,eAAe,WAAW;AAAA,MAChC;AAGA,UAAI,SAAO,UAAK,mBAAL,mBAAqB,YAAY,cAAc,KAAK,WAAW;AACxE,YAAM,MAAM,KAAK,eAAe,QAAsF,KAAK,WAAW,EAAE,MAAM,WAAW,IAAI,CAAC;AAC9J,YAAI,eAAe,GAAG;AAAA,MACxB;AACA,aAAO;AAAA,IACT;AAAA,IAEA,iBAAiB,QAAoB;AACnC,WAAK,iBAAiB;AAAA,IACxB;AAAA,EACF,GAEa,oBAAN,MAAwB;AAAA,IAAxB;AACL,0BAAU,YAAiE,CAAC;AAAA;AAAA,IAE5E,UAAmB,OAAsB,SAAqB;AAC5D,kBAAK,SAAS,KAAK,EAAE,OAAO,QAAQ,CAAC,GACrC,MAAM,UAAU,OAAO,GAEhB;AAAA,IACT;AAAA,IAEA,YAAqB,OAAsB,SAAqB;AAC9D,UAAI,IAAI,KAAK,SAAS;AACtB,aAAO;AACL,YAAI,KAAK,SAAS,CAAC,EAAE,UAAU,SAC7B,KAAK,SAAS,CAAC,EAAE,YAAY,SAAS;AACtC,eAAK,SAAS,OAAO,GAAG,CAAC,GACzB,MAAM,YAAY,OAAO;AACzB;AAAA,QACF;AAGF,aAAO;AAAA,IACT;AAAA,IAEA,iBAAiB;AACf,UAAI,IAAI,KAAK,SAAS;AACtB,aAAO;AACL,aAAK,SAAS,CAAC,EAAE,MAAM,YAAY,KAAK,SAAS,CAAC,EAAE,OAAO;AAE7D,kBAAK,WAAW,CAAC,GAEV;AAAA,IACT;AAAA,EACF,GAWa,aAAN,MAAiB;AAAA,IAMtB,YAAY,SAAiB,UAAkB,OAAgB,QAAiB;AALhF;AACA;AACA;AACA;AAGE,MAAI,UAAU,UAAa,WAAW,WACpC,QAAQ,SACR,SAAS,WAOX,KAAK,UAAU,KAAK,IAAI,SAAS,KAAe,GAMhD,KAAK,WAAW,KAAK,IAAI,UAAU,MAAgB,GAMnD,KAAK,SAAS,KAAK,IAAI,UAAU,MAAgB,GAMjD,KAAK,QAAQ,KAAK,IAAI,SAAS,KAAe;AAAA,IAChD;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,cAAc;AACZ,aAAO,KAAK,YAAY,KAAK;AAAA,IAC/B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,eAAe;AACb,aAAO,KAAK,YAAY,KAAK,SAAS,KAAK,aAAa,KAAK;AAAA,IAC/D;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,SAAS,KAAa,MAAc;AAClC,aAAO,OAAO,KAAK,WAAW,OAAO,KAAK,SACxC,QAAQ,KAAK,YAAY,QAAQ,KAAK;AAAA,IAC1C;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,WAAW;AACT,aAAI,KAAK,aAAa,IACb,IAAI,KAAK,OAAO,IAAI,KAAK,QAAQ,MAGjC,IAAI,KAAK,OAAO,IAAI,KAAK,QAAQ,MAAM,KAAK,KAAK,IAAI,KAAK,MAAM;AAAA,IAE3E;AAAA,EACF,GAQa,mBAAN,MAAuB;AAAA,IAAvB;AACL,0CAAe;AAAA;AAAA,EACjB,GASa,aAAN,cAAyB,iBAAiB;AAAA,IA0E/C,cAAc;AACZ,YAAM;AA1ER,qCAAU;AAOV;AAAA;AAAA;AAAA;AAAA;AAAA,mCAAQ;AAOR;AAAA;AAAA;AAAA;AAAA;AAAA,mCAAQ;AAOR;AAAA;AAAA;AAAA;AAAA;AAAA,mCAAQ;AAOR;AAAA;AAAA;AAAA;AAAA;AAAA,mCAAuB;AAOvB;AAAA;AAAA;AAAA;AAAA;AAAA,uCAA8B;AAO9B;AAAA;AAAA;AAAA;AAAA;AAAA,2CAAgB;AAOhB;AAAA;AAAA;AAAA;AAAA;AAAA,oCAA2B;AAO3B;AAAA;AAAA;AAAA;AAAA;AAAA,kCAAiB,CAAC;AAOlB;AAAA;AAAA;AAAA;AAAA;AAAA,oCAAgB;AAQhB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAAmB;AAAA,IAInB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAOA,OAAO,OAA4B;AACjC,aAAO,KAAK,UAAU,MAAM,SAC1B,KAAK,UAAU,MAAM,SACrB,KAAK,cAAc,MAAM,aACzB,KAAK,UAAU,MAAM;AAAA,IACzB;AAAA,EACF,GAWa,mBAAN,cAA+B,iBAAiB;AAAA,IAkBrD,cAAc;AACZ,YAAM;AAlBR,2CAAgB;AAOhB;AAAA;AAAA;AAAA;AAAA;AAAA,mCAAoB;AAQpB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,yCAAc;AAAA,IAId;AAAA,EACF,GAUa,kBAAN,MAAsB;AAAA,IAAtB;AACL,kDAA4B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAS5B,SAAS,gBAA0C;AACjD,aAAQ,iBAAiB,KAAK,yBAAyB,iBAAiB,KAAK,yBAAyB;AAAA,IACxG;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,SAAS,gBAAgC;AACvC,UAAI,mBAAmB,KAAK,sBAG5B;AAAA,YAAI,KAAK,yBAAyB;AAChC,gBAAM,IAAI,MAAM,qGAAqG;AAEvH,YAAI,CAAC,eAAe;AAClB,gBAAM,IAAI,MAAM,+EAA+E;AAEjG,YAAI,CAAC,eAAe;AAClB,gBAAM,IAAI,MAAM,+EAA+E;AAEjG,aAAK,uBAAuB;AAAA;AAAA,IAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,WAAW,gBAAgC;AACzC,UAAK,KAAK,sBAGV;AAAA,YAAI,KAAK,yBAAyB;AAChC,gBAAM,IAAI,MAAM,uFAAuF;AAEzG,aAAK,uBAAuB;AAAA;AAAA,IAC9B;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,oBAA6B;AAC3B,aAAQ,KAAK,uBAAuB,KAAK,qBAAqB,kBAAkB,IAAI;AAAA,IACtF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,oBAA6B;AAC3B,aAAQ,KAAK,uBAAuB,KAAK,qBAAqB,kBAAkB,IAAI;AAAA,IACtF;AAAA,EACF;AAEA,WAAS,eAAe,WAAmB;AACzC,WAAO,UAAU,QAAQ,oJAAoJ,EAAE;AAAA,EACjL;AAOO,MAAM,sBAAN,MAA0B;AAAA,IAA1B;AACL,0BAAU,kBAAyC,CAAC;AAAA;AAAA,IAEpD,mBAAmB;AACjB,aAAO,KAAK;AAAA,IACd;AAAA,IAEA,UAAU;AACR,WAAK,UAAU;AAAA,IACjB;AAAA;AAAA,IAGA,KAAK,SAA2B,WAAmB,UAA8C,SAA6C,YAAY,IAAI;AAC5J,MAAI,YACF,QAAQ,iBAAiB,WAAW,UAAU,OAAO,GACrD,KAAK,eAAe,KAAK,EAAE,SAAS,WAAW,UAAU,UAAU,CAAC;AAAA,IAExE;AAAA;AAAA,IAGA,OAAO,SAA2B,WAAmB,UAA8C;AACjG,MAAI,2BAAS,uBACX,QAAQ,oBAAoB,WAAW,QAAQ;AAAA,IAEnD;AAAA,IAEA,kBAAkB,SAA2B,WAAmB;AAC9D,UAAM,eAAe,KAAK,eAAe,KAAK,OAAK,EAAE,YAAY,WAAW,EAAE,cAAc,SAAS;AACrG,MAAI,gBACF,KAAK,OAAO,aAAa,SAAS,aAAa,WAAW,aAAa,QAAQ;AAAA,IAEnF;AAAA;AAAA;AAAA;AAAA,IAKA,UAAU,WAA+B;AACvC,UAAI,WAAW;AACb,YAAM,aAAa,MAAM,QAAQ,SAAS,IAAI,YAAY,CAAC,SAAS;AAIpE,iBAAS,IAAI,KAAK,eAAe,SAAS,GAAG,KAAK,GAAG,EAAE,GAAG;AACxD,cAAM,eAAe,KAAK,eAAe,CAAC;AAC1C,cAAI,WAAW,KAAK,OAAK,MAAM,aAAa,SAAS,GAAG;AACtD,gBAAM,EAAE,SAAS,WAAW,SAAS,IAAI;AACzC,iBAAK,OAAO,SAAS,WAAW,QAAQ,GACxC,KAAK,eAAe,OAAO,GAAG,CAAC;AAAA,UACjC;AAAA,QACF;AAAA,MACF;AAEE,eAAO,KAAK,eAAe,SAAS,KAAG;AACrC,cAAM,eAAe,KAAK,eAAe,IAAI,GACvC,EAAE,SAAS,WAAW,SAAS,IAAI;AACzC,eAAK,OAAO,SAAS,WAAW,QAAQ;AAAA,QAC1C;AAAA,IAEJ;AAAA,EACF,GAEa,SAAN,MAAM,OAAM;AAAA,IAoCjB,OAAc,WAAW,KAAU;AACjC,aAAO,OAAO,OAAQ,cAAc,OAAO,IAAI,YAAa,YAAY,OAAO,IAAI,QAAS;AAAA,IAC9F;AAAA,IAEA,OAAc,cAAc,KAAU;AACpC,UAAI,CAAC,OAAO,OAAM,SAAS,KAAK,GAAG,MAAM;AACvC,eAAO;AAGT,UAAM,QAAQ,OAAM,SAAS,GAAG;AAChC,UAAI,CAAC;AACH,eAAO;AAET,UAAM,OAAO,OAAM,OAAO,KAAK,OAAO,aAAa,KAAK,MAAM;AAC9D,aAAO,OAAO,QAAS,cAAc,OAAM,WAAW,KAAK,IAAI,MAAM,OAAM;AAAA,IAC7E;AAAA,IAEA,OAAc,wBAAwB,SAAsB;AAC1D,UAAI,SAAS,GAAG,MAAM,GAAG,OAAO,GAAG,QAAQ,GAErC,eAAe,OAAO,eAAe,GACrC,cAAc,OAAO,cAAc,GACnC,iBAAiB,OAAM,qBAAqB,GAC5C,gBAAgB,eAAe,KAC/B,iBAAiB,eAAe,MAChC,YAAY,OAAM,OAAO,OAAO;AAEtC,UAAI,WAAW;AACb,YAAM,mBAAmB,UAAU,OAAO,GACpC,oBAAoB,UAAU,QAAQ;AAC5C,cAAM,mBAAmB,eACzB,SAAS,gBAAgB,mBAAmB,gBAC5C,OAAO,oBAAoB,gBAC3B,QAAQ,eAAe,oBAAoB;AAAA,MAC7C;AAEA,aAAO,EAAE,KAAK,QAAQ,MAAM,MAAM;AAAA,IACpC;AAAA,IAEA,OAAc,UAAmB,MAAgB;AAC/C,UAAI,SAAS,MAAM,KAAK,MAAM,aAAa,OACzC,SAAS,KAAK,CAAC,GACf,IAAI,GACJ,OAAO,IACH,SAAS,KAAK;AAiBpB,WAfI,OAAO,UAAW,aACpB,OAAO,QACP,SAAS,KAAK,CAAC,KAAK,CAAC,GACrB,OAEA,SAAS,UAAU,CAAC,GAElB,OAAO,UAAW,YAAY,CAAC,OAAM,WAAW,MAAM,MACxD,SAAS,CAAC,IAER,MAAM,WAER,SAAS,MACT,MAEK,IAAI,QAAQ;AACjB,YAAI,OAAM,UAAU,UAAU,KAAK,CAAC,CAAC;AACnC,eAAK,QAAQ;AAEX,YADA,OAAO,QAAQ,IAAI,GACf,WAAS,eAAe,WAAW,UAGnC,QAAQ,SAAS,OAAM,cAAc,IAAI,MAC1C,cAAc,MAAM,QAAQ,IAAI,OACjC,MAAM,OAAO,IAAI,GACb,eAAe,CAAC,MAAM,QAAQ,GAAG,IACnC,QAAQ,CAAC,IACA,CAAC,eAAe,CAAC,OAAM,cAAc,GAAG,IACjD,QAAQ,CAAC,IAET,QAAQ,KAEV,cAAc,IACd,OAAO,IAAI,IAAI,OAAM,OAAO,MAAM,OAAO,IAAI,KACpC,SAAS,WAClB,OAAO,IAAI,IAAI;AAKvB,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAYA,OAAc,iBACZ,SACA,gBACA,gBAC0B;AAC1B,UAAM,MAAM,SAAS,cAAiB,OAAO;AAE7C,aAAI,kBACF,OAAO,KAAK,cAAc,EAAE,QAAQ,CAAC,iBAAiB;AACpD,QAAI,iBAAiB,eACnB,QAAQ,KAAK,uHAAuH,OAAO,2IAC5B;AAGjH,YAAM,WAAW,eAAe,YAA2C;AAC3E,QAAI,OAAO,YAAa,WACtB,OAAO,OAAO,IAAI,YAAiB,GAAa,QAAQ,IAExD,IAAI,YAAiB,IAAK,eAAuB,YAA2C;AAAA,MAEhG,CAAC,GAEC,yCAAgB,eAClB,eAAe,YAAY,GAAG,GAEzB;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAUA,OAAc,oBAAoB,OAAyD,OAAkC,aAAqB;AAChJ,aAAI,iBAAiB,mBAEZ,CAAC,EAAE,IAAI,KAAK,MAAM,YAAY,CAAC,MAAmB,EAAE,IAAI,CAAC,EAAE,KAAK,EAAE,KAAK,MAAM,eAAe,KAC1F,iBAAiB,cACnB,MAAM,IAAI,IAEZ,OAAO,KAAK;AAAA,IACrB;AAAA,IAEA,OAAc,aAA0C,SAA0C;AAChG,aAAO,2BAAS;AACd,gBAAQ,YAAY,QAAQ,UAAU;AAExC,aAAO;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,OAAc,gBAAgB,YAAY,IAAc;AACtD,aAAO,UAAU,MAAM,GAAG,EAAE,OAAO,SAAO,GAAG;AAAA,IAC/C;AAAA,IAEA,OAAc,UAAU,KAAkB,MAA0B;AAClE,UAAI,OAAO;AAEX,UAAI,KAAK;AACP,YAAM,aAAa,SAAS,WAAW,iBAAiB,eAClD,QAAQ,SAAS,WAAW,CAAC,OAAO,QAAQ,IAAI,CAAC,QAAQ,OAAO;AACtE,eAAO,IAAI,UAAU;AACrB,iBAAW,QAAQ,OAAO;AACxB,cAAM,WAAY,WAAW,OAAM,eAAe,KAAK,WAAW,IAAI,EAAE,KAAK,EAAE,KAAK;AACpF,kBAAQ;AAAA,QACV;AAAA,MACF;AACA,aAAO;AAAA,IACT;AAAA,IAEA,OAAc,UAAa,OAAyC;AAClE,aAAoC,SAAU,QAAW,UAAU;AAAA,IACrE;AAAA,IAEA,OAAc,eAAe,KAAqE,UAAkB;AAClH,aAAI,mBAAK,mBACA,OAAO,iBAAiB,KAAK,IAAI,EAAE,iBAAiB,QAAQ,IAE9D;AAAA,IACT;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IASA,OAAc,mBAAmB,IAAiB,YAAY,IAAM;AAClE,UAAI,YAAY,IAEV,kBAAkB,CAAC,SAAsB;AAC7C,YAAM,QAAQ,KAAK,SAAS;AAI5B,YAHA,YAAY,MAAM,SAAS,QAAQ,GAG9B,MAAM,WAAW,KAAK,EAAE,EAAE,SAAS,MAAM,GAAI;AAChD,cAAM,UAAU,MAAM,MAAM,WAAW,KAAK,CAAC;AAC7C,iBAAO,QAAQ,UAAU,IAAI,UAAU,QAAQ,CAAC,EAAG,UAAU,CAAC,KAAK;AAAA,QACrE;AACA,YAAM,oBAAqB,CAAC,MAAM,SAAS,GAAG,KAAK,MAAM,SAAS,IAAI,GAChE,OAAO,MAAM;AAAA,UAChB,MAAM,QAAQ,GAAG,IAAI,KAAO,MAAM,QAAQ,IAAI,IAAI;AAAA,UACnD,MAAM,SAAS,GAAG,IAAI,MAAM,YAAY,GAAG,IAAI,MAAM;AAAA,QACvD;AACA,eAAI,aAAa,qBAAqB,CAAC,KAAK,WAAW,QAAQ,IACtD,YAAY,KAAK,UAAU,IAE7B;AAAA,MACT;AASA,aAAO;AAAA,QACL,SARwB,CAAC,SAAgC;AAz4B/D;AA04BM,cAAM,iBAAiB,yGACjB,YAAY,cACZ,QAAQ,KAAK,SAAS,EAAE,QAAQ,gBAAgB,EAAE;AACxD,kBAAO,WAAM,MAAM,MAAM,QAAQ,GAAG,IAAI,GAAG,MAAM,QAAQ,GAAG,CAAC,EAAE,MAAM,SAAS,MAAvE,YAA4E,CAAC;AAAA,QACtF,GAG4B,EAAE;AAAA,QAC5B,MAAM,gBAAgB,EAAE;AAAA,QACxB,SAAS;AAAA,MACX;AAAA,IACF;AAAA,IAEA,OAAc,mBAAmB,eAA4B,SAAsB;AAv5BrF;AAw5BI,0BAAc,eAAd,WAA0B,aAAa,SAAS,cAAc;AAAA,IAChE;AAAA,IAEA,OAAc,cAAc,KAAU;AACpC,aAAI,OAAQ,OACH,KAEF,OAAO,QAAQ,GAAG,EAAE,WAAW;AAAA,IACxC;AAAA,IAEA,OAAc,OAAO;AAAA,IAAE;AAAA,IAEvB,OAAc,OAAO,IAAwB;AAC3C,UAAI,CAAC,MAAM,CAAC,GAAG;AACb;AAEF,UAAM,MAAM,GAAG,sBAAsB,GAC/B,UAAU,SAAS;AAEzB,aAAO;AAAA,QACL,KAAK,IAAI,MAAM,OAAO,cAAc,QAAQ;AAAA,QAC5C,MAAM,IAAI,OAAO,OAAO,cAAc,QAAQ;AAAA,MAChD;AAAA,IACF;AAAA,IAEA,OAAc,uBAAuB;AACnC,aAAO;AAAA,QACL,MAAM,OAAO,eAAe,SAAS,gBAAgB,cAAc;AAAA,QACnE,KAAK,OAAO,eAAe,SAAS,gBAAgB,aAAa;AAAA,MACnE;AAAA,IACF;AAAA,IAEA,OAAc,MAAM,IAAiB,OAAwC;AAC3E,UAAI,GAAC,MAAM,CAAC,GAAG,wBACf;AAAA,YAAI,UAAU;AACZ,iBAAO,GAAG,sBAAsB,EAAE;AAEpC,eAAM,aAAa,IAAI,SAAS,KAAK;AAAA;AAAA,IACvC;AAAA,IAEA,OAAc,OAAO,IAAiB,OAAwC;AAC5E,UAAK,IACL;AAAA,YAAI,UAAU;AACZ,iBAAO,GAAG,sBAAsB,EAAE;AAEpC,eAAM,aAAa,IAAI,UAAU,KAAK;AAAA;AAAA,IACxC;AAAA,IAEA,OAAc,aAAa,IAAiB,OAAe,KAAkC;AAC3F,MAAI,OAAO,OAAQ,aACjB,MAAM,IAAI,IACD,OAAO,OAAQ,WACxB,GAAG,MAAM,KAAoC,IAAI,MAEjD,GAAG,MAAM,KAAoC,IAAI,MAAM;AAAA,IAE3D;AAAA,IAEA,OAAc,SAAS,QAAqB,OAAoB;AAC9D,aAAI,CAAC,UAAU,CAAC,QACP,KAIF,CADY,OAAM,QAAQ,KAAK,EACnB,MAAM,CAAC,MACpB,WAAW,CAIhB;AAAA,IACH;AAAA,IAEA,OAAc,SAAS,IAAiB;AACtC,aAAO,GAAG,gBAAgB,KAAK,GAAG,iBAAiB;AAAA,IACrD;AAAA,IAEA,OAAc,QAAQ,IAA8B,UAAmB;AACrE,UAAM,UAA2C,CAAC,GAC5C,UAAU,aAAa,YACvB,SAAS,aAAa;AAE5B,cAAQ,KAAK,GAAG,eAA6B,OAAO,YAC9C,GAAC,MAAM,CAAC,GAAG;AAGf,QAAI,SACE,OAAM,SAAS,EAAiB,KAClC,QAAQ,KAAK,EAAE,IAER,UACJ,OAAM,SAAS,EAAiB,KACnC,QAAQ,KAAK,EAAE,KAER,CAAC,YAAa,GAAW,QAAQ,QAAQ,MAClD,QAAQ,KAAK,EAAE;AAGnB,aAAO;AAAA,IACT;AAAA,IAEA,OAAc,QAAQ,OAAwB;AAC5C,UAAM,IAAI,WAAW,KAAe;AACpC,aAAI,MAAM,CAAC,IACF,IAEF;AAAA,IACT;AAAA,IAEA,OAAc,KAAK,IAAiC,OAAO,IAAI;AAC7D,MAAI,MAAM,QAAQ,EAAE,IAClB,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,UAAU,IAAI,IAExC,GAAG,MAAM,UAAU;AAAA,IAEvB;AAAA,IAEA,OAAc,KAAK,IAAiC;AAClD,MAAI,MAAM,QAAQ,EAAE,IAClB,GAAG,QAAQ,CAAC,MAAM,EAAE,MAAM,UAAU,MAAM,IAE1C,GAAG,MAAM,UAAU;AAAA,IAEvB;AAAA,IAEA,OAAc,QAAQ,IAAiC,UAAoB;AACzE,aAAO,OAAM,eAAe,IAAI,WAAW,QAAQ;AAAA,IACrD;AAAA,IAEA,OAAc,UAAU,IAAiC,UAAoB;AAC3E,aAAO,OAAM,eAAe,IAAI,aAAa,QAAQ;AAAA,IACvD;AAAA,IAEA,OAAc,eAAe,IAAiC,gBAAyC,UAAoB;AACzH,UAAK,OAAe,WAAW,QAAW;AACxC,QAAC,OAAe,OAAO,EAAE,EAAE,cAAc,EAAE,QAAQ,QAAQ;AAC3D;AAAA,MACF;AACA,MAAC,mBAAmB,YAAa,OAAM,KAAK,EAAE,IAAI,OAAM,KAAK,EAAE,GAC/D,SAAS;AAAA,IACX;AAAA,IAEA,OAAc,cAAc,WAAgB,QAAa;AACvD,MAAI,OAAO,UAAW,YACpB,OAAO,KAAK,MAAM,EAAE,QAAQ,SAAO;AACjC,QAAI,OAAO,eAAe,GAAG,KAAK,CAAC,UAAU,eAAe,GAAG,MAC7D,UAAU,GAAG,IAAI,OAAO,GAAG;AAAA,MAE/B,CAAC;AAAA,IAEL;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQA,OAAc,+BAAwC,QAAqB,OAAW;AACpF,UAAI;AACF,iBAAW,QAAQ;AACjB,UAAI,MAAM,IAAI,aAAa,cAAc,OAAQ,MAAM,IAAI,EAAiB,oBAAqB,cAC9F,MAAM,IAAI,EAAiB,iBAAiB,MAAM;AAAA,IAI3D;AAAA,EACF;AAlbE;AAAA,gBAFW,QAEI,YAAW,OAAO,iBACjC,cAHW,QAGI,cAAkB,CAAC,IAClC,cAJW,QAII,YAAW,OAAM,WAAW,WAC3C,cALW,QAKI,UAAS,OAAM,WAAW,iBACzC,cANW,QAMI,cAAa,OAAM,OAAO,WACzC,cAPW,QAOI,wBAAuB,OAAM,WAAW,KAAK,MAAM,IAClE,cARW,QAQG,WAAU;AAAA;AAAA,IAEtB,UAAU,oBAAI,QAAQ;AAAA;AAAA,IAEtB,KAAK,SAAU,SAAc,KAAa,KAAU;AAClD,MAAK,KAAK,SAAS,IAAI,OAAO,KAC5B,KAAK,SAAS,IAAI,SAAS,oBAAI,IAAI,CAAC,GAEtC,KAAK,SAAS,IAAI,OAAO,EAAE,IAAI,KAAK,GAAG;AAAA,IACzC;AAAA;AAAA,IAEA,KAAK,SAAU,SAAc,KAAa;AACxC,UAAM,KAAK,KAAK,SAAS,IAAI,OAAO;AACpC,aAAI,KACK,GAAG,IAAI,GAAG,IAEZ;AAAA,IACT;AAAA;AAAA,IAEA,QAAQ,SAAU,SAAc,KAAa;AAC3C,UAAM,MAAM,KAAK,SAAS,IAAI,OAAO,EAAE,OAAO,GAAG;AACjD,aAAM,KAAK,SAAS,IAAI,OAAO,EAAE,SAAS,KACxC,KAAK,SAAS,OAAO,OAAO,GAEvB;AAAA,IACT;AAAA,EACF;AAlCK,MAAM,QAAN,QAsbM,wBAAwB,IAAI,gBAAgB,GAGnD,YAAY;AAAA,IAChB,OAAO;AAAA,IACP,WAAW;AAAA,IACX,cAAc;AAAA,IACd,OAAO;AAAA,IACP,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,aAAa;AAAA,IACb,YAAY;AAAA,IACZ,gBAAgB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,IAQhB,kBAAkB;AAAA,IAElB,SAAS;AAAA,MACP,OAAO;AAAA,MACP,WAAW;AAAA,MACX,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,KAAK;AAAA,MACL,OAAO;AAAA,MACP,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,QAAQ;AAAA,MACR,MAAM;AAAA,MACN,WAAW;AAAA,MACX,SAAS;AAAA,MACT,OAAO;AAAA,MACP,KAAK;AAAA,MACL,IAAI;AAAA,MACJ,GAAG;AAAA,IACL;AAAA,IACA,mBAAmB;AAAA,IAEnB,sBAAsB;AAAA,MACpB,MAAM;AAAA,MACN,WAAW;AAAA,MACX,gBAAgB;AAAA,MAChB,gBAAgB;AAAA,MAChB,mBAAmB;AAAA,MACnB,mBAAmB;AAAA,IACrB;AAAA,IAEA,iBAAmB;AAAA,MACjB,QAAQ;AAAA,MACR,OAAO;AAAA,MACP,SAAS;AAAA,MACT,mBAAmB;AAAA,MACnB,oBAAoB;AAAA,IACtB;AAAA,IAEA,kBAAoB;AAAA,MAClB,UAAU;AAAA,MACV,YAAY;AAAA,MACZ,SAAS;AAAA,MACT,SAAS;AAAA,IACX;AAAA,IAEA,iBAAmB;AAAA,MACjB,MAAM;AAAA,MACN,aAAa;AAAA,MACb,mBAAmB;AAAA,MACnB,sBAAsB;AAAA,MACtB,gBAAgB;AAAA,IAClB;AAAA,IAEA,eAAe;AAAA,MACb,MAAM;AAAA,MACN,UAAU;AAAA,MACV,MAAM;AAAA,IACR;AAAA,EACF,GAEa;AAAA,IACX;AAAA,IAAY;AAAA,IAAO;AAAA,IAAW;AAAA,IAAc;AAAA,IAAO;AAAA,IAAa;AAAA,IAAY;AAAA,IAC5E;AAAA,IAAgB;AAAA,IAAkB;AAAA,IAAS;AAAA,IAAmB;AAAA,IAAsB;AAAA,IACpF;AAAA,IAAkB;AAAA,IAAiB;AAAA,EACrC,IAAI;AAGJ,EAAiB,OAAO,UAAW,eAAe,OAAO,UACvD,OAAO,QAAQ,OAAO;", + "sourcesContent": ["/**\r\n * Contains core SlickGrid classes.\r\n * @module Core\r\n * @namespace Slick\r\n */\r\n\r\nimport type {\r\n AnyFunction,\r\n CSSStyleDeclarationWritable,\r\n EditController,\r\n ElementEventListener,\r\n Handler,\r\n InferDOMType,\r\n MergeTypes\r\n} from './models/index.js';\r\n\r\nexport interface BasePubSub {\r\n publish(_eventName: string | any, _data?: ArgType): any;\r\n subscribe(_eventName: string | Function, _callback: (data: ArgType) => void): any;\r\n}\r\n\r\n/**\r\n * An event object for passing data to event handlers and letting them control propagation.\r\n *

This is pretty much identical to how W3C and jQuery implement events.

\r\n * @class EventData\r\n * @constructor\r\n */\r\nexport class SlickEventData {\r\n protected _isPropagationStopped = false;\r\n protected _isImmediatePropagationStopped = false;\r\n protected _isDefaultPrevented = false;\r\n protected returnValues: string[] = [];\r\n protected returnValue: any = undefined;\r\n protected _eventTarget?: EventTarget | null;\r\n protected nativeEvent?: Event | null;\r\n protected arguments_?: ArgType;\r\n\r\n // public props that can be optionally pulled from the provided Event in constructor\r\n // they are all optional props because it really depends on the type of Event provided (KeyboardEvent, MouseEvent, ...)\r\n readonly altKey?: boolean;\r\n readonly ctrlKey?: boolean;\r\n readonly metaKey?: boolean;\r\n readonly shiftKey?: boolean;\r\n readonly key?: string;\r\n readonly keyCode?: number;\r\n readonly clientX?: number;\r\n readonly clientY?: number;\r\n readonly offsetX?: number;\r\n readonly offsetY?: number;\r\n readonly pageX?: number;\r\n readonly pageY?: number;\r\n readonly bubbles?: boolean;\r\n readonly target?: HTMLElement;\r\n readonly type?: string;\r\n readonly which?: number;\r\n readonly x?: number;\r\n readonly y?: number;\r\n\r\n get defaultPrevented() {\r\n return this._isDefaultPrevented;\r\n }\r\n\r\n constructor(protected event?: Event | null, protected args?: ArgType) {\r\n this.nativeEvent = event;\r\n this.arguments_ = args;\r\n\r\n // when we already have an event, we want to keep some of the event properties\r\n // looping through some props is the only way to keep and sync these properties to the returned EventData\r\n if (event) {\r\n [\r\n 'altKey', 'ctrlKey', 'metaKey', 'shiftKey', 'key', 'keyCode',\r\n 'clientX', 'clientY', 'offsetX', 'offsetY', 'pageX', 'pageY',\r\n 'bubbles', 'target', 'type', 'which', 'x', 'y'\r\n ].forEach(key => (this as any)[key] = event[key as keyof Event]);\r\n }\r\n this._eventTarget = this.nativeEvent ? this.nativeEvent.target : undefined;\r\n }\r\n\r\n /**\r\n * Stops event from propagating up the DOM tree.\r\n * @method stopPropagation\r\n */\r\n stopPropagation() {\r\n this._isPropagationStopped = true;\r\n this.nativeEvent?.stopPropagation();\r\n }\r\n\r\n /**\r\n * Returns whether stopPropagation was called on this event object.\r\n * @method isPropagationStopped\r\n * @return {Boolean}\r\n */\r\n isPropagationStopped() {\r\n return this._isPropagationStopped;\r\n }\r\n\r\n /**\r\n * Prevents the rest of the handlers from being executed.\r\n * @method stopImmediatePropagation\r\n */\r\n stopImmediatePropagation() {\r\n this._isImmediatePropagationStopped = true;\r\n if (this.nativeEvent) {\r\n this.nativeEvent.stopImmediatePropagation();\r\n }\r\n };\r\n\r\n /**\r\n * Returns whether stopImmediatePropagation was called on this event object.\\\r\n * @method isImmediatePropagationStopped\r\n * @return {Boolean}\r\n */\r\n isImmediatePropagationStopped() {\r\n return this._isImmediatePropagationStopped;\r\n };\r\n\r\n getNativeEvent() {\r\n return this.nativeEvent as E;\r\n }\r\n\r\n preventDefault() {\r\n if (this.nativeEvent) {\r\n this.nativeEvent.preventDefault();\r\n }\r\n this._isDefaultPrevented = true;\r\n }\r\n\r\n isDefaultPrevented() {\r\n if (this.nativeEvent) {\r\n return this.nativeEvent.defaultPrevented;\r\n }\r\n return this._isDefaultPrevented;\r\n }\r\n\r\n addReturnValue(value: any) {\r\n this.returnValues.push(value);\r\n if (this.returnValue === undefined && value !== undefined) {\r\n this.returnValue = value;\r\n }\r\n }\r\n\r\n getReturnValue() {\r\n return this.returnValue;\r\n }\r\n\r\n getArguments() {\r\n return this.arguments_;\r\n }\r\n}\r\n\r\n/**\r\n * A simple publisher-subscriber implementation.\r\n * @class Event\r\n * @constructor\r\n */\r\nexport class SlickEvent {\r\n protected _handlers: Handler[] = [];\r\n protected _pubSubService?: BasePubSub;\r\n\r\n get subscriberCount() {\r\n return this._handlers.length;\r\n }\r\n\r\n /**\r\n * Constructor\r\n * @param {String} [eventName] - event name that could be used for dispatching CustomEvent (when enabled)\r\n * @param {BasePubSub} [pubSubService] - event name that could be used for dispatching CustomEvent (when enabled)\r\n */\r\n constructor(protected readonly eventName?: string, protected readonly pubSub?: BasePubSub) {\r\n this._pubSubService = pubSub;\r\n }\r\n\r\n /**\r\n * Adds an event handler to be called when the event is fired.\r\n *

Event handler will receive two arguments - an EventData and the data\r\n * object the event was fired with.

\r\n * @method subscribe\r\n * @param {Function} fn - Event handler.\r\n */\r\n subscribe(fn: Handler) {\r\n this._handlers.push(fn);\r\n }\r\n\r\n /**\r\n * Removes an event handler added with subscribe(fn).\r\n * @method unsubscribe\r\n * @param {Function} [fn] - Event handler to be removed.\r\n */\r\n unsubscribe(fn?: Handler) {\r\n for (let i = this._handlers.length - 1; i >= 0; i--) {\r\n if (this._handlers[i] === fn) {\r\n this._handlers.splice(i, 1);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Fires an event notifying all subscribers.\r\n * @method notify\r\n * @param {Object} args Additional data object to be passed to all handlers.\r\n * @param {EventData} [event] - An EventData object to be passed to all handlers.\r\n * For DOM events, an existing W3C event object can be passed in.\r\n * @param {Object} [scope] - The scope (\"this\") within which the handler will be executed.\r\n * If not specified, the scope will be set to the Event instance.\r\n */\r\n notify(args: ArgType, evt?: SlickEventData | Event | MergeTypes, Event> | null, scope?: any) {\r\n const sed: SlickEventData = evt instanceof SlickEventData\r\n ? evt\r\n : new SlickEventData(evt, args);\r\n scope = scope || this;\r\n\r\n for (let i = 0; i < this._handlers.length && !(sed.isPropagationStopped() || sed.isImmediatePropagationStopped()); i++) {\r\n const returnValue = this._handlers[i].call(scope, sed, args);\r\n sed.addReturnValue(returnValue);\r\n }\r\n\r\n // user can optionally add a global PubSub Service which makes it easy to publish/subscribe to events\r\n if (typeof this._pubSubService?.publish === 'function' && this.eventName) {\r\n const ret = this._pubSubService.publish<{ args: ArgType; eventData?: SlickEventData; nativeEvent?: Event; }>(this.eventName, { args, eventData: sed });\r\n sed.addReturnValue(ret);\r\n }\r\n return sed;\r\n }\r\n\r\n setPubSubService(pubSub: BasePubSub) {\r\n this._pubSubService = pubSub;\r\n }\r\n}\r\n\r\nexport class SlickEventHandler {\r\n protected handlers: Array<{ event: SlickEvent; handler: Handler; }> = [];\r\n\r\n subscribe(event: SlickEvent, handler: Handler) {\r\n this.handlers.push({ event, handler });\r\n event.subscribe(handler);\r\n\r\n return this as SlickEventHandler; // allow chaining\r\n }\r\n\r\n unsubscribe(event: SlickEvent, handler: Handler) {\r\n let i = this.handlers.length;\r\n while (i--) {\r\n if (this.handlers[i].event === event &&\r\n this.handlers[i].handler === handler) {\r\n this.handlers.splice(i, 1);\r\n event.unsubscribe(handler);\r\n return;\r\n }\r\n }\r\n\r\n return this as SlickEventHandler; // allow chaining\r\n }\r\n\r\n unsubscribeAll() {\r\n let i = this.handlers.length;\r\n while (i--) {\r\n this.handlers[i].event.unsubscribe(this.handlers[i].handler);\r\n }\r\n this.handlers = [];\r\n\r\n return this as SlickEventHandler; // allow chaining\r\n }\r\n}\r\n\r\n/**\r\n * A structure containing a range of cells.\r\n * @class Range\r\n * @constructor\r\n * @param fromRow {Integer} Starting row.\r\n * @param fromCell {Integer} Starting cell.\r\n * @param toRow {Integer} Optional. Ending row. Defaults to fromRow.\r\n * @param toCell {Integer} Optional. Ending cell. Defaults to fromCell.\r\n */\r\nexport class SlickRange {\r\n fromRow: number;\r\n fromCell: number;\r\n toCell: number;\r\n toRow: number;\r\n\r\n constructor(fromRow: number, fromCell: number, toRow?: number, toCell?: number) {\r\n if (toRow === undefined && toCell === undefined) {\r\n toRow = fromRow;\r\n toCell = fromCell;\r\n }\r\n\r\n /**\r\n * @property fromRow\r\n * @type {Integer}\r\n */\r\n this.fromRow = Math.min(fromRow, toRow as number);\r\n\r\n /**\r\n * @property fromCell\r\n * @type {Integer}\r\n */\r\n this.fromCell = Math.min(fromCell, toCell as number);\r\n\r\n /**\r\n * @property toCell\r\n * @type {Integer}\r\n */\r\n this.toCell = Math.max(fromCell, toCell as number);\r\n\r\n /**\r\n * @property toRow\r\n * @type {Integer}\r\n */\r\n this.toRow = Math.max(fromRow, toRow as number);\r\n }\r\n\r\n\r\n /**\r\n * Returns whether a range represents a single row.\r\n * @method isSingleRow\r\n * @return {Boolean}\r\n */\r\n isSingleRow() {\r\n return this.fromRow === this.toRow;\r\n }\r\n\r\n /**\r\n * Returns whether a range represents a single cell.\r\n * @method isSingleCell\r\n * @return {Boolean}\r\n */\r\n isSingleCell() {\r\n return this.fromRow === this.toRow && this.fromCell === this.toCell;\r\n }\r\n\r\n /**\r\n * Returns whether a range contains a given cell.\r\n * @method contains\r\n * @param row {Integer}\r\n * @param cell {Integer}\r\n * @return {Boolean}\r\n */\r\n contains(row: number, cell: number) {\r\n return row >= this.fromRow && row <= this.toRow &&\r\n cell >= this.fromCell && cell <= this.toCell;\r\n }\r\n\r\n /**\r\n * Returns a readable representation of a range.\r\n * @method toString\r\n * @return {String}\r\n */\r\n toString() {\r\n if (this.isSingleCell()) {\r\n return `(${this.fromRow}:${this.fromCell})`;\r\n }\r\n else {\r\n return `(${this.fromRow}:${this.fromCell} - ${this.toRow}:${this.toCell})`;\r\n }\r\n };\r\n}\r\n\r\n/**\r\n * Create a handle element for Excel style drag-replace\r\n * @class DragExtendHandle\r\n * @constructor\r\n * @param gridUid {String} string UID of parent grid\r\n */\r\nexport class SlickDragExtendHandle {\r\n id: string;\r\n cssClass = 'slick-drag-replace-handle';\r\n\r\n constructor(gridUid: string) {\r\n this.id = gridUid + \"_drag_replace_handle\";\r\n }\r\n\r\n getHandleHtml() {\r\n return '

';\r\n //console.log('DragReplaceEl.getStringEl');\r\n }\r\n\r\n removeEl() {\r\n const dragReplaceEl = document.getElementById(this.id);\r\n if (dragReplaceEl) { dragReplaceEl.remove(); }\r\n //console.log('DragReplaceEl.removeEl');\r\n }\r\n\r\n createEl(activeCellNode: any) {\r\n const dragReplaceEl = document.createElement(\"div\");\r\n dragReplaceEl.classList.add(\"slick-drag-replace-handle\");\r\n dragReplaceEl.setAttribute(\"id\", this.id);\r\n activeCellNode.appendChild(dragReplaceEl);\r\n console.log('DragReplaceEl.createEl');\r\n }\r\n}\r\n\r\n/**\r\n * A base class that all special / non-data rows (like Group and GroupTotals) derive from.\r\n * @class NonDataItem\r\n * @constructor\r\n */\r\nexport class SlickNonDataItem {\r\n __nonDataRow = true;\r\n}\r\n\r\n\r\n/**\r\n * Information about a group of rows.\r\n * @class Group\r\n * @extends Slick.NonDataItem\r\n * @constructor\r\n */\r\nexport class SlickGroup extends SlickNonDataItem {\r\n __group = true;\r\n\r\n /**\r\n * Grouping level, starting with 0.\r\n * @property level\r\n * @type {Number}\r\n */\r\n level = 0;\r\n\r\n /**\r\n * Number of rows in the group.\r\n * @property count\r\n * @type {Integer}\r\n */\r\n count = 0;\r\n\r\n /**\r\n * Grouping value.\r\n * @property value\r\n * @type {Object}\r\n */\r\n value = null;\r\n\r\n /**\r\n * Formatted display value of the group.\r\n * @property title\r\n * @type {String}\r\n */\r\n title: string | null = null;\r\n\r\n /**\r\n * Whether a group is collapsed.\r\n * @property collapsed\r\n * @type {Boolean}\r\n */\r\n collapsed: boolean | number = false;\r\n\r\n /**\r\n * Whether a group selection checkbox is checked.\r\n * @property selectChecked\r\n * @type {Boolean}\r\n */\r\n selectChecked = false;\r\n\r\n /**\r\n * GroupTotals, if any.\r\n * @property totals\r\n * @type {GroupTotals}\r\n */\r\n totals: SlickGroupTotals = null as any;\r\n\r\n /**\r\n * Rows that are part of the group.\r\n * @property rows\r\n * @type {Array}\r\n */\r\n rows: number[] = [];\r\n\r\n /**\r\n * Sub-groups that are part of the group.\r\n * @property groups\r\n * @type {Array}\r\n */\r\n groups: any[] = null as any;\r\n\r\n /**\r\n * A unique key used to identify the group. This key can be used in calls to DataView\r\n * collapseGroup() or expandGroup().\r\n * @property groupingKey\r\n * @type {Object}\r\n */\r\n groupingKey: any = null;\r\n\r\n constructor() {\r\n super();\r\n }\r\n /**\r\n * Compares two Group instances.\r\n * @method equals\r\n * @return {Boolean}\r\n * @param group {Group} Group instance to compare to.\r\n */\r\n equals(group: SlickGroup): boolean {\r\n return this.value === group.value &&\r\n this.count === group.count &&\r\n this.collapsed === group.collapsed &&\r\n this.title === group.title;\r\n };\r\n}\r\n\r\n/**\r\n * Information about group totals.\r\n * An instance of GroupTotals will be created for each totals row and passed to the aggregators\r\n * so that they can store arbitrary data in it. That data can later be accessed by group totals\r\n * formatters during the display.\r\n * @class GroupTotals\r\n * @extends Slick.NonDataItem\r\n * @constructor\r\n */\r\nexport class SlickGroupTotals extends SlickNonDataItem {\r\n __groupTotals = true;\r\n\r\n /**\r\n * Parent Group.\r\n * @param group\r\n * @type {Group}\r\n */\r\n group: SlickGroup = null as any;\r\n\r\n /**\r\n * Whether the totals have been fully initialized / calculated.\r\n * Will be set to false for lazy-calculated group totals.\r\n * @param initialized\r\n * @type {Boolean}\r\n */\r\n initialized = false;\r\n\r\n constructor() {\r\n super();\r\n }\r\n}\r\n\r\n/**\r\n * A locking helper to track the active edit controller and ensure that only a single controller\r\n * can be active at a time. This prevents a whole class of state and validation synchronization\r\n * issues. An edit controller (such as SlickGrid) can query if an active edit is in progress\r\n * and attempt a commit or cancel before proceeding.\r\n * @class EditorLock\r\n * @constructor\r\n */\r\nexport class SlickEditorLock {\r\n activeEditController: any = null;\r\n\r\n /**\r\n * Returns true if a specified edit controller is active (has the edit lock).\r\n * If the parameter is not specified, returns true if any edit controller is active.\r\n * @method isActive\r\n * @param editController {EditController}\r\n * @return {Boolean}\r\n */\r\n isActive(editController?: EditController): boolean {\r\n return (editController ? this.activeEditController === editController : this.activeEditController !== null);\r\n };\r\n\r\n /**\r\n * Sets the specified edit controller as the active edit controller (acquire edit lock).\r\n * If another edit controller is already active, and exception will be throw new Error(.\r\n * @method activate\r\n * @param editController {EditController} edit controller acquiring the lock\r\n */\r\n activate(editController: EditController) {\r\n if (editController === this.activeEditController) { // already activated?\r\n return;\r\n }\r\n if (this.activeEditController !== null) {\r\n throw new Error(`Slick.EditorLock.activate: an editController is still active, can't activate another editController`);\r\n }\r\n if (!editController.commitCurrentEdit) {\r\n throw new Error('Slick.EditorLock.activate: editController must implement .commitCurrentEdit()');\r\n }\r\n if (!editController.cancelCurrentEdit) {\r\n throw new Error('Slick.EditorLock.activate: editController must implement .cancelCurrentEdit()');\r\n }\r\n this.activeEditController = editController;\r\n };\r\n\r\n /**\r\n * Unsets the specified edit controller as the active edit controller (release edit lock).\r\n * If the specified edit controller is not the active one, an exception will be throw new Error(.\r\n * @method deactivate\r\n * @param editController {EditController} edit controller releasing the lock\r\n */\r\n deactivate(editController: EditController) {\r\n if (!this.activeEditController) {\r\n return;\r\n }\r\n if (this.activeEditController !== editController) {\r\n throw new Error('Slick.EditorLock.deactivate: specified editController is not the currently active one');\r\n }\r\n this.activeEditController = null;\r\n };\r\n\r\n /**\r\n * Attempts to commit the current edit by calling \"commitCurrentEdit\" method on the active edit\r\n * controller and returns whether the commit attempt was successful (commit may fail due to validation\r\n * errors, etc.). Edit controller's \"commitCurrentEdit\" must return true if the commit has succeeded\r\n * and false otherwise. If no edit controller is active, returns true.\r\n * @method commitCurrentEdit\r\n * @return {Boolean}\r\n */\r\n commitCurrentEdit(): boolean {\r\n return (this.activeEditController ? this.activeEditController.commitCurrentEdit() : true);\r\n };\r\n\r\n /**\r\n * Attempts to cancel the current edit by calling \"cancelCurrentEdit\" method on the active edit\r\n * controller and returns whether the edit was successfully cancelled. If no edit controller is\r\n * active, returns true.\r\n * @method cancelCurrentEdit\r\n * @return {Boolean}\r\n */\r\n cancelCurrentEdit(): boolean {\r\n return (this.activeEditController ? this.activeEditController.cancelCurrentEdit() : true);\r\n };\r\n}\r\n\r\nfunction regexSanitizer(dirtyHtml: string) {\r\n return dirtyHtml.replace(/(\\b)(on[a-z]+)(\\s*)=|javascript:([^>]*)[^>]*|(<\\s*)(\\/*)script([<>]*).*(<\\s*)(\\/*)script(>*)|(<)(\\/*)(script|script defer)(.*)(>|>\">)/gi, '');\r\n}\r\n\r\n/**\r\n * A simple binding event service to keep track of all JavaScript events with callback listeners,\r\n * it allows us to unbind event(s) and their listener(s) by calling a simple unbind method call.\r\n * Unbinding is a necessary step to make sure that all event listeners are removed to avoid memory leaks when destroing the grid\r\n */\r\nexport class BindingEventService {\r\n protected _boundedEvents: ElementEventListener[] = [];\r\n\r\n getBoundedEvents() {\r\n return this._boundedEvents;\r\n }\r\n\r\n destroy() {\r\n this.unbindAll();\r\n }\r\n\r\n /** Bind an event listener to any element */\r\n bind(element: Element | Window, eventName: string, listener: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions, groupName = '') {\r\n if (element) {\r\n element.addEventListener(eventName, listener, options);\r\n this._boundedEvents.push({ element, eventName, listener, groupName });\r\n }\r\n }\r\n\r\n /** Unbind all will remove every every event handlers that were bounded earlier */\r\n unbind(element: Element | Window, eventName: string, listener: EventListenerOrEventListenerObject) {\r\n if (element?.removeEventListener) {\r\n element.removeEventListener(eventName, listener);\r\n }\r\n }\r\n\r\n unbindByEventName(element: Element | Window, eventName: string) {\r\n const boundedEvent = this._boundedEvents.find(e => e.element === element && e.eventName === eventName);\r\n if (boundedEvent) {\r\n this.unbind(boundedEvent.element, boundedEvent.eventName, boundedEvent.listener);\r\n }\r\n }\r\n\r\n /**\r\n * Unbind all event listeners that were bounded, optionally provide a group name to unbind all listeners assigned to that specific group only.\r\n */\r\n unbindAll(groupName?: string | string[]) {\r\n if (groupName) {\r\n const groupNames = Array.isArray(groupName) ? groupName : [groupName];\r\n\r\n // unbind only the bounded event with a specific group\r\n // Note: we need to loop in reverse order to avoid array reindexing (causing index offset) after a splice is called\r\n for (let i = this._boundedEvents.length - 1; i >= 0; --i) {\r\n const boundedEvent = this._boundedEvents[i];\r\n if (groupNames.some(g => g === boundedEvent.groupName)) {\r\n const { element, eventName, listener } = boundedEvent;\r\n this.unbind(element, eventName, listener);\r\n this._boundedEvents.splice(i, 1);\r\n }\r\n }\r\n } else {\r\n // unbind everything\r\n while (this._boundedEvents.length > 0) {\r\n const boundedEvent = this._boundedEvents.pop() as ElementEventListener;\r\n const { element, eventName, listener } = boundedEvent;\r\n this.unbind(element, eventName, listener);\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport class Utils {\r\n // jQuery's extend\r\n private static getProto = Object.getPrototypeOf;\r\n private static class2type: any = {};\r\n private static toString = Utils.class2type.toString;\r\n private static hasOwn = Utils.class2type.hasOwnProperty;\r\n private static fnToString = Utils.hasOwn.toString;\r\n private static ObjectFunctionString = Utils.fnToString.call(Object);\r\n public static storage = {\r\n // https://stackoverflow.com/questions/29222027/vanilla-alternative-to-jquery-data-function-any-native-javascript-alternati\r\n _storage: new WeakMap(),\r\n // eslint-disable-next-line object-shorthand\r\n put: function (element: any, key: string, obj: any) {\r\n if (!this._storage.has(element)) {\r\n this._storage.set(element, new Map());\r\n }\r\n this._storage.get(element).set(key, obj);\r\n },\r\n // eslint-disable-next-line object-shorthand\r\n get: function (element: any, key: string) {\r\n const el = this._storage.get(element);\r\n if (el) {\r\n return el.get(key);\r\n }\r\n return null;\r\n },\r\n // eslint-disable-next-line object-shorthand\r\n remove: function (element: any, key: string) {\r\n const ret = this._storage.get(element).delete(key);\r\n if (!(this._storage.get(element).size === 0)) {\r\n this._storage.delete(element);\r\n }\r\n return ret;\r\n }\r\n };\r\n\r\n public static isFunction(obj: any) {\r\n return typeof obj === 'function' && typeof obj.nodeType !== 'number' && typeof obj.item !== 'function';\r\n }\r\n\r\n public static isPlainObject(obj: any) {\r\n if (!obj || Utils.toString.call(obj) !== '[object Object]') {\r\n return false;\r\n }\r\n\r\n const proto = Utils.getProto(obj);\r\n if (!proto) {\r\n return true;\r\n }\r\n const Ctor = Utils.hasOwn.call(proto, 'constructor') && proto.constructor;\r\n return typeof Ctor === 'function' && Utils.fnToString.call(Ctor) === Utils.ObjectFunctionString;\r\n }\r\n\r\n public static calculateAvailableSpace(element: HTMLElement) {\r\n let bottom = 0, top = 0, left = 0, right = 0;\r\n\r\n const windowHeight = window.innerHeight || 0;\r\n const windowWidth = window.innerWidth || 0;\r\n const scrollPosition = Utils.windowScrollPosition();\r\n const pageScrollTop = scrollPosition.top;\r\n const pageScrollLeft = scrollPosition.left;\r\n const elmOffset = Utils.offset(element);\r\n\r\n if (elmOffset) {\r\n const elementOffsetTop = elmOffset.top || 0;\r\n const elementOffsetLeft = elmOffset.left || 0;\r\n top = elementOffsetTop - pageScrollTop;\r\n bottom = windowHeight - (elementOffsetTop - pageScrollTop);\r\n left = elementOffsetLeft - pageScrollLeft;\r\n right = windowWidth - (elementOffsetLeft - pageScrollLeft);\r\n }\r\n\r\n return { top, bottom, left, right };\r\n }\r\n\r\n public static extend(...args: any[]): T {\r\n let options, name, src, copy, copyIsArray, clone,\r\n target = args[0],\r\n i = 1,\r\n deep = false;\r\n const length = args.length;\r\n\r\n if (typeof target === 'boolean') {\r\n deep = target;\r\n target = args[i] || {};\r\n i++;\r\n } else {\r\n target = target || {};\r\n }\r\n if (typeof target !== 'object' && !Utils.isFunction(target)) {\r\n target = {};\r\n }\r\n if (i === length) {\r\n // eslint-disable-next-line @typescript-eslint/no-this-alias\r\n target = this;\r\n i--;\r\n }\r\n for (; i < length; i++) {\r\n if (Utils.isDefined(options = args[i])) {\r\n for (name in options) {\r\n copy = options[name];\r\n if (name === '__proto__' || target === copy) {\r\n continue;\r\n }\r\n if (deep && copy && (Utils.isPlainObject(copy) ||\r\n (copyIsArray = Array.isArray(copy)))) {\r\n src = target[name];\r\n if (copyIsArray && !Array.isArray(src)) {\r\n clone = [];\r\n } else if (!copyIsArray && !Utils.isPlainObject(src)) {\r\n clone = {};\r\n } else {\r\n clone = src;\r\n }\r\n copyIsArray = false;\r\n target[name] = Utils.extend(deep, clone, copy);\r\n } else if (copy !== undefined) {\r\n target[name] = copy;\r\n }\r\n }\r\n }\r\n }\r\n return target as T;\r\n }\r\n\r\n /**\r\n * Create a DOM Element with any optional attributes or properties.\r\n * It will only accept valid DOM element properties that `createElement` would accept.\r\n * For example: `createDomElement('div', { className: 'my-css-class' })`,\r\n * for style or dataset you need to use nested object `{ style: { display: 'none' }}\r\n * The last argument is to optionally append the created element to a parent container element.\r\n * @param {String} tagName - html tag\r\n * @param {Object} options - element properties\r\n * @param {[HTMLElement]} appendToParent - parent element to append to\r\n */\r\n public static createDomElement(\r\n tagName: T,\r\n elementOptions?: null | { [P in K]: InferDOMType },\r\n appendToParent?: Element\r\n ): HTMLElementTagNameMap[T] {\r\n const elm = document.createElement(tagName);\r\n\r\n if (elementOptions) {\r\n Object.keys(elementOptions).forEach((elmOptionKey) => {\r\n if (elmOptionKey === 'innerHTML') {\r\n console.warn(`[SlickGrid] For better CSP (Content Security Policy) support, do not use \"innerHTML\" directly in \"createDomElement('${tagName}', { innerHTML: 'some html'})\"` +\r\n `, it is better as separate assignment: \"const elm = createDomElement('span'); elm.innerHTML = 'some html';\"`);\r\n }\r\n\r\n const elmValue = elementOptions[elmOptionKey as keyof typeof elementOptions];\r\n if (typeof elmValue === 'object') {\r\n Object.assign(elm[elmOptionKey as K] as object, elmValue);\r\n } else {\r\n elm[elmOptionKey as K] = (elementOptions as any)[elmOptionKey as keyof typeof elementOptions];\r\n }\r\n });\r\n }\r\n if (appendToParent?.appendChild) {\r\n appendToParent.appendChild(elm);\r\n }\r\n return elm;\r\n }\r\n\r\n /**\r\n * From any input provided, return the HTML string (when a string is provided, it will be returned \"as is\" but when it's a number it will be converted to string)\r\n * When detecting HTMLElement/DocumentFragment, we can also specify which HTML type to retrieve innerHTML or outerHTML.\r\n * We can get the HTML by looping through all fragment `childNodes`\r\n * @param {DocumentFragment | HTMLElement | string | number} input\r\n * @param {'innerHTML' | 'outerHTML'} [type] - when the input is a DocumentFragment or HTMLElement, which type of HTML do you want to return? 'innerHTML' or 'outerHTML'\r\n * @returns {String}\r\n */\r\n public static getHtmlStringOutput(input: DocumentFragment | HTMLElement | string | number, type: 'innerHTML' | 'outerHTML' = 'innerHTML'): string {\r\n if (input instanceof DocumentFragment) {\r\n // a DocumentFragment doesn't have innerHTML/outerHTML, but we can loop through all children and concatenate them all to an HTML string\r\n return [].map.call(input.childNodes, (x: HTMLElement) => x[type]).join('') || input.textContent || '';\r\n } else if (input instanceof HTMLElement) {\r\n return input[type];\r\n }\r\n return String(input); // reaching this line means it's already a string (or number) so just return it as string\r\n }\r\n\r\n public static emptyElement(element?: T | null): T | undefined | null {\r\n while (element?.firstChild) {\r\n element.removeChild(element.firstChild);\r\n }\r\n return element;\r\n }\r\n\r\n /**\r\n * Accepts string containing the class or space-separated list of classes, and\r\n * returns list of individual classes.\r\n * Method properly takes into account extra whitespaces in the `className`\r\n * e.g.: \" class1 class2 \" => will result in `['class1', 'class2']`.\r\n * @param {String} className - space separated list of class names\r\n */\r\n public static classNameToList(className = ''): string[] {\r\n return className.split(' ').filter(cls => cls);\r\n }\r\n\r\n public static innerSize(elm: HTMLElement, type: 'height' | 'width') {\r\n let size = 0;\r\n\r\n if (elm) {\r\n const clientSize = type === 'height' ? 'clientHeight' : 'clientWidth';\r\n const sides = type === 'height' ? ['top', 'bottom'] : ['left', 'right'];\r\n size = elm[clientSize];\r\n for (const side of sides) {\r\n const sideSize = (parseFloat(Utils.getElementProp(elm, `padding-${side}`) || '') || 0);\r\n size -= sideSize;\r\n }\r\n }\r\n return size;\r\n }\r\n\r\n public static isDefined(value: T | undefined | null): value is T {\r\n return value !== undefined && value !== null && value !== '';\r\n }\r\n\r\n public static getElementProp(elm: HTMLElement & { getComputedStyle?: () => CSSStyleDeclaration }, property: string) {\r\n if (elm?.getComputedStyle) {\r\n return window.getComputedStyle(elm, null).getPropertyValue(property);\r\n }\r\n return null;\r\n }\r\n\r\n /**\r\n * Get the function details (param & body) of a function.\r\n * It supports regular function and also ES6 arrow functions\r\n * @param {Function} fn - function to analyze\r\n * @param {Boolean} [addReturn] - when using ES6 function as single liner, we could add the missing `return ...`\r\n * @returns\r\n */\r\n public static getFunctionDetails(fn: AnyFunction, addReturn = true) {\r\n let isAsyncFn = false;\r\n\r\n const getFunctionBody = (func: AnyFunction) => {\r\n const fnStr = func.toString();\r\n isAsyncFn = fnStr.includes('async ');\r\n\r\n // when fn is one liner arrow fn returning an object in brackets e.g. `() => ({ hello: 'world' })`\r\n if ((fnStr.replaceAll(' ', '').includes('=>({'))) {\r\n const matches = fnStr.match(/(({.*}))/g) || [];\r\n return matches.length >= 1 ? `return ${matches[0]!.trimStart()}` : fnStr;\r\n }\r\n const isOneLinerArrowFn = (!fnStr.includes('{') && fnStr.includes('=>'));\r\n const body = fnStr.substring(\r\n (fnStr.indexOf('{') + 1) || (fnStr.indexOf('=>') + 2),\r\n fnStr.includes('}') ? fnStr.lastIndexOf('}') : fnStr.length\r\n );\r\n if (addReturn && isOneLinerArrowFn && !body.startsWith('return')) {\r\n return 'return ' + body.trimStart(); // add the `return ...` to the body for ES6 arrow fn\r\n }\r\n return body;\r\n };\r\n\r\n const getFunctionParams = (func: AnyFunction): string[] => {\r\n const STRIP_COMMENTS = /(\\/\\/.*$)|(\\/\\*[\\s\\S]*?\\*\\/)|(\\s*=[^,)]*(('(?:\\\\'|[^'\\r\\n])*')|(\"(?:\\\\\"|[^\"\\r\\n])*\"))|(\\s*=[^,)]*))/mg;\r\n const ARG_NAMES = /([^\\s,]+)/g;\r\n const fnStr = func.toString().replace(STRIP_COMMENTS, '');\r\n return fnStr.slice(fnStr.indexOf('(') + 1, fnStr.indexOf(')')).match(ARG_NAMES) ?? [];\r\n };\r\n\r\n return {\r\n params: getFunctionParams(fn),\r\n body: getFunctionBody(fn),\r\n isAsync: isAsyncFn,\r\n };\r\n }\r\n\r\n public static insertAfterElement(referenceNode: HTMLElement, newNode: HTMLElement) {\r\n referenceNode.parentNode?.insertBefore(newNode, referenceNode.nextSibling);\r\n }\r\n\r\n public static isEmptyObject(obj: any) {\r\n if (obj === null || obj === undefined) {\r\n return true;\r\n }\r\n return Object.entries(obj).length === 0;\r\n }\r\n\r\n public static noop() { }\r\n\r\n public static offset(el: HTMLElement | null) {\r\n if (!el || !el.getBoundingClientRect) {\r\n return undefined;\r\n }\r\n const box = el.getBoundingClientRect();\r\n const docElem = document.documentElement;\r\n\r\n return {\r\n top: box.top + window.pageYOffset - docElem.clientTop,\r\n left: box.left + window.pageXOffset - docElem.clientLeft\r\n };\r\n }\r\n\r\n public static windowScrollPosition() {\r\n return {\r\n left: window.pageXOffset || document.documentElement.scrollLeft || 0,\r\n top: window.pageYOffset || document.documentElement.scrollTop || 0,\r\n };\r\n }\r\n\r\n public static width(el: HTMLElement, value?: number | string): number | void {\r\n if (!el || !el.getBoundingClientRect) { return; }\r\n if (value === undefined) {\r\n return el.getBoundingClientRect().width;\r\n }\r\n Utils.setStyleSize(el, 'width', value);\r\n }\r\n\r\n public static height(el: HTMLElement, value?: number | string): number | void {\r\n if (!el) { return; }\r\n if (value === undefined) {\r\n return el.getBoundingClientRect().height;\r\n }\r\n Utils.setStyleSize(el, 'height', value);\r\n }\r\n\r\n public static setStyleSize(el: HTMLElement, style: string, val?: number | string | Function) {\r\n if (typeof val === 'function') {\r\n val = val();\r\n } else if (typeof val === 'string') {\r\n el.style[style as CSSStyleDeclarationWritable] = val;\r\n } else {\r\n el.style[style as CSSStyleDeclarationWritable] = val + 'px';\r\n }\r\n }\r\n\r\n public static contains(parent: HTMLElement, child: HTMLElement) {\r\n if (!parent || !child) {\r\n return false;\r\n }\r\n\r\n const parentList = Utils.parents(child);\r\n return !parentList.every((p) => {\r\n if (parent === p) {\r\n return false;\r\n }\r\n return true;\r\n });\r\n }\r\n\r\n public static isHidden(el: HTMLElement) {\r\n return el.offsetWidth === 0 && el.offsetHeight === 0;\r\n }\r\n\r\n public static parents(el: HTMLElement | ParentNode, selector?: string) {\r\n const parents: Array = [];\r\n const visible = selector === ':visible';\r\n const hidden = selector === ':hidden';\r\n\r\n while ((el = el.parentNode as ParentNode) && el !== document) {\r\n if (!el || !el.parentNode) {\r\n break;\r\n }\r\n if (hidden) {\r\n if (Utils.isHidden(el as HTMLElement)) {\r\n parents.push(el);\r\n }\r\n } else if (visible) {\r\n if (!Utils.isHidden(el as HTMLElement)) {\r\n parents.push(el);\r\n }\r\n } else if (!selector || (el as any).matches(selector)) {\r\n parents.push(el);\r\n }\r\n }\r\n return parents;\r\n }\r\n\r\n public static toFloat(value: string | number) {\r\n const x = parseFloat(value as string);\r\n if (isNaN(x)) {\r\n return 0;\r\n }\r\n return x;\r\n }\r\n\r\n public static show(el: HTMLElement | HTMLElement[], type = '') {\r\n if (Array.isArray(el)) {\r\n el.forEach((e) => e.style.display = type);\r\n } else {\r\n el.style.display = type;\r\n }\r\n }\r\n\r\n public static hide(el: HTMLElement | HTMLElement[]) {\r\n if (Array.isArray(el)) {\r\n el.forEach((e) => e.style.display = 'none');\r\n } else {\r\n el.style.display = 'none';\r\n }\r\n }\r\n\r\n public static slideUp(el: HTMLElement | HTMLElement[], callback: Function) {\r\n return Utils.slideAnimation(el, 'slideUp', callback);\r\n }\r\n\r\n public static slideDown(el: HTMLElement | HTMLElement[], callback: Function) {\r\n return Utils.slideAnimation(el, 'slideDown', callback);\r\n }\r\n\r\n public static slideAnimation(el: HTMLElement | HTMLElement[], slideDirection: 'slideDown' | 'slideUp', callback: Function) {\r\n if ((window as any).jQuery !== undefined) {\r\n (window as any).jQuery(el)[slideDirection]('fast', callback);\r\n return;\r\n }\r\n (slideDirection === 'slideUp') ? Utils.hide(el) : Utils.show(el);\r\n callback();\r\n }\r\n\r\n public static applyDefaults(targetObj: any, srcObj: any) {\r\n if (typeof srcObj === 'object') {\r\n Object.keys(srcObj).forEach(key => {\r\n if (srcObj.hasOwnProperty(key) && !targetObj.hasOwnProperty(key)) {\r\n targetObj[key] = srcObj[key];\r\n }\r\n });\r\n }\r\n }\r\n\r\n /**\r\n * User could optionally add PubSub Service to SlickEvent\r\n * When it is defined then a SlickEvent `notify()` call will also dispatch it by using the PubSub publish() method\r\n * @param {BasePubSub} [pubSubService]\r\n * @param {*} scope\r\n */\r\n public static addSlickEventPubSubWhenDefined(pubSub?: BasePubSub, scope?: T) {\r\n if (pubSub) {\r\n for (const prop in scope) {\r\n if (scope[prop] instanceof SlickEvent && typeof (scope[prop] as SlickEvent).setPubSubService === 'function') {\r\n (scope[prop] as SlickEvent).setPubSubService(pubSub);\r\n }\r\n }\r\n }\r\n }\r\n}\r\n\r\nexport const SlickGlobalEditorLock = new SlickEditorLock();\r\n\r\n// export Slick namespace on both global & window objects\r\nconst SlickCore = {\r\n Event: SlickEvent,\r\n EventData: SlickEventData,\r\n EventHandler: SlickEventHandler,\r\n Range: SlickRange,\r\n DragExtendHandle: SlickDragExtendHandle,\r\n NonDataRow: SlickNonDataItem,\r\n Group: SlickGroup,\r\n GroupTotals: SlickGroupTotals,\r\n EditorLock: SlickEditorLock,\r\n RegexSanitizer: regexSanitizer,\r\n\r\n /**\r\n * A global singleton editor lock.\r\n * @class GlobalEditorLock\r\n * @static\r\n * @constructor\r\n */\r\n GlobalEditorLock: SlickGlobalEditorLock,\r\n\r\n keyCode: {\r\n SPACE: 8,\r\n BACKSPACE: 8,\r\n DELETE: 46,\r\n DOWN: 40,\r\n END: 35,\r\n ENTER: 13,\r\n ESCAPE: 27,\r\n HOME: 36,\r\n INSERT: 45,\r\n LEFT: 37,\r\n PAGE_DOWN: 34,\r\n PAGE_UP: 33,\r\n RIGHT: 39,\r\n TAB: 9,\r\n UP: 38,\r\n A: 65\r\n },\r\n preClickClassName: 'slick-edit-preclick',\r\n\r\n GridAutosizeColsMode: {\r\n None: 'NOA',\r\n LegacyOff: 'LOF',\r\n LegacyForceFit: 'LFF',\r\n IgnoreViewport: 'IGV',\r\n FitColsToViewport: 'FCV',\r\n FitViewportToCols: 'FVC'\r\n },\r\n\r\n 'ColAutosizeMode': {\r\n Locked: 'LCK',\r\n Guide: 'GUI',\r\n Content: 'CON',\r\n ContentExpandOnly: 'CXO',\r\n ContentIntelligent: 'CTI'\r\n },\r\n\r\n 'RowSelectionMode': {\r\n FirstRow: 'FS1',\r\n FirstNRows: 'FSN',\r\n AllRows: 'ALL',\r\n LastRow: 'LS1'\r\n },\r\n\r\n 'CellSelectionMode': {\r\n Select: \"SEL\",\r\n Replace: \"REP\"\r\n },\r\n\r\n 'ValueFilterMode': {\r\n None: 'NONE',\r\n DeDuplicate: 'DEDP',\r\n GetGreatestAndSub: 'GR8T',\r\n GetLongestTextAndSub: 'LNSB',\r\n GetLongestText: 'LNSC'\r\n },\r\n\r\n WidthEvalMode: {\r\n Auto: 'AUTO',\r\n TextOnly: 'CANV',\r\n HTML: 'HTML'\r\n }\r\n};\r\n\r\nexport const {\r\n EditorLock, Event, EventData, EventHandler, Group, GroupTotals, NonDataRow, Range, DragExtendHandle,\r\n RegexSanitizer, GlobalEditorLock, keyCode, preClickClassName, GridAutosizeColsMode, ColAutosizeMode,\r\n RowSelectionMode, CellSelectionMode,ValueFilterMode, WidthEvalMode\r\n} = SlickCore;\r\n\r\n// also add to global object when exist\r\nif (IIFE_ONLY && typeof global !== 'undefined' && window.Slick) {\r\n global.Slick = window.Slick;\r\n}\r\n"], + "mappings": "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", "names": [] } diff --git a/dist/browser/slick.grid.js b/dist/browser/slick.grid.js index dec741f6..5b540994 100644 --- a/dist/browser/slick.grid.js +++ b/dist/browser/slick.grid.js @@ -5,7 +5,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key != "symbol" ? key + "" : key, value); // src/slick.grid.ts - var BindingEventService = Slick.BindingEventService, ColAutosizeMode = Slick.ColAutosizeMode, SlickEvent = Slick.Event, SlickEventData = Slick.EventData, GlobalEditorLock = Slick.GlobalEditorLock, GridAutosizeColsMode = Slick.GridAutosizeColsMode, keyCode = Slick.keyCode, preClickClassName = Slick.preClickClassName, SlickRange = Slick.Range, RowSelectionMode = Slick.RowSelectionMode, ValueFilterMode = Slick.ValueFilterMode, Utils = Slick.Utils, WidthEvalMode = Slick.WidthEvalMode, Draggable = Slick.Draggable, MouseWheel = Slick.MouseWheel, Resizable = Slick.Resizable; + var BindingEventService = Slick.BindingEventService, ColAutosizeMode = Slick.ColAutosizeMode, SlickEvent = Slick.Event, SlickEventData = Slick.EventData, GlobalEditorLock = Slick.GlobalEditorLock, GridAutosizeColsMode = Slick.GridAutosizeColsMode, keyCode = Slick.keyCode, preClickClassName = Slick.preClickClassName, SlickRange = Slick.Range, RowSelectionMode = Slick.RowSelectionMode, CellSelectionMode = Slick.CellSelectionMode, ValueFilterMode = Slick.ValueFilterMode, Utils = Slick.Utils, WidthEvalMode = Slick.WidthEvalMode, Draggable = Slick.Draggable, MouseWheel = Slick.MouseWheel, Resizable = Slick.Resizable, DragExtendHandle = Slick.DragExtendHandle; var SlickGrid = class { /** * Creates a new instance of the grid. @@ -245,6 +245,7 @@ __publicField(this, "initialized", !1); __publicField(this, "_container"); __publicField(this, "uid", `slickgrid_${Math.round(1e6 * Math.random())}`); + __publicField(this, "dragReplaceEl", new DragExtendHandle(this.uid)); __publicField(this, "_focusSink"); __publicField(this, "_focusSink2"); __publicField(this, "_groupHeaders", []); @@ -309,6 +310,8 @@ __publicField(this, "activePosY"); __publicField(this, "activeRow"); __publicField(this, "activeCell"); + __publicField(this, "selectionBottomRow"); + __publicField(this, "selectionRightCell"); __publicField(this, "activeCellNode", null); __publicField(this, "currentEditor", null); __publicField(this, "serializedEditorValue"); @@ -329,6 +332,7 @@ __publicField(this, "scrollLeft", 0); __publicField(this, "selectionModel"); __publicField(this, "selectedRows", []); + __publicField(this, "selectedRanges", []); __publicField(this, "plugins", []); __publicField(this, "cellCssClasses", {}); __publicField(this, "columnsById", {}); @@ -476,7 +480,8 @@ this._bindingEventService.bind(element, "keydown", this.handleKeyDown.bind(this)), this._bindingEventService.bind(element, "click", this.handleClick.bind(this)), this._bindingEventService.bind(element, "dblclick", this.handleDblClick.bind(this)), this._bindingEventService.bind(element, "contextmenu", this.handleContextMenu.bind(this)), this._bindingEventService.bind(element, "mouseover", this.handleCellMouseOver.bind(this)), this._bindingEventService.bind(element, "mouseout", this.handleCellMouseOut.bind(this)); }), Draggable && (this.slickDraggableInstance = Draggable({ containerElement: this._container, - allowDragFrom: "div.slick-cell", + allowDragFrom: "div.slick-cell, div." + this.dragReplaceEl.cssClass, + dragFromClassDetectArr: [{ tag: "dragReplaceHandle", id: this.dragReplaceEl.id }], // the slick cell parent must always contain `.dnd` and/or `.cell-reorder` class to be identified as draggable allowDragFromClosest: "div.slick-cell.dnd, div.slick-cell.cell-reorder", preventDragFromKeys: this._options.preventDragFromKeys, @@ -1897,18 +1902,51 @@ * @param {SlickRange_[]} ranges - The list of selected row and cell ranges. */ handleSelectedRangesChanged(e, ranges) { - var _a, _b; - let ne = e.getNativeEvent(), previousSelectedRows = this.selectedRows.slice(0); - this.selectedRows = []; + var _a, _b, _c, _d; + let ne = e.getNativeEvent(), selectionMode = (_b = (_a = ne == null ? void 0 : ne.detail) == null ? void 0 : _a.selectionMode) != null ? _b : "", prevSelectedRanges = this.selectedRanges.slice(0); + if (this.selectedRanges = ranges, selectionMode === CellSelectionMode.Replace && prevSelectedRanges && prevSelectedRanges.length === 1 && this.selectedRanges && this.selectedRanges.length === 1) { + let prevSelectedRange = prevSelectedRanges[0], prevSelectedRange_rowCount = prevSelectedRange.toRow - prevSelectedRange.fromRow + 1, prevSelectedRange_cellCount = prevSelectedRange.toCell - prevSelectedRange.fromCell + 1, selectedRange = this.selectedRanges[0], selectedRange_rowCount = selectedRange.toRow - selectedRange.fromRow + 1, selectedRange_cellCount = selectedRange.toCell - selectedRange.fromCell + 1; + if (selectedRange_rowCount >= prevSelectedRange_rowCount && selectedRange_cellCount >= prevSelectedRange_cellCount) { + let copyToRange = { + fromRow: selectedRange.fromRow < prevSelectedRange.fromRow ? selectedRange.fromRow : prevSelectedRange.toRow + 1, + rowCount: selectedRange_rowCount - prevSelectedRange_rowCount, + fromCell: selectedRange.fromCell, + cellCount: selectedRange_cellCount + // - prevSelectedRange_cellCount + }, fromRowOffset = 0, fromCellOffset = 0; + for (let i = 0; i < copyToRange.rowCount; i++) { + let toRow = this.getDataItem(copyToRange.fromRow + i), fromRow = this.getDataItem(prevSelectedRange.fromRow + fromRowOffset); + fromCellOffset = 0; + for (let j = 0; j < copyToRange.cellCount; j++) { + let toColDef = this.columns[copyToRange.fromCell + j], fromColDef = this.columns[prevSelectedRange.fromCell + fromCellOffset]; + if (!toColDef.hidden && !fromColDef.hidden) { + let val = fromRow[fromColDef.field]; + this._options.dataItemColumnValueExtractor && (val = this._options.dataItemColumnValueExtractor(fromRow, fromColDef)), toRow[toColDef.field] = val; + } + fromCellOffset++, fromCellOffset >= prevSelectedRange_cellCount && (fromCellOffset = 0); + } + fromRowOffset++, fromRowOffset >= prevSelectedRange_rowCount && (fromRowOffset = 0); + } + this.invalidate(); + } + } + let previousSelectedRows = this.selectedRows.slice(0); + this.selectionBottomRow = -1, this.selectionRightCell = -1, this.dragReplaceEl.removeEl(), this.selectedRows = []; let hash = {}; - for (let i = 0; i < ranges.length; i++) + for (let i = 0; i < ranges.length; i++) { for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { hash[j] || (this.selectedRows.push(j), hash[j] = {}); for (let k = ranges[i].fromCell; k <= ranges[i].toCell; k++) this.canCellBeSelected(j, k) && (hash[j][this.columns[k].id] = this._options.selectedCellCssClass); } - if (this.setCellCssStyles(this._options.selectedCellCssClass || "", hash), this.simpleArrayEquals(previousSelectedRows, this.selectedRows)) { - let caller = (_b = (_a = ne == null ? void 0 : ne.detail) == null ? void 0 : _a.caller) != null ? _b : "click", selectedRowsSet = new Set(this.getSelectedRows()), previousSelectedRowsSet = new Set(previousSelectedRows), newSelectedAdditions = Array.from(selectedRowsSet).filter((i) => !previousSelectedRowsSet.has(i)), newSelectedDeletions = Array.from(previousSelectedRowsSet).filter((i) => !selectedRowsSet.has(i)); + this.selectionBottomRow < ranges[i].toRow && (this.selectionBottomRow = ranges[i].toRow), this.selectionRightCell < ranges[i].toCell && (this.selectionRightCell = ranges[i].toCell); + } + if (this.setCellCssStyles(this._options.selectedCellCssClass || "", hash), this.selectionBottomRow >= 0 && this.selectionRightCell >= 0) { + let lowerRightCell = this.getCellNode(this.selectionBottomRow, this.selectionRightCell); + this.dragReplaceEl.createEl(lowerRightCell); + } + if (this.simpleArraysNotEqual(previousSelectedRows, this.selectedRows)) { + let caller = (_d = (_c = ne == null ? void 0 : ne.detail) == null ? void 0 : _c.caller) != null ? _d : "click", selectedRowsSet = new Set(this.getSelectedRows()), previousSelectedRowsSet = new Set(previousSelectedRows), newSelectedAdditions = Array.from(selectedRowsSet).filter((i) => !previousSelectedRowsSet.has(i)), newSelectedDeletions = Array.from(previousSelectedRowsSet).filter((i) => !selectedRowsSet.has(i)); this.trigger(this.onSelectedRowsChanged, { rows: this.getSelectedRows(), previousSelectedRows, @@ -1918,6 +1956,10 @@ }, e); } } + // compare 2 simple arrays (integers or strings only, do not use to compare object arrays) + simpleArraysNotEqual(arr1, arr2) { + return Array.isArray(arr1) && Array.isArray(arr2) && arr2.sort().toString() !== arr1.sort().toString(); + } /** * Processes a mouse wheel event by adjusting the vertical scroll (scrollTop) based on deltaY (scaled by rowHeight) * and horizontal scroll (scrollLeft) based on deltaX. It then calls the internal scroll handler with the “mousewheel” @@ -1969,7 +2011,7 @@ } // Called when a drag operation completes; it triggers the onDragEnd event with the current drag position and event. handleDragEnd(e, dd) { - this.trigger(this.onDragEnd, dd, e); + console.log("SlickGrid.handleDragEnd " + dd.matchClassTag), dd.matchClassTag === "dragReplaceHandle" && this.dragReplaceEl.removeEl(), this.trigger(this.onDragEnd, dd, e); } /** * Handles keydown events for grid navigation and editing. @@ -2296,7 +2338,7 @@ this.canvasWidth = this.getCanvasWidth(), this._options.createTopHeaderPanel && Utils.width(this._topHeaderPanel, (_a = this._options.topHeaderPanelWidth) != null ? _a : this.canvasWidth); let widthChanged = this.canvasWidth !== oldCanvasWidth || this.canvasWidthL !== oldCanvasWidthL || this.canvasWidthR !== oldCanvasWidthR; if (widthChanged || this.hasFrozenColumns() || this.hasFrozenRows) - if (Utils.width(this._canvasTopL, this.canvasWidthL - 0.1), this.getHeadersWidth(), Utils.width(this._headerL, this.headersWidthL), Utils.width(this._headerR, this.headersWidthR), this.hasFrozenColumns()) { + if (Utils.width(this._canvasTopL, this.canvasWidthL), this.getHeadersWidth(), Utils.width(this._headerL, this.headersWidthL), Utils.width(this._headerR, this.headersWidthR), this.hasFrozenColumns()) { let cWidth = Utils.width(this._container) || 0; if (cWidth > 0 && this.canvasWidthL > cWidth && this._options.throwWhenFrozenNotAllViewable) throw new Error("[SlickGrid] Frozen columns cannot be wider than the actual grid container width. Make sure to have less columns freezed or make your grid container wider"); @@ -2617,7 +2659,7 @@ m.cellAttrs.hasOwnProperty(key) && cellDiv.setAttribute(key, m.cellAttrs[key]); }), item) { let cellResult = Object.prototype.toString.call(formatterResult) !== "[object Object]" ? formatterResult : formatterResult.html || formatterResult.text; - this.applyHtmlCode(cellDiv, cellResult); + this.applyHtmlCode(cellDiv, cellResult), row === this.selectionBottomRow && cell === this.selectionRightCell && this._options.showCellSelection && this.dragReplaceEl.createEl(cellDiv); } divRow.appendChild(cellDiv), formatterResult.insertElementAfterTarget && Utils.insertAfterElement(cellDiv, formatterResult.insertElementAfterTarget), this.rowsCache[row].cellRenderQueue.push(cell), this.rowsCache[row].cellColSpans[cell] = colspan; } diff --git a/dist/browser/slick.grid.js.map b/dist/browser/slick.grid.js.map index e94f66d2..725c4d60 100644 --- a/dist/browser/slick.grid.js.map +++ b/dist/browser/slick.grid.js.map @@ -1,7 +1,7 @@ { "version": 3, "sources": ["../../src/slick.grid.ts"], - "sourcesContent": ["// @ts-ignore\r\nimport type { SortableEvent, SortableInstance, SortableOptions } from 'sortablejs';\r\n\r\nimport type {\r\n AutoSize,\r\n CellPosition,\r\n CellViewportRange,\r\n Column,\r\n ColumnMetadata,\r\n ColumnSort,\r\n CssStyleHash,\r\n CSSStyleDeclarationWritable,\r\n CustomDataView,\r\n DOMEvent,\r\n DragPosition,\r\n DragRowMove,\r\n Editor,\r\n EditorArguments,\r\n EditorConstructor,\r\n EditController,\r\n Formatter,\r\n FormatterOverrideCallback,\r\n FormatterResultObject,\r\n FormatterResultWithHtml,\r\n FormatterResultWithText,\r\n GridOption as BaseGridOption,\r\n InteractionBase,\r\n ItemMetadata,\r\n MenuCommandItemCallbackArgs,\r\n MultiColumnSort,\r\n OnActivateChangedOptionsEventArgs,\r\n OnActiveCellChangedEventArgs,\r\n OnAddNewRowEventArgs,\r\n OnAfterSetColumnsEventArgs,\r\n OnAutosizeColumnsEventArgs,\r\n OnBeforeUpdateColumnsEventArgs,\r\n OnBeforeAppendCellEventArgs,\r\n OnBeforeCellEditorDestroyEventArgs,\r\n OnBeforeColumnsResizeEventArgs,\r\n OnBeforeEditCellEventArgs,\r\n OnBeforeHeaderCellDestroyEventArgs,\r\n OnBeforeHeaderRowCellDestroyEventArgs,\r\n OnBeforeFooterRowCellDestroyEventArgs,\r\n OnBeforeSetColumnsEventArgs,\r\n OnCellChangeEventArgs,\r\n OnCellCssStylesChangedEventArgs,\r\n OnClickEventArgs,\r\n OnColumnsDragEventArgs,\r\n OnColumnsReorderedEventArgs,\r\n OnColumnsResizedEventArgs,\r\n OnColumnsResizeDblClickEventArgs,\r\n OnCompositeEditorChangeEventArgs,\r\n OnDblClickEventArgs,\r\n OnFooterContextMenuEventArgs,\r\n OnFooterRowCellRenderedEventArgs,\r\n OnHeaderCellRenderedEventArgs,\r\n OnFooterClickEventArgs,\r\n OnHeaderClickEventArgs,\r\n OnHeaderContextMenuEventArgs,\r\n OnHeaderMouseEventArgs,\r\n OnHeaderRowCellRenderedEventArgs,\r\n OnKeyDownEventArgs,\r\n OnPreHeaderContextMenuEventArgs,\r\n OnPreHeaderClickEventArgs,\r\n OnRenderedEventArgs,\r\n OnSelectedRowsChangedEventArgs,\r\n OnSetOptionsEventArgs,\r\n OnScrollEventArgs,\r\n OnValidationErrorEventArgs,\r\n PagingInfo,\r\n RowInfo,\r\n SelectionModel,\r\n SingleColumnSort,\r\n SlickGridModel,\r\n SlickPlugin,\r\n} from './models/index.js';\r\nimport {\r\n type BasePubSub,\r\n BindingEventService as BindingEventService_,\r\n ColAutosizeMode as ColAutosizeMode_,\r\n GlobalEditorLock as GlobalEditorLock_,\r\n GridAutosizeColsMode as GridAutosizeColsMode_,\r\n keyCode as keyCode_,\r\n preClickClassName as preClickClassName_,\r\n RowSelectionMode as RowSelectionMode_,\r\n type SlickEditorLock,\r\n SlickEvent as SlickEvent_,\r\n SlickEventData as SlickEventData_,\r\n SlickRange as SlickRange_,\r\n Utils as Utils_,\r\n ValueFilterMode as ValueFilterMode_,\r\n WidthEvalMode as WidthEvalMode_,\r\n} from './slick.core.js';\r\nimport { Draggable as Draggable_, MouseWheel as MouseWheel_, Resizable as Resizable_ } from './slick.interactions.js';\r\n\r\n// for (iife) load Slick methods from global Slick object, or use imports for (esm)\r\nconst BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_;\r\nconst ColAutosizeMode = IIFE_ONLY ? Slick.ColAutosizeMode : ColAutosizeMode_;\r\nconst SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_;\r\nconst SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_;\r\nconst GlobalEditorLock = IIFE_ONLY ? Slick.GlobalEditorLock : GlobalEditorLock_;\r\nconst GridAutosizeColsMode = IIFE_ONLY ? Slick.GridAutosizeColsMode : GridAutosizeColsMode_;\r\nconst keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_;\r\nconst preClickClassName = IIFE_ONLY ? Slick.preClickClassName : preClickClassName_;\r\nconst SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_;\r\nconst RowSelectionMode = IIFE_ONLY ? Slick.RowSelectionMode : RowSelectionMode_;\r\nconst ValueFilterMode = IIFE_ONLY ? Slick.ValueFilterMode : ValueFilterMode_;\r\nconst Utils = IIFE_ONLY ? Slick.Utils : Utils_;\r\nconst WidthEvalMode = IIFE_ONLY ? Slick.WidthEvalMode : WidthEvalMode_;\r\nconst Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_;\r\nconst MouseWheel = IIFE_ONLY ? Slick.MouseWheel : MouseWheel_;\r\nconst Resizable = IIFE_ONLY ? Slick.Resizable : Resizable_;\r\n\r\n/**\r\n * @license\r\n * (c) 2009-present Michael Leibman\r\n * michael{dot}leibman{at}gmail{dot}com\r\n * http://github.com/mleibman/slickgrid\r\n *\r\n * Distributed under MIT license.\r\n * All rights reserved.\r\n *\r\n * SlickGrid v5.15.3\r\n *\r\n * NOTES:\r\n * Cell/row DOM manipulations are done directly bypassing JS DOM manipulation methods.\r\n * This increases the speed dramatically, but can only be done safely because there are no event handlers\r\n * or data associated with any cell/row DOM nodes. Cell editors must make sure they implement .destroy()\r\n * and do proper cleanup.\r\n */\r\n\r\n//////////////////////////////////////////////////////////////////////////////////////////////\r\n// SlickGrid class implementation (available as SlickGrid)\r\n\r\ninterface RowCaching {\r\n rowNode: HTMLElement[] | null,\r\n cellColSpans: Array;\r\n cellNodesByColumnIdx: HTMLElement[];\r\n cellRenderQueue: any[];\r\n}\r\n\r\nexport class SlickGrid = Column, O extends BaseGridOption = BaseGridOption> {\r\n //////////////////////////////////////////////////////////////////////////////////////////////\r\n // Public API\r\n slickGridVersion = '5.15.3';\r\n\r\n /** optional grid state clientId */\r\n cid = '';\r\n\r\n // Events\r\n onActiveCellChanged: SlickEvent_;\r\n onActiveCellPositionChanged: SlickEvent_<{ grid: SlickGrid; }>;\r\n onAddNewRow: SlickEvent_;\r\n onAfterSetColumns: SlickEvent_;\r\n onAutosizeColumns: SlickEvent_;\r\n onBeforeAppendCell: SlickEvent_;\r\n onBeforeCellEditorDestroy: SlickEvent_;\r\n onBeforeColumnsResize: SlickEvent_;\r\n onBeforeDestroy: SlickEvent_<{ grid: SlickGrid; }>;\r\n onBeforeEditCell: SlickEvent_;\r\n onBeforeFooterRowCellDestroy: SlickEvent_;\r\n onBeforeHeaderCellDestroy: SlickEvent_;\r\n onBeforeHeaderRowCellDestroy: SlickEvent_;\r\n onBeforeRemoveCachedRow: SlickEvent_<{ row: number; grid: SlickGrid }>;\r\n onBeforeSetColumns: SlickEvent_;\r\n onBeforeSort: SlickEvent_;\r\n onBeforeUpdateColumns: SlickEvent_;\r\n onCellChange: SlickEvent_;\r\n onCellCssStylesChanged: SlickEvent_;\r\n onClick: SlickEvent_;\r\n onColumnsReordered: SlickEvent_;\r\n onColumnsDrag: SlickEvent_;\r\n onColumnsResized: SlickEvent_;\r\n onColumnsResizeDblClick: SlickEvent_;\r\n onCompositeEditorChange: SlickEvent_;\r\n onContextMenu: SlickEvent_;\r\n onDrag: SlickEvent_;\r\n onDblClick: SlickEvent_;\r\n onDragInit: SlickEvent_;\r\n onDragStart: SlickEvent_;\r\n onDragEnd: SlickEvent_;\r\n onFooterClick: SlickEvent_;\r\n onFooterContextMenu: SlickEvent_;\r\n onFooterRowCellRendered: SlickEvent_;\r\n onHeaderCellRendered: SlickEvent_;\r\n onHeaderClick: SlickEvent_;\r\n onHeaderContextMenu: SlickEvent_;\r\n onHeaderMouseEnter: SlickEvent_;\r\n onHeaderMouseLeave: SlickEvent_;\r\n onHeaderRowCellRendered: SlickEvent_;\r\n onHeaderRowMouseEnter: SlickEvent_;\r\n onHeaderRowMouseLeave: SlickEvent_;\r\n onPreHeaderContextMenu: SlickEvent_;\r\n onPreHeaderClick: SlickEvent_;\r\n onKeyDown: SlickEvent_;\r\n onMouseEnter: SlickEvent_;\r\n onMouseLeave: SlickEvent_;\r\n onRendered: SlickEvent_;\r\n onScroll: SlickEvent_;\r\n onSelectedRowsChanged: SlickEvent_;\r\n onSetOptions: SlickEvent_;\r\n onActivateChangedOptions: SlickEvent_;\r\n onSort: SlickEvent_;\r\n onValidationError: SlickEvent_;\r\n onViewportChanged: SlickEvent_<{ grid: SlickGrid; }>;\r\n\r\n // ---\r\n // protected variables\r\n\r\n // shared across all grids on the page\r\n protected scrollbarDimensions?: { height: number; width: number; };\r\n protected maxSupportedCssHeight!: number; // browser's breaking point\r\n\r\n protected canvas: HTMLCanvasElement | null = null;\r\n protected canvas_context: CanvasRenderingContext2D | null = null;\r\n\r\n // settings\r\n protected _options!: O;\r\n protected _defaults: BaseGridOption = {\r\n alwaysShowVerticalScroll: false,\r\n alwaysAllowHorizontalScroll: false,\r\n explicitInitialization: false,\r\n rowHeight: 25,\r\n defaultColumnWidth: 80,\r\n enableHtmlRendering: true,\r\n enableAddRow: false,\r\n leaveSpaceForNewRows: false,\r\n editable: false,\r\n autoEdit: true,\r\n autoEditNewRow: true,\r\n autoCommitEdit: false,\r\n suppressActiveCellChangeOnEdit: false,\r\n enableCellNavigation: true,\r\n enableColumnReorder: true,\r\n unorderableColumnCssClass: 'unorderable',\r\n asyncEditorLoading: false,\r\n asyncEditorLoadDelay: 100,\r\n forceFitColumns: false,\r\n enableAsyncPostRender: false,\r\n asyncPostRenderDelay: 50,\r\n enableAsyncPostRenderCleanup: false,\r\n asyncPostRenderCleanupDelay: 40,\r\n auto: false,\r\n nonce: '',\r\n editorLock: GlobalEditorLock,\r\n showColumnHeader: true,\r\n showHeaderRow: false,\r\n headerRowHeight: 25,\r\n createFooterRow: false,\r\n showFooterRow: false,\r\n footerRowHeight: 25,\r\n createPreHeaderPanel: false,\r\n createTopHeaderPanel: false,\r\n showPreHeaderPanel: false,\r\n showTopHeaderPanel: false,\r\n preHeaderPanelHeight: 25,\r\n showTopPanel: false,\r\n topPanelHeight: 25,\r\n preHeaderPanelWidth: 'auto', // mostly useful for Draggable Grouping dropzone to take full width\r\n topHeaderPanelHeight: 25,\r\n topHeaderPanelWidth: 'auto', // mostly useful for Draggable Grouping dropzone to take full width\r\n formatterFactory: null,\r\n editorFactory: null,\r\n cellFlashingCssClass: 'flashing',\r\n rowHighlightCssClass: 'highlight-animate',\r\n rowHighlightDuration: 400,\r\n selectedCellCssClass: 'selected',\r\n multiSelect: true,\r\n enableCellRowSpan: false,\r\n enableTextSelectionOnCells: false,\r\n dataItemColumnValueExtractor: null,\r\n frozenBottom: false,\r\n frozenColumn: -1,\r\n frozenRow: -1,\r\n frozenRightViewportMinWidth: 100,\r\n throwWhenFrozenNotAllViewable: false,\r\n fullWidthRows: false,\r\n multiColumnSort: false,\r\n numberedMultiColumnSort: false,\r\n tristateMultiColumnSort: false,\r\n sortColNumberInSeparateSpan: false,\r\n defaultFormatter: this.defaultFormatter,\r\n forceSyncScrolling: false,\r\n addNewRowCssClass: 'new-row',\r\n preserveCopiedSelectionOnPaste: false,\r\n preventDragFromKeys: ['ctrlKey', 'metaKey'],\r\n showCellSelection: true,\r\n viewportClass: undefined,\r\n minRowBuffer: 3,\r\n emulatePagingWhenScrolling: true, // when scrolling off bottom of viewport, place new row at top of viewport\r\n editorCellNavOnLRKeys: false,\r\n enableMouseWheelScrollHandler: true,\r\n doPaging: true,\r\n autosizeColsMode: GridAutosizeColsMode.LegacyOff,\r\n autosizeColPaddingPx: 4,\r\n rowTopOffsetRenderType: 'top',\r\n scrollRenderThrottling: 10,\r\n autosizeTextAvgToMWidthRatio: 0.75,\r\n viewportSwitchToScrollModeWidthPercent: undefined,\r\n viewportMinWidthPx: undefined,\r\n viewportMaxWidthPx: undefined,\r\n suppressCssChangesOnHiddenInit: false,\r\n ffMaxSupportedCssHeight: 6000000,\r\n maxSupportedCssHeight: 1000000000,\r\n maxPartialRowSpanRemap: 5000,\r\n sanitizer: undefined, // sanitize function, built in basic sanitizer is: Slick.RegexSanitizer(dirtyHtml)\r\n logSanitizedHtml: false, // log to console when sanitised - recommend true for testing of dev and production\r\n mixinDefaults: true,\r\n shadowRoot: undefined\r\n };\r\n\r\n protected _columnDefaults = {\r\n name: '',\r\n headerCssClass: null,\r\n defaultSortAsc: true,\r\n focusable: true,\r\n hidden: false,\r\n minWidth: 30,\r\n maxWidth: undefined,\r\n rerenderOnResize: false,\r\n reorderable: true,\r\n resizable: true,\r\n sortable: false,\r\n selectable: true,\r\n } as Partial;\r\n\r\n protected _columnAutosizeDefaults: AutoSize = {\r\n ignoreHeaderText: false,\r\n colValueArray: undefined,\r\n allowAddlPercent: undefined,\r\n formatterOverride: undefined,\r\n autosizeMode: ColAutosizeMode.ContentIntelligent,\r\n rowSelectionModeOnInit: undefined,\r\n rowSelectionMode: RowSelectionMode.FirstNRows,\r\n rowSelectionCount: 100,\r\n valueFilterMode: ValueFilterMode.None,\r\n widthEvalMode: WidthEvalMode.Auto,\r\n sizeToRemaining: undefined,\r\n widthPx: undefined,\r\n contentSizePx: 0,\r\n headerWidthPx: 0,\r\n colDataTypeOf: undefined\r\n };\r\n\r\n protected _columnResizeTimer?: number;\r\n protected _executionBlockTimer?: number;\r\n protected _flashCellTimer?: number;\r\n protected _highlightRowTimer?: number;\r\n\r\n // scroller\r\n protected th!: number; // virtual height\r\n protected h!: number; // real scrollable height\r\n protected ph!: number; // page height\r\n protected n!: number; // number of pages\r\n protected cj!: number; // \"jumpiness\" coefficient\r\n\r\n protected page = 0; // current page\r\n protected offset = 0; // current page offset\r\n protected vScrollDir = 1;\r\n protected _bindingEventService = new BindingEventService();\r\n protected initialized = false;\r\n protected _container!: HTMLElement;\r\n protected uid = `slickgrid_${Math.round(1000000 * Math.random())}`;\r\n protected _focusSink!: HTMLDivElement;\r\n protected _focusSink2!: HTMLDivElement;\r\n protected _groupHeaders: HTMLDivElement[] = [];\r\n protected _headerScroller: HTMLDivElement[] = [];\r\n protected _headers: HTMLDivElement[] = [];\r\n protected _headerRows!: HTMLDivElement[];\r\n protected _headerRowScroller!: HTMLDivElement[];\r\n protected _headerRowSpacerL!: HTMLDivElement;\r\n protected _headerRowSpacerR!: HTMLDivElement;\r\n protected _footerRow!: HTMLDivElement[];\r\n protected _footerRowScroller!: HTMLDivElement[];\r\n protected _footerRowSpacerL!: HTMLDivElement;\r\n protected _footerRowSpacerR!: HTMLDivElement;\r\n protected _preHeaderPanel!: HTMLDivElement;\r\n protected _preHeaderPanelScroller!: HTMLDivElement;\r\n protected _preHeaderPanelSpacer!: HTMLDivElement;\r\n protected _preHeaderPanelR!: HTMLDivElement;\r\n protected _preHeaderPanelScrollerR!: HTMLDivElement;\r\n protected _preHeaderPanelSpacerR!: HTMLDivElement;\r\n protected _topHeaderPanel!: HTMLDivElement;\r\n protected _topHeaderPanelScroller!: HTMLDivElement;\r\n protected _topHeaderPanelSpacer!: HTMLDivElement;\r\n protected _topPanelScrollers!: HTMLDivElement[];\r\n protected _topPanels!: HTMLDivElement[];\r\n protected _viewport!: HTMLDivElement[];\r\n protected _canvas!: HTMLDivElement[];\r\n protected _style?: HTMLStyleElement;\r\n protected _boundAncestors: HTMLElement[] = [];\r\n protected stylesheet?: { cssRules: Array<{ selectorText: string; }>; rules: Array<{ selectorText: string; }>; } | null;\r\n protected columnCssRulesL?: Array<{ selectorText: string; }>;\r\n protected columnCssRulesR?: Array<{ selectorText: string; }>;\r\n protected viewportH = 0;\r\n protected viewportW = 0;\r\n protected canvasWidth = 0;\r\n protected canvasWidthL = 0;\r\n protected canvasWidthR = 0;\r\n protected headersWidth = 0;\r\n protected headersWidthL = 0;\r\n protected headersWidthR = 0;\r\n protected viewportHasHScroll = false;\r\n protected viewportHasVScroll = false;\r\n protected headerColumnWidthDiff = 0;\r\n protected headerColumnHeightDiff = 0; // border+padding\r\n protected cellWidthDiff = 0;\r\n protected cellHeightDiff = 0;\r\n protected absoluteColumnMinWidth!: number;\r\n protected hasFrozenRows = false;\r\n protected frozenRowsHeight = 0;\r\n protected actualFrozenRow = -1;\r\n protected paneTopH = 0;\r\n protected paneBottomH = 0;\r\n protected viewportTopH = 0;\r\n protected viewportBottomH = 0;\r\n protected topPanelH = 0;\r\n protected headerRowH = 0;\r\n protected footerRowH = 0;\r\n\r\n protected tabbingDirection = 1;\r\n protected _activeCanvasNode!: HTMLDivElement;\r\n protected _activeViewportNode!: HTMLDivElement;\r\n protected activePosX!: number;\r\n protected activePosY!: number;\r\n protected activeRow!: number;\r\n protected activeCell!: number;\r\n protected activeCellNode: HTMLDivElement | null = null;\r\n protected currentEditor: Editor | null = null;\r\n protected serializedEditorValue: any;\r\n protected editController?: EditController;\r\n protected _prevDataLength = 0;\r\n protected _prevInvalidatedRowsCount = 0;\r\n protected _rowSpanIsCached = false;\r\n protected _colsWithRowSpanCache: { [colIdx: number]: Set } = {};\r\n protected rowsCache: Record = {};\r\n protected renderedRows = 0;\r\n protected numVisibleRows = 0;\r\n protected prevScrollTop = 0;\r\n protected scrollHeight = 0;\r\n protected scrollTop = 0;\r\n protected lastRenderedScrollTop = 0;\r\n protected lastRenderedScrollLeft = 0;\r\n protected prevScrollLeft = 0;\r\n protected scrollLeft = 0;\r\n\r\n protected selectionModel?: SelectionModel;\r\n protected selectedRows: number[] = [];\r\n\r\n protected plugins: SlickPlugin[] = [];\r\n protected cellCssClasses: CssStyleHash = {};\r\n\r\n protected columnsById: Record = {};\r\n protected sortColumns: ColumnSort[] = [];\r\n protected columnPosLeft: number[] = [];\r\n protected columnPosRight: number[] = [];\r\n\r\n protected pagingActive = false;\r\n protected pagingIsLastPage = false;\r\n\r\n protected scrollThrottle!: { enqueue: () => void; dequeue: () => void; };\r\n\r\n // async call handles\r\n protected h_editorLoader?: number;\r\n protected h_postrender?: number;\r\n protected h_postrenderCleanup?: number;\r\n protected postProcessedRows: any = {};\r\n protected postProcessToRow: number = null as any;\r\n protected postProcessFromRow: number = null as any;\r\n protected postProcessedCleanupQueue: Array<{\r\n actionType: string;\r\n groupId: number;\r\n node: HTMLElement | HTMLElement[];\r\n columnIdx?: number;\r\n rowIdx?: number;\r\n }> = [];\r\n protected postProcessgroupId = 0;\r\n\r\n // perf counters\r\n protected counter_rows_rendered = 0;\r\n protected counter_rows_removed = 0;\r\n\r\n protected _paneHeaderL!: HTMLDivElement;\r\n protected _paneHeaderR!: HTMLDivElement;\r\n protected _paneTopL!: HTMLDivElement;\r\n protected _paneTopR!: HTMLDivElement;\r\n protected _paneBottomL!: HTMLDivElement;\r\n protected _paneBottomR!: HTMLDivElement;\r\n protected _headerScrollerL!: HTMLDivElement;\r\n protected _headerScrollerR!: HTMLDivElement;\r\n protected _headerL!: HTMLDivElement;\r\n protected _headerR!: HTMLDivElement;\r\n protected _groupHeadersL!: HTMLDivElement;\r\n protected _groupHeadersR!: HTMLDivElement;\r\n protected _headerRowScrollerL!: HTMLDivElement;\r\n protected _headerRowScrollerR!: HTMLDivElement;\r\n protected _footerRowScrollerL!: HTMLDivElement;\r\n protected _footerRowScrollerR!: HTMLDivElement;\r\n protected _headerRowL!: HTMLDivElement;\r\n protected _headerRowR!: HTMLDivElement;\r\n protected _footerRowL!: HTMLDivElement;\r\n protected _footerRowR!: HTMLDivElement;\r\n protected _topPanelScrollerL!: HTMLDivElement;\r\n protected _topPanelScrollerR!: HTMLDivElement;\r\n protected _topPanelL!: HTMLDivElement;\r\n protected _topPanelR!: HTMLDivElement;\r\n protected _viewportTopL!: HTMLDivElement;\r\n protected _viewportTopR!: HTMLDivElement;\r\n protected _viewportBottomL!: HTMLDivElement;\r\n protected _viewportBottomR!: HTMLDivElement;\r\n protected _canvasTopL!: HTMLDivElement;\r\n protected _canvasTopR!: HTMLDivElement;\r\n protected _canvasBottomL!: HTMLDivElement;\r\n protected _canvasBottomR!: HTMLDivElement;\r\n protected _viewportScrollContainerX!: HTMLDivElement;\r\n protected _viewportScrollContainerY!: HTMLDivElement;\r\n protected _headerScrollContainer!: HTMLDivElement;\r\n protected _headerRowScrollContainer!: HTMLDivElement;\r\n protected _footerRowScrollContainer!: HTMLDivElement;\r\n\r\n // store css attributes if display:none is active in container or parent\r\n protected cssShow = { position: 'absolute', visibility: 'hidden', display: 'block' };\r\n protected _hiddenParents: HTMLElement[] = [];\r\n protected oldProps: Array> = [];\r\n protected enforceFrozenRowHeightRecalc = false;\r\n protected columnResizeDragging = false;\r\n protected slickDraggableInstance: InteractionBase | null = null;\r\n protected slickMouseWheelInstances: Array = [];\r\n protected slickResizableInstances: Array = [];\r\n protected sortableSideLeftInstance?: SortableInstance;\r\n protected sortableSideRightInstance?: SortableInstance;\r\n protected logMessageCount = 0;\r\n protected logMessageMaxCount = 30;\r\n protected _pubSubService?: BasePubSub;\r\n\r\n /**\r\n * Creates a new instance of the grid.\r\n * @class SlickGrid\r\n * @constructor\r\n * @param {Node} container - Container node to create the grid in.\r\n * @param {Array|Object} data - An array of objects for databinding or an external DataView.\r\n * @param {Array} columns - An array of column definitions.\r\n * @param {Object} [options] - Grid Options\r\n * @param {Object} [externalPubSub] - optional External PubSub Service to use by SlickEvent\r\n **/\r\n constructor(protected readonly container: HTMLElement | string, protected data: CustomDataView | TData[], protected columns: C[], options: Partial, protected readonly externalPubSub?: BasePubSub) {\r\n this._container = typeof this.container === 'string'\r\n ? document.querySelector(this.container) as HTMLDivElement\r\n : this.container;\r\n\r\n if (!this._container) {\r\n throw new Error(`SlickGrid requires a valid container, ${this.container} does not exist in the DOM.`);\r\n }\r\n\r\n this._pubSubService = externalPubSub;\r\n this.onActiveCellChanged = new SlickEvent('onActiveCellChanged', externalPubSub);\r\n this.onActiveCellPositionChanged = new SlickEvent<{ grid: SlickGrid; }>('onActiveCellPositionChanged', externalPubSub);\r\n this.onAddNewRow = new SlickEvent('onAddNewRow', externalPubSub);\r\n this.onAfterSetColumns = new SlickEvent('onAfterSetColumns', externalPubSub);\r\n this.onAutosizeColumns = new SlickEvent('onAutosizeColumns', externalPubSub);\r\n this.onBeforeAppendCell = new SlickEvent('onBeforeAppendCell', externalPubSub);\r\n this.onBeforeCellEditorDestroy = new SlickEvent('onBeforeCellEditorDestroy', externalPubSub);\r\n this.onBeforeColumnsResize = new SlickEvent('onBeforeColumnsResize', externalPubSub);\r\n this.onBeforeDestroy = new SlickEvent<{ grid: SlickGrid; }>('onBeforeDestroy', externalPubSub);\r\n this.onBeforeEditCell = new SlickEvent('onBeforeEditCell', externalPubSub);\r\n this.onBeforeFooterRowCellDestroy = new SlickEvent('onBeforeFooterRowCellDestroy', externalPubSub);\r\n this.onBeforeHeaderCellDestroy = new SlickEvent('onBeforeHeaderCellDestroy', externalPubSub);\r\n this.onBeforeHeaderRowCellDestroy = new SlickEvent('onBeforeHeaderRowCellDestroy', externalPubSub);\r\n this.onBeforeRemoveCachedRow = new SlickEvent<{ row: number; grid: SlickGrid }>('onRowRemovedFromCache', externalPubSub);\r\n this.onBeforeSetColumns = new SlickEvent('onBeforeSetColumns', externalPubSub);\r\n this.onBeforeSort = new SlickEvent('onBeforeSort', externalPubSub);\r\n this.onBeforeUpdateColumns = new SlickEvent('onBeforeUpdateColumns', externalPubSub);\r\n this.onCellChange = new SlickEvent('onCellChange', externalPubSub);\r\n this.onCellCssStylesChanged = new SlickEvent('onCellCssStylesChanged', externalPubSub);\r\n this.onClick = new SlickEvent('onClick', externalPubSub);\r\n this.onColumnsReordered = new SlickEvent('onColumnsReordered', externalPubSub);\r\n this.onColumnsDrag = new SlickEvent('onColumnsDrag', externalPubSub);\r\n this.onColumnsResized = new SlickEvent('onColumnsResized', externalPubSub);\r\n this.onColumnsResizeDblClick = new SlickEvent('onColumnsResizeDblClick', externalPubSub);\r\n this.onCompositeEditorChange = new SlickEvent('onCompositeEditorChange', externalPubSub);\r\n this.onContextMenu = new SlickEvent('onContextMenu', externalPubSub);\r\n this.onDrag = new SlickEvent('onDrag', externalPubSub);\r\n this.onDblClick = new SlickEvent('onDblClick', externalPubSub);\r\n this.onDragInit = new SlickEvent('onDragInit', externalPubSub);\r\n this.onDragStart = new SlickEvent('onDragStart', externalPubSub);\r\n this.onDragEnd = new SlickEvent('onDragEnd', externalPubSub);\r\n this.onFooterClick = new SlickEvent('onFooterClick', externalPubSub);\r\n this.onFooterContextMenu = new SlickEvent('onFooterContextMenu', externalPubSub);\r\n this.onFooterRowCellRendered = new SlickEvent('onFooterRowCellRendered', externalPubSub);\r\n this.onHeaderCellRendered = new SlickEvent('onHeaderCellRendered', externalPubSub);\r\n this.onHeaderClick = new SlickEvent('onHeaderClick', externalPubSub);\r\n this.onHeaderContextMenu = new SlickEvent('onHeaderContextMenu', externalPubSub);\r\n this.onHeaderMouseEnter = new SlickEvent('onHeaderMouseEnter', externalPubSub);\r\n this.onHeaderMouseLeave = new SlickEvent('onHeaderMouseLeave', externalPubSub);\r\n this.onHeaderRowCellRendered = new SlickEvent('onHeaderRowCellRendered', externalPubSub);\r\n this.onHeaderRowMouseEnter = new SlickEvent('onHeaderRowMouseEnter', externalPubSub);\r\n this.onHeaderRowMouseLeave = new SlickEvent('onHeaderRowMouseLeave', externalPubSub);\r\n this.onPreHeaderClick = new SlickEvent('onPreHeaderClick', externalPubSub);\r\n this.onPreHeaderContextMenu = new SlickEvent('onPreHeaderContextMenu', externalPubSub);\r\n this.onKeyDown = new SlickEvent('onKeyDown', externalPubSub);\r\n this.onMouseEnter = new SlickEvent('onMouseEnter', externalPubSub);\r\n this.onMouseLeave = new SlickEvent('onMouseLeave', externalPubSub);\r\n this.onRendered = new SlickEvent('onRendered', externalPubSub);\r\n this.onScroll = new SlickEvent('onScroll', externalPubSub);\r\n this.onSelectedRowsChanged = new SlickEvent('onSelectedRowsChanged', externalPubSub);\r\n this.onSetOptions = new SlickEvent('onSetOptions', externalPubSub);\r\n this.onActivateChangedOptions = new SlickEvent('onActivateChangedOptions', externalPubSub);\r\n this.onSort = new SlickEvent('onSort', externalPubSub);\r\n this.onValidationError = new SlickEvent('onValidationError', externalPubSub);\r\n this.onViewportChanged = new SlickEvent<{ grid: SlickGrid; }>('onViewportChanged', externalPubSub);\r\n\r\n this.initialize(options);\r\n }\r\n\r\n //////////////////////////////////////////////////////////////////////////////////////////////\r\n // Grid and Dom Initialisation\r\n //////////////////////////////////////////////////////////////////////////////////////////////\r\n\r\n /** Initializes the grid. */\r\n init() {\r\n this.finishInitialization();\r\n }\r\n\r\n /**\r\n * Processes the provided grid options (mixing in default settings as needed),\r\n * validates required modules (for example, ensuring Sortable.js is loaded if column reordering is enabled),\r\n * and creates all necessary DOM elements for the grid (including header containers, viewports, canvases, panels, etc.).\r\n * It also caches CSS if the container or its ancestors are hidden and calls finish.\r\n *\r\n * @param {Partial} options - Partial grid options to be applied during initialization.\r\n */\r\n protected initialize(options: Partial) {\r\n // calculate these only once and share between grid instances\r\n if (options?.mixinDefaults) {\r\n // use provided options and then assign defaults\r\n if (!this._options) { this._options = options as O; }\r\n Utils.applyDefaults(this._options, this._defaults);\r\n } else {\r\n this._options = Utils.extend(true, {}, this._defaults, options);\r\n }\r\n this.scrollThrottle = this.actionThrottle(this.render.bind(this), this._options.scrollRenderThrottling as number);\r\n this.maxSupportedCssHeight = this.maxSupportedCssHeight || this.getMaxSupportedCssHeight();\r\n this.validateAndEnforceOptions();\r\n this._columnDefaults.width = this._options.defaultColumnWidth;\r\n\r\n if (!this._options.suppressCssChangesOnHiddenInit) {\r\n this.cacheCssForHiddenInit();\r\n }\r\n\r\n this.updateColumnProps();\r\n\r\n // validate loaded JavaScript modules against requested options\r\n if (this._options.enableColumnReorder && (!Sortable || !Sortable.create)) {\r\n throw new Error('SlickGrid requires Sortable.js module to be loaded');\r\n }\r\n\r\n this.editController = {\r\n commitCurrentEdit: this.commitCurrentEdit.bind(this),\r\n cancelCurrentEdit: this.cancelCurrentEdit.bind(this),\r\n };\r\n\r\n Utils.emptyElement(this._container);\r\n this._container.style.outline = String(0);\r\n this._container.classList.add(this.uid);\r\n this._container.classList.add('ui-widget');\r\n this._container.setAttribute('role', 'grid');\r\n\r\n const containerStyles = window.getComputedStyle(this._container);\r\n if (!(/relative|absolute|fixed/).test(containerStyles.position)) {\r\n this._container.style.position = 'relative';\r\n }\r\n\r\n this._focusSink = Utils.createDomElement('div', { tabIndex: 0, style: { position: 'fixed', width: '0px', height: '0px', top: '0px', left: '0px', outline: '0px' } }, this._container);\r\n\r\n if (this._options.createTopHeaderPanel) {\r\n this._topHeaderPanelScroller = Utils.createDomElement('div', { className: 'slick-topheader-panel slick-state-default', style: { overflow: 'hidden', position: 'relative' } }, this._container);\r\n this._topHeaderPanelScroller.appendChild(document.createElement('div'));\r\n this._topHeaderPanel = Utils.createDomElement('div', null, this._topHeaderPanelScroller);\r\n this._topHeaderPanelSpacer = Utils.createDomElement('div', { style: { display: 'block', height: '1px', position: 'absolute', top: '0px', left: '0px' } }, this._topHeaderPanelScroller);\r\n\r\n if (!this._options.showTopHeaderPanel) {\r\n Utils.hide(this._topHeaderPanelScroller);\r\n }\r\n }\r\n\r\n // Containers used for scrolling frozen columns and rows\r\n this._paneHeaderL = Utils.createDomElement('div', { className: 'slick-pane slick-pane-header slick-pane-left', tabIndex: 0 }, this._container);\r\n this._paneHeaderR = Utils.createDomElement('div', { className: 'slick-pane slick-pane-header slick-pane-right', tabIndex: 0 }, this._container);\r\n this._paneTopL = Utils.createDomElement('div', { className: 'slick-pane slick-pane-top slick-pane-left', tabIndex: 0 }, this._container);\r\n this._paneTopR = Utils.createDomElement('div', { className: 'slick-pane slick-pane-top slick-pane-right', tabIndex: 0 }, this._container);\r\n this._paneBottomL = Utils.createDomElement('div', { className: 'slick-pane slick-pane-bottom slick-pane-left', tabIndex: 0 }, this._container);\r\n this._paneBottomR = Utils.createDomElement('div', { className: 'slick-pane slick-pane-bottom slick-pane-right', tabIndex: 0 }, this._container);\r\n\r\n if (this._options.createPreHeaderPanel) {\r\n this._preHeaderPanelScroller = Utils.createDomElement('div', { className: 'slick-preheader-panel ui-state-default slick-state-default', style: { overflow: 'hidden', position: 'relative' } }, this._paneHeaderL);\r\n this._preHeaderPanelScroller.appendChild(document.createElement('div'));\r\n this._preHeaderPanel = Utils.createDomElement('div', null, this._preHeaderPanelScroller);\r\n this._preHeaderPanelSpacer = Utils.createDomElement('div', { style: { display: 'block', height: '1px', position: 'absolute', top: '0px', left: '0px' } }, this._preHeaderPanelScroller);\r\n\r\n this._preHeaderPanelScrollerR = Utils.createDomElement('div', { className: 'slick-preheader-panel ui-state-default slick-state-default', style: { overflow: 'hidden', position: 'relative' } }, this._paneHeaderR);\r\n this._preHeaderPanelR = Utils.createDomElement('div', null, this._preHeaderPanelScrollerR);\r\n this._preHeaderPanelSpacerR = Utils.createDomElement('div', { style: { display: 'block', height: '1px', position: 'absolute', top: '0px', left: '0px' } }, this._preHeaderPanelScrollerR);\r\n\r\n if (!this._options.showPreHeaderPanel) {\r\n Utils.hide(this._preHeaderPanelScroller);\r\n Utils.hide(this._preHeaderPanelScrollerR);\r\n }\r\n }\r\n\r\n // Append the header scroller containers\r\n this._headerScrollerL = Utils.createDomElement('div', { className: 'slick-header ui-state-default slick-state-default slick-header-left' }, this._paneHeaderL);\r\n this._headerScrollerR = Utils.createDomElement('div', { className: 'slick-header ui-state-default slick-state-default slick-header-right' }, this._paneHeaderR);\r\n\r\n // Cache the header scroller containers\r\n this._headerScroller.push(this._headerScrollerL);\r\n this._headerScroller.push(this._headerScrollerR);\r\n\r\n // Append the columnn containers to the headers\r\n this._headerL = Utils.createDomElement('div', { className: 'slick-header-columns slick-header-columns-left', role: 'row', style: { left: '-1000px' } }, this._headerScrollerL);\r\n this._headerR = Utils.createDomElement('div', { className: 'slick-header-columns slick-header-columns-right', role: 'row', style: { left: '-1000px' } }, this._headerScrollerR);\r\n\r\n // Cache the header columns\r\n this._headers = [this._headerL, this._headerR];\r\n\r\n this._headerRowScrollerL = Utils.createDomElement('div', { className: 'slick-headerrow ui-state-default slick-state-default' }, this._paneTopL);\r\n this._headerRowScrollerR = Utils.createDomElement('div', { className: 'slick-headerrow ui-state-default slick-state-default' }, this._paneTopR);\r\n\r\n this._headerRowScroller = [this._headerRowScrollerL, this._headerRowScrollerR];\r\n\r\n this._headerRowSpacerL = Utils.createDomElement('div', { style: { display: 'block', height: '1px', position: 'absolute', top: '0px', left: '0px' } }, this._headerRowScrollerL);\r\n this._headerRowSpacerR = Utils.createDomElement('div', { style: { display: 'block', height: '1px', position: 'absolute', top: '0px', left: '0px' } }, this._headerRowScrollerR);\r\n\r\n this._headerRowL = Utils.createDomElement('div', { className: 'slick-headerrow-columns slick-headerrow-columns-left' }, this._headerRowScrollerL);\r\n this._headerRowR = Utils.createDomElement('div', { className: 'slick-headerrow-columns slick-headerrow-columns-right' }, this._headerRowScrollerR);\r\n\r\n this._headerRows = [this._headerRowL, this._headerRowR];\r\n\r\n // Append the top panel scroller\r\n this._topPanelScrollerL = Utils.createDomElement('div', { className: 'slick-top-panel-scroller ui-state-default slick-state-default' }, this._paneTopL);\r\n this._topPanelScrollerR = Utils.createDomElement('div', { className: 'slick-top-panel-scroller ui-state-default slick-state-default' }, this._paneTopR);\r\n\r\n this._topPanelScrollers = [this._topPanelScrollerL, this._topPanelScrollerR];\r\n\r\n // Append the top panel\r\n this._topPanelL = Utils.createDomElement('div', { className: 'slick-top-panel', style: { width: '10000px' } }, this._topPanelScrollerL);\r\n this._topPanelR = Utils.createDomElement('div', { className: 'slick-top-panel', style: { width: '10000px' } }, this._topPanelScrollerR);\r\n\r\n this._topPanels = [this._topPanelL, this._topPanelR];\r\n\r\n if (!this._options.showColumnHeader) {\r\n this._headerScroller.forEach((el) => {\r\n Utils.hide(el);\r\n });\r\n }\r\n\r\n if (!this._options.showTopPanel) {\r\n this._topPanelScrollers.forEach((scroller) => {\r\n Utils.hide(scroller);\r\n });\r\n }\r\n\r\n if (!this._options.showHeaderRow) {\r\n this._headerRowScroller.forEach((scroller) => {\r\n Utils.hide(scroller);\r\n });\r\n }\r\n\r\n // Append the viewport containers\r\n this._viewportTopL = Utils.createDomElement('div', { className: 'slick-viewport slick-viewport-top slick-viewport-left', tabIndex: 0 }, this._paneTopL);\r\n this._viewportTopR = Utils.createDomElement('div', { className: 'slick-viewport slick-viewport-top slick-viewport-right', tabIndex: 0 }, this._paneTopR);\r\n this._viewportBottomL = Utils.createDomElement('div', { className: 'slick-viewport slick-viewport-bottom slick-viewport-left', tabIndex: 0 }, this._paneBottomL);\r\n this._viewportBottomR = Utils.createDomElement('div', { className: 'slick-viewport slick-viewport-bottom slick-viewport-right', tabIndex: 0 }, this._paneBottomR);\r\n\r\n // Cache the viewports\r\n this._viewport = [this._viewportTopL, this._viewportTopR, this._viewportBottomL, this._viewportBottomR];\r\n if (this._options.viewportClass) {\r\n this._viewport.forEach((view) => {\r\n view.classList.add(...Utils.classNameToList((this._options.viewportClass)));\r\n });\r\n }\r\n\r\n // Default the active viewport to the top left\r\n this._activeViewportNode = this._viewportTopL;\r\n\r\n // Append the canvas containers\r\n this._canvasTopL = Utils.createDomElement('div', { className: 'grid-canvas grid-canvas-top grid-canvas-left', tabIndex: 0 }, this._viewportTopL);\r\n this._canvasTopR = Utils.createDomElement('div', { className: 'grid-canvas grid-canvas-top grid-canvas-right', tabIndex: 0 }, this._viewportTopR);\r\n this._canvasBottomL = Utils.createDomElement('div', { className: 'grid-canvas grid-canvas-bottom grid-canvas-left', tabIndex: 0 }, this._viewportBottomL);\r\n this._canvasBottomR = Utils.createDomElement('div', { className: 'grid-canvas grid-canvas-bottom grid-canvas-right', tabIndex: 0 }, this._viewportBottomR);\r\n\r\n // Cache the canvases\r\n this._canvas = [this._canvasTopL, this._canvasTopR, this._canvasBottomL, this._canvasBottomR];\r\n\r\n this.scrollbarDimensions = this.scrollbarDimensions || this.measureScrollbar();\r\n const canvasWithScrollbarWidth = this.getCanvasWidth() + this.scrollbarDimensions.width;\r\n\r\n // Default the active canvas to the top left\r\n this._activeCanvasNode = this._canvasTopL;\r\n\r\n // top-header\r\n if (this._topHeaderPanelSpacer) {\r\n Utils.width(this._topHeaderPanelSpacer, canvasWithScrollbarWidth);\r\n }\r\n\r\n // pre-header\r\n if (this._preHeaderPanelSpacer) {\r\n Utils.width(this._preHeaderPanelSpacer, canvasWithScrollbarWidth);\r\n }\r\n\r\n this._headers.forEach((el) => {\r\n Utils.width(el, this.getHeadersWidth());\r\n });\r\n\r\n Utils.width(this._headerRowSpacerL, canvasWithScrollbarWidth);\r\n Utils.width(this._headerRowSpacerR, canvasWithScrollbarWidth);\r\n\r\n // footer Row\r\n if (this._options.createFooterRow) {\r\n this._footerRowScrollerR = Utils.createDomElement('div', { className: 'slick-footerrow ui-state-default slick-state-default' }, this._paneTopR);\r\n this._footerRowScrollerL = Utils.createDomElement('div', { className: 'slick-footerrow ui-state-default slick-state-default' }, this._paneTopL);\r\n\r\n this._footerRowScroller = [this._footerRowScrollerL, this._footerRowScrollerR];\r\n\r\n this._footerRowSpacerL = Utils.createDomElement('div', { style: { display: 'block', height: '1px', position: 'absolute', top: '0px', left: '0px' } }, this._footerRowScrollerL);\r\n Utils.width(this._footerRowSpacerL, canvasWithScrollbarWidth);\r\n this._footerRowSpacerR = Utils.createDomElement('div', { style: { display: 'block', height: '1px', position: 'absolute', top: '0px', left: '0px' } }, this._footerRowScrollerR);\r\n Utils.width(this._footerRowSpacerR, canvasWithScrollbarWidth);\r\n\r\n this._footerRowL = Utils.createDomElement('div', { className: 'slick-footerrow-columns slick-footerrow-columns-left' }, this._footerRowScrollerL);\r\n this._footerRowR = Utils.createDomElement('div', { className: 'slick-footerrow-columns slick-footerrow-columns-right' }, this._footerRowScrollerR);\r\n\r\n this._footerRow = [this._footerRowL, this._footerRowR];\r\n\r\n if (!this._options.showFooterRow) {\r\n this._footerRowScroller.forEach((scroller) => {\r\n Utils.hide(scroller);\r\n });\r\n }\r\n }\r\n\r\n this._focusSink2 = this._focusSink.cloneNode(true) as HTMLDivElement;\r\n this._container.appendChild(this._focusSink2);\r\n\r\n if (!this._options.explicitInitialization) {\r\n this.finishInitialization();\r\n }\r\n }\r\n\r\n /**\r\n * Completes grid initialisation by calculating viewport dimensions, measuring cell padding and border differences,\r\n * disabling text selection (except on editable inputs), setting frozen options and pane visibility,\r\n * updating column caches, creating column headers and footers, setting up column sorting,\r\n * creating CSS rules, binding ancestor scroll events, and binding various event handlers\r\n * (e.g. for scrolling, mouse, keyboard, drag-and-drop).\r\n * It also starts up any asynchronous post\u2013render processing if enabled.\r\n */\r\n protected finishInitialization() {\r\n if (!this.initialized) {\r\n this.initialized = true;\r\n\r\n this.getViewportWidth();\r\n this.getViewportHeight();\r\n\r\n // header columns and cells may have different padding/border skewing width calculations (box-sizing, hello?)\r\n // calculate the diff so we can set consistent sizes\r\n this.measureCellPaddingAndBorder();\r\n\r\n // for usability reasons, all text selection in SlickGrid is disabled\r\n // with the exception of input and textarea elements (selection must\r\n // be enabled there so that editors work as expected); note that\r\n // selection in grid cells (grid body) is already unavailable in\r\n // all browsers except IE\r\n this.disableSelection(this._headers); // disable all text selection in header (including input and textarea)\r\n\r\n if (!this._options.enableTextSelectionOnCells) {\r\n // disable text selection in grid cells except in input and textarea elements\r\n // (this is IE-specific, because selectstart event will only fire in IE)\r\n this._viewport.forEach((view) => {\r\n this._bindingEventService.bind(view, 'selectstart', (event) => {\r\n if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement) {\r\n return;\r\n }\r\n });\r\n });\r\n }\r\n\r\n this.setFrozenOptions();\r\n this.setPaneFrozenClasses();\r\n this.setPaneVisibility();\r\n this.setScroller();\r\n this.setOverflow();\r\n\r\n this.updateColumnCaches();\r\n this.createColumnHeaders();\r\n this.createColumnFooter();\r\n this.setupColumnSort();\r\n this.createCssRules();\r\n this.resizeCanvas();\r\n this.bindAncestorScrollEvents();\r\n\r\n this._bindingEventService.bind(this._container, 'resize', this.resizeCanvas.bind(this));\r\n this._viewport.forEach((view) => {\r\n this._bindingEventService.bind(view, 'scroll', this.handleScroll.bind(this));\r\n });\r\n\r\n if (this._options.enableMouseWheelScrollHandler) {\r\n this._viewport.forEach((view) => {\r\n this.slickMouseWheelInstances.push(MouseWheel({\r\n element: view,\r\n onMouseWheel: this.handleMouseWheel.bind(this)\r\n }));\r\n });\r\n }\r\n\r\n this._headerScroller.forEach((el) => {\r\n this._bindingEventService.bind(el, 'contextmenu', this.handleHeaderContextMenu.bind(this) as EventListener);\r\n this._bindingEventService.bind(el, 'click', this.handleHeaderClick.bind(this) as EventListener);\r\n });\r\n\r\n this._headerRowScroller.forEach((scroller) => {\r\n this._bindingEventService.bind(scroller, 'scroll', this.handleHeaderRowScroll.bind(this) as EventListener);\r\n });\r\n\r\n if (this._options.createFooterRow) {\r\n this._footerRow.forEach((footer) => {\r\n this._bindingEventService.bind(footer, 'contextmenu', this.handleFooterContextMenu.bind(this) as EventListener);\r\n this._bindingEventService.bind(footer, 'click', this.handleFooterClick.bind(this) as EventListener);\r\n });\r\n\r\n this._footerRowScroller.forEach((scroller) => {\r\n this._bindingEventService.bind(scroller, 'scroll', this.handleFooterRowScroll.bind(this) as EventListener);\r\n });\r\n }\r\n\r\n if (this._options.createTopHeaderPanel) {\r\n this._bindingEventService.bind(this._topHeaderPanelScroller, 'scroll', this.handleTopHeaderPanelScroll.bind(this) as EventListener);\r\n }\r\n\r\n if (this._options.createPreHeaderPanel) {\r\n this._bindingEventService.bind(this._preHeaderPanelScroller, 'scroll', this.handlePreHeaderPanelScroll.bind(this) as EventListener);\r\n this._bindingEventService.bind(this._preHeaderPanelScroller, 'contextmenu', this.handlePreHeaderContextMenu.bind(this) as EventListener);\r\n this._bindingEventService.bind(this._preHeaderPanelScrollerR, 'contextmenu', this.handlePreHeaderContextMenu.bind(this) as EventListener);\r\n this._bindingEventService.bind(this._preHeaderPanelScroller, 'click', this.handlePreHeaderClick.bind(this) as EventListener);\r\n this._bindingEventService.bind(this._preHeaderPanelScrollerR, 'click', this.handlePreHeaderClick.bind(this) as EventListener);\r\n }\r\n\r\n this._bindingEventService.bind(this._focusSink, 'keydown', this.handleKeyDown.bind(this) as EventListener);\r\n this._bindingEventService.bind(this._focusSink2, 'keydown', this.handleKeyDown.bind(this) as EventListener);\r\n\r\n this._canvas.forEach((element) => {\r\n this._bindingEventService.bind(element, 'keydown', this.handleKeyDown.bind(this) as EventListener);\r\n this._bindingEventService.bind(element, 'click', this.handleClick.bind(this) as EventListener);\r\n this._bindingEventService.bind(element, 'dblclick', this.handleDblClick.bind(this) as EventListener);\r\n this._bindingEventService.bind(element, 'contextmenu', this.handleContextMenu.bind(this) as EventListener);\r\n this._bindingEventService.bind(element, 'mouseover', this.handleCellMouseOver.bind(this) as EventListener);\r\n this._bindingEventService.bind(element, 'mouseout', this.handleCellMouseOut.bind(this) as EventListener);\r\n });\r\n\r\n if (Draggable) {\r\n this.slickDraggableInstance = Draggable({\r\n containerElement: this._container,\r\n allowDragFrom: 'div.slick-cell',\r\n // the slick cell parent must always contain `.dnd` and/or `.cell-reorder` class to be identified as draggable\r\n allowDragFromClosest: 'div.slick-cell.dnd, div.slick-cell.cell-reorder',\r\n preventDragFromKeys: this._options.preventDragFromKeys,\r\n onDragInit: this.handleDragInit.bind(this),\r\n onDragStart: this.handleDragStart.bind(this),\r\n onDrag: this.handleDrag.bind(this),\r\n onDragEnd: this.handleDragEnd.bind(this)\r\n });\r\n }\r\n\r\n if (!this._options.suppressCssChangesOnHiddenInit) {\r\n this.restoreCssFromHiddenInit();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Finds all container ancestors/parents (including the grid container itself) that are hidden (i.e. have display:none)\r\n * and temporarily applies visible CSS properties (absolute positioning, hidden visibility, block display)\r\n * so that dimensions can be measured correctly.\r\n * It stores the original CSS properties in an internal array for later restoration.\r\n *\r\n * Related to issue: https://github.com/6pac/SlickGrid/issues/568 */\r\n cacheCssForHiddenInit() {\r\n this._hiddenParents = Utils.parents(this._container, ':hidden') as HTMLElement[];\r\n this.oldProps = [];\r\n this._hiddenParents.forEach(el => {\r\n const old: Partial = {};\r\n Object.keys(this.cssShow).forEach(name => {\r\n if (this.cssShow) {\r\n old[name as any] = el.style[name as 'position' | 'visibility' | 'display'];\r\n el.style[name as any] = this.cssShow[name as 'position' | 'visibility' | 'display'];\r\n }\r\n });\r\n this.oldProps.push(old);\r\n });\r\n }\r\n\r\n /**\r\n * Restores the original CSS properties for the container and its hidden\r\n * ancestors that were modified by cacheCssForHiddenInit.\r\n * This ensures that after initial measurements the DOM elements revert\r\n * to their original style settings.\r\n */\r\n restoreCssFromHiddenInit() {\r\n // finish handle display:none on container or container parents\r\n // - put values back the way they were\r\n let i = 0;\r\n if (this._hiddenParents) {\r\n this._hiddenParents.forEach(el => {\r\n const old = this.oldProps[i++];\r\n Object.keys(this.cssShow).forEach(name => {\r\n if (this.cssShow) {\r\n el.style[name as CSSStyleDeclarationWritable] = (old as any)[name];\r\n }\r\n });\r\n });\r\n this._hiddenParents = [];\r\n }\r\n }\r\n\r\n /**\r\n * Registers an external plugin to the grid\u2019s internal plugin list.\r\n * Once added, it immediately initialises the plugin by calling its init()\r\n * method with the grid instance.\r\n * @param {T} plugin - The plugin instance to be registered.\r\n */\r\n registerPlugin(plugin: T) {\r\n this.plugins.unshift(plugin);\r\n plugin.init(this as unknown as SlickGridModel);\r\n }\r\n\r\n /**\r\n * Unregister (destroy) an external Plugin.\r\n * Searches for the specified plugin in the grid\u2019s plugin list.\r\n * When found, it calls the plugin\u2019s destroy() method and removes the plugin from the list,\r\n * thereby unregistering it from the grid.\r\n * @param {T} plugin - The plugin instance to be registered.\r\n */\r\n unregisterPlugin(plugin: SlickPlugin) {\r\n for (let i = this.plugins.length; i >= 0; i--) {\r\n if (this.plugins[i] === plugin) {\r\n this.plugins[i]?.destroy();\r\n this.plugins.splice(i, 1);\r\n break;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Destroy (dispose) of SlickGrid\r\n *\r\n * Unbinds all event handlers, cancels any active cell edits, triggers the onBeforeDestroy event,\r\n * unregisters and destroys plugins, destroys sortable and other interaction instances,\r\n * unbinds ancestor scroll events, removes CSS rules, unbinds events from all key DOM elements\r\n * (canvas, viewports, header, footer, etc.), empties the grid container, removes the grid\u2019s uid class,\r\n * and clears all timers. Optionally, if shouldDestroyAllElements is true,\r\n * calls destroyAllElements to nullify all DOM references.\r\n *\r\n * @param {boolean} shouldDestroyAllElements - do we want to destroy (nullify) all DOM elements as well? This help in avoiding mem leaks\r\n */\r\n destroy(shouldDestroyAllElements?: boolean) {\r\n this._bindingEventService.unbindAll();\r\n this.slickDraggableInstance = this.destroyAllInstances(this.slickDraggableInstance) as null;\r\n this.slickMouseWheelInstances = this.destroyAllInstances(this.slickMouseWheelInstances) as InteractionBase[];\r\n this.slickResizableInstances = this.destroyAllInstances(this.slickResizableInstances) as InteractionBase[];\r\n this.getEditorLock()?.cancelCurrentEdit();\r\n\r\n this.trigger(this.onBeforeDestroy, {});\r\n\r\n let i = this.plugins.length;\r\n while (i--) {\r\n this.unregisterPlugin(this.plugins[i]);\r\n }\r\n\r\n if (this._options.enableColumnReorder && typeof this.sortableSideLeftInstance?.destroy === 'function') {\r\n this.sortableSideLeftInstance?.destroy();\r\n this.sortableSideRightInstance?.destroy();\r\n }\r\n\r\n this.unbindAncestorScrollEvents();\r\n this._bindingEventService.unbindByEventName(this._container, 'resize');\r\n this.removeCssRules();\r\n\r\n this._canvas.forEach((element) => {\r\n this._bindingEventService.unbindByEventName(element, 'keydown');\r\n this._bindingEventService.unbindByEventName(element, 'click');\r\n this._bindingEventService.unbindByEventName(element, 'dblclick');\r\n this._bindingEventService.unbindByEventName(element, 'contextmenu');\r\n this._bindingEventService.unbindByEventName(element, 'mouseover');\r\n this._bindingEventService.unbindByEventName(element, 'mouseout');\r\n });\r\n this._viewport.forEach((view) => {\r\n this._bindingEventService.unbindByEventName(view, 'scroll');\r\n });\r\n\r\n this._headerScroller.forEach((el) => {\r\n this._bindingEventService.unbindByEventName(el, 'contextmenu');\r\n this._bindingEventService.unbindByEventName(el, 'click');\r\n });\r\n\r\n this._headerRowScroller.forEach((scroller) => {\r\n this._bindingEventService.unbindByEventName(scroller, 'scroll');\r\n });\r\n\r\n if (this._footerRow) {\r\n this._footerRow.forEach((footer) => {\r\n this._bindingEventService.unbindByEventName(footer, 'contextmenu');\r\n this._bindingEventService.unbindByEventName(footer, 'click');\r\n });\r\n }\r\n\r\n if (this._footerRowScroller) {\r\n this._footerRowScroller.forEach((scroller) => {\r\n this._bindingEventService.unbindByEventName(scroller, 'scroll');\r\n });\r\n }\r\n\r\n if (this._preHeaderPanelScroller) {\r\n this._bindingEventService.unbindByEventName(this._preHeaderPanelScroller, 'scroll');\r\n }\r\n\r\n if (this._topHeaderPanelScroller) {\r\n this._bindingEventService.unbindByEventName(this._topHeaderPanelScroller, 'scroll');\r\n }\r\n\r\n this._bindingEventService.unbindByEventName(this._focusSink, 'keydown');\r\n this._bindingEventService.unbindByEventName(this._focusSink2, 'keydown');\r\n\r\n const resizeHandles = this._container.querySelectorAll('.slick-resizable-handle');\r\n [].forEach.call(resizeHandles, (handle) => {\r\n this._bindingEventService.unbindByEventName(handle, 'dblclick');\r\n });\r\n\r\n const headerColumns = this._container.querySelectorAll('.slick-header-column');\r\n [].forEach.call(headerColumns, (column) => {\r\n this._bindingEventService.unbindByEventName(column, 'mouseenter');\r\n this._bindingEventService.unbindByEventName(column, 'mouseleave');\r\n\r\n this._bindingEventService.unbindByEventName(column, 'mouseenter');\r\n this._bindingEventService.unbindByEventName(column, 'mouseleave');\r\n });\r\n\r\n Utils.emptyElement(this._container);\r\n this._container.classList.remove(this.uid);\r\n this.clearAllTimers();\r\n\r\n if (shouldDestroyAllElements) {\r\n this.destroyAllElements();\r\n }\r\n }\r\n\r\n /**\r\n * Call destroy method, when exists, on all the instance(s) it found\r\n *\r\n * Given either a single instance or an array of instances (e.g. draggable, mousewheel, resizable),\r\n * pops each one and calls its destroy method if available, then resets the input to an empty array\r\n * (or null for a single instance). Returns the reset value.\r\n *\r\n * @params instances - can be a single instance or a an array of instances\r\n */\r\n protected destroyAllInstances(inputInstances: null | InteractionBase | Array) {\r\n if (inputInstances) {\r\n const instances = Array.isArray(inputInstances) ? inputInstances : [inputInstances];\r\n let instance: InteractionBase | undefined;\r\n while (Utils.isDefined(instance = instances.pop())) {\r\n if (instance && typeof instance.destroy === 'function') {\r\n instance.destroy();\r\n }\r\n }\r\n }\r\n // reset instance(s)\r\n inputInstances = (Array.isArray(inputInstances) ? [] : null);\r\n return inputInstances;\r\n }\r\n\r\n /**\r\n * Sets all internal references to DOM elements\r\n * (e.g. canvas containers, headers, viewports, focus sinks, etc.)\r\n * to null so that they can be garbage collected.\r\n */\r\n protected destroyAllElements() {\r\n this._activeCanvasNode = null as any;\r\n this._activeViewportNode = null as any;\r\n this._boundAncestors = null as any;\r\n this._canvas = null as any;\r\n this._canvasTopL = null as any;\r\n this._canvasTopR = null as any;\r\n this._canvasBottomL = null as any;\r\n this._canvasBottomR = null as any;\r\n this._container = null as any;\r\n this._focusSink = null as any;\r\n this._focusSink2 = null as any;\r\n this._groupHeaders = null as any;\r\n this._groupHeadersL = null as any;\r\n this._groupHeadersR = null as any;\r\n this._headerL = null as any;\r\n this._headerR = null as any;\r\n this._headers = null as any;\r\n this._headerRows = null as any;\r\n this._headerRowL = null as any;\r\n this._headerRowR = null as any;\r\n this._headerRowSpacerL = null as any;\r\n this._headerRowSpacerR = null as any;\r\n this._headerRowScrollContainer = null as any;\r\n this._headerRowScroller = null as any;\r\n this._headerRowScrollerL = null as any;\r\n this._headerRowScrollerR = null as any;\r\n this._headerScrollContainer = null as any;\r\n this._headerScroller = null as any;\r\n this._headerScrollerL = null as any;\r\n this._headerScrollerR = null as any;\r\n this._hiddenParents = null as any;\r\n this._footerRow = null as any;\r\n this._footerRowL = null as any;\r\n this._footerRowR = null as any;\r\n this._footerRowSpacerL = null as any;\r\n this._footerRowSpacerR = null as any;\r\n this._footerRowScroller = null as any;\r\n this._footerRowScrollerL = null as any;\r\n this._footerRowScrollerR = null as any;\r\n this._footerRowScrollContainer = null as any;\r\n this._preHeaderPanel = null as any;\r\n this._preHeaderPanelR = null as any;\r\n this._preHeaderPanelScroller = null as any;\r\n this._preHeaderPanelScrollerR = null as any;\r\n this._preHeaderPanelSpacer = null as any;\r\n this._preHeaderPanelSpacerR = null as any;\r\n this._topPanels = null as any;\r\n this._topPanelScrollers = null as any;\r\n this._style = null as any;\r\n this._topPanelScrollerL = null as any;\r\n this._topPanelScrollerR = null as any;\r\n this._topPanelL = null as any;\r\n this._topPanelR = null as any;\r\n this._paneHeaderL = null as any;\r\n this._paneHeaderR = null as any;\r\n this._paneTopL = null as any;\r\n this._paneTopR = null as any;\r\n this._paneBottomL = null as any;\r\n this._paneBottomR = null as any;\r\n this._viewport = null as any;\r\n this._viewportTopL = null as any;\r\n this._viewportTopR = null as any;\r\n this._viewportBottomL = null as any;\r\n this._viewportBottomR = null as any;\r\n this._viewportScrollContainerX = null as any;\r\n this._viewportScrollContainerY = null as any;\r\n }\r\n\r\n /** Returns an object containing all of the Grid options set on the grid. See a list of Grid Options here. */\r\n getOptions() {\r\n return this._options;\r\n }\r\n\r\n /**\r\n * Extends grid options with a given hash. If an there is an active edit, the grid will attempt to commit the changes and only continue if the attempt succeeds.\r\n * @param {Object} options - an object with configuration options.\r\n * @param {Boolean} [suppressRender] - do we want to supress the grid re-rendering? (defaults to false)\r\n * @param {Boolean} [suppressColumnSet] - do we want to supress the columns set, via \"setColumns()\" method? (defaults to false)\r\n * @param {Boolean} [suppressSetOverflow] - do we want to suppress the call to `setOverflow`\r\n */\r\n setOptions(newOptions: Partial, suppressRender?: boolean, suppressColumnSet?: boolean, suppressSetOverflow?: boolean): void {\r\n this.prepareForOptionsChange();\r\n\r\n if (this._options.enableAddRow !== newOptions.enableAddRow) {\r\n this.invalidateRow(this.getDataLength());\r\n }\r\n\r\n // before applying column freeze, we need our viewports to be scrolled back to left to avoid misaligned column headers\r\n if (newOptions.frozenColumn !== undefined && newOptions.frozenColumn >= 0) {\r\n this.getViewports().forEach(vp => vp.scrollLeft = 0);\r\n this.handleScroll(); // trigger scroll to realign column headers as well\r\n }\r\n\r\n const originalOptions = Utils.extend(true, {}, this._options);\r\n this._options = Utils.extend(this._options, newOptions);\r\n this.trigger(this.onSetOptions, { optionsBefore: originalOptions, optionsAfter: this._options });\r\n\r\n this.internal_setOptions(suppressRender, suppressColumnSet, suppressSetOverflow);\r\n }\r\n\r\n /**\r\n * If option.mixinDefaults is true then external code maintains a reference to the options object. In this case there is no need\r\n * to call setOptions() - changes can be made directly to the object. However setOptions() also performs some recalibration of the\r\n * grid in reaction to changed options. activateChangedOptions call the same recalibration routines as setOptions() would have.\r\n * @param {Boolean} [suppressRender] - do we want to supress the grid re-rendering? (defaults to false)\r\n * @param {Boolean} [suppressColumnSet] - do we want to supress the columns set, via \"setColumns()\" method? (defaults to false)\r\n * @param {Boolean} [suppressSetOverflow] - do we want to suppress the call to `setOverflow`\r\n */\r\n activateChangedOptions(suppressRender?: boolean, suppressColumnSet?: boolean, suppressSetOverflow?: boolean): void {\r\n this.prepareForOptionsChange();\r\n this.invalidateRow(this.getDataLength());\r\n\r\n this.trigger(this.onActivateChangedOptions, { options: this._options });\r\n\r\n this.internal_setOptions(suppressRender, suppressColumnSet, suppressSetOverflow);\r\n }\r\n\r\n /**\r\n * Attempts to commit any active cell edit via the editor lock; if successful, calls makeActiveCellNormal to exit edit mode.\r\n *\r\n * @returns {void} - Does not return a value.\r\n */\r\n protected prepareForOptionsChange() {\r\n if (!this.getEditorLock().commitCurrentEdit()) {\r\n return;\r\n }\r\n\r\n this.makeActiveCellNormal();\r\n }\r\n\r\n /**\r\n * Depending on new options, sets column header visibility, validates options, sets frozen options,\r\n * forces viewport height recalculation if needed, updates viewport overflow, re-renders the grid (unless suppressed),\r\n * sets the scroller elements, and reinitialises mouse wheel scrolling as needed.\r\n *\r\n * @param {boolean} [suppressRender] - If `true`, prevents the grid from re-rendering.\r\n * @param {boolean} [suppressColumnSet] - If `true`, prevents the columns from being reset.\r\n * @param {boolean} [suppressSetOverflow] - If `true`, prevents updating the viewport overflow setting.\r\n */\r\n protected internal_setOptions(suppressRender?: boolean, suppressColumnSet?: boolean, suppressSetOverflow?: boolean): void {\r\n if (this._options.showColumnHeader !== undefined) {\r\n this.setColumnHeaderVisibility(this._options.showColumnHeader);\r\n }\r\n this.validateAndEnforceOptions();\r\n this.setFrozenOptions();\r\n\r\n // when user changed frozen row option, we need to force a recalculation of each viewport heights\r\n if (this._options.frozenBottom !== undefined) {\r\n this.enforceFrozenRowHeightRecalc = true;\r\n }\r\n\r\n this._viewport.forEach((view) => {\r\n view.style.overflowY = this._options.autoHeight ? 'hidden' : 'auto';\r\n });\r\n if (!suppressRender) {\r\n this.render();\r\n }\r\n\r\n this.setScroller();\r\n if (!suppressSetOverflow) {\r\n this.setOverflow();\r\n }\r\n\r\n if (!suppressColumnSet) {\r\n this.setColumns(this.columns);\r\n }\r\n\r\n if (this._options.enableMouseWheelScrollHandler && this._viewport && (!this.slickMouseWheelInstances || this.slickMouseWheelInstances.length === 0)) {\r\n this._viewport.forEach((view) => {\r\n this.slickMouseWheelInstances.push(MouseWheel({\r\n element: view,\r\n onMouseWheel: this.handleMouseWheel.bind(this)\r\n }));\r\n });\r\n } else if (this._options.enableMouseWheelScrollHandler === false) {\r\n this.destroyAllInstances(this.slickMouseWheelInstances); // remove scroll handler when option is disable\r\n }\r\n }\r\n\r\n /**\r\n *\r\n * Ensures consistency in option setting, by thastIF autoHeight IS enabled, leaveSpaceForNewRows is set to FALSE.\r\n * And, if forceFitColumns is True, then autosizeColsMode is set to LegacyForceFit.\r\n */\r\n validateAndEnforceOptions(): void {\r\n if (this._options.autoHeight) {\r\n this._options.leaveSpaceForNewRows = false;\r\n }\r\n if (this._options.forceFitColumns) {\r\n this._options.autosizeColsMode = GridAutosizeColsMode.LegacyForceFit;\r\n }\r\n }\r\n\r\n /**\r\n * Unregisters a current selection model and registers a new one. See the definition of SelectionModel for more information.\r\n * @param {Object} selectionModel A SelectionModel.\r\n */\r\n setSelectionModel(model: SelectionModel) {\r\n if (this.selectionModel) {\r\n this.selectionModel.onSelectedRangesChanged.unsubscribe(this.handleSelectedRangesChanged.bind(this));\r\n if (this.selectionModel.destroy) {\r\n this.selectionModel.destroy();\r\n }\r\n }\r\n\r\n this.selectionModel = model;\r\n if (this.selectionModel) {\r\n this.selectionModel.init(this as unknown as SlickGridModel);\r\n this.selectionModel.onSelectedRangesChanged.subscribe(this.handleSelectedRangesChanged.bind(this));\r\n }\r\n }\r\n\r\n /** Returns the current SelectionModel. See here for more information about SelectionModels. */\r\n getSelectionModel() {\r\n return this.selectionModel;\r\n }\r\n\r\n /** add/remove frozen class to left headers/footer when defined */\r\n protected setPaneFrozenClasses(): void {\r\n const classAction = this.hasFrozenColumns() ? 'add' : 'remove';\r\n for (const elm of [this._paneHeaderL, this._paneTopL, this._paneBottomL]) {\r\n elm.classList[classAction]('frozen');\r\n }\r\n }\r\n\r\n //////////////////////////////////////////////////////////////////////\r\n // End Grid and DOM Initialisation\r\n //////////////////////////////////////////////////////////////////////\r\n\r\n\r\n //////////////////////////////////////////////////////////////////////\r\n // Column Management, Headers and Footers\r\n //////////////////////////////////////////////////////////////////////\r\n\r\n // Returns a boolean indicating whether the grid is configured with frozen columns.\r\n protected hasFrozenColumns() {\r\n return this._options.frozenColumn! > -1;\r\n }\r\n\r\n /**\r\n * Updates an existing column definition and a corresponding header DOM element with the new title and tooltip.\r\n * @param {Number|String} columnId Column id.\r\n * @param {string | HTMLElement | DocumentFragment} [title] New column name.\r\n * @param {String} [toolTip] New column tooltip.\r\n */\r\n updateColumnHeader(columnId: number | string, title?: string | HTMLElement | DocumentFragment, toolTip?: string) {\r\n if (this.initialized) {\r\n const idx = this.getColumnIndex(columnId);\r\n if (!Utils.isDefined(idx)) {\r\n return;\r\n }\r\n\r\n const columnDef = this.columns[idx];\r\n const header: HTMLElement | undefined = this.getColumnByIndex(idx);\r\n if (header) {\r\n if (title !== undefined) {\r\n this.columns[idx].name = title;\r\n }\r\n if (toolTip !== undefined) {\r\n this.columns[idx].toolTip = toolTip;\r\n }\r\n\r\n this.trigger(this.onBeforeHeaderCellDestroy, {\r\n node: header,\r\n column: columnDef,\r\n grid: this\r\n });\r\n\r\n header.setAttribute('title', toolTip || '');\r\n if (title !== undefined) {\r\n this.applyHtmlCode(header.children[0] as HTMLElement, title);\r\n }\r\n\r\n this.trigger(this.onHeaderCellRendered, {\r\n node: header,\r\n column: columnDef,\r\n grid: this\r\n });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Get the Header DOM element\r\n * @param {C} columnDef - column definition\r\n */\r\n getHeader(columnDef: C) {\r\n if (!columnDef) {\r\n return this.hasFrozenColumns() ? this._headers : this._headerL;\r\n }\r\n const idx = this.getColumnIndex(columnDef.id);\r\n return this.hasFrozenColumns() ? ((idx <= this._options.frozenColumn!) ? this._headerL : this._headerR) : this._headerL;\r\n }\r\n\r\n /**\r\n * Get a specific Header Column DOM element by its column Id or index\r\n * @param {Number|String} columnIdOrIdx - column Id or index\r\n */\r\n getHeaderColumn(columnIdOrIdx: number | string) {\r\n const idx = (typeof columnIdOrIdx === 'number' ? columnIdOrIdx : this.getColumnIndex(columnIdOrIdx));\r\n const targetHeader = this.hasFrozenColumns() ? ((idx <= this._options.frozenColumn!) ? this._headerL : this._headerR) : this._headerL;\r\n const targetIndex = this.hasFrozenColumns() ? ((idx <= this._options.frozenColumn!) ? idx : idx - this._options.frozenColumn! - 1) : idx;\r\n\r\n return targetHeader.children[targetIndex] as HTMLDivElement;\r\n }\r\n\r\n /** Get the Header Row DOM element */\r\n getHeaderRow() {\r\n return this.hasFrozenColumns() ? this._headerRows : this._headerRows[0];\r\n }\r\n\r\n /** Get the Footer DOM element */\r\n getFooterRow() {\r\n return this.hasFrozenColumns() ? this._footerRow : this._footerRow[0];\r\n }\r\n\r\n /**\r\n * Get Header Row Column DOM element by its column Id or index\r\n * @param {Number|String} columnIdOrIdx - column Id or index\r\n */\r\n getHeaderRowColumn(columnIdOrIdx: number | string) {\r\n let idx = (typeof columnIdOrIdx === 'number' ? columnIdOrIdx : this.getColumnIndex(columnIdOrIdx));\r\n let headerRowTarget: HTMLDivElement;\r\n\r\n if (this.hasFrozenColumns()) {\r\n if (idx <= this._options.frozenColumn!) {\r\n headerRowTarget = this._headerRowL;\r\n } else {\r\n headerRowTarget = this._headerRowR;\r\n idx -= this._options.frozenColumn! + 1;\r\n }\r\n } else {\r\n headerRowTarget = this._headerRowL;\r\n }\r\n\r\n return headerRowTarget.children[idx] as HTMLDivElement;\r\n }\r\n\r\n /**\r\n * Get the Footer Row Column DOM element by its column Id or index\r\n * @param {Number|String} columnIdOrIdx - column Id or index\r\n */\r\n getFooterRowColumn(columnIdOrIdx: number | string) {\r\n let idx = (typeof columnIdOrIdx === 'number' ? columnIdOrIdx : this.getColumnIndex(columnIdOrIdx));\r\n let footerRowTarget: HTMLDivElement;\r\n\r\n if (this.hasFrozenColumns()) {\r\n if (idx <= this._options.frozenColumn!) {\r\n footerRowTarget = this._footerRowL;\r\n } else {\r\n footerRowTarget = this._footerRowR;\r\n\r\n idx -= this._options.frozenColumn! + 1;\r\n }\r\n } else {\r\n footerRowTarget = this._footerRowL;\r\n }\r\n\r\n return footerRowTarget.children[idx] as HTMLDivElement;\r\n }\r\n\r\n /**\r\n * If footer rows are enabled, clears existing footer cells then iterates over all columns.\r\n * For each visible column, it creates a footer cell element (adding \u201Cfrozen\u201D classes if needed),\r\n * stores the column definition in the element\u2019s storage, and triggers the onFooterRowCellRendered event.\r\n */\r\n protected createColumnFooter() {\r\n if (this._options.createFooterRow) {\r\n this._footerRow.forEach((footer) => {\r\n const columnElements = footer.querySelectorAll('.slick-footerrow-column');\r\n columnElements.forEach((column) => {\r\n const columnDef = Utils.storage.get(column, 'column');\r\n this.trigger(this.onBeforeFooterRowCellDestroy, {\r\n node: column,\r\n column: columnDef,\r\n grid: this\r\n });\r\n });\r\n });\r\n\r\n Utils.emptyElement(this._footerRowL);\r\n Utils.emptyElement(this._footerRowR);\r\n\r\n for (let i = 0; i < this.columns.length; i++) {\r\n const m = this.columns[i];\r\n if (!m || m.hidden) { continue; }\r\n\r\n const footerRowCell = Utils.createDomElement('div', { className: `ui-state-default slick-state-default slick-footerrow-column l${i} r${i}` }, this.hasFrozenColumns() && (i > this._options.frozenColumn!) ? this._footerRowR : this._footerRowL);\r\n const className = this.hasFrozenColumns() && i <= this._options.frozenColumn! ? 'frozen' : null;\r\n if (className) {\r\n footerRowCell.classList.add(className);\r\n }\r\n\r\n Utils.storage.put(footerRowCell, 'column', m);\r\n\r\n this.trigger(this.onFooterRowCellRendered, {\r\n node: footerRowCell,\r\n column: m,\r\n grid: this\r\n });\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * For each header container, binds a click event that\u2014\r\n * if the clicked header is sortable and no column resizing is in progress\u2014\r\n * --> toggles the sort direction (or adds/removes the column in a multi\u2013column sort),\r\n * --> triggers onBeforeSort\r\n * --> and if not cancelled, updates the sort columns and triggers onSort.\r\n */\r\n protected setupColumnSort() {\r\n this._headers.forEach((header) => {\r\n this._bindingEventService.bind(header, 'click', (e: any) => {\r\n if (this.columnResizeDragging) {\r\n return;\r\n }\r\n\r\n if (e.target.classList.contains('slick-resizable-handle')) {\r\n return;\r\n }\r\n\r\n const coll = e.target.closest('.slick-header-column');\r\n if (!coll) {\r\n return;\r\n }\r\n\r\n const column = Utils.storage.get(coll, 'column');\r\n if (column.sortable) {\r\n if (!this.getEditorLock()?.commitCurrentEdit()) {\r\n return;\r\n }\r\n\r\n const previousSortColumns = this.sortColumns.slice();\r\n let sortColumn: ColumnSort | null = null;\r\n let i = 0;\r\n for (; i < this.sortColumns.length; i++) {\r\n if (this.sortColumns[i].columnId === column.id) {\r\n sortColumn = this.sortColumns[i];\r\n sortColumn.sortAsc = !sortColumn.sortAsc;\r\n break;\r\n }\r\n }\r\n const hadSortCol = !!sortColumn;\r\n\r\n if (this._options.tristateMultiColumnSort) {\r\n if (!sortColumn) {\r\n sortColumn = { columnId: column.id, sortAsc: column.defaultSortAsc, sortCol: column };\r\n }\r\n if (hadSortCol && sortColumn.sortAsc) {\r\n // three state: remove sort rather than go back to ASC\r\n this.sortColumns.splice(i, 1);\r\n sortColumn = null;\r\n }\r\n if (!this._options.multiColumnSort) {\r\n this.sortColumns = [];\r\n }\r\n if (sortColumn && (!hadSortCol || !this._options.multiColumnSort)) {\r\n this.sortColumns.push(sortColumn);\r\n }\r\n } else {\r\n // legacy behaviour\r\n if (e.metaKey && this._options.multiColumnSort) {\r\n if (sortColumn) {\r\n this.sortColumns.splice(i, 1);\r\n }\r\n } else {\r\n if ((!e.shiftKey && !e.metaKey) || !this._options.multiColumnSort) {\r\n this.sortColumns = [];\r\n }\r\n\r\n if (!sortColumn) {\r\n sortColumn = { columnId: column.id, sortAsc: column.defaultSortAsc, sortCol: column };\r\n this.sortColumns.push(sortColumn);\r\n } else if (this.sortColumns.length === 0) {\r\n this.sortColumns.push(sortColumn);\r\n }\r\n }\r\n }\r\n\r\n let onSortArgs;\r\n if (!this._options.multiColumnSort) {\r\n onSortArgs = {\r\n multiColumnSort: false,\r\n previousSortColumns,\r\n columnId: (this.sortColumns.length > 0 ? column.id : null),\r\n sortCol: (this.sortColumns.length > 0 ? column : null),\r\n sortAsc: (this.sortColumns.length > 0 ? this.sortColumns[0].sortAsc : true)\r\n };\r\n } else {\r\n onSortArgs = {\r\n multiColumnSort: true,\r\n previousSortColumns,\r\n sortCols: this.sortColumns.map((col) => {\r\n const tempCol = this.columns[this.getColumnIndex(col.columnId)];\r\n return !tempCol || tempCol.hidden ? null : { columnId: tempCol.id, sortCol: tempCol, sortAsc: col.sortAsc };\r\n }).filter((el) => el)\r\n };\r\n }\r\n\r\n if (this.trigger(this.onBeforeSort, onSortArgs, e).getReturnValue() !== false) {\r\n this.setSortColumns(this.sortColumns);\r\n this.trigger(this.onSort, onSortArgs, e);\r\n }\r\n }\r\n });\r\n });\r\n }\r\n\r\n /**\r\n * Clears any existing header cells and header row cells, recalculates header widths,\r\n * then iterates over each visible column to create header cell elements\r\n * (and header row cells if enabled) with appropriate content, CSS classes, event bindings,\r\n * and sort indicator elements. Also triggers before\u2013destroy and rendered events as needed.\r\n */\r\n protected createColumnHeaders() {\r\n this._headers.forEach((header) => {\r\n const columnElements = header.querySelectorAll('.slick-header-column');\r\n columnElements.forEach((column) => {\r\n const columnDef = Utils.storage.get(column, 'column');\r\n if (columnDef) {\r\n this.trigger(this.onBeforeHeaderCellDestroy, {\r\n node: column,\r\n column: columnDef,\r\n grid: this\r\n });\r\n }\r\n });\r\n });\r\n\r\n Utils.emptyElement(this._headerL);\r\n Utils.emptyElement(this._headerR);\r\n\r\n this.getHeadersWidth();\r\n\r\n Utils.width(this._headerL, this.headersWidthL);\r\n Utils.width(this._headerR, this.headersWidthR);\r\n\r\n this._headerRows.forEach((row) => {\r\n const columnElements = row.querySelectorAll('.slick-headerrow-column');\r\n columnElements.forEach((column) => {\r\n const columnDef = Utils.storage.get(column, 'column');\r\n if (columnDef) {\r\n this.trigger(this.onBeforeHeaderRowCellDestroy, {\r\n node: this,\r\n column: columnDef,\r\n grid: this\r\n });\r\n }\r\n });\r\n });\r\n\r\n Utils.emptyElement(this._headerRowL);\r\n Utils.emptyElement(this._headerRowR);\r\n\r\n if (this._options.createFooterRow) {\r\n const footerRowLColumnElements = this._footerRowL.querySelectorAll('.slick-footerrow-column');\r\n footerRowLColumnElements.forEach((column) => {\r\n const columnDef = Utils.storage.get(column, 'column');\r\n if (columnDef) {\r\n this.trigger(this.onBeforeFooterRowCellDestroy, {\r\n node: this,\r\n column: columnDef,\r\n grid: this\r\n });\r\n }\r\n });\r\n Utils.emptyElement(this._footerRowL);\r\n\r\n if (this.hasFrozenColumns()) {\r\n const footerRowRColumnElements = this._footerRowR.querySelectorAll('.slick-footerrow-column');\r\n footerRowRColumnElements.forEach((column) => {\r\n const columnDef = Utils.storage.get(column, 'column');\r\n if (columnDef) {\r\n this.trigger(this.onBeforeFooterRowCellDestroy, {\r\n node: this,\r\n column: columnDef,\r\n grid: this\r\n });\r\n }\r\n });\r\n Utils.emptyElement(this._footerRowR);\r\n }\r\n }\r\n\r\n for (let i = 0; i < this.columns.length; i++) {\r\n const m: C = this.columns[i];\r\n if (m.hidden) { continue; }\r\n\r\n const headerTarget = this.hasFrozenColumns() ? ((i <= this._options.frozenColumn!) ? this._headerL : this._headerR) : this._headerL;\r\n const headerRowTarget = this.hasFrozenColumns() ? ((i <= this._options.frozenColumn!) ? this._headerRowL : this._headerRowR) : this._headerRowL;\r\n\r\n const header = Utils.createDomElement('div', { id: `${this.uid + m.id}`, dataset: { id: String(m.id) }, role: 'columnheader', className: 'ui-state-default slick-state-default slick-header-column' }, headerTarget);\r\n if (m.toolTip) {\r\n header.title = m.toolTip;\r\n }\r\n if (!m.reorderable) {\r\n header.classList.add(this._options.unorderableColumnCssClass!);\r\n }\r\n const colNameElm = Utils.createDomElement('span', { className: 'slick-column-name' }, header);\r\n this.applyHtmlCode(colNameElm, m.name as string);\r\n\r\n Utils.width(header, m.width! - this.headerColumnWidthDiff);\r\n\r\n let classname = m.headerCssClass || null;\r\n if (classname) {\r\n header.classList.add(...Utils.classNameToList(classname));\r\n }\r\n classname = this.hasFrozenColumns() && i <= this._options.frozenColumn! ? 'frozen' : null;\r\n if (classname) {\r\n header.classList.add(classname);\r\n }\r\n\r\n this._bindingEventService.bind(header, 'mouseenter', this.handleHeaderMouseEnter.bind(this) as EventListener);\r\n this._bindingEventService.bind(header, 'mouseleave', this.handleHeaderMouseLeave.bind(this) as EventListener);\r\n\r\n Utils.storage.put(header, 'column', m);\r\n\r\n if (this._options.enableColumnReorder || m.sortable) {\r\n this._bindingEventService.bind(header, 'mouseenter', this.handleHeaderMouseHoverOn.bind(this) as EventListener);\r\n this._bindingEventService.bind(header, 'mouseleave', this.handleHeaderMouseHoverOff.bind(this) as EventListener);\r\n }\r\n\r\n if (m.hasOwnProperty('headerCellAttrs') && m.headerCellAttrs instanceof Object) {\r\n Object.keys(m.headerCellAttrs).forEach(key => {\r\n if (m.headerCellAttrs.hasOwnProperty(key)) {\r\n header.setAttribute(key, m.headerCellAttrs[key]);\r\n }\r\n });\r\n }\r\n\r\n if (m.sortable) {\r\n header.classList.add('slick-header-sortable');\r\n Utils.createDomElement('div', { className: `slick-sort-indicator ${this._options.numberedMultiColumnSort && !this._options.sortColNumberInSeparateSpan ? ' slick-sort-indicator-numbered' : ''}` }, header);\r\n if (this._options.numberedMultiColumnSort && this._options.sortColNumberInSeparateSpan) {\r\n Utils.createDomElement('div', { className: 'slick-sort-indicator-numbered' }, header);\r\n }\r\n }\r\n\r\n this.trigger(this.onHeaderCellRendered, {\r\n node: header,\r\n column: m,\r\n grid: this\r\n });\r\n\r\n if (this._options.showHeaderRow) {\r\n const headerRowCell = Utils.createDomElement('div', { className: `ui-state-default slick-state-default slick-headerrow-column l${i} r${i}` }, headerRowTarget);\r\n const frozenClasses = this.hasFrozenColumns() && i <= this._options.frozenColumn! ? 'frozen' : null;\r\n if (frozenClasses) {\r\n headerRowCell.classList.add(frozenClasses);\r\n }\r\n\r\n this._bindingEventService.bind(headerRowCell, 'mouseenter', this.handleHeaderRowMouseEnter.bind(this) as EventListener);\r\n this._bindingEventService.bind(headerRowCell, 'mouseleave', this.handleHeaderRowMouseLeave.bind(this) as EventListener);\r\n\r\n Utils.storage.put(headerRowCell, 'column', m);\r\n\r\n this.trigger(this.onHeaderRowCellRendered, {\r\n node: headerRowCell,\r\n column: m,\r\n grid: this\r\n });\r\n }\r\n if (this._options.createFooterRow && this._options.showFooterRow) {\r\n const footerRowTarget = this.hasFrozenColumns() ? ((i <= this._options.frozenColumn!) ? this._footerRow[0] : this._footerRow[1]) : this._footerRow[0];\r\n const footerRowCell = Utils.createDomElement('div', { className: `ui-state-default slick-state-default slick-footerrow-column l${i} r${i}` }, footerRowTarget);\r\n Utils.storage.put(footerRowCell, 'column', m);\r\n\r\n this.trigger(this.onFooterRowCellRendered, {\r\n node: footerRowCell,\r\n column: m,\r\n grid: this\r\n });\r\n }\r\n }\r\n\r\n this.setSortColumns(this.sortColumns);\r\n this.setupColumnResize();\r\n if (this._options.enableColumnReorder) {\r\n if (typeof this._options.enableColumnReorder === 'function') {\r\n this._options.enableColumnReorder(this as unknown as SlickGridModel, this._headers, this.headerColumnWidthDiff, this.setColumns as any, this.setupColumnResize, this.columns, this.getColumnIndex, this.uid, this.trigger);\r\n } else {\r\n this.setupColumnReorder();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Destroys any existing sortable instances and creates new ones on the left and right header\r\n * containers using the Sortable library. Configures options including animation,\r\n * drag handle selectors, auto-scroll, and callbacks (onStart, onEnd) that\r\n * update the column order, set columns, trigger onColumnsReordered, and reapply column resizing.\r\n */\r\n protected setupColumnReorder() {\r\n this.sortableSideLeftInstance?.destroy();\r\n this.sortableSideRightInstance?.destroy();\r\n\r\n let columnScrollTimer: any = null;\r\n\r\n const scrollColumnsRight = () => this._viewportScrollContainerX.scrollLeft = this._viewportScrollContainerX.scrollLeft + 10;\r\n const scrollColumnsLeft = () => this._viewportScrollContainerX.scrollLeft = this._viewportScrollContainerX.scrollLeft - 10;\r\n\r\n let canDragScroll = false;\r\n const sortableOptions = {\r\n animation: 50,\r\n direction: 'horizontal',\r\n chosenClass: 'slick-header-column-active',\r\n ghostClass: 'slick-sortable-placeholder',\r\n draggable: '.slick-header-column',\r\n dragoverBubble: false,\r\n revertClone: true,\r\n scroll: !this.hasFrozenColumns(), // enable auto-scroll\r\n // lock unorderable columns by using a combo of filter + onMove\r\n filter: `.${this._options.unorderableColumnCssClass}`,\r\n onMove: (event: MouseEvent & { related: HTMLElement; }) => {\r\n return !event.related.classList.contains(this._options.unorderableColumnCssClass as string);\r\n },\r\n onStart: (e: SortableEvent) => {\r\n e.item.classList.add('slick-header-column-active');\r\n canDragScroll = !this.hasFrozenColumns() || Utils.offset(e.item)!.left > Utils.offset(this._viewportScrollContainerX)!.left;\r\n\r\n if (canDragScroll && e.originalEvent.pageX > this._container.clientWidth) {\r\n if (!(columnScrollTimer)) {\r\n columnScrollTimer = window.setInterval(scrollColumnsRight, 100);\r\n }\r\n } else if (canDragScroll && e.originalEvent.pageX < Utils.offset(this._viewportScrollContainerX)!.left) {\r\n if (!(columnScrollTimer)) {\r\n columnScrollTimer = window.setInterval(scrollColumnsLeft, 100);\r\n }\r\n } else {\r\n window.clearInterval(columnScrollTimer);\r\n columnScrollTimer = null;\r\n }\r\n },\r\n onEnd: (e: SortableEvent) => {\r\n e.item.classList.remove('slick-header-column-active');\r\n window.clearInterval(columnScrollTimer);\r\n columnScrollTimer = null;\r\n\r\n if (!this.getEditorLock()?.commitCurrentEdit()) {\r\n return;\r\n }\r\n\r\n let reorderedIds = this.sortableSideLeftInstance?.toArray() ?? [];\r\n reorderedIds = reorderedIds.concat(this.sortableSideRightInstance?.toArray() ?? []);\r\n\r\n const reorderedColumns: C[] = [];\r\n for (let i = 0; i < reorderedIds.length; i++) {\r\n reorderedColumns.push(this.columns[this.getColumnIndex(reorderedIds[i])]);\r\n }\r\n this.setColumns(reorderedColumns);\r\n\r\n this.trigger(this.onColumnsReordered, { impactedColumns: this.columns });\r\n e.stopPropagation();\r\n this.setupColumnResize();\r\n if (this.activeCellNode) {\r\n this.setFocus(); // refocus on active cell\r\n }\r\n }\r\n } as SortableOptions;\r\n\r\n this.sortableSideLeftInstance = Sortable.create(this._headerL, sortableOptions);\r\n this.sortableSideRightInstance = Sortable.create(this._headerR, sortableOptions);\r\n }\r\n\r\n /**\r\n * Returns a concatenated array containing the children (header column elements) from both the left and right header containers.\r\n * @returns {HTMLElement[]} - An array of header column elements.\r\n */\r\n protected getHeaderChildren() {\r\n const a = Array.from(this._headers[0].children);\r\n const b = Array.from(this._headers[1].children);\r\n return a.concat(b) as HTMLElement[];\r\n }\r\n\r\n /**\r\n * When a resizable handle is double\u2013clicked, extracts the column identifier from the parent element\u2019s id\r\n * (by removing the grid uid) and triggers the onColumnsResizeDblClick event with that identifier.\r\n * @param {MouseEvent & { target: HTMLDivElement }} evt - The double-click event on the resizable handle.\r\n */\r\n protected handleResizeableDoubleClick(evt: MouseEvent & { target: HTMLDivElement; }) {\r\n const triggeredByColumn = evt.target.parentElement!.id.replace(this.uid, '');\r\n this.trigger(this.onColumnsResizeDblClick, { triggeredByColumn });\r\n }\r\n\r\n /**\r\n * Ensures the Resizable module is available and then iterates over header children to remove\r\n * any existing resizable handles. Determines which columns are resizable (tracking the first\r\n * and last resizable columns) and for each eligible column, creates a resizable handle,\r\n * binds a double\u2013click event, and creates a Resizable instance with callbacks for onResizeStart,\r\n * onResize, and onResizeEnd. These callbacks manage column width adjustments (including force\u2013fit\r\n * and frozen column considerations), update header and canvas widths, trigger related events,\r\n * and re\u2013render the grid as needed.\r\n * @returns {void}\r\n */\r\n protected setupColumnResize() {\r\n if (typeof Resizable === 'undefined') {\r\n throw new Error(`Slick.Resizable is undefined, make sure to import \"slick.interactions.js\"`);\r\n }\r\n\r\n let j: number;\r\n let k: number;\r\n let c: C;\r\n let pageX: number;\r\n let minPageX: number;\r\n let maxPageX: number;\r\n let firstResizable: number | undefined;\r\n let lastResizable = -1;\r\n let frozenLeftColMaxWidth = 0;\r\n\r\n const children: HTMLElement[] = this.getHeaderChildren();\r\n const vc = this.getVisibleColumns();\r\n for (let i = 0; i < children.length; i++) {\r\n const child = children[i];\r\n const handles = child.querySelectorAll('.slick-resizable-handle');\r\n handles.forEach((handle) => handle.remove());\r\n\r\n if (i >= vc.length || !vc[i]) {\r\n continue;\r\n }\r\n\r\n if (vc[i].resizable) {\r\n if (firstResizable === undefined) {\r\n firstResizable = i;\r\n }\r\n lastResizable = i;\r\n }\r\n }\r\n\r\n if (firstResizable === undefined) {\r\n return;\r\n }\r\n\r\n for (let i = 0; i < children.length; i++) {\r\n const colElm = children[i];\r\n\r\n if (i >= vc.length || !vc[i]) {\r\n continue;\r\n }\r\n if (i < firstResizable || (this._options.forceFitColumns && i >= lastResizable)) {\r\n continue;\r\n }\r\n\r\n const resizeableHandle = Utils.createDomElement('div', { className: 'slick-resizable-handle', role: 'separator', ariaOrientation: 'horizontal' }, colElm);\r\n this._bindingEventService.bind(resizeableHandle, 'dblclick', this.handleResizeableDoubleClick.bind(this) as EventListener);\r\n\r\n this.slickResizableInstances.push(\r\n Resizable({\r\n resizeableElement: colElm as HTMLElement,\r\n resizeableHandleElement: resizeableHandle,\r\n onResizeStart: (e, resizeElms): boolean | void => {\r\n const targetEvent = (e as TouchEvent).touches ? (e as TouchEvent).changedTouches[0] : e;\r\n if (!this.getEditorLock()?.commitCurrentEdit()) {\r\n return false;\r\n }\r\n pageX = (targetEvent as MouseEvent).pageX;\r\n frozenLeftColMaxWidth = 0;\r\n resizeElms.resizeableElement.classList.add('slick-header-column-active');\r\n let shrinkLeewayOnRight: number | null = null;\r\n let stretchLeewayOnRight: number | null = null;\r\n // lock each column's width option to current width\r\n for (let pw = 0; pw < children.length; pw++) {\r\n if (pw >= vc.length || !vc[pw]) {\r\n continue;\r\n }\r\n vc[pw].previousWidth = children[pw].offsetWidth;\r\n }\r\n if (this._options.forceFitColumns) {\r\n shrinkLeewayOnRight = 0;\r\n stretchLeewayOnRight = 0;\r\n // colums on right affect maxPageX/minPageX\r\n for (j = i + 1; j < vc.length; j++) {\r\n c = vc[j];\r\n if (c?.resizable) {\r\n if (stretchLeewayOnRight !== null) {\r\n if (c.maxWidth) {\r\n stretchLeewayOnRight += c.maxWidth - (c.previousWidth || 0);\r\n } else {\r\n stretchLeewayOnRight = null;\r\n }\r\n }\r\n shrinkLeewayOnRight += (c.previousWidth || 0) - Math.max(c.minWidth || 0, this.absoluteColumnMinWidth);\r\n }\r\n }\r\n }\r\n let shrinkLeewayOnLeft = 0;\r\n let stretchLeewayOnLeft: number | null = 0;\r\n for (j = 0; j <= i; j++) {\r\n // columns on left only affect minPageX\r\n c = vc[j];\r\n if (c?.resizable) {\r\n if (stretchLeewayOnLeft !== null) {\r\n if (c.maxWidth) {\r\n stretchLeewayOnLeft += c.maxWidth - (c.previousWidth || 0);\r\n } else {\r\n stretchLeewayOnLeft = null;\r\n }\r\n }\r\n shrinkLeewayOnLeft += (c.previousWidth || 0) - Math.max(c.minWidth || 0, this.absoluteColumnMinWidth);\r\n }\r\n }\r\n if (shrinkLeewayOnRight === null) {\r\n shrinkLeewayOnRight = 100000;\r\n }\r\n if (shrinkLeewayOnLeft === null) {\r\n shrinkLeewayOnLeft = 100000;\r\n }\r\n if (stretchLeewayOnRight === null) {\r\n stretchLeewayOnRight = 100000;\r\n }\r\n if (stretchLeewayOnLeft === null) {\r\n stretchLeewayOnLeft = 100000;\r\n }\r\n maxPageX = pageX + Math.min(shrinkLeewayOnRight, stretchLeewayOnLeft);\r\n minPageX = pageX - Math.min(shrinkLeewayOnLeft, stretchLeewayOnRight);\r\n },\r\n onResize: (e, resizeElms) => {\r\n const targetEvent = (e as TouchEvent).touches ? (e as TouchEvent).changedTouches[0] : e;\r\n this.columnResizeDragging = true;\r\n let actualMinWidth;\r\n const d = Math.min(maxPageX, Math.max(minPageX, (targetEvent as MouseEvent).pageX)) - pageX;\r\n let x;\r\n let newCanvasWidthL = 0;\r\n let newCanvasWidthR = 0;\r\n const viewportWidth = this.getViewportInnerWidth();\r\n\r\n if (d < 0) { // shrink column\r\n x = d;\r\n\r\n for (j = i; j >= 0; j--) {\r\n c = vc[j];\r\n if (c?.resizable && !c.hidden) {\r\n actualMinWidth = Math.max(c.minWidth || 0, this.absoluteColumnMinWidth);\r\n if (x && (c.previousWidth || 0) + x < actualMinWidth) {\r\n x += (c.previousWidth || 0) - actualMinWidth;\r\n c.width = actualMinWidth;\r\n } else {\r\n c.width = (c.previousWidth || 0) + x;\r\n x = 0;\r\n }\r\n }\r\n }\r\n\r\n for (k = 0; k <= i; k++) {\r\n c = vc[k];\r\n if (!c || c.hidden) { continue; }\r\n\r\n if (this.hasFrozenColumns() && (k > this._options.frozenColumn!)) {\r\n newCanvasWidthR += c.width || 0;\r\n } else {\r\n newCanvasWidthL += c.width || 0;\r\n }\r\n }\r\n\r\n if (this._options.forceFitColumns) {\r\n x = -d;\r\n for (j = i + 1; j < vc.length; j++) {\r\n c = vc[j];\r\n if (!c || c.hidden) { continue; }\r\n if (c.resizable) {\r\n if (x && c.maxWidth && (c.maxWidth - (c.previousWidth || 0) < x)) {\r\n x -= c.maxWidth - (c.previousWidth || 0);\r\n c.width = c.maxWidth;\r\n } else {\r\n c.width = (c.previousWidth || 0) + x;\r\n x = 0;\r\n }\r\n\r\n if (this.hasFrozenColumns() && (j > this._options.frozenColumn!)) {\r\n newCanvasWidthR += c.width || 0;\r\n } else {\r\n newCanvasWidthL += c.width || 0;\r\n }\r\n }\r\n }\r\n } else {\r\n for (j = i + 1; j < vc.length; j++) {\r\n c = vc[j];\r\n if (!c || c.hidden) { continue; }\r\n\r\n if (this.hasFrozenColumns() && (j > this._options.frozenColumn!)) {\r\n newCanvasWidthR += c.width || 0;\r\n } else {\r\n newCanvasWidthL += c.width || 0;\r\n }\r\n }\r\n }\r\n\r\n if (this._options.forceFitColumns) {\r\n x = -d;\r\n for (j = i + 1; j < vc.length; j++) {\r\n c = vc[j];\r\n if (!c || c.hidden) { continue; }\r\n if (c.resizable) {\r\n if (x && c.maxWidth && (c.maxWidth - (c.previousWidth || 0) < x)) {\r\n x -= c.maxWidth - (c.previousWidth || 0);\r\n c.width = c.maxWidth;\r\n } else {\r\n c.width = (c.previousWidth || 0) + x;\r\n x = 0;\r\n }\r\n }\r\n }\r\n }\r\n } else { // stretch column\r\n x = d;\r\n\r\n newCanvasWidthL = 0;\r\n newCanvasWidthR = 0;\r\n\r\n for (j = i; j >= 0; j--) {\r\n c = vc[j];\r\n if (!c || c.hidden) { continue; }\r\n if (c.resizable) {\r\n if (x && c.maxWidth && (c.maxWidth - (c.previousWidth || 0) < x)) {\r\n x -= c.maxWidth - (c.previousWidth || 0);\r\n c.width = c.maxWidth;\r\n } else {\r\n const newWidth = (c.previousWidth || 0) + x;\r\n const resizedCanvasWidthL = this.canvasWidthL + x;\r\n\r\n if (this.hasFrozenColumns() && (j <= this._options.frozenColumn!)) {\r\n // if we're on the left frozen side, we need to make sure that our left section width never goes over the total viewport width\r\n if (newWidth > frozenLeftColMaxWidth && resizedCanvasWidthL < (viewportWidth - this._options.frozenRightViewportMinWidth!)) {\r\n frozenLeftColMaxWidth = newWidth; // keep max column width ref, if we go over the limit this number will stop increasing\r\n }\r\n c.width = ((resizedCanvasWidthL + this._options.frozenRightViewportMinWidth!) > viewportWidth) ? frozenLeftColMaxWidth : newWidth;\r\n } else {\r\n c.width = newWidth;\r\n }\r\n x = 0;\r\n }\r\n }\r\n }\r\n\r\n for (k = 0; k <= i; k++) {\r\n c = vc[k];\r\n if (!c || c.hidden) { continue; }\r\n\r\n if (this.hasFrozenColumns() && (k > this._options.frozenColumn!)) {\r\n newCanvasWidthR += c.width || 0;\r\n } else {\r\n newCanvasWidthL += c.width || 0;\r\n }\r\n }\r\n\r\n if (this._options.forceFitColumns) {\r\n x = -d;\r\n for (j = i + 1; j < vc.length; j++) {\r\n c = vc[j];\r\n if (!c || c.hidden) { continue; }\r\n if (c.resizable) {\r\n actualMinWidth = Math.max(c.minWidth || 0, this.absoluteColumnMinWidth);\r\n if (x && (c.previousWidth || 0) + x < actualMinWidth) {\r\n x += (c.previousWidth || 0) - actualMinWidth;\r\n c.width = actualMinWidth;\r\n } else {\r\n c.width = (c.previousWidth || 0) + x;\r\n x = 0;\r\n }\r\n\r\n if (this.hasFrozenColumns() && (j > this._options.frozenColumn!)) {\r\n newCanvasWidthR += c.width || 0;\r\n } else {\r\n newCanvasWidthL += c.width || 0;\r\n }\r\n }\r\n }\r\n } else {\r\n for (j = i + 1; j < vc.length; j++) {\r\n c = vc[j];\r\n if (!c || c.hidden) { continue; }\r\n\r\n if (this.hasFrozenColumns() && (j > this._options.frozenColumn!)) {\r\n // eslint-disable-next-line @typescript-eslint/no-unused-vars\r\n newCanvasWidthR += c.width || 0;\r\n } else {\r\n newCanvasWidthL += c.width || 0;\r\n }\r\n }\r\n }\r\n }\r\n\r\n if (this.hasFrozenColumns() && newCanvasWidthL !== this.canvasWidthL) {\r\n Utils.width(this._headerL, newCanvasWidthL + 1000);\r\n Utils.setStyleSize(this._paneHeaderR, 'left', newCanvasWidthL);\r\n }\r\n\r\n this.applyColumnHeaderWidths();\r\n if (this._options.syncColumnCellResize) {\r\n this.applyColumnWidths();\r\n }\r\n this.trigger(this.onColumnsDrag, {\r\n triggeredByColumn: resizeElms.resizeableElement,\r\n resizeHandle: resizeElms.resizeableHandleElement\r\n });\r\n },\r\n onResizeEnd: (_e, resizeElms) => {\r\n resizeElms.resizeableElement.classList.remove('slick-header-column-active');\r\n\r\n const triggeredByColumn = resizeElms.resizeableElement.id.replace(this.uid, '');\r\n if (this.trigger(this.onBeforeColumnsResize, { triggeredByColumn }).getReturnValue() === true) {\r\n this.applyColumnHeaderWidths();\r\n }\r\n let newWidth;\r\n for (j = 0; j < vc.length; j++) {\r\n c = vc[j];\r\n if (!c || c.hidden) { continue; }\r\n newWidth = children[j].offsetWidth;\r\n\r\n if (c.previousWidth !== newWidth && c.rerenderOnResize) {\r\n this.invalidateAllRows();\r\n }\r\n }\r\n this.updateCanvasWidth(true);\r\n this.render();\r\n this.trigger(this.onColumnsResized, { triggeredByColumn });\r\n window.clearTimeout(this._columnResizeTimer);\r\n this._columnResizeTimer = window.setTimeout(() => { this.columnResizeDragging = false; }, 300);\r\n }\r\n })\r\n );\r\n }\r\n }\r\n\r\n /**\r\n * Validates and sets the frozenColumn option (ensuring it is within valid bounds, or setting it to \u20131)\r\n * and, if a frozenRow is specified (greater than \u20131), sets the grid\u2019s frozen\u2013row flags,\r\n * computes the frozenRowsHeight (based on rowHeight), and determines the actual frozen row index\r\n * depending on whether frozenBottom is enabled.\r\n */\r\n protected setFrozenOptions() {\r\n this._options.frozenColumn = (this._options.frozenColumn! >= 0 && this._options.frozenColumn! < this.columns.length)\r\n ? parseInt(this._options.frozenColumn as unknown as string, 10)\r\n : -1;\r\n\r\n if (this._options.frozenRow! > -1) {\r\n this.hasFrozenRows = true;\r\n this.frozenRowsHeight = (this._options.frozenRow!) * this._options.rowHeight!;\r\n const dataLength = this.getDataLength();\r\n\r\n this.actualFrozenRow = (this._options.frozenBottom)\r\n ? (dataLength - this._options.frozenRow!)\r\n : this._options.frozenRow!;\r\n } else {\r\n this.hasFrozenRows = false;\r\n }\r\n }\r\n\r\n //////////////////////////////////////////////////////////////////////////////////////////////\r\n // Column Management - Autosizing\r\n //////////////////////////////////////////////////////////////////////////////////////////////\r\n\r\n /**\r\n * Proportionally resize a specific column by its name, index or Id\r\n *\r\n * Resizes based on its content, but determines the column definition from the provided identifier or index.\r\n * Then, obtains a grid canvas and calls getColAutosizeWidth to compute and update the column\u2019s width.\r\n */\r\n autosizeColumn(columnOrIndexOrId: number | string, isInit?: boolean) {\r\n let colDef: C | null = null;\r\n let colIndex = -1;\r\n if (typeof columnOrIndexOrId === 'number') {\r\n colDef = this.columns[columnOrIndexOrId];\r\n colIndex = columnOrIndexOrId;\r\n } else if (typeof columnOrIndexOrId === 'string') {\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (this.columns[i].id === columnOrIndexOrId) { colDef = this.columns[i]; colIndex = i; }\r\n }\r\n }\r\n if (!colDef) {\r\n return;\r\n }\r\n const gridCanvas = this.getCanvasNode(0, 0) as HTMLElement;\r\n this.getColAutosizeWidth(colDef, colIndex, gridCanvas, isInit || false, colIndex);\r\n }\r\n\r\n /**\r\n * Returns true if the column should be treated as locked (i.e. not resized) based on autosize settings.\r\n * The decision is based on whether header text is not ignored, sizeToRemaining is false,\r\n * content size equals header width, and the current width is less than 100 pixels.\r\n *\r\n * @param {AutoSize} [autoSize={}] - The autosize configuration for the column.\r\n * @returns {boolean} - Returns `true` if the column should be treated as locked, otherwise `false`.\r\n */\r\n protected treatAsLocked(autoSize: AutoSize = {}): boolean {\r\n // treat as locked (don't resize) if small and header is the widest part\r\n return !autoSize.ignoreHeaderText\r\n && !autoSize.sizeToRemaining\r\n && (autoSize.contentSizePx === autoSize.headerWidthPx)\r\n && ((autoSize.widthPx ?? 0) < 100);\r\n }\r\n\r\n /** Proportionately resizes all columns to fill available horizontal space.\r\n * This does not take the cell contents into consideration.\r\n *\r\n * It does this by temporarily caching CSS for hidden containers, calling the internal autosizing logic\r\n * (internalAutosizeColumns) with the autosize mode and initialisation flag,\r\n * then restores the original CSS.\r\n */\r\n autosizeColumns(autosizeMode?: string, isInit?: boolean) {\r\n const checkHiddenParents = !(this._hiddenParents?.length);\r\n if (checkHiddenParents) {\r\n this.cacheCssForHiddenInit();\r\n }\r\n this.internalAutosizeColumns(autosizeMode, isInit);\r\n if (checkHiddenParents) {\r\n this.restoreCssFromHiddenInit();\r\n }\r\n }\r\n\r\n /**\r\n * Implements the main autosizing algorithm. Depending on the autosize mode,\r\n * it may call legacyAutosizeColumns (for legacy force\u2013fit modes), or proceed\r\n * to compute column widths based on available viewport width. It iterates over columns\r\n * to accumulate total widths, locked widths, and then adjusts widths proportionally.\r\n * Finally, it calls reRenderColumns to update the grid.\r\n *\r\n * @param {string} [autosizeMode] - The autosize mode. If undefined, defaults to `autosizeColsMode` from options.\r\n * @param {boolean} [isInit] - If `true`, applies initial settings for autosizing.\r\n */\r\n protected internalAutosizeColumns(autosizeMode?: string, isInit?: boolean) {\r\n // LogColWidths();\r\n autosizeMode = autosizeMode || this._options.autosizeColsMode;\r\n if (autosizeMode === GridAutosizeColsMode.LegacyForceFit || autosizeMode === GridAutosizeColsMode.LegacyOff) {\r\n this.legacyAutosizeColumns();\r\n return;\r\n }\r\n\r\n if (autosizeMode === GridAutosizeColsMode.None) {\r\n return;\r\n }\r\n\r\n // test for brower canvas support, canvas_context!=null if supported\r\n this.canvas = document.createElement('canvas');\r\n if (this.canvas?.getContext) { this.canvas_context = this.canvas.getContext('2d'); }\r\n\r\n // pass in the grid canvas\r\n const gridCanvas = this.getCanvasNode(0, 0) as HTMLElement;\r\n const viewportWidth = this.getViewportInnerWidth();\r\n\r\n // iterate columns to get autosizes\r\n let i: number;\r\n let c: C;\r\n let colWidth: number;\r\n let reRender = false;\r\n let totalWidth = 0;\r\n let totalWidthLessSTR = 0;\r\n let strColsMinWidth = 0;\r\n let totalMinWidth = 0;\r\n let totalLockedColWidth = 0;\r\n for (i = 0; i < this.columns.length; i++) {\r\n c = this.columns[i];\r\n this.getColAutosizeWidth(c, i, gridCanvas, isInit || false, i);\r\n totalLockedColWidth += (c.autoSize?.autosizeMode === ColAutosizeMode.Locked ? (c.width || 0) : (this.treatAsLocked(c.autoSize) ? c.autoSize?.widthPx || 0 : 0));\r\n totalMinWidth += (c.autoSize?.autosizeMode === ColAutosizeMode.Locked ? (c.width || 0) : (this.treatAsLocked(c.autoSize) ? c.autoSize?.widthPx || 0 : c.minWidth || 0));\r\n totalWidth += (c.autoSize?.widthPx || 0);\r\n totalWidthLessSTR += (c.autoSize?.sizeToRemaining ? 0 : c.autoSize?.widthPx || 0);\r\n strColsMinWidth += (c.autoSize?.sizeToRemaining ? c.minWidth || 0 : 0);\r\n }\r\n const strColTotalGuideWidth = totalWidth - totalWidthLessSTR;\r\n\r\n if (autosizeMode === GridAutosizeColsMode.FitViewportToCols) {\r\n // - if viewport with is outside MinViewportWidthPx and MaxViewportWidthPx, then the viewport is set to\r\n // MinViewportWidthPx or MaxViewportWidthPx and the FitColsToViewport algorithm is used\r\n // - viewport is resized to fit columns\r\n let setWidth = totalWidth + (this.scrollbarDimensions?.width ?? 0);\r\n autosizeMode = GridAutosizeColsMode.IgnoreViewport;\r\n\r\n if (this._options.viewportMaxWidthPx && setWidth > this._options.viewportMaxWidthPx) {\r\n setWidth = this._options.viewportMaxWidthPx;\r\n autosizeMode = GridAutosizeColsMode.FitColsToViewport;\r\n } else if (this._options.viewportMinWidthPx && setWidth < this._options.viewportMinWidthPx) {\r\n setWidth = this._options.viewportMinWidthPx;\r\n autosizeMode = GridAutosizeColsMode.FitColsToViewport;\r\n } else {\r\n // falling back to IgnoreViewport will size the columns as-is, with render checking\r\n // for (i = 0; i < columns.length; i++) { columns[i].width = columns[i].autoSize.widthPx; }\r\n }\r\n Utils.width(this._container, setWidth);\r\n }\r\n\r\n if (autosizeMode === GridAutosizeColsMode.FitColsToViewport) {\r\n if (strColTotalGuideWidth > 0 && totalWidthLessSTR < viewportWidth - strColsMinWidth) {\r\n // if addl space remains in the viewport and there are SizeToRemaining cols, just the SizeToRemaining cols expand proportionally to fill viewport\r\n for (i = 0; i < this.columns.length; i++) {\r\n c = this.columns[i];\r\n if (!c || c.hidden) { continue; }\r\n\r\n const totalSTRViewportWidth = viewportWidth - totalWidthLessSTR;\r\n if (c.autoSize?.sizeToRemaining) {\r\n colWidth = totalSTRViewportWidth * (c.autoSize?.widthPx || 0) / strColTotalGuideWidth;\r\n } else {\r\n colWidth = (c.autoSize?.widthPx || 0);\r\n }\r\n if (c.rerenderOnResize && (c.width || 0) !== colWidth) {\r\n reRender = true;\r\n }\r\n c.width = colWidth;\r\n }\r\n } else if ((this._options.viewportSwitchToScrollModeWidthPercent && totalWidthLessSTR + strColsMinWidth > viewportWidth * this._options.viewportSwitchToScrollModeWidthPercent / 100)\r\n || (totalMinWidth > viewportWidth)) {\r\n // if the total columns width is wider than the viewport by switchToScrollModeWidthPercent, switch to IgnoreViewport mode\r\n autosizeMode = GridAutosizeColsMode.IgnoreViewport;\r\n } else {\r\n // otherwise (ie. no SizeToRemaining cols or viewport smaller than columns) all cols other than 'Locked' scale in proportion to fill viewport\r\n // and SizeToRemaining get minWidth\r\n let unallocatedColWidth = totalWidthLessSTR - totalLockedColWidth;\r\n let unallocatedViewportWidth = viewportWidth - totalLockedColWidth - strColsMinWidth;\r\n for (i = 0; i < this.columns.length; i++) {\r\n c = this.columns[i];\r\n if (!c || c.hidden) { continue; }\r\n\r\n colWidth = c.width || 0;\r\n if (c.autoSize?.autosizeMode !== ColAutosizeMode.Locked && !this.treatAsLocked(c.autoSize)) {\r\n if (c.autoSize?.sizeToRemaining) {\r\n colWidth = c.minWidth || 0;\r\n } else {\r\n // size width proportionally to free space (we know we have enough room due to the earlier calculations)\r\n colWidth = unallocatedViewportWidth / unallocatedColWidth * (c.autoSize?.widthPx || 0) - 1;\r\n if (colWidth < (c.minWidth || 0)) {\r\n colWidth = c.minWidth || 0;\r\n }\r\n\r\n // remove the just allocated widths from the allocation pool\r\n unallocatedColWidth -= (c.autoSize?.widthPx || 0);\r\n unallocatedViewportWidth -= colWidth;\r\n }\r\n }\r\n if (this.treatAsLocked(c.autoSize)) {\r\n colWidth = (c.autoSize?.widthPx || 0);\r\n if (colWidth < (c.minWidth || 0)) {\r\n colWidth = c.minWidth || 0;\r\n }\r\n }\r\n if (c.rerenderOnResize && c.width !== colWidth) {\r\n reRender = true;\r\n }\r\n c.width = colWidth;\r\n }\r\n }\r\n }\r\n\r\n if (autosizeMode === GridAutosizeColsMode.IgnoreViewport) {\r\n // just size columns as-is\r\n for (i = 0; i < this.columns.length; i++) {\r\n if (!this.columns[i] || this.columns[i].hidden) { continue; }\r\n\r\n colWidth = this.columns[i].autoSize?.widthPx || 0;\r\n if (this.columns[i].rerenderOnResize && this.columns[i].width !== colWidth) {\r\n reRender = true;\r\n }\r\n this.columns[i].width = colWidth;\r\n }\r\n }\r\n\r\n this.reRenderColumns(reRender);\r\n }\r\n\r\n /**\r\n * Calculates the ideal autosize width for a given column. First, it sets the default width from the column definition.\r\n * If the autosize mode is not Locked or Guide, then for ContentIntelligent mode it determines the column\u2019s data type\r\n * (handling booleans, numbers, strings, dates, moments) and adjusts autosize settings accordingly.\r\n * It then calls getColContentSize to compute the width needed by the content, applies an additional\r\n * percentage multiplier and padding, clamps to min/max widths, and if in ContentExpandOnly mode ensures\r\n * the width is at least the default width. The computed width is stored in autoSize.widthPx.\r\n *\r\n * @param {C} columnDef - The column definition containing autosize settings and constraints.\r\n * @param {number} colIndex - The index of the column within the grid.\r\n * @param {HTMLElement} gridCanvas - The grid's canvas element where temporary elements will be created.\r\n * @param {boolean} isInit - If `true`, applies initial settings for row selection mode.\r\n * @param {number} colArrayIndex - The index of the column in the column array (used for multi-column adjustments).\r\n */\r\n protected getColAutosizeWidth(columnDef: C, colIndex: number, gridCanvas: HTMLElement, isInit: boolean, colArrayIndex: number) {\r\n const autoSize = columnDef.autoSize as AutoSize;\r\n\r\n // set to width as default\r\n autoSize.widthPx = columnDef.width;\r\n if (autoSize.autosizeMode === ColAutosizeMode.Locked\r\n || autoSize.autosizeMode === ColAutosizeMode.Guide) {\r\n return;\r\n }\r\n\r\n const dl = this.getDataLength(); // getDataItem();\r\n const isoDateRegExp = new RegExp(/\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(\\.\\d{3})?Z/);\r\n\r\n // ContentIntelligent takes settings from column data type\r\n if (autoSize.autosizeMode === ColAutosizeMode.ContentIntelligent) {\r\n // default to column colDataTypeOf (can be used if initially there are no data rows)\r\n let colDataTypeOf = autoSize.colDataTypeOf;\r\n let colDataItem: any;\r\n if (dl > 0) {\r\n const tempRow = this.getDataItem(0);\r\n if (tempRow) {\r\n colDataItem = tempRow[columnDef.field as keyof TData];\r\n\r\n // check for dates in hiding\r\n if (isoDateRegExp.test(colDataItem)) { colDataItem = Date.parse(colDataItem); }\r\n\r\n colDataTypeOf = typeof colDataItem;\r\n if (colDataTypeOf === 'object') {\r\n if (colDataItem instanceof Date) { colDataTypeOf = 'date'; }\r\n if (typeof moment !== 'undefined' && colDataItem instanceof moment) { colDataTypeOf = 'moment'; }\r\n }\r\n }\r\n }\r\n if (colDataTypeOf === 'boolean') {\r\n autoSize.colValueArray = [true, false];\r\n }\r\n if (colDataTypeOf === 'number') {\r\n autoSize.valueFilterMode = ValueFilterMode.GetGreatestAndSub;\r\n autoSize.rowSelectionMode = RowSelectionMode.AllRows;\r\n }\r\n if (colDataTypeOf === 'string') {\r\n autoSize.valueFilterMode = ValueFilterMode.GetLongestText;\r\n autoSize.rowSelectionMode = RowSelectionMode.AllRows;\r\n autoSize.allowAddlPercent = 5;\r\n }\r\n if (colDataTypeOf === 'date') {\r\n autoSize.colValueArray = [new Date(2009, 8, 30, 12, 20, 20)]; // Sep 30th 2009, 12:20:20 AM\r\n }\r\n if (colDataTypeOf === 'moment' && typeof moment !== 'undefined') {\r\n autoSize.colValueArray = [moment([2009, 8, 30, 12, 20, 20])]; // Sep 30th 2009, 12:20:20 AM\r\n }\r\n }\r\n\r\n // at this point, the autosizeMode is effectively 'Content', so proceed to get size\r\n let colWidth = autoSize.contentSizePx = this.getColContentSize(columnDef, colIndex, gridCanvas, isInit, colArrayIndex);\r\n\r\n if (colWidth === 0) {\r\n colWidth = autoSize.widthPx || 0;\r\n }\r\n\r\n const addlPercentMultiplier = (autoSize.allowAddlPercent ? (1 + autoSize.allowAddlPercent / 100) : 1);\r\n colWidth = colWidth * addlPercentMultiplier + (this._options.autosizeColPaddingPx || 0);\r\n if (columnDef.minWidth && colWidth < columnDef.minWidth) { colWidth = columnDef.minWidth; }\r\n if (columnDef.maxWidth && colWidth > columnDef.maxWidth) { colWidth = columnDef.maxWidth; }\r\n\r\n if (autoSize.autosizeMode === ColAutosizeMode.ContentExpandOnly || ((columnDef?.editor as any)?.ControlFillsColumn)) {\r\n // only use content width if it's wider than the default column width (this is used for dropdowns and other fixed width controls)\r\n if (colWidth < (columnDef.width || 0)) {\r\n colWidth = columnDef.width || 0;\r\n }\r\n }\r\n autoSize.widthPx = colWidth;\r\n }\r\n\r\n /**\r\n * Determines the width needed to render a column\u2019s content. It first measures the header width (if not ignored)\r\n * and uses it as a baseline. If an explicit colValueArray is provided, it measures that; otherwise, it creates\r\n * a RowInfo object to select a range of rows based on the rowSelectionMode. Depending on the valueFilterMode\r\n * (e.g. DeDuplicate, GetGreatestAndSub, GetLongestTextAndSub, GetLongestText), it adjusts the values to measure.\r\n * It then calls getColWidth (using either canvas text measurement or DOM measurement) and returns the maximum\r\n * of the header width and computed content width (adjusted by a ratio, if applicable).\r\n *\r\n * @param {C} columnDef - The column definition containing formatting and auto-sizing options.\r\n * @param {number} colIndex - The index of the column within the grid.\r\n * @param {HTMLElement} gridCanvas - The grid's canvas element where temporary elements will be created.\r\n * @param {boolean} isInit - If `true`, applies initial row selection mode settings.\r\n * @param {number} colArrayIndex - The index of the column in the column array (used for multi-column adjustments).\r\n * @returns {number} - The computed optimal column width in pixels.\r\n */\r\n protected getColContentSize(columnDef: C, colIndex: number, gridCanvas: HTMLElement, isInit: boolean, colArrayIndex: number) {\r\n const autoSize = columnDef.autoSize as AutoSize;\r\n let widthAdjustRatio = 1;\r\n\r\n // at this point, the autosizeMode is effectively 'Content', so proceed to get size\r\n\r\n // get header width, if we are taking notice of it\r\n let i: number;\r\n let tempVal: any;\r\n let maxLen = 0;\r\n let maxColWidth = 0;\r\n autoSize.headerWidthPx = 0;\r\n if (!autoSize.ignoreHeaderText) {\r\n autoSize.headerWidthPx = this.getColHeaderWidth(columnDef);\r\n }\r\n if (autoSize.headerWidthPx === 0) {\r\n autoSize.headerWidthPx = (columnDef.width ? columnDef.width\r\n : (columnDef.maxWidth ? columnDef.maxWidth\r\n : (columnDef.minWidth ? columnDef.minWidth : 20)\r\n )\r\n );\r\n }\r\n\r\n if (autoSize.colValueArray) {\r\n // if an array of values are specified, just pass them in instead of data\r\n maxColWidth = this.getColWidth(columnDef, gridCanvas, autoSize.colValueArray as any);\r\n return Math.max(autoSize.headerWidthPx, maxColWidth);\r\n }\r\n\r\n // select rows to evaluate using rowSelectionMode and rowSelectionCount\r\n const rowInfo = {} as RowInfo;\r\n rowInfo.colIndex = colIndex;\r\n rowInfo.rowCount = this.getDataLength();\r\n rowInfo.startIndex = 0;\r\n rowInfo.endIndex = rowInfo.rowCount - 1;\r\n rowInfo.valueArr = null;\r\n rowInfo.getRowVal = (j: number) => this.getDataItem(j)[columnDef.field as keyof TData];\r\n\r\n const rowSelectionMode = (isInit ? autoSize.rowSelectionModeOnInit : undefined) || autoSize.rowSelectionMode;\r\n\r\n if (rowSelectionMode === RowSelectionMode.FirstRow) { rowInfo.endIndex = 0; }\r\n if (rowSelectionMode === RowSelectionMode.LastRow) { rowInfo.endIndex = rowInfo.startIndex = rowInfo.rowCount - 1; }\r\n if (rowSelectionMode === RowSelectionMode.FirstNRows) { rowInfo.endIndex = Math.min(autoSize.rowSelectionCount || 0, rowInfo.rowCount) - 1; }\r\n\r\n // now use valueFilterMode to further filter selected rows\r\n if (autoSize.valueFilterMode === ValueFilterMode.DeDuplicate) {\r\n const rowsDict: any = {};\r\n for (i = rowInfo.startIndex; i <= rowInfo.endIndex; i++) {\r\n rowsDict[rowInfo.getRowVal(i)] = true;\r\n }\r\n if (Object.keys) {\r\n rowInfo.valueArr = Object.keys(rowsDict);\r\n } else {\r\n rowInfo.valueArr = [];\r\n for (const v in rowsDict) {\r\n if (rowsDict) {\r\n rowInfo.valueArr.push(v);\r\n }\r\n }\r\n }\r\n rowInfo.startIndex = 0;\r\n rowInfo.endIndex = rowInfo.length - 1;\r\n }\r\n\r\n if (autoSize.valueFilterMode === ValueFilterMode.GetGreatestAndSub) {\r\n // get greatest abs value in data\r\n let maxVal;\r\n let maxAbsVal = 0;\r\n for (i = rowInfo.startIndex; i <= rowInfo.endIndex; i++) {\r\n tempVal = rowInfo.getRowVal(i);\r\n if (Math.abs(tempVal) > maxAbsVal) {\r\n maxVal = tempVal; maxAbsVal = Math.abs(tempVal);\r\n }\r\n }\r\n // now substitute a '9' for all characters (to get widest width) and convert back to a number\r\n maxVal = '' + maxVal;\r\n maxVal = Array(maxVal.length + 1).join('9');\r\n maxVal = +maxVal;\r\n\r\n rowInfo.valueArr = [maxVal];\r\n rowInfo.startIndex = rowInfo.endIndex = 0;\r\n }\r\n\r\n if (autoSize.valueFilterMode === ValueFilterMode.GetLongestTextAndSub) {\r\n // get greatest abs value in data\r\n for (i = rowInfo.startIndex; i <= rowInfo.endIndex; i++) {\r\n tempVal = rowInfo.getRowVal(i);\r\n if ((tempVal || '').length > maxLen) { maxLen = tempVal.length; }\r\n }\r\n // now substitute a 'm' for all characters\r\n tempVal = Array(maxLen + 1).join('m');\r\n widthAdjustRatio = this._options.autosizeTextAvgToMWidthRatio || 0;\r\n\r\n rowInfo.maxLen = maxLen;\r\n rowInfo.valueArr = [tempVal];\r\n rowInfo.startIndex = rowInfo.endIndex = 0;\r\n }\r\n\r\n if (autoSize.valueFilterMode === ValueFilterMode.GetLongestText) {\r\n // get greatest abs value in data\r\n maxLen = 0; let maxIndex = 0;\r\n for (i = rowInfo.startIndex; i <= rowInfo.endIndex; i++) {\r\n tempVal = rowInfo.getRowVal(i);\r\n if ((tempVal || '').length > maxLen) { maxLen = tempVal.length; maxIndex = i; }\r\n }\r\n // now substitute a 'c' for all characters\r\n tempVal = rowInfo.getRowVal(maxIndex);\r\n rowInfo.maxLen = maxLen;\r\n rowInfo.valueArr = [tempVal];\r\n rowInfo.startIndex = rowInfo.endIndex = 0;\r\n }\r\n\r\n // !!! HACK !!!!\r\n if (rowInfo.maxLen && rowInfo.maxLen > 30 && colArrayIndex > 1) { autoSize.sizeToRemaining = true; }\r\n maxColWidth = this.getColWidth(columnDef, gridCanvas, rowInfo) * widthAdjustRatio;\r\n return Math.max(autoSize.headerWidthPx, maxColWidth);\r\n }\r\n\r\n /**\r\n * Creates a temporary row and cell element (with absolute positioning, hidden visibility, and nowrap) and iterates\r\n * over the selected rows (as defined in a RowInfo object or provided value array) to render the cell content using\r\n * the column formatter. If in text-only mode and canvas measurement is enabled, uses canvas.measureText;\r\n * otherwise, uses DOM offsetWidth after applying the formatter result to the cell.\r\n * Returns the maximum measured width.\r\n *\r\n * @param {C} columnDef - The column definition containing formatting and auto-sizing options.\r\n * @param {HTMLElement} gridCanvas - The grid's canvas element where the temporary row will be added.\r\n * @param {RowInfo} rowInfo - Object containing row start/end indices and values for width evaluation.\r\n * @returns {number} - The computed optimal column width in pixels.\r\n */\r\n protected getColWidth(columnDef: C, gridCanvas: HTMLElement, rowInfo: RowInfo) {\r\n const rowEl = Utils.createDomElement('div', { className: 'slick-row ui-widget-content' }, gridCanvas);\r\n const cellEl = Utils.createDomElement('div', { className: 'slick-cell' }, rowEl);\r\n\r\n cellEl.style.position = 'absolute';\r\n cellEl.style.visibility = 'hidden';\r\n cellEl.style.textOverflow = 'initial';\r\n cellEl.style.whiteSpace = 'nowrap';\r\n\r\n let i: number;\r\n let len: number;\r\n let max = 0;\r\n let maxText = '';\r\n let formatterResult: string | FormatterResultWithHtml | FormatterResultWithText | HTMLElement | DocumentFragment;\r\n let val: any;\r\n\r\n // get mode - if text only display, use canvas otherwise html element\r\n let useCanvas = (columnDef.autoSize!.widthEvalMode === WidthEvalMode.TextOnly);\r\n\r\n if (columnDef.autoSize?.widthEvalMode === WidthEvalMode.Auto) {\r\n const noFormatter = !columnDef.formatterOverride && !columnDef.formatter;\r\n const formatterIsText = ((columnDef?.formatterOverride as { ReturnsTextOnly: boolean })?.ReturnsTextOnly)\r\n || (!columnDef.formatterOverride && (columnDef.formatter as any)?.ReturnsTextOnly);\r\n useCanvas = noFormatter || formatterIsText;\r\n }\r\n\r\n // use canvas - very fast, but text-only\r\n if (this.canvas_context && useCanvas) {\r\n const style = getComputedStyle(cellEl);\r\n this.canvas_context.font = style.fontSize + ' ' + style.fontFamily;\r\n for (i = rowInfo.startIndex; i <= rowInfo.endIndex; i++) {\r\n // row is either an array or values or a single value\r\n val = (rowInfo.valueArr ? rowInfo.valueArr[i] : rowInfo.getRowVal(i));\r\n if (columnDef.formatterOverride) {\r\n // use formatterOverride as first preference\r\n formatterResult = (columnDef.formatterOverride as FormatterOverrideCallback)(i, rowInfo.colIndex, val, columnDef, this.getDataItem(i), this as unknown as SlickGridModel);\r\n } else if (columnDef.formatter) {\r\n // otherwise, use formatter\r\n formatterResult = columnDef.formatter(i, rowInfo.colIndex, val, columnDef, this.getDataItem(i), this as unknown as SlickGridModel);\r\n } else {\r\n // otherwise, use plain text\r\n formatterResult = '' + val;\r\n }\r\n len = formatterResult ? this.canvas_context.measureText(formatterResult as string).width : 0;\r\n if (len > max) {\r\n max = len;\r\n maxText = formatterResult as string;\r\n }\r\n }\r\n\r\n cellEl.textContent = maxText;\r\n len = cellEl.offsetWidth;\r\n\r\n rowEl.remove();\r\n return len;\r\n }\r\n\r\n for (i = rowInfo.startIndex; i <= rowInfo.endIndex; i++) {\r\n val = (rowInfo.valueArr ? rowInfo.valueArr[i] : rowInfo.getRowVal(i));\r\n if (columnDef.formatterOverride) {\r\n // use formatterOverride as first preference\r\n formatterResult = (columnDef.formatterOverride as FormatterOverrideCallback)(i, rowInfo.colIndex, val, columnDef, this.getDataItem(i), this as unknown as SlickGridModel);\r\n } else if (columnDef.formatter) {\r\n // otherwise, use formatter\r\n formatterResult = columnDef.formatter(i, rowInfo.colIndex, val, columnDef, this.getDataItem(i), this as unknown as SlickGridModel);\r\n } else {\r\n // otherwise, use plain text\r\n formatterResult = '' + val;\r\n }\r\n this.applyFormatResultToCellNode(formatterResult, cellEl);\r\n len = cellEl.offsetWidth;\r\n if (len > max) { max = len; }\r\n }\r\n\r\n rowEl.remove();\r\n return max;\r\n }\r\n\r\n /**\r\n * Determines the width of a column header by first attempting to find the header element using an ID composed of the\r\n * grid\u2019s uid and the column\u2019s id. If found, clones the element, makes it absolutely positioned and hidden,\r\n * inserts it into the DOM, measures its offsetWidth, and then removes it. If the header element does not exist yet,\r\n * creates a temporary header element with the column\u2019s name and measures its width before removing it.\r\n * Returns the computed header width.\r\n *\r\n * @param {C} columnDef - The column definition containing the header information.\r\n * @returns {number} - The computed width of the column header in pixels.\r\n */\r\n protected getColHeaderWidth(columnDef: C) {\r\n let width = 0;\r\n // if (columnDef && (!columnDef.resizable || columnDef._autoCalcWidth === true)) { return; }\r\n const headerColElId = this.getUID() + columnDef.id;\r\n let headerColEl = document.getElementById(headerColElId) as HTMLElement;\r\n const dummyHeaderColElId = `${headerColElId}_`;\r\n const clone = headerColEl.cloneNode(true) as HTMLElement;\r\n if (headerColEl) {\r\n // headers have been created, use clone technique\r\n clone.id = dummyHeaderColElId;\r\n clone.style.cssText = 'position: absolute; visibility: hidden;right: auto;text-overflow: initial;white-space: nowrap;';\r\n headerColEl.parentNode!.insertBefore(clone, headerColEl);\r\n width = clone.offsetWidth;\r\n clone.parentNode!.removeChild(clone);\r\n } else {\r\n // headers have not yet been created, create a new node\r\n const header = this.getHeader(columnDef) as HTMLElement;\r\n headerColEl = Utils.createDomElement('div', { id: dummyHeaderColElId, className: 'ui-state-default slick-state-default slick-header-column' }, header);\r\n const colNameElm = Utils.createDomElement('span', { className: 'slick-column-name' }, headerColEl);\r\n this.applyHtmlCode(colNameElm, columnDef.name!);\r\n clone.style.cssText = 'position: absolute; visibility: hidden;right: auto;text-overflow: initial;white-space: nowrap;';\r\n if (columnDef.headerCssClass) {\r\n headerColEl.classList.add(...Utils.classNameToList(columnDef.headerCssClass));\r\n }\r\n width = headerColEl.offsetWidth;\r\n header.removeChild(headerColEl);\r\n }\r\n return width;\r\n }\r\n\r\n /**\r\n * Iterates over all columns to collect current widths (skipping hidden ones), calculates total width\r\n * and available shrink leeway, then enters a \u201Cshrink\u201D loop if the total width exceeds the available\r\n * viewport width and a \u201Cgrow\u201D loop if below. Finally, it applies the computed widths to the columns\r\n * and calls reRenderColumns (with a flag if any width changed) to update the grid.\r\n */\r\n protected legacyAutosizeColumns() {\r\n let i;\r\n let c: C | undefined;\r\n let shrinkLeeway = 0;\r\n let total = 0;\r\n let prevTotal = 0;\r\n const widths: number[] = [];\r\n const availWidth = this.getViewportInnerWidth();\r\n\r\n for (i = 0; i < this.columns.length; i++) {\r\n c = this.columns[i];\r\n if (!c || c.hidden) {\r\n widths.push(0);\r\n continue;\r\n }\r\n widths.push(c.width || 0);\r\n total += c.width || 0;\r\n if (c.resizable) {\r\n shrinkLeeway += (c.width || 0) - Math.max((c.minWidth || 0), this.absoluteColumnMinWidth);\r\n }\r\n }\r\n\r\n // shrink\r\n prevTotal = total;\r\n while (total > availWidth && shrinkLeeway) {\r\n const shrinkProportion = (total - availWidth) / shrinkLeeway;\r\n for (i = 0; i < this.columns.length && total > availWidth; i++) {\r\n c = this.columns[i];\r\n if (!c || c.hidden) { continue; }\r\n const width = widths[i];\r\n if (!c.resizable || width <= c.minWidth! || width <= this.absoluteColumnMinWidth) {\r\n continue;\r\n }\r\n const absMinWidth = Math.max(c.minWidth!, this.absoluteColumnMinWidth);\r\n let shrinkSize = Math.floor(shrinkProportion * (width - absMinWidth)) || 1;\r\n shrinkSize = Math.min(shrinkSize, width - absMinWidth);\r\n total -= shrinkSize;\r\n shrinkLeeway -= shrinkSize;\r\n widths[i] -= shrinkSize;\r\n }\r\n if (prevTotal <= total) { // avoid infinite loop\r\n break;\r\n }\r\n prevTotal = total;\r\n }\r\n\r\n // grow\r\n prevTotal = total;\r\n while (total < availWidth) {\r\n const growProportion = availWidth / total;\r\n for (i = 0; i < this.columns.length && total < availWidth; i++) {\r\n c = this.columns[i];\r\n if (!c || c.hidden) { continue; }\r\n const currentWidth = widths[i];\r\n let growSize;\r\n\r\n if (!c.resizable || c.maxWidth! <= currentWidth) {\r\n growSize = 0;\r\n } else {\r\n growSize = Math.min(Math.floor(growProportion * currentWidth) - currentWidth, (c.maxWidth! - currentWidth) || 1000000) || 1;\r\n }\r\n total += growSize;\r\n widths[i] += (total <= availWidth ? growSize : 0);\r\n }\r\n if (prevTotal >= total) { // avoid infinite loop\r\n break;\r\n }\r\n prevTotal = total;\r\n }\r\n\r\n let reRender = false;\r\n for (i = 0; i < this.columns.length; i++) {\r\n if (!c || c.hidden) { continue; }\r\n\r\n if (this.columns[i].rerenderOnResize && this.columns[i].width !== widths[i]) {\r\n reRender = true;\r\n }\r\n this.columns[i].width = widths[i];\r\n }\r\n\r\n this.reRenderColumns(reRender);\r\n }\r\n\r\n /**\r\n * Apply Columns Widths in the UI and optionally invalidate & re-render the columns when specified\r\n * @param {Boolean} shouldReRender - should we invalidate and re-render the grid?\r\n */\r\n reRenderColumns(reRender?: boolean) {\r\n this.applyColumnHeaderWidths();\r\n this.updateCanvasWidth(true);\r\n\r\n this.trigger(this.onAutosizeColumns, { columns: this.columns });\r\n\r\n if (reRender) {\r\n this.invalidateAllRows();\r\n this.render();\r\n }\r\n }\r\n\r\n /**\r\n * Returns an array of column definitions filtered to exclude any that are marked as hidden.\r\n *\r\n * @returns\r\n */\r\n getVisibleColumns() {\r\n return this.columns.filter(c => !c.hidden);\r\n }\r\n\r\n /**\r\n * Returns the index of a column with a given id. Since columns can be reordered by the user, this can be used to get the column definition independent of the order:\r\n * @param {String | Number} id A column id.\r\n */\r\n getColumnIndex(id: number | string): number {\r\n return this.columnsById[id];\r\n }\r\n\r\n /**\r\n * Iterates over the header elements (from both left and right headers) and updates each header\u2019s width based on the\r\n * corresponding visible column\u2019s width minus a computed adjustment (headerColumnWidthDiff).\r\n * Finally, it updates the internal column caches.\r\n *\r\n * @returns\r\n */\r\n protected applyColumnHeaderWidths() {\r\n if (!this.initialized) {\r\n return;\r\n }\r\n\r\n let columnIndex = 0;\r\n const vc = this.getVisibleColumns();\r\n this._headers.forEach((header) => {\r\n for (let i = 0; i < header.children.length; i++, columnIndex++) {\r\n const h = header.children[i] as HTMLElement;\r\n const col = vc[columnIndex] || {};\r\n const width = (col.width || 0) - this.headerColumnWidthDiff;\r\n if (Utils.width(h) !== width) {\r\n Utils.width(h, width);\r\n }\r\n }\r\n });\r\n\r\n this.updateColumnCaches();\r\n }\r\n\r\n /**\r\n * Iterates over all columns (skipping hidden ones) and, for each, retrieves the associated CSS rules\r\n * (using getColumnCssRules). It then sets the left and right CSS properties so that the columns align\r\n * correctly within the grid canvas. It also updates the cumulative offset for non\u2013frozen columns.\r\n */\r\n protected applyColumnWidths() {\r\n let x = 0;\r\n let w = 0;\r\n let rule: any;\r\n for (let i = 0; i < this.columns.length; i++) {\r\n if (!this.columns[i]?.hidden) {\r\n w = this.columns[i].width || 0;\r\n\r\n rule = this.getColumnCssRules(i);\r\n rule.left.style.left = `${x}px`;\r\n rule.right.style.right = (((this._options.frozenColumn !== -1 && i > this._options.frozenColumn!) ? this.canvasWidthR : this.canvasWidthL) - x - w) + 'px';\r\n\r\n // If this column is frozen, reset the css left value since the\r\n // column starts in a new viewport.\r\n if (this._options.frozenColumn !== i) {\r\n x += this.columns[i].width!;\r\n }\r\n }\r\n if (this._options.frozenColumn === i) {\r\n x = 0;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * A convenience method that creates a sort configuration for one column (with the given sort direction)\r\n * and calls setSortColumns with it. Accepts a columnId string and an ascending boolean.\r\n * Applies a sort glyph in either ascending or descending form to the header of the column.\r\n * Note that this does not actually sort the column. It only adds the sort glyph to the header.\r\n *\r\n * @param {String | Number} columnId\r\n * @param {Boolean} ascending\r\n */\r\n setSortColumn(columnId: number | string, ascending: boolean) {\r\n this.setSortColumns([{ columnId, sortAsc: ascending }]);\r\n }\r\n\r\n /**\r\n * Get column by index - iterates over header containers and returns the header column\r\n * element corresponding to the given index.\r\n *\r\n * @param {Number} id - column index\r\n * @returns\r\n */\r\n getColumnByIndex(id: number) {\r\n let result: HTMLElement | undefined;\r\n this._headers.every((header) => {\r\n const length = header.children.length;\r\n if (id < length) {\r\n result = header.children[id] as HTMLElement;\r\n return false;\r\n }\r\n id -= length;\r\n return true;\r\n });\r\n\r\n return result;\r\n }\r\n\r\n /**\r\n * Accepts an array of objects in the form [ { columnId: [string], sortAsc: [boolean] }, ... ] to\r\n * define the grid's sort order. When called, this will apply a sort glyph in either ascending\r\n * or descending form to the header of each column specified in the array.\r\n * Note that this does not actually sort the column. It only adds the sort glyph to the header.\r\n *\r\n * @param {ColumnSort[]} cols - column sort\r\n */\r\n setSortColumns(cols: ColumnSort[]) {\r\n this.sortColumns = cols;\r\n\r\n const numberCols = this._options.numberedMultiColumnSort && this.sortColumns.length > 1;\r\n this._headers.forEach((header) => {\r\n let indicators = header.querySelectorAll('.slick-header-column-sorted');\r\n indicators.forEach((indicator) => {\r\n indicator.classList.remove('slick-header-column-sorted');\r\n });\r\n\r\n indicators = header.querySelectorAll('.slick-sort-indicator');\r\n indicators.forEach((indicator) => {\r\n indicator.classList.remove('slick-sort-indicator-asc');\r\n indicator.classList.remove('slick-sort-indicator-desc');\r\n });\r\n indicators = header.querySelectorAll('.slick-sort-indicator-numbered');\r\n indicators.forEach((el) => {\r\n el.textContent = '';\r\n });\r\n });\r\n\r\n let i = 1;\r\n this.sortColumns.forEach((col) => {\r\n if (!Utils.isDefined(col.sortAsc)) {\r\n col.sortAsc = true;\r\n }\r\n\r\n const columnIndex = this.getColumnIndex(col.columnId);\r\n if (Utils.isDefined(columnIndex)) {\r\n const column = this.getColumnByIndex(columnIndex);\r\n if (column) {\r\n column.classList.add('slick-header-column-sorted');\r\n let indicator = column.querySelector('.slick-sort-indicator');\r\n indicator?.classList.add(col.sortAsc ? 'slick-sort-indicator-asc' : 'slick-sort-indicator-desc');\r\n\r\n if (numberCols) {\r\n indicator = column.querySelector('.slick-sort-indicator-numbered');\r\n if (indicator) {\r\n indicator.textContent = String(i);\r\n }\r\n }\r\n }\r\n }\r\n i++;\r\n });\r\n }\r\n\r\n /** Returns the current array of column definitions. */\r\n getColumns() {\r\n return this.columns;\r\n }\r\n\r\n /** Get sorted columns representing the current sorting state of the grid **/\r\n getSortColumns(): ColumnSort[] {\r\n return this.sortColumns;\r\n }\r\n\r\n /**\r\n * Iterates over all columns to compute and store their left and right boundaries\r\n * (based on cumulative widths). Resets the offset when a frozen column is encountered.\r\n */\r\n protected updateColumnCaches() {\r\n // Pre-calculate cell boundaries.\r\n this.columnPosLeft = [];\r\n this.columnPosRight = [];\r\n let x = 0;\r\n for (let i = 0, ii = this.columns.length; i < ii; i++) {\r\n if (!this.columns[i] || this.columns[i].hidden) { continue; }\r\n\r\n this.columnPosLeft[i] = x;\r\n this.columnPosRight[i] = x + (this.columns[i].width || 0);\r\n\r\n if (this._options.frozenColumn === i) {\r\n x = 0;\r\n } else {\r\n x += this.columns[i].width || 0;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Iterates over each column to (a) save its original width as widthRequest,\r\n * (b) apply default properties (using mixinDefaults if set) to both the column\r\n * and its autoSize property, (c) update the columnsById mapping, and (d) adjust\r\n * the width if it is less than minWidth or greater than maxWidth.\r\n */\r\n protected updateColumnProps() {\r\n this.columnsById = {};\r\n for (let i = 0; i < this.columns.length; i++) {\r\n let m: C = this.columns[i];\r\n if (m.width) {\r\n m.widthRequest = m.width;\r\n }\r\n\r\n if (this._options.mixinDefaults) {\r\n Utils.applyDefaults(m, this._columnDefaults);\r\n if (!m.autoSize) { m.autoSize = {}; }\r\n Utils.applyDefaults(m.autoSize, this._columnAutosizeDefaults);\r\n } else {\r\n m = this.columns[i] = Utils.extend({}, this._columnDefaults, m);\r\n m.autoSize = Utils.extend({}, this._columnAutosizeDefaults, m.autoSize);\r\n }\r\n\r\n this.columnsById[m.id] = i;\r\n if (m.minWidth && ((m.width || 0) < m.minWidth)) {\r\n m.width = m.minWidth;\r\n }\r\n if (m.maxWidth && ((m.width || 0) > m.maxWidth)) {\r\n m.width = m.maxWidth;\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Sets grid columns. Column headers will be recreated and all rendered rows will be removed.\r\n * To rerender the grid (if necessary), call render().\r\n * @param {Column[]} columnDefinitions An array of column definitions.\r\n */\r\n setColumns(columnDefinitions: C[]) {\r\n this.trigger(this.onBeforeSetColumns, { previousColumns: this.columns, newColumns: columnDefinitions, grid: this });\r\n this.columns = columnDefinitions;\r\n this.updateColumnsInternal();\r\n this.trigger(this.onAfterSetColumns, { newColumns: columnDefinitions, grid: this });\r\n }\r\n\r\n /** Update columns for when a hidden property has changed but the column list itself has not changed. */\r\n updateColumns() {\r\n this.trigger(this.onBeforeUpdateColumns, { columns: this.columns, grid: this });\r\n this.updateColumnsInternal();\r\n }\r\n\r\n /**\r\n * Triggers onBeforeUpdateColumns and calls updateColumnsInternal to update column properties,\r\n * caches, header/footer elements, CSS rules, canvas dimensions, and selections without changing the column array.\r\n */\r\n protected updateColumnsInternal() {\r\n this.updateColumnProps();\r\n this.updateColumnCaches();\r\n\r\n if (this.initialized) {\r\n this.setPaneFrozenClasses();\r\n this.setPaneVisibility();\r\n this.setOverflow();\r\n\r\n this.invalidateAllRows();\r\n this.createColumnHeaders();\r\n this.createColumnFooter();\r\n this.removeCssRules();\r\n this.createCssRules();\r\n this.resizeCanvas();\r\n this.updateCanvasWidth();\r\n this.applyColumnHeaderWidths();\r\n this.applyColumnWidths();\r\n this.handleScroll();\r\n this.getSelectionModel()?.refreshSelections();\r\n }\r\n }\r\n\r\n /////////////////////////////////////////////////////////////////////\r\n /// End Column Management\r\n /////////////////////////////////////////////////////////////////////\r\n\r\n /////////////////////////////////////////////////////////////////////\r\n /// Data Management and Editing\r\n /////////////////////////////////////////////////////////////////////\r\n\r\n /** Get Editor lock */\r\n getEditorLock() {\r\n return this._options.editorLock as SlickEditorLock;\r\n }\r\n\r\n /** Get Editor Controller */\r\n getEditController() {\r\n return this.editController;\r\n }\r\n\r\n /**\r\n * Sets a new source for databinding and removes all rendered rows. Note that this doesn't render the new rows - you can follow it with a call to render() to do that.\r\n * @param {CustomDataView|Array<*>} newData New databinding source using a regular JavaScript array.. or a custom object exposing getItem(index) and getLength() functions.\r\n * @param {Number} [scrollToTop] If true, the grid will reset the vertical scroll position to the top of the grid.\r\n */\r\n setData(newData: CustomDataView | TData[], scrollToTop?: boolean) {\r\n this.data = newData;\r\n this.invalidateAllRows();\r\n this.updateRowCount();\r\n if (scrollToTop) {\r\n this.scrollTo(0);\r\n }\r\n }\r\n\r\n /** Returns an array of every data object, unless you're using DataView in which case it returns a DataView object. */\r\n getData | U[]>(): U {\r\n return this.data as U;\r\n }\r\n\r\n /** Returns the size of the databinding source. */\r\n getDataLength() {\r\n if ((this.data as CustomDataView).getLength) {\r\n return (this.data as CustomDataView).getLength();\r\n } else {\r\n return (this.data as TData[])?.length ?? 0;\r\n }\r\n }\r\n\r\n /**\r\n * Returns the number of data items plus an extra row if enableAddRow is true and paging conditions allow.\r\n *\r\n * @returns\r\n */\r\n protected getDataLengthIncludingAddNew() {\r\n return this.getDataLength() + (!this._options.enableAddRow ? 0\r\n : (!this.pagingActive || this.pagingIsLastPage ? 1 : 0)\r\n );\r\n }\r\n\r\n /**\r\n * Returns the databinding item at a given position.\r\n * @param {Number} index Item row index.\r\n */\r\n getDataItem(i: number): TData {\r\n if ((this.data as CustomDataView).getItem) {\r\n return (this.data as CustomDataView).getItem(i) as TData;\r\n } else {\r\n return (this.data as TData[])[i] as TData;\r\n }\r\n }\r\n\r\n /** Are we using a DataView? */\r\n hasDataView() {\r\n return !Array.isArray(this.data);\r\n }\r\n\r\n /**\r\n * Returns item metadata by a row index when it exists\r\n * @param {Number} row\r\n * @returns {ItemMetadata | null}\r\n */\r\n getItemMetadaWhenExists(row: number): ItemMetadata | null {\r\n return 'getItemMetadata' in this.data ? (this.data as CustomDataView).getItemMetadata(row) : null;\r\n }\r\n\r\n /**\r\n * Determines the proper formatter for a given cell by checking row metadata for column overrides,\r\n * then falling back to the column\u2019s formatter, a formatter from the formatterFactory, or the default formatter.\r\n *\r\n * @param {number} row - The row index of the cell.\r\n * @param {C} column - The column definition containing formatting options.\r\n * @returns {Formatter} - The resolved formatter function for the specified cell.\r\n */\r\n protected getFormatter(row: number, column: C): Formatter {\r\n const rowMetadata = (this.data as CustomDataView)?.getItemMetadata?.(row);\r\n\r\n // look up by id, then index\r\n const columnOverrides = rowMetadata?.columns &&\r\n (rowMetadata.columns[column.id] || rowMetadata.columns[this.getColumnIndex(column.id)]);\r\n\r\n return ((columnOverrides?.formatter) ||\r\n (rowMetadata?.formatter) ||\r\n column.formatter ||\r\n (this._options.formatterFactory?.getFormatter(column)) ||\r\n this._options.defaultFormatter) as Formatter;\r\n }\r\n\r\n /**\r\n * Retrieves the editor (or editor constructor) for the specified cell by first checking for an override\r\n * in row metadata and then falling back to the column\u2019s editor or an editor from the editorFactory.\r\n *\r\n * @param {number} row - The row index of the cell.\r\n * @param {number} cell - The column index of the cell.\r\n * @returns {Editor | EditorConstructor | null | undefined} - The editor instance or constructor if available, otherwise `null` or `undefined`.\r\n */\r\n protected getEditor(row: number, cell: number): Editor | EditorConstructor | null | undefined {\r\n const column = this.columns[cell];\r\n const rowMetadata = this.getItemMetadaWhenExists(row);\r\n const columnMetadata = rowMetadata?.columns;\r\n\r\n if (columnMetadata?.[column.id]?.editor !== undefined) {\r\n return columnMetadata[column.id].editor;\r\n }\r\n if (columnMetadata?.[cell]?.editor !== undefined) {\r\n return columnMetadata[cell].editor;\r\n }\r\n\r\n return (column.editor || (this._options?.editorFactory?.getEditor(column)));\r\n }\r\n\r\n /**\r\n * Returns the value for the specified column from a given data item. If a dataItemColumnValueExtractor\r\n * is provided in options, it is used; otherwise, the property named by the column\u2019s field is returned.\r\n *\r\n * @param {TData} item - The data item containing the requested value.\r\n * @param {C} columnDef - The column definition containing the field key.\r\n * @returns {*} - The extracted value from the data item based on the column definition.\r\n */\r\n protected getDataItemValueForColumn(item: TData, columnDef: C) {\r\n if (this._options.dataItemColumnValueExtractor) {\r\n return this._options.dataItemColumnValueExtractor(item, columnDef) as TData;\r\n }\r\n return item[columnDef.field as keyof TData];\r\n }\r\n\r\n //////////////////////////////////////////////////////////////////////////////////////////////\r\n // Data Management and Editing - Cell Switchers\r\n ////////////////////////////////////////////////////////////////////\r\n\r\n /** Resets active cell by making cell normal and other internal reset. */\r\n resetActiveCell() {\r\n this.setActiveCellInternal(null, false);\r\n }\r\n\r\n /** Clear active cell by making cell normal & removing \"active\" CSS class. */\r\n unsetActiveCell() {\r\n if (Utils.isDefined(this.activeCellNode)) {\r\n this.makeActiveCellNormal();\r\n this.activeCellNode.classList.remove('active');\r\n this.rowsCache[this.activeRow]?.rowNode?.forEach((node) => node.classList.remove('active'));\r\n }\r\n }\r\n\r\n /** @alias `setFocus` */\r\n focus() {\r\n this.setFocus();\r\n }\r\n\r\n // Sets focus to one of the hidden focus sink elements based on the current tabbing direction.\r\n protected setFocus() {\r\n if (this.tabbingDirection === -1) {\r\n this._focusSink.focus();\r\n } else {\r\n this._focusSink2.focus();\r\n }\r\n }\r\n\r\n /**\r\n * Clears any previously active cell (removing \u201Cactive\u201D CSS classes), sets the new active cell,\r\n * calculates its position, and updates active row and cell indices.\r\n * If conditions are met (grid is editable and `opt_editMode` is `true`),\r\n * it initiates editing on the cell (with an asynchronous delay if configured).\r\n * Finally, it triggers `onActiveCellChanged` unless suppressed.\r\n *\r\n * @param {HTMLDivElement | null} newCell - The new active cell element, or `null` to deactivate the current cell.\r\n * @param {boolean | null} [opt_editMode] - If `true`, enables edit mode for the active cell.\r\n * If `null` or `undefined`, it follows `autoEditNewRow` and `autoEdit` settings.\r\n * @param {boolean | null} [preClickModeOn] - If `true`, indicates that the cell was activated by a pre-click action.\r\n * @param {boolean} [suppressActiveCellChangedEvent] - If `true`, prevents triggering `onActiveCellChanged` event.\r\n * @param {Event | SlickEvent_} [e] - The event that triggered the cell activation (if applicable).\r\n */\r\n protected setActiveCellInternal(newCell: HTMLDivElement | null, opt_editMode?: boolean | null, preClickModeOn?: boolean | null, suppressActiveCellChangedEvent?: boolean, e?: Event | SlickEvent_) {\r\n // make current active cell as normal cell & remove \"active\" CSS classes\r\n this.unsetActiveCell();\r\n\r\n // let activeCellChanged = (this.activeCellNode !== newCell);\r\n this.activeCellNode = newCell;\r\n\r\n if (Utils.isDefined(this.activeCellNode)) {\r\n const activeCellOffset = Utils.offset(this.activeCellNode);\r\n let rowOffset = Math.floor(Utils.offset(Utils.parents(this.activeCellNode, '.grid-canvas')[0] as HTMLElement)!.top);\r\n const isBottom = Utils.parents(this.activeCellNode, '.grid-canvas-bottom').length;\r\n\r\n if (this.hasFrozenRows && isBottom) {\r\n rowOffset -= (this._options.frozenBottom)\r\n ? Utils.height(this._canvasTopL) as number\r\n : this.frozenRowsHeight;\r\n }\r\n\r\n const cell = this.getCellFromPoint(activeCellOffset!.left, Math.ceil(activeCellOffset!.top) - rowOffset);\r\n this.activeRow = cell.row;\r\n this.activePosY = cell.row;\r\n this.activeCell = this.activePosX = this.getCellFromNode(this.activeCellNode);\r\n\r\n if (!Utils.isDefined(opt_editMode) && this._options.autoEditNewRow) {\r\n opt_editMode = (this.activeRow === this.getDataLength()) || this._options.autoEdit;\r\n }\r\n\r\n if (this._options.showCellSelection) {\r\n // make sure to never activate more than 1 cell at a time\r\n document.querySelectorAll('.slick-cell.active').forEach((node) => node.classList.remove('active'));\r\n this.activeCellNode.classList.add('active');\r\n this.rowsCache[this.activeRow]?.rowNode?.forEach((node) => node.classList.add('active'));\r\n }\r\n\r\n if (this._options.editable && opt_editMode && this.isCellPotentiallyEditable(this.activeRow, this.activeCell)) {\r\n if (this._options.asyncEditorLoading) {\r\n window.clearTimeout(this.h_editorLoader);\r\n this.h_editorLoader = window.setTimeout(() => {\r\n this.makeActiveCellEditable(undefined, preClickModeOn, e);\r\n }, this._options.asyncEditorLoadDelay);\r\n } else {\r\n this.makeActiveCellEditable(undefined, preClickModeOn, e);\r\n }\r\n }\r\n } else {\r\n this.activeRow = this.activeCell = null as any;\r\n }\r\n\r\n // this optimisation causes trouble - MLeibman #329\r\n // if (activeCellChanged) {\r\n if (!suppressActiveCellChangedEvent) {\r\n this.trigger(this.onActiveCellChanged, this.getActiveCell() as OnActiveCellChangedEventArgs);\r\n }\r\n // }\r\n }\r\n\r\n /**\r\n * Checks whether data for the row is loaded, whether the cell is in an \u201CAdd New\u201D row\r\n * (and the column disallows insert triggering), and whether an editor exists and the cell is not hidden.\r\n * Returns true if the cell is editable.\r\n *\r\n * @param {number} row - The row index of the cell.\r\n * @param {number} cell - The cell index (column index) within the row.\r\n * @returns {boolean} - Returns `true` if the cell is editable, otherwise `false`.\r\n */\r\n protected isCellPotentiallyEditable(row: number, cell: number) {\r\n const dataLength = this.getDataLength();\r\n // is the data for this row loaded?\r\n if (row < dataLength && !this.getDataItem(row)) {\r\n return false;\r\n }\r\n\r\n // are we in the Add New row? Can we create new from this cell?\r\n if (this.columns[cell].cannotTriggerInsert && row >= dataLength) {\r\n return false;\r\n }\r\n\r\n // does this cell have an editor?\r\n if (!this.columns[cell] || this.columns[cell].hidden || !this.getEditor(row, cell)) {\r\n return false;\r\n }\r\n\r\n return true;\r\n }\r\n\r\n /**\r\n * Make the cell normal again (for example after destroying cell editor),\r\n * we can also optionally refocus on the current active cell (again possibly after closing cell editor)\r\n *\r\n * If an editor is active, triggers onBeforeCellEditorDestroy and calls the editor\u2019s destroy method.\r\n * It then removes \u201Ceditable\u201D and \u201Cinvalid\u201D CSS classes from the active cell, re\u2013applies the formatter\r\n * to restore the cell\u2019s original content, invalidates any post\u2013processing results,\r\n * and deactivates the editor lock. Optionally, it can also re\u2013focus the grid.\r\n * In IE, it clears any text selection to ensure focus is properly reset.\r\n *\r\n * @param {Boolean} [refocusActiveCell]\r\n */\r\n protected makeActiveCellNormal(refocusActiveCell = false) {\r\n if (!this.currentEditor) {\r\n return;\r\n }\r\n this.trigger(this.onBeforeCellEditorDestroy, { editor: this.currentEditor });\r\n this.currentEditor.destroy();\r\n this.currentEditor = null;\r\n\r\n if (this.activeCellNode) {\r\n const d = this.getDataItem(this.activeRow);\r\n this.activeCellNode.classList.remove('editable');\r\n this.activeCellNode.classList.remove('invalid');\r\n if (d) {\r\n const column = this.columns[this.activeCell];\r\n const formatter = this.getFormatter(this.activeRow, column);\r\n const formatterResult = formatter(this.activeRow, this.activeCell, this.getDataItemValueForColumn(d, column), column, d, this as unknown as SlickGridModel);\r\n this.applyFormatResultToCellNode(formatterResult, this.activeCellNode);\r\n this.invalidatePostProcessingResults(this.activeRow);\r\n }\r\n if (refocusActiveCell) {\r\n this.setFocus();\r\n }\r\n }\r\n\r\n // if there previously was text selected on a page (such as selected text in the edit cell just removed),\r\n // IE can't set focus to anything else correctly\r\n if (navigator.userAgent.toLowerCase().match(/msie/)) {\r\n this.clearTextSelection();\r\n }\r\n\r\n this.getEditorLock()?.deactivate(this.editController as EditController);\r\n }\r\n\r\n /**\r\n * A public method that starts editing on the active cell by calling\r\n * makeActiveCellEditable with the provided editor, pre\u2013click flag, and event.\r\n */\r\n editActiveCell(editor: EditorConstructor, preClickModeOn?: boolean | null, e?: Event) {\r\n this.makeActiveCellEditable(editor, preClickModeOn, e);\r\n }\r\n\r\n /**\r\n * Makes the currently active cell editable by initializing an editor instance.\r\n *\r\n * @param {EditorConstructor} [editor] - An optional custom editor constructor to use for editing.\r\n * @param {boolean | null} [preClickModeOn] - Indicates if pre-click mode is enabled.\r\n * @param {Event | SlickEvent_} [e] - The event that triggered editing.\r\n *\r\n * @throws {Error} If called when the grid is not editable.\r\n */\r\n protected makeActiveCellEditable(editor?: EditorConstructor, preClickModeOn?: boolean | null, e?: Event | SlickEvent_) {\r\n if (!this.activeCellNode) {\r\n return;\r\n }\r\n if (!this._options.editable) {\r\n throw new Error('SlickGrid makeActiveCellEditable : should never get called when this._options.editable is false');\r\n }\r\n\r\n // cancel pending async call if there is one\r\n window.clearTimeout(this.h_editorLoader);\r\n\r\n if (!this.isCellPotentiallyEditable(this.activeRow, this.activeCell)) {\r\n return;\r\n }\r\n\r\n const columnDef = this.columns[this.activeCell];\r\n const item = this.getDataItem(this.activeRow);\r\n\r\n if (this.trigger(this.onBeforeEditCell, { row: this.activeRow, cell: this.activeCell, item, column: columnDef, target: 'grid' }).getReturnValue() === false) {\r\n this.setFocus();\r\n return;\r\n }\r\n\r\n this.getEditorLock()?.activate(this.editController as EditController);\r\n this.activeCellNode.classList.add('editable');\r\n\r\n const useEditor = editor || this.getEditor(this.activeRow, this.activeCell);\r\n // editor was null and columnMetadata and editorFactory returned null or undefined\r\n // the editor must be constructable. Also makes sure that useEditor is of type EditorConstructor\r\n if (!useEditor || typeof useEditor !== 'function') {\r\n return;\r\n }\r\n\r\n // don't clear the cell if a custom editor is passed through\r\n if (!editor && !useEditor.suppressClearOnEdit) {\r\n Utils.emptyElement(this.activeCellNode);\r\n }\r\n\r\n let metadata = this.getItemMetadaWhenExists(this.activeRow);\r\n metadata = metadata?.columns as any;\r\n const columnMetaData = metadata && (metadata[columnDef.id as keyof ItemMetadata] || (metadata as any)[this.activeCell]);\r\n\r\n\r\n const editorArgs: EditorArguments = {\r\n grid: this,\r\n gridPosition: this.absBox(this._container),\r\n position: this.absBox(this.activeCellNode),\r\n container: this.activeCellNode,\r\n column: columnDef,\r\n columnMetaData,\r\n item: item || {},\r\n event: e as Event,\r\n commitChanges: this.commitEditAndSetFocus.bind(this),\r\n cancelChanges: this.cancelEditAndSetFocus.bind(this)\r\n };\r\n\r\n this.currentEditor = new useEditor(editorArgs);\r\n\r\n if (item && this.currentEditor) {\r\n this.currentEditor.loadValue(item);\r\n if (preClickModeOn && this.currentEditor?.preClick) {\r\n this.currentEditor.preClick();\r\n }\r\n }\r\n\r\n this.serializedEditorValue = this.currentEditor?.serializeValue();\r\n\r\n if (this.currentEditor?.position) {\r\n this.handleActiveCellPositionChange();\r\n }\r\n }\r\n\r\n /**\r\n * Commits the current edit and sets focus back to the grid.\r\n * If the commit fails due to validation, the focus remains in the editor.\r\n */\r\n protected commitEditAndSetFocus() {\r\n // if the commit fails, it would do so due to a validation error\r\n // if so, do not steal the focus from the editor\r\n if (this.getEditorLock()?.commitCurrentEdit()) {\r\n this.setFocus();\r\n if (this._options.autoEdit && !this._options.autoCommitEdit) {\r\n this.navigateDown();\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Cancels the current edit and restores focus to the grid.\r\n */\r\n protected cancelEditAndSetFocus() {\r\n if (this.getEditorLock()?.cancelCurrentEdit()) {\r\n this.setFocus();\r\n }\r\n }\r\n\r\n // IEditor implementation for the editor lock\r\n\r\n /**\r\n * Commits the current edit, validating and applying changes if necessary.\r\n * If validation fails, an error is triggered and focus remains in the editor.\r\n *\r\n * @returns {boolean} Whether the edit was successfully committed.\r\n */\r\n protected commitCurrentEdit() {\r\n const self = this as SlickGrid;\r\n const item = self.getDataItem(self.activeRow);\r\n const column = self.columns[self.activeCell];\r\n\r\n if (self.currentEditor) {\r\n if (self.currentEditor.isValueChanged()) {\r\n const validationResults = self.currentEditor.validate();\r\n\r\n if (validationResults.valid) {\r\n const row = self.activeRow;\r\n const cell = self.activeCell;\r\n const editor = self.currentEditor;\r\n const serializedValue = self.currentEditor.serializeValue();\r\n const prevSerializedValue = self.serializedEditorValue;\r\n\r\n if (self.activeRow < self.getDataLength()) {\r\n const editCommand = {\r\n row,\r\n cell,\r\n editor,\r\n serializedValue,\r\n prevSerializedValue,\r\n execute: () => {\r\n editor.applyValue(item, serializedValue);\r\n self.updateRow(row);\r\n self.trigger(self.onCellChange, { command: 'execute', row, cell, item, column });\r\n },\r\n undo: () => {\r\n editor.applyValue(item, prevSerializedValue);\r\n self.updateRow(row);\r\n self.trigger(self.onCellChange, { command: 'undo', row, cell, item, column, });\r\n }\r\n };\r\n\r\n if (self._options.editCommandHandler) {\r\n self.makeActiveCellNormal(true);\r\n self._options.editCommandHandler(item, column, editCommand);\r\n } else {\r\n editCommand.execute();\r\n self.makeActiveCellNormal(true);\r\n }\r\n\r\n } else {\r\n const newItem = {};\r\n self.currentEditor.applyValue(newItem, self.currentEditor.serializeValue());\r\n self.makeActiveCellNormal(true);\r\n self.trigger(self.onAddNewRow, { item: newItem, column });\r\n }\r\n\r\n // check whether the lock has been re-acquired by event handlers\r\n return !self.getEditorLock()?.isActive();\r\n } else {\r\n // Re-add the CSS class to trigger transitions, if any.\r\n if (self.activeCellNode) {\r\n self.activeCellNode.classList.remove('invalid');\r\n Utils.width(self.activeCellNode);// force layout\r\n self.activeCellNode.classList.add('invalid');\r\n }\r\n\r\n self.trigger(self.onValidationError, {\r\n editor: self.currentEditor,\r\n cellNode: self.activeCellNode,\r\n validationResults,\r\n row: self.activeRow,\r\n cell: self.activeCell,\r\n column\r\n });\r\n\r\n self.currentEditor.focus();\r\n return false;\r\n }\r\n }\r\n\r\n self.makeActiveCellNormal(true);\r\n }\r\n return true;\r\n }\r\n\r\n /**\r\n * Cancels the current edit and restores the cell to normal mode.\r\n *\r\n * @returns {boolean} Always returns true.\r\n */\r\n protected cancelCurrentEdit() {\r\n this.makeActiveCellNormal();\r\n return true;\r\n }\r\n\r\n /** Returns an array of row indices corresponding to the currently selected rows. */\r\n getSelectedRows() {\r\n if (!this.selectionModel) {\r\n throw new Error('SlickGrid Selection model is not set');\r\n }\r\n return this.selectedRows.slice(0);\r\n }\r\n\r\n /**\r\n * Accepts an array of row indices and applies the current selectedCellCssClass to the cells in the row, respecting whether cells have been flagged as selectable.\r\n * @param {Array} rowsArray - an array of row numbers.\r\n * @param {String} [caller] - an optional string to identify who called the method\r\n */\r\n setSelectedRows(rows: number[], caller?: string) {\r\n if (!this.selectionModel) {\r\n throw new Error('SlickGrid Selection model is not set');\r\n }\r\n if (this && this.getEditorLock && !this.getEditorLock()?.isActive()) {\r\n this.selectionModel.setSelectedRanges(this.rowsToRanges(rows), caller || 'SlickGrid.setSelectedRows');\r\n }\r\n }\r\n\r\n ///////////////////////////////////////////////////////////////////////////\r\n // Event Handling and Interactivity\r\n /////////////////////////////////////////////////////////////////////////\r\n\r\n /**\r\n * A generic helper that creates (or uses) a SlickEventData from the provided event,\r\n * attaches the grid instance to the event arguments, and calls notify on the given event.\r\n * Returns the result of the notification.\r\n *\r\n * @param {SlickEvent_} evt - The Slick event instance to trigger.\r\n * @param {ArgType} [args] - Optional arguments to pass with the event.\r\n * @param {Event | SlickEventData_} [e] - The original event object or SlickEventData.\r\n * @returns {*} - The result of the event notification.\r\n */\r\n protected trigger(evt: SlickEvent_, args?: ArgType, e?: Event | SlickEventData_) {\r\n const event: SlickEventData_ = (e || new SlickEventData(e, args)) as SlickEventData_;\r\n const eventArgs = (args || {}) as ArgType & { grid: SlickGrid; };\r\n eventArgs.grid = this;\r\n return evt.notify(eventArgs, event, this);\r\n }\r\n\r\n /**\r\n * Handles the mouseout event for a cell.\r\n * Triggers the `onMouseLeave` event.\r\n *\r\n * @param {MouseEvent & { target: HTMLElement }} e - The mouse event.\r\n */\r\n protected handleCellMouseOut(e: MouseEvent & { target: HTMLElement; }) {\r\n this.trigger(this.onMouseLeave, {}, e);\r\n }\r\n\r\n /**\r\n * Handles mouse hover over a header cell.\r\n * Adds CSS classes to indicate a hover state.\r\n *\r\n * @param {Event | SlickEventData_} e - The mouse event.\r\n */\r\n protected handleHeaderMouseHoverOn(e: Event | SlickEventData_) {\r\n (e as any)?.target.classList.add('ui-state-hover', 'slick-state-hover');\r\n }\r\n\r\n /**\r\n * Handles mouse hover off a header cell.\r\n * Removes CSS classes indicating a hover state.\r\n *\r\n * @param {Event | SlickEventData_} e - The mouse event.\r\n */\r\n protected handleHeaderMouseHoverOff(e: Event | SlickEventData_) {\r\n (e as any)?.target.classList.remove('ui-state-hover', 'slick-state-hover');\r\n }\r\n\r\n /**\r\n * Called when the grid\u2019s selection model reports a change. It builds a new selection\r\n * (and CSS hash for selected cells) from the provided ranges, applies the new cell CSS styles,\r\n * and if the selection has changed from the previous state, triggers the onSelectedRowsChanged\r\n * event with details about added and removed selections.\r\n *\r\n * @param {SlickEventData_} e - The Slick event data for selection changes.\r\n * @param {SlickRange_[]} ranges - The list of selected row and cell ranges.\r\n */\r\n protected handleSelectedRangesChanged(e: SlickEventData_, ranges: SlickRange_[]) {\r\n const ne = e.getNativeEvent();\r\n const previousSelectedRows = this.selectedRows.slice(0); // shallow copy previously selected rows for later comparison\r\n this.selectedRows = [];\r\n const hash: CssStyleHash = {};\r\n for (let i = 0; i < ranges.length; i++) {\r\n for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) {\r\n if (!hash[j]) { // prevent duplicates\r\n this.selectedRows.push(j);\r\n hash[j] = {};\r\n }\r\n for (let k = ranges[i].fromCell; k <= ranges[i].toCell; k++) {\r\n if (this.canCellBeSelected(j, k)) {\r\n hash[j][this.columns[k].id] = this._options.selectedCellCssClass;\r\n }\r\n }\r\n }\r\n }\r\n\r\n this.setCellCssStyles(this._options.selectedCellCssClass || '', hash);\r\n\r\n if (this.simpleArrayEquals(previousSelectedRows, this.selectedRows)) {\r\n const caller = ne?.detail?.caller ?? 'click';\r\n // Use Set for faster performance\r\n const selectedRowsSet = new Set(this.getSelectedRows());\r\n const previousSelectedRowsSet = new Set(previousSelectedRows);\r\n\r\n const newSelectedAdditions = Array.from(selectedRowsSet).filter(i => !previousSelectedRowsSet.has(i));\r\n const newSelectedDeletions = Array.from(previousSelectedRowsSet).filter(i => !selectedRowsSet.has(i));\r\n\r\n this.trigger(this.onSelectedRowsChanged, {\r\n rows: this.getSelectedRows(),\r\n previousSelectedRows,\r\n caller,\r\n changedSelectedRows: newSelectedAdditions,\r\n changedUnselectedRows: newSelectedDeletions\r\n }, e);\r\n }\r\n }\r\n\r\n /**\r\n * Processes a mouse wheel event by adjusting the vertical scroll (scrollTop) based on deltaY (scaled by rowHeight)\r\n * and horizontal scroll (scrollLeft) based on deltaX. It then calls the internal scroll handler with the \u201Cmousewheel\u201D\r\n * type and, if any scrolling occurred, prevents the default action.\r\n *\r\n * @param {MouseEvent} e - The mouse event.\r\n * @param {number} _delta - Unused delta value.\r\n * @param {number} deltaX - The horizontal scroll delta.\r\n * @param {number} deltaY - The vertical scroll delta.\r\n */\r\n protected handleMouseWheel(e: MouseEvent, _delta: number, deltaX: number, deltaY: number) {\r\n this.scrollHeight = this._viewportScrollContainerY.scrollHeight;\r\n if (e.shiftKey) {\r\n this.scrollLeft = this._viewportScrollContainerX.scrollLeft + (deltaX * 10);\r\n } else {\r\n this.scrollTop = Math.max(0, this._viewportScrollContainerY.scrollTop - (deltaY * this._options.rowHeight!));\r\n this.scrollLeft = this._viewportScrollContainerX.scrollLeft + (deltaX * 10);\r\n }\r\n const handled = this._handleScroll('mousewheel');\r\n if (handled) {\r\n e.preventDefault();\r\n }\r\n }\r\n\r\n /**\r\n * Called when a drag is initiated. It retrieves the cell from the event; if the cell does not exist or is not selectable,\r\n * it returns false. Otherwise, it triggers the onDragInit event and returns the event\u2019s return value if\r\n * propagation is stopped, else returns false to cancel the drag.\r\n *\r\n * @param {DragEvent} e - The drag event.\r\n * @param {DragPosition} dd - The drag position data.\r\n * @returns {boolean} - Whether the drag is valid or should be cancelled.\r\n */\r\n protected handleDragInit(e: DragEvent, dd: DragPosition) {\r\n const cell = this.getCellFromEvent(e);\r\n if (!cell || !this.cellExists(cell.row, cell.cell)) {\r\n return false;\r\n }\r\n\r\n const retval = this.trigger(this.onDragInit, dd, e);\r\n if (retval.isImmediatePropagationStopped()) {\r\n return retval.getReturnValue();\r\n }\r\n\r\n // if nobody claims to be handling drag'n'drop by stopping immediate propagation,\r\n // cancel out of it\r\n return false;\r\n }\r\n\r\n /**\r\n * Similar to handleDragInit, this method retrieves the cell from the event\r\n * and triggers the `onDragStart` event. If the event propagation is stopped,\r\n * it returns the specified value; otherwise, it returns false.\r\n *\r\n * @param {DragEvent} e - The drag event that initiated the action.\r\n * @param {DragPosition} dd - The current drag position.\r\n * @returns {boolean} - The result of the event trigger or false if propagation was not stopped.\r\n */\r\n protected handleDragStart(e: DragEvent, dd: DragPosition) {\r\n const cell = this.getCellFromEvent(e);\r\n if (!cell || !this.cellExists(cell.row, cell.cell)) {\r\n return false;\r\n }\r\n\r\n const retval = this.trigger(this.onDragStart, dd, e);\r\n if (retval.isImmediatePropagationStopped()) {\r\n return retval.getReturnValue();\r\n }\r\n\r\n return false;\r\n }\r\n\r\n // Triggers the onDrag event with the current drag position and event, and returns the event\u2019s return value.\r\n protected handleDrag(e: DragEvent, dd: DragPosition) {\r\n return this.trigger(this.onDrag, dd, e).getReturnValue();\r\n }\r\n\r\n // Called when a drag operation completes; it triggers the onDragEnd event with the current drag position and event.\r\n protected handleDragEnd(e: DragEvent, dd: DragPosition) {\r\n this.trigger(this.onDragEnd, dd, e);\r\n }\r\n\r\n /**\r\n * Handles keydown events for grid navigation and editing.\r\n * It triggers the `onKeyDown` event and, based on the key pressed (such as HOME, END, arrow keys, PAGE_UP/DOWN, TAB, ENTER, ESC),\r\n * calls the appropriate navigation or editing method. If the key event is handled,\r\n * it stops propagation and prevents the default browser behaviour.\r\n *\r\n * @param {KeyboardEvent & { originalEvent: Event; }} e - The keydown event, with the original event attached.\r\n */\r\n protected handleKeyDown(e: KeyboardEvent & { originalEvent: Event; }) {\r\n const retval = this.trigger(this.onKeyDown, { row: this.activeRow, cell: this.activeCell }, e);\r\n let handled: boolean | undefined | void = retval.isImmediatePropagationStopped();\r\n\r\n if (!handled) {\r\n if (!e.shiftKey && !e.altKey) {\r\n if (this._options.editable && this.currentEditor?.keyCaptureList) {\r\n if (this.currentEditor.keyCaptureList.indexOf(e.which) > -1) {\r\n return;\r\n }\r\n }\r\n if (e.ctrlKey && e.key === 'Home') {\r\n this.navigateTopStart();\r\n } else if (e.ctrlKey && e.key === 'End') {\r\n this.navigateBottomEnd();\r\n } else if (e.ctrlKey && e.key === 'ArrowUp') {\r\n this.navigateTop();\r\n } else if (e.ctrlKey && e.key === 'ArrowDown') {\r\n this.navigateBottom();\r\n } else if ((e.ctrlKey && e.key === 'ArrowLeft') || (!e.ctrlKey && e.key === 'Home')) {\r\n this.navigateRowStart();\r\n } else if ((e.ctrlKey && e.key === 'ArrowRight') || (!e.ctrlKey && e.key === 'End')) {\r\n this.navigateRowEnd();\r\n }\r\n }\r\n }\r\n if (!handled) {\r\n if (!e.shiftKey && !e.altKey && !e.ctrlKey) {\r\n // editor may specify an array of keys to bubble\r\n if (this._options.editable && this.currentEditor?.keyCaptureList) {\r\n if (this.currentEditor.keyCaptureList.indexOf(e.which) > -1) {\r\n return;\r\n }\r\n }\r\n if (e.which === keyCode.ESCAPE) {\r\n if (!this.getEditorLock()?.isActive()) {\r\n return; // no editing mode to cancel, allow bubbling and default processing (exit without cancelling the event)\r\n }\r\n this.cancelEditAndSetFocus();\r\n } else if (e.which === keyCode.PAGE_DOWN) {\r\n this.navigatePageDown();\r\n handled = true;\r\n } else if (e.which === keyCode.PAGE_UP) {\r\n this.navigatePageUp();\r\n handled = true;\r\n } else if (e.which === keyCode.LEFT) {\r\n handled = this.navigateLeft();\r\n } else if (e.which === keyCode.RIGHT) {\r\n handled = this.navigateRight();\r\n } else if (e.which === keyCode.UP) {\r\n handled = this.navigateUp();\r\n } else if (e.which === keyCode.DOWN) {\r\n handled = this.navigateDown();\r\n } else if (e.which === keyCode.TAB) {\r\n handled = this.navigateNext();\r\n } else if (e.which === keyCode.ENTER) {\r\n if (this._options.editable) {\r\n if (this.currentEditor) {\r\n // adding new row\r\n if (this.activeRow === this.getDataLength()) {\r\n this.navigateDown();\r\n } else {\r\n this.commitEditAndSetFocus();\r\n }\r\n } else {\r\n if (this.getEditorLock()?.commitCurrentEdit()) {\r\n this.makeActiveCellEditable(undefined, undefined, e);\r\n }\r\n }\r\n }\r\n handled = true;\r\n }\r\n } else if (e.which === keyCode.TAB && e.shiftKey && !e.ctrlKey && !e.altKey) {\r\n handled = this.navigatePrev();\r\n }\r\n }\r\n\r\n if (handled) {\r\n // the event has been handled so don't let parent element (bubbling/propagation) or browser (default) handle it\r\n e.stopPropagation();\r\n e.preventDefault();\r\n try {\r\n (e as any).originalEvent.keyCode = 0; // prevent default behaviour for special keys in IE browsers (F3, F5, etc.)\r\n }\r\n // ignore exceptions - setting the original event's keycode throws access denied exception for \"Ctrl\"\r\n // (hitting control key only, nothing else), \"Shift\" (maybe others)\r\n // eslint-disable-next-line no-empty\r\n catch (error) { }\r\n }\r\n }\r\n\r\n /**\r\n * Handles a click event on the grid. It logs the event (for debugging), ensures focus is restored if necessary,\r\n * triggers the onClick event, and if the clicked cell is selectable and not already active, scrolls it into view\r\n * and activates it.\r\n *\r\n * @param {DOMEvent | SlickEventData_} evt - The click event, either a native DOM event or a Slick event.\r\n */\r\n protected handleClick(evt: DOMEvent | SlickEventData_) {\r\n const e = evt instanceof SlickEventData ? evt.getNativeEvent() : evt;\r\n\r\n if (!this.currentEditor) {\r\n // if this click resulted in some cell child node getting focus,\r\n // don't steal it back - keyboard events will still bubble up\r\n // IE9+ seems to default DIVs to tabIndex=0 instead of -1, so check for cell clicks directly.\r\n if ((e as DOMEvent).target !== document.activeElement || (e as DOMEvent).target.classList.contains('slick-cell')) {\r\n const selection = this.getTextSelection(); // store text-selection and restore it after\r\n this.setFocus();\r\n this.setTextSelection(selection as Range);\r\n }\r\n }\r\n\r\n const cell = this.getCellFromEvent(e);\r\n if (!cell || (this.currentEditor !== null && this.activeRow === cell.row && this.activeCell === cell.cell)) {\r\n return;\r\n }\r\n\r\n evt = this.trigger(this.onClick, { row: cell.row, cell: cell.cell }, evt || e);\r\n if ((evt as any).isImmediatePropagationStopped()) {\r\n return;\r\n }\r\n\r\n // this optimisation causes trouble - MLeibman #329\r\n // if ((activeCell !== cell.cell || activeRow !== cell.row) && canCellBeActive(cell.row, cell.cell)) {\r\n if (this.canCellBeActive(cell.row, cell.cell)) {\r\n if (!this.getEditorLock()?.isActive() || this.getEditorLock()?.commitCurrentEdit()) {\r\n this.scrollRowIntoView(cell.row, false);\r\n\r\n const preClickModeOn = ((e as DOMEvent).target?.className === preClickClassName);\r\n const column = this.columns[cell.cell];\r\n const suppressActiveCellChangedEvent = !!(this._options.editable && column?.editor && this._options.suppressActiveCellChangeOnEdit);\r\n this.setActiveCellInternal(this.getCellNode(cell.row, cell.cell), null, preClickModeOn, suppressActiveCellChangedEvent, (e as DOMEvent));\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves the cell DOM element from the event target.\r\n * If the cell exists and is not currently being edited, triggers the onContextMenu event.\r\n */\r\n protected handleContextMenu(e: Event & { target: HTMLElement; }) {\r\n const cell = e.target.closest('.slick-cell');\r\n if (!cell) {\r\n return;\r\n }\r\n\r\n // are we editing this cell?\r\n if (this.activeCellNode === cell && this.currentEditor !== null) {\r\n return;\r\n }\r\n\r\n this.trigger(this.onContextMenu, {}, e);\r\n }\r\n\r\n /**\r\n * Retrieves the cell from the event and triggers the onDblClick event.\r\n * If the event is not prevented and the grid is editable,\r\n * it initiates cell editing by calling gotoCell with edit mode enabled.\r\n */\r\n protected handleDblClick(e: MouseEvent) {\r\n const cell = this.getCellFromEvent(e);\r\n if (!cell || (this.currentEditor !== null && this.activeRow === cell.row && this.activeCell === cell.cell)) {\r\n return;\r\n }\r\n\r\n this.trigger(this.onDblClick, { row: cell.row, cell: cell.cell }, e);\r\n if (e.defaultPrevented) {\r\n return;\r\n }\r\n\r\n if (this._options.editable) {\r\n this.gotoCell(cell.row, cell.cell, true, e);\r\n }\r\n }\r\n\r\n /**\r\n * When the mouse enters a header column element, retrieves the column definition from the element\u2019s\r\n * stored data and triggers the onHeaderMouseEnter event with the column and grid reference.\r\n */\r\n protected handleHeaderMouseEnter(e: MouseEvent & { target: HTMLElement; }) {\r\n const c = Utils.storage.get(e.target.closest('.slick-header-column'), 'column');\r\n if (!c) {\r\n return;\r\n }\r\n this.trigger(this.onHeaderMouseEnter, {\r\n column: c,\r\n grid: this\r\n }, e);\r\n }\r\n\r\n /**\r\n * Similar to handleHeaderMouseEnter, but triggers the onHeaderMouseLeave event\r\n * when the mouse leaves a header column element.\r\n */\r\n protected handleHeaderMouseLeave(e: MouseEvent & { target: HTMLElement; }) {\r\n const c = Utils.storage.get(e.target.closest('.slick-header-column'), 'column');\r\n if (!c) {\r\n return;\r\n }\r\n this.trigger(this.onHeaderMouseLeave, {\r\n column: c,\r\n grid: this\r\n }, e);\r\n }\r\n\r\n /**\r\n * Retrieves the column from the header row cell element and triggers the onHeaderRowMouseEnter event.\r\n */\r\n protected handleHeaderRowMouseEnter(e: MouseEvent & { target: HTMLElement; }) {\r\n const c = Utils.storage.get(e.target.closest('.slick-headerrow-column'), 'column');\r\n if (!c) {\r\n return;\r\n }\r\n this.trigger(this.onHeaderRowMouseEnter, {\r\n column: c,\r\n grid: this\r\n }, e);\r\n }\r\n\r\n /**\r\n * Retrieves the column from the header row cell element and triggers the onHeaderRowMouseLeave event.\r\n */\r\n protected handleHeaderRowMouseLeave(e: MouseEvent & { target: HTMLElement; }) {\r\n const c = Utils.storage.get(e.target.closest('.slick-headerrow-column'), 'column');\r\n if (!c) {\r\n return;\r\n }\r\n this.trigger(this.onHeaderRowMouseLeave, {\r\n column: c,\r\n grid: this\r\n }, e);\r\n }\r\n\r\n /**\r\n * Retrieves the header column element and its associated column definition,\r\n * then triggers the onHeaderContextMenu event with the column data.\r\n */\r\n protected handleHeaderContextMenu(e: MouseEvent & { target: HTMLElement; }) {\r\n const header = e.target.closest('.slick-header-column');\r\n const column = header && Utils.storage.get(header, 'column');\r\n this.trigger(this.onHeaderContextMenu, { column }, e);\r\n }\r\n\r\n /**\r\n * If not in the middle of a column resize, retrieves the header column element and its column definition, then triggers the onHeaderClick event.\r\n */\r\n protected handleHeaderClick(e: MouseEvent & { target: HTMLElement; }) {\r\n if (!this.columnResizeDragging) {\r\n const header = e.target.closest('.slick-header-column');\r\n const column = header && Utils.storage.get(header, 'column');\r\n if (column) {\r\n this.trigger(this.onHeaderClick, { column }, e);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Triggers the onPreHeaderContextMenu event with the event target (typically the pre\u2013header panel).\r\n */\r\n protected handlePreHeaderContextMenu(e: MouseEvent & { target: HTMLElement; }) {\r\n this.trigger(this.onPreHeaderContextMenu, { node: e.target }, e);\r\n }\r\n\r\n /**\r\n * If not resizing columns, triggers the onPreHeaderClick event with the event target.\r\n */\r\n protected handlePreHeaderClick(e: MouseEvent & { target: HTMLElement; }) {\r\n if (!this.columnResizeDragging) {\r\n this.trigger(this.onPreHeaderClick, { node: e.target }, e);\r\n }\r\n }\r\n\r\n /**\r\n * Retrieves the footer cell element and its column definition, then triggers the onFooterContextMenu event.\r\n */\r\n protected handleFooterContextMenu(e: MouseEvent & { target: HTMLElement; }) {\r\n const footer = e.target.closest('.slick-footerrow-column');\r\n const column = footer && Utils.storage.get(footer, 'column');\r\n this.trigger(this.onFooterContextMenu, { column }, e);\r\n }\r\n\r\n /**\r\n * Retrieves the footer cell element and its column definition, then triggers the onFooterClick event.\r\n */\r\n protected handleFooterClick(e: MouseEvent & { target: HTMLElement; }) {\r\n const footer = e.target.closest('.slick-footerrow-column');\r\n const column = footer && Utils.storage.get(footer, 'column');\r\n this.trigger(this.onFooterClick, { column }, e);\r\n }\r\n\r\n /**\r\n * Triggers the onMouseEnter event when the mouse pointer enters a cell element.\r\n */\r\n protected handleCellMouseOver(e: MouseEvent & { target: HTMLElement; }) {\r\n this.trigger(this.onMouseEnter, {}, e);\r\n }\r\n\r\n /**\r\n * Handles the change in the position of the active cell.\r\n * Triggers the `onActiveCellPositionChanged` event and adjusts the editor visibility and positioning.\r\n */\r\n protected handleActiveCellPositionChange() {\r\n if (!this.activeCellNode) {\r\n return;\r\n }\r\n\r\n this.trigger(this.onActiveCellPositionChanged, {});\r\n\r\n if (this.currentEditor) {\r\n const cellBox = this.getActiveCellPosition();\r\n if (this.currentEditor.show && this.currentEditor.hide) {\r\n if (!cellBox.visible) {\r\n this.currentEditor.hide();\r\n } else {\r\n this.currentEditor.show();\r\n }\r\n }\r\n\r\n if (this.currentEditor.position) {\r\n this.currentEditor.position(cellBox);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * limits the frequency at which the provided action is executed.\r\n * call enqueue to execute the action - it will execute either immediately or, if it was executed less than minPeriod_ms in the past, as soon as minPeriod_ms has expired.\r\n * call dequeue to cancel any pending action.\r\n */\r\n protected actionThrottle(action: () => void, minPeriod_ms: number) {\r\n let blocked = false;\r\n let queued = false;\r\n\r\n const enqueue = () => {\r\n if (!blocked) {\r\n blockAndExecute();\r\n } else {\r\n queued = true;\r\n }\r\n };\r\n\r\n const dequeue = () => {\r\n queued = false;\r\n };\r\n\r\n const blockAndExecute = () => {\r\n blocked = true;\r\n window.clearTimeout(this._executionBlockTimer);\r\n this._executionBlockTimer = window.setTimeout(unblock, minPeriod_ms);\r\n action.call(this);\r\n };\r\n\r\n const unblock = () => {\r\n if (queued) {\r\n dequeue();\r\n blockAndExecute();\r\n } else {\r\n blocked = false;\r\n }\r\n };\r\n\r\n return {\r\n enqueue: enqueue.bind(this),\r\n dequeue: dequeue.bind(this)\r\n };\r\n }\r\n\r\n /**\r\n * Returns a hash containing row and cell indexes from a standard W3C event.\r\n * @param {*} event A standard W3C event.\r\n */\r\n getCellFromEvent(evt: Event | SlickEventData_) {\r\n const e = evt instanceof SlickEventData ? evt.getNativeEvent() : evt;\r\n const targetEvent: any = (e as TouchEvent).touches ? (e as TouchEvent).touches[0] : e;\r\n\r\n const cellNode = (e as Event & { target: HTMLElement }).target.closest('.slick-cell');\r\n if (!cellNode) {\r\n return null;\r\n }\r\n\r\n let row = this.getRowFromNode(cellNode.parentNode as HTMLElement);\r\n\r\n if (this.hasFrozenRows) {\r\n let rowOffset = 0;\r\n const c = Utils.offset(Utils.parents(cellNode, '.grid-canvas')[0] as HTMLElement);\r\n const isBottom = Utils.parents(cellNode, '.grid-canvas-bottom').length;\r\n\r\n if (isBottom) {\r\n rowOffset = (this._options.frozenBottom) ? Utils.height(this._canvasTopL) as number : this.frozenRowsHeight;\r\n }\r\n\r\n row = this.getCellFromPoint(targetEvent.clientX - c!.left, targetEvent.clientY - c!.top + rowOffset + document.documentElement.scrollTop).row;\r\n }\r\n\r\n const cell = this.getCellFromNode(cellNode as HTMLElement);\r\n\r\n if (!Utils.isDefined(row) || !Utils.isDefined(cell)) {\r\n return null;\r\n }\r\n return { row, cell };\r\n }\r\n\r\n /////////////////////////////////////////////////////////////////////\r\n // End Event Management and Interactivity\r\n ///////////////////////////////////////////////////////////////////\r\n\r\n //////////////////////////////////////////////////////////////////////\r\n // Rendering and Layout Management\r\n ///////////////////////////////////////////////////////////////////\r\n\r\n /**\r\n * Apply HTML code by 3 different ways depending on what is provided as input and what options are enabled.\r\n * 1. value is an HTMLElement or DocumentFragment, then first empty the target and simply append the HTML to the target element.\r\n * 2. value is string and `enableHtmlRendering` is enabled, then use `target.innerHTML = value;`\r\n * 3. value is string and `enableHtmlRendering` is disabled, then use `target.textContent = value;`\r\n * @param {HTMLElement} target - target element to apply to\r\n * @param {string | HTMLElement | DocumentFragment} val - input value can be either a string or an HTMLElement\r\n * @param {{ emptyTarget?: boolean; skipEmptyReassignment?: boolean; }} [options] -\r\n * `emptyTarget`, defaults to true, will empty the target.\r\n * `skipEmptyReassignment`, defaults to true, when enabled it will not try to reapply an empty value when the target is already empty\r\n */\r\n applyHtmlCode(target: HTMLElement, val: string | HTMLElement | DocumentFragment, options?: { emptyTarget?: boolean; skipEmptyReassignment?: boolean; }) {\r\n if (target) {\r\n if (val instanceof HTMLElement || val instanceof DocumentFragment) {\r\n // first empty target and then append new HTML element\r\n const emptyTarget = options?.emptyTarget !== false;\r\n if (emptyTarget) {\r\n Utils.emptyElement(target);\r\n }\r\n target.appendChild(val);\r\n } else {\r\n // when it's already empty and we try to reassign empty, it's probably ok to skip the assignment\r\n const skipEmptyReassignment = options?.skipEmptyReassignment !== false;\r\n if (skipEmptyReassignment && !Utils.isDefined(val) && !target.innerHTML) {\r\n return;\r\n }\r\n\r\n let sanitizedText = val;\r\n if (typeof sanitizedText === 'number' || typeof sanitizedText === 'boolean') {\r\n target.textContent = sanitizedText;\r\n } else {\r\n sanitizedText = this.sanitizeHtmlString(val as string);\r\n\r\n // apply HTML when enableHtmlRendering is enabled but make sure we do have a value (without a value, it will simply use `textContent` to clear text content)\r\n if (this._options.enableHtmlRendering && sanitizedText) {\r\n target.innerHTML = sanitizedText;\r\n } else {\r\n target.textContent = sanitizedText;\r\n }\r\n }\r\n }\r\n }\r\n }\r\n\r\n /** Get Grid Canvas Node DOM Element */\r\n getCanvasNode(columnIdOrIdx?: number | string, rowIndex?: number) {\r\n return this._getContainerElement(this.getCanvases(), columnIdOrIdx, rowIndex) as HTMLDivElement;\r\n }\r\n\r\n /** Get the canvas DOM element */\r\n getActiveCanvasNode(e?: Event | SlickEventData_) {\r\n if (e === undefined) {\r\n return this._activeCanvasNode;\r\n }\r\n\r\n if (e instanceof SlickEventData) {\r\n e = e.getNativeEvent();\r\n }\r\n\r\n this._activeCanvasNode = (e as any)?.target.closest('.grid-canvas');\r\n return this._activeCanvasNode;\r\n }\r\n\r\n /** Get the canvas DOM element */\r\n getCanvases() {\r\n return this._canvas;\r\n }\r\n\r\n /** Get the Viewport DOM node element */\r\n getViewportNode(columnIdOrIdx?: number | string, rowIndex?: number) {\r\n return this._getContainerElement(this.getViewports(), columnIdOrIdx, rowIndex);\r\n }\r\n\r\n /** Get all the Viewport node elements */\r\n getViewports() {\r\n return this._viewport;\r\n }\r\n\r\n /**\r\n * Calls setActiveViewportNode (using the provided event) to set the active viewport,\r\n * then returns the active viewport DOM element.\r\n *\r\n * @param e\r\n * @returns\r\n */\r\n getActiveViewportNode(e: Event | SlickEventData_) {\r\n this.setActiveViewportNode(e);\r\n\r\n return this._activeViewportNode;\r\n }\r\n\r\n /**\r\n * Sets an active viewport node\r\n *\r\n * @param {number | string} [columnIdOrIdx] - The column identifier or index.\r\n * @param {number} [rowIndex] - The row index.\r\n * @returns {HTMLElement} The corresponding viewport element.\r\n */\r\n setActiveViewportNode(e: Event | SlickEventData_) {\r\n if (e instanceof SlickEventData) {\r\n e = e.getNativeEvent();\r\n }\r\n this._activeViewportNode = (e as any)?.target.closest('.slick-viewport');\r\n return this._activeViewportNode;\r\n }\r\n\r\n /** Get the headers width in pixel\r\n *\r\n * Iterates over all columns to accumulate the widths for the left and right header sections,\r\n * adds scrollbar width if needed, and adjusts for frozen columns.\r\n * Returns the computed overall header width in pixels.\r\n */\r\n getHeadersWidth() {\r\n this.headersWidth = this.headersWidthL = this.headersWidthR = 0;\r\n const includeScrollbar = !this._options.autoHeight;\r\n\r\n let i = 0;\r\n const ii = this.columns.length;\r\n for (i = 0; i < ii; i++) {\r\n if (!this.columns[i] || this.columns[i].hidden) { continue; }\r\n\r\n const width = this.columns[i].width;\r\n\r\n if ((this._options.frozenColumn!) > -1 && (i > this._options.frozenColumn!)) {\r\n this.headersWidthR += width || 0;\r\n } else {\r\n this.headersWidthL += width || 0;\r\n }\r\n }\r\n\r\n if (includeScrollbar) {\r\n if ((this._options.frozenColumn!) > -1 && (i > this._options.frozenColumn!)) {\r\n this.headersWidthR += this.scrollbarDimensions?.width ?? 0;\r\n } else {\r\n this.headersWidthL += this.scrollbarDimensions?.width ?? 0;\r\n }\r\n }\r\n\r\n if (this.hasFrozenColumns()) {\r\n this.headersWidthL = this.headersWidthL + 1000;\r\n\r\n this.headersWidthR = Math.max(this.headersWidthR, this.viewportW) + this.headersWidthL;\r\n this.headersWidthR += this.scrollbarDimensions?.width ?? 0;\r\n } else {\r\n this.headersWidthL += this.scrollbarDimensions?.width ?? 0;\r\n this.headersWidthL = Math.max(this.headersWidthL, this.viewportW) + 1000;\r\n }\r\n\r\n this.headersWidth = this.headersWidthL + this.headersWidthR;\r\n return Math.max(this.headersWidth, this.viewportW) + 1000;\r\n }\r\n\r\n /** Get the grid canvas width\r\n *\r\n * Computes the available width (considering vertical scrollbar if present),\r\n * then iterates over the columns (left vs. right based on frozen columns) to sum their widths.\r\n * If full\u2013width rows are enabled, extra width is added. Returns the total calculated width.\r\n */\r\n getCanvasWidth(): number {\r\n const availableWidth = this.getViewportInnerWidth();\r\n let i = this.columns.length;\r\n\r\n this.canvasWidthL = this.canvasWidthR = 0;\r\n\r\n while (i--) {\r\n if (!this.columns[i] || this.columns[i].hidden) { continue; }\r\n\r\n if (this.hasFrozenColumns() && (i > this._options.frozenColumn!)) {\r\n this.canvasWidthR += this.columns[i].width || 0;\r\n } else {\r\n this.canvasWidthL += this.columns[i].width || 0;\r\n }\r\n }\r\n let totalRowWidth = this.canvasWidthL + this.canvasWidthR;\r\n if (this._options.fullWidthRows) {\r\n const extraWidth = Math.max(totalRowWidth, availableWidth) - totalRowWidth;\r\n if (extraWidth > 0) {\r\n totalRowWidth += extraWidth;\r\n if (this.hasFrozenColumns()) {\r\n this.canvasWidthR += extraWidth;\r\n } else {\r\n this.canvasWidthL += extraWidth;\r\n }\r\n }\r\n }\r\n return totalRowWidth;\r\n }\r\n\r\n /**\r\n * Recalculates the canvas width by calling getCanvasWidth and then adjusts widths of header containers,\r\n * canvases, panels, and viewports. If widths have changed (or forced), it applies the new column widths\r\n * by calling applyColumnWidths.\r\n *\r\n * @param {boolean} [forceColumnWidthsUpdate] - Whether to force an update of column widths.\r\n */\r\n protected updateCanvasWidth(forceColumnWidthsUpdate?: boolean) {\r\n const oldCanvasWidth = this.canvasWidth;\r\n const oldCanvasWidthL = this.canvasWidthL;\r\n const oldCanvasWidthR = this.canvasWidthR;\r\n this.canvasWidth = this.getCanvasWidth();\r\n\r\n if (this._options.createTopHeaderPanel) {\r\n Utils.width(this._topHeaderPanel, this._options.topHeaderPanelWidth ?? this.canvasWidth);\r\n }\r\n\r\n const widthChanged = this.canvasWidth !== oldCanvasWidth || this.canvasWidthL !== oldCanvasWidthL || this.canvasWidthR !== oldCanvasWidthR;\r\n\r\n if (widthChanged || this.hasFrozenColumns() || this.hasFrozenRows) {\r\n // in some browsers horizontal is showing when it shouldn't and removing 0.1px patches this issue (or even 0.0001 would be enough)\r\n Utils.width(this._canvasTopL, this.canvasWidthL - 0.1);\r\n\r\n this.getHeadersWidth();\r\n\r\n Utils.width(this._headerL, this.headersWidthL);\r\n Utils.width(this._headerR, this.headersWidthR);\r\n\r\n if (this.hasFrozenColumns()) {\r\n const cWidth = Utils.width(this._container) || 0;\r\n if (cWidth > 0 && this.canvasWidthL > cWidth && this._options.throwWhenFrozenNotAllViewable) {\r\n throw new Error('[SlickGrid] Frozen columns cannot be wider than the actual grid container width. '\r\n + 'Make sure to have less columns freezed or make your grid container wider');\r\n }\r\n Utils.width(this._canvasTopR, this.canvasWidthR);\r\n\r\n Utils.width(this._paneHeaderL, this.canvasWidthL);\r\n Utils.setStyleSize(this._paneHeaderR, 'left', this.canvasWidthL);\r\n Utils.setStyleSize(this._paneHeaderR, 'width', this.viewportW - this.canvasWidthL);\r\n\r\n Utils.width(this._paneTopL, this.canvasWidthL);\r\n Utils.setStyleSize(this._paneTopR, 'left', this.canvasWidthL);\r\n Utils.width(this._paneTopR, this.viewportW - this.canvasWidthL);\r\n\r\n Utils.width(this._headerRowScrollerL, this.canvasWidthL);\r\n Utils.width(this._headerRowScrollerR, this.viewportW - this.canvasWidthL);\r\n\r\n Utils.width(this._headerRowL, this.canvasWidthL);\r\n Utils.width(this._headerRowR, this.canvasWidthR);\r\n\r\n if (this._options.createFooterRow) {\r\n Utils.width(this._footerRowScrollerL, this.canvasWidthL);\r\n Utils.width(this._footerRowScrollerR, this.viewportW - this.canvasWidthL);\r\n\r\n Utils.width(this._footerRowL, this.canvasWidthL);\r\n Utils.width(this._footerRowR, this.canvasWidthR);\r\n }\r\n if (this._options.createPreHeaderPanel) {\r\n Utils.width(this._preHeaderPanel, this._options.preHeaderPanelWidth ?? this.canvasWidth);\r\n }\r\n Utils.width(this._viewportTopL, this.canvasWidthL);\r\n Utils.width(this._viewportTopR, this.viewportW - this.canvasWidthL);\r\n\r\n if (this.hasFrozenRows) {\r\n Utils.width(this._paneBottomL, this.canvasWidthL);\r\n Utils.setStyleSize(this._paneBottomR, 'left', this.canvasWidthL);\r\n\r\n Utils.width(this._viewportBottomL, this.canvasWidthL);\r\n Utils.width(this._viewportBottomR, this.viewportW - this.canvasWidthL);\r\n\r\n Utils.width(this._canvasBottomL, this.canvasWidthL);\r\n Utils.width(this._canvasBottomR, this.canvasWidthR);\r\n }\r\n } else {\r\n Utils.width(this._paneHeaderL, '100%');\r\n Utils.width(this._paneTopL, '100%');\r\n Utils.width(this._headerRowScrollerL, '100%');\r\n Utils.width(this._headerRowL, this.canvasWidth);\r\n\r\n if (this._options.createFooterRow) {\r\n Utils.width(this._footerRowScrollerL, '100%');\r\n Utils.width(this._footerRowL, this.canvasWidth);\r\n }\r\n\r\n if (this._options.createPreHeaderPanel) {\r\n Utils.width(this._preHeaderPanel, this._options.preHeaderPanelWidth ?? this.canvasWidth);\r\n }\r\n Utils.width(this._viewportTopL, '100%');\r\n\r\n if (this.hasFrozenRows) {\r\n Utils.width(this._viewportBottomL, '100%');\r\n Utils.width(this._canvasBottomL, this.canvasWidthL);\r\n }\r\n }\r\n }\r\n\r\n this.viewportHasHScroll = (this.canvasWidth >= this.viewportW - (this.scrollbarDimensions?.width ?? 0));\r\n\r\n Utils.width(this._headerRowSpacerL, this.canvasWidth + (this.viewportHasVScroll ? (this.scrollbarDimensions?.width ?? 0) : 0));\r\n Utils.width(this._headerRowSpacerR, this.canvasWidth + (this.viewportHasVScroll ? (this.scrollbarDimensions?.width ?? 0) : 0));\r\n\r\n if (this._options.createFooterRow) {\r\n Utils.width(this._footerRowSpacerL, this.canvasWidth + (this.viewportHasVScroll ? (this.scrollbarDimensions?.width ?? 0) : 0));\r\n Utils.width(this._footerRowSpacerR, this.canvasWidth + (this.viewportHasVScroll ? (this.scrollbarDimensions?.width ?? 0) : 0));\r\n }\r\n\r\n if (widthChanged || forceColumnWidthsUpdate) {\r\n this.applyColumnWidths();\r\n }\r\n }\r\n\r\n /** @alias `getPreHeaderPanelLeft` */\r\n getPreHeaderPanel() {\r\n return this._preHeaderPanel;\r\n }\r\n\r\n /** Get the Pre-Header Panel Left DOM node element */\r\n getPreHeaderPanelLeft() {\r\n return this._preHeaderPanel;\r\n }\r\n\r\n /** Get the Pre-Header Panel Right DOM node element */\r\n getPreHeaderPanelRight() {\r\n return this._preHeaderPanelR;\r\n }\r\n\r\n /** Get the Top-Header Panel DOM node element */\r\n getTopHeaderPanel() {\r\n return this._topHeaderPanel;\r\n }\r\n\r\n /**\r\n * Based on whether frozen columns (and/or rows) are enabled, shows or hides the right\u2013side header\r\n * and top panes as well as the bottom panes. If no frozen columns exist, hides right\u2013side panes;\r\n * otherwise, conditionally shows or hides the bottom panes depending on whether frozen rows exist.\r\n */\r\n protected setPaneVisibility() {\r\n if (this.hasFrozenColumns()) {\r\n Utils.show(this._paneHeaderR);\r\n Utils.show(this._paneTopR);\r\n\r\n if (this.hasFrozenRows) {\r\n Utils.show(this._paneBottomL);\r\n Utils.show(this._paneBottomR);\r\n } else {\r\n Utils.hide(this._paneBottomR);\r\n Utils.hide(this._paneBottomL);\r\n }\r\n } else {\r\n Utils.hide(this._paneHeaderR);\r\n Utils.hide(this._paneTopR);\r\n Utils.hide(this._paneBottomR);\r\n\r\n if (this.hasFrozenRows) {\r\n Utils.show(this._paneBottomL);\r\n } else {\r\n Utils.hide(this._paneBottomR);\r\n Utils.hide(this._paneBottomL);\r\n }\r\n }\r\n }\r\n\r\n /**\r\n * Sets the CSS overflowX and overflowY styles for all four viewport elements\r\n * (top\u2013left, top\u2013right, bottom\u2013left, bottom\u2013right) based on the grid\u2019s frozen columns/rows status\r\n * and options such as alwaysAllowHorizontalScroll and alwaysShowVerticalScroll.\r\n * If a viewportClass is specified in options, the class is added to each viewport.\r\n */\r\n protected setOverflow() {\r\n this._viewportTopL.style.overflowX = (this.hasFrozenColumns()) ? (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'hidden' : 'scroll') : (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'hidden' : 'auto');\r\n this._viewportTopL.style.overflowY = (!this.hasFrozenColumns() && this._options.alwaysShowVerticalScroll) ? 'scroll' : ((this.hasFrozenColumns()) ? (this.hasFrozenRows ? 'hidden' : 'hidden') : (this.hasFrozenRows ? 'scroll' : 'auto'));\r\n\r\n this._viewportTopR.style.overflowX = (this.hasFrozenColumns()) ? (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'hidden' : 'scroll') : (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'hidden' : 'auto');\r\n this._viewportTopR.style.overflowY = this._options.alwaysShowVerticalScroll ? 'scroll' : ((this.hasFrozenColumns()) ? (this.hasFrozenRows ? 'scroll' : 'auto') : (this.hasFrozenRows ? 'scroll' : 'auto'));\r\n\r\n this._viewportBottomL.style.overflowX = (this.hasFrozenColumns()) ? (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'scroll' : 'auto') : (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'auto' : 'auto');\r\n this._viewportBottomL.style.overflowY = (!this.hasFrozenColumns() && this._options.alwaysShowVerticalScroll) ? 'scroll' : ((this.hasFrozenColumns()) ? (this.hasFrozenRows ? 'hidden' : 'hidden') : (this.hasFrozenRows ? 'scroll' : 'auto'));\r\n\r\n this._viewportBottomR.style.overflowX = (this.hasFrozenColumns()) ? (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'scroll' : 'auto') : (this.hasFrozenRows && !this._options.alwaysAllowHorizontalScroll ? 'auto' : 'auto');\r\n this._viewportBottomR.style.overflowY = this._options.alwaysShowVerticalScroll ? 'scroll' : ((this.hasFrozenColumns()) ? (this.hasFrozenRows ? 'auto' : 'auto') : (this.hasFrozenRows ? 'auto' : 'auto'));\r\n\r\n if (this._options.viewportClass) {\r\n const viewportClassList = Utils.classNameToList(this._options.viewportClass);\r\n this._viewportTopL.classList.add(...viewportClassList);\r\n this._viewportTopR.classList.add(...viewportClassList);\r\n this._viewportBottomL.classList.add(...viewportClassList);\r\n this._viewportBottomR.classList.add(...viewportClassList);\r\n }\r\n }\r\n\r\n /**\r\n * Creates a + + + +

Example - Context Menu Plugin

+ + + + + +
+
+
+

+ Demonstrates: + Slick.Plugins.HybridSelectionModel / Slick.Plugins.CellMenu +

+
    +
  • Slick.Plugins.HybridSelectionModel
  • +
  • This selectionmodel is a combination of the row and column selectionmodels.
  • +
  • for a given row, the selectionmodel that applies is selected by the following in priority order:
  • +
  • 1) if options.rowSelectOverride is defined, call rowSelectOverride(rowData, plugin, grid), return value is true to use rowSelectionModel
  • +
  • 2) if the RowMovweManager plugin is used, and rowMoveManager.isHandlerColumn is true, select RowSelectionModel
  • +
  • 3) if the column name is in options.rowSelectColumnIdArr[] then apply rowSelectionModel
  • +

    +
  • Slick.Plugins.ContextMenu - Context Menu (global from any columns right+click)
  • +
  • This plugin subscribes internally to the "onContextMenu" event
  • +
  • Mouse Right-Click the row to open a Context Menu (global any columns)
  • +
  • The context menu showing on left columns will show only commands (from Title to EffortDriven)
  • +
  • + The context menu showing on Priority column will show list of options that will be applied to the Priority + cell +
  • +
+

View Source:

+ +
+ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/examples/example-spreadsheet-dragfill.html b/examples/example-spreadsheet-dragfill.html new file mode 100644 index 00000000..21967243 --- /dev/null +++ b/examples/example-spreadsheet-dragfill.html @@ -0,0 +1,146 @@ + + + + + + SlickGrid spreadsheet - no formula Editor + + + + + +

Example - Spreadsheet Drag-Fill

+
+
+
+
+ +
+

Demonstrates:

+
    +
  • Virtual scrolling on both rows and columns.
  • +
  • Select a range of cells with a mouse
  • +
  • Use Ctrl-C and Ctrl-V keyboard shortcuts to cut and paste cells
  • +
  • Use Esc to cancel a copy and paste operation
  • +
  • Edit the cell and select a cell range to paste the range
  • +
+

View Source:

+ +
+
+ + + + + + + + + + + + + + + + + diff --git a/examples/example-spreadsheet.html b/examples/example-spreadsheet.html index 293cf8da..ef490450 100644 --- a/examples/example-spreadsheet.html +++ b/examples/example-spreadsheet.html @@ -83,7 +83,7 @@

Range Selection

enableAddRow: true, enableCellNavigation: true, asyncEditorLoading: false, - autoEdit: false + autoEdit: true }; var columns = [ diff --git a/src/global.d.ts b/src/global.d.ts index 4f2d5663..1f6e2518 100644 --- a/src/global.d.ts +++ b/src/global.d.ts @@ -7,6 +7,7 @@ import type { keyCode, preClickClassName, RowSelectionMode, + CellSelectionMode, SlickEvent, SlickEventData, SlickEventHandler, @@ -14,7 +15,9 @@ import type { SlickGroup, SlickGroupTotals, SlickRange, + SlickCopyRange, Utils, + SelectionUtils, ValueFilterMode, WidthEvalMode, } from './slick.core.js'; @@ -108,12 +111,16 @@ declare global { }, preClickClassName: typeof preClickClassName, Range: typeof SlickRange, + CopyRange: typeof SlickCopyRange, + DragExtendHandle: typeof SlickDragExtendHandle, Resizable: typeof Resizable, RowMoveManager: typeof SlickRowMoveManager, RowSelectionMode: typeof RowSelectionMode, + CellSelectionMode: typeof CellSelectionMode, RowSelectionModel: typeof SlickRowSelectionModel, State: typeof SlickState, Utils: typeof Utils, + SelectionUtils: typeof SelectionUtils, ValueFilterMode: typeof ValueFilterMode, WidthEvalMode: typeof WidthEvalMode, }; diff --git a/src/models/drag.interface.ts b/src/models/drag.interface.ts index 64a8cda7..713a49d8 100644 --- a/src/models/drag.interface.ts +++ b/src/models/drag.interface.ts @@ -15,6 +15,7 @@ export interface DragPosition { startX: number; startY: number; range: DragRange; + matchClassTag: string; } export interface DragRange { @@ -26,6 +27,10 @@ export interface DragRange { row?: number; cell?: number; }; + rowCount?: number; + cellCount?: number; + wasDraggedUp?: boolean; + wasDraggedLeft?: boolean; } export interface DragRowMove { @@ -50,4 +55,5 @@ export interface DragRowMove { startX: number; startY: number; range: DragRange; + matchClassTag: string; } \ No newline at end of file diff --git a/src/models/gridEvents.interface.ts b/src/models/gridEvents.interface.ts index 25d969ca..31ce8145 100644 --- a/src/models/gridEvents.interface.ts +++ b/src/models/gridEvents.interface.ts @@ -1,6 +1,6 @@ -import type { Column, CompositeEditorOption, CssStyleHash, Editor, EditorValidationResult, GridOption } from './index.js'; -import type { SlickGrid } from '../slick.grid.js'; - +import type { Column, CompositeEditorOption, CssStyleHash, Editor, EditorValidationResult, GridOption } from './index'; +import type { SlickGrid } from '../slick.grid'; +import type { SlickRange, SlickCopyRange } from '../slick.core'; export interface SlickGridArg { grid: SlickGrid; } export interface OnActiveCellChangedEventArgs extends SlickGridArg { cell: number; row: number; } export interface OnAddNewRowEventArgs extends SlickGridArg { item: any; column: Column; } @@ -46,4 +46,5 @@ export interface OnDragEventArgs extends SlickGridArg { available: HTMLDivElement | HTMLDivElement[]; drag: HTMLDivElement; drop: HTMLDivElement | HTMLDivElement[]; helper: HTMLDivElement; proxy: HTMLDivElement; target: HTMLDivElement; mode: string; row: number; rows: number[]; startX: number; startY: number; -} \ No newline at end of file +} +export interface OnDragReplaceCellsEventArgs extends SlickGridArg { prevSelectedRange: SlickRange; selectedRange: SlickRange; copyToRange: SlickCopyRange; } diff --git a/src/models/interactions.interface.ts b/src/models/interactions.interface.ts index 4ecdc96d..53b37cbe 100644 --- a/src/models/interactions.interface.ts +++ b/src/models/interactions.interface.ts @@ -6,6 +6,18 @@ import type { DragPosition } from './drag.interface.js'; export interface InteractionBase { destroy: () => void; } +export interface ClassDetectElement { + /** tag to be returned on match */ + tag: string + + /** element id to match */ + id?: string; + + /** element cssSelector to match */ + cssSelector?: string; +} + + export interface DraggableOption { /** container DOM element, defaults to "document" */ containerElement?: HTMLElement | Document; @@ -30,6 +42,8 @@ export interface DraggableOption { /** drag ended callback */ onDragEnd?: (e: DragEvent, dd: DragPosition) => boolean | void; + + dragFromClassDetectArr?: Array } export interface MouseWheelOption { diff --git a/src/plugins/slick.cellrangedecorator.ts b/src/plugins/slick.cellrangedecorator.ts index 2e3ca97c..c9f8885a 100644 --- a/src/plugins/slick.cellrangedecorator.ts +++ b/src/plugins/slick.cellrangedecorator.ts @@ -25,6 +25,7 @@ export class SlickCellRangeDecorator implements SlickPlugin { // protected props protected _options: CellRangeDecoratorOption; protected _elem?: HTMLDivElement | null; + protected _selectionCss: CSSStyleDeclaration; protected _defaults = { selectionCssClass: 'slick-range-decorator', selectionCss: { @@ -36,12 +37,21 @@ export class SlickCellRangeDecorator implements SlickPlugin { constructor(protected readonly grid: SlickGrid, options?: Partial) { this._options = Utils.extend(true, {}, this._defaults, options); + this._selectionCss = options?.selectionCss || {} as CSSStyleDeclaration; } destroy() { this.hide(); } + getSelectionCss() { + return this._selectionCss; + } + + setSelectionCss(cssProps: CSSStyleDeclaration) { + this._selectionCss = cssProps; + } + init() { } hide() { @@ -53,8 +63,8 @@ export class SlickCellRangeDecorator implements SlickPlugin { if (!this._elem) { this._elem = document.createElement('div'); this._elem.className = this._options.selectionCssClass; - Object.keys(this._options.selectionCss as CSSStyleDeclaration).forEach((cssStyleKey) => { - this._elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this._options.selectionCss[cssStyleKey as CSSStyleDeclarationWritable]; + Object.keys(this._selectionCss as CSSStyleDeclaration).forEach((cssStyleKey) => { + this._elem!.style[cssStyleKey as CSSStyleDeclarationWritable] = this._selectionCss[cssStyleKey as CSSStyleDeclarationWritable]; }); this._elem.style.position = 'absolute'; const canvasNode = this.grid.getActiveCanvasNode(); diff --git a/src/plugins/slick.cellrangeselector.ts b/src/plugins/slick.cellrangeselector.ts index 109a4236..b84265a9 100644 --- a/src/plugins/slick.cellrangeselector.ts +++ b/src/plugins/slick.cellrangeselector.ts @@ -1,4 +1,4 @@ -import { SlickEvent as SlickEvent_, type SlickEventData, SlickEventHandler as SlickEventHandler_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js'; +import { CellSelectionMode as CellSelectionMode_, SlickEvent as SlickEvent_, type SlickEventData, SlickEventHandler as SlickEventHandler_, SlickRange as SlickRange_, Utils as Utils_, SelectionUtils as SelectionUtils_ } from '../slick.core.js'; import { Draggable as Draggable_ } from '../slick.interactions.js'; import { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js'; import type { CellRangeSelectorOption, DragPosition, DragRange, DragRowMove, GridOption, MouseOffsetViewport, OnScrollEventArgs, SlickPlugin } from '../models/index.js'; @@ -11,19 +11,22 @@ const SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_; const Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_; const SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_; const Utils = IIFE_ONLY ? Slick.Utils : Utils_; +const SelectionUtils = IIFE_ONLY ? Slick.SelectionUtils : SelectionUtils_; +const CellSelectionMode = IIFE_ONLY ? Slick.CellSelectionMode : CellSelectionMode_; export class SlickCellRangeSelector implements SlickPlugin { // -- // public API pluginName = 'CellRangeSelector' as const; onBeforeCellRangeSelected = new SlickEvent<{ row: number; cell: number; }>('onBeforeCellRangeSelected'); - onCellRangeSelected = new SlickEvent<{ range: SlickRange_; }>('onCellRangeSelected'); - onCellRangeSelecting = new SlickEvent<{ range: SlickRange_; }>('onCellRangeSelecting'); + onCellRangeSelected = new SlickEvent<{ range: SlickRange_; selectionMode: string; allowAutoEdit: boolean; }>('onCellRangeSelected'); + onCellRangeSelecting = new SlickEvent<{ range: SlickRange_; selectionMode: string; allowAutoEdit: boolean; }>('onCellRangeSelecting'); // -- // protected props protected _grid!: SlickGrid; protected _currentlySelectedRange: DragRange | null = null; + protected _previousSelectedRange: DragRange | null = null; protected _canvas: HTMLElement | null = null; protected _decorator!: SlickCellRangeDecorator_; protected _gridOptions!: GridOption; @@ -31,6 +34,7 @@ export class SlickCellRangeSelector implements SlickPlugin { protected _dragging = false; protected _handler = new SlickEventHandler(); protected _options: CellRangeSelectorOption; + protected _selectionMode: string = CellSelectionMode.Select; protected _defaults = { autoScroll: true, minIntervalToShowNextCell: 30, @@ -96,6 +100,14 @@ export class SlickCellRangeSelector implements SlickPlugin { return this._decorator; } + getSelectionMode() { + return this._selectionMode; + } + + setSelectionMode(mode: string) { + this._selectionMode = mode; + } + protected handleScroll(_e: SlickEventData, args: OnScrollEventArgs) { this._scrollTop = args.scrollTop; this._scrollLeft = args.scrollLeft; @@ -167,11 +179,19 @@ export class SlickCellRangeSelector implements SlickPlugin { startY += this._scrollTop; } - const start = this._grid.getCellFromPoint(startX, startY); + let start: { row: number | undefined, cell: number | undefined; } | null; + this._selectionMode = CellSelectionMode.Select; + if (dd.matchClassTag !== 'dragReplaceHandle') { + start = this._grid.getCellFromPoint(startX, startY); + this._previousSelectedRange = null; + } else { + start = this._grid.getActiveCell() || { row: undefined, cell: undefined }; + this._selectionMode = CellSelectionMode.Replace; + } dd.range = { start, end: {} }; this._currentlySelectedRange = dd.range; - return this._decorator.show(new SlickRange(start.row, start.cell)); + return this._decorator.show(new SlickRange(start.row ?? 0, start.cell ?? 0)); } protected handleDrag(evt: SlickEventData, dd: DragRowMove) { @@ -353,10 +373,15 @@ export class SlickCellRangeSelector implements SlickPlugin { if (dd?.range) { dd.range.end = end; - const range = new SlickRange(dd.range.start.row ?? 0, dd.range.start.cell ?? 0, end.row, end.cell); + let cornerCell = !this._previousSelectedRange ? dd.range.start : SelectionUtils.normalRangeOppositeCellFromCopy(this._previousSelectedRange, end); + this._currentlySelectedRange = dd.range; + + const range = new Slick.Range(cornerCell.row, cornerCell.cell, end.row, end.cell); + this._decorator.show(range); this.onCellRangeSelecting.notify({ - range + range, selectionMode: '', + allowAutoEdit: false }); } } @@ -375,19 +400,26 @@ export class SlickCellRangeSelector implements SlickPlugin { e.stopImmediatePropagation(); this.stopIntervalTimer(); - this.onCellRangeSelected.notify({ - range: new SlickRange( + let r = new SlickRange( dd.range.start.row ?? 0, dd.range.start.cell ?? 0, dd.range.end.row, dd.range.end.cell - ) - }); + ); + + this.onCellRangeSelected.notify({ range: r, selectionMode: this._selectionMode, allowAutoEdit: (this._selectionMode === "SEL" && r.isSingleCell()) }); + this._previousSelectedRange = SelectionUtils.normaliseDragRange(dd.range); + + console.log('handleDragEnd'); } getCurrentRange() { return this._currentlySelectedRange; } + + getPreviousRange() { + return this._previousSelectedRange; + } } // extend Slick namespace on window object when building as iife diff --git a/src/plugins/slick.cellselectionmodel.ts b/src/plugins/slick.cellselectionmodel.ts index 9528b5fb..1c2a104d 100644 --- a/src/plugins/slick.cellselectionmodel.ts +++ b/src/plugins/slick.cellselectionmodel.ts @@ -102,7 +102,7 @@ export class SlickCellSelectionModel { this._cachedPageRowCount = 0; } - setSelectedRanges(ranges: SlickRange_[], caller = 'SlickCellSelectionModel.setSelectedRanges') { + setSelectedRanges(ranges: SlickRange_[], caller = 'SlickCellSelectionModel.setSelectedRanges', selectionMode: string) { // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; } @@ -113,7 +113,7 @@ export class SlickCellSelectionModel { if (rangeHasChanged) { // provide extra "caller" argument through SlickEventData event to avoid breaking the previous pubsub event structure // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged` - const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller } }), this._ranges); + const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges); this.onSelectedRangesChanged.notify(this._ranges, eventData); } } @@ -123,7 +123,7 @@ export class SlickCellSelectionModel { } refreshSelections() { - this.setSelectedRanges(this.getSelectedRanges()); + this.setSelectedRanges(this.getSelectedRanges(), undefined, ''); } protected handleBeforeCellRangeSelected(e: SlickEventData_): boolean | void { @@ -133,9 +133,9 @@ export class SlickCellSelectionModel { } } - protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; }) { + protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; selectionMode: string; }) { this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, false, false, true); - this.setSelectedRanges([args.range]); + this.setSelectedRanges([args.range], undefined, args.selectionMode); } protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) { @@ -144,10 +144,10 @@ export class SlickCellSelectionModel { const isRowDefined = Utils.isDefined(args.row); if (this._options?.selectActiveCell && isRowDefined && isCellDefined) { - this.setSelectedRanges([new SlickRange(args.row, args.cell)]); + this.setSelectedRanges([new SlickRange(args.row, args.cell)], undefined, ''); } else if (!this._options?.selectActiveCell || (!isRowDefined && !isCellDefined)) { // clear the previous selection once the cell changes - this.setSelectedRanges([]); + this.setSelectedRanges([], undefined, ''); } } @@ -282,7 +282,7 @@ export class SlickCellSelectionModel { ranges.push(last); } - this.setSelectedRanges(ranges); + this.setSelectedRanges(ranges, undefined, ''); e.preventDefault(); e.stopPropagation(); diff --git a/src/plugins/slick.hybridselectionmodel.ts b/src/plugins/slick.hybridselectionmodel.ts new file mode 100644 index 00000000..c158ba99 --- /dev/null +++ b/src/plugins/slick.hybridselectionmodel.ts @@ -0,0 +1,517 @@ +import { keyCode as keyCode_, SlickEvent as SlickEvent_, SlickEventData as SlickEventData_, SlickRange as SlickRange_, Utils as Utils_ } from '../slick.core.js'; +import { Draggable as Draggable_ } from '../slick.interactions.js'; +import { SlickCellRangeDecorator as SlickCellRangeDecorator_ } from './slick.cellrangedecorator.js'; +import { SlickCellRangeSelector as SlickCellRangeSelector_ } from './slick.cellrangeselector.js'; +import type { Column, CustomDataView, OnActiveCellChangedEventArgs } from '../models/index.js'; +import type { SlickDataView } from '../slick.dataview.js'; +import type { SlickCrossGridRowMoveManager as SlickCrossGridRowMoveManager_ } from './slick.crossgridrowmovemanager.js'; +import type { SlickRowMoveManager as SlickRowMoveManager_ } from './slick.rowmovemanager.js'; +import type { SlickGrid } from '../slick.grid.js'; + +// for (iife) load Slick methods from global Slick object, or use imports for (esm) +const Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_; +const keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_; +const SlickEvent = IIFE_ONLY ? Slick.Event : SlickEvent_; +const SlickEventData = IIFE_ONLY ? Slick.EventData : SlickEventData_; +const SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_; +const SlickCellRangeDecorator = IIFE_ONLY ? Slick.CellRangeDecorator : SlickCellRangeDecorator_; +const SlickCellRangeSelector = IIFE_ONLY ? Slick.CellRangeSelector : SlickCellRangeSelector_; +const Utils = IIFE_ONLY ? Slick.Utils : Utils_; + +export declare type RowSelectOverride = (data: OnActiveCellChangedEventArgs, selectionModel: SlickHybridSelectionModel, grid: SlickGrid) => boolean; + +export interface HybridSelectionModelOption { + selectActiveCell: boolean; + selectActiveRow: boolean; + cellRangeSelector?: SlickCellRangeSelector_; + dragToSelect: boolean; + autoScrollWhenDrag: boolean; + handleRowMoveManagerColumn: boolean; // Row Selection on RowMoveManage column + rowSelectColumnIdArr: string[]; // Row Selection on these columns + rowSelectOverride: RowSelectOverride | undefined; // function to toggle Row Selection Models +} + +export class SlickHybridSelectionModel { + // hybrid selection model is CellSelectionModel except when selecting + // specific columns, which behave as RowSelectionModel + + // -- + // public API + pluginName = 'HybridSelectionModel' as const; + onSelectedRangesChanged = new SlickEvent('onSelectedRangesChanged'); + + // -- + // protected props + protected _cachedPageRowCount = 0; + protected _dataView?: CustomDataView | SlickDataView; + protected _grid!: SlickGrid; + protected _prevSelectedRow?: number; + protected _prevKeyDown = ''; + protected _ranges: SlickRange_[] = []; + protected _selector: SlickCellRangeSelector_; + protected _isRowMoveManagerHandler: any; + protected _activeSelectionIsRow = false; + protected _options?: HybridSelectionModelOption; + protected _defaults: HybridSelectionModelOption = { + selectActiveCell: true, + selectActiveRow: true, + dragToSelect: false, + autoScrollWhenDrag: true, + handleRowMoveManagerColumn: true, // Row Selection on RowMoveManage column + rowSelectColumnIdArr: [], // Row Selection on these columns + rowSelectOverride: undefined, // function to toggle Row Selection Models + cellRangeSelector: undefined + + }; + + constructor(options?: HybridSelectionModelOption) { + this._options = Utils.extend(true, {}, this._defaults, options); + + if (options === undefined || options.cellRangeSelector === undefined) { + this._selector = new SlickCellRangeSelector({ selectionCss: { border: '2px solid black' } as CSSStyleDeclaration }); + } else { + this._selector = options.cellRangeSelector; + } + } + + // Region: Setup + // ----------------------------------------------------------------------------- + + init(grid: SlickGrid) { + if (Draggable === undefined) { + throw new Error('Slick.Draggable is undefined, make sure to import "slick.interactions.js"'); + } + + this._grid = grid; + Utils.addSlickEventPubSubWhenDefined(grid.getPubSubService(), this); + + if (!this._selector && this._options?.dragToSelect) { + if (!SlickCellRangeDecorator) { + throw new Error('Slick.CellRangeDecorator is required when option dragToSelect set to true'); + } + this._selector = new SlickCellRangeSelector({ + selectionCss: { border: 'none' } as CSSStyleDeclaration, + autoScroll: this._options?.autoScrollWhenDrag + }); + } + + if (grid.hasDataView()) { + this._dataView = grid.getData(); + } + this._grid.onActiveCellChanged.subscribe(this.handleActiveCellChange.bind(this)); + this._grid.onKeyDown.subscribe(this.handleKeyDown.bind(this)); + this._grid.onClick.subscribe(this.handleClick.bind(this)); + if (this._selector) { + grid.registerPlugin(this._selector); + this._selector.onCellRangeSelected.subscribe(this.handleCellRangeSelected.bind(this)); + this._selector.onBeforeCellRangeSelected.subscribe(this.handleBeforeCellRangeSelected.bind(this)); + } + } + + destroy() { + this._grid.onActiveCellChanged.unsubscribe(this.handleActiveCellChange.bind(this)); + this._grid.onKeyDown.unsubscribe(this.handleKeyDown.bind(this)); + this._grid.onClick.unsubscribe(this.handleClick.bind(this)); + this._selector.onCellRangeSelected.unsubscribe(this.handleCellRangeSelected.bind(this)); + this._selector.onBeforeCellRangeSelected.unsubscribe(this.handleBeforeCellRangeSelected.bind(this)); + this._grid.unregisterPlugin(this._selector); + this._selector?.destroy(); + } + + // Region: CellSelectionModel Members + // ----------------------------------------------------------------------------- + + protected removeInvalidRanges(ranges: SlickRange_[]) { + const result: SlickRange_[] = []; + + for (let i = 0; i < ranges.length; i++) { + const r = ranges[i]; + if (this._grid.canCellBeSelected(r.fromRow, r.fromCell) && this._grid.canCellBeSelected(r.toRow, r.toCell)) { + result.push(r); + } + } + + return result; + } + + protected rangesAreEqual(range1: SlickRange_[], range2: SlickRange_[]) { + let areDifferent = (range1.length !== range2.length); + if (!areDifferent) { + for (let i = 0; i < range1.length; i++) { + if ( + range1[i].fromCell !== range2[i].fromCell + || range1[i].fromRow !== range2[i].fromRow + || range1[i].toCell !== range2[i].toCell + || range1[i].toRow !== range2[i].toRow + ) { + areDifferent = true; + break; + } + } + } + return !areDifferent; + } + + // Region: RowSelectionModel Members + // ----------------------------------------------------------------------------- + + protected rangesToRows(ranges: SlickRange_[]): number[] { + const rows: number[] = []; + for (let i = 0; i < ranges.length; i++) { + for (let j = ranges[i].fromRow; j <= ranges[i].toRow; j++) { + rows.push(j); + } + } + return rows; + } + + protected rowsToRanges(rows: number[]) { + const ranges: SlickRange_[] = []; + const lastCell = this._grid.getColumns().length - 1; + rows.forEach(row => ranges.push(new SlickRange(row, 0, row, lastCell))); + return ranges; + } + + protected getRowsRange(from: number, to: number) { + let i; + const rows: number[] = []; + for (i = from; i <= to; i++) { + rows.push(i); + } + for (i = to; i < from; i++) { + rows.push(i); + } + return rows; + } + + getSelectedRows() { + return this.rangesToRows(this._ranges); + } + + setSelectedRows(rows: number[]) { + this.setSelectedRanges(this.rowsToRanges(rows), 'SlickRowSelectionModel.setSelectedRows', ''); + } + + // Region: Shared Members + // ----------------------------------------------------------------------------- + + /** Provide a way to force a recalculation of page row count (for example on grid resize) */ + resetPageRowCount() { + this._cachedPageRowCount = 0; + } + + setSelectedRanges(ranges: SlickRange_[], caller = 'SlickHybridSelectionModel.setSelectedRanges', selectionMode: string) { + // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged + if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; } + + // if range has not changed, don't fire onSelectedRangesChanged + const rangeHasChanged = !this.rangesAreEqual(this._ranges, ranges); + + if (this._activeSelectionIsRow) { + this._ranges = ranges; + + // provide extra "caller" argument through SlickEventData event to avoid breaking the previous pubsub event structure + // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged` + const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges); + this.onSelectedRangesChanged.notify(this._ranges, eventData); + } else { + this._ranges = this.removeInvalidRanges(ranges); + if (rangeHasChanged) { + // provide extra "caller" argument through SlickEventData event to avoid breaking the previous pubsub event structure + // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged` + const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges); + this.onSelectedRangesChanged.notify(this._ranges, eventData); + } + } + } + + currentSelectionModeIsRow() { + return this._activeSelectionIsRow; + } + + getSelectedRanges() { + return this._ranges; + } + + refreshSelections() { + if (this._activeSelectionIsRow) { + this.setSelectedRows(this.getSelectedRows()); + } else { + this.setSelectedRanges(this.getSelectedRanges(), undefined, ''); + } + } + + getRowMoveManagerPlugin(): SlickRowMoveManager_ | SlickCrossGridRowMoveManager_ | undefined { + return this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager'); + } + + rowSelectionModelIsActive(data: OnActiveCellChangedEventArgs): boolean { + // work out required selection mode + if (this._options?.rowSelectOverride) { + return this._options?.rowSelectOverride(data, this, this._grid); + } + + if (this._options?.handleRowMoveManagerColumn) { + const rowMoveManager = this.getRowMoveManagerPlugin(); + if (rowMoveManager?.isHandlerColumn(data.cell)) { return true; } + } + + var targetColumn = this._grid.getVisibleColumns()[data.cell]; + return this._options?.rowSelectColumnIdArr.includes('' + targetColumn.id) || false; + } + + protected handleActiveCellChange(_e: SlickEventData_, args: OnActiveCellChangedEventArgs) { + this._prevSelectedRow = undefined; + const isCellDefined = Utils.isDefined(args.cell); + const isRowDefined = Utils.isDefined(args.row); + this._activeSelectionIsRow = this.rowSelectionModelIsActive(args); + + if (this._activeSelectionIsRow) { + if (this._options?.selectActiveRow && args.row !== null) { + this.setSelectedRanges([new Slick.Range(args.row, 0, args.row, this._grid.getColumns().length - 1)], undefined, ''); + } + } else { + if (this._options?.selectActiveCell && isRowDefined && isCellDefined) { + this.setSelectedRanges([new SlickRange(args.row, args.cell)], undefined, ''); + } else if (!this._options?.selectActiveCell || (!isRowDefined && !isCellDefined)) { + // clear the previous selection once the cell changes + this.setSelectedRanges([], undefined, ''); + } + } + } + + protected isKeyAllowed(key: string) { + return ['ArrowLeft', 'ArrowRight', 'ArrowUp', 'ArrowDown', 'PageDown', 'PageUp', 'Home', 'End'].some(k => k === key); + } + + protected handleKeyDown(e: SlickEventData_) { + if (!this._activeSelectionIsRow) { + let ranges: SlickRange_[], last: SlickRange_; + const colLn = this._grid.getColumns().length; + const active = this._grid.getActiveCell(); + let dataLn = 0; + if (this._dataView && 'getPagingInfo' in this._dataView) { + dataLn = this._dataView?.getPagingInfo().pageSize || this._dataView.getLength(); + } else { + dataLn = this._grid.getDataLength(); + } + + if (active && (e.shiftKey || e.ctrlKey) && !e.altKey && this.isKeyAllowed(e.key as string)) { + ranges = this.getSelectedRanges().slice(); + if (!ranges.length) { + ranges.push(new SlickRange(active.row, active.cell)); + } + // keyboard can work with last range only + last = ranges.pop() as SlickRange_; + + // can't handle selection out of active cell + if (!last.contains(active.row, active.cell)) { + last = new SlickRange(active.row, active.cell); + } + + let dRow = last.toRow - last.fromRow; + let dCell = last.toCell - last.fromCell; + + // walking direction + const dirRow = active.row === last.fromRow ? 1 : -1; + const dirCell = active.cell === last.fromCell ? 1 : -1; + const isSingleKeyMove = e.key!.startsWith('Arrow'); + let toCell: undefined | number = undefined; + let toRow = 0; + + if (isSingleKeyMove && !e.ctrlKey) { + // single cell move: (Arrow{Up/ArrowDown/ArrowLeft/ArrowRight}) + if (e.key === 'ArrowLeft') { + dCell -= dirCell; + } else if (e.key === 'ArrowRight') { + dCell += dirCell; + } else if (e.key === 'ArrowUp') { + dRow -= dirRow; + } else if (e.key === 'ArrowDown') { + dRow += dirRow; + } + toRow = active.row + dirRow * dRow; + } else { + // multiple cell moves: (Home, End, Page{Up/Down}) + if (this._cachedPageRowCount < 1) { + this._cachedPageRowCount = this._grid.getViewportRowCount(); + } + if (this._prevSelectedRow === undefined) { + this._prevSelectedRow = active.row; + } + + if (e.shiftKey && !e.ctrlKey && e.key === 'Home') { + toCell = 0; + toRow = active.row; + } else if (e.shiftKey && !e.ctrlKey && e.key === 'End') { + toCell = colLn - 1; + toRow = active.row; + } else if (e.ctrlKey && e.shiftKey && e.key === 'Home') { + toCell = 0; + toRow = 0; + } else if (e.ctrlKey && e.shiftKey && e.key === 'End') { + toCell = colLn - 1; + toRow = dataLn - 1; + } else if (e.key === 'PageUp') { + if (this._prevSelectedRow >= 0) { + toRow = this._prevSelectedRow - this._cachedPageRowCount; + } + if (toRow < 0) { + toRow = 0; + } + } else if (e.key === 'PageDown') { + if (this._prevSelectedRow <= dataLn - 1) { + toRow = this._prevSelectedRow + this._cachedPageRowCount; + } + if (toRow > dataLn - 1) { + toRow = dataLn - 1; + } + } + this._prevSelectedRow = toRow; + } + + // define new selection range + toCell ??= active.cell + dirCell * dCell; + const new_last = new SlickRange(active.row, active.cell, toRow, toCell); + if (this.removeInvalidRanges([new_last]).length) { + ranges.push(new_last); + const viewRow = dirRow > 0 ? new_last.toRow : new_last.fromRow; + const viewCell = dirCell > 0 ? new_last.toCell : new_last.fromCell; + + if (isSingleKeyMove) { + this._grid.scrollRowIntoView(viewRow); + this._grid.scrollCellIntoView(viewRow, viewCell); + } else { + this._grid.scrollRowIntoView(toRow); + this._grid.scrollCellIntoView(toRow, viewCell); + } + } else { + ranges.push(last); + } + + this.setSelectedRanges(ranges, undefined, ''); + + e.preventDefault(); + e.stopPropagation(); + this._prevKeyDown = e.key as string; + } + } else { + const activeRow = this._grid.getActiveCell(); + if (this._grid.getOptions().multiSelect && activeRow + && e.shiftKey && !e.ctrlKey && !e.altKey && !e.metaKey + && (e.which === keyCode.UP || e.which === keyCode.DOWN)) { + let selectedRows = this.getSelectedRows(); + selectedRows.sort(function (x, y) { + return x - y; + }); + + if (!selectedRows.length) { + selectedRows = [activeRow.row]; + } + + let top = selectedRows[0]; + let bottom = selectedRows[selectedRows.length - 1]; + let active: number; + + if (e.which === keyCode.DOWN) { + active = activeRow.row < bottom || top === bottom ? ++bottom : ++top; + } else { + active = activeRow.row < bottom ? --bottom : --top; + } + + if (active >= 0 && active < this._grid.getDataLength()) { + this._grid.scrollRowIntoView(active); + const tempRanges = this.rowsToRanges(this.getRowsRange(top, bottom)); + this.setSelectedRanges(tempRanges, undefined, ''); + } + + e.preventDefault(); + e.stopPropagation(); + } + } + } + + protected handleClick(e: SlickEventData_): boolean | void { + if (!this._activeSelectionIsRow) { return; } + + const cell = this._grid.getCellFromEvent(e); + if (!cell || !this._grid.canCellBeActive(cell.row, cell.cell)) { + return false; + } + + if (!this._grid.getOptions().multiSelect || ( + !e.ctrlKey && !e.shiftKey && !e.metaKey)) { + return false; + } + + let selection = this.rangesToRows(this._ranges); + const idx = selection.indexOf(cell.row); + + if (idx === -1 && (e.ctrlKey || e.metaKey)) { + selection.push(cell.row); + this._grid.setActiveCell(cell.row, cell.cell); + } else if (idx !== -1 && (e.ctrlKey || e.metaKey)) { + selection = selection.filter((o) => o !== cell.row); + this._grid.setActiveCell(cell.row, cell.cell); + } else if (selection.length && e.shiftKey) { + const last = selection.pop() as number; + const from = Math.min(cell.row, last); + const to = Math.max(cell.row, last); + selection = []; + for (let i = from; i <= to; i++) { + if (i !== last) { + selection.push(i); + } + } + selection.push(last); + this._grid.setActiveCell(cell.row, cell.cell); + } + + const tempRanges = this.rowsToRanges(selection); + this.setSelectedRanges(tempRanges, undefined, ''); + e.stopImmediatePropagation(); + + return true; + } + + protected handleBeforeCellRangeSelected(e: SlickEventData_, cell: { row: number; cell: number; }): boolean | void { + if (this._activeSelectionIsRow) { + if (!this._isRowMoveManagerHandler) { + const rowMoveManager = this._grid.getPluginByName('RowMoveManager') || this._grid.getPluginByName('CrossGridRowMoveManager'); + this._isRowMoveManagerHandler = rowMoveManager ? rowMoveManager.isHandlerColumn : Utils.noop; + } + if (this._grid.getEditorLock().isActive() || this._isRowMoveManagerHandler(cell.cell)) { + e.stopPropagation(); + return false; + } + this._grid.setActiveCell(cell.row, cell.cell); + } else { + if (this._grid.getEditorLock().isActive()) { + e.stopPropagation(); + return false; + } + } + } + + protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; selectionMode: string; allowAutoEdit?: boolean; }) { + if (this._activeSelectionIsRow) { + if (!this._grid.getOptions().multiSelect || !this._options?.selectActiveRow) { + return false; + } + this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)], undefined, args.selectionMode); + } else { + this._grid.setActiveCell(args.range.fromRow, args.range.fromCell, (args.allowAutoEdit ? undefined : false), false, true); + this.setSelectedRanges([args.range], undefined, args.selectionMode); + } + return true; + } +} + +// extend Slick namespace on window object when building as iife +if (IIFE_ONLY && window.Slick) { + Utils.extend(true, window, { + Slick: { + HybridSelectionModel: SlickHybridSelectionModel + } + }); +} diff --git a/src/plugins/slick.rowselectionmodel.ts b/src/plugins/slick.rowselectionmodel.ts index 80ecaff7..db0460d6 100644 --- a/src/plugins/slick.rowselectionmodel.ts +++ b/src/plugins/slick.rowselectionmodel.ts @@ -1,4 +1,5 @@ import { + // CellSelectionMode as CellSelectionMode_, keyCode as keyCode_, SlickEvent as SlickEvent_, SlickEventData as SlickEventData_, @@ -141,7 +142,7 @@ export class SlickRowSelectionModel { this.setSelectedRanges(this.rowsToRanges(rows), 'SlickRowSelectionModel.setSelectedRows'); } - setSelectedRanges(ranges: SlickRange_[], caller = 'SlickRowSelectionModel.setSelectedRanges') { + setSelectedRanges(ranges: SlickRange_[], caller = 'SlickRowSelectionModel.setSelectedRanges', selectionMode?: string) { // simple check for: empty selection didn't change, prevent firing onSelectedRangesChanged if ((!this._ranges || this._ranges.length === 0) && (!ranges || ranges.length === 0)) { return; @@ -150,7 +151,7 @@ export class SlickRowSelectionModel { // provide extra "caller" argument through SlickEventData event to avoid breaking the previous pubsub event structure // that only accepts an array of selected range `SlickRange[]`, the SlickEventData args will be merged and used later by `onSelectedRowsChanged` - const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller } }), this._ranges); + const eventData = new SlickEventData(new CustomEvent('click', { detail: { caller, selectionMode } }), this._ranges); this.onSelectedRangesChanged.notify(this._ranges, eventData); } @@ -256,11 +257,11 @@ export class SlickRowSelectionModel { this._grid.setActiveCell(cell.row, cell.cell); } - protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; }): boolean | void { + protected handleCellRangeSelected(_e: SlickEventData_, args: { range: SlickRange_; selectionMode: string; }): boolean | void { if (!this._grid.getOptions().multiSelect || !this._options.selectActiveRow) { return false; } - this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)]); + this.setSelectedRanges([new SlickRange(args.range.fromRow, 0, args.range.toRow, this._grid.getColumns().length - 1)], undefined, args.selectionMode); } } diff --git a/src/slick.core.ts b/src/slick.core.ts index 85ef6aa3..18937b97 100644 --- a/src/slick.core.ts +++ b/src/slick.core.ts @@ -11,8 +11,10 @@ import type { ElementEventListener, Handler, InferDOMType, - MergeTypes + MergeTypes, + DragRange } from './models/index.js'; +import { SlickGrid } from './slick.grid.js'; export interface BasePubSub { publish(_eventName: string | any, _data?: ArgType): any; @@ -327,6 +329,24 @@ export class SlickRange { return this.fromRow === this.toRow && this.fromCell === this.toCell; } + /** + * Row Count. + * @method rowCount + * @return {Number} + */ + rowCount() { + return this.toRow - this.fromRow + 1; + } + + /** + * Cell Count. + * @method cellCount + * @return {Number} + */ + cellCount() { + return this.toCell - this.fromCell + 1; + } + /** * Returns whether a range contains a given cell. * @method contains @@ -354,6 +374,63 @@ export class SlickRange { }; } +/** + * A structure containing a range of cells to copy to. + * @class SlickCopyRange + * @constructor + * @param fromRow {Integer} Starting row. + * @param fromCell {Integer} Starting cell. + * @param rowCount {Integer} Row Count. + * @param cellCount {Integer} Cell Count. + */ +export class SlickCopyRange { + fromRow: number; + fromCell: number; + rowCount: number; + cellCount: number; + + constructor(fromRow: number, fromCell: number, rowCount: number, cellCount: number) { + this.fromRow = fromRow; + this.fromCell = fromCell; + this.rowCount = rowCount; + this.cellCount = cellCount; + } + +} + +/** + * Create a handle element for Excel style drag-replace + * @class DragExtendHandle + * @constructor + * @param gridUid {String} string UID of parent grid + */ +export class SlickDragExtendHandle { + id: string; + cssClass = 'slick-drag-replace-handle'; + + constructor(gridUid: string) { + this.id = gridUid + "_drag_replace_handle"; + } + + getHandleHtml() { + return '
'; + //console.log('DragReplaceEl.getStringEl'); + } + + removeEl() { + const dragReplaceEl = document.getElementById(this.id); + if (dragReplaceEl) { dragReplaceEl.remove(); } + //console.log('DragReplaceEl.removeEl'); + } + + createEl(activeCellNode: any) { + const dragReplaceEl = document.createElement("div"); + dragReplaceEl.classList.add("slick-drag-replace-handle"); + dragReplaceEl.setAttribute("id", this.id); + activeCellNode.appendChild(dragReplaceEl); + console.log('DragReplaceEl.createEl'); + } +} /** * A base class that all special / non-data rows (like Group and GroupTotals) derive from. @@ -1086,6 +1163,145 @@ export class Utils { } } +export class SelectionUtils { + + // |---0----|---1----|---2----|---3----|---4----|---5----| + // 0 | | | | ^ | | | + // |--------|--------|--------|--------|--------|--------| + // 1 | | | | | | | + // |--------|--------|--------|--------|--------|--------| + // 2 | | | 1 | 2 | > h | | + // |--------|--------|--------|--------|--------|--------| + // 3 | < | | 4 | 5 x| > h | > | + // |--------|--------|--------|--------|--------|--------| + // 4 | | | > v | > v | > v | | + // |--------|--------|--------|--------|--------|--------| + // 5 | | | | v | | | + // |--------|--------|--------|--------|--------|--------| + // + // original range (1,2,4,5) expanded one cell to right and down + // '> h' indicates horizontal target copy area + // '> v' indicates vertical target copy area + // note bottom right (corner) cell is considered part of vertical copy area + + public static normaliseDragRange(rawRange: DragRange) { + // depending how the range is created (drag up/down) the start row/cell may be + // greater or less thatn the end row/cell. Create a guaranteed left/down + // progressive range (ie. start row/cell < end row/cell) + + let rtn : DragRange = { + start : { + row: (rawRange.end.row ?? 0) > (rawRange.start.row ?? 0) ? rawRange.start.row : rawRange.end.row, + cell: (rawRange.end.cell ?? 0) > (rawRange.start.cell ?? 0) ? rawRange.start.cell : rawRange.end.cell + }, + end : { + row: (rawRange.end.row ?? 0) > (rawRange.start.row ?? 0) ? rawRange.end.row : rawRange.start.row, + cell: (rawRange.end.cell ?? 0) > (rawRange.start.cell ?? 0) ? rawRange.end.cell : rawRange.start.cell + } + }; + rtn.rowCount = (rtn.end.row ?? 0) - (rtn.start.row ?? 0) + 1; + rtn.cellCount = (rtn.end.cell ?? 0) - (rtn.start.cell ?? 0) + 1; + + rtn.wasDraggedUp = (rawRange.end.row ?? 0) < (rawRange.start.row ?? 0); + rtn.wasDraggedLeft = (rawRange.end.row ?? 0) < (rawRange.start.row ?? 0); + + return rtn; + } + + public static copyRangeIsLarger(baseRange: SlickRange, copyToRange : SlickRange) : boolean { + return copyToRange.fromRow < baseRange.fromRow + || copyToRange.fromCell < baseRange.fromCell + || copyToRange.toRow > baseRange.toRow + || copyToRange.toCell > baseRange.toCell + ; + } + + public static normalRangeOppositeCellFromCopy(normalisedDragRange : DragRange, targetCell : { row: number, cell: number }) : { row: number, cell: number } { + let row = targetCell.row < (normalisedDragRange.end.row || 0) + ? (normalisedDragRange.end.row || 0) + : (normalisedDragRange.start.row || 0) + ; + let cell = targetCell.cell < (normalisedDragRange.end.cell || 0) + ? (normalisedDragRange.end.cell || 0) + : (normalisedDragRange.start.cell || 0) + ; + return { row: row, cell: cell }; + } + + // copy to range above or below - includes corner space target range + public static verticalTargetRange(baseRange: SlickRange, copyToRange : SlickRange) { + let copyUp = copyToRange.fromRow < baseRange.fromRow; + let copyDown = copyToRange.toRow > baseRange.toRow; + if (!copyUp && !copyDown) { + return null; + } + let rtn; + if (copyUp) { + rtn = new Slick.Range(copyToRange.fromRow, copyToRange.fromCell, baseRange.fromRow - 1, copyToRange.toCell); + } else { + rtn = new Slick.Range(baseRange.toRow + 1, copyToRange.fromCell, copyToRange.toRow, copyToRange.toCell); + } + return rtn; + } + + // copy to range left or right - excludes corner space target range + public static horizontalTargetRange(baseRange: SlickRange, copyToRange : SlickRange) { + let copyLeft = copyToRange.fromCell < baseRange.fromCell; + let copyRight = copyToRange.toCell > baseRange.toCell; + if (!copyLeft && !copyRight) { + return null; + } + let rtn; + if (copyLeft) { + rtn = new Slick.Range(baseRange.fromRow, copyToRange.fromCell, baseRange.toRow, baseRange.fromCell - 1); + } else { + rtn = new Slick.Range(baseRange.fromRow, baseRange.toCell + 1, baseRange.toRow, copyToRange.toCell); + } + return rtn; + } + + + public static defaultCopyDraggedCellRange(e , args) { + let verticalTargetRange = Slick.SelectionUtils.verticalTargetRange(args.prevSelectedRange, args.selectedRange); + let horizontalTargetRange = Slick.SelectionUtils.horizontalTargetRange(args.prevSelectedRange, args.selectedRange); + + if (verticalTargetRange) { Slick.SelectionUtils.copyCellsToTargetRange(args.prevSelectedRange, verticalTargetRange, args.grid); } + if (horizontalTargetRange) { Slick.SelectionUtils.copyCellsToTargetRange(args.prevSelectedRange, horizontalTargetRange, args.grid); } + } + + + public static copyCellsToTargetRange(baseRange : SlickRange, targetRange: SlickRange, grid : SlickGrid) { + let fromRowOffset = 0, fromCellOffset = 0; + let columns = grid.getVisibleColumns(); + let options = grid.getOptions(); + + for (var i=0; i < targetRange.rowCount() ; i++){ + var toRow = grid.getDataItem(targetRange.fromRow + i); + var fromRow = grid.getDataItem(baseRange.fromRow + fromRowOffset); + fromCellOffset = 0; + + for (var j=0; j < targetRange.cellCount(); j++) { + var toColDef = columns[targetRange.fromCell + j]; + var fromColDef = columns[baseRange.fromCell + fromCellOffset]; + + if (!toColDef.hidden && !fromColDef.hidden) { + var val = fromRow[fromColDef.field]; + if (options.dataItemColumnValueExtractor) { + val = options.dataItemColumnValueExtractor(fromRow, fromColDef); + } + toRow[toColDef.field] = val; + } + + fromCellOffset++; + if (fromCellOffset >= baseRange.cellCount()) fromCellOffset = 0; + } + + fromRowOffset++; + if (fromRowOffset >= baseRange.rowCount()) fromRowOffset = 0; + } + } +} + export const SlickGlobalEditorLock = new SlickEditorLock(); // export Slick namespace on both global & window objects @@ -1094,6 +1310,8 @@ const SlickCore = { EventData: SlickEventData, EventHandler: SlickEventHandler, Range: SlickRange, + CopyRange: SlickCopyRange, + DragExtendHandle: SlickDragExtendHandle, NonDataRow: SlickNonDataItem, Group: SlickGroup, GroupTotals: SlickGroupTotals, @@ -1152,6 +1370,11 @@ const SlickCore = { LastRow: 'LS1' }, + 'CellSelectionMode': { + Select: "SEL", + Replace: "REP" + }, + 'ValueFilterMode': { None: 'NONE', DeDuplicate: 'DEDP', @@ -1168,9 +1391,9 @@ const SlickCore = { }; export const { - EditorLock, Event, EventData, EventHandler, Group, GroupTotals, NonDataRow, Range, + EditorLock, Event, EventData, EventHandler, Group, GroupTotals, NonDataRow, Range, CopyRange, DragExtendHandle, RegexSanitizer, GlobalEditorLock, keyCode, preClickClassName, GridAutosizeColsMode, ColAutosizeMode, - RowSelectionMode, ValueFilterMode, WidthEvalMode + RowSelectionMode, CellSelectionMode,ValueFilterMode, WidthEvalMode } = SlickCore; // also add to global object when exist diff --git a/src/slick.grid.ts b/src/slick.grid.ts index 01576033..eb698761 100644 --- a/src/slick.grid.ts +++ b/src/slick.grid.ts @@ -67,6 +67,7 @@ import type { OnSetOptionsEventArgs, OnScrollEventArgs, OnValidationErrorEventArgs, + OnDragReplaceCellsEventArgs, PagingInfo, RowInfo, SelectionModel, @@ -83,15 +84,20 @@ import { keyCode as keyCode_, preClickClassName as preClickClassName_, RowSelectionMode as RowSelectionMode_, + CellSelectionMode as CellSelectionMode_, type SlickEditorLock, SlickEvent as SlickEvent_, SlickEventData as SlickEventData_, SlickRange as SlickRange_, Utils as Utils_, + SelectionUtils as SelectionUtils_, ValueFilterMode as ValueFilterMode_, WidthEvalMode as WidthEvalMode_, -} from './slick.core.js'; -import { Draggable as Draggable_, MouseWheel as MouseWheel_, Resizable as Resizable_ } from './slick.interactions.js'; + DragExtendHandle as DragExtendHandle_, + SlickCopyRange as SlickCopyRange_ +} from './slick.core'; +import { Draggable as Draggable_, MouseWheel as MouseWheel_, Resizable as Resizable_ } from './slick.interactions'; +import { SlickColumnPicker } from 'dist/types'; // for (iife) load Slick methods from global Slick object, or use imports for (esm) const BindingEventService = IIFE_ONLY ? Slick.BindingEventService : BindingEventService_; @@ -103,13 +109,17 @@ const GridAutosizeColsMode = IIFE_ONLY ? Slick.GridAutosizeColsMode : GridAutosi const keyCode = IIFE_ONLY ? Slick.keyCode : keyCode_; const preClickClassName = IIFE_ONLY ? Slick.preClickClassName : preClickClassName_; const SlickRange = IIFE_ONLY ? Slick.Range : SlickRange_; +const SlickCopyRange = IIFE_ONLY ? Slick.CopyRange : SlickCopyRange_; const RowSelectionMode = IIFE_ONLY ? Slick.RowSelectionMode : RowSelectionMode_; +const CellSelectionMode = IIFE_ONLY ? Slick.CellSelectionMode : CellSelectionMode_; const ValueFilterMode = IIFE_ONLY ? Slick.ValueFilterMode : ValueFilterMode_; const Utils = IIFE_ONLY ? Slick.Utils : Utils_; +const SelectionUtils = IIFE_ONLY ? Slick.SelectionUtils : Utils_; const WidthEvalMode = IIFE_ONLY ? Slick.WidthEvalMode : WidthEvalMode_; const Draggable = IIFE_ONLY ? Slick.Draggable : Draggable_; const MouseWheel = IIFE_ONLY ? Slick.MouseWheel : MouseWheel_; const Resizable = IIFE_ONLY ? Slick.Resizable : Resizable_; +const DragExtendHandle = IIFE_ONLY ? Slick.DragExtendHandle : DragExtendHandle_; /** * @license @@ -203,6 +213,7 @@ export class SlickGrid = Column, O e onSort: SlickEvent_; onValidationError: SlickEvent_; onViewportChanged: SlickEvent_<{ grid: SlickGrid; }>; + onDragReplaceCells: SlickEvent_; // --- // protected variables @@ -361,6 +372,7 @@ export class SlickGrid = Column, O e protected initialized = false; protected _container!: HTMLElement; protected uid = `slickgrid_${Math.round(1000000 * Math.random())}`; + protected dragReplaceEl = new DragExtendHandle(this.uid); protected _focusSink!: HTMLDivElement; protected _focusSink2!: HTMLDivElement; protected _groupHeaders: HTMLDivElement[] = []; @@ -425,6 +437,8 @@ export class SlickGrid = Column, O e protected activePosY!: number; protected activeRow!: number; protected activeCell!: number; + protected selectionBottomRow!: number; + protected selectionRightCell!: number; protected activeCellNode: HTMLDivElement | null = null; protected currentEditor: Editor | null = null; protected serializedEditorValue: any; @@ -446,6 +460,7 @@ export class SlickGrid = Column, O e protected selectionModel?: SelectionModel; protected selectedRows: number[] = []; + protected selectedRanges: SlickRange_[] = []; protected plugins: SlickPlugin[] = []; protected cellCssClasses: CssStyleHash = {}; @@ -608,6 +623,7 @@ export class SlickGrid = Column, O e this.onSort = new SlickEvent('onSort', externalPubSub); this.onValidationError = new SlickEvent('onValidationError', externalPubSub); this.onViewportChanged = new SlickEvent<{ grid: SlickGrid; }>('onViewportChanged', externalPubSub); + this.onDragReplaceCells = new SlickEvent('onDragReplaceCells', externalPubSub); this.initialize(options); } @@ -959,7 +975,8 @@ export class SlickGrid = Column, O e if (Draggable) { this.slickDraggableInstance = Draggable({ containerElement: this._container, - allowDragFrom: 'div.slick-cell', + allowDragFrom: 'div.slick-cell, div.' + this.dragReplaceEl.cssClass, + dragFromClassDetectArr: [{ tag: 'dragReplaceHandle', id: this.dragReplaceEl.id }], // the slick cell parent must always contain `.dnd` and/or `.cell-reorder` class to be identified as draggable allowDragFromClosest: 'div.slick-cell.dnd, div.slick-cell.cell-reorder', preventDragFromKeys: this._options.preventDragFromKeys, @@ -3885,7 +3902,31 @@ export class SlickGrid = Column, O e */ protected handleSelectedRangesChanged(e: SlickEventData_, ranges: SlickRange_[]) { const ne = e.getNativeEvent(); + const selectionMode = ne?.detail?.selectionMode ?? ''; + + // drag and replace functionality + const prevSelectedRanges = this.selectedRanges.slice(0); + this.selectedRanges = ranges; + + if (selectionMode === CellSelectionMode.Replace + && prevSelectedRanges && prevSelectedRanges.length === 1 + && this.selectedRanges && this.selectedRanges.length === 1) { + const prevSelectedRange = prevSelectedRanges[0]; + const selectedRange = this.selectedRanges[0]; + + // check range has expanded + if (Slick.SelectionUtils.copyRangeIsLarger(prevSelectedRange, selectedRange)) { + this.trigger(this.onDragReplaceCells, { prevSelectedRange: prevSelectedRange, selectedRange: selectedRange }); + this.invalidate(); + } + } + const previousSelectedRows = this.selectedRows.slice(0); // shallow copy previously selected rows for later comparison + + this.selectionBottomRow = -1; + this.selectionRightCell = -1; + this.dragReplaceEl.removeEl(); + this.selectedRows = []; const hash: CssStyleHash = {}; for (let i = 0; i < ranges.length; i++) { @@ -3900,11 +3941,18 @@ export class SlickGrid = Column, O e } } } + if (this.selectionBottomRow < ranges[i].toRow) { this.selectionBottomRow = ranges[i].toRow; } + if (this.selectionRightCell < ranges[i].toCell) { this.selectionRightCell = ranges[i].toCell; } } this.setCellCssStyles(this._options.selectedCellCssClass || '', hash); - if (this.simpleArrayEquals(previousSelectedRows, this.selectedRows)) { + if (this.selectionBottomRow >= 0 && this.selectionRightCell >= 0) { + const lowerRightCell = this.getCellNode(this.selectionBottomRow, this.selectionRightCell) + this.dragReplaceEl.createEl(lowerRightCell); + } + + if (this.simpleArraysNotEqual(previousSelectedRows, this.selectedRows)) { const caller = ne?.detail?.caller ?? 'click'; // Use Set for faster performance const selectedRowsSet = new Set(this.getSelectedRows()); @@ -3923,6 +3971,11 @@ export class SlickGrid = Column, O e } } + // compare 2 simple arrays (integers or strings only, do not use to compare object arrays) + simpleArraysNotEqual(arr1: any[], arr2: any[]) { + return Array.isArray(arr1) && Array.isArray(arr2) && arr2.sort().toString() !== arr1.sort().toString(); + } + /** * Processes a mouse wheel event by adjusting the vertical scroll (scrollTop) based on deltaY (scaled by rowHeight) * and horizontal scroll (scrollLeft) based on deltaX. It then calls the internal scroll handler with the “mousewheel” @@ -3982,11 +4035,13 @@ export class SlickGrid = Column, O e * @returns {boolean} - The result of the event trigger or false if propagation was not stopped. */ protected handleDragStart(e: DragEvent, dd: DragPosition) { - const cell = this.getCellFromEvent(e); + const cell = this.getCellFromEvent(e); if (!cell || !this.cellExists(cell.row, cell.cell)) { return false; } + if (this.currentEditor) { this.getEditorLock().commitCurrentEdit(); } + const retval = this.trigger(this.onDragStart, dd, e); if (retval.isImmediatePropagationStopped()) { return retval.getReturnValue(); @@ -4002,6 +4057,9 @@ export class SlickGrid = Column, O e // Called when a drag operation completes; it triggers the onDragEnd event with the current drag position and event. protected handleDragEnd(e: DragEvent, dd: DragPosition) { + if (dd.matchClassTag === 'dragReplaceHandle') { + this.dragReplaceEl.removeEl(); + } this.trigger(this.onDragEnd, dd, e); } @@ -5266,6 +5324,11 @@ export class SlickGrid = Column, O e if (item) { const cellResult = (Object.prototype.toString.call(formatterResult) !== '[object Object]' ? formatterResult : (formatterResult as FormatterResultWithHtml).html || (formatterResult as FormatterResultWithText).text); this.applyHtmlCode(cellDiv, cellResult as string | HTMLElement); + + // add drag-to-replace handle + if (row === this.selectionBottomRow && cell === this.selectionRightCell && this._options.showCellSelection) { + this.dragReplaceEl.createEl(cellDiv); + } } divRow.appendChild(cellDiv); diff --git a/src/slick.interactions.ts b/src/slick.interactions.ts index b08ac89b..99a90e6a 100644 --- a/src/slick.interactions.ts +++ b/src/slick.interactions.ts @@ -1,4 +1,4 @@ -import type { DragItem, DragPosition, DraggableOption, MouseWheelOption, ResizableOption } from './models/index.js'; +import type { DragItem, DragPosition, ClassDetectElement, DraggableOption, MouseWheelOption, ResizableOption } from './models/index.js'; import { Utils as Utils_ } from './slick.core.js'; // for (iife) load Slick methods from global Slick object, or use imports for (esm) @@ -18,6 +18,8 @@ const Utils = IIFE_ONLY ? Slick.Utils : Utils_; * https://betterprogramming.pub/perfecting-drag-and-drop-in-pure-vanilla-javascript-a761184b797a * available optional options: * - containerElement: container DOM element, defaults to "document" + * - dragFromClassDetectArr: array of tags and query selectors/ids to match on dragstart, used to determine + * drag source element. eg: [ { tag: 'B', id: 'myElement' }, { tag: 'A', cssSelector: 'div.myClass' } ] * - allowDragFrom: when defined, only allow dragging from an element that matches a specific query selector * - allowDragFromClosest: when defined, only allow dragging from an element or its parent matching a specific .closest() query selector * - onDragInit: drag initialized callback @@ -37,6 +39,7 @@ export function Draggable(options: DraggableOption) { let deltaX: number; let deltaY: number; let dragStarted: boolean; + let matchClassTag: string; if (!containerElement) { containerElement = document.body; @@ -88,12 +91,25 @@ export function Draggable(options: DraggableOption) { if (!options.allowDragFrom || (options.allowDragFrom && (element.matches(options.allowDragFrom)) || (options.allowDragFromClosest && element.closest(options.allowDragFromClosest)))) { originaldd.dragHandle = element as HTMLElement; + + matchClassTag = ''; + if (options.dragFromClassDetectArr) { + for (let o: ClassDetectElement, i = 0; i < options.dragFromClassDetectArr.length; i++) { + o = options.dragFromClassDetectArr[i]; + + if ((o.id && element.id === o.id) || (o.cssSelector && element.matches(o.cssSelector))) { + matchClassTag = o.tag; + break; + } + } + } + const winScrollPos = Utils.windowScrollPosition(); startX = winScrollPos.left + targetEvent.clientX; startY = winScrollPos.top + targetEvent.clientY; deltaX = targetEvent.clientX - targetEvent.clientX; deltaY = targetEvent.clientY - targetEvent.clientY; - originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target }); + originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target, matchClassTag }); const result = executeDragCallbackWhenDefined(onDragInit as (e: DragEvent, dd: DragPosition) => boolean | void, event, originaldd as DragItem); if (result !== false) { @@ -103,6 +119,7 @@ export function Draggable(options: DraggableOption) { document.body.addEventListener('touchend', userReleased); document.body.addEventListener('touchcancel', userReleased); } + console.log('userPressed.matchClassTag: ' + matchClassTag); } } } @@ -115,12 +132,12 @@ export function Draggable(options: DraggableOption) { const { target } = targetEvent; if (!dragStarted) { - originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target }); + originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target, matchClassTag }); executeDragCallbackWhenDefined(onDragStart, event, originaldd as DragItem); dragStarted = true; } - originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target }); + originaldd = Object.assign(originaldd, { deltaX, deltaY, startX, startY, target, matchClassTag }); executeDragCallbackWhenDefined(onDrag, event, originaldd as DragItem); } } diff --git a/src/styles/slick-alpine-theme.scss b/src/styles/slick-alpine-theme.scss index 3fc660ea..3427af04 100644 --- a/src/styles/slick-alpine-theme.scss +++ b/src/styles/slick-alpine-theme.scss @@ -857,4 +857,12 @@ button.slick-btn { background-color: var(--alpine-button-primary-hover-color, v.$alpine-button-primary-hover-color); } } -} \ No newline at end of file +} +.slick-drag-replace-handle { + height: 7px; + width: 7px; + background: gray; + position: absolute; + bottom: 0; + right: 0; +} diff --git a/src/styles/slick-default-theme.scss b/src/styles/slick-default-theme.scss index d3d61f60..c3ef11b3 100644 --- a/src/styles/slick-default-theme.scss +++ b/src/styles/slick-default-theme.scss @@ -159,6 +159,15 @@ classes should alter those! -webkit-animation-name: slickgrid-invalid-hilite; } +.slick-drag-replace-handle { + height: 7px; + width: 7px; + background: gray; + position: absolute; + bottom: 0; + right: 0; +} + @-moz-keyframes slickgrid-invalid-hilite { from { box-shadow: 0 0 6px red; } to { box-shadow: none; } diff --git a/src/styles/slick.grid.scss b/src/styles/slick.grid.scss index 39ccd548..0aea7db8 100644 --- a/src/styles/slick.grid.scss +++ b/src/styles/slick.grid.scss @@ -273,3 +273,4 @@ classes should alter those! outline: 0; width: 100%; } +